aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2014-01-07 21:39:47 +0100
committerMax Kellermann <max@duempel.org>2014-01-09 09:05:58 +0100
commit322b0616322760dc162447563d8f4da7e024ca90 (patch)
tree2f87cb3ce061556161797aba9f57ee08de5b9e21
parent43847f2244a34064af24704aac4cfad4a3c76f7d (diff)
downloadmpd-322b0616322760dc162447563d8f4da7e024ca90.tar.gz
mpd-322b0616322760dc162447563d8f4da7e024ca90.tar.xz
mpd-322b0616322760dc162447563d8f4da7e024ca90.zip
DetachedSong: fork of struct Song
From now on, struct Song will be used by the database only, and DetachedSong will be used by everybody else. DetachedSong is easier to use, but Song has lower overhead.
-rw-r--r--Makefile.am4
-rw-r--r--src/DatabasePlaylist.cxx3
-rw-r--r--src/DatabasePrint.cxx18
-rw-r--r--src/DatabaseQueue.cxx6
-rw-r--r--src/DecoderAPI.cxx4
-rw-r--r--src/DecoderControl.cxx15
-rw-r--r--src/DecoderControl.hxx10
-rw-r--r--src/DecoderThread.cxx13
-rw-r--r--src/DetachedSong.cxx (renamed from src/SongPointer.hxx)76
-rw-r--r--src/DetachedSong.hxx184
-rw-r--r--src/Directory.cxx8
-rw-r--r--src/Directory.hxx4
-rw-r--r--src/DirectorySave.cxx8
-rw-r--r--src/Instance.cxx4
-rw-r--r--src/Instance.hxx3
-rw-r--r--src/Mapper.cxx17
-rw-r--r--src/Mapper.hxx5
-rw-r--r--src/MemorySongEnumerator.cxx4
-rw-r--r--src/MemorySongEnumerator.hxx8
-rw-r--r--src/Partition.cxx6
-rw-r--r--src/Partition.hxx4
-rw-r--r--src/PlayerControl.cxx32
-rw-r--r--src/PlayerControl.hxx36
-rw-r--r--src/PlayerThread.cxx70
-rw-r--r--src/Playlist.cxx43
-rw-r--r--src/Playlist.hxx14
-rw-r--r--src/PlaylistControl.cxx7
-rw-r--r--src/PlaylistEdit.cxx48
-rw-r--r--src/PlaylistFile.cxx21
-rw-r--r--src/PlaylistFile.hxx4
-rw-r--r--src/PlaylistPrint.cxx5
-rw-r--r--src/PlaylistQueue.cxx10
-rw-r--r--src/PlaylistSave.cxx5
-rw-r--r--src/PlaylistSave.hxx4
-rw-r--r--src/PlaylistSong.cxx81
-rw-r--r--src/PlaylistSong.hxx6
-rw-r--r--src/PlaylistTag.cxx36
-rw-r--r--src/PlaylistUpdate.cxx13
-rw-r--r--src/Queue.cxx16
-rw-r--r--src/Queue.hxx10
-rw-r--r--src/QueuePrint.cxx2
-rw-r--r--src/QueueSave.cxx35
-rw-r--r--src/Song.cxx96
-rw-r--r--src/Song.hxx67
-rw-r--r--src/SongEnumerator.hxx4
-rw-r--r--src/SongFilter.cxx23
-rw-r--r--src/SongFilter.hxx7
-rw-r--r--src/SongPrint.cxx54
-rw-r--r--src/SongPrint.hxx7
-rw-r--r--src/SongSave.cxx56
-rw-r--r--src/SongSave.hxx8
-rw-r--r--src/SongSticker.cxx10
-rw-r--r--src/SongUpdate.cxx8
-rw-r--r--src/UpdateRemove.cxx5
-rw-r--r--src/cue/CueParser.cxx35
-rw-r--r--src/cue/CueParser.hxx10
-rw-r--r--src/db/ProxyDatabasePlugin.cxx25
-rw-r--r--src/playlist/AsxPlaylistPlugin.cxx42
-rw-r--r--src/playlist/CuePlaylistPlugin.cxx6
-rw-r--r--src/playlist/DespotifyPlaylistPlugin.cxx16
-rw-r--r--src/playlist/EmbeddedCuePlaylistPlugin.cxx16
-rw-r--r--src/playlist/ExtM3uPlaylistPlugin.cxx13
-rw-r--r--src/playlist/M3uPlaylistPlugin.cxx8
-rw-r--r--src/playlist/PlsPlaylistPlugin.cxx17
-rw-r--r--src/playlist/RssPlaylistPlugin.cxx42
-rw-r--r--src/playlist/SoundCloudPlaylistPlugin.cxx8
-rw-r--r--src/playlist/XspfPlaylistPlugin.cxx37
-rw-r--r--test/DumpDatabase.cxx5
-rw-r--r--test/dump_playlist.cxx34
-rw-r--r--test/test_queue_priority.cxx41
70 files changed, 817 insertions, 785 deletions
diff --git a/Makefile.am b/Makefile.am
index 20cc603ca..a63c9d1d7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -192,6 +192,7 @@ src_mpd_SOURCES = \
src/ReplayGainConfig.cxx src/ReplayGainConfig.hxx \
src/ReplayGainInfo.cxx src/ReplayGainInfo.hxx \
src/SignalHandlers.cxx src/SignalHandlers.hxx \
+ src/DetachedSong.cxx src/DetachedSong.hxx \
src/Song.cxx src/Song.hxx \
src/SongUpdate.cxx \
src/SongPrint.cxx src/SongPrint.hxx \
@@ -206,7 +207,6 @@ src_mpd_SOURCES = \
src/TextInputStream.cxx \
src/Volume.cxx src/Volume.hxx \
src/SongFilter.cxx src/SongFilter.hxx \
- src/SongPointer.hxx \
src/PlaylistFile.cxx src/PlaylistFile.hxx \
src/Timer.cxx
@@ -1271,7 +1271,7 @@ test_dump_playlist_SOURCES = test/dump_playlist.cxx \
$(DECODER_SRC) \
src/Log.cxx src/LogBackend.cxx \
src/IOThread.cxx \
- src/Song.cxx src/TagSave.cxx \
+ src/TagSave.cxx \
src/TagFile.cxx \
src/CheckAudioFormat.cxx \
src/TextInputStream.cxx \
diff --git a/src/DatabasePlaylist.cxx b/src/DatabasePlaylist.cxx
index b0cb19589..a29c9f2bc 100644
--- a/src/DatabasePlaylist.cxx
+++ b/src/DatabasePlaylist.cxx
@@ -23,6 +23,7 @@
#include "PlaylistFile.hxx"
#include "DatabaseGlue.hxx"
#include "DatabasePlugin.hxx"
+#include "DetachedSong.hxx"
#include <functional>
@@ -30,7 +31,7 @@ static bool
AddSong(const char *playlist_path_utf8,
Song &song, Error &error)
{
- return spl_append_song(playlist_path_utf8, song, error);
+ return spl_append_song(playlist_path_utf8, DetachedSong(song), error);
}
bool
diff --git a/src/DatabasePrint.cxx b/src/DatabasePrint.cxx
index e500ee0c8..feb58a263 100644
--- a/src/DatabasePrint.cxx
+++ b/src/DatabasePrint.cxx
@@ -55,26 +55,24 @@ PrintDirectoryFull(Client &client, const Directory &directory)
static void
print_playlist_in_directory(Client &client,
- const Directory &directory,
+ const Directory *directory,
const char *name_utf8)
{
- if (directory.IsRoot())
+ if (directory == nullptr || directory->IsRoot())
client_printf(client, "playlist: %s\n", name_utf8);
else
client_printf(client, "playlist: %s/%s\n",
- directory.GetPath(), name_utf8);
+ directory->GetPath(), name_utf8);
}
static bool
PrintSongBrief(Client &client, const Song &song)
{
- assert(song.parent != nullptr);
-
song_print_uri(client, song);
if (song.tag != nullptr && song.tag->has_playlist)
/* this song file has an embedded CUE sheet */
- print_playlist_in_directory(client, *song.parent, song.uri);
+ print_playlist_in_directory(client, song.parent, song.uri);
return true;
}
@@ -82,13 +80,11 @@ PrintSongBrief(Client &client, const Song &song)
static bool
PrintSongFull(Client &client, const Song &song)
{
- assert(song.parent != nullptr);
-
song_print_info(client, song);
if (song.tag != nullptr && song.tag->has_playlist)
/* this song file has an embedded CUE sheet */
- print_playlist_in_directory(client, *song.parent, song.uri);
+ print_playlist_in_directory(client, song.parent, song.uri);
return true;
}
@@ -98,7 +94,7 @@ PrintPlaylistBrief(Client &client,
const PlaylistInfo &playlist,
const Directory &directory)
{
- print_playlist_in_directory(client, directory, playlist.name.c_str());
+ print_playlist_in_directory(client, &directory, playlist.name.c_str());
return true;
}
@@ -107,7 +103,7 @@ PrintPlaylistFull(Client &client,
const PlaylistInfo &playlist,
const Directory &directory)
{
- print_playlist_in_directory(client, directory, playlist.name.c_str());
+ print_playlist_in_directory(client, &directory, playlist.name.c_str());
if (playlist.mtime > 0)
time_print(client, "Last-Modified", playlist.mtime);
diff --git a/src/DatabaseQueue.cxx b/src/DatabaseQueue.cxx
index b093ce073..3dac54630 100644
--- a/src/DatabaseQueue.cxx
+++ b/src/DatabaseQueue.cxx
@@ -23,14 +23,16 @@
#include "DatabasePlugin.hxx"
#include "Partition.hxx"
#include "util/Error.hxx"
+#include "DetachedSong.hxx"
#include <functional>
static bool
-AddToQueue(Partition &partition, Song &song, Error &error)
+AddToQueue(Partition &partition, const Song &song, Error &error)
{
PlaylistResult result =
- partition.playlist.AppendSong(partition.pc, &song, nullptr);
+ partition.playlist.AppendSong(partition.pc, DetachedSong(song),
+ nullptr);
if (result != PlaylistResult::SUCCESS) {
error.Set(playlist_domain, int(result), "Playlist error");
return false;
diff --git a/src/DecoderAPI.cxx b/src/DecoderAPI.cxx
index a3f2fc3e0..148d20005 100644
--- a/src/DecoderAPI.cxx
+++ b/src/DecoderAPI.cxx
@@ -28,7 +28,7 @@
#include "MusicPipe.hxx"
#include "DecoderControl.hxx"
#include "DecoderInternal.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "InputStream.hxx"
#include "util/Error.hxx"
#include "Log.hxx"
@@ -467,7 +467,7 @@ decoder_data(Decoder &decoder,
const auto dest =
chunk->Write(dc.out_audio_format,
decoder.timestamp -
- dc.song->start_ms / 1000.0,
+ dc.song->GetStartMS() / 1000.0,
kbit_rate);
if (dest.IsNull()) {
/* the chunk is full, flush it */
diff --git a/src/DecoderControl.cxx b/src/DecoderControl.cxx
index b63dba3a0..4e5e894e3 100644
--- a/src/DecoderControl.cxx
+++ b/src/DecoderControl.cxx
@@ -20,7 +20,7 @@
#include "config.h"
#include "DecoderControl.hxx"
#include "MusicPipe.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include <assert.h>
@@ -36,8 +36,7 @@ DecoderControl::~DecoderControl()
{
ClearError();
- if (song != nullptr)
- song->Free();
+ delete song;
}
void
@@ -53,7 +52,7 @@ DecoderControl::WaitForDecoder()
}
bool
-DecoderControl::IsCurrentSong(const Song &_song) const
+DecoderControl::IsCurrentSong(const DetachedSong &_song) const
{
switch (state) {
case DecoderState::STOP:
@@ -62,7 +61,7 @@ DecoderControl::IsCurrentSong(const Song &_song) const
case DecoderState::START:
case DecoderState::DECODE:
- return SongEquals(*song, _song);
+ return song->IsSame(_song);
}
assert(false);
@@ -70,16 +69,14 @@ DecoderControl::IsCurrentSong(const Song &_song) const
}
void
-DecoderControl::Start(Song *_song,
+DecoderControl::Start(DetachedSong *_song,
unsigned _start_ms, unsigned _end_ms,
MusicBuffer &_buffer, MusicPipe &_pipe)
{
assert(_song != nullptr);
assert(_pipe.IsEmpty());
- if (song != nullptr)
- song->Free();
-
+ delete song;
song = _song;
start_ms = _start_ms;
end_ms = _end_ms;
diff --git a/src/DecoderControl.hxx b/src/DecoderControl.hxx
index 863398dca..bacdad347 100644
--- a/src/DecoderControl.hxx
+++ b/src/DecoderControl.hxx
@@ -36,7 +36,7 @@
#undef ERROR
#endif
-struct Song;
+class DetachedSong;
class MusicBuffer;
class MusicPipe;
@@ -123,7 +123,7 @@ struct DecoderControl {
* This is a duplicate, and must be freed when this attribute
* is cleared.
*/
- Song *song;
+ DetachedSong *song;
/**
* The initial seek position (in milliseconds), e.g. to the
@@ -293,10 +293,10 @@ struct DecoderControl {
* Caller must lock the object.
*/
gcc_pure
- bool IsCurrentSong(const Song &_song) const;
+ bool IsCurrentSong(const DetachedSong &_song) const;
gcc_pure
- bool LockIsCurrentSong(const Song &_song) const {
+ bool LockIsCurrentSong(const DetachedSong &_song) const {
Lock();
const bool result = IsCurrentSong(_song);
Unlock();
@@ -360,7 +360,7 @@ public:
* @param pipe the pipe which receives the decoded chunks (owned by
* the caller)
*/
- void Start(Song *song, unsigned start_ms, unsigned end_ms,
+ void Start(DetachedSong *song, unsigned start_ms, unsigned end_ms,
MusicBuffer &buffer, MusicPipe &pipe);
void Stop();
diff --git a/src/DecoderThread.cxx b/src/DecoderThread.cxx
index f71462bdf..7099b3bd4 100644
--- a/src/DecoderThread.cxx
+++ b/src/DecoderThread.cxx
@@ -23,7 +23,7 @@
#include "DecoderInternal.hxx"
#include "DecoderError.hxx"
#include "DecoderPlugin.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "system/FatalError.hxx"
#include "Mapper.hxx"
#include "fs/Traits.hxx"
@@ -347,11 +347,10 @@ decoder_run_file(Decoder &decoder, const char *path_fs)
static void
decoder_run_song(DecoderControl &dc,
- const Song &song, const char *uri)
+ const DetachedSong &song, const char *uri)
{
Decoder decoder(dc, dc.start_ms > 0,
- song.tag != nullptr && song.IsFile()
- ? new Tag(*song.tag) : nullptr);
+ new Tag(song.GetTag()));
int ret;
dc.state = DecoderState::START;
@@ -381,7 +380,7 @@ decoder_run_song(DecoderControl &dc,
else {
dc.state = DecoderState::ERROR;
- const char *error_uri = song.uri;
+ const char *error_uri = song.GetURI();
const std::string allocated = uri_remove_auth(error_uri);
if (!allocated.empty())
error_uri = allocated.c_str();
@@ -399,10 +398,10 @@ decoder_run(DecoderControl &dc)
dc.ClearError();
assert(dc.song != nullptr);
- const Song &song = *dc.song;
+ const DetachedSong &song = *dc.song;
const std::string uri = song.IsFile()
- ? std::string(map_song_fs(song).c_str())
+ ? map_song_fs(song).c_str()
: song.GetURI();
if (uri.empty()) {
diff --git a/src/SongPointer.hxx b/src/DetachedSong.cxx
index ded3b3e1d..4b1d51a41 100644
--- a/src/SongPointer.hxx
+++ b/src/DetachedSong.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,47 +17,35 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_SONG_POINTER_HXX
-#define MPD_SONG_POINTER_HXX
-
+#include "config.h"
+#include "DetachedSong.hxx"
#include "Song.hxx"
-
-#include <utility>
-
-class SongPointer {
- Song *song;
-
-public:
- explicit SongPointer(Song *_song)
- :song(_song) {}
-
- SongPointer(const SongPointer &) = delete;
-
- SongPointer(SongPointer &&other):song(other.song) {
- other.song = nullptr;
- }
-
- ~SongPointer() {
- if (song != nullptr)
- song->Free();
- }
-
- SongPointer &operator=(const SongPointer &) = delete;
-
- SongPointer &operator=(SongPointer &&other) {
- std::swap(song, other.song);
- return *this;
- }
-
- operator const Song *() const {
- return song;
- }
-
- Song *Steal() {
- auto result = song;
- song = nullptr;
- return result;
- }
-};
-
-#endif
+#include "util/UriUtil.hxx"
+#include "fs/Traits.hxx"
+
+DetachedSong::DetachedSong(const Song &other)
+ :uri(other.GetURI().c_str()),
+ tag(other.tag != nullptr ? *other.tag : Tag()),
+ mtime(other.mtime),
+ start_ms(other.start_ms), end_ms(other.end_ms) {}
+
+bool
+DetachedSong::IsRemote() const
+{
+ return uri_has_scheme(uri.c_str());
+}
+
+bool
+DetachedSong::IsAbsoluteFile() const
+{
+ return PathTraitsUTF8::IsAbsolute(uri.c_str());
+}
+
+double
+DetachedSong::GetDuration() const
+{
+ if (end_ms > 0)
+ return (end_ms - start_ms) / 1000.0;
+
+ return tag.time - start_ms / 1000.0;
+}
diff --git a/src/DetachedSong.hxx b/src/DetachedSong.hxx
new file mode 100644
index 000000000..bee6a73a4
--- /dev/null
+++ b/src/DetachedSong.hxx
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DETACHED_SONG_HXX
+#define MPD_DETACHED_SONG_HXX
+
+#include "check.h"
+#include "tag/Tag.hxx"
+#include "Compiler.h"
+
+#include <string>
+#include <utility>
+
+#include <time.h>
+
+struct Song;
+
+class DetachedSong {
+ /**
+ * An UTF-8-encoded URI referring to the song file. This can
+ * be one of:
+ *
+ * - an absolute URL with a scheme
+ * (e.g. "http://example.com/foo.mp3")
+ *
+ * - an absolute file name
+ *
+ * - a file name relative to the music directory
+ */
+ std::string uri;
+
+ Tag tag;
+
+ time_t mtime;
+
+ /**
+ * Start of this sub-song within the file in milliseconds.
+ */
+ unsigned start_ms;
+
+ /**
+ * End of this sub-song within the file in milliseconds.
+ * Unused if zero.
+ */
+ unsigned end_ms;
+
+public:
+ explicit DetachedSong(const DetachedSong &other)
+ :uri(other.uri),
+ tag(other.tag),
+ mtime(other.mtime),
+ start_ms(other.start_ms), end_ms(other.end_ms) {}
+
+ explicit DetachedSong(const Song &other);
+
+ explicit DetachedSong(const char *_uri)
+ :uri(_uri),
+ mtime(0), start_ms(0), end_ms(0) {}
+
+ explicit DetachedSong(const std::string &_uri)
+ :uri(std::move(_uri)),
+ mtime(0), start_ms(0), end_ms(0) {}
+
+ explicit DetachedSong(std::string &&_uri)
+ :uri(std::move(_uri)),
+ mtime(0), start_ms(0), end_ms(0) {}
+
+ template<typename U>
+ DetachedSong(U &&_uri, Tag &&_tag)
+ :uri(std::forward<U>(_uri)),
+ tag(std::move(_tag)),
+ mtime(0), start_ms(0), end_ms(0) {}
+
+ DetachedSong(DetachedSong &&other)
+ :uri(std::move(other.uri)),
+ tag(std::move(other.tag)),
+ mtime(other.mtime),
+ start_ms(other.start_ms), end_ms(other.end_ms) {}
+
+ gcc_pure
+ const char *GetURI() const {
+ return uri.c_str();
+ }
+
+ template<typename T>
+ void SetURI(T &&_uri) {
+ uri = std::forward<T>(_uri);
+ }
+
+ /**
+ * Returns true if both objects refer to the same physical
+ * song.
+ */
+ gcc_pure
+ bool IsSame(const DetachedSong &other) const {
+ return uri == other.uri;
+ }
+
+ gcc_pure gcc_nonnull_all
+ bool IsURI(const char *other_uri) const {
+ return uri == other_uri;
+ }
+
+ gcc_pure
+ bool IsRemote() const;
+
+ gcc_pure
+ bool IsFile() const {
+ return !IsRemote();
+ }
+
+ gcc_pure
+ bool IsAbsoluteFile() const;
+
+ gcc_pure
+ bool IsInDatabase() const {
+ return IsFile() && !IsAbsoluteFile();
+ }
+
+ const Tag &GetTag() const {
+ return tag;
+ }
+
+ Tag &WritableTag() {
+ return tag;
+ }
+
+ void SetTag(const Tag &_tag) {
+ tag = Tag(_tag);
+ }
+
+ void SetTag(Tag &&_tag) {
+ tag = std::move(_tag);
+ }
+
+ void MoveTagFrom(DetachedSong &&other) {
+ tag = std::move(other.tag);
+ }
+
+ time_t GetLastModified() const {
+ return mtime;
+ }
+
+ void SetLastModified(time_t _value) {
+ mtime = _value;
+ }
+
+ unsigned GetStartMS() const {
+ return start_ms;
+ }
+
+ void SetStartMS(unsigned _value) {
+ start_ms = _value;
+ }
+
+ unsigned GetEndMS() const {
+ return end_ms;
+ }
+
+ void SetEndMS(unsigned _value) {
+ end_ms = _value;
+ }
+
+ gcc_pure
+ double GetDuration() const;
+};
+
+#endif
diff --git a/src/Directory.cxx b/src/Directory.cxx
index 750fee896..9180427f9 100644
--- a/src/Directory.cxx
+++ b/src/Directory.cxx
@@ -48,14 +48,6 @@ Directory::Allocate(const char *path)
path);
}
-Directory::Directory()
-{
- INIT_LIST_HEAD(&children);
- INIT_LIST_HEAD(&songs);
-
- path[0] = 0;
-}
-
Directory::Directory(const char *_path)
:mtime(0), have_stat(false)
{
diff --git a/src/Directory.hxx b/src/Directory.hxx
index 1ef4693e2..24ba685e6 100644
--- a/src/Directory.hxx
+++ b/src/Directory.hxx
@@ -95,10 +95,6 @@ protected:
static Directory *Allocate(const char *path);
public:
- /**
- * Default constructor, needed for #detached_root.
- */
- Directory();
~Directory();
/**
diff --git a/src/DirectorySave.cxx b/src/DirectorySave.cxx
index 709184289..f8ee5b0bd 100644
--- a/src/DirectorySave.cxx
+++ b/src/DirectorySave.cxx
@@ -22,6 +22,7 @@
#include "Directory.hxx"
#include "Song.hxx"
#include "SongSave.hxx"
+#include "DetachedSong.hxx"
#include "PlaylistDatabase.hxx"
#include "fs/TextFile.hxx"
#include "util/StringUtil.hxx"
@@ -132,7 +133,6 @@ directory_load(TextFile &file, Directory &directory, Error &error)
return false;
} else if (StringStartsWith(line, SONG_BEGIN)) {
const char *name = line + sizeof(SONG_BEGIN) - 1;
- Song *song;
if (directory.FindSong(name) != nullptr) {
error.Format(directory_domain,
@@ -140,11 +140,13 @@ directory_load(TextFile &file, Directory &directory, Error &error)
return false;
}
- song = song_load(file, &directory, name, error);
+ DetachedSong *song = song_load(file, name, error);
if (song == nullptr)
return false;
- directory.AddSong(song);
+ directory.AddSong(Song::NewFrom(std::move(*song),
+ &directory));
+ delete song;
} else if (StringStartsWith(line, PLAYLIST_META_BEGIN)) {
const char *name = line + sizeof(PLAYLIST_META_BEGIN) - 1;
if (!playlist_metadata_load(file, directory.playlists,
diff --git a/src/Instance.cxx b/src/Instance.cxx
index daad94212..a033ed82f 100644
--- a/src/Instance.cxx
+++ b/src/Instance.cxx
@@ -23,9 +23,9 @@
#include "Idle.hxx"
void
-Instance::DeleteSong(const Song &song)
+Instance::DeleteSong(const char *uri)
{
- partition->DeleteSong(song);
+ partition->DeleteSong(uri);
}
void
diff --git a/src/Instance.hxx b/src/Instance.hxx
index a0dfd1b94..45b1e44f1 100644
--- a/src/Instance.hxx
+++ b/src/Instance.hxx
@@ -24,14 +24,13 @@
class ClientList;
struct Partition;
-struct Song;
struct Instance {
ClientList *client_list;
Partition *partition;
- void DeleteSong(const Song &song);
+ void DeleteSong(const char *uri);
/**
* The database has been modified. Propagate the change to
diff --git a/src/Mapper.cxx b/src/Mapper.cxx
index fbe1e8c34..6910b4983 100644
--- a/src/Mapper.cxx
+++ b/src/Mapper.cxx
@@ -25,6 +25,7 @@
#include "Mapper.hxx"
#include "Directory.hxx"
#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "fs/AllocatedPath.hxx"
#include "fs/Traits.hxx"
#include "fs/Charset.hxx"
@@ -219,14 +220,18 @@ map_detached_song_fs(const char *uri_utf8)
AllocatedPath
map_song_fs(const Song &song)
{
- assert(song.IsFile());
+ return song.parent == nullptr
+ ? map_detached_song_fs(song.uri)
+ : map_directory_child_fs(*song.parent, song.uri);
+}
- if (song.IsInDatabase())
- return song.IsDetached()
- ? map_detached_song_fs(song.uri)
- : map_directory_child_fs(*song.parent, song.uri);
+AllocatedPath
+map_song_fs(const DetachedSong &song)
+{
+ if (song.IsAbsoluteFile())
+ return AllocatedPath::FromUTF8(song.GetURI());
else
- return AllocatedPath::FromUTF8(song.uri);
+ return map_uri_fs(song.GetURI());
}
std::string
diff --git a/src/Mapper.hxx b/src/Mapper.hxx
index c757c421e..be61ab43a 100644
--- a/src/Mapper.hxx
+++ b/src/Mapper.hxx
@@ -33,6 +33,7 @@
class AllocatedPath;
struct Directory;
struct Song;
+class DetachedSong;
void
mapper_init(AllocatedPath &&music_dir, AllocatedPath &&playlist_dir);
@@ -116,6 +117,10 @@ gcc_pure
AllocatedPath
map_song_fs(const Song &song);
+gcc_pure
+AllocatedPath
+map_song_fs(const DetachedSong &song);
+
/**
* Maps a file system path (relative to the music directory or
* absolute) to a relative path in UTF-8 encoding.
diff --git a/src/MemorySongEnumerator.cxx b/src/MemorySongEnumerator.cxx
index 7c9d05daa..3bb17083c 100644
--- a/src/MemorySongEnumerator.cxx
+++ b/src/MemorySongEnumerator.cxx
@@ -20,13 +20,13 @@
#include "config.h"
#include "MemorySongEnumerator.hxx"
-Song *
+DetachedSong *
MemorySongEnumerator::NextSong()
{
if (songs.empty())
return nullptr;
- auto result = songs.front().Steal();
+ auto result = new DetachedSong(std::move(songs.front()));
songs.pop_front();
return result;
}
diff --git a/src/MemorySongEnumerator.hxx b/src/MemorySongEnumerator.hxx
index 46086a064..085e16bc6 100644
--- a/src/MemorySongEnumerator.hxx
+++ b/src/MemorySongEnumerator.hxx
@@ -21,18 +21,18 @@
#define MPD_MEMORY_PLAYLIST_PROVIDER_HXX
#include "SongEnumerator.hxx"
-#include "SongPointer.hxx"
+#include "DetachedSong.hxx"
#include <forward_list>
class MemorySongEnumerator final : public SongEnumerator {
- std::forward_list<SongPointer> songs;
+ std::forward_list<DetachedSong> songs;
public:
- MemorySongEnumerator(std::forward_list<SongPointer> &&_songs)
+ MemorySongEnumerator(std::forward_list<DetachedSong> &&_songs)
:songs(std::move(_songs)) {}
- virtual Song *NextSong() override;
+ virtual DetachedSong *NextSong() override;
};
#endif
diff --git a/src/Partition.cxx b/src/Partition.cxx
index 55750cfad..9c30ce0a7 100644
--- a/src/Partition.cxx
+++ b/src/Partition.cxx
@@ -19,7 +19,7 @@
#include "config.h"
#include "Partition.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
void
Partition::DatabaseModified()
@@ -30,10 +30,10 @@ Partition::DatabaseModified()
void
Partition::TagModified()
{
- Song *song = pc.LockReadTaggedSong();
+ DetachedSong *song = pc.LockReadTaggedSong();
if (song != nullptr) {
playlist.TagModified(std::move(*song));
- song->Free();
+ delete song;
}
}
diff --git a/src/Partition.hxx b/src/Partition.hxx
index 512ba3bca..69c6f0175 100644
--- a/src/Partition.hxx
+++ b/src/Partition.hxx
@@ -76,8 +76,8 @@ struct Partition {
return playlist.DeleteRange(pc, start, end);
}
- void DeleteSong(const Song &song) {
- playlist.DeleteSong(pc, song);
+ void DeleteSong(const char *uri) {
+ playlist.DeleteSong(pc, uri);
}
void Shuffle(unsigned start, unsigned end) {
diff --git a/src/PlayerControl.cxx b/src/PlayerControl.cxx
index 74b4673bc..baaf7bc3b 100644
--- a/src/PlayerControl.cxx
+++ b/src/PlayerControl.cxx
@@ -20,7 +20,7 @@
#include "config.h"
#include "PlayerControl.hxx"
#include "Idle.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include <algorithm>
@@ -42,15 +42,12 @@ PlayerControl::PlayerControl(unsigned _buffer_chunks,
PlayerControl::~PlayerControl()
{
- if (next_song != nullptr)
- next_song->Free();
-
- if (tagged_song != nullptr)
- tagged_song->Free();
+ delete next_song;
+ delete tagged_song;
}
void
-PlayerControl::Play(Song *song)
+PlayerControl::Play(DetachedSong *song)
{
assert(song != nullptr);
@@ -195,26 +192,23 @@ PlayerControl::ClearError()
}
void
-PlayerControl::LockSetTaggedSong(const Song &song)
+PlayerControl::LockSetTaggedSong(const DetachedSong &song)
{
Lock();
- if (tagged_song != nullptr)
- tagged_song->Free();
- tagged_song = song.DupDetached();
+ delete tagged_song;
+ tagged_song = new DetachedSong(song);
Unlock();
}
void
PlayerControl::ClearTaggedSong()
{
- if (tagged_song != nullptr) {
- tagged_song->Free();
- tagged_song = nullptr;
- }
+ delete tagged_song;
+ tagged_song = nullptr;
}
void
-PlayerControl::EnqueueSong(Song *song)
+PlayerControl::EnqueueSong(DetachedSong *song)
{
assert(song != nullptr);
@@ -224,15 +218,13 @@ PlayerControl::EnqueueSong(Song *song)
}
bool
-PlayerControl::Seek(Song *song, float seek_time)
+PlayerControl::Seek(DetachedSong *song, float seek_time)
{
assert(song != nullptr);
Lock();
- if (next_song != nullptr)
- next_song->Free();
-
+ delete next_song;
next_song = song;
seek_where = seek_time;
SynchronousCommand(PlayerCommand::SEEK);
diff --git a/src/PlayerControl.hxx b/src/PlayerControl.hxx
index 61bb408d2..6919d2cb1 100644
--- a/src/PlayerControl.hxx
+++ b/src/PlayerControl.hxx
@@ -29,7 +29,7 @@
#include <stdint.h>
-struct Song;
+class DetachedSong;
enum class PlayerState : uint8_t {
STOP,
@@ -131,16 +131,16 @@ struct PlayerControl {
Error error;
/**
- * A copy of the current #Song after its tags have been
- * updated by the decoder (for example, a radio stream that
- * has sent a new tag after switching to the next song). This
- * shall be used by the GlobalEvents::TAG handler to update
- * the current #Song in the queue.
+ * A copy of the current #DetachedSong after its tags have
+ * been updated by the decoder (for example, a radio stream
+ * that has sent a new tag after switching to the next song).
+ * This shall be used by the GlobalEvents::TAG handler to
+ * update the current #DetachedSong in the queue.
*
* Protected by #mutex. Set by the PlayerThread and consumed
* by the main thread.
*/
- Song *tagged_song;
+ DetachedSong *tagged_song;
uint16_t bit_rate;
AudioFormat audio_format;
@@ -153,7 +153,7 @@ struct PlayerControl {
* This is a duplicate, and must be freed when this attribute
* is cleared.
*/
- Song *next_song;
+ DetachedSong *next_song;
double seek_where;
@@ -299,7 +299,7 @@ public:
* @param song the song to be queued; the given instance will
* be owned and freed by the player
*/
- void Play(Song *song);
+ void Play(DetachedSong *song);
/**
* see PlayerCommand::CANCEL
@@ -371,9 +371,9 @@ public:
/**
* Set the #tagged_song attribute to a newly allocated copy of
- * the given #Song. Locks and unlocks the object.
+ * the given #DetachedSong. Locks and unlocks the object.
*/
- void LockSetTaggedSong(const Song &song);
+ void LockSetTaggedSong(const DetachedSong &song);
void ClearTaggedSong();
@@ -382,8 +382,8 @@ public:
*
* Caller must lock the object.
*/
- Song *ReadTaggedSong() {
- Song *result = tagged_song;
+ DetachedSong *ReadTaggedSong() {
+ DetachedSong *result = tagged_song;
tagged_song = nullptr;
return result;
}
@@ -391,9 +391,9 @@ public:
/**
* Like ReadTaggedSong(), but locks and unlocks the object.
*/
- Song *LockReadTaggedSong() {
+ DetachedSong *LockReadTaggedSong() {
Lock();
- Song *result = ReadTaggedSong();
+ DetachedSong *result = ReadTaggedSong();
Unlock();
return result;
}
@@ -403,7 +403,7 @@ public:
void UpdateAudio();
private:
- void EnqueueSongLocked(Song *song) {
+ void EnqueueSongLocked(DetachedSong *song) {
assert(song != nullptr);
assert(next_song == nullptr);
@@ -416,7 +416,7 @@ public:
* @param song the song to be queued; the given instance will be owned
* and freed by the player
*/
- void EnqueueSong(Song *song);
+ void EnqueueSong(DetachedSong *song);
/**
* Makes the player thread seek the specified song to a position.
@@ -426,7 +426,7 @@ public:
* @return true on success, false on failure (e.g. if MPD isn't
* playing currently)
*/
- bool Seek(Song *song, float seek_time);
+ bool Seek(DetachedSong *song, float seek_time);
void SetCrossFade(float cross_fade_seconds);
diff --git a/src/PlayerThread.cxx b/src/PlayerThread.cxx
index 84d248d7b..814269b50 100644
--- a/src/PlayerThread.cxx
+++ b/src/PlayerThread.cxx
@@ -24,7 +24,7 @@
#include "MusicPipe.hxx"
#include "MusicBuffer.hxx"
#include "MusicChunk.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "system/FatalError.hxx"
#include "CrossFade.hxx"
#include "PlayerControl.hxx"
@@ -92,7 +92,7 @@ class Player {
/**
* the song currently being played
*/
- Song *song;
+ DetachedSong *song;
/**
* is cross fading enabled?
@@ -290,12 +290,12 @@ Player::StartDecoder(MusicPipe &_pipe)
assert(queued || pc.command == PlayerCommand::SEEK);
assert(pc.next_song != nullptr);
- unsigned start_ms = pc.next_song->start_ms;
+ unsigned start_ms = pc.next_song->GetStartMS();
if (pc.command == PlayerCommand::SEEK)
start_ms += (unsigned)(pc.seek_where * 1000);
- dc.Start(pc.next_song->DupDetached(),
- start_ms, pc.next_song->end_ms,
+ dc.Start(new DetachedSong(*pc.next_song),
+ start_ms, pc.next_song->GetEndMS(),
buffer, _pipe);
}
@@ -329,7 +329,7 @@ Player::WaitForDecoder()
if (error.IsDefined()) {
pc.SetError(PlayerError::DECODER, std::move(error));
- pc.next_song->Free();
+ delete pc.next_song;
pc.next_song = nullptr;
pc.Unlock();
@@ -339,9 +339,7 @@ Player::WaitForDecoder()
pc.ClearTaggedSong();
- if (song != nullptr)
- song->Free();
-
+ delete song;
song = pc.next_song;
elapsed_time = 0.0;
@@ -370,17 +368,20 @@ Player::WaitForDecoder()
* indicated by the decoder plugin.
*/
static double
-real_song_duration(const Song &song, double decoder_duration)
+real_song_duration(const DetachedSong &song, double decoder_duration)
{
if (decoder_duration <= 0.0)
/* the decoder plugin didn't provide information; fall
back to Song::GetDuration() */
return song.GetDuration();
- if (song.end_ms > 0 && song.end_ms / 1000.0 < decoder_duration)
- return (song.end_ms - song.start_ms) / 1000.0;
+ const unsigned start_ms = song.GetStartMS();
+ const unsigned end_ms = song.GetEndMS();
+
+ if (end_ms > 0 && end_ms / 1000.0 < decoder_duration)
+ return (end_ms - start_ms) / 1000.0;
- return decoder_duration - song.start_ms / 1000.0;
+ return decoder_duration - start_ms / 1000.0;
}
bool
@@ -458,10 +459,10 @@ Player::CheckDecoderStartup()
decoder_starting = false;
if (!paused && !OpenOutput()) {
- const auto uri = dc.song->GetURI();
FormatError(player_domain,
"problems opening audio device "
- "while playing \"%s\"", uri.c_str());
+ "while playing \"%s\"",
+ dc.song->GetURI());
return true;
}
@@ -516,7 +517,7 @@ Player::SeekDecoder()
{
assert(pc.next_song != nullptr);
- const unsigned start_ms = pc.next_song->start_ms;
+ const unsigned start_ms = pc.next_song->GetStartMS();
if (!dc.LockIsCurrentSong(*pc.next_song)) {
/* the decoder is already decoding the "next" song -
@@ -542,7 +543,7 @@ Player::SeekDecoder()
ClearAndReplacePipe(dc.pipe);
}
- pc.next_song->Free();
+ delete pc.next_song;
pc.next_song = nullptr;
queued = false;
}
@@ -658,7 +659,7 @@ Player::ProcessCommand()
pc.Lock();
}
- pc.next_song->Free();
+ delete pc.next_song;
pc.next_song = nullptr;
queued = false;
pc.CommandFinished();
@@ -681,17 +682,14 @@ Player::ProcessCommand()
}
static void
-update_song_tag(PlayerControl &pc, Song &song, const Tag &new_tag)
+update_song_tag(PlayerControl &pc, DetachedSong &song, const Tag &new_tag)
{
if (song.IsFile())
/* don't update tags of local files, only remote
streams may change tags dynamically */
return;
- Tag *old_tag = song.tag;
- song.tag = new Tag(new_tag);
-
- delete old_tag;
+ song.SetTag(new_tag);
pc.LockSetTaggedSong(song);
@@ -713,7 +711,7 @@ update_song_tag(PlayerControl &pc, Song &song, const Tag &new_tag)
*/
static bool
play_chunk(PlayerControl &pc,
- Song &song, struct music_chunk *chunk,
+ DetachedSong &song, struct music_chunk *chunk,
MusicBuffer &buffer,
const AudioFormat format,
Error &error)
@@ -880,10 +878,7 @@ Player::SongBorder()
{
xfade_state = CrossFadeState::UNKNOWN;
- {
- const auto uri = song->GetURI();
- FormatDefault(player_domain, "played \"%s\"", uri.c_str());
- }
+ FormatDefault(player_domain, "played \"%s\"", song->GetURI());
ReplacePipe(dc.pipe);
@@ -1079,9 +1074,8 @@ Player::Run()
delete cross_fade_tag;
if (song != nullptr) {
- const auto uri = song->GetURI();
- FormatDefault(player_domain, "played \"%s\"", uri.c_str());
- song->Free();
+ FormatDefault(player_domain, "played \"%s\"", song->GetURI());
+ delete song;
}
pc.Lock();
@@ -1090,7 +1084,7 @@ Player::Run()
if (queued) {
assert(pc.next_song != nullptr);
- pc.next_song->Free();
+ delete pc.next_song;
pc.next_song = nullptr;
}
@@ -1139,10 +1133,8 @@ player_task(void *arg)
/* fall through */
case PlayerCommand::PAUSE:
- if (pc.next_song != nullptr) {
- pc.next_song->Free();
- pc.next_song = nullptr;
- }
+ delete pc.next_song;
+ pc.next_song = nullptr;
pc.CommandFinished();
break;
@@ -1177,10 +1169,8 @@ player_task(void *arg)
return;
case PlayerCommand::CANCEL:
- if (pc.next_song != nullptr) {
- pc.next_song->Free();
- pc.next_song = nullptr;
- }
+ delete pc.next_song;
+ pc.next_song = nullptr;
pc.CommandFinished();
break;
diff --git a/src/Playlist.cxx b/src/Playlist.cxx
index f566527ac..0720e490d 100644
--- a/src/Playlist.cxx
+++ b/src/Playlist.cxx
@@ -21,23 +21,23 @@
#include "Playlist.hxx"
#include "PlaylistError.hxx"
#include "PlayerControl.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "Idle.hxx"
#include "Log.hxx"
#include <assert.h>
void
-playlist::TagModified(Song &&song)
+playlist::TagModified(DetachedSong &&song)
{
- if (!playing || song.tag == nullptr)
+ if (!playing)
return;
assert(current >= 0);
- Song &current_song = queue.GetOrder(current);
- if (SongEquals(song, current_song))
- current_song.ReplaceTag(std::move(*song.tag));
+ DetachedSong &current_song = queue.GetOrder(current);
+ if (song.IsSame(current_song))
+ current_song.MoveTagFrom(std::move(song));
queue.ModifyAtOrder(current);
queue.IncrementVersion();
@@ -55,15 +55,12 @@ playlist_queue_song_order(playlist &playlist, PlayerControl &pc,
playlist.queued = order;
- Song *song = playlist.queue.GetOrder(order).DupDetached();
+ const DetachedSong &song = playlist.queue.GetOrder(order);
- {
- const auto uri = song->GetURI();
- FormatDebug(playlist_domain, "queue song %i:\"%s\"",
- playlist.queued, uri.c_str());
- }
+ FormatDebug(playlist_domain, "queue song %i:\"%s\"",
+ playlist.queued, song.GetURI());
- pc.EnqueueSong(song);
+ pc.EnqueueSong(new DetachedSong(song));
}
/**
@@ -88,7 +85,7 @@ playlist_song_started(playlist &playlist, PlayerControl &pc)
idle_add(IDLE_PLAYER);
}
-const Song *
+const DetachedSong *
playlist::GetQueuedSong() const
{
return playing && queued >= 0
@@ -97,7 +94,7 @@ playlist::GetQueuedSong() const
}
void
-playlist::UpdateQueuedSong(PlayerControl &pc, const Song *prev)
+playlist::UpdateQueuedSong(PlayerControl &pc, const DetachedSong *prev)
{
if (!playing)
return;
@@ -124,7 +121,7 @@ playlist::UpdateQueuedSong(PlayerControl &pc, const Song *prev)
current = queue.PositionToOrder(current_position);
}
- const Song *const next_song = next_order >= 0
+ const DetachedSong *const next_song = next_order >= 0
? &queue.GetOrder(next_order)
: nullptr;
@@ -148,15 +145,11 @@ playlist::PlayOrder(PlayerControl &pc, int order)
playing = true;
queued = -1;
- Song *song = queue.GetOrder(order).DupDetached();
+ const DetachedSong &song = queue.GetOrder(order);
- {
- const auto uri = song->GetURI();
- FormatDebug(playlist_domain, "play %i:\"%s\"",
- order, uri.c_str());
- }
+ FormatDebug(playlist_domain, "play %i:\"%s\"", order, song.GetURI());
- pc.Play(song);
+ pc.Play(new DetachedSong(song));
current = order;
}
@@ -173,7 +166,7 @@ playlist::SyncWithPlayer(PlayerControl &pc)
pc.Lock();
const PlayerState pc_state = pc.GetState();
- const Song *pc_next_song = pc.next_song;
+ const DetachedSong *pc_next_song = pc.next_song;
pc.Unlock();
if (pc_state == PlayerState::STOP)
@@ -286,7 +279,7 @@ playlist::SetRandom(PlayerControl &pc, bool status)
if (status == queue.random)
return;
- const Song *const queued_song = GetQueuedSong();
+ const DetachedSong *const queued_song = GetQueuedSong();
queue.random = status;
diff --git a/src/Playlist.hxx b/src/Playlist.hxx
index 2116abe7c..e578bfcee 100644
--- a/src/Playlist.hxx
+++ b/src/Playlist.hxx
@@ -25,7 +25,7 @@
enum TagType : uint8_t;
struct PlayerControl;
-struct Song;
+class DetachedSong;
class Error;
struct playlist {
@@ -100,7 +100,7 @@ struct playlist {
* none if there is none (yet?) or if MPD isn't playing.
*/
gcc_pure
- const Song *GetQueuedSong() const;
+ const DetachedSong *GetQueuedSong() const;
/**
* This is the "PLAYLIST" event handler. It is invoked by the
@@ -125,7 +125,7 @@ protected:
* @param prev the song which was previously queued, as
* determined by playlist_get_queued_song()
*/
- void UpdateQueuedSong(PlayerControl &pc, const Song *prev);
+ void UpdateQueuedSong(PlayerControl &pc, const DetachedSong *prev);
public:
void Clear(PlayerControl &pc);
@@ -135,7 +135,7 @@ public:
* thread. Apply the given song's tag to the current song if
* the song matches.
*/
- void TagModified(Song &&song);
+ void TagModified(DetachedSong &&song);
/**
* The database has been modified. Pull all updates.
@@ -143,7 +143,7 @@ public:
void DatabaseModified();
PlaylistResult AppendSong(PlayerControl &pc,
- Song *song,
+ DetachedSong &&song,
unsigned *added_id=nullptr);
/**
@@ -162,7 +162,7 @@ public:
protected:
void DeleteInternal(PlayerControl &pc,
- unsigned song, const Song **queued_p);
+ unsigned song, const DetachedSong **queued_p);
public:
PlaylistResult DeletePosition(PlayerControl &pc,
@@ -184,7 +184,7 @@ public:
PlaylistResult DeleteRange(PlayerControl &pc,
unsigned start, unsigned end);
- void DeleteSong(PlayerControl &pc, const Song &song);
+ void DeleteSong(PlayerControl &pc, const char *uri);
void Shuffle(PlayerControl &pc, unsigned start, unsigned end);
diff --git a/src/PlaylistControl.cxx b/src/PlaylistControl.cxx
index 2dbd75d6e..9ba7f7485 100644
--- a/src/PlaylistControl.cxx
+++ b/src/PlaylistControl.cxx
@@ -26,7 +26,7 @@
#include "Playlist.hxx"
#include "PlaylistError.hxx"
#include "PlayerControl.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "Log.hxx"
void
@@ -195,7 +195,7 @@ playlist::SeekSongPosition(PlayerControl &pc, unsigned song, float seek_time)
if (!queue.IsValidPosition(song))
return PlaylistResult::BAD_RANGE;
- const Song *queued_song = GetQueuedSong();
+ const DetachedSong *queued_song = GetQueuedSong();
unsigned i = queue.random
? queue.PositionToOrder(song)
@@ -215,8 +215,7 @@ playlist::SeekSongPosition(PlayerControl &pc, unsigned song, float seek_time)
queued_song = nullptr;
}
- Song *the_song = queue.GetOrder(i).DupDetached();
- if (!pc.Seek(the_song, seek_time)) {
+ if (!pc.Seek(new DetachedSong(queue.GetOrder(i)), seek_time)) {
UpdateQueuedSong(pc, queued_song);
return PlaylistResult::NOT_PLAYING;
diff --git a/src/PlaylistEdit.cxx b/src/PlaylistEdit.cxx
index 668612a1a..a55f9b151 100644
--- a/src/PlaylistEdit.cxx
+++ b/src/PlaylistEdit.cxx
@@ -30,6 +30,7 @@
#include "util/UriUtil.hxx"
#include "util/Error.hxx"
#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "Idle.hxx"
#include "DatabaseGlue.hxx"
#include "DatabasePlugin.hxx"
@@ -64,23 +65,23 @@ playlist::AppendFile(PlayerControl &pc,
if (song == nullptr)
return PlaylistResult::NO_SUCH_SONG;
- const auto result = AppendSong(pc, song, added_id);
+ const auto result = AppendSong(pc, DetachedSong(*song), added_id);
song->Free();
return result;
}
PlaylistResult
playlist::AppendSong(PlayerControl &pc,
- Song *song, unsigned *added_id)
+ DetachedSong &&song, unsigned *added_id)
{
unsigned id;
if (queue.IsFull())
return PlaylistResult::TOO_LARGE;
- const Song *const queued_song = GetQueuedSong();
+ const DetachedSong *const queued_song = GetQueuedSong();
- id = queue.Append(song, 0);
+ id = queue.Append(std::move(song), 0);
if (queue.random) {
/* shuffle the new song into the list of remaining
@@ -110,25 +111,24 @@ playlist::AppendURI(PlayerControl &pc,
{
FormatDebug(playlist_domain, "add to playlist: %s", uri);
- const Database *db = nullptr;
- Song *song;
+ DetachedSong *song;
if (uri_has_scheme(uri)) {
- song = Song::NewRemote(uri);
+ song = new DetachedSong(uri);
} else {
- db = GetDatabase();
+ const Database *db = GetDatabase();
if (db == nullptr)
return PlaylistResult::NO_SUCH_SONG;
- song = db->GetSong(uri, IgnoreError());
- if (song == nullptr)
+ Song *tmp = db->GetSong(uri, IgnoreError());
+ if (tmp == nullptr)
return PlaylistResult::NO_SUCH_SONG;
+
+ song = new DetachedSong(*tmp);
+ db->ReturnSong(tmp);
}
- PlaylistResult result = AppendSong(pc, song, added_id);
- if (db != nullptr)
- db->ReturnSong(song);
- else
- song->Free();
+ PlaylistResult result = AppendSong(pc, std::move(*song), added_id);
+ delete song;
return result;
}
@@ -139,7 +139,7 @@ playlist::SwapPositions(PlayerControl &pc, unsigned song1, unsigned song2)
if (!queue.IsValidPosition(song1) || !queue.IsValidPosition(song2))
return PlaylistResult::BAD_RANGE;
- const Song *const queued_song = GetQueuedSong();
+ const DetachedSong *const queued_song = GetQueuedSong();
queue.SwapPositions(song1, song2);
@@ -193,7 +193,7 @@ playlist::SetPriorityRange(PlayerControl &pc,
/* remember "current" and "queued" */
const int current_position = GetCurrentPosition();
- const Song *const queued_song = GetQueuedSong();
+ const DetachedSong *const queued_song = GetQueuedSong();
/* apply the priority changes */
@@ -225,7 +225,7 @@ playlist::SetPriorityId(PlayerControl &pc,
void
playlist::DeleteInternal(PlayerControl &pc,
- unsigned song, const Song **queued_p)
+ unsigned song, const DetachedSong **queued_p)
{
assert(song < GetLength());
@@ -275,7 +275,7 @@ playlist::DeletePosition(PlayerControl &pc, unsigned song)
if (song >= queue.GetLength())
return PlaylistResult::BAD_RANGE;
- const Song *queued_song = GetQueuedSong();
+ const DetachedSong *queued_song = GetQueuedSong();
DeleteInternal(pc, song, &queued_song);
@@ -297,7 +297,7 @@ playlist::DeleteRange(PlayerControl &pc, unsigned start, unsigned end)
if (start >= end)
return PlaylistResult::SUCCESS;
- const Song *queued_song = GetQueuedSong();
+ const DetachedSong *queued_song = GetQueuedSong();
do {
DeleteInternal(pc, --end, &queued_song);
@@ -320,10 +320,10 @@ playlist::DeleteId(PlayerControl &pc, unsigned id)
}
void
-playlist::DeleteSong(PlayerControl &pc, const struct Song &song)
+playlist::DeleteSong(PlayerControl &pc, const char *uri)
{
for (int i = queue.GetLength() - 1; i >= 0; --i)
- if (SongEquals(song, queue.Get(i)))
+ if (queue.Get(i).IsURI(uri))
DeletePosition(pc, i);
}
@@ -341,7 +341,7 @@ playlist::MoveRange(PlayerControl &pc, unsigned start, unsigned end, int to)
/* nothing happens */
return PlaylistResult::SUCCESS;
- const Song *const queued_song = GetQueuedSong();
+ const DetachedSong *const queued_song = GetQueuedSong();
/*
* (to < 0) => move to offset from current song
@@ -401,7 +401,7 @@ playlist::Shuffle(PlayerControl &pc, unsigned start, unsigned end)
/* needs at least two entries. */
return;
- const Song *const queued_song = GetQueuedSong();
+ const DetachedSong *const queued_song = GetQueuedSong();
if (playing && current >= 0) {
unsigned current_position = queue.OrderToPosition(current);
diff --git a/src/PlaylistFile.cxx b/src/PlaylistFile.cxx
index 3f9845166..0f84cc4eb 100644
--- a/src/PlaylistFile.cxx
+++ b/src/PlaylistFile.cxx
@@ -24,7 +24,7 @@
#include "PlaylistVector.hxx"
#include "DatabasePlugin.hxx"
#include "DatabaseGlue.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "Mapper.hxx"
#include "fs/TextFile.hxx"
#include "ConfigGlobal.hxx"
@@ -361,7 +361,7 @@ spl_remove_index(const char *utf8path, unsigned pos, Error &error)
}
bool
-spl_append_song(const char *utf8path, const Song &song, Error &error)
+spl_append_song(const char *utf8path, const DetachedSong &song, Error &error)
{
if (spl_map(error).IsNull())
return false;
@@ -402,22 +402,21 @@ bool
spl_append_uri(const char *url, const char *utf8file, Error &error)
{
if (uri_has_scheme(url)) {
- Song *song = Song::NewRemote(url);
- bool success = spl_append_song(utf8file, *song, error);
- song->Free();
- return success;
+ return spl_append_song(utf8file, DetachedSong(url),
+ error);
} else {
const Database *db = GetDatabase(error);
if (db == nullptr)
return false;
- Song *song = db->GetSong(url, error);
- if (song == nullptr)
+ Song *tmp = db->GetSong(url, error);
+ if (tmp == nullptr)
return false;
- bool success = spl_append_song(utf8file, *song, error);
- db->ReturnSong(song);
- return success;
+ const DetachedSong song(*tmp);
+ db->ReturnSong(tmp);
+
+ return spl_append_song(utf8file, song, error);
}
}
diff --git a/src/PlaylistFile.hxx b/src/PlaylistFile.hxx
index 92030b1eb..33fe00eb7 100644
--- a/src/PlaylistFile.hxx
+++ b/src/PlaylistFile.hxx
@@ -23,7 +23,7 @@
#include <vector>
#include <string>
-struct Song;
+class DetachedSong;
class PlaylistVector;
class Error;
@@ -68,7 +68,7 @@ bool
spl_remove_index(const char *utf8path, unsigned pos, Error &error);
bool
-spl_append_song(const char *utf8path, const Song &song, Error &error);
+spl_append_song(const char *utf8path, const DetachedSong &song, Error &error);
bool
spl_append_uri(const char *file, const char *utf8file, Error &error);
diff --git a/src/PlaylistPrint.cxx b/src/PlaylistPrint.cxx
index e92d6e18a..cee64f859 100644
--- a/src/PlaylistPrint.cxx
+++ b/src/PlaylistPrint.cxx
@@ -31,6 +31,7 @@
#include "Client.hxx"
#include "InputStream.hxx"
#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "fs/Traits.hxx"
#include "util/Error.hxx"
#include "thread/Cond.hxx"
@@ -151,7 +152,7 @@ playlist_provider_print(Client &client, const char *uri,
? PathTraitsUTF8::GetParent(uri)
: std::string(".");
- Song *song;
+ DetachedSong *song;
while ((song = e.NextSong()) != nullptr) {
song = playlist_check_translate_song(song, base_uri.c_str(),
false);
@@ -163,7 +164,7 @@ playlist_provider_print(Client &client, const char *uri,
else
song_print_uri(client, *song);
- song->Free();
+ delete song;
}
}
diff --git a/src/PlaylistQueue.cxx b/src/PlaylistQueue.cxx
index 3a7660134..d2dce977a 100644
--- a/src/PlaylistQueue.cxx
+++ b/src/PlaylistQueue.cxx
@@ -24,7 +24,7 @@
#include "Playlist.hxx"
#include "InputStream.hxx"
#include "SongEnumerator.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "thread/Cond.hxx"
#include "fs/Traits.hxx"
@@ -38,13 +38,13 @@ playlist_load_into_queue(const char *uri, SongEnumerator &e,
? PathTraitsUTF8::GetParent(uri)
: std::string(".");
- Song *song;
+ DetachedSong *song;
for (unsigned i = 0;
i < end_index && (song = e.NextSong()) != nullptr;
++i) {
if (i < start_index) {
/* skip songs before the start index */
- song->Free();
+ delete song;
continue;
}
@@ -53,8 +53,8 @@ playlist_load_into_queue(const char *uri, SongEnumerator &e,
if (song == nullptr)
continue;
- PlaylistResult result = dest.AppendSong(pc, song);
- song->Free();
+ PlaylistResult result = dest.AppendSong(pc, std::move(*song));
+ delete song;
if (result != PlaylistResult::SUCCESS)
return result;
}
diff --git a/src/PlaylistSave.cxx b/src/PlaylistSave.cxx
index 02195bd15..3bc66d16e 100644
--- a/src/PlaylistSave.cxx
+++ b/src/PlaylistSave.cxx
@@ -23,6 +23,7 @@
#include "PlaylistError.hxx"
#include "Playlist.hxx"
#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "Mapper.hxx"
#include "Idle.hxx"
#include "fs/AllocatedPath.hxx"
@@ -36,7 +37,7 @@
#include <string.h>
void
-playlist_print_song(FILE *file, const Song &song)
+playlist_print_song(FILE *file, const DetachedSong &song)
{
if (playlist_saveAbsolutePaths && song.IsInDatabase()) {
const auto path = map_song_fs(song);
@@ -44,7 +45,7 @@ playlist_print_song(FILE *file, const Song &song)
fprintf(file, "%s\n", path.c_str());
} else {
const auto uri_utf8 = song.GetURI();
- const auto uri_fs = AllocatedPath::FromUTF8(uri_utf8.c_str());
+ const auto uri_fs = AllocatedPath::FromUTF8(uri_utf8);
if (!uri_fs.IsNull())
fprintf(file, "%s\n", uri_fs.c_str());
diff --git a/src/PlaylistSave.hxx b/src/PlaylistSave.hxx
index 70b40f3fa..d9126aed2 100644
--- a/src/PlaylistSave.hxx
+++ b/src/PlaylistSave.hxx
@@ -24,14 +24,14 @@
#include <stdio.h>
-struct Song;
struct queue;
struct playlist;
struct PlayerControl;
+class DetachedSong;
class Error;
void
-playlist_print_song(FILE *fp, const Song &song);
+playlist_print_song(FILE *file, const DetachedSong &song);
void
playlist_print_uri(FILE *fp, const char *uri);
diff --git a/src/PlaylistSong.cxx b/src/PlaylistSong.cxx
index f016e46b6..2da644bc2 100644
--- a/src/PlaylistSong.cxx
+++ b/src/PlaylistSong.cxx
@@ -24,41 +24,42 @@
#include "DatabaseGlue.hxx"
#include "ls.hxx"
#include "tag/Tag.hxx"
+#include "tag/TagBuilder.hxx"
#include "fs/AllocatedPath.hxx"
#include "fs/Traits.hxx"
#include "util/UriUtil.hxx"
#include "util/Error.hxx"
+#include "DetachedSong.hxx"
#include "Song.hxx"
#include <assert.h>
#include <string.h>
static void
-merge_song_metadata(Song *dest, const Song *base,
- const Song *add)
+merge_song_metadata(DetachedSong *dest, const DetachedSong *base,
+ const DetachedSong *add)
{
- dest->tag = base->tag != nullptr
- ? (add->tag != nullptr
- ? Tag::Merge(*base->tag, *add->tag)
- : new Tag(*base->tag))
- : (add->tag != nullptr
- ? new Tag(*add->tag)
- : nullptr);
-
- dest->mtime = base->mtime;
- dest->start_ms = add->start_ms;
- dest->end_ms = add->end_ms;
+ {
+ TagBuilder builder(add->GetTag());
+ builder.Complement(base->GetTag());
+ dest->SetTag(builder.Commit());
+ }
+
+ dest->SetLastModified(base->GetLastModified());
+ dest->SetStartMS(add->GetStartMS());
+ dest->SetEndMS(add->GetEndMS());
}
-static Song *
-apply_song_metadata(Song *dest, const Song *src)
+static DetachedSong *
+apply_song_metadata(DetachedSong *dest, const DetachedSong *src)
{
- Song *tmp;
+ DetachedSong *tmp;
assert(dest != nullptr);
assert(src != nullptr);
- if (src->tag == nullptr && src->start_ms == 0 && src->end_ms == 0)
+ if (!src->GetTag().IsDefined() &&
+ src->GetStartMS() == 0 && src->GetEndMS() == 0)
return dest;
if (dest->IsInDatabase()) {
@@ -70,37 +71,41 @@ apply_song_metadata(Song *dest, const Song *src)
if (path_utf8.empty())
path_utf8 = path_fs.c_str();
- tmp = Song::NewFile(path_utf8.c_str(), nullptr);
+ tmp = new DetachedSong(std::move(path_utf8));
merge_song_metadata(tmp, dest, src);
} else {
- tmp = Song::NewFile(dest->uri, nullptr);
+ tmp = new DetachedSong(dest->GetURI());
merge_song_metadata(tmp, dest, src);
}
- if (dest->tag != nullptr && dest->tag->time > 0 &&
- src->start_ms > 0 && src->end_ms == 0 &&
- src->start_ms / 1000 < (unsigned)dest->tag->time)
+ if (dest->GetTag().IsDefined() && dest->GetTag().time > 0 &&
+ src->GetStartMS() > 0 && src->GetEndMS() == 0 &&
+ src->GetStartMS() / 1000 < (unsigned)dest->GetTag().time)
/* the range is open-ended, and the playlist plugin
did not know the total length of the song file
(e.g. last track on a CUE file); fix it up here */
- tmp->tag->time = dest->tag->time - src->start_ms / 1000;
+ tmp->WritableTag().time =
+ dest->GetTag().time - src->GetStartMS() / 1000;
- dest->Free();
+ delete dest;
return tmp;
}
-static Song *
-playlist_check_load_song(const Song *song, const char *uri, bool secure)
+static DetachedSong *
+playlist_check_load_song(const DetachedSong *song, const char *uri, bool secure)
{
- Song *dest;
+ DetachedSong *dest;
if (uri_has_scheme(uri)) {
- dest = Song::NewRemote(uri);
+ dest = new DetachedSong(uri);
} else if (PathTraitsUTF8::IsAbsolute(uri) && secure) {
- dest = Song::LoadFile(uri, nullptr);
- if (dest == nullptr)
+ Song *tmp = Song::LoadFile(uri, nullptr);
+ if (tmp == nullptr)
return nullptr;
+
+ dest = new DetachedSong(*tmp);
+ delete tmp;
} else {
const Database *db = GetDatabase();
if (db == nullptr)
@@ -111,22 +116,22 @@ playlist_check_load_song(const Song *song, const char *uri, bool secure)
/* not found in database */
return nullptr;
- dest = tmp->DupDetached();
+ dest = new DetachedSong(*tmp);
db->ReturnSong(tmp);
}
return apply_song_metadata(dest, song);
}
-Song *
-playlist_check_translate_song(Song *song, const char *base_uri,
+DetachedSong *
+playlist_check_translate_song(DetachedSong *song, const char *base_uri,
bool secure)
{
if (song->IsInDatabase())
/* already ok */
return song;
- const char *uri = song->uri;
+ const char *uri = song->GetURI();
if (uri_has_scheme(uri)) {
if (uri_supported_scheme(uri))
@@ -134,7 +139,7 @@ playlist_check_translate_song(Song *song, const char *base_uri,
return song;
else {
/* unsupported remote song */
- song->Free();
+ delete song;
return nullptr;
}
}
@@ -156,7 +161,7 @@ playlist_check_translate_song(Song *song, const char *base_uri,
else if (!secure) {
/* local files must be relative to the music
directory when "secure" is enabled */
- song->Free();
+ delete song;
return nullptr;
}
@@ -169,8 +174,8 @@ playlist_check_translate_song(Song *song, const char *base_uri,
uri = full_uri.c_str();
}
- Song *dest = playlist_check_load_song(song, uri, secure);
- song->Free();
+ DetachedSong *dest = playlist_check_load_song(song, uri, secure);
+ delete song;
return dest;
}
diff --git a/src/PlaylistSong.hxx b/src/PlaylistSong.hxx
index d0db99868..9cfb09b24 100644
--- a/src/PlaylistSong.hxx
+++ b/src/PlaylistSong.hxx
@@ -20,7 +20,7 @@
#ifndef MPD_PLAYLIST_SONG_HXX
#define MPD_PLAYLIST_SONG_HXX
-struct Song;
+class DetachedSong;
/**
* Verifies the song, returns nullptr if it is unsafe. Translate the
@@ -30,8 +30,8 @@ struct Song;
* @param secure if true, then local files are only allowed if they
* are relative to base_uri
*/
-Song *
-playlist_check_translate_song(Song *song, const char *base_uri,
+DetachedSong *
+playlist_check_translate_song(DetachedSong *song, const char *base_uri,
bool secure);
#endif
diff --git a/src/PlaylistTag.cxx b/src/PlaylistTag.cxx
index 2897f3252..672111ea5 100644
--- a/src/PlaylistTag.cxx
+++ b/src/PlaylistTag.cxx
@@ -26,7 +26,7 @@
#include "config.h"
#include "Playlist.hxx"
#include "PlaylistError.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "tag/Tag.hxx"
#include "tag/TagBuilder.hxx"
#include "util/Error.hxx"
@@ -42,22 +42,19 @@ playlist::AddSongIdTag(unsigned id, TagType tag_type, const char *value,
return false;
}
- Song &song = queue.Get(position);
+ DetachedSong &song = queue.Get(position);
if (song.IsFile()) {
error.Set(playlist_domain, int(PlaylistResult::DENIED),
"Cannot edit tags of local file");
return false;
}
- TagBuilder tag;
- if (song.tag != nullptr) {
- tag = std::move(*song.tag);
- delete song.tag;
+ {
+ TagBuilder tag(std::move(song.WritableTag()));
+ tag.AddItem(tag_type, value);
+ song.SetTag(tag.Commit());
}
- tag.AddItem(tag_type, value);
- song.tag = tag.CommitNew();
-
queue.ModifyAtPosition(position);
OnModified();
return true;
@@ -74,24 +71,21 @@ playlist::ClearSongIdTag(unsigned id, TagType tag_type,
return false;
}
- Song &song = queue.Get(position);
+ DetachedSong &song = queue.Get(position);
if (song.IsFile()) {
error.Set(playlist_domain, int(PlaylistResult::DENIED),
"Cannot edit tags of local file");
return false;
}
- if (song.tag == nullptr)
- return true;
-
- TagBuilder tag(std::move(*song.tag));
- delete song.tag;
-
- if (tag_type == TAG_NUM_OF_ITEM_TYPES)
- tag.RemoveAll();
- else
- tag.RemoveType(tag_type);
- song.tag = tag.CommitNew();
+ {
+ TagBuilder tag(std::move(song.WritableTag()));
+ if (tag_type == TAG_NUM_OF_ITEM_TYPES)
+ tag.RemoveAll();
+ else
+ tag.RemoveType(tag_type);
+ song.SetTag(tag.Commit());
+ }
queue.ModifyAtPosition(position);
OnModified();
diff --git a/src/PlaylistUpdate.cxx b/src/PlaylistUpdate.cxx
index 0e72ef671..91adc01b7 100644
--- a/src/PlaylistUpdate.cxx
+++ b/src/PlaylistUpdate.cxx
@@ -22,35 +22,36 @@
#include "DatabaseGlue.hxx"
#include "DatabasePlugin.hxx"
#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "tag/Tag.hxx"
#include "Idle.hxx"
#include "util/Error.hxx"
static bool
-UpdatePlaylistSong(const Database &db, Song &song)
+UpdatePlaylistSong(const Database &db, DetachedSong &song)
{
- if (!song.IsInDatabase() || !song.IsDetached())
+ if (!song.IsInDatabase())
/* only update Songs instances that are "detached"
from the Database */
return false;
- Song *original = db.GetSong(song.uri, IgnoreError());
+ Song *original = db.GetSong(song.GetURI(), IgnoreError());
if (original == nullptr)
/* not found - shouldn't happen, because the update
thread should ensure that all stale Song instances
have been purged */
return false;
- if (original->mtime == song.mtime) {
+ if (original->mtime == song.GetLastModified()) {
/* not modified */
db.ReturnSong(original);
return false;
}
- song.mtime = original->mtime;
+ song.SetLastModified(original->mtime);
if (original->tag != nullptr)
- song.ReplaceTag(Tag(*original->tag));
+ song.SetTag(*original->tag);
db.ReturnSong(original);
return true;
diff --git a/src/Queue.cxx b/src/Queue.cxx
index 92f92f77c..d4d6531f9 100644
--- a/src/Queue.cxx
+++ b/src/Queue.cxx
@@ -19,7 +19,7 @@
#include "config.h"
#include "Queue.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
queue::queue(unsigned _max_length)
:max_length(_max_length), length(0),
@@ -84,7 +84,7 @@ queue::ModifyAtOrder(unsigned _order)
}
unsigned
-queue::Append(Song *song, uint8_t priority)
+queue::Append(DetachedSong &&song, uint8_t priority)
{
assert(!IsFull());
@@ -92,7 +92,7 @@ queue::Append(Song *song, uint8_t priority)
const unsigned id = id_table.Insert(position);
auto &item = items[position];
- item.song = song->DupDetached();
+ item.song = new DetachedSong(std::move(song));
item.id = id;
item.version = version;
item.priority = priority;
@@ -219,11 +219,7 @@ queue::DeletePosition(unsigned position)
{
assert(position < length);
- {
- Song &song = Get(position);
- assert(!song.IsInDatabase() || song.IsDetached());
- song.Free();
- }
+ delete items[position].song;
const unsigned id = PositionToId(position);
const unsigned _order = PositionToOrder(position);
@@ -257,9 +253,7 @@ queue::Clear()
for (unsigned i = 0; i < length; i++) {
Item *item = &items[i];
- assert(!item->song->IsInDatabase() ||
- item->song->IsDetached());
- item->song->Free();
+ delete item->song;
id_table.Erase(item->id);
}
diff --git a/src/Queue.hxx b/src/Queue.hxx
index da90d4a3d..8f893dbfa 100644
--- a/src/Queue.hxx
+++ b/src/Queue.hxx
@@ -29,7 +29,7 @@
#include <assert.h>
#include <stdint.h>
-struct Song;
+class DetachedSong;
/**
* A queue of songs. This is the backend of the playlist: it contains
@@ -53,7 +53,7 @@ struct queue {
* information attached.
*/
struct Item {
- Song *song;
+ DetachedSong *song;
/** the unique id of this item in the queue */
unsigned id;
@@ -200,7 +200,7 @@ struct queue {
/**
* Returns the song at the specified position.
*/
- Song &Get(unsigned position) const {
+ DetachedSong &Get(unsigned position) const {
assert(position < length);
return *items[position].song;
@@ -209,7 +209,7 @@ struct queue {
/**
* Returns the song at the specified order number.
*/
- Song &GetOrder(unsigned _order) const {
+ DetachedSong &GetOrder(unsigned _order) const {
return Get(OrderToPosition(_order));
}
@@ -268,7 +268,7 @@ struct queue {
*
* @param priority the priority of this new queue item
*/
- unsigned Append(Song *song, uint8_t priority);
+ unsigned Append(DetachedSong &&song, uint8_t priority);
/**
* Swaps two songs, addressed by their position.
diff --git a/src/QueuePrint.cxx b/src/QueuePrint.cxx
index fec4a36fb..ab00331b0 100644
--- a/src/QueuePrint.cxx
+++ b/src/QueuePrint.cxx
@@ -94,7 +94,7 @@ queue_find(Client &client, const queue &queue,
const SongFilter &filter)
{
for (unsigned i = 0; i < queue.GetLength(); i++) {
- const Song &song = queue.Get(i);
+ const DetachedSong &song = queue.Get(i);
if (filter.Match(song))
queue_print_song_info(client, queue, i);
diff --git a/src/QueueSave.cxx b/src/QueueSave.cxx
index f3fc7c6d1..b84c051bb 100644
--- a/src/QueueSave.cxx
+++ b/src/QueueSave.cxx
@@ -21,7 +21,7 @@
#include "QueueSave.hxx"
#include "Queue.hxx"
#include "PlaylistError.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "SongSave.hxx"
#include "DatabasePlugin.hxx"
#include "DatabaseGlue.hxx"
@@ -37,20 +37,19 @@
#define PRIO_LABEL "Prio: "
static void
-queue_save_database_song(FILE *fp, int idx, const Song &song)
+queue_save_database_song(FILE *fp, int idx, const DetachedSong &song)
{
- const auto uri = song.GetURI();
- fprintf(fp, "%i:%s\n", idx, uri.c_str());
+ fprintf(fp, "%i:%s\n", idx, song.GetURI());
}
static void
-queue_save_full_song(FILE *fp, const Song &song)
+queue_save_full_song(FILE *fp, const DetachedSong &song)
{
song_save(fp, song);
}
static void
-queue_save_song(FILE *fp, int idx, const Song &song)
+queue_save_song(FILE *fp, int idx, const DetachedSong &song)
{
if (song.IsInDatabase())
queue_save_database_song(fp, idx, song);
@@ -85,8 +84,7 @@ queue_load_song(TextFile &file, const char *line, queue &queue)
return;
}
- const Database *db = nullptr;
- Song *song;
+ DetachedSong *song;
if (StringStartsWith(line, SONG_BEGIN)) {
const char *uri = line + sizeof(SONG_BEGIN) - 1;
@@ -94,7 +92,7 @@ queue_load_song(TextFile &file, const char *line, queue &queue)
return;
Error error;
- song = song_load(file, nullptr, uri, error);
+ song = song_load(file, uri, error);
if (song == nullptr) {
LogError(error);
return;
@@ -111,22 +109,21 @@ queue_load_song(TextFile &file, const char *line, queue &queue)
const char *uri = endptr + 1;
if (uri_has_scheme(uri)) {
- song = Song::NewRemote(uri);
+ song = new DetachedSong(uri);
} else {
- db = GetDatabase();
+ const Database *db = GetDatabase();
if (db == nullptr)
return;
- song = db->GetSong(uri, IgnoreError());
- if (song == nullptr)
+ Song *tmp = db->GetSong(uri, IgnoreError());
+ if (tmp == nullptr)
return;
+
+ song = new DetachedSong(*tmp);
+ db->ReturnSong(tmp);
}
}
- queue.Append(song, priority);
-
- if (db != nullptr)
- db->ReturnSong(song);
- else
- song->Free();
+ queue.Append(std::move(*song), priority);
+ delete song;
}
diff --git a/src/Song.cxx b/src/Song.cxx
index ae0c70ab9..bb74a6951 100644
--- a/src/Song.cxx
+++ b/src/Song.cxx
@@ -22,13 +22,12 @@
#include "Directory.hxx"
#include "tag/Tag.hxx"
#include "util/Alloc.hxx"
+#include "DetachedSong.hxx"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
-Directory detached_root;
-
static Song *
song_alloc(const char *uri, Directory *parent)
{
@@ -51,57 +50,22 @@ song_alloc(const char *uri, Directory *parent)
}
Song *
-Song::NewRemote(const char *uri)
+Song::NewFrom(DetachedSong &&other, Directory *parent)
{
- return song_alloc(uri, nullptr);
+ Song *song = song_alloc(other.GetURI(), parent);
+ song->tag = new Tag(std::move(other.WritableTag()));
+ song->mtime = other.GetLastModified();
+ song->start_ms = other.GetStartMS();
+ song->end_ms = other.GetEndMS();
+ return song;
}
Song *
Song::NewFile(const char *path, Directory *parent)
{
- assert((parent == nullptr) == (*path == '/'));
-
return song_alloc(path, parent);
}
-Song *
-Song::ReplaceURI(const char *new_uri)
-{
- Song *new_song = song_alloc(new_uri, parent);
- new_song->tag = tag;
- new_song->mtime = mtime;
- new_song->start_ms = start_ms;
- new_song->end_ms = end_ms;
- free(this);
- return new_song;
-}
-
-Song *
-Song::NewDetached(const char *uri)
-{
- assert(uri != nullptr);
-
- return song_alloc(uri, &detached_root);
-}
-
-Song *
-Song::DupDetached() const
-{
- Song *song;
- if (IsInDatabase()) {
- const auto new_uri = GetURI();
- song = NewDetached(new_uri.c_str());
- } else
- song = song_alloc(uri, nullptr);
-
- song->tag = tag != nullptr ? new Tag(*tag) : nullptr;
- song->mtime = mtime;
- song->start_ms = start_ms;
- song->end_ms = end_ms;
-
- return song;
-}
-
void
Song::Free()
{
@@ -109,54 +73,12 @@ Song::Free()
free(this);
}
-void
-Song::ReplaceTag(Tag &&_tag)
-{
- if (tag == nullptr)
- tag = new Tag();
- *tag = std::move(_tag);
-}
-
-gcc_pure
-static inline bool
-directory_equals(const Directory &a, const Directory &b)
-{
- return strcmp(a.path, b.path) == 0;
-}
-
-gcc_pure
-static inline bool
-directory_is_same(const Directory *a, const Directory *b)
-{
- return a == b ||
- (a != nullptr && b != nullptr &&
- directory_equals(*a, *b));
-
-}
-
-bool
-SongEquals(const Song &a, const Song &b)
-{
- if (a.parent != nullptr && b.parent != nullptr &&
- !directory_equals(*a.parent, *b.parent) &&
- (a.parent == &detached_root || b.parent == &detached_root)) {
- /* must compare the full URI if one of the objects is
- "detached" */
- const auto au = a.GetURI();
- const auto bu = b.GetURI();
- return au == bu;
- }
-
- return directory_is_same(a.parent, b.parent) &&
- strcmp(a.uri, b.uri) == 0;
-}
-
std::string
Song::GetURI() const
{
assert(*uri);
- if (!IsInDatabase() || parent->IsRoot())
+ if (parent == nullptr || parent->IsRoot())
return std::string(uri);
else {
const char *path = parent->GetPath();
diff --git a/src/Song.hxx b/src/Song.hxx
index 21e158560..c9719edd7 100644
--- a/src/Song.hxx
+++ b/src/Song.hxx
@@ -32,13 +32,12 @@
#define SONG_TIME "Time: "
struct Tag;
+struct Directory;
+class DetachedSong;
/**
- * A dummy #directory instance that is used for "detached" song
- * copies.
+ * A song file inside the configured music directory.
*/
-extern struct Directory detached_root;
-
struct Song {
/**
* Pointers to the siblings of this directory within the
@@ -51,7 +50,14 @@ struct Song {
struct list_head siblings;
Tag *tag;
+
+ /**
+ * The #Directory that contains this song. May be nullptr if
+ * the current database plugin does not manage the parent
+ * directory this way.
+ */
Directory *parent;
+
time_t mtime;
/**
@@ -65,11 +71,14 @@ struct Song {
*/
unsigned end_ms;
+ /**
+ * The file name. If #parent is nullptr, then this is the URI
+ * relative to the music directory.
+ */
char uri[sizeof(int)];
- /** allocate a new song with a remote URL */
gcc_malloc
- static Song *NewRemote(const char *uri);
+ static Song *NewFrom(DetachedSong &&other, Directory *parent);
/** allocate a new song with a local file name */
gcc_malloc
@@ -87,47 +96,8 @@ struct Song {
return LoadFile(path_utf8, &parent);
}
- /**
- * Replaces the URI of a song object. The given song object
- * is destroyed, and a newly allocated one is returned. It
- * does not update the reference within the parent directory;
- * the caller is responsible for doing that.
- */
- gcc_malloc
- Song *ReplaceURI(const char *uri);
-
- /**
- * Creates a "detached" song object.
- */
- gcc_malloc
- static Song *NewDetached(const char *uri);
-
- /**
- * Creates a duplicate of the song object. If the object is
- * in the database, it creates a "detached" copy of this song,
- * see Song::IsDetached().
- */
- gcc_malloc
- Song *DupDetached() const;
-
void Free();
- bool IsInDatabase() const {
- return parent != nullptr;
- }
-
- bool IsFile() const {
- return IsInDatabase() || uri[0] == '/';
- }
-
- bool IsDetached() const {
- assert(IsInDatabase());
-
- return parent == &detached_root;
- }
-
- void ReplaceTag(Tag &&tag);
-
bool UpdateFile();
bool UpdateFileInArchive();
@@ -142,11 +112,4 @@ struct Song {
double GetDuration() const;
};
-/**
- * Returns true if both objects refer to the same physical song.
- */
-gcc_pure
-bool
-SongEquals(const Song &a, const Song &b);
-
#endif
diff --git a/src/SongEnumerator.hxx b/src/SongEnumerator.hxx
index 0e268a31a..2515647d1 100644
--- a/src/SongEnumerator.hxx
+++ b/src/SongEnumerator.hxx
@@ -20,7 +20,7 @@
#ifndef MPD_SONG_ENUMERATOR_HXX
#define MPD_SONG_ENUMERATOR_HXX
-struct Song;
+class DetachedSong;
/**
* An object which provides serial access to a number of #Song
@@ -35,7 +35,7 @@ public:
* freeing the returned #Song object. Returns nullptr if
* there are no more songs.
*/
- virtual Song *NextSong() = 0;
+ virtual DetachedSong *NextSong() = 0;
};
#endif
diff --git a/src/SongFilter.cxx b/src/SongFilter.cxx
index 8d90c5fc8..53ab784c2 100644
--- a/src/SongFilter.cxx
+++ b/src/SongFilter.cxx
@@ -20,6 +20,7 @@
#include "config.h"
#include "SongFilter.hxx"
#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "tag/Tag.hxx"
#include "util/ASCII.hxx"
#include "util/UriUtil.hxx"
@@ -151,6 +152,18 @@ SongFilter::Item::Match(const Song &song) const
return song.tag != NULL && Match(*song.tag);
}
+bool
+SongFilter::Item::Match(const DetachedSong &song) const
+{
+ if (tag == LOCATE_TAG_BASE_TYPE)
+ return uri_is_child_or_same(value.c_str(), song.GetURI());
+
+ if (tag == LOCATE_TAG_FILE_TYPE)
+ return StringMatch(song.GetURI());
+
+ return Match(song.GetTag());
+}
+
SongFilter::SongFilter(unsigned tag, const char *value, bool fold_case)
{
items.push_back(Item(tag, value, fold_case));
@@ -203,6 +216,16 @@ SongFilter::Match(const Song &song) const
return true;
}
+bool
+SongFilter::Match(const DetachedSong &song) const
+{
+ for (const auto &i : items)
+ if (!i.Match(song))
+ return false;
+
+ return true;
+}
+
std::string
SongFilter::GetBase() const
{
diff --git a/src/SongFilter.hxx b/src/SongFilter.hxx
index b15127c07..16970c350 100644
--- a/src/SongFilter.hxx
+++ b/src/SongFilter.hxx
@@ -38,6 +38,7 @@
struct Tag;
struct TagItem;
struct Song;
+class DetachedSong;
class SongFilter {
public:
@@ -80,6 +81,9 @@ public:
gcc_pure
bool Match(const Song &song) const;
+
+ gcc_pure
+ bool Match(const DetachedSong &song) const;
};
private:
@@ -105,6 +109,9 @@ public:
gcc_pure
bool Match(const Song &song) const;
+ gcc_pure
+ bool Match(const DetachedSong &song) const;
+
const std::list<Item> &GetItems() const {
return items;
}
diff --git a/src/SongPrint.cxx b/src/SongPrint.cxx
index ea164d02b..b3e0c7b58 100644
--- a/src/SongPrint.cxx
+++ b/src/SongPrint.cxx
@@ -20,6 +20,7 @@
#include "config.h"
#include "SongPrint.hxx"
#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "Directory.hxx"
#include "TimePrint.hxx"
#include "TagPrint.hxx"
@@ -27,21 +28,31 @@
#include "Client.hxx"
#include "util/UriUtil.hxx"
+static void
+song_print_uri(Client &client, const char *uri)
+{
+ const std::string allocated = uri_remove_auth(uri);
+ if (!allocated.empty())
+ uri = allocated.c_str();
+
+ client_printf(client, "%s%s\n", SONG_FILE,
+ map_to_relative_path(uri));
+}
+
void
song_print_uri(Client &client, const Song &song)
{
- if (song.IsInDatabase() && !song.parent->IsRoot()) {
+ if (song.parent != nullptr && !song.parent->IsRoot()) {
client_printf(client, "%s%s/%s\n", SONG_FILE,
song.parent->GetPath(), song.uri);
- } else {
- const char *uri = song.uri;
- const std::string allocated = uri_remove_auth(uri);
- if (!allocated.empty())
- uri = allocated.c_str();
+ } else
+ song_print_uri(client, song.uri);
+}
- client_printf(client, "%s%s\n", SONG_FILE,
- map_to_relative_path(uri));
- }
+void
+song_print_uri(Client &client, const DetachedSong &song)
+{
+ song_print_uri(client, song.GetURI());
}
void
@@ -66,3 +77,28 @@ song_print_info(Client &client, const Song &song)
if (song.tag != nullptr)
tag_print(client, *song.tag);
}
+
+void
+song_print_info(Client &client, const DetachedSong &song)
+{
+ song_print_uri(client, song);
+
+ const unsigned start_ms = song.GetStartMS();
+ const unsigned end_ms = song.GetEndMS();
+
+ if (end_ms > 0)
+ client_printf(client, "Range: %u.%03u-%u.%03u\n",
+ start_ms / 1000,
+ start_ms % 1000,
+ end_ms / 1000,
+ end_ms % 1000);
+ else if (start_ms > 0)
+ client_printf(client, "Range: %u.%03u-\n",
+ start_ms / 1000,
+ start_ms % 1000);
+
+ if (song.GetLastModified() > 0)
+ time_print(client, "Last-Modified", song.GetLastModified());
+
+ tag_print(client, song.GetTag());
+}
diff --git a/src/SongPrint.hxx b/src/SongPrint.hxx
index f8df89d38..5779af4c3 100644
--- a/src/SongPrint.hxx
+++ b/src/SongPrint.hxx
@@ -21,12 +21,19 @@
#define MPD_SONG_PRINT_HXX
struct Song;
+class DetachedSong;
class Client;
void
+song_print_info(Client &client, const DetachedSong &song);
+
+void
song_print_info(Client &client, const Song &song);
void
song_print_uri(Client &client, const Song &song);
+void
+song_print_uri(Client &client, const DetachedSong &song);
+
#endif
diff --git a/src/SongSave.cxx b/src/SongSave.cxx
index 18ac63d12..d524f327e 100644
--- a/src/SongSave.cxx
+++ b/src/SongSave.cxx
@@ -20,6 +20,7 @@
#include "config.h"
#include "SongSave.hxx"
#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "TagSave.hxx"
#include "fs/TextFile.hxx"
#include "tag/Tag.hxx"
@@ -36,15 +37,21 @@
static constexpr Domain song_save_domain("song_save");
+static void
+range_save(FILE *file, unsigned start_ms, unsigned end_ms)
+{
+ if (end_ms > 0)
+ fprintf(file, "Range: %u-%u\n", start_ms, end_ms);
+ else if (start_ms > 0)
+ fprintf(file, "Range: %u-\n", start_ms);
+}
+
void
song_save(FILE *fp, const Song &song)
{
fprintf(fp, SONG_BEGIN "%s\n", song.uri);
- if (song.end_ms > 0)
- fprintf(fp, "Range: %u-%u\n", song.start_ms, song.end_ms);
- else if (song.start_ms > 0)
- fprintf(fp, "Range: %u-\n", song.start_ms);
+ range_save(fp, song.start_ms, song.end_ms);
if (song.tag != nullptr)
tag_save(fp, *song.tag);
@@ -53,13 +60,24 @@ song_save(FILE *fp, const Song &song)
fprintf(fp, SONG_END "\n");
}
-Song *
-song_load(TextFile &file, Directory *parent, const char *uri,
+void
+song_save(FILE *fp, const DetachedSong &song)
+{
+ fprintf(fp, SONG_BEGIN "%s\n", song.GetURI());
+
+ range_save(fp, song.GetStartMS(), song.GetEndMS());
+
+ tag_save(fp, song.GetTag());
+
+ fprintf(fp, SONG_MTIME ": %li\n", (long)song.GetLastModified());
+ fprintf(fp, SONG_END "\n");
+}
+
+DetachedSong *
+song_load(TextFile &file, const char *uri,
Error &error)
{
- Song *song = parent != nullptr
- ? Song::NewFile(uri, parent)
- : Song::NewRemote(uri);
+ DetachedSong *song = new DetachedSong(uri);
TagBuilder tag;
@@ -68,7 +86,7 @@ song_load(TextFile &file, Directory *parent, const char *uri,
strcmp(line, SONG_END) != 0) {
char *colon = strchr(line, ':');
if (colon == nullptr || colon == line) {
- song->Free();
+ delete song;
error.Format(song_save_domain,
"unknown line in db: %s", line);
@@ -86,15 +104,19 @@ song_load(TextFile &file, Directory *parent, const char *uri,
} else if (strcmp(line, "Playlist") == 0) {
tag.SetHasPlaylist(strcmp(value, "yes") == 0);
} else if (strcmp(line, SONG_MTIME) == 0) {
- song->mtime = atoi(value);
+ song->SetLastModified(atoi(value));
} else if (strcmp(line, "Range") == 0) {
char *endptr;
- song->start_ms = strtoul(value, &endptr, 10);
- if (*endptr == '-')
- song->end_ms = strtoul(endptr + 1, nullptr, 10);
+ unsigned start_ms = strtoul(value, &endptr, 10);
+ unsigned end_ms = *endptr == '-'
+ ? strtoul(endptr + 1, nullptr, 10)
+ : 0;
+
+ song->SetStartMS(start_ms);
+ song->SetEndMS(end_ms);
} else {
- song->Free();
+ delete song;
error.Format(song_save_domain,
"unknown line in db: %s", line);
@@ -102,8 +124,6 @@ song_load(TextFile &file, Directory *parent, const char *uri,
}
}
- if (tag.IsDefined())
- song->tag = tag.CommitNew();
-
+ song->SetTag(tag.Commit());
return song;
}
diff --git a/src/SongSave.hxx b/src/SongSave.hxx
index 40fb4abf7..6b458679a 100644
--- a/src/SongSave.hxx
+++ b/src/SongSave.hxx
@@ -26,12 +26,16 @@
struct Song;
struct Directory;
+class DetachedSong;
class TextFile;
class Error;
void
song_save(FILE *fp, const Song &song);
+void
+song_save(FILE *fp, const DetachedSong &song);
+
/**
* Loads a song from the input file. Reading stops after the
* "song_end" line.
@@ -39,8 +43,8 @@ song_save(FILE *fp, const Song &song);
* @param error location to store the error occurring
* @return true on success, false on error
*/
-Song *
-song_load(TextFile &file, Directory *parent, const char *uri,
+DetachedSong *
+song_load(TextFile &file, const char *uri,
Error &error);
#endif
diff --git a/src/SongSticker.cxx b/src/SongSticker.cxx
index 88bea1501..b17820fd5 100644
--- a/src/SongSticker.cxx
+++ b/src/SongSticker.cxx
@@ -31,8 +31,6 @@
std::string
sticker_song_get_value(const Song &song, const char *name)
{
- assert(song.IsInDatabase());
-
const auto uri = song.GetURI();
return sticker_load_value("song", uri.c_str(), name);
}
@@ -41,8 +39,6 @@ bool
sticker_song_set_value(const Song &song,
const char *name, const char *value)
{
- assert(song.IsInDatabase());
-
const auto uri = song.GetURI();
return sticker_store_value("song", uri.c_str(), name, value);
}
@@ -50,8 +46,6 @@ sticker_song_set_value(const Song &song,
bool
sticker_song_delete(const Song &song)
{
- assert(song.IsInDatabase());
-
const auto uri = song.GetURI();
return sticker_delete("song", uri.c_str());
}
@@ -59,8 +53,6 @@ sticker_song_delete(const Song &song)
bool
sticker_song_delete_value(const Song &song, const char *name)
{
- assert(song.IsInDatabase());
-
const auto uri = song.GetURI();
return sticker_delete_value("song", uri.c_str(), name);
}
@@ -68,8 +60,6 @@ sticker_song_delete_value(const Song &song, const char *name)
struct sticker *
sticker_song_get(const Song &song)
{
- assert(song.IsInDatabase());
-
const auto uri = song.GetURI();
return sticker_load("song", uri.c_str());
}
diff --git a/src/SongUpdate.cxx b/src/SongUpdate.cxx
index 953c23961..cba3c6957 100644
--- a/src/SongUpdate.cxx
+++ b/src/SongUpdate.cxx
@@ -78,8 +78,6 @@ tag_scan_fallback(Path path,
bool
Song::UpdateFile()
{
- assert(IsFile());
-
const auto path_fs = map_song_fs(*this);
if (path_fs.IsNull())
return false;
@@ -107,13 +105,9 @@ Song::UpdateFile()
bool
Song::UpdateFileInArchive()
{
- const char *suffix;
-
- assert(IsFile());
-
/* check if there's a suffix and a plugin */
- suffix = uri_get_suffix(uri);
+ const char *suffix = uri_get_suffix(uri);
if (suffix == nullptr)
return false;
diff --git a/src/UpdateRemove.cxx b/src/UpdateRemove.cxx
index 91588330a..374a767b8 100644
--- a/src/UpdateRemove.cxx
+++ b/src/UpdateRemove.cxx
@@ -60,7 +60,10 @@ song_remove_event(void)
sticker_song_delete(*removed_song);
#endif
- instance->DeleteSong(*removed_song);
+ {
+ const auto uri = removed_song->GetURI();
+ instance->DeleteSong(uri.c_str());
+ }
/* clear "removed_song" and send signal to update thread */
remove_mutex.lock();
diff --git a/src/cue/CueParser.cxx b/src/cue/CueParser.cxx
index 4a4f823ab..f05a69ae3 100644
--- a/src/cue/CueParser.cxx
+++ b/src/cue/CueParser.cxx
@@ -22,7 +22,7 @@
#include "util/Alloc.hxx"
#include "util/StringUtil.hxx"
#include "util/CharUtil.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "tag/Tag.hxx"
#include <assert.h>
@@ -38,14 +38,9 @@ CueParser::CueParser()
CueParser::~CueParser()
{
- if (current != nullptr)
- current->Free();
-
- if (previous != nullptr)
- previous->Free();
-
- if (finished != nullptr)
- finished->Free();
+ delete current;
+ delete previous;
+ delete finished;
}
static const char *
@@ -169,8 +164,8 @@ CueParser::Commit()
if (current == nullptr)
return;
- assert(current->tag == nullptr);
- current->tag = song_tag.CommitNew();
+ assert(!current->GetTag().IsDefined());
+ current->SetTag(song_tag.Commit());
finished = previous;
previous = current;
@@ -249,8 +244,8 @@ CueParser::Feed2(char *p)
}
state = TRACK;
- current = Song::NewRemote(filename.c_str());
- assert(current->tag == nullptr);
+ current = new DetachedSong(std::move(filename));
+ assert(!current->GetTag().IsDefined());
song_tag = header_tag;
song_tag.AddItem(TAG_TRACK, nr);
@@ -272,14 +267,14 @@ CueParser::Feed2(char *p)
return;
if (!last_updated && previous != nullptr &&
- previous->start_ms < (unsigned)position_ms) {
+ previous->GetStartMS() < (unsigned)position_ms) {
last_updated = true;
- previous->end_ms = position_ms;
- previous->tag->time =
- (previous->end_ms - previous->start_ms + 500) / 1000;
+ previous->SetEndMS(position_ms);
+ previous->WritableTag().time =
+ (previous->GetEndMS() - previous->GetStartMS() + 500) / 1000;
}
- current->start_ms = position_ms;
+ current->SetStartMS(position_ms);
}
}
@@ -305,7 +300,7 @@ CueParser::Finish()
end = true;
}
-Song *
+DetachedSong *
CueParser::Get()
{
if (finished == nullptr && end) {
@@ -317,7 +312,7 @@ CueParser::Get()
previous = nullptr;
}
- Song *song = finished;
+ DetachedSong *song = finished;
finished = nullptr;
return song;
}
diff --git a/src/cue/CueParser.hxx b/src/cue/CueParser.hxx
index bcc759c37..9a32a33c4 100644
--- a/src/cue/CueParser.hxx
+++ b/src/cue/CueParser.hxx
@@ -26,7 +26,7 @@
#include <string>
-struct Song;
+class DetachedSong;
struct Tag;
class CueParser {
@@ -74,19 +74,19 @@ class CueParser {
/**
* The song currently being edited.
*/
- Song *current;
+ DetachedSong *current;
/**
* The previous song. It is remembered because its end_time
* will be set to the current song's start time.
*/
- Song *previous;
+ DetachedSong *previous;
/**
* A song that is completely finished and can be returned to
* the caller via cue_parser_get().
*/
- Song *finished;
+ DetachedSong *finished;
/**
* Set to true after previous.end_time has been updated to the
@@ -125,7 +125,7 @@ public:
* @return a song object that must be freed by the caller, or NULL if
* no song was finished at this time
*/
- Song *Get();
+ DetachedSong *Get();
private:
gcc_pure
diff --git a/src/db/ProxyDatabasePlugin.cxx b/src/db/ProxyDatabasePlugin.cxx
index 3b0f4b4f0..a9da1717d 100644
--- a/src/db/ProxyDatabasePlugin.cxx
+++ b/src/db/ProxyDatabasePlugin.cxx
@@ -340,20 +340,19 @@ void
ProxyDatabase::ReturnSong(Song *song) const
{
assert(song != nullptr);
- assert(song->IsInDatabase());
- assert(song->IsDetached());
+ assert(song->parent == nullptr);
song->Free();
}
static bool
-Visit(struct mpd_connection *connection, const char *uri,
+Visit(struct mpd_connection *connection, Directory &root, const char *uri,
bool recursive, const SongFilter *filter,
VisitDirectory visit_directory, VisitSong visit_song,
VisitPlaylist visit_playlist, Error &error);
static bool
-Visit(struct mpd_connection *connection,
+Visit(struct mpd_connection *connection, Directory &root,
bool recursive, const SongFilter *filter,
const struct mpd_directory *directory,
VisitDirectory visit_directory, VisitSong visit_song,
@@ -362,7 +361,7 @@ Visit(struct mpd_connection *connection,
const char *path = mpd_directory_get_path(directory);
if (visit_directory) {
- Directory *d = Directory::NewGeneric(path, &detached_root);
+ Directory *d = Directory::NewGeneric(path, &root);
bool success = visit_directory(*d, error);
d->Free();
if (!success)
@@ -370,7 +369,7 @@ Visit(struct mpd_connection *connection,
}
if (recursive &&
- !Visit(connection, path, recursive, filter,
+ !Visit(connection, root, path, recursive, filter,
visit_directory, visit_song, visit_playlist, error))
return false;
@@ -394,7 +393,7 @@ Copy(TagBuilder &tag, TagType d_tag,
static Song *
Convert(const struct mpd_song *song)
{
- Song *s = Song::NewDetached(mpd_song_get_uri(song));
+ Song *s = Song::NewFile(mpd_song_get_uri(song), nullptr);
s->mtime = mpd_song_get_last_modified(song);
s->start_ms = mpd_song_get_start(song) * 1000;
@@ -434,7 +433,7 @@ Visit(const SongFilter *filter,
}
static bool
-Visit(const struct mpd_playlist *playlist,
+Visit(const struct mpd_playlist *playlist, Directory &root,
VisitPlaylist visit_playlist, Error &error)
{
if (!visit_playlist)
@@ -443,7 +442,7 @@ Visit(const struct mpd_playlist *playlist,
PlaylistInfo p(mpd_playlist_get_path(playlist),
mpd_playlist_get_last_modified(playlist));
- return visit_playlist(p, detached_root, error);
+ return visit_playlist(p, root, error);
}
class ProxyEntity {
@@ -485,7 +484,7 @@ ReceiveEntities(struct mpd_connection *connection)
}
static bool
-Visit(struct mpd_connection *connection, const char *uri,
+Visit(struct mpd_connection *connection, Directory &root, const char *uri,
bool recursive, const SongFilter *filter,
VisitDirectory visit_directory, VisitSong visit_song,
VisitPlaylist visit_playlist, Error &error)
@@ -503,7 +502,7 @@ Visit(struct mpd_connection *connection, const char *uri,
break;
case MPD_ENTITY_TYPE_DIRECTORY:
- if (!Visit(connection, recursive, filter,
+ if (!Visit(connection, root, recursive, filter,
mpd_entity_get_directory(entity),
visit_directory, visit_song, visit_playlist,
error))
@@ -518,7 +517,7 @@ Visit(struct mpd_connection *connection, const char *uri,
break;
case MPD_ENTITY_TYPE_PLAYLIST:
- if (!Visit(mpd_entity_get_playlist(entity),
+ if (!Visit(mpd_entity_get_playlist(entity), root,
visit_playlist, error))
return false;
break;
@@ -577,7 +576,7 @@ ProxyDatabase::Visit(const DatabaseSelection &selection,
return ::SearchSongs(connection, selection, visit_song, error);
/* fall back to recursive walk (slow!) */
- return ::Visit(connection, selection.uri.c_str(),
+ return ::Visit(connection, *root, selection.uri.c_str(),
selection.recursive, selection.filter,
visit_directory, visit_song, visit_playlist,
error);
diff --git a/src/playlist/AsxPlaylistPlugin.cxx b/src/playlist/AsxPlaylistPlugin.cxx
index ccd98a711..11bdc93d6 100644
--- a/src/playlist/AsxPlaylistPlugin.cxx
+++ b/src/playlist/AsxPlaylistPlugin.cxx
@@ -43,7 +43,7 @@ struct AsxParser {
* The list of songs (in reverse order because that's faster
* while adding).
*/
- std::forward_list<SongPointer> songs;
+ std::forward_list<DetachedSong> songs;
/**
* The current position in the XML file.
@@ -60,10 +60,9 @@ struct AsxParser {
TagType tag_type;
/**
- * The current song. It is allocated after the "location"
- * element.
+ * The current song URI. It is set by the "ref" element.
*/
- Song *song;
+ std::string location;
TagBuilder tag_builder;
@@ -96,7 +95,7 @@ asx_start_element(gcc_unused GMarkupParseContext *context,
case AsxParser::ROOT:
if (StringEqualsCaseASCII(element_name, "entry")) {
parser->state = AsxParser::ENTRY;
- parser->song = Song::NewRemote("asx:");
+ parser->location.clear();
parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
}
@@ -107,17 +106,8 @@ asx_start_element(gcc_unused GMarkupParseContext *context,
const gchar *href = get_attribute(attribute_names,
attribute_values,
"href");
- if (href != nullptr) {
- /* create new song object; we cannot
- replace the existing song's URI,
- because that attribute is
- immutable */
- Song *song = Song::NewRemote(href);
- if (parser->song != nullptr)
- parser->song->Free();
-
- parser->song = song;
- }
+ if (href != nullptr)
+ parser->location = href;
} else if (StringEqualsCaseASCII(element_name, "author"))
/* is that correct? or should it be COMPOSER
or PERFORMER? */
@@ -142,12 +132,9 @@ asx_end_element(gcc_unused GMarkupParseContext *context,
case AsxParser::ENTRY:
if (StringEqualsCaseASCII(element_name, "entry")) {
- if (strcmp(parser->song->uri, "asx:") != 0) {
- assert(parser->song->tag == nullptr);
- parser->song->tag = parser->tag_builder.CommitNew();
- parser->songs.emplace_front(parser->song);
- } else
- parser->song->Free();
+ if (!parser->location.empty())
+ parser->songs.emplace_front(std::move(parser->location),
+ parser->tag_builder.Commit());
parser->state = AsxParser::ROOT;
} else
@@ -186,15 +173,6 @@ static const GMarkupParser asx_parser = {
nullptr,
};
-static void
-asx_parser_destroy(gpointer data)
-{
- AsxParser *parser = (AsxParser *)data;
-
- if (parser->state >= AsxParser::ENTRY)
- parser->song->Free();
-}
-
/*
* The playlist object
*
@@ -215,7 +193,7 @@ asx_open_stream(InputStream &is)
context = g_markup_parse_context_new(&asx_parser,
G_MARKUP_TREAT_CDATA_AS_TEXT,
- &parser, asx_parser_destroy);
+ &parser, nullptr);
while (true) {
nbytes = is.LockRead(buffer, sizeof(buffer), error2);
diff --git a/src/playlist/CuePlaylistPlugin.cxx b/src/playlist/CuePlaylistPlugin.cxx
index 00aa758b0..1da76d372 100644
--- a/src/playlist/CuePlaylistPlugin.cxx
+++ b/src/playlist/CuePlaylistPlugin.cxx
@@ -36,7 +36,7 @@ class CuePlaylist final : public SongEnumerator {
:is(_is), tis(is) {
}
- virtual Song *NextSong() override;
+ virtual DetachedSong *NextSong() override;
};
static SongEnumerator *
@@ -45,10 +45,10 @@ cue_playlist_open_stream(InputStream &is)
return new CuePlaylist(is);
}
-Song *
+DetachedSong *
CuePlaylist::NextSong()
{
- Song *song = parser.Get();
+ DetachedSong *song = parser.Get();
if (song != nullptr)
return song;
diff --git a/src/playlist/DespotifyPlaylistPlugin.cxx b/src/playlist/DespotifyPlaylistPlugin.cxx
index 67400e20a..d73c7fe72 100644
--- a/src/playlist/DespotifyPlaylistPlugin.cxx
+++ b/src/playlist/DespotifyPlaylistPlugin.cxx
@@ -23,7 +23,7 @@
#include "PlaylistPlugin.hxx"
#include "MemorySongEnumerator.hxx"
#include "tag/Tag.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "Log.hxx"
extern "C" {
@@ -34,10 +34,9 @@ extern "C" {
#include <stdlib.h>
static void
-add_song(std::forward_list<SongPointer> &songs, ds_track &track)
+add_song(std::forward_list<DetachedSong> &songs, ds_track &track)
{
const char *dsp_scheme = despotify_playlist_plugin.schemes[0];
- Song *song;
char uri[128];
char *ds_uri;
@@ -52,15 +51,12 @@ add_song(std::forward_list<SongPointer> &songs, ds_track &track)
return;
}
- song = Song::NewRemote(uri);
- song->tag = new Tag(mpd_despotify_tag_from_track(track));
-
- songs.emplace_front(song);
+ songs.emplace_front(uri, mpd_despotify_tag_from_track(track));
}
static bool
parse_track(struct despotify_session *session,
- std::forward_list<SongPointer> &songs,
+ std::forward_list<DetachedSong> &songs,
struct ds_link *link)
{
struct ds_track *track = despotify_link_get_track(session, link);
@@ -73,7 +69,7 @@ parse_track(struct despotify_session *session,
static bool
parse_playlist(struct despotify_session *session,
- std::forward_list<SongPointer> &songs,
+ std::forward_list<DetachedSong> &songs,
struct ds_link *link)
{
ds_playlist *playlist = despotify_link_get_playlist(session, link);
@@ -103,7 +99,7 @@ despotify_playlist_open_uri(const char *url,
return nullptr;
}
- std::forward_list<SongPointer> songs;
+ std::forward_list<DetachedSong> songs;
bool parse_result;
switch (link->type) {
diff --git a/src/playlist/EmbeddedCuePlaylistPlugin.cxx b/src/playlist/EmbeddedCuePlaylistPlugin.cxx
index 9dfecbf46..77df51778 100644
--- a/src/playlist/EmbeddedCuePlaylistPlugin.cxx
+++ b/src/playlist/EmbeddedCuePlaylistPlugin.cxx
@@ -30,7 +30,7 @@
#include "tag/TagHandler.hxx"
#include "tag/TagId3.hxx"
#include "tag/ApeTag.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "TagFile.hxx"
#include "cue/CueParser.hxx"
#include "fs/Traits.hxx"
@@ -69,7 +69,7 @@ public:
delete parser;
}
- virtual Song *NextSong() override;
+ virtual DetachedSong *NextSong() override;
};
static void
@@ -124,10 +124,10 @@ embcue_playlist_open_uri(const char *uri,
return playlist;
}
-Song *
+DetachedSong *
EmbeddedCuePlaylist::NextSong()
{
- Song *song = parser->Get();
+ DetachedSong *song = parser->Get();
if (song != nullptr)
return song;
@@ -145,14 +145,16 @@ EmbeddedCuePlaylist::NextSong()
parser->Feed(line);
song = parser->Get();
- if (song != nullptr)
- return song->ReplaceURI(filename.c_str());
+ if (song != nullptr) {
+ song->SetURI(filename);
+ return song;
+ }
}
parser->Finish();
song = parser->Get();
if (song != nullptr)
- song = song->ReplaceURI(filename.c_str());
+ song->SetURI(filename);
return song;
}
diff --git a/src/playlist/ExtM3uPlaylistPlugin.cxx b/src/playlist/ExtM3uPlaylistPlugin.cxx
index 4f0c111ad..51211988c 100644
--- a/src/playlist/ExtM3uPlaylistPlugin.cxx
+++ b/src/playlist/ExtM3uPlaylistPlugin.cxx
@@ -21,7 +21,7 @@
#include "ExtM3uPlaylistPlugin.hxx"
#include "PlaylistPlugin.hxx"
#include "SongEnumerator.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "tag/Tag.hxx"
#include "tag/TagBuilder.hxx"
#include "util/StringUtil.hxx"
@@ -44,7 +44,7 @@ public:
strcmp(line.c_str(), "#EXTM3U") == 0;
}
- virtual Song *NextSong() override;
+ virtual DetachedSong *NextSong() override;
};
static SongEnumerator *
@@ -101,20 +101,19 @@ extm3u_parse_tag(const char *line)
return tag.CommitNew();
}
-Song *
+DetachedSong *
ExtM3uPlaylist::NextSong()
{
Tag *tag = NULL;
std::string line;
const char *line_s;
- Song *song;
do {
if (!tis.ReadLine(line)) {
delete tag;
return NULL;
}
-
+
line_s = line.c_str();
if (StringStartsWith(line_s, "#EXTINF:")) {
@@ -126,8 +125,8 @@ ExtM3uPlaylist::NextSong()
line_s = strchug_fast(line_s);
} while (line_s[0] == '#' || *line_s == 0);
- song = Song::NewRemote(line_s);
- song->tag = tag;
+ DetachedSong *song = new DetachedSong(line_s, std::move(*tag));
+ delete tag;
return song;
}
diff --git a/src/playlist/M3uPlaylistPlugin.cxx b/src/playlist/M3uPlaylistPlugin.cxx
index 3f99bdfdf..09f2c91cb 100644
--- a/src/playlist/M3uPlaylistPlugin.cxx
+++ b/src/playlist/M3uPlaylistPlugin.cxx
@@ -21,7 +21,7 @@
#include "M3uPlaylistPlugin.hxx"
#include "PlaylistPlugin.hxx"
#include "SongEnumerator.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "util/StringUtil.hxx"
#include "TextInputStream.hxx"
@@ -33,7 +33,7 @@ public:
:tis(is) {
}
- virtual Song *NextSong() override;
+ virtual DetachedSong *NextSong() override;
};
static SongEnumerator *
@@ -42,7 +42,7 @@ m3u_open_stream(InputStream &is)
return new M3uPlaylist(is);
}
-Song *
+DetachedSong *
M3uPlaylist::NextSong()
{
std::string line;
@@ -56,7 +56,7 @@ M3uPlaylist::NextSong()
line_s = strchug_fast(line_s);
} while (line_s[0] == '#' || *line_s == 0);
- return Song::NewRemote(line_s);
+ return new DetachedSong(line_s);
}
static const char *const m3u_suffixes[] = {
diff --git a/src/playlist/PlsPlaylistPlugin.cxx b/src/playlist/PlsPlaylistPlugin.cxx
index 3fd420d89..103451dfe 100644
--- a/src/playlist/PlsPlaylistPlugin.cxx
+++ b/src/playlist/PlsPlaylistPlugin.cxx
@@ -22,7 +22,7 @@
#include "PlaylistPlugin.hxx"
#include "MemorySongEnumerator.hxx"
#include "InputStream.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "tag/TagBuilder.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
@@ -37,7 +37,7 @@
static constexpr Domain pls_domain("pls");
static void
-pls_parser(GKeyFile *keyfile, std::forward_list<SongPointer> &songs)
+pls_parser(GKeyFile *keyfile, std::forward_list<DetachedSong> &songs)
{
gchar *value;
GError *error = nullptr;
@@ -61,8 +61,8 @@ pls_parser(GKeyFile *keyfile, std::forward_list<SongPointer> &songs)
for (; num_entries > 0; --num_entries) {
char key[64];
sprintf(key, "File%u", num_entries);
- value = g_key_file_get_string(keyfile, "playlist", key,
- &error);
+ char *uri = g_key_file_get_string(keyfile, "playlist", key,
+ &error);
if(error) {
FormatError(pls_domain, "Invalid PLS entry %s: '%s'",
key, error->message);
@@ -70,9 +70,6 @@ pls_parser(GKeyFile *keyfile, std::forward_list<SongPointer> &songs)
return;
}
- Song *song = Song::NewRemote(value);
- g_free(value);
-
TagBuilder tag;
sprintf(key, "Title%u", num_entries);
@@ -89,8 +86,8 @@ pls_parser(GKeyFile *keyfile, std::forward_list<SongPointer> &songs)
if (length > 0)
tag.SetTime(length);
- song->tag = tag.CommitNew();
- songs.emplace_front(song);
+ songs.emplace_front(uri, tag.Commit());
+ g_free(uri);
}
}
@@ -135,7 +132,7 @@ pls_open_stream(InputStream &is)
return nullptr;
}
- std::forward_list<SongPointer> songs;
+ std::forward_list<DetachedSong> songs;
pls_parser(keyfile, songs);
g_key_file_free(keyfile);
diff --git a/src/playlist/RssPlaylistPlugin.cxx b/src/playlist/RssPlaylistPlugin.cxx
index c4dd2f8c3..558c74619 100644
--- a/src/playlist/RssPlaylistPlugin.cxx
+++ b/src/playlist/RssPlaylistPlugin.cxx
@@ -43,7 +43,7 @@ struct RssParser {
* The list of songs (in reverse order because that's faster
* while adding).
*/
- std::forward_list<SongPointer> songs;
+ std::forward_list<DetachedSong> songs;
/**
* The current position in the XML file.
@@ -60,10 +60,10 @@ struct RssParser {
TagType tag_type;
/**
- * The current song. It is allocated after the "location"
+ * The current song URI. It is set by the "enclosure"
* element.
*/
- Song *song;
+ std::string location;
TagBuilder tag_builder;
@@ -95,7 +95,7 @@ rss_start_element(gcc_unused GMarkupParseContext *context,
case RssParser::ROOT:
if (StringEqualsCaseASCII(element_name, "item")) {
parser->state = RssParser::ITEM;
- parser->song = Song::NewRemote("rss:");
+ parser->location.clear();
parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
}
@@ -106,18 +106,8 @@ rss_start_element(gcc_unused GMarkupParseContext *context,
const gchar *href = get_attribute(attribute_names,
attribute_values,
"url");
- if (href != nullptr) {
- /* create new song object; we cannot
- replace the existing song's URI,
- because that attribute is
- immutable */
- Song *song = Song::NewRemote(href);
-
- if (parser->song != nullptr)
- parser->song->Free();
-
- parser->song = song;
- }
+ if (href != nullptr)
+ parser->location = href;
} else if (StringEqualsCaseASCII(element_name, "title"))
parser->tag_type = TAG_TITLE;
else if (StringEqualsCaseASCII(element_name, "itunes:author"))
@@ -140,12 +130,9 @@ rss_end_element(gcc_unused GMarkupParseContext *context,
case RssParser::ITEM:
if (StringEqualsCaseASCII(element_name, "item")) {
- if (strcmp(parser->song->uri, "rss:") != 0) {
- assert(parser->song->tag == nullptr);
- parser->song->tag = parser->tag_builder.CommitNew();
- parser->songs.emplace_front(parser->song);
- } else
- parser->song->Free();
+ if (!parser->location.empty())
+ parser->songs.emplace_front(std::move(parser->location),
+ parser->tag_builder.Commit());
parser->state = RssParser::ROOT;
} else
@@ -183,15 +170,6 @@ static const GMarkupParser rss_parser = {
nullptr,
};
-static void
-rss_parser_destroy(gpointer data)
-{
- RssParser *parser = (RssParser *)data;
-
- if (parser->state >= RssParser::ITEM)
- parser->song->Free();
-}
-
/*
* The playlist object
*
@@ -212,7 +190,7 @@ rss_open_stream(InputStream &is)
context = g_markup_parse_context_new(&rss_parser,
G_MARKUP_TREAT_CDATA_AS_TEXT,
- &parser, rss_parser_destroy);
+ &parser, nullptr);
while (true) {
nbytes = is.LockRead(buffer, sizeof(buffer), error2);
diff --git a/src/playlist/SoundCloudPlaylistPlugin.cxx b/src/playlist/SoundCloudPlaylistPlugin.cxx
index cfbd0e8b5..50a9cb214 100644
--- a/src/playlist/SoundCloudPlaylistPlugin.cxx
+++ b/src/playlist/SoundCloudPlaylistPlugin.cxx
@@ -108,7 +108,7 @@ struct parse_data {
char* title;
int got_url; /* nesting level of last stream_url */
- std::forward_list<SongPointer> songs;
+ std::forward_list<DetachedSong> songs;
};
static int
@@ -214,16 +214,14 @@ handle_end_map(void *ctx)
char *u = g_strconcat(data->stream_url, "?client_id=",
soundcloud_config.apikey.c_str(), nullptr);
- Song *s = Song::NewRemote(u);
- g_free(u);
TagBuilder tag;
tag.SetTime(data->duration / 1000);
if (data->title != nullptr)
tag.AddItem(TAG_NAME, data->title);
- s->tag = tag.CommitNew();
- data->songs.emplace_front(s);
+ data->songs.emplace_front(u, tag.Commit());
+ g_free(u);
return 1;
}
diff --git a/src/playlist/XspfPlaylistPlugin.cxx b/src/playlist/XspfPlaylistPlugin.cxx
index 08fe49191..7c20df57d 100644
--- a/src/playlist/XspfPlaylistPlugin.cxx
+++ b/src/playlist/XspfPlaylistPlugin.cxx
@@ -21,6 +21,7 @@
#include "XspfPlaylistPlugin.hxx"
#include "PlaylistPlugin.hxx"
#include "MemorySongEnumerator.hxx"
+#include "DetachedSong.hxx"
#include "InputStream.hxx"
#include "tag/TagBuilder.hxx"
#include "util/Error.hxx"
@@ -41,7 +42,7 @@ struct XspfParser {
* The list of songs (in reverse order because that's faster
* while adding).
*/
- std::forward_list<SongPointer> songs;
+ std::forward_list<DetachedSong> songs;
/**
* The current position in the XML file.
@@ -59,10 +60,9 @@ struct XspfParser {
TagType tag_type;
/**
- * The current song. It is allocated after the "location"
- * element.
+ * The current song URI. It is set by the "location" element.
*/
- Song *song;
+ std::string location;
TagBuilder tag_builder;
@@ -95,7 +95,7 @@ xspf_start_element(gcc_unused GMarkupParseContext *context,
case XspfParser::TRACKLIST:
if (strcmp(element_name, "track") == 0) {
parser->state = XspfParser::TRACK;
- parser->song = nullptr;
+ parser->location.clear();
parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
}
@@ -149,11 +149,9 @@ xspf_end_element(gcc_unused GMarkupParseContext *context,
case XspfParser::TRACK:
if (strcmp(element_name, "track") == 0) {
- if (parser->song != nullptr) {
- assert(parser->song->tag == nullptr);
- parser->song->tag = parser->tag_builder.CommitNew();
- parser->songs.emplace_front(parser->song);
- }
+ if (!parser->location.empty())
+ parser->songs.emplace_front(std::move(parser->location),
+ parser->tag_builder.Commit());
parser->state = XspfParser::TRACKLIST;
} else
@@ -181,7 +179,7 @@ xspf_text(gcc_unused GMarkupParseContext *context,
break;
case XspfParser::TRACK:
- if (parser->song != nullptr &&
+ if (!parser->location.empty() &&
parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
parser->tag_builder.AddItem(parser->tag_type,
text, text_len);
@@ -189,11 +187,7 @@ xspf_text(gcc_unused GMarkupParseContext *context,
break;
case XspfParser::LOCATION:
- if (parser->song == nullptr) {
- char *uri = g_strndup(text, text_len);
- parser->song = Song::NewRemote(uri);
- g_free(uri);
- }
+ parser->location.assign(text, text_len);
break;
}
@@ -207,15 +201,6 @@ static const GMarkupParser xspf_parser = {
nullptr,
};
-static void
-xspf_parser_destroy(gpointer data)
-{
- XspfParser *parser = (XspfParser *)data;
-
- if (parser->state >= XspfParser::TRACK && parser->song != nullptr)
- parser->song->Free();
-}
-
/*
* The playlist object
*
@@ -236,7 +221,7 @@ xspf_open_stream(InputStream &is)
context = g_markup_parse_context_new(&xspf_parser,
G_MARKUP_TREAT_CDATA_AS_TEXT,
- &parser, xspf_parser_destroy);
+ &parser, nullptr);
while (true) {
nbytes = is.LockRead(buffer, sizeof(buffer), error2);
diff --git a/test/DumpDatabase.cxx b/test/DumpDatabase.cxx
index 14bb4ba14..6e9259238 100644
--- a/test/DumpDatabase.cxx
+++ b/test/DumpDatabase.cxx
@@ -49,7 +49,10 @@ DumpDirectory(const Directory &directory, Error &)
static bool
DumpSong(Song &song, Error &)
{
- cout << "S " << song.parent->path << "/" << song.uri << endl;
+ cout << "S ";
+ if (song.parent != nullptr && !song.parent->IsRoot())
+ cout << song.parent->path << "/";
+ cout << song.uri << endl;
return true;
}
diff --git a/test/dump_playlist.cxx b/test/dump_playlist.cxx
index 2ac855a84..b0702d3be 100644
--- a/test/dump_playlist.cxx
+++ b/test/dump_playlist.cxx
@@ -19,9 +19,8 @@
#include "config.h"
#include "TagSave.hxx"
-#include "Song.hxx"
+#include "DetachedSong.hxx"
#include "SongEnumerator.hxx"
-#include "Directory.hxx"
#include "InputStream.hxx"
#include "ConfigGlobal.hxx"
#include "DecoderList.hxx"
@@ -39,14 +38,10 @@
#include <unistd.h>
#include <stdlib.h>
-Directory::Directory() {}
-Directory::~Directory() {}
-
int main(int argc, char **argv)
{
const char *uri;
InputStream *is = NULL;
- Song *song;
if (argc != 3) {
fprintf(stderr, "Usage: dump_playlist CONFIG URI\n");
@@ -114,24 +109,27 @@ int main(int argc, char **argv)
/* dump the playlist */
+ DetachedSong *song;
while ((song = playlist->NextSong()) != NULL) {
- printf("%s\n", song->uri);
+ printf("%s\n", song->GetURI());
+
+ const unsigned start_ms = song->GetStartMS();
+ const unsigned end_ms = song->GetEndMS();
- if (song->end_ms > 0)
+ if (end_ms > 0)
printf("range: %u:%02u..%u:%02u\n",
- song->start_ms / 60000,
- (song->start_ms / 1000) % 60,
- song->end_ms / 60000,
- (song->end_ms / 1000) % 60);
- else if (song->start_ms > 0)
+ start_ms / 60000,
+ (start_ms / 1000) % 60,
+ end_ms / 60000,
+ (end_ms / 1000) % 60);
+ else if (start_ms > 0)
printf("range: %u:%02u..\n",
- song->start_ms / 60000,
- (song->start_ms / 1000) % 60);
+ start_ms / 60000,
+ (start_ms / 1000) % 60);
- if (song->tag != NULL)
- tag_save(stdout, *song->tag);
+ tag_save(stdout, song->GetTag());
- song->Free();
+ delete song;
}
/* deinitialize everything */
diff --git a/test/test_queue_priority.cxx b/test/test_queue_priority.cxx
index a1037798c..953103f9a 100644
--- a/test/test_queue_priority.cxx
+++ b/test/test_queue_priority.cxx
@@ -1,7 +1,6 @@
#include "config.h"
#include "Queue.hxx"
-#include "Song.hxx"
-#include "Directory.hxx"
+#include "DetachedSong.hxx"
#include "util/Macros.hxx"
#include <cppunit/TestFixture.h>
@@ -9,21 +8,8 @@
#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/extensions/HelperMacros.h>
-Directory detached_root;
-
-Directory::Directory() {}
-Directory::~Directory() {}
-
-Song *
-Song::DupDetached() const
-{
- return const_cast<Song *>(this);
-}
-
-void
-Song::Free()
-{
-}
+Tag::Tag(const Tag &) {}
+void Tag::Clear() {}
static void
check_descending_priority(const struct queue *queue,
@@ -53,12 +39,29 @@ public:
void
QueuePriorityTest::TestPriority()
{
- static Song songs[16];
+ DetachedSong songs[16] = {
+ DetachedSong("0.ogg"),
+ DetachedSong("1.ogg"),
+ DetachedSong("2.ogg"),
+ DetachedSong("3.ogg"),
+ DetachedSong("4.ogg"),
+ DetachedSong("5.ogg"),
+ DetachedSong("6.ogg"),
+ DetachedSong("7.ogg"),
+ DetachedSong("8.ogg"),
+ DetachedSong("9.ogg"),
+ DetachedSong("a.ogg"),
+ DetachedSong("b.ogg"),
+ DetachedSong("c.ogg"),
+ DetachedSong("d.ogg"),
+ DetachedSong("e.ogg"),
+ DetachedSong("f.ogg"),
+ };
struct queue queue(32);
for (unsigned i = 0; i < ARRAY_SIZE(songs); ++i)
- queue.Append(&songs[i], 0);
+ queue.Append(DetachedSong(songs[i]), 0);
CPPUNIT_ASSERT_EQUAL(unsigned(ARRAY_SIZE(songs)), queue.GetLength());