Commit f5ae1ce0 authored by Max Kellermann's avatar Max Kellermann

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.
parent 738d6f10
...@@ -196,6 +196,7 @@ src_mpd_SOURCES = \ ...@@ -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 \
......
...@@ -19,7 +19,7 @@ ...@@ -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 { ...@@ -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, ...@@ -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;
} }
......
...@@ -30,7 +30,7 @@ ...@@ -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);
......
...@@ -35,7 +35,7 @@ ...@@ -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: ...@@ -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.
......
...@@ -26,7 +26,7 @@ ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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);
......
...@@ -29,7 +29,7 @@ ...@@ -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,
......
...@@ -31,7 +31,7 @@ DatabaseSelection::DatabaseSelection(const char *_uri, bool _recursive, ...@@ -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);
} }
...@@ -25,7 +25,7 @@ ...@@ -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 { ...@@ -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
...@@ -31,7 +31,7 @@ DatabaseDetachSong(const char *uri, Error &error) ...@@ -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;
......
...@@ -23,12 +23,12 @@ ...@@ -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;
......
...@@ -19,13 +19,13 @@ ...@@ -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) {}
......
...@@ -29,10 +29,10 @@ ...@@ -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 { ...@@ -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)
......
...@@ -24,6 +24,7 @@ ...@@ -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() ...@@ -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, ...@@ -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) {
......
...@@ -17,17 +17,17 @@ ...@@ -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
{
if (end_ms > 0)
return (end_ms - start_ms) / 1000.0;
struct directory detached_root; if (tag->time <= 0)
return 0;
Song * return tag->time - start_ms / 1000.0;
song_dup_detached(gcc_unused const Song *src)
{
abort();
} }
/*
* 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
...@@ -218,7 +218,7 @@ map_detached_song_fs(const char *uri_utf8) ...@@ -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);
} }
......
...@@ -33,6 +33,7 @@ ...@@ -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); ...@@ -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
......
...@@ -29,7 +29,6 @@ ...@@ -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"
......
...@@ -30,12 +30,14 @@ ...@@ -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) ...@@ -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;
......
...@@ -22,7 +22,6 @@ ...@@ -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"
......
...@@ -29,7 +29,6 @@ ...@@ -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>
......
...@@ -21,7 +21,7 @@ ...@@ -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) ...@@ -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) ...@@ -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;
......
...@@ -23,6 +23,7 @@ ...@@ -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 ...@@ -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;
} }
...@@ -29,9 +29,7 @@ ...@@ -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 { ...@@ -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
...@@ -20,6 +20,7 @@ ...@@ -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 ...@@ -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 ...@@ -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) ...@@ -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 ...@@ -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))
......
...@@ -38,6 +38,7 @@ ...@@ -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: ...@@ -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: ...@@ -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;
......
...@@ -19,7 +19,7 @@ ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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
......
...@@ -20,7 +20,7 @@ ...@@ -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 ...@@ -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);
......
...@@ -20,6 +20,7 @@ ...@@ -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 @@ ...@@ -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, ...@@ -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 { ...@@ -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) ...@@ -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)
{ {
......
...@@ -24,7 +24,7 @@ ...@@ -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; ...@@ -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); ...@@ -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); ...@@ -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);
......
...@@ -21,9 +21,10 @@ ...@@ -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;
......
...@@ -20,7 +20,8 @@ ...@@ -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)
......
...@@ -24,6 +24,7 @@ ...@@ -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) ...@@ -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
{ {
......
...@@ -39,7 +39,7 @@ struct sticker_song_find_data { ...@@ -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[]) ...@@ -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[]) ...@@ -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[]) ...@@ -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[]) ...@@ -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);
......
...@@ -51,7 +51,7 @@ LazyDatabase::Close() ...@@ -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 ...@@ -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);
......
...@@ -41,9 +41,9 @@ public: ...@@ -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,
......
...@@ -24,11 +24,12 @@ ...@@ -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 @@ ...@@ -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: ...@@ -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 { ...@@ -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() ...@@ -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 ...@@ -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, ...@@ -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, ...@@ -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);
......
...@@ -22,6 +22,7 @@ ...@@ -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() ...@@ -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, ...@@ -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");
......
...@@ -22,6 +22,7 @@ ...@@ -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 { ...@@ -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: ...@@ -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,
......
...@@ -31,7 +31,7 @@ ...@@ -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 @@ ...@@ -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: ...@@ -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() ...@@ -187,34 +212,20 @@ UpnpDatabase::Close()
} }
void void
UpnpDatabase::ReturnSong(Song *song) const UpnpDatabase::ReturnSong(const LightSong *_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)
{ {
if (*uri == 0) assert(_song != nullptr);
uri = dirent.url.c_str();
Song *s = Song::NewFile(uri, nullptr); UpnpSong *song = (UpnpSong *)const_cast<LightSong *>(_song);
s->tag = std::move(dirent.tag); delete song;
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 ...@@ -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, ...@@ -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;
} }
/** /**
......
...@@ -21,7 +21,6 @@ ...@@ -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"
......
...@@ -21,7 +21,6 @@ ...@@ -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"
......
...@@ -23,7 +23,6 @@ ...@@ -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"
......
...@@ -23,7 +23,7 @@ ...@@ -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 &) ...@@ -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;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment