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 = \
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 \
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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.
......
......@@ -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);
......
......@@ -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,
......
......@@ -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);
}
......@@ -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
......@@ -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;
......
......@@ -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;
......
......@@ -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) {}
......
......@@ -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)
......
......@@ -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) {
......
......@@ -17,17 +17,17 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "Song.hxx"
#include "directory.h"
#include "Compiler.h"
#include "LightSong.hxx"
#include "tag/Tag.hxx"
#include <stdlib.h>
double
LightSong::GetDuration() const
{
if (end_ms > 0)
return (end_ms - start_ms) / 1000.0;
struct directory detached_root;
if (tag->time <= 0)
return 0;
Song *
song_dup_detached(gcc_unused const Song *src)
{
abort();
return tag->time - start_ms / 1000.0;
}
/*
* 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)
}
DetachedSong
map_song_detach(const Song &song)
map_song_detach(const LightSong &song)
{
return DetachedSong(song);
}
......
......@@ -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
......
......@@ -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"
......
......@@ -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;
......
......@@ -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"
......
......@@ -29,7 +29,6 @@
#include "util/UriUtil.hxx"
#include "util/Error.hxx"
#include "DetachedSong.hxx"
#include "Song.hxx"
#include <assert.h>
#include <string.h>
......
......@@ -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;
......
......@@ -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;
}
......@@ -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
......@@ -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))
......
......@@ -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;
......
......@@ -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
......
......@@ -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);
......
......@@ -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)
{
......
......@@ -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);
......
......@@ -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;
......
......@@ -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)
......
......@@ -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
{
......
......@@ -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);
......
......@@ -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);
......
......@@ -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,
......
......@@ -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);
......
......@@ -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");
......
......@@ -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,
......
......@@ -31,7 +31,7 @@
#include "DatabaseError.hxx"
#include "PlaylistVector.hxx"
#include "Directory.hxx"
#include "Song.hxx"
#include "LightSong.hxx"
#include "ConfigData.hxx"
#include "tag/TagBuilder.hxx"
#include "tag/TagTable.hxx"
......@@ -49,6 +49,31 @@
static const char *const rootid = "0";
class UpnpSong : public LightSong {
std::string uri2;
Tag tag2;
public:
explicit UpnpSong(UPnPDirObject &&object)
:uri2(std::move(object.url)), tag2(std::move(object.tag)) {
directory = nullptr;
uri = uri2.c_str();
tag = &tag2;
mtime = 0;
start_ms = end_ms = 0;
}
UpnpSong(UPnPDirObject &&object, const char *_uri)
:uri2(_uri), tag2(std::move(object.tag)) {
directory = nullptr;
uri = uri2.c_str();
tag = &tag2;
mtime = 0;
start_ms = end_ms = 0;
}
};
class UpnpDatabase : public Database {
LibUPnP *m_lib;
UPnPDeviceDirectory *m_superdir;
......@@ -61,9 +86,9 @@ public:
virtual bool Open(Error &error) override;
virtual void Close() override;
virtual Song *GetSong(const char *uri_utf8,
Error &error) const override;
virtual void ReturnSong(Song *song) const;
virtual const LightSong *GetSong(const char *uri_utf8,
Error &error) const override;
virtual void ReturnSong(const LightSong *song) const;
virtual bool Visit(const DatabaseSelection &selection,
VisitDirectory visit_directory,
......@@ -187,34 +212,20 @@ UpnpDatabase::Close()
}
void
UpnpDatabase::ReturnSong(Song *song) const
{
assert(song != nullptr);
song->Free();
}
// If uri is empty, we use the object's url instead. This happens
// when the target of a Visit() is a song, which only happens when
// "add"ing AFAIK. Visit() calls us with a null uri so that the url
// appropriate for fetching is used instead.
static Song *
upnpItemToSong(UPnPDirObject &&dirent, const char *uri)
UpnpDatabase::ReturnSong(const LightSong *_song) const
{
if (*uri == 0)
uri = dirent.url.c_str();
assert(_song != nullptr);
Song *s = Song::NewFile(uri, nullptr);
s->tag = std::move(dirent.tag);
return s;
UpnpSong *song = (UpnpSong *)const_cast<LightSong *>(_song);
delete song;
}
// Get song info by path. We can receive either the id path, or the titles
// one
Song *
const LightSong *
UpnpDatabase::GetSong(const char *uri, Error &error) const
{
Song *song = nullptr;
UpnpSong *song = nullptr;
auto vpath = stringToTokens(uri, "/", true);
if (vpath.size() >= 2) {
ContentDirectoryService server;
......@@ -232,7 +243,8 @@ UpnpDatabase::GetSong(const char *uri, Error &error) const
error))
return nullptr;
}
song = upnpItemToSong(std::move(dirent), "");
song = new UpnpSong(std::move(dirent));
}
if (song == nullptr)
error.Format(db_domain, DB_NOT_FOUND, "No such song: %s", uri);
......@@ -357,12 +369,9 @@ visitSong(UPnPDirObject &&meta, const char *path,
{
if (!visit_song)
return true;
Song *s = upnpItemToSong(std::move(meta), path);
if (!selection.Match(*s))
return true;
bool success = visit_song(*s, error);
s->Free();
return success;
const UpnpSong song(std::move(meta), path);
return !selection.Match(song) || visit_song(song, error);
}
/**
......
......@@ -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"
......
......@@ -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"
......
......@@ -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"
......
......@@ -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;
}
......
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