LightSong: new class to be used by DatabasePlugin callbacks

Detach the Song class completely from the public API, only to be used
by SimpleDatabase and the update thread.
This commit is contained in:
Max Kellermann 2014-01-19 10:51:34 +01:00
parent 738d6f1040
commit f5ae1ce00b
44 changed files with 387 additions and 238 deletions

View File

@ -196,6 +196,7 @@ src_mpd_SOURCES = \
src/ReplayGainInfo.cxx src/ReplayGainInfo.hxx \ src/ReplayGainInfo.cxx src/ReplayGainInfo.hxx \
src/SignalHandlers.cxx src/SignalHandlers.hxx \ src/SignalHandlers.cxx src/SignalHandlers.hxx \
src/DetachedSong.cxx src/DetachedSong.hxx \ src/DetachedSong.cxx src/DetachedSong.hxx \
src/LightSong.cxx src/LightSong.hxx \
src/Song.cxx src/Song.hxx \ src/Song.cxx src/Song.hxx \
src/SongUpdate.cxx \ src/SongUpdate.cxx \
src/SongPrint.cxx src/SongPrint.hxx \ src/SongPrint.cxx src/SongPrint.hxx \

View File

@ -19,7 +19,7 @@
#include "DatabaseHelpers.hxx" #include "DatabaseHelpers.hxx"
#include "DatabasePlugin.hxx" #include "DatabasePlugin.hxx"
#include "Song.hxx" #include "LightSong.hxx"
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include <functional> #include <functional>
@ -37,9 +37,9 @@ struct StringLess {
typedef std::set<const char *, StringLess> StringSet; typedef std::set<const char *, StringLess> StringSet;
static bool 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; bool found = false;
for (unsigned i = 0; i < tag->num_items; ++i) { for (unsigned i = 0; i < tag->num_items; ++i) {
@ -102,11 +102,11 @@ StatsVisitTag(DatabaseStats &stats, StringSet &artists, StringSet &albums,
static bool static bool
StatsVisitSong(DatabaseStats &stats, StringSet &artists, StringSet &albums, StatsVisitSong(DatabaseStats &stats, StringSet &artists, StringSet &albums,
Song &song) const LightSong &song)
{ {
++stats.song_count; ++stats.song_count;
StatsVisitTag(stats, artists, albums, song.tag); StatsVisitTag(stats, artists, albums, *song.tag);
return true; return true;
} }

View File

@ -30,7 +30,7 @@
static bool static bool
AddSong(const char *playlist_path_utf8, 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), return spl_append_song(playlist_path_utf8, map_song_detach(song),
error); error);

View File

@ -35,7 +35,7 @@
struct config_param; struct config_param;
struct DatabaseSelection; struct DatabaseSelection;
struct db_visitor; struct db_visitor;
struct Song; struct LightSong;
class Error; class Error;
class EventLoop; class EventLoop;
class DatabaseListener; class DatabaseListener;
@ -94,14 +94,14 @@ public:
* @param uri_utf8 the URI of the song within the music * @param uri_utf8 the URI of the song within the music
* directory (UTF-8) * directory (UTF-8)
*/ */
virtual Song *GetSong(const char *uri_utf8, virtual const LightSong *GetSong(const char *uri_utf8,
Error &error) const = 0; Error &error) const = 0;
/** /**
* Mark the song object as "unused". Call this on objects * Mark the song object as "unused". Call this on objects
* returned by GetSong(). * returned by GetSong().
*/ */
virtual void ReturnSong(Song *song) const = 0; virtual void ReturnSong(const LightSong *song) const = 0;
/** /**
* Visit the selected entities. * Visit the selected entities.

View File

@ -26,7 +26,7 @@
#include "Directory.hxx" #include "Directory.hxx"
#include "Client.hxx" #include "Client.hxx"
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include "Song.hxx" #include "LightSong.hxx"
#include "DatabaseGlue.hxx" #include "DatabaseGlue.hxx"
#include "DatabasePlugin.hxx" #include "DatabasePlugin.hxx"
@ -52,6 +52,17 @@ PrintDirectoryFull(Client &client, const Directory &directory)
return true; 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 static void
print_playlist_in_directory(Client &client, print_playlist_in_directory(Client &client,
@ -66,25 +77,25 @@ print_playlist_in_directory(Client &client,
} }
static bool static bool
PrintSongBrief(Client &client, const Song &song) PrintSongBrief(Client &client, const LightSong &song)
{ {
song_print_uri(client, song); song_print_uri(client, song);
if (song.tag.has_playlist) if (song.tag->has_playlist)
/* this song file has an embedded CUE sheet */ /* 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; return true;
} }
static bool static bool
PrintSongFull(Client &client, const Song &song) PrintSongFull(Client &client, const LightSong &song)
{ {
song_print_info(client, song); song_print_info(client, song);
if (song.tag.has_playlist) if (song.tag->has_playlist)
/* this song file has an embedded CUE sheet */ /* 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; return true;
} }
@ -146,7 +157,7 @@ static void printSearchStats(Client &client, SearchStats *stats)
} }
static bool static bool
stats_visitor_song(SearchStats &stats, Song &song) stats_visitor_song(SearchStats &stats, const LightSong &song)
{ {
stats.numberOfSongs++; stats.numberOfSongs++;
stats.playTime += song.GetDuration(); stats.playTime += song.GetDuration();
@ -195,7 +206,7 @@ printInfoForAllIn(Client &client, const char *uri_utf8,
} }
static bool static bool
PrintSongURIVisitor(Client &client, Song &song) PrintSongURIVisitor(Client &client, const LightSong &song)
{ {
song_print_uri(client, song); song_print_uri(client, song);

View File

@ -29,7 +29,7 @@
#include <functional> #include <functional>
static bool static bool
AddToQueue(Partition &partition, const Song &song, Error &error) AddToQueue(Partition &partition, const LightSong &song, Error &error)
{ {
PlaylistResult result = PlaylistResult result =
partition.playlist.AppendSong(partition.pc, partition.playlist.AppendSong(partition.pc,

View File

@ -31,7 +31,7 @@ DatabaseSelection::DatabaseSelection(const char *_uri, bool _recursive,
} }
bool bool
DatabaseSelection::Match(const Song &song) const DatabaseSelection::Match(const LightSong &song) const
{ {
return filter == nullptr || filter->Match(song); return filter == nullptr || filter->Match(song);
} }

View File

@ -25,7 +25,7 @@
#include <string> #include <string>
class SongFilter; class SongFilter;
struct Song; struct LightSong;
struct DatabaseSelection { struct DatabaseSelection {
/** /**
@ -45,7 +45,7 @@ struct DatabaseSelection {
const SongFilter *_filter=nullptr); const SongFilter *_filter=nullptr);
gcc_pure gcc_pure
bool Match(const Song &song) const; bool Match(const LightSong &song) const;
}; };
#endif #endif

View File

@ -31,7 +31,7 @@ DatabaseDetachSong(const char *uri, Error &error)
if (db == nullptr) if (db == nullptr)
return nullptr; return nullptr;
Song *tmp = db->GetSong(uri, error); const LightSong *tmp = db->GetSong(uri, error);
if (tmp == nullptr) if (tmp == nullptr)
return nullptr; return nullptr;

View File

@ -23,12 +23,12 @@
#include <functional> #include <functional>
struct Directory; struct Directory;
struct Song; struct LightSong;
struct PlaylistInfo; struct PlaylistInfo;
class Error; class Error;
typedef std::function<bool(const Directory &, Error &)> VisitDirectory; 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 &, typedef std::function<bool(const PlaylistInfo &, const Directory &,
Error &)> VisitPlaylist; Error &)> VisitPlaylist;

View File

@ -19,13 +19,13 @@
#include "config.h" #include "config.h"
#include "DetachedSong.hxx" #include "DetachedSong.hxx"
#include "Song.hxx" #include "LightSong.hxx"
#include "util/UriUtil.hxx" #include "util/UriUtil.hxx"
#include "fs/Traits.hxx" #include "fs/Traits.hxx"
DetachedSong::DetachedSong(const Song &other) DetachedSong::DetachedSong(const LightSong &other)
:uri(other.GetURI().c_str()), :uri(other.GetURI().c_str()),
tag(other.tag), tag(*other.tag),
mtime(other.mtime), mtime(other.mtime),
start_ms(other.start_ms), end_ms(other.end_ms) {} start_ms(other.start_ms), end_ms(other.end_ms) {}

View File

@ -29,10 +29,10 @@
#include <time.h> #include <time.h>
struct Song; struct LightSong;
class DetachedSong { 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 * An UTF-8-encoded URI referring to the song file. This can
@ -62,7 +62,7 @@ class DetachedSong {
*/ */
unsigned end_ms; unsigned end_ms;
explicit DetachedSong(const Song &other); explicit DetachedSong(const LightSong &other);
public: public:
explicit DetachedSong(const DetachedSong &other) explicit DetachedSong(const DetachedSong &other)

View File

@ -24,6 +24,7 @@
#include "DatabaseLock.hxx" #include "DatabaseLock.hxx"
#include "SongSort.hxx" #include "SongSort.hxx"
#include "Song.hxx" #include "Song.hxx"
#include "LightSong.hxx"
#include "fs/Traits.hxx" #include "fs/Traits.hxx"
#include "util/Alloc.hxx" #include "util/Alloc.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
@ -251,6 +252,20 @@ Directory::Sort()
child->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 bool
Directory::Walk(bool recursive, const SongFilter *filter, Directory::Walk(bool recursive, const SongFilter *filter,
VisitDirectory visit_directory, VisitSong visit_song, VisitDirectory visit_directory, VisitSong visit_song,
@ -261,10 +276,12 @@ Directory::Walk(bool recursive, const SongFilter *filter,
if (visit_song) { if (visit_song) {
Song *song; Song *song;
directory_for_each_song(song, *this) directory_for_each_song(song, *this) {
if ((filter == nullptr || filter->Match(*song)) && const LightSong song2 = ExportSong(*song);
!visit_song(*song, error)) if ((filter == nullptr || filter->Match(song2)) &&
!visit_song(song2, error))
return false; return false;
}
} }
if (visit_playlist) { if (visit_playlist) {

View File

@ -17,17 +17,17 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include "config.h" #include "LightSong.hxx"
#include "Song.hxx" #include "tag/Tag.hxx"
#include "directory.h"
#include "Compiler.h"
#include <stdlib.h> double
LightSong::GetDuration() const
struct directory detached_root;
Song *
song_dup_detached(gcc_unused const Song *src)
{ {
abort(); if (end_ms > 0)
return (end_ms - start_ms) / 1000.0;
if (tag->time <= 0)
return 0;
return tag->time - start_ms / 1000.0;
} }

82
src/LightSong.hxx Normal file
View File

@ -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

View File

@ -218,7 +218,7 @@ map_detached_song_fs(const char *uri_utf8)
} }
DetachedSong DetachedSong
map_song_detach(const Song &song) map_song_detach(const LightSong &song)
{ {
return DetachedSong(song); return DetachedSong(song);
} }

View File

@ -33,6 +33,7 @@
class AllocatedPath; class AllocatedPath;
struct Directory; struct Directory;
struct Song; struct Song;
struct LightSong;
class DetachedSong; class DetachedSong;
void void
@ -112,7 +113,7 @@ map_directory_child_fs(const Directory &directory, const char *name);
*/ */
gcc_pure gcc_pure
DetachedSong 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 * Determines the file system path of a song. This must not be a

View File

@ -29,7 +29,6 @@
#include "PlayerControl.hxx" #include "PlayerControl.hxx"
#include "util/UriUtil.hxx" #include "util/UriUtil.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "Song.hxx"
#include "DetachedSong.hxx" #include "DetachedSong.hxx"
#include "Mapper.hxx" #include "Mapper.hxx"
#include "Idle.hxx" #include "Idle.hxx"

View File

@ -30,12 +30,14 @@
#include "DatabasePlugin.hxx" #include "DatabasePlugin.hxx"
#include "Client.hxx" #include "Client.hxx"
#include "InputStream.hxx" #include "InputStream.hxx"
#include "Song.hxx"
#include "DetachedSong.hxx" #include "DetachedSong.hxx"
#include "fs/Traits.hxx" #include "fs/Traits.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "thread/Cond.hxx" #include "thread/Cond.hxx"
#define SONG_FILE "file: "
#define SONG_TIME "Time: "
void void
playlist_print_uris(Client &client, const playlist &playlist) playlist_print_uris(Client &client, const playlist &playlist)
{ {
@ -118,7 +120,7 @@ PrintSongDetails(Client &client, const char *uri_utf8)
if (db == nullptr) if (db == nullptr)
return false; return false;
Song *song = db->GetSong(uri_utf8, IgnoreError()); auto *song = db->GetSong(uri_utf8, IgnoreError());
if (song == nullptr) if (song == nullptr)
return false; return false;

View File

@ -22,7 +22,6 @@
#include "PlaylistFile.hxx" #include "PlaylistFile.hxx"
#include "PlaylistError.hxx" #include "PlaylistError.hxx"
#include "Playlist.hxx" #include "Playlist.hxx"
#include "Song.hxx"
#include "DetachedSong.hxx" #include "DetachedSong.hxx"
#include "Mapper.hxx" #include "Mapper.hxx"
#include "Idle.hxx" #include "Idle.hxx"

View File

@ -29,7 +29,6 @@
#include "util/UriUtil.hxx" #include "util/UriUtil.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "DetachedSong.hxx" #include "DetachedSong.hxx"
#include "Song.hxx"
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>

View File

@ -21,7 +21,7 @@
#include "Playlist.hxx" #include "Playlist.hxx"
#include "DatabaseGlue.hxx" #include "DatabaseGlue.hxx"
#include "DatabasePlugin.hxx" #include "DatabasePlugin.hxx"
#include "Song.hxx" #include "LightSong.hxx"
#include "DetachedSong.hxx" #include "DetachedSong.hxx"
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include "Idle.hxx" #include "Idle.hxx"
@ -35,7 +35,7 @@ UpdatePlaylistSong(const Database &db, DetachedSong &song)
from the Database */ from the Database */
return false; return false;
Song *original = db.GetSong(song.GetURI(), IgnoreError()); const LightSong *original = db.GetSong(song.GetURI(), IgnoreError());
if (original == nullptr) if (original == nullptr)
/* not found - shouldn't happen, because the update /* not found - shouldn't happen, because the update
thread should ensure that all stale Song instances thread should ensure that all stale Song instances
@ -49,7 +49,7 @@ UpdatePlaylistSong(const Database &db, DetachedSong &song)
} }
song.SetLastModified(original->mtime); song.SetLastModified(original->mtime);
song.SetTag(original->tag); song.SetTag(*original->tag);
db.ReturnSong(original); db.ReturnSong(original);
return true; return true;

View File

@ -23,6 +23,7 @@
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include "util/VarSize.hxx" #include "util/VarSize.hxx"
#include "DetachedSong.hxx" #include "DetachedSong.hxx"
#include "LightSong.hxx"
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
@ -94,14 +95,16 @@ Song::GetURI() const
} }
} }
double LightSong
Song::GetDuration() const Song::Export() const
{ {
if (end_ms > 0) LightSong dest;
return (end_ms - start_ms) / 1000.0; dest.directory = parent->IsRoot()
? nullptr : parent->GetPath();
if (tag.time <= 0) dest.uri = uri;
return 0; dest.tag = &tag;
dest.mtime = mtime;
return tag.time - start_ms / 1000.0; dest.start_ms = start_ms;
dest.end_ms = end_ms;
return dest;
} }

View File

@ -29,9 +29,7 @@
#include <assert.h> #include <assert.h>
#include <time.h> #include <time.h>
#define SONG_FILE "file: " struct LightSong;
#define SONG_TIME "Time: "
struct Directory; struct Directory;
class DetachedSong; class DetachedSong;
@ -112,7 +110,7 @@ struct Song {
std::string GetURI() const; std::string GetURI() const;
gcc_pure gcc_pure
double GetDuration() const; LightSong Export() const;
}; };
#endif #endif

View File

@ -20,6 +20,7 @@
#include "config.h" #include "config.h"
#include "SongFilter.hxx" #include "SongFilter.hxx"
#include "Song.hxx" #include "Song.hxx"
#include "LightSong.hxx"
#include "DetachedSong.hxx" #include "DetachedSong.hxx"
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
@ -137,7 +138,19 @@ SongFilter::Item::Match(const Tag &_tag) const
} }
bool 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) { if (tag == LOCATE_TAG_BASE_TYPE) {
const auto uri = song.GetURI(); const auto uri = song.GetURI();
@ -149,19 +162,7 @@ SongFilter::Item::Match(const Song &song) const
return StringMatch(uri.c_str()); return StringMatch(uri.c_str());
} }
return Match(song.tag); 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());
} }
SongFilter::SongFilter(unsigned tag, const char *value, bool fold_case) SongFilter::SongFilter(unsigned tag, const char *value, bool fold_case)
@ -207,7 +208,7 @@ SongFilter::Parse(unsigned argc, char *argv[], bool fold_case)
} }
bool bool
SongFilter::Match(const Song &song) const SongFilter::Match(const DetachedSong &song) const
{ {
for (const auto &i : items) for (const auto &i : items)
if (!i.Match(song)) if (!i.Match(song))
@ -217,7 +218,7 @@ SongFilter::Match(const Song &song) const
} }
bool bool
SongFilter::Match(const DetachedSong &song) const SongFilter::Match(const LightSong &song) const
{ {
for (const auto &i : items) for (const auto &i : items)
if (!i.Match(song)) if (!i.Match(song))

View File

@ -38,6 +38,7 @@
struct Tag; struct Tag;
struct TagItem; struct TagItem;
struct Song; struct Song;
struct LightSong;
class DetachedSong; class DetachedSong;
class SongFilter { class SongFilter {
@ -80,10 +81,10 @@ public:
bool Match(const Tag &tag) const; bool Match(const Tag &tag) const;
gcc_pure gcc_pure
bool Match(const Song &song) const; bool Match(const DetachedSong &song) const;
gcc_pure gcc_pure
bool Match(const DetachedSong &song) const; bool Match(const LightSong &song) const;
}; };
private: private:
@ -107,10 +108,10 @@ public:
bool Match(const Tag &tag) const; bool Match(const Tag &tag) const;
gcc_pure gcc_pure
bool Match(const Song &song) const; bool Match(const DetachedSong &song) const;
gcc_pure gcc_pure
bool Match(const DetachedSong &song) const; bool Match(const LightSong &song) const;
const std::list<Item> &GetItems() const { const std::list<Item> &GetItems() const {
return items; return items;

View File

@ -19,7 +19,7 @@
#include "config.h" #include "config.h"
#include "SongPrint.hxx" #include "SongPrint.hxx"
#include "Song.hxx" #include "LightSong.hxx"
#include "DetachedSong.hxx" #include "DetachedSong.hxx"
#include "Directory.hxx" #include "Directory.hxx"
#include "TimePrint.hxx" #include "TimePrint.hxx"
@ -28,6 +28,8 @@
#include "Client.hxx" #include "Client.hxx"
#include "util/UriUtil.hxx" #include "util/UriUtil.hxx"
#define SONG_FILE "file: "
static void static void
song_print_uri(Client &client, const char *uri) song_print_uri(Client &client, const char *uri)
{ {
@ -40,11 +42,11 @@ song_print_uri(Client &client, const char *uri)
} }
void 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, client_printf(client, "%s%s/%s\n", SONG_FILE,
song.parent->GetPath(), song.uri); song.directory, song.uri);
} else } else
song_print_uri(client, song.uri); song_print_uri(client, song.uri);
} }
@ -56,7 +58,7 @@ song_print_uri(Client &client, const DetachedSong &song)
} }
void void
song_print_info(Client &client, const Song &song) song_print_info(Client &client, const LightSong &song)
{ {
song_print_uri(client, song); song_print_uri(client, song);
@ -74,7 +76,7 @@ song_print_info(Client &client, const Song &song)
if (song.mtime > 0) if (song.mtime > 0)
time_print(client, "Last-Modified", song.mtime); time_print(client, "Last-Modified", song.mtime);
tag_print(client, song.tag); tag_print(client, *song.tag);
} }
void void

View File

@ -20,7 +20,7 @@
#ifndef MPD_SONG_PRINT_HXX #ifndef MPD_SONG_PRINT_HXX
#define MPD_SONG_PRINT_HXX #define MPD_SONG_PRINT_HXX
struct Song; struct LightSong;
class DetachedSong; class DetachedSong;
class Client; class Client;
@ -28,10 +28,10 @@ void
song_print_info(Client &client, const DetachedSong &song); song_print_info(Client &client, const DetachedSong &song);
void void
song_print_info(Client &client, const Song &song); song_print_info(Client &client, const LightSong &song);
void void
song_print_uri(Client &client, const Song &song); song_print_uri(Client &client, const LightSong &song);
void void
song_print_uri(Client &client, const DetachedSong &song); song_print_uri(Client &client, const DetachedSong &song);

View File

@ -20,6 +20,7 @@
#include "config.h" #include "config.h"
#include "SongSticker.hxx" #include "SongSticker.hxx"
#include "StickerDatabase.hxx" #include "StickerDatabase.hxx"
#include "LightSong.hxx"
#include "Song.hxx" #include "Song.hxx"
#include "Directory.hxx" #include "Directory.hxx"
@ -29,14 +30,14 @@
#include <string.h> #include <string.h>
std::string 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(); const auto uri = song.GetURI();
return sticker_load_value("song", uri.c_str(), name); return sticker_load_value("song", uri.c_str(), name);
} }
bool bool
sticker_song_set_value(const Song &song, sticker_song_set_value(const LightSong &song,
const char *name, const char *value) const char *name, const char *value)
{ {
const auto uri = song.GetURI(); const auto uri = song.GetURI();
@ -44,21 +45,21 @@ sticker_song_set_value(const Song &song,
} }
bool bool
sticker_song_delete(const Song &song) sticker_song_delete(const LightSong &song)
{ {
const auto uri = song.GetURI(); const auto uri = song.GetURI();
return sticker_delete("song", uri.c_str()); return sticker_delete("song", uri.c_str());
} }
bool 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(); const auto uri = song.GetURI();
return sticker_delete_value("song", uri.c_str(), name); return sticker_delete_value("song", uri.c_str(), name);
} }
struct sticker * struct sticker *
sticker_song_get(const Song &song) sticker_song_get(const LightSong &song)
{ {
const auto uri = song.GetURI(); const auto uri = song.GetURI();
return sticker_load("song", uri.c_str()); return sticker_load("song", uri.c_str());
@ -69,7 +70,7 @@ struct sticker_song_find_data {
const char *base_uri; const char *base_uri;
size_t base_uri_length; 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);
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); Song *song = data->directory->LookupSong(uri + data->base_uri_length);
if (song != nullptr) if (song != nullptr)
data->func(*song, value, data->user_data); data->func(song->Export(), value, data->user_data);
} }
bool bool
sticker_song_find(Directory &directory, const char *name, 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),
void *user_data) void *user_data)
{ {

View File

@ -24,7 +24,7 @@
#include <string> #include <string>
struct Song; struct LightSong;
struct Directory; struct Directory;
struct sticker; struct sticker;
@ -34,28 +34,28 @@ struct sticker;
*/ */
gcc_pure gcc_pure
std::string 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 * Sets a sticker value in the specified song. Overwrites existing
* values. * values.
*/ */
bool bool
sticker_song_set_value(const Song &song, sticker_song_set_value(const LightSong &song,
const char *name, const char *value); const char *name, const char *value);
/** /**
* Deletes a sticker from the database. All values are deleted. * Deletes a sticker from the database. All values are deleted.
*/ */
bool bool
sticker_song_delete(const Song &song); sticker_song_delete(const LightSong &song);
/** /**
* Deletes a sticker value. Does nothing if the sticker did not * Deletes a sticker value. Does nothing if the sticker did not
* exist. * exist.
*/ */
bool 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. * 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 * @return a sticker object, or NULL on error or if there is no sticker
*/ */
sticker * sticker *
sticker_song_get(const Song &song); sticker_song_get(const LightSong &song);
/** /**
* Finds stickers with the specified name below the specified * Finds stickers with the specified name below the specified
@ -79,7 +79,7 @@ sticker_song_get(const Song &song);
*/ */
bool bool
sticker_song_find(Directory &directory, const char *name, 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),
void *user_data); void *user_data);

View File

@ -21,9 +21,10 @@
#include "TagPrint.hxx" #include "TagPrint.hxx"
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include "tag/TagSettings.h" #include "tag/TagSettings.h"
#include "Song.hxx"
#include "Client.hxx" #include "Client.hxx"
#define SONG_TIME "Time: "
void tag_print_types(Client &client) void tag_print_types(Client &client)
{ {
int i; int i;

View File

@ -20,7 +20,8 @@
#include "config.h" #include "config.h"
#include "TagSave.hxx" #include "TagSave.hxx"
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include "Song.hxx"
#define SONG_TIME "Time: "
void void
tag_save(FILE *file, const Tag &tag) tag_save(FILE *file, const Tag &tag)

View File

@ -24,6 +24,7 @@
#include "thread/Mutex.hxx" #include "thread/Mutex.hxx"
#include "thread/Cond.hxx" #include "thread/Cond.hxx"
#include "Song.hxx" #include "Song.hxx"
#include "LightSong.hxx"
#include "Main.hxx" #include "Main.hxx"
#include "Instance.hxx" #include "Instance.hxx"
#include "Log.hxx" #include "Log.hxx"
@ -57,7 +58,7 @@ song_remove_event(void)
#ifdef ENABLE_SQLITE #ifdef ENABLE_SQLITE
/* if the song has a sticker, remove it */ /* if the song has a sticker, remove it */
if (sticker_enabled()) if (sticker_enabled())
sticker_song_delete(*removed_song); sticker_song_delete(removed_song->Export());
#endif #endif
{ {

View File

@ -39,7 +39,7 @@ struct sticker_song_find_data {
}; };
static void 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) void *user_data)
{ {
struct sticker_song_find_data *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 */ /* get song song_id key */
if (argc == 5 && strcmp(argv[1], "get") == 0) { 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) if (song == nullptr)
return print_error(client, error); return print_error(client, error);
@ -76,7 +76,7 @@ handle_sticker_song(Client &client, int argc, char *argv[])
return CommandResult::OK; return CommandResult::OK;
/* list song song_id */ /* list song song_id */
} else if (argc == 4 && strcmp(argv[1], "list") == 0) { } 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) if (song == nullptr)
return print_error(client, error); return print_error(client, error);
@ -90,7 +90,7 @@ handle_sticker_song(Client &client, int argc, char *argv[])
return CommandResult::OK; return CommandResult::OK;
/* set song song_id id key */ /* set song song_id id key */
} else if (argc == 6 && strcmp(argv[1], "set") == 0) { } 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) if (song == nullptr)
return print_error(client, error); return print_error(client, error);
@ -106,7 +106,7 @@ handle_sticker_song(Client &client, int argc, char *argv[])
/* delete song song_id [key] */ /* delete song song_id [key] */
} else if ((argc == 4 || argc == 5) && } else if ((argc == 4 || argc == 5) &&
strcmp(argv[1], "delete") == 0) { strcmp(argv[1], "delete") == 0) {
Song *song = db->GetSong(argv[3], error); const LightSong *song = db->GetSong(argv[3], error);
if (song == nullptr) if (song == nullptr)
return print_error(client, error); return print_error(client, error);

View File

@ -51,7 +51,7 @@ LazyDatabase::Close()
} }
} }
Song * const LightSong *
LazyDatabase::GetSong(const char *uri, Error &error) const LazyDatabase::GetSong(const char *uri, Error &error) const
{ {
return EnsureOpen(error) return EnsureOpen(error)
@ -60,7 +60,7 @@ LazyDatabase::GetSong(const char *uri, Error &error) const
} }
void void
LazyDatabase::ReturnSong(Song *song) const LazyDatabase::ReturnSong(const LightSong *song) const
{ {
assert(open); assert(open);

View File

@ -41,9 +41,9 @@ public:
virtual void Close() override; virtual void Close() override;
virtual Song *GetSong(const char *uri_utf8, virtual const LightSong *GetSong(const char *uri_utf8,
Error &error) const override; Error &error) const override;
virtual void ReturnSong(Song *song) const; virtual void ReturnSong(const LightSong *song) const;
virtual bool Visit(const DatabaseSelection &selection, virtual bool Visit(const DatabaseSelection &selection,
VisitDirectory visit_directory, VisitDirectory visit_directory,

View File

@ -24,11 +24,12 @@
#include "DatabaseSelection.hxx" #include "DatabaseSelection.hxx"
#include "DatabaseError.hxx" #include "DatabaseError.hxx"
#include "Directory.hxx" #include "Directory.hxx"
#include "Song.hxx" #include "LightSong.hxx"
#include "SongFilter.hxx" #include "SongFilter.hxx"
#include "Compiler.h" #include "Compiler.h"
#include "ConfigData.hxx" #include "ConfigData.hxx"
#include "tag/TagBuilder.hxx" #include "tag/TagBuilder.hxx"
#include "tag/Tag.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "protocol/Ack.hxx" #include "protocol/Ack.hxx"
@ -44,6 +45,25 @@
#include <string> #include <string>
#include <list> #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 { class ProxyDatabase final : public Database, SocketMonitor, IdleMonitor {
DatabaseListener &listener; DatabaseListener &listener;
@ -79,9 +99,9 @@ public:
virtual bool Open(Error &error) override; virtual bool Open(Error &error) override;
virtual void Close() override; virtual void Close() override;
virtual Song *GetSong(const char *uri_utf8, virtual const LightSong *GetSong(const char *uri_utf8,
Error &error) const override; Error &error) const override;
virtual void ReturnSong(Song *song) const; virtual void ReturnSong(const LightSong *song) const;
virtual bool Visit(const DatabaseSelection &selection, virtual bool Visit(const DatabaseSelection &selection,
VisitDirectory visit_directory, VisitDirectory visit_directory,
@ -144,6 +164,38 @@ static constexpr struct {
{ TAG_NUM_OF_ITEM_TYPES, MPD_TAG_COUNT } { 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 gcc_const
static enum mpd_tag_type static enum mpd_tag_type
Convert(TagType tag_type) Convert(TagType tag_type)
@ -424,10 +476,7 @@ ProxyDatabase::OnIdle()
SocketMonitor::ScheduleRead(); SocketMonitor::ScheduleRead();
} }
static Song * const LightSong *
Convert(const struct mpd_song *song);
Song *
ProxyDatabase::GetSong(const char *uri, Error &error) const ProxyDatabase::GetSong(const char *uri, Error &error) const
{ {
// TODO: eliminate the const_cast // TODO: eliminate the const_cast
@ -452,18 +501,17 @@ ProxyDatabase::GetSong(const char *uri, Error &error) const
return nullptr; return nullptr;
} }
Song *song2 = Convert(song); return new AllocatedProxySong(song);
mpd_song_free(song);
return song2;
} }
void void
ProxyDatabase::ReturnSong(Song *song) const ProxyDatabase::ReturnSong(const LightSong *_song) const
{ {
assert(song != nullptr); assert(_song != nullptr);
assert(song->parent == nullptr);
song->Free(); AllocatedProxySong *song = (AllocatedProxySong *)
const_cast<LightSong *>(_song);
delete song;
} }
static bool static bool
@ -493,60 +541,23 @@ Visit(struct mpd_connection *connection, Directory &root,
return true; 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 gcc_pure
static bool static bool
Match(const SongFilter *filter, const Song &song) Match(const SongFilter *filter, const LightSong &song)
{ {
return filter == nullptr || filter->Match(song); return filter == nullptr || filter->Match(song);
} }
static bool static bool
Visit(const SongFilter *filter, Visit(const SongFilter *filter,
const struct mpd_song *song, const mpd_song *_song,
VisitSong visit_song, Error &error) VisitSong visit_song, Error &error)
{ {
if (!visit_song) if (!visit_song)
return true; return true;
Song *s = Convert(song); const ProxySong song(_song);
bool success = !Match(filter, *s) || visit_song(*s, error); return !Match(filter, song) || visit_song(song, error);
s->Free();
return success;
} }
static bool static bool
@ -664,12 +675,10 @@ SearchSongs(struct mpd_connection *connection,
bool result = true; bool result = true;
struct mpd_song *song; struct mpd_song *song;
while (result && (song = mpd_recv_song(connection)) != nullptr) { while (result && (song = mpd_recv_song(connection)) != nullptr) {
Song *song2 = Convert(song); AllocatedProxySong song2(song);
mpd_song_free(song);
result = !Match(selection.filter, *song2) || result = !Match(selection.filter, song2) ||
visit_song(*song2, error); visit_song(song2, error);
song2->Free();
} }
mpd_response_finish(connection); mpd_response_finish(connection);

View File

@ -22,6 +22,7 @@
#include "DatabaseSelection.hxx" #include "DatabaseSelection.hxx"
#include "DatabaseHelpers.hxx" #include "DatabaseHelpers.hxx"
#include "Directory.hxx" #include "Directory.hxx"
#include "Song.hxx"
#include "SongFilter.hxx" #include "SongFilter.hxx"
#include "DatabaseSave.hxx" #include "DatabaseSave.hxx"
#include "DatabaseLock.hxx" #include "DatabaseLock.hxx"
@ -193,29 +194,34 @@ SimpleDatabase::Close()
delete root; delete root;
} }
Song * const LightSong *
SimpleDatabase::GetSong(const char *uri, Error &error) const SimpleDatabase::GetSong(const char *uri, Error &error) const
{ {
assert(root != nullptr); assert(root != nullptr);
assert(borrowed_song_count == 0);
db_lock(); db_lock();
Song *song = root->LookupSong(uri); const Song *song = root->LookupSong(uri);
db_unlock(); db_unlock();
if (song == nullptr) if (song == nullptr) {
error.Format(db_domain, DB_NOT_FOUND, error.Format(db_domain, DB_NOT_FOUND,
"No such song: %s", uri); "No such song: %s", uri);
return nullptr;
}
light_song = song->Export();
#ifndef NDEBUG #ifndef NDEBUG
else ++borrowed_song_count;
++borrowed_song_count;
#endif #endif
return song; return &light_song;
} }
void 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 #ifndef NDEBUG
assert(borrowed_song_count > 0); assert(borrowed_song_count > 0);
@ -247,9 +253,11 @@ SimpleDatabase::Visit(const DatabaseSelection &selection,
if (directory == nullptr) { if (directory == nullptr) {
if (visit_song) { if (visit_song) {
Song *song = root->LookupSong(selection.uri.c_str()); Song *song = root->LookupSong(selection.uri.c_str());
if (song != nullptr) if (song != nullptr) {
return !selection.Match(*song) || const LightSong song2 = song->Export();
visit_song(*song, error); return !selection.Match(song2) ||
visit_song(song2, error);
}
} }
error.Set(db_domain, DB_NOT_FOUND, "No such directory"); error.Set(db_domain, DB_NOT_FOUND, "No such directory");

View File

@ -22,6 +22,7 @@
#include "DatabasePlugin.hxx" #include "DatabasePlugin.hxx"
#include "fs/AllocatedPath.hxx" #include "fs/AllocatedPath.hxx"
#include "LightSong.hxx"
#include "Compiler.h" #include "Compiler.h"
#include <cassert> #include <cassert>
@ -36,6 +37,11 @@ class SimpleDatabase : public Database {
time_t mtime; time_t mtime;
/**
* A buffer for GetSong().
*/
mutable LightSong light_song;
#ifndef NDEBUG #ifndef NDEBUG
mutable unsigned borrowed_song_count; mutable unsigned borrowed_song_count;
#endif #endif
@ -60,9 +66,9 @@ public:
virtual bool Open(Error &error) override; virtual bool Open(Error &error) override;
virtual void Close() override; virtual void Close() override;
virtual Song *GetSong(const char *uri_utf8, virtual const LightSong *GetSong(const char *uri_utf8,
Error &error) const override; Error &error) const override;
virtual void ReturnSong(Song *song) const; virtual void ReturnSong(const LightSong *song) const;
virtual bool Visit(const DatabaseSelection &selection, virtual bool Visit(const DatabaseSelection &selection,
VisitDirectory visit_directory, VisitDirectory visit_directory,

View File

@ -31,7 +31,7 @@
#include "DatabaseError.hxx" #include "DatabaseError.hxx"
#include "PlaylistVector.hxx" #include "PlaylistVector.hxx"
#include "Directory.hxx" #include "Directory.hxx"
#include "Song.hxx" #include "LightSong.hxx"
#include "ConfigData.hxx" #include "ConfigData.hxx"
#include "tag/TagBuilder.hxx" #include "tag/TagBuilder.hxx"
#include "tag/TagTable.hxx" #include "tag/TagTable.hxx"
@ -49,6 +49,31 @@
static const char *const rootid = "0"; 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 { class UpnpDatabase : public Database {
LibUPnP *m_lib; LibUPnP *m_lib;
UPnPDeviceDirectory *m_superdir; UPnPDeviceDirectory *m_superdir;
@ -61,9 +86,9 @@ public:
virtual bool Open(Error &error) override; virtual bool Open(Error &error) override;
virtual void Close() override; virtual void Close() override;
virtual Song *GetSong(const char *uri_utf8, virtual const LightSong *GetSong(const char *uri_utf8,
Error &error) const override; Error &error) const override;
virtual void ReturnSong(Song *song) const; virtual void ReturnSong(const LightSong *song) const;
virtual bool Visit(const DatabaseSelection &selection, virtual bool Visit(const DatabaseSelection &selection,
VisitDirectory visit_directory, VisitDirectory visit_directory,
@ -187,34 +212,20 @@ UpnpDatabase::Close()
} }
void void
UpnpDatabase::ReturnSong(Song *song) const UpnpDatabase::ReturnSong(const LightSong *_song) const
{ {
assert(song != nullptr); assert(_song != nullptr);
song->Free(); UpnpSong *song = (UpnpSong *)const_cast<LightSong *>(_song);
} delete song;
// 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)
{
if (*uri == 0)
uri = dirent.url.c_str();
Song *s = Song::NewFile(uri, nullptr);
s->tag = std::move(dirent.tag);
return s;
} }
// Get song info by path. We can receive either the id path, or the titles // Get song info by path. We can receive either the id path, or the titles
// one // one
Song * const LightSong *
UpnpDatabase::GetSong(const char *uri, Error &error) const UpnpDatabase::GetSong(const char *uri, Error &error) const
{ {
Song *song = nullptr; UpnpSong *song = nullptr;
auto vpath = stringToTokens(uri, "/", true); auto vpath = stringToTokens(uri, "/", true);
if (vpath.size() >= 2) { if (vpath.size() >= 2) {
ContentDirectoryService server; ContentDirectoryService server;
@ -232,7 +243,8 @@ UpnpDatabase::GetSong(const char *uri, Error &error) const
error)) error))
return nullptr; return nullptr;
} }
song = upnpItemToSong(std::move(dirent), "");
song = new UpnpSong(std::move(dirent));
} }
if (song == nullptr) if (song == nullptr)
error.Format(db_domain, DB_NOT_FOUND, "No such song: %s", uri); 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) if (!visit_song)
return true; return true;
Song *s = upnpItemToSong(std::move(meta), path);
if (!selection.Match(*s)) const UpnpSong song(std::move(meta), path);
return true; return !selection.Match(song) || visit_song(song, error);
bool success = visit_song(*s, error);
s->Free();
return success;
} }
/** /**

View File

@ -21,7 +21,6 @@
#include "AsxPlaylistPlugin.hxx" #include "AsxPlaylistPlugin.hxx"
#include "PlaylistPlugin.hxx" #include "PlaylistPlugin.hxx"
#include "MemorySongEnumerator.hxx" #include "MemorySongEnumerator.hxx"
#include "Song.hxx"
#include "tag/TagBuilder.hxx" #include "tag/TagBuilder.hxx"
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"

View File

@ -21,7 +21,6 @@
#include "RssPlaylistPlugin.hxx" #include "RssPlaylistPlugin.hxx"
#include "PlaylistPlugin.hxx" #include "PlaylistPlugin.hxx"
#include "MemorySongEnumerator.hxx" #include "MemorySongEnumerator.hxx"
#include "Song.hxx"
#include "tag/TagBuilder.hxx" #include "tag/TagBuilder.hxx"
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"

View File

@ -23,7 +23,6 @@
#include "MemorySongEnumerator.hxx" #include "MemorySongEnumerator.hxx"
#include "ConfigData.hxx" #include "ConfigData.hxx"
#include "InputStream.hxx" #include "InputStream.hxx"
#include "Song.hxx"
#include "tag/TagBuilder.hxx" #include "tag/TagBuilder.hxx"
#include "util/StringUtil.hxx" #include "util/StringUtil.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"

View File

@ -23,7 +23,7 @@
#include "DatabaseSelection.hxx" #include "DatabaseSelection.hxx"
#include "DatabaseListener.hxx" #include "DatabaseListener.hxx"
#include "Directory.hxx" #include "Directory.hxx"
#include "Song.hxx" #include "LightSong.hxx"
#include "PlaylistVector.hxx" #include "PlaylistVector.hxx"
#include "ConfigGlobal.hxx" #include "ConfigGlobal.hxx"
#include "ConfigData.hxx" #include "ConfigData.hxx"
@ -65,11 +65,11 @@ DumpDirectory(const Directory &directory, Error &)
} }
static bool static bool
DumpSong(Song &song, Error &) DumpSong(const LightSong &song, Error &)
{ {
cout << "S "; cout << "S ";
if (song.parent != nullptr && !song.parent->IsRoot()) if (song.directory != nullptr)
cout << song.parent->path << "/"; cout << song.directory << "/";
cout << song.uri << endl; cout << song.uri << endl;
return true; return true;
} }