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/SignalHandlers.cxx src/SignalHandlers.hxx \
src/DetachedSong.cxx src/DetachedSong.hxx \
src/LightSong.cxx src/LightSong.hxx \
src/Song.cxx src/Song.hxx \
src/SongUpdate.cxx \
src/SongPrint.cxx src/SongPrint.hxx \

View File

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

View File

@ -30,7 +30,7 @@
static bool
AddSong(const char *playlist_path_utf8,
Song &song, Error &error)
const LightSong &song, Error &error)
{
return spl_append_song(playlist_path_utf8, map_song_detach(song),
error);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,6 +24,7 @@
#include "DatabaseLock.hxx"
#include "SongSort.hxx"
#include "Song.hxx"
#include "LightSong.hxx"
#include "fs/Traits.hxx"
#include "util/Alloc.hxx"
#include "util/Error.hxx"
@ -251,6 +252,20 @@ Directory::Sort()
child->Sort();
}
static LightSong
ExportSong(const Song &src)
{
LightSong dest;
dest.directory = src.parent->IsRoot()
? nullptr : src.parent->GetPath();
dest.uri = src.uri;
dest.tag = &src.tag;
dest.mtime = src.mtime;
dest.start_ms = src.start_ms;
dest.end_ms = src.end_ms;
return dest;
}
bool
Directory::Walk(bool recursive, const SongFilter *filter,
VisitDirectory visit_directory, VisitSong visit_song,
@ -261,10 +276,12 @@ Directory::Walk(bool recursive, const SongFilter *filter,
if (visit_song) {
Song *song;
directory_for_each_song(song, *this)
if ((filter == nullptr || filter->Match(*song)) &&
!visit_song(*song, error))
directory_for_each_song(song, *this) {
const LightSong song2 = ExportSong(*song);
if ((filter == nullptr || filter->Match(song2)) &&
!visit_song(song2, error))
return false;
}
}
if (visit_playlist) {

View File

@ -17,17 +17,17 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "Song.hxx"
#include "directory.h"
#include "Compiler.h"
#include "LightSong.hxx"
#include "tag/Tag.hxx"
#include <stdlib.h>
struct directory detached_root;
Song *
song_dup_detached(gcc_unused const Song *src)
double
LightSong::GetDuration() const
{
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
map_song_detach(const Song &song)
map_song_detach(const LightSong &song)
{
return DetachedSong(song);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,11 +24,12 @@
#include "DatabaseSelection.hxx"
#include "DatabaseError.hxx"
#include "Directory.hxx"
#include "Song.hxx"
#include "LightSong.hxx"
#include "SongFilter.hxx"
#include "Compiler.h"
#include "ConfigData.hxx"
#include "tag/TagBuilder.hxx"
#include "tag/Tag.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "protocol/Ack.hxx"
@ -44,6 +45,25 @@
#include <string>
#include <list>
class ProxySong : public LightSong {
Tag tag2;
public:
explicit ProxySong(const mpd_song *song);
};
class AllocatedProxySong : public ProxySong {
mpd_song *const song;
public:
explicit AllocatedProxySong(mpd_song *_song)
:ProxySong(_song), song(_song) {}
~AllocatedProxySong() {
mpd_song_free(song);
}
};
class ProxyDatabase final : public Database, SocketMonitor, IdleMonitor {
DatabaseListener &listener;
@ -79,9 +99,9 @@ public:
virtual bool Open(Error &error) override;
virtual void Close() override;
virtual Song *GetSong(const char *uri_utf8,
virtual const LightSong *GetSong(const char *uri_utf8,
Error &error) const override;
virtual void ReturnSong(Song *song) const;
virtual void ReturnSong(const LightSong *song) const;
virtual bool Visit(const DatabaseSelection &selection,
VisitDirectory visit_directory,
@ -144,6 +164,38 @@ static constexpr struct {
{ TAG_NUM_OF_ITEM_TYPES, MPD_TAG_COUNT }
};
static void
Copy(TagBuilder &tag, TagType d_tag,
const struct mpd_song *song, enum mpd_tag_type s_tag)
{
for (unsigned i = 0;; ++i) {
const char *value = mpd_song_get_tag(song, s_tag, i);
if (value == nullptr)
break;
tag.AddItem(d_tag, value);
}
}
ProxySong::ProxySong(const mpd_song *song)
{
directory = nullptr;
uri = mpd_song_get_uri(song);
tag = &tag2;
mtime = mpd_song_get_last_modified(song);
start_ms = mpd_song_get_start(song) * 1000;
end_ms = mpd_song_get_end(song) * 1000;
TagBuilder tag_builder;
tag_builder.SetTime(mpd_song_get_duration(song));
for (const auto *i = &tag_table[0]; i->d != TAG_NUM_OF_ITEM_TYPES; ++i)
Copy(tag_builder, i->d, song, i->s);
tag_builder.Commit(tag2);
}
gcc_const
static enum mpd_tag_type
Convert(TagType tag_type)
@ -424,10 +476,7 @@ ProxyDatabase::OnIdle()
SocketMonitor::ScheduleRead();
}
static Song *
Convert(const struct mpd_song *song);
Song *
const LightSong *
ProxyDatabase::GetSong(const char *uri, Error &error) const
{
// TODO: eliminate the const_cast
@ -452,18 +501,17 @@ ProxyDatabase::GetSong(const char *uri, Error &error) const
return nullptr;
}
Song *song2 = Convert(song);
mpd_song_free(song);
return song2;
return new AllocatedProxySong(song);
}
void
ProxyDatabase::ReturnSong(Song *song) const
ProxyDatabase::ReturnSong(const LightSong *_song) const
{
assert(song != nullptr);
assert(song->parent == nullptr);
assert(_song != nullptr);
song->Free();
AllocatedProxySong *song = (AllocatedProxySong *)
const_cast<LightSong *>(_song);
delete song;
}
static bool
@ -493,60 +541,23 @@ Visit(struct mpd_connection *connection, Directory &root,
return true;
}
static void
Copy(TagBuilder &tag, TagType d_tag,
const struct mpd_song *song, enum mpd_tag_type s_tag)
{
for (unsigned i = 0;; ++i) {
const char *value = mpd_song_get_tag(song, s_tag, i);
if (value == nullptr)
break;
tag.AddItem(d_tag, value);
}
}
static Song *
Convert(const struct mpd_song *song)
{
Song *s = Song::NewFile(mpd_song_get_uri(song), nullptr);
s->mtime = mpd_song_get_last_modified(song);
s->start_ms = mpd_song_get_start(song) * 1000;
s->end_ms = mpd_song_get_end(song) * 1000;
TagBuilder tag;
tag.SetTime(mpd_song_get_duration(song));
for (const auto *i = &tag_table[0]; i->d != TAG_NUM_OF_ITEM_TYPES; ++i)
Copy(tag, i->d, song, i->s);
tag.Commit(s->tag);
return s;
}
gcc_pure
static bool
Match(const SongFilter *filter, const Song &song)
Match(const SongFilter *filter, const LightSong &song)
{
return filter == nullptr || filter->Match(song);
}
static bool
Visit(const SongFilter *filter,
const struct mpd_song *song,
const mpd_song *_song,
VisitSong visit_song, Error &error)
{
if (!visit_song)
return true;
Song *s = Convert(song);
bool success = !Match(filter, *s) || visit_song(*s, error);
s->Free();
return success;
const ProxySong song(_song);
return !Match(filter, song) || visit_song(song, error);
}
static bool
@ -664,12 +675,10 @@ SearchSongs(struct mpd_connection *connection,
bool result = true;
struct mpd_song *song;
while (result && (song = mpd_recv_song(connection)) != nullptr) {
Song *song2 = Convert(song);
mpd_song_free(song);
AllocatedProxySong song2(song);
result = !Match(selection.filter, *song2) ||
visit_song(*song2, error);
song2->Free();
result = !Match(selection.filter, song2) ||
visit_song(song2, error);
}
mpd_response_finish(connection);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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