diff options
Diffstat (limited to '')
44 files changed, 386 insertions, 237 deletions
diff --git a/Makefile.am b/Makefile.am index 65ffc300c..882fd4e05 100644 --- a/Makefile.am +++ b/Makefile.am @@ -196,6 +196,7 @@ src_mpd_SOURCES = \ src/ReplayGainInfo.cxx src/ReplayGainInfo.hxx \ src/SignalHandlers.cxx src/SignalHandlers.hxx \ src/DetachedSong.cxx src/DetachedSong.hxx \ + src/LightSong.cxx src/LightSong.hxx \ src/Song.cxx src/Song.hxx \ src/SongUpdate.cxx \ src/SongPrint.cxx src/SongPrint.hxx \ diff --git a/src/DatabaseHelpers.cxx b/src/DatabaseHelpers.cxx index e7bd006f0..58e7aaa3b 100644 --- a/src/DatabaseHelpers.cxx +++ b/src/DatabaseHelpers.cxx @@ -19,7 +19,7 @@ #include "DatabaseHelpers.hxx" #include "DatabasePlugin.hxx" -#include "Song.hxx" +#include "LightSong.hxx" #include "tag/Tag.hxx" #include <functional> @@ -37,9 +37,9 @@ struct StringLess { typedef std::set<const char *, StringLess> StringSet; static bool -CollectTags(StringSet &set, TagType tag_type, Song &song) +CollectTags(StringSet &set, TagType tag_type, const LightSong &song) { - const Tag *tag = &song.tag; + const Tag *tag = song.tag; bool found = false; for (unsigned i = 0; i < tag->num_items; ++i) { @@ -102,11 +102,11 @@ StatsVisitTag(DatabaseStats &stats, StringSet &artists, StringSet &albums, static bool StatsVisitSong(DatabaseStats &stats, StringSet &artists, StringSet &albums, - Song &song) + const LightSong &song) { ++stats.song_count; - StatsVisitTag(stats, artists, albums, song.tag); + StatsVisitTag(stats, artists, albums, *song.tag); return true; } diff --git a/src/DatabasePlaylist.cxx b/src/DatabasePlaylist.cxx index 5f7c85a78..58742ca64 100644 --- a/src/DatabasePlaylist.cxx +++ b/src/DatabasePlaylist.cxx @@ -30,7 +30,7 @@ static bool AddSong(const char *playlist_path_utf8, - Song &song, Error &error) + const LightSong &song, Error &error) { return spl_append_song(playlist_path_utf8, map_song_detach(song), error); diff --git a/src/DatabasePlugin.hxx b/src/DatabasePlugin.hxx index 3af44d7dd..2ded7f736 100644 --- a/src/DatabasePlugin.hxx +++ b/src/DatabasePlugin.hxx @@ -35,7 +35,7 @@ struct config_param; struct DatabaseSelection; struct db_visitor; -struct Song; +struct LightSong; class Error; class EventLoop; class DatabaseListener; @@ -94,14 +94,14 @@ public: * @param uri_utf8 the URI of the song within the music * directory (UTF-8) */ - virtual Song *GetSong(const char *uri_utf8, - Error &error) const = 0; + virtual const LightSong *GetSong(const char *uri_utf8, + Error &error) const = 0; /** * Mark the song object as "unused". Call this on objects * returned by GetSong(). */ - virtual void ReturnSong(Song *song) const = 0; + virtual void ReturnSong(const LightSong *song) const = 0; /** * Visit the selected entities. diff --git a/src/DatabasePrint.cxx b/src/DatabasePrint.cxx index 514cf3f7b..7b5975275 100644 --- a/src/DatabasePrint.cxx +++ b/src/DatabasePrint.cxx @@ -26,7 +26,7 @@ #include "Directory.hxx" #include "Client.hxx" #include "tag/Tag.hxx" -#include "Song.hxx" +#include "LightSong.hxx" #include "DatabaseGlue.hxx" #include "DatabasePlugin.hxx" @@ -52,6 +52,17 @@ PrintDirectoryFull(Client &client, const Directory &directory) return true; } +static void +print_playlist_in_directory(Client &client, + const char *directory, + const char *name_utf8) +{ + if (directory == nullptr) + client_printf(client, "playlist: %s\n", name_utf8); + else + client_printf(client, "playlist: %s/%s\n", + directory, name_utf8); +} static void print_playlist_in_directory(Client &client, @@ -66,25 +77,25 @@ print_playlist_in_directory(Client &client, } static bool -PrintSongBrief(Client &client, const Song &song) +PrintSongBrief(Client &client, const LightSong &song) { song_print_uri(client, song); - if (song.tag.has_playlist) + if (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.directory, song.uri); return true; } static bool -PrintSongFull(Client &client, const Song &song) +PrintSongFull(Client &client, const LightSong &song) { song_print_info(client, song); - if (song.tag.has_playlist) + if (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.directory, song.uri); return true; } @@ -146,7 +157,7 @@ static void printSearchStats(Client &client, SearchStats *stats) } static bool -stats_visitor_song(SearchStats &stats, Song &song) +stats_visitor_song(SearchStats &stats, const LightSong &song) { stats.numberOfSongs++; stats.playTime += song.GetDuration(); @@ -195,7 +206,7 @@ printInfoForAllIn(Client &client, const char *uri_utf8, } static bool -PrintSongURIVisitor(Client &client, Song &song) +PrintSongURIVisitor(Client &client, const LightSong &song) { song_print_uri(client, song); diff --git a/src/DatabaseQueue.cxx b/src/DatabaseQueue.cxx index 0ed073285..ee1dbd57c 100644 --- a/src/DatabaseQueue.cxx +++ b/src/DatabaseQueue.cxx @@ -29,7 +29,7 @@ #include <functional> static bool -AddToQueue(Partition &partition, const Song &song, Error &error) +AddToQueue(Partition &partition, const LightSong &song, Error &error) { PlaylistResult result = partition.playlist.AppendSong(partition.pc, diff --git a/src/DatabaseSelection.cxx b/src/DatabaseSelection.cxx index 1dedafec0..035321252 100644 --- a/src/DatabaseSelection.cxx +++ b/src/DatabaseSelection.cxx @@ -31,7 +31,7 @@ DatabaseSelection::DatabaseSelection(const char *_uri, bool _recursive, } bool -DatabaseSelection::Match(const Song &song) const +DatabaseSelection::Match(const LightSong &song) const { return filter == nullptr || filter->Match(song); } diff --git a/src/DatabaseSelection.hxx b/src/DatabaseSelection.hxx index 3447b5eee..a39ce7afe 100644 --- a/src/DatabaseSelection.hxx +++ b/src/DatabaseSelection.hxx @@ -25,7 +25,7 @@ #include <string> class SongFilter; -struct Song; +struct LightSong; struct DatabaseSelection { /** @@ -45,7 +45,7 @@ struct DatabaseSelection { const SongFilter *_filter=nullptr); gcc_pure - bool Match(const Song &song) const; + bool Match(const LightSong &song) const; }; #endif diff --git a/src/DatabaseSong.cxx b/src/DatabaseSong.cxx index a8f2188cc..592d38b85 100644 --- a/src/DatabaseSong.cxx +++ b/src/DatabaseSong.cxx @@ -31,7 +31,7 @@ DatabaseDetachSong(const char *uri, Error &error) if (db == nullptr) return nullptr; - Song *tmp = db->GetSong(uri, error); + const LightSong *tmp = db->GetSong(uri, error); if (tmp == nullptr) return nullptr; diff --git a/src/DatabaseVisitor.hxx b/src/DatabaseVisitor.hxx index 6f7ff3deb..486407765 100644 --- a/src/DatabaseVisitor.hxx +++ b/src/DatabaseVisitor.hxx @@ -23,12 +23,12 @@ #include <functional> struct Directory; -struct Song; +struct LightSong; struct PlaylistInfo; class Error; typedef std::function<bool(const Directory &, Error &)> VisitDirectory; -typedef std::function<bool(struct Song &, Error &)> VisitSong; +typedef std::function<bool(const LightSong &, Error &)> VisitSong; typedef std::function<bool(const PlaylistInfo &, const Directory &, Error &)> VisitPlaylist; diff --git a/src/DetachedSong.cxx b/src/DetachedSong.cxx index e8b75f618..4e52afb0c 100644 --- a/src/DetachedSong.cxx +++ b/src/DetachedSong.cxx @@ -19,13 +19,13 @@ #include "config.h" #include "DetachedSong.hxx" -#include "Song.hxx" +#include "LightSong.hxx" #include "util/UriUtil.hxx" #include "fs/Traits.hxx" -DetachedSong::DetachedSong(const Song &other) +DetachedSong::DetachedSong(const LightSong &other) :uri(other.GetURI().c_str()), - tag(other.tag), + tag(*other.tag), mtime(other.mtime), start_ms(other.start_ms), end_ms(other.end_ms) {} diff --git a/src/DetachedSong.hxx b/src/DetachedSong.hxx index 7d841a4c2..2ef3cdf3e 100644 --- a/src/DetachedSong.hxx +++ b/src/DetachedSong.hxx @@ -29,10 +29,10 @@ #include <time.h> -struct Song; +struct LightSong; class DetachedSong { - friend DetachedSong map_song_detach(const Song &song); + friend DetachedSong map_song_detach(const LightSong &song); /** * An UTF-8-encoded URI referring to the song file. This can @@ -62,7 +62,7 @@ class DetachedSong { */ unsigned end_ms; - explicit DetachedSong(const Song &other); + explicit DetachedSong(const LightSong &other); public: explicit DetachedSong(const DetachedSong &other) diff --git a/src/Directory.cxx b/src/Directory.cxx index ff2c00b05..210d7cb67 100644 --- a/src/Directory.cxx +++ b/src/Directory.cxx @@ -24,6 +24,7 @@ #include "DatabaseLock.hxx" #include "SongSort.hxx" #include "Song.hxx" +#include "LightSong.hxx" #include "fs/Traits.hxx" #include "util/Alloc.hxx" #include "util/Error.hxx" @@ -251,6 +252,20 @@ Directory::Sort() child->Sort(); } +static LightSong +ExportSong(const Song &src) +{ + LightSong dest; + dest.directory = src.parent->IsRoot() + ? nullptr : src.parent->GetPath(); + dest.uri = src.uri; + dest.tag = &src.tag; + dest.mtime = src.mtime; + dest.start_ms = src.start_ms; + dest.end_ms = src.end_ms; + return dest; +} + bool Directory::Walk(bool recursive, const SongFilter *filter, VisitDirectory visit_directory, VisitSong visit_song, @@ -261,10 +276,12 @@ Directory::Walk(bool recursive, const SongFilter *filter, if (visit_song) { Song *song; - directory_for_each_song(song, *this) - if ((filter == nullptr || filter->Match(*song)) && - !visit_song(*song, error)) + directory_for_each_song(song, *this) { + const LightSong song2 = ExportSong(*song); + if ((filter == nullptr || filter->Match(song2)) && + !visit_song(song2, error)) return false; + } } if (visit_playlist) { diff --git a/test/FakeSong.cxx b/src/LightSong.cxx index e2fae4d6e..af1e801f8 100644 --- a/test/FakeSong.cxx +++ b/src/LightSong.cxx @@ -17,17 +17,17 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "config.h" -#include "Song.hxx" -#include "directory.h" -#include "Compiler.h" +#include "LightSong.hxx" +#include "tag/Tag.hxx" -#include <stdlib.h> +double +LightSong::GetDuration() const +{ + if (end_ms > 0) + return (end_ms - start_ms) / 1000.0; -struct directory detached_root; + if (tag->time <= 0) + return 0; -Song * -song_dup_detached(gcc_unused const Song *src) -{ - abort(); + return tag->time - start_ms / 1000.0; } diff --git a/src/LightSong.hxx b/src/LightSong.hxx new file mode 100644 index 000000000..b93d5876c --- /dev/null +++ b/src/LightSong.hxx @@ -0,0 +1,82 @@ +/* + * 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_LIGHT_SONG_HXX +#define MPD_LIGHT_SONG_HXX + +#include "Compiler.h" + +#include <string> + +#include <time.h> + +struct Tag; + +/** + * A reference to a song file. Unlike the other "Song" classes in the + * MPD code base, this one consists only of pointers. It is supposed + * to be as light as possible while still providing all the + * information MPD has about a song file. This class does not manage + * any memory, and the pointers become invalid quickly. Only to be + * used to pass around during well-defined situations. + */ +struct LightSong { + /** + * If this is not nullptr, then it denotes a prefix for the + * #uri. To build the full URI, join directory and uri with a + * slash. + */ + const char *directory; + + const char *uri; + + /** + * Must not be nullptr. + */ + const 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; + + gcc_pure + std::string GetURI() const { + if (directory == nullptr) + return std::string(uri); + + std::string result(directory); + result.push_back('/'); + result.append(uri); + return result; + } + + gcc_pure + double GetDuration() const; +}; + +#endif diff --git a/src/Mapper.cxx b/src/Mapper.cxx index d41fba957..8fafce12d 100644 --- a/src/Mapper.cxx +++ b/src/Mapper.cxx @@ -218,7 +218,7 @@ map_detached_song_fs(const char *uri_utf8) } DetachedSong -map_song_detach(const Song &song) +map_song_detach(const LightSong &song) { return DetachedSong(song); } diff --git a/src/Mapper.hxx b/src/Mapper.hxx index 18a5ca3fe..5c01a9aff 100644 --- a/src/Mapper.hxx +++ b/src/Mapper.hxx @@ -33,6 +33,7 @@ class AllocatedPath; struct Directory; struct Song; +struct LightSong; class DetachedSong; void @@ -112,7 +113,7 @@ map_directory_child_fs(const Directory &directory, const char *name); */ gcc_pure DetachedSong -map_song_detach(const Song &song); +map_song_detach(const LightSong &song); /** * Determines the file system path of a song. This must not be a diff --git a/src/PlaylistEdit.cxx b/src/PlaylistEdit.cxx index 0eea62e18..cbae02fef 100644 --- a/src/PlaylistEdit.cxx +++ b/src/PlaylistEdit.cxx @@ -29,7 +29,6 @@ #include "PlayerControl.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" -#include "Song.hxx" #include "DetachedSong.hxx" #include "Mapper.hxx" #include "Idle.hxx" diff --git a/src/PlaylistPrint.cxx b/src/PlaylistPrint.cxx index dcd0953d6..34143d8a8 100644 --- a/src/PlaylistPrint.cxx +++ b/src/PlaylistPrint.cxx @@ -30,12 +30,14 @@ #include "DatabasePlugin.hxx" #include "Client.hxx" #include "InputStream.hxx" -#include "Song.hxx" #include "DetachedSong.hxx" #include "fs/Traits.hxx" #include "util/Error.hxx" #include "thread/Cond.hxx" +#define SONG_FILE "file: " +#define SONG_TIME "Time: " + void playlist_print_uris(Client &client, const playlist &playlist) { @@ -118,7 +120,7 @@ PrintSongDetails(Client &client, const char *uri_utf8) if (db == nullptr) return false; - Song *song = db->GetSong(uri_utf8, IgnoreError()); + auto *song = db->GetSong(uri_utf8, IgnoreError()); if (song == nullptr) return false; diff --git a/src/PlaylistSave.cxx b/src/PlaylistSave.cxx index 6d9b41b62..9a01aa954 100644 --- a/src/PlaylistSave.cxx +++ b/src/PlaylistSave.cxx @@ -22,7 +22,6 @@ #include "PlaylistFile.hxx" #include "PlaylistError.hxx" #include "Playlist.hxx" -#include "Song.hxx" #include "DetachedSong.hxx" #include "Mapper.hxx" #include "Idle.hxx" diff --git a/src/PlaylistSong.cxx b/src/PlaylistSong.cxx index 5d61c7f71..5d3011988 100644 --- a/src/PlaylistSong.cxx +++ b/src/PlaylistSong.cxx @@ -29,7 +29,6 @@ #include "util/UriUtil.hxx" #include "util/Error.hxx" #include "DetachedSong.hxx" -#include "Song.hxx" #include <assert.h> #include <string.h> diff --git a/src/PlaylistUpdate.cxx b/src/PlaylistUpdate.cxx index 55b2e9f0a..755589786 100644 --- a/src/PlaylistUpdate.cxx +++ b/src/PlaylistUpdate.cxx @@ -21,7 +21,7 @@ #include "Playlist.hxx" #include "DatabaseGlue.hxx" #include "DatabasePlugin.hxx" -#include "Song.hxx" +#include "LightSong.hxx" #include "DetachedSong.hxx" #include "tag/Tag.hxx" #include "Idle.hxx" @@ -35,7 +35,7 @@ UpdatePlaylistSong(const Database &db, DetachedSong &song) from the Database */ return false; - Song *original = db.GetSong(song.GetURI(), IgnoreError()); + const LightSong *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 @@ -49,7 +49,7 @@ UpdatePlaylistSong(const Database &db, DetachedSong &song) } song.SetLastModified(original->mtime); - song.SetTag(original->tag); + song.SetTag(*original->tag); db.ReturnSong(original); return true; diff --git a/src/Song.cxx b/src/Song.cxx index 384307642..565b9af98 100644 --- a/src/Song.cxx +++ b/src/Song.cxx @@ -23,6 +23,7 @@ #include "tag/Tag.hxx" #include "util/VarSize.hxx" #include "DetachedSong.hxx" +#include "LightSong.hxx" #include <assert.h> #include <string.h> @@ -94,14 +95,16 @@ Song::GetURI() const } } -double -Song::GetDuration() const +LightSong +Song::Export() const { - if (end_ms > 0) - return (end_ms - start_ms) / 1000.0; - - if (tag.time <= 0) - return 0; - - return tag.time - start_ms / 1000.0; + LightSong dest; + dest.directory = parent->IsRoot() + ? nullptr : parent->GetPath(); + dest.uri = uri; + dest.tag = &tag; + dest.mtime = mtime; + dest.start_ms = start_ms; + dest.end_ms = end_ms; + return dest; } diff --git a/src/Song.hxx b/src/Song.hxx index 81f9f0c90..824fb429c 100644 --- a/src/Song.hxx +++ b/src/Song.hxx @@ -29,9 +29,7 @@ #include <assert.h> #include <time.h> -#define SONG_FILE "file: " -#define SONG_TIME "Time: " - +struct LightSong; struct Directory; class DetachedSong; @@ -112,7 +110,7 @@ struct Song { std::string GetURI() const; gcc_pure - double GetDuration() const; + LightSong Export() const; }; #endif diff --git a/src/SongFilter.cxx b/src/SongFilter.cxx index dccbab925..594ac3abc 100644 --- a/src/SongFilter.cxx +++ b/src/SongFilter.cxx @@ -20,6 +20,7 @@ #include "config.h" #include "SongFilter.hxx" #include "Song.hxx" +#include "LightSong.hxx" #include "DetachedSong.hxx" #include "tag/Tag.hxx" #include "util/ASCII.hxx" @@ -137,7 +138,19 @@ SongFilter::Item::Match(const Tag &_tag) const } bool -SongFilter::Item::Match(const Song &song) const +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()); +} + +bool +SongFilter::Item::Match(const LightSong &song) const { if (tag == LOCATE_TAG_BASE_TYPE) { const auto uri = song.GetURI(); @@ -149,19 +162,7 @@ SongFilter::Item::Match(const Song &song) const return StringMatch(uri.c_str()); } - return 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()); + return Match(*song.tag); } SongFilter::SongFilter(unsigned tag, const char *value, bool fold_case) @@ -207,7 +208,7 @@ SongFilter::Parse(unsigned argc, char *argv[], bool fold_case) } bool -SongFilter::Match(const Song &song) const +SongFilter::Match(const DetachedSong &song) const { for (const auto &i : items) if (!i.Match(song)) @@ -217,7 +218,7 @@ SongFilter::Match(const Song &song) const } bool -SongFilter::Match(const DetachedSong &song) const +SongFilter::Match(const LightSong &song) const { for (const auto &i : items) if (!i.Match(song)) diff --git a/src/SongFilter.hxx b/src/SongFilter.hxx index d53c98357..74d7187c9 100644 --- a/src/SongFilter.hxx +++ b/src/SongFilter.hxx @@ -38,6 +38,7 @@ struct Tag; struct TagItem; struct Song; +struct LightSong; class DetachedSong; class SongFilter { @@ -80,10 +81,10 @@ public: bool Match(const Tag &tag) const; gcc_pure - bool Match(const Song &song) const; + bool Match(const DetachedSong &song) const; gcc_pure - bool Match(const DetachedSong &song) const; + bool Match(const LightSong &song) const; }; private: @@ -107,10 +108,10 @@ public: bool Match(const Tag &tag) const; gcc_pure - bool Match(const Song &song) const; + bool Match(const DetachedSong &song) const; gcc_pure - bool Match(const DetachedSong &song) const; + bool Match(const LightSong &song) const; const std::list<Item> &GetItems() const { return items; diff --git a/src/SongPrint.cxx b/src/SongPrint.cxx index 67b622356..810518c21 100644 --- a/src/SongPrint.cxx +++ b/src/SongPrint.cxx @@ -19,7 +19,7 @@ #include "config.h" #include "SongPrint.hxx" -#include "Song.hxx" +#include "LightSong.hxx" #include "DetachedSong.hxx" #include "Directory.hxx" #include "TimePrint.hxx" @@ -28,6 +28,8 @@ #include "Client.hxx" #include "util/UriUtil.hxx" +#define SONG_FILE "file: " + static void song_print_uri(Client &client, const char *uri) { @@ -40,11 +42,11 @@ song_print_uri(Client &client, const char *uri) } void -song_print_uri(Client &client, const Song &song) +song_print_uri(Client &client, const LightSong &song) { - if (song.parent != nullptr && !song.parent->IsRoot()) { + if (song.directory != nullptr) { client_printf(client, "%s%s/%s\n", SONG_FILE, - song.parent->GetPath(), song.uri); + song.directory, song.uri); } else song_print_uri(client, song.uri); } @@ -56,7 +58,7 @@ song_print_uri(Client &client, const DetachedSong &song) } void -song_print_info(Client &client, const Song &song) +song_print_info(Client &client, const LightSong &song) { song_print_uri(client, song); @@ -74,7 +76,7 @@ song_print_info(Client &client, const Song &song) if (song.mtime > 0) time_print(client, "Last-Modified", song.mtime); - tag_print(client, song.tag); + tag_print(client, *song.tag); } void diff --git a/src/SongPrint.hxx b/src/SongPrint.hxx index 7bbf6e19c..16a9ee6ff 100644 --- a/src/SongPrint.hxx +++ b/src/SongPrint.hxx @@ -20,7 +20,7 @@ #ifndef MPD_SONG_PRINT_HXX #define MPD_SONG_PRINT_HXX -struct Song; +struct LightSong; class DetachedSong; class Client; @@ -28,10 +28,10 @@ void song_print_info(Client &client, const DetachedSong &song); void -song_print_info(Client &client, const Song &song); +song_print_info(Client &client, const LightSong &song); void -song_print_uri(Client &client, const Song &song); +song_print_uri(Client &client, const LightSong &song); void song_print_uri(Client &client, const DetachedSong &song); diff --git a/src/SongSticker.cxx b/src/SongSticker.cxx index 8c5499bc2..55143d278 100644 --- a/src/SongSticker.cxx +++ b/src/SongSticker.cxx @@ -20,6 +20,7 @@ #include "config.h" #include "SongSticker.hxx" #include "StickerDatabase.hxx" +#include "LightSong.hxx" #include "Song.hxx" #include "Directory.hxx" @@ -29,14 +30,14 @@ #include <string.h> std::string -sticker_song_get_value(const Song &song, const char *name) +sticker_song_get_value(const LightSong &song, const char *name) { const auto uri = song.GetURI(); return sticker_load_value("song", uri.c_str(), name); } bool -sticker_song_set_value(const Song &song, +sticker_song_set_value(const LightSong &song, const char *name, const char *value) { const auto uri = song.GetURI(); @@ -44,21 +45,21 @@ sticker_song_set_value(const Song &song, } bool -sticker_song_delete(const Song &song) +sticker_song_delete(const LightSong &song) { const auto uri = song.GetURI(); return sticker_delete("song", uri.c_str()); } bool -sticker_song_delete_value(const Song &song, const char *name) +sticker_song_delete_value(const LightSong &song, const char *name) { const auto uri = song.GetURI(); return sticker_delete_value("song", uri.c_str(), name); } struct sticker * -sticker_song_get(const Song &song) +sticker_song_get(const LightSong &song) { const auto uri = song.GetURI(); return sticker_load("song", uri.c_str()); @@ -69,7 +70,7 @@ struct sticker_song_find_data { const char *base_uri; size_t base_uri_length; - void (*func)(Song &song, const char *value, + void (*func)(const LightSong &song, const char *value, void *user_data); void *user_data; }; @@ -86,12 +87,12 @@ sticker_song_find_cb(const char *uri, const char *value, void *user_data) Song *song = data->directory->LookupSong(uri + data->base_uri_length); if (song != nullptr) - data->func(*song, value, data->user_data); + data->func(song->Export(), value, data->user_data); } bool sticker_song_find(Directory &directory, const char *name, - void (*func)(Song &song, const char *value, + void (*func)(const LightSong &song, const char *value, void *user_data), void *user_data) { diff --git a/src/SongSticker.hxx b/src/SongSticker.hxx index b626e63e3..2f977bd21 100644 --- a/src/SongSticker.hxx +++ b/src/SongSticker.hxx @@ -24,7 +24,7 @@ #include <string> -struct Song; +struct LightSong; struct Directory; struct sticker; @@ -34,28 +34,28 @@ struct sticker; */ gcc_pure std::string -sticker_song_get_value(const Song &song, const char *name); +sticker_song_get_value(const LightSong &song, const char *name); /** * Sets a sticker value in the specified song. Overwrites existing * values. */ bool -sticker_song_set_value(const Song &song, +sticker_song_set_value(const LightSong &song, const char *name, const char *value); /** * Deletes a sticker from the database. All values are deleted. */ bool -sticker_song_delete(const Song &song); +sticker_song_delete(const LightSong &song); /** * Deletes a sticker value. Does nothing if the sticker did not * exist. */ bool -sticker_song_delete_value(const Song &song, const char *name); +sticker_song_delete_value(const LightSong &song, const char *name); /** * Loads the sticker for the specified song. @@ -64,7 +64,7 @@ sticker_song_delete_value(const Song &song, const char *name); * @return a sticker object, or NULL on error or if there is no sticker */ sticker * -sticker_song_get(const Song &song); +sticker_song_get(const LightSong &song); /** * Finds stickers with the specified name below the specified @@ -79,7 +79,7 @@ sticker_song_get(const Song &song); */ bool sticker_song_find(Directory &directory, const char *name, - void (*func)(Song &song, const char *value, + void (*func)(const LightSong &song, const char *value, void *user_data), void *user_data); diff --git a/src/TagPrint.cxx b/src/TagPrint.cxx index 14d3abcf0..4f5ea8be3 100644 --- a/src/TagPrint.cxx +++ b/src/TagPrint.cxx @@ -21,9 +21,10 @@ #include "TagPrint.hxx" #include "tag/Tag.hxx" #include "tag/TagSettings.h" -#include "Song.hxx" #include "Client.hxx" +#define SONG_TIME "Time: " + void tag_print_types(Client &client) { int i; diff --git a/src/TagSave.cxx b/src/TagSave.cxx index 24058906e..3a291e115 100644 --- a/src/TagSave.cxx +++ b/src/TagSave.cxx @@ -20,7 +20,8 @@ #include "config.h" #include "TagSave.hxx" #include "tag/Tag.hxx" -#include "Song.hxx" + +#define SONG_TIME "Time: " void tag_save(FILE *file, const Tag &tag) diff --git a/src/UpdateRemove.cxx b/src/UpdateRemove.cxx index cc57fbe94..bed7a92ab 100644 --- a/src/UpdateRemove.cxx +++ b/src/UpdateRemove.cxx @@ -24,6 +24,7 @@ #include "thread/Mutex.hxx" #include "thread/Cond.hxx" #include "Song.hxx" +#include "LightSong.hxx" #include "Main.hxx" #include "Instance.hxx" #include "Log.hxx" @@ -57,7 +58,7 @@ song_remove_event(void) #ifdef ENABLE_SQLITE /* if the song has a sticker, remove it */ if (sticker_enabled()) - sticker_song_delete(*removed_song); + sticker_song_delete(removed_song->Export()); #endif { diff --git a/src/command/StickerCommands.cxx b/src/command/StickerCommands.cxx index 93a13140b..4272dee69 100644 --- a/src/command/StickerCommands.cxx +++ b/src/command/StickerCommands.cxx @@ -39,7 +39,7 @@ struct sticker_song_find_data { }; static void -sticker_song_find_print_cb(Song &song, const char *value, +sticker_song_find_print_cb(const LightSong &song, const char *value, void *user_data) { struct sticker_song_find_data *data = @@ -59,7 +59,7 @@ handle_sticker_song(Client &client, int argc, char *argv[]) /* get song song_id key */ if (argc == 5 && strcmp(argv[1], "get") == 0) { - Song *song = db->GetSong(argv[3], error); + const LightSong *song = db->GetSong(argv[3], error); if (song == nullptr) return print_error(client, error); @@ -76,7 +76,7 @@ handle_sticker_song(Client &client, int argc, char *argv[]) return CommandResult::OK; /* list song song_id */ } else if (argc == 4 && strcmp(argv[1], "list") == 0) { - Song *song = db->GetSong(argv[3], error); + const LightSong *song = db->GetSong(argv[3], error); if (song == nullptr) return print_error(client, error); @@ -90,7 +90,7 @@ handle_sticker_song(Client &client, int argc, char *argv[]) return CommandResult::OK; /* set song song_id id key */ } else if (argc == 6 && strcmp(argv[1], "set") == 0) { - Song *song = db->GetSong(argv[3], error); + const LightSong *song = db->GetSong(argv[3], error); if (song == nullptr) return print_error(client, error); @@ -106,7 +106,7 @@ handle_sticker_song(Client &client, int argc, char *argv[]) /* delete song song_id [key] */ } else if ((argc == 4 || argc == 5) && strcmp(argv[1], "delete") == 0) { - Song *song = db->GetSong(argv[3], error); + const LightSong *song = db->GetSong(argv[3], error); if (song == nullptr) return print_error(client, error); diff --git a/src/db/LazyDatabase.cxx b/src/db/LazyDatabase.cxx index 0718c3dcd..6a01ffb82 100644 --- a/src/db/LazyDatabase.cxx +++ b/src/db/LazyDatabase.cxx @@ -51,7 +51,7 @@ LazyDatabase::Close() } } -Song * +const LightSong * LazyDatabase::GetSong(const char *uri, Error &error) const { return EnsureOpen(error) @@ -60,7 +60,7 @@ LazyDatabase::GetSong(const char *uri, Error &error) const } void -LazyDatabase::ReturnSong(Song *song) const +LazyDatabase::ReturnSong(const LightSong *song) const { assert(open); diff --git a/src/db/LazyDatabase.hxx b/src/db/LazyDatabase.hxx index 7f97aa40d..f718ecb3f 100644 --- a/src/db/LazyDatabase.hxx +++ b/src/db/LazyDatabase.hxx @@ -41,9 +41,9 @@ public: virtual void Close() override; - virtual Song *GetSong(const char *uri_utf8, - Error &error) const override; - virtual void ReturnSong(Song *song) const; + virtual const LightSong *GetSong(const char *uri_utf8, + Error &error) const override; + virtual void ReturnSong(const LightSong *song) const; virtual bool Visit(const DatabaseSelection &selection, VisitDirectory visit_directory, diff --git a/src/db/ProxyDatabasePlugin.cxx b/src/db/ProxyDatabasePlugin.cxx index e5e9ac76f..f65e4f3d0 100644 --- a/src/db/ProxyDatabasePlugin.cxx +++ b/src/db/ProxyDatabasePlugin.cxx @@ -24,11 +24,12 @@ #include "DatabaseSelection.hxx" #include "DatabaseError.hxx" #include "Directory.hxx" -#include "Song.hxx" +#include "LightSong.hxx" #include "SongFilter.hxx" #include "Compiler.h" #include "ConfigData.hxx" #include "tag/TagBuilder.hxx" +#include "tag/Tag.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "protocol/Ack.hxx" @@ -44,6 +45,25 @@ #include <string> #include <list> +class ProxySong : public LightSong { + Tag tag2; + +public: + explicit ProxySong(const mpd_song *song); +}; + +class AllocatedProxySong : public ProxySong { + mpd_song *const song; + +public: + explicit AllocatedProxySong(mpd_song *_song) + :ProxySong(_song), song(_song) {} + + ~AllocatedProxySong() { + mpd_song_free(song); + } +}; + class ProxyDatabase final : public Database, SocketMonitor, IdleMonitor { DatabaseListener &listener; @@ -79,9 +99,9 @@ public: virtual bool Open(Error &error) override; virtual void Close() override; - virtual Song *GetSong(const char *uri_utf8, + virtual const LightSong *GetSong(const char *uri_utf8, Error &error) const override; - virtual void ReturnSong(Song *song) const; + virtual void ReturnSong(const LightSong *song) const; virtual bool Visit(const DatabaseSelection &selection, VisitDirectory visit_directory, @@ -144,6 +164,38 @@ static constexpr struct { { TAG_NUM_OF_ITEM_TYPES, MPD_TAG_COUNT } }; +static void +Copy(TagBuilder &tag, TagType d_tag, + const struct mpd_song *song, enum mpd_tag_type s_tag) +{ + + for (unsigned i = 0;; ++i) { + const char *value = mpd_song_get_tag(song, s_tag, i); + if (value == nullptr) + break; + + tag.AddItem(d_tag, value); + } +} + +ProxySong::ProxySong(const mpd_song *song) +{ + directory = nullptr; + uri = mpd_song_get_uri(song); + tag = &tag2; + mtime = mpd_song_get_last_modified(song); + start_ms = mpd_song_get_start(song) * 1000; + end_ms = mpd_song_get_end(song) * 1000; + + TagBuilder tag_builder; + tag_builder.SetTime(mpd_song_get_duration(song)); + + for (const auto *i = &tag_table[0]; i->d != TAG_NUM_OF_ITEM_TYPES; ++i) + Copy(tag_builder, i->d, song, i->s); + + tag_builder.Commit(tag2); +} + gcc_const static enum mpd_tag_type Convert(TagType tag_type) @@ -424,10 +476,7 @@ ProxyDatabase::OnIdle() SocketMonitor::ScheduleRead(); } -static Song * -Convert(const struct mpd_song *song); - -Song * +const LightSong * ProxyDatabase::GetSong(const char *uri, Error &error) const { // TODO: eliminate the const_cast @@ -452,18 +501,17 @@ ProxyDatabase::GetSong(const char *uri, Error &error) const return nullptr; } - Song *song2 = Convert(song); - mpd_song_free(song); - return song2; + return new AllocatedProxySong(song); } void -ProxyDatabase::ReturnSong(Song *song) const +ProxyDatabase::ReturnSong(const LightSong *_song) const { - assert(song != nullptr); - assert(song->parent == nullptr); + assert(_song != nullptr); - song->Free(); + AllocatedProxySong *song = (AllocatedProxySong *) + const_cast<LightSong *>(_song); + delete song; } static bool @@ -493,60 +541,23 @@ Visit(struct mpd_connection *connection, Directory &root, return true; } -static void -Copy(TagBuilder &tag, TagType d_tag, - const struct mpd_song *song, enum mpd_tag_type s_tag) -{ - - for (unsigned i = 0;; ++i) { - const char *value = mpd_song_get_tag(song, s_tag, i); - if (value == nullptr) - break; - - tag.AddItem(d_tag, value); - } -} - -static Song * -Convert(const struct mpd_song *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; - s->end_ms = mpd_song_get_end(song) * 1000; - - TagBuilder tag; - tag.SetTime(mpd_song_get_duration(song)); - - for (const auto *i = &tag_table[0]; i->d != TAG_NUM_OF_ITEM_TYPES; ++i) - Copy(tag, i->d, song, i->s); - - tag.Commit(s->tag); - - return s; -} - gcc_pure static bool -Match(const SongFilter *filter, const Song &song) +Match(const SongFilter *filter, const LightSong &song) { return filter == nullptr || filter->Match(song); } static bool Visit(const SongFilter *filter, - const struct mpd_song *song, + const mpd_song *_song, VisitSong visit_song, Error &error) { if (!visit_song) return true; - Song *s = Convert(song); - bool success = !Match(filter, *s) || visit_song(*s, error); - s->Free(); - - return success; + const ProxySong song(_song); + return !Match(filter, song) || visit_song(song, error); } static bool @@ -664,12 +675,10 @@ SearchSongs(struct mpd_connection *connection, bool result = true; struct mpd_song *song; while (result && (song = mpd_recv_song(connection)) != nullptr) { - Song *song2 = Convert(song); - mpd_song_free(song); + AllocatedProxySong song2(song); - result = !Match(selection.filter, *song2) || - visit_song(*song2, error); - song2->Free(); + result = !Match(selection.filter, song2) || + visit_song(song2, error); } mpd_response_finish(connection); diff --git a/src/db/SimpleDatabasePlugin.cxx b/src/db/SimpleDatabasePlugin.cxx index c33db3831..3d947c042 100644 --- a/src/db/SimpleDatabasePlugin.cxx +++ b/src/db/SimpleDatabasePlugin.cxx @@ -22,6 +22,7 @@ #include "DatabaseSelection.hxx" #include "DatabaseHelpers.hxx" #include "Directory.hxx" +#include "Song.hxx" #include "SongFilter.hxx" #include "DatabaseSave.hxx" #include "DatabaseLock.hxx" @@ -193,29 +194,34 @@ SimpleDatabase::Close() delete root; } -Song * +const LightSong * SimpleDatabase::GetSong(const char *uri, Error &error) const { assert(root != nullptr); + assert(borrowed_song_count == 0); db_lock(); - Song *song = root->LookupSong(uri); + const Song *song = root->LookupSong(uri); db_unlock(); - if (song == nullptr) + if (song == nullptr) { error.Format(db_domain, DB_NOT_FOUND, "No such song: %s", uri); + return nullptr; + } + + light_song = song->Export(); + #ifndef NDEBUG - else - ++borrowed_song_count; + ++borrowed_song_count; #endif - return song; + return &light_song; } void -SimpleDatabase::ReturnSong(gcc_unused Song *song) const +SimpleDatabase::ReturnSong(gcc_unused const LightSong *song) const { - assert(song != nullptr); + assert(song == &light_song); #ifndef NDEBUG assert(borrowed_song_count > 0); @@ -247,9 +253,11 @@ SimpleDatabase::Visit(const DatabaseSelection &selection, if (directory == nullptr) { if (visit_song) { Song *song = root->LookupSong(selection.uri.c_str()); - if (song != nullptr) - return !selection.Match(*song) || - visit_song(*song, error); + if (song != nullptr) { + const LightSong song2 = song->Export(); + return !selection.Match(song2) || + visit_song(song2, error); + } } error.Set(db_domain, DB_NOT_FOUND, "No such directory"); diff --git a/src/db/SimpleDatabasePlugin.hxx b/src/db/SimpleDatabasePlugin.hxx index d51174194..509b91e4e 100644 --- a/src/db/SimpleDatabasePlugin.hxx +++ b/src/db/SimpleDatabasePlugin.hxx @@ -22,6 +22,7 @@ #include "DatabasePlugin.hxx" #include "fs/AllocatedPath.hxx" +#include "LightSong.hxx" #include "Compiler.h" #include <cassert> @@ -36,6 +37,11 @@ class SimpleDatabase : public Database { time_t mtime; + /** + * A buffer for GetSong(). + */ + mutable LightSong light_song; + #ifndef NDEBUG mutable unsigned borrowed_song_count; #endif @@ -60,9 +66,9 @@ public: virtual bool Open(Error &error) override; virtual void Close() override; - virtual Song *GetSong(const char *uri_utf8, - Error &error) const override; - virtual void ReturnSong(Song *song) const; + virtual const LightSong *GetSong(const char *uri_utf8, + Error &error) const override; + virtual void ReturnSong(const LightSong *song) const; virtual bool Visit(const DatabaseSelection &selection, VisitDirectory visit_directory, diff --git a/src/db/UpnpDatabasePlugin.cxx b/src/db/UpnpDatabasePlugin.cxx index dbf04f818..0768488a3 100644 --- a/src/db/UpnpDatabasePlugin.cxx +++ b/src/db/UpnpDatabasePlugin.cxx @@ -31,7 +31,7 @@ #include "DatabaseError.hxx" #include "PlaylistVector.hxx" #include "Directory.hxx" -#include "Song.hxx" +#include "LightSong.hxx" #include "ConfigData.hxx" #include "tag/TagBuilder.hxx" #include "tag/TagTable.hxx" @@ -49,6 +49,31 @@ static const char *const rootid = "0"; +class UpnpSong : public LightSong { + std::string uri2; + + Tag tag2; + +public: + explicit UpnpSong(UPnPDirObject &&object) + :uri2(std::move(object.url)), tag2(std::move(object.tag)) { + directory = nullptr; + uri = uri2.c_str(); + tag = &tag2; + mtime = 0; + start_ms = end_ms = 0; + } + + UpnpSong(UPnPDirObject &&object, const char *_uri) + :uri2(_uri), tag2(std::move(object.tag)) { + directory = nullptr; + uri = uri2.c_str(); + tag = &tag2; + mtime = 0; + start_ms = end_ms = 0; + } +}; + class UpnpDatabase : public Database { LibUPnP *m_lib; UPnPDeviceDirectory *m_superdir; @@ -61,9 +86,9 @@ public: virtual bool Open(Error &error) override; virtual void Close() override; - virtual Song *GetSong(const char *uri_utf8, - Error &error) const override; - virtual void ReturnSong(Song *song) const; + virtual const LightSong *GetSong(const char *uri_utf8, + Error &error) const override; + virtual void ReturnSong(const LightSong *song) const; virtual bool Visit(const DatabaseSelection &selection, VisitDirectory visit_directory, @@ -187,34 +212,20 @@ UpnpDatabase::Close() } void -UpnpDatabase::ReturnSong(Song *song) const -{ - assert(song != nullptr); - - song->Free(); -} - -// If uri is empty, we use the object's url instead. This happens -// when the target of a Visit() is a song, which only happens when -// "add"ing AFAIK. Visit() calls us with a null uri so that the url -// appropriate for fetching is used instead. -static Song * -upnpItemToSong(UPnPDirObject &&dirent, const char *uri) +UpnpDatabase::ReturnSong(const LightSong *_song) const { - if (*uri == 0) - uri = dirent.url.c_str(); + assert(_song != nullptr); - Song *s = Song::NewFile(uri, nullptr); - s->tag = std::move(dirent.tag); - return s; + UpnpSong *song = (UpnpSong *)const_cast<LightSong *>(_song); + delete song; } // Get song info by path. We can receive either the id path, or the titles // one -Song * +const LightSong * UpnpDatabase::GetSong(const char *uri, Error &error) const { - Song *song = nullptr; + UpnpSong *song = nullptr; auto vpath = stringToTokens(uri, "/", true); if (vpath.size() >= 2) { ContentDirectoryService server; @@ -232,7 +243,8 @@ UpnpDatabase::GetSong(const char *uri, Error &error) const error)) return nullptr; } - song = upnpItemToSong(std::move(dirent), ""); + + song = new UpnpSong(std::move(dirent)); } if (song == nullptr) error.Format(db_domain, DB_NOT_FOUND, "No such song: %s", uri); @@ -357,12 +369,9 @@ visitSong(UPnPDirObject &&meta, const char *path, { if (!visit_song) return true; - Song *s = upnpItemToSong(std::move(meta), path); - if (!selection.Match(*s)) - return true; - bool success = visit_song(*s, error); - s->Free(); - return success; + + const UpnpSong song(std::move(meta), path); + return !selection.Match(song) || visit_song(song, error); } /** diff --git a/src/playlist/AsxPlaylistPlugin.cxx b/src/playlist/AsxPlaylistPlugin.cxx index 7c988a539..24eb26077 100644 --- a/src/playlist/AsxPlaylistPlugin.cxx +++ b/src/playlist/AsxPlaylistPlugin.cxx @@ -21,7 +21,6 @@ #include "AsxPlaylistPlugin.hxx" #include "PlaylistPlugin.hxx" #include "MemorySongEnumerator.hxx" -#include "Song.hxx" #include "tag/TagBuilder.hxx" #include "util/ASCII.hxx" #include "util/Error.hxx" diff --git a/src/playlist/RssPlaylistPlugin.cxx b/src/playlist/RssPlaylistPlugin.cxx index 253ff7ad2..550a4630e 100644 --- a/src/playlist/RssPlaylistPlugin.cxx +++ b/src/playlist/RssPlaylistPlugin.cxx @@ -21,7 +21,6 @@ #include "RssPlaylistPlugin.hxx" #include "PlaylistPlugin.hxx" #include "MemorySongEnumerator.hxx" -#include "Song.hxx" #include "tag/TagBuilder.hxx" #include "util/ASCII.hxx" #include "util/Error.hxx" diff --git a/src/playlist/SoundCloudPlaylistPlugin.cxx b/src/playlist/SoundCloudPlaylistPlugin.cxx index b0282b5da..bf68acd3b 100644 --- a/src/playlist/SoundCloudPlaylistPlugin.cxx +++ b/src/playlist/SoundCloudPlaylistPlugin.cxx @@ -23,7 +23,6 @@ #include "MemorySongEnumerator.hxx" #include "ConfigData.hxx" #include "InputStream.hxx" -#include "Song.hxx" #include "tag/TagBuilder.hxx" #include "util/StringUtil.hxx" #include "util/Error.hxx" diff --git a/test/DumpDatabase.cxx b/test/DumpDatabase.cxx index ade6b5345..60c20e4ae 100644 --- a/test/DumpDatabase.cxx +++ b/test/DumpDatabase.cxx @@ -23,7 +23,7 @@ #include "DatabaseSelection.hxx" #include "DatabaseListener.hxx" #include "Directory.hxx" -#include "Song.hxx" +#include "LightSong.hxx" #include "PlaylistVector.hxx" #include "ConfigGlobal.hxx" #include "ConfigData.hxx" @@ -65,11 +65,11 @@ DumpDirectory(const Directory &directory, Error &) } static bool -DumpSong(Song &song, Error &) +DumpSong(const LightSong &song, Error &) { cout << "S "; - if (song.parent != nullptr && !song.parent->IsRoot()) - cout << song.parent->path << "/"; + if (song.directory != nullptr) + cout << song.directory << "/"; cout << song.uri << endl; return true; } |