db/update: scan CUE playlist contents
This commit adds a PlaylistPlugin attribute "as_folder" which for now is only enabled in the "CUE" playlist plugin (which handles separate "*.cue" files). If a playlist with this flag set is being scanned during database update, it will be parsed and its contents will be added to the database. This allows clients to inspect them like directories and its contents will be searchable. Closes https://github.com/MusicPlayerDaemon/MPD/issues/39
This commit is contained in:
@@ -18,24 +18,91 @@
|
||||
*/
|
||||
|
||||
#include "Walk.hxx"
|
||||
#include "UpdateDomain.hxx"
|
||||
#include "db/DatabaseLock.hxx"
|
||||
#include "db/PlaylistVector.hxx"
|
||||
#include "db/plugins/simple/Directory.hxx"
|
||||
#include "song/DetachedSong.hxx"
|
||||
#include "input/InputStream.hxx"
|
||||
#include "playlist/PlaylistPlugin.hxx"
|
||||
#include "playlist/PlaylistRegistry.hxx"
|
||||
#include "playlist/PlaylistStream.hxx"
|
||||
#include "playlist/SongEnumerator.hxx"
|
||||
#include "storage/FileInfo.hxx"
|
||||
#include "storage/StorageInterface.hxx"
|
||||
#include "util/StringFormat.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
void
|
||||
UpdateWalk::UpdatePlaylistFile(Directory &parent, const char *name,
|
||||
const StorageFileInfo &info,
|
||||
const PlaylistPlugin &plugin) noexcept
|
||||
{
|
||||
assert(plugin.open_stream);
|
||||
|
||||
Directory *directory =
|
||||
LockMakeVirtualDirectoryIfModified(parent, name, info,
|
||||
DEVICE_PLAYLIST);
|
||||
if (directory == nullptr)
|
||||
/* not modified */
|
||||
return;
|
||||
|
||||
const auto uri_utf8 = storage.MapUTF8(directory->GetPath());
|
||||
|
||||
FormatDebug(update_domain, "scanning playlist '%s'", uri_utf8.c_str());
|
||||
|
||||
try {
|
||||
Mutex mutex;
|
||||
auto e = plugin.open_stream(InputStream::OpenReady(uri_utf8.c_str(),
|
||||
mutex));
|
||||
if (!e) {
|
||||
/* unsupported URI? roll back.. */
|
||||
editor.LockDeleteDirectory(directory);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned track = 0;
|
||||
|
||||
while (true) {
|
||||
auto song = e->NextSong();
|
||||
if (!song)
|
||||
break;
|
||||
|
||||
auto db_song = std::make_unique<Song>(std::move(*song),
|
||||
*directory);
|
||||
db_song->target = "../" + db_song->filename;
|
||||
db_song->filename = StringFormat<64>("track%04u",
|
||||
++track);
|
||||
|
||||
{
|
||||
const ScopeDatabaseLock protect;
|
||||
directory->AddSong(std::move(db_song));
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
FormatError(std::current_exception(),
|
||||
"Failed to scan playlist '%s'", uri_utf8.c_str());
|
||||
editor.LockDeleteDirectory(directory);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
UpdateWalk::UpdatePlaylistFile(Directory &directory,
|
||||
const char *name, const char *suffix,
|
||||
const StorageFileInfo &info) noexcept
|
||||
{
|
||||
if (!playlist_suffix_supported(suffix))
|
||||
const auto *const plugin = FindPlaylistPluginBySuffix(suffix);
|
||||
if (plugin == nullptr)
|
||||
return false;
|
||||
|
||||
if (plugin->as_folder)
|
||||
UpdatePlaylistFile(directory, name, info, *plugin);
|
||||
|
||||
PlaylistInfo pi(name, info.mtime);
|
||||
|
||||
const ScopeDatabaseLock protect;
|
||||
if (directory.playlists.UpdateOrInsert(std::move(pi)))
|
||||
modified = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
struct StorageFileInfo;
|
||||
struct Directory;
|
||||
struct ArchivePlugin;
|
||||
struct PlaylistPlugin;
|
||||
class ArchiveFile;
|
||||
class Storage;
|
||||
class ExcludeList;
|
||||
@@ -118,6 +119,10 @@ private:
|
||||
}
|
||||
#endif
|
||||
|
||||
void UpdatePlaylistFile(Directory &parent, const char *name,
|
||||
const StorageFileInfo &info,
|
||||
const PlaylistPlugin &plugin) noexcept;
|
||||
|
||||
bool UpdatePlaylistFile(Directory &directory,
|
||||
const char *name, const char *suffix,
|
||||
const StorageFileInfo &info) noexcept;
|
||||
|
||||
Reference in New Issue
Block a user