db/update: convert to OO API
Move global variables into the new classes. That may allow multiple update threads for multiple databases one day.
This commit is contained in:
parent
a31738f6f1
commit
c6725884bc
18
Makefile.am
18
Makefile.am
|
@ -124,15 +124,15 @@ src_mpd_SOURCES = \
|
||||||
src/filter/FilterInternal.hxx \
|
src/filter/FilterInternal.hxx \
|
||||||
src/filter/FilterRegistry.cxx src/filter/FilterRegistry.hxx \
|
src/filter/FilterRegistry.cxx src/filter/FilterRegistry.hxx \
|
||||||
src/db/update/UpdateDomain.cxx src/db/update/UpdateDomain.hxx \
|
src/db/update/UpdateDomain.cxx src/db/update/UpdateDomain.hxx \
|
||||||
src/db/update/UpdateGlue.cxx src/db/update/UpdateGlue.hxx \
|
src/db/update/Service.cxx src/db/update/Service.hxx \
|
||||||
src/db/update/UpdateQueue.cxx src/db/update/UpdateQueue.hxx \
|
src/db/update/UpdateGlue.cxx \
|
||||||
|
src/db/update/Queue.cxx src/db/update/Queue.hxx \
|
||||||
src/db/update/UpdateIO.cxx src/db/update/UpdateIO.hxx \
|
src/db/update/UpdateIO.cxx src/db/update/UpdateIO.hxx \
|
||||||
src/db/update/UpdateDatabase.cxx src/db/update/UpdateDatabase.hxx \
|
src/db/update/Editor.cxx src/db/update/Editor.hxx \
|
||||||
src/db/update/UpdateWalk.cxx src/db/update/UpdateWalk.hxx \
|
src/db/update/Walk.cxx src/db/update/Walk.hxx \
|
||||||
src/db/update/UpdateSong.cxx src/db/update/UpdateSong.hxx \
|
src/db/update/UpdateSong.cxx \
|
||||||
src/db/update/UpdateContainer.cxx src/db/update/UpdateContainer.hxx \
|
src/db/update/Container.cxx \
|
||||||
src/db/update/UpdateInternal.hxx \
|
src/db/update/Remove.cxx src/db/update/Remove.hxx \
|
||||||
src/db/update/UpdateRemove.cxx src/db/update/UpdateRemove.hxx \
|
|
||||||
src/db/update/ExcludeList.cxx src/db/update/ExcludeList.hxx \
|
src/db/update/ExcludeList.cxx src/db/update/ExcludeList.hxx \
|
||||||
src/client/Client.cxx src/client/Client.hxx \
|
src/client/Client.cxx src/client/Client.hxx \
|
||||||
src/client/ClientInternal.hxx \
|
src/client/ClientInternal.hxx \
|
||||||
|
@ -486,7 +486,7 @@ if ENABLE_ARCHIVE
|
||||||
noinst_LIBRARIES += libarchive.a
|
noinst_LIBRARIES += libarchive.a
|
||||||
|
|
||||||
src_mpd_SOURCES += \
|
src_mpd_SOURCES += \
|
||||||
src/db/update/UpdateArchive.cxx src/db/update/UpdateArchive.hxx
|
src/db/update/Archive.cxx
|
||||||
|
|
||||||
libarchive_a_SOURCES = \
|
libarchive_a_SOURCES = \
|
||||||
src/archive/ArchiveDomain.cxx src/archive/ArchiveDomain.hxx \
|
src/archive/ArchiveDomain.cxx src/archive/ArchiveDomain.hxx \
|
||||||
|
|
|
@ -33,9 +33,6 @@ class EventLoop;
|
||||||
|
|
||||||
namespace GlobalEvents {
|
namespace GlobalEvents {
|
||||||
enum Event {
|
enum Event {
|
||||||
/** database update was finished */
|
|
||||||
UPDATE,
|
|
||||||
|
|
||||||
/** during database update, a song was deleted */
|
/** during database update, a song was deleted */
|
||||||
DELETE,
|
DELETE,
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
class NeighborGlue;
|
class NeighborGlue;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class UpdateService;
|
||||||
class ClientList;
|
class ClientList;
|
||||||
struct Partition;
|
struct Partition;
|
||||||
|
|
||||||
|
@ -42,6 +43,8 @@ struct Instance final
|
||||||
NeighborGlue *neighbors;
|
NeighborGlue *neighbors;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
UpdateService *update;
|
||||||
|
|
||||||
ClientList *client_list;
|
ClientList *client_list;
|
||||||
|
|
||||||
Partition *partition;
|
Partition *partition;
|
||||||
|
|
17
src/Main.cxx
17
src/Main.cxx
|
@ -23,7 +23,7 @@
|
||||||
#include "CommandLine.hxx"
|
#include "CommandLine.hxx"
|
||||||
#include "PlaylistFile.hxx"
|
#include "PlaylistFile.hxx"
|
||||||
#include "PlaylistGlobal.hxx"
|
#include "PlaylistGlobal.hxx"
|
||||||
#include "db/update/UpdateGlue.hxx"
|
#include "db/update/Service.hxx"
|
||||||
#include "MusicChunk.hxx"
|
#include "MusicChunk.hxx"
|
||||||
#include "StateFile.hxx"
|
#include "StateFile.hxx"
|
||||||
#include "PlayerThread.hxx"
|
#include "PlayerThread.hxx"
|
||||||
|
@ -448,10 +448,13 @@ int mpd_main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
decoder_plugin_init_all();
|
decoder_plugin_init_all();
|
||||||
update_global_init();
|
|
||||||
|
|
||||||
const bool create_db = !glue_db_init_and_load();
|
const bool create_db = !glue_db_init_and_load();
|
||||||
|
|
||||||
|
instance->update = db_is_simple()
|
||||||
|
? new UpdateService(*main_loop)
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
glue_sticker_init();
|
glue_sticker_init();
|
||||||
|
|
||||||
command_init();
|
command_init();
|
||||||
|
@ -490,7 +493,7 @@ int mpd_main(int argc, char *argv[])
|
||||||
if (create_db) {
|
if (create_db) {
|
||||||
/* the database failed to load: recreate the
|
/* the database failed to load: recreate the
|
||||||
database */
|
database */
|
||||||
unsigned job = update_enqueue("", true);
|
unsigned job = instance->update->Enqueue("", true);
|
||||||
if (job == 0)
|
if (job == 0)
|
||||||
FatalError("directory update failed");
|
FatalError("directory update failed");
|
||||||
}
|
}
|
||||||
|
@ -504,8 +507,9 @@ int mpd_main(int argc, char *argv[])
|
||||||
|
|
||||||
if (config_get_bool(CONF_AUTO_UPDATE, false)) {
|
if (config_get_bool(CONF_AUTO_UPDATE, false)) {
|
||||||
#ifdef ENABLE_INOTIFY
|
#ifdef ENABLE_INOTIFY
|
||||||
if (mapper_has_music_directory())
|
if (mapper_has_music_directory() &&
|
||||||
mpd_inotify_init(*main_loop,
|
instance->update != nullptr)
|
||||||
|
mpd_inotify_init(*main_loop, *instance->update,
|
||||||
config_get_unsigned(CONF_AUTO_UPDATE_DEPTH,
|
config_get_unsigned(CONF_AUTO_UPDATE_DEPTH,
|
||||||
G_MAXUINT));
|
G_MAXUINT));
|
||||||
#else
|
#else
|
||||||
|
@ -558,6 +562,8 @@ int mpd_main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
delete instance->update;
|
||||||
|
|
||||||
const clock_t start = clock();
|
const clock_t start = clock();
|
||||||
DatabaseGlobalDeinit();
|
DatabaseGlobalDeinit();
|
||||||
FormatDebug(main_domain,
|
FormatDebug(main_domain,
|
||||||
|
@ -575,7 +581,6 @@ int mpd_main(int argc, char *argv[])
|
||||||
mapper_finish();
|
mapper_finish();
|
||||||
delete instance->partition;
|
delete instance->partition;
|
||||||
command_finish();
|
command_finish();
|
||||||
update_global_finish();
|
|
||||||
decoder_plugin_deinit_all();
|
decoder_plugin_deinit_all();
|
||||||
#ifdef ENABLE_ARCHIVE
|
#ifdef ENABLE_ARCHIVE
|
||||||
archive_plugin_deinit_all();
|
archive_plugin_deinit_all();
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "OtherCommands.hxx"
|
#include "OtherCommands.hxx"
|
||||||
#include "DatabaseCommands.hxx"
|
#include "DatabaseCommands.hxx"
|
||||||
#include "db/update/UpdateGlue.hxx"
|
#include "db/update/Service.hxx"
|
||||||
#include "CommandError.hxx"
|
#include "CommandError.hxx"
|
||||||
#include "db/Uri.hxx"
|
#include "db/Uri.hxx"
|
||||||
#include "DetachedSong.hxx"
|
#include "DetachedSong.hxx"
|
||||||
|
@ -46,6 +46,7 @@
|
||||||
#include "client/ClientFile.hxx"
|
#include "client/ClientFile.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
|
#include "Instance.hxx"
|
||||||
#include "Idle.hxx"
|
#include "Idle.hxx"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
@ -186,7 +187,6 @@ CommandResult
|
||||||
handle_update(Client &client, gcc_unused int argc, char *argv[])
|
handle_update(Client &client, gcc_unused int argc, char *argv[])
|
||||||
{
|
{
|
||||||
const char *path = "";
|
const char *path = "";
|
||||||
unsigned ret;
|
|
||||||
|
|
||||||
assert(argc <= 2);
|
assert(argc <= 2);
|
||||||
if (argc == 2) {
|
if (argc == 2) {
|
||||||
|
@ -202,7 +202,13 @@ handle_update(Client &client, gcc_unused int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = update_enqueue(path, false);
|
UpdateService *update = client.partition.instance.update;
|
||||||
|
if (update == nullptr) {
|
||||||
|
command_error(client, ACK_ERROR_NO_EXIST, "No database");
|
||||||
|
return CommandResult::ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned ret = update->Enqueue(path, false);
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
client_printf(client, "updating_db: %i\n", ret);
|
client_printf(client, "updating_db: %i\n", ret);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
|
@ -217,7 +223,6 @@ CommandResult
|
||||||
handle_rescan(Client &client, gcc_unused int argc, char *argv[])
|
handle_rescan(Client &client, gcc_unused int argc, char *argv[])
|
||||||
{
|
{
|
||||||
const char *path = "";
|
const char *path = "";
|
||||||
unsigned ret;
|
|
||||||
|
|
||||||
assert(argc <= 2);
|
assert(argc <= 2);
|
||||||
if (argc == 2) {
|
if (argc == 2) {
|
||||||
|
@ -230,7 +235,13 @@ handle_rescan(Client &client, gcc_unused int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = update_enqueue(path, true);
|
UpdateService *update = client.partition.instance.update;
|
||||||
|
if (update == nullptr) {
|
||||||
|
command_error(client, ACK_ERROR_NO_EXIST, "No database");
|
||||||
|
return CommandResult::ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned ret = update->Enqueue(path, true);
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
client_printf(client, "updating_db: %i\n", ret);
|
client_printf(client, "updating_db: %i\n", ret);
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
|
|
|
@ -22,10 +22,11 @@
|
||||||
#include "CommandError.hxx"
|
#include "CommandError.hxx"
|
||||||
#include "Playlist.hxx"
|
#include "Playlist.hxx"
|
||||||
#include "PlaylistPrint.hxx"
|
#include "PlaylistPrint.hxx"
|
||||||
#include "db/update/UpdateGlue.hxx"
|
#include "db/update/Service.hxx"
|
||||||
#include "client/Client.hxx"
|
#include "client/Client.hxx"
|
||||||
#include "mixer/Volume.hxx"
|
#include "mixer/Volume.hxx"
|
||||||
#include "Partition.hxx"
|
#include "Partition.hxx"
|
||||||
|
#include "Instance.hxx"
|
||||||
#include "protocol/Result.hxx"
|
#include "protocol/Result.hxx"
|
||||||
#include "protocol/ArgParser.hxx"
|
#include "protocol/ArgParser.hxx"
|
||||||
#include "AudioFormat.hxx"
|
#include "AudioFormat.hxx"
|
||||||
|
@ -111,7 +112,6 @@ handle_status(Client &client,
|
||||||
gcc_unused int argc, gcc_unused char *argv[])
|
gcc_unused int argc, gcc_unused char *argv[])
|
||||||
{
|
{
|
||||||
const char *state = nullptr;
|
const char *state = nullptr;
|
||||||
int updateJobId;
|
|
||||||
int song;
|
int song;
|
||||||
|
|
||||||
const auto player_status = client.player_control.GetStatus();
|
const auto player_status = client.player_control.GetStatus();
|
||||||
|
@ -187,7 +187,11 @@ handle_status(Client &client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((updateJobId = isUpdatingDB())) {
|
const UpdateService *update_service = client.partition.instance.update;
|
||||||
|
unsigned updateJobId = update_service != nullptr
|
||||||
|
? update_service->GetId()
|
||||||
|
: 0;
|
||||||
|
if (updateJobId != 0) {
|
||||||
client_printf(client,
|
client_printf(client,
|
||||||
COMMAND_STATUS_UPDATING_DB ": %i\n",
|
COMMAND_STATUS_UPDATING_DB ": %i\n",
|
||||||
updateJobId);
|
updateJobId);
|
||||||
|
|
|
@ -18,8 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h" /* must be first for large file support */
|
#include "config.h" /* must be first for large file support */
|
||||||
#include "UpdateArchive.hxx"
|
#include "Walk.hxx"
|
||||||
#include "UpdateInternal.hxx"
|
|
||||||
#include "UpdateDomain.hxx"
|
#include "UpdateDomain.hxx"
|
||||||
#include "db/DatabaseLock.hxx"
|
#include "db/DatabaseLock.hxx"
|
||||||
#include "db/Directory.hxx"
|
#include "db/Directory.hxx"
|
||||||
|
@ -35,10 +34,11 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static void
|
void
|
||||||
update_archive_tree(Directory &directory, const char *name)
|
UpdateWalk::UpdateArchiveTree(Directory &directory, const char *name)
|
||||||
{
|
{
|
||||||
const char *tmp = strchr(name, '/');
|
const char *tmp = strchr(name, '/');
|
||||||
if (tmp) {
|
if (tmp) {
|
||||||
|
@ -51,7 +51,7 @@ update_archive_tree(Directory &directory, const char *name)
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
//create directories first
|
//create directories first
|
||||||
update_archive_tree(*subdir, tmp+1);
|
UpdateArchiveTree(*subdir, tmp + 1);
|
||||||
} else {
|
} else {
|
||||||
if (strlen(name) == 0) {
|
if (strlen(name) == 0) {
|
||||||
LogWarning(update_domain,
|
LogWarning(update_domain,
|
||||||
|
@ -78,6 +78,21 @@ update_archive_tree(Directory &directory, const char *name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class UpdateArchiveVisitor final : public ArchiveVisitor {
|
||||||
|
UpdateWalk &walk;
|
||||||
|
Directory *directory;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UpdateArchiveVisitor(UpdateWalk &_walk, Directory *_directory)
|
||||||
|
:walk(_walk), directory(_directory) {}
|
||||||
|
|
||||||
|
virtual void VisitArchiveEntry(const char *path_utf8) override {
|
||||||
|
FormatDebug(update_domain,
|
||||||
|
"adding archive file: %s", path_utf8);
|
||||||
|
walk.UpdateArchiveTree(*directory, path_utf8);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the file listing from an archive file.
|
* Updates the file listing from an archive file.
|
||||||
*
|
*
|
||||||
|
@ -86,10 +101,10 @@ update_archive_tree(Directory &directory, const char *name)
|
||||||
* @param st stat() information on the archive file
|
* @param st stat() information on the archive file
|
||||||
* @param plugin the archive plugin which fits this archive type
|
* @param plugin the archive plugin which fits this archive type
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
update_archive_file2(Directory &parent, const char *name,
|
UpdateWalk::UpdateArchiveFile(Directory &parent, const char *name,
|
||||||
const struct stat *st,
|
const struct stat *st,
|
||||||
const struct archive_plugin *plugin)
|
const archive_plugin &plugin)
|
||||||
{
|
{
|
||||||
db_lock();
|
db_lock();
|
||||||
Directory *directory = parent.FindChild(name);
|
Directory *directory = parent.FindChild(name);
|
||||||
|
@ -105,7 +120,7 @@ update_archive_file2(Directory &parent, const char *name,
|
||||||
|
|
||||||
/* open archive */
|
/* open archive */
|
||||||
Error error;
|
Error error;
|
||||||
ArchiveFile *file = archive_file_open(plugin, path_fs.c_str(), error);
|
ArchiveFile *file = archive_file_open(&plugin, path_fs.c_str(), error);
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
LogError(error);
|
LogError(error);
|
||||||
return;
|
return;
|
||||||
|
@ -126,44 +141,21 @@ update_archive_file2(Directory &parent, const char *name,
|
||||||
|
|
||||||
directory->mtime = st->st_mtime;
|
directory->mtime = st->st_mtime;
|
||||||
|
|
||||||
class UpdateArchiveVisitor final : public ArchiveVisitor {
|
UpdateArchiveVisitor visitor(*this, directory);
|
||||||
Directory *directory;
|
|
||||||
|
|
||||||
public:
|
|
||||||
UpdateArchiveVisitor(Directory *_directory)
|
|
||||||
:directory(_directory) {}
|
|
||||||
|
|
||||||
virtual void VisitArchiveEntry(const char *path_utf8) override {
|
|
||||||
FormatDebug(update_domain,
|
|
||||||
"adding archive file: %s", path_utf8);
|
|
||||||
update_archive_tree(*directory, path_utf8);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
UpdateArchiveVisitor visitor(directory);
|
|
||||||
file->Visit(visitor);
|
file->Visit(visitor);
|
||||||
file->Close();
|
file->Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
update_archive_file(Directory &directory,
|
UpdateWalk::UpdateArchiveFile(Directory &directory,
|
||||||
const char *name, const char *suffix,
|
const char *name, const char *suffix,
|
||||||
const struct stat *st)
|
const struct stat *st)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_ARCHIVE
|
|
||||||
const struct archive_plugin *plugin =
|
const struct archive_plugin *plugin =
|
||||||
archive_plugin_from_suffix(suffix);
|
archive_plugin_from_suffix(suffix);
|
||||||
if (plugin == nullptr)
|
if (plugin == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
update_archive_file2(directory, name, st, plugin);
|
UpdateArchiveFile(directory, name, st, *plugin);
|
||||||
return true;
|
return true;
|
||||||
#else
|
|
||||||
(void)directory;
|
|
||||||
(void)name;
|
|
||||||
(void)suffix;
|
|
||||||
(void)st;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
|
@ -18,9 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h" /* must be first for large file support */
|
#include "config.h" /* must be first for large file support */
|
||||||
#include "UpdateContainer.hxx"
|
#include "Walk.hxx"
|
||||||
#include "UpdateInternal.hxx"
|
|
||||||
#include "UpdateDatabase.hxx"
|
|
||||||
#include "UpdateDomain.hxx"
|
#include "UpdateDomain.hxx"
|
||||||
#include "db/DatabaseLock.hxx"
|
#include "db/DatabaseLock.hxx"
|
||||||
#include "db/Directory.hxx"
|
#include "db/Directory.hxx"
|
||||||
|
@ -33,19 +31,13 @@
|
||||||
#include "tag/TagBuilder.hxx"
|
#include "tag/TagBuilder.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
/**
|
Directory *
|
||||||
* Create the specified directory object if it does not exist already
|
UpdateWalk::MakeDirectoryIfModified(Directory &parent, const char *name,
|
||||||
* or if the #stat object indicates that it has been modified since
|
const struct stat *st)
|
||||||
* the last update. Returns nullptr when it exists already and is
|
|
||||||
* unmodified.
|
|
||||||
*
|
|
||||||
* The caller must lock the database.
|
|
||||||
*/
|
|
||||||
static Directory *
|
|
||||||
make_directory_if_modified(Directory &parent, const char *name,
|
|
||||||
const struct stat *st)
|
|
||||||
{
|
{
|
||||||
Directory *directory = parent.FindChild(name);
|
Directory *directory = parent.FindChild(name);
|
||||||
|
|
||||||
|
@ -56,7 +48,7 @@ make_directory_if_modified(Directory &parent, const char *name,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete_directory(directory);
|
editor.DeleteDirectory(directory);
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,10 +65,9 @@ SupportsContainerSuffix(const DecoderPlugin &plugin, const char *suffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
update_container_file(Directory &directory,
|
UpdateWalk::UpdateContainerFile(Directory &directory,
|
||||||
const char *name,
|
const char *name, const char *suffix,
|
||||||
const struct stat *st,
|
const struct stat *st)
|
||||||
const char *suffix)
|
|
||||||
{
|
{
|
||||||
const DecoderPlugin *_plugin = decoder_plugins_find([suffix](const DecoderPlugin &plugin){
|
const DecoderPlugin *_plugin = decoder_plugins_find([suffix](const DecoderPlugin &plugin){
|
||||||
return SupportsContainerSuffix(plugin, suffix);
|
return SupportsContainerSuffix(plugin, suffix);
|
||||||
|
@ -86,7 +77,7 @@ update_container_file(Directory &directory,
|
||||||
const DecoderPlugin &plugin = *_plugin;
|
const DecoderPlugin &plugin = *_plugin;
|
||||||
|
|
||||||
db_lock();
|
db_lock();
|
||||||
Directory *contdir = make_directory_if_modified(directory, name, st);
|
Directory *contdir = MakeDirectoryIfModified(directory, name, st);
|
||||||
if (contdir == nullptr) {
|
if (contdir == nullptr) {
|
||||||
/* not modified */
|
/* not modified */
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
@ -128,7 +119,7 @@ update_container_file(Directory &directory,
|
||||||
|
|
||||||
if (tnum == 1) {
|
if (tnum == 1) {
|
||||||
db_lock();
|
db_lock();
|
||||||
delete_directory(contdir);
|
editor.DeleteDirectory(contdir);
|
||||||
db_unlock();
|
db_unlock();
|
||||||
return false;
|
return false;
|
||||||
} else
|
} else
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h" /* must be first for large file support */
|
#include "config.h" /* must be first for large file support */
|
||||||
#include "UpdateDatabase.hxx"
|
#include "Editor.hxx"
|
||||||
#include "UpdateRemove.hxx"
|
#include "Remove.hxx"
|
||||||
#include "db/PlaylistVector.hxx"
|
#include "db/PlaylistVector.hxx"
|
||||||
#include "db/Directory.hxx"
|
#include "db/Directory.hxx"
|
||||||
#include "db/Song.hxx"
|
#include "db/Song.hxx"
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
void
|
void
|
||||||
delete_song(Directory &dir, Song *del)
|
DatabaseEditor::DeleteSong(Directory &dir, Song *del)
|
||||||
{
|
{
|
||||||
assert(del->parent == &dir);
|
assert(del->parent == &dir);
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ delete_song(Directory &dir, Song *del)
|
||||||
db_unlock(); /* temporary unlock, because update_remove_song() blocks */
|
db_unlock(); /* temporary unlock, because update_remove_song() blocks */
|
||||||
|
|
||||||
/* now take it out of the playlist (in the main_task) */
|
/* now take it out of the playlist (in the main_task) */
|
||||||
update_remove_song(del);
|
remove.Remove(del);
|
||||||
|
|
||||||
/* finally, all possible references gone, free it */
|
/* finally, all possible references gone, free it */
|
||||||
del->Free();
|
del->Free();
|
||||||
|
@ -53,32 +53,32 @@ delete_song(Directory &dir, Song *del)
|
||||||
*
|
*
|
||||||
* Caller must lock the #db_mutex.
|
* Caller must lock the #db_mutex.
|
||||||
*/
|
*/
|
||||||
static void
|
inline void
|
||||||
clear_directory(Directory &directory)
|
DatabaseEditor::ClearDirectory(Directory &directory)
|
||||||
{
|
{
|
||||||
Directory *child, *n;
|
Directory *child, *n;
|
||||||
directory_for_each_child_safe(child, n, directory)
|
directory_for_each_child_safe(child, n, directory)
|
||||||
delete_directory(child);
|
DeleteDirectory(child);
|
||||||
|
|
||||||
Song *song, *ns;
|
Song *song, *ns;
|
||||||
directory_for_each_song_safe(song, ns, directory) {
|
directory_for_each_song_safe(song, ns, directory) {
|
||||||
assert(song->parent == &directory);
|
assert(song->parent == &directory);
|
||||||
delete_song(directory, song);
|
DeleteSong(directory, song);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
delete_directory(Directory *directory)
|
DatabaseEditor::DeleteDirectory(Directory *directory)
|
||||||
{
|
{
|
||||||
assert(directory->parent != nullptr);
|
assert(directory->parent != nullptr);
|
||||||
|
|
||||||
clear_directory(*directory);
|
ClearDirectory(*directory);
|
||||||
|
|
||||||
directory->Delete();
|
directory->Delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
delete_name_in(Directory &parent, const char *name)
|
DatabaseEditor::DeleteNameIn(Directory &parent, const char *name)
|
||||||
{
|
{
|
||||||
bool modified = false;
|
bool modified = false;
|
||||||
|
|
||||||
|
@ -86,13 +86,13 @@ delete_name_in(Directory &parent, const char *name)
|
||||||
Directory *directory = parent.FindChild(name);
|
Directory *directory = parent.FindChild(name);
|
||||||
|
|
||||||
if (directory != nullptr) {
|
if (directory != nullptr) {
|
||||||
delete_directory(directory);
|
DeleteDirectory(directory);
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Song *song = parent.FindSong(name);
|
Song *song = parent.FindSong(name);
|
||||||
if (song != nullptr) {
|
if (song != nullptr) {
|
||||||
delete_song(parent, song);
|
DeleteSong(parent, song);
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,30 +21,40 @@
|
||||||
#define MPD_UPDATE_DATABASE_HXX
|
#define MPD_UPDATE_DATABASE_HXX
|
||||||
|
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
|
#include "Remove.hxx"
|
||||||
|
|
||||||
struct Directory;
|
struct Directory;
|
||||||
struct Song;
|
struct Song;
|
||||||
|
class UpdateRemoveService;
|
||||||
|
|
||||||
/**
|
class DatabaseEditor final {
|
||||||
* Caller must lock the #db_mutex.
|
UpdateRemoveService remove;
|
||||||
*/
|
|
||||||
void
|
|
||||||
delete_song(Directory &parent, Song *song);
|
|
||||||
|
|
||||||
/**
|
public:
|
||||||
* Recursively free a directory and all its contents.
|
DatabaseEditor(EventLoop &_loop)
|
||||||
*
|
:remove(_loop) {}
|
||||||
* Caller must lock the #db_mutex.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
delete_directory(Directory *directory);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Caller must NOT lock the #db_mutex.
|
* Caller must lock the #db_mutex.
|
||||||
*
|
*/
|
||||||
* @return true if the database was modified
|
void DeleteSong(Directory &parent, Song *song);
|
||||||
*/
|
|
||||||
bool
|
/**
|
||||||
delete_name_in(Directory &parent, const char *name);
|
* Recursively free a directory and all its contents.
|
||||||
|
*
|
||||||
|
* Caller must lock the #db_mutex.
|
||||||
|
*/
|
||||||
|
void DeleteDirectory(Directory *directory);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caller must NOT lock the #db_mutex.
|
||||||
|
*
|
||||||
|
* @return true if the database was modified
|
||||||
|
*/
|
||||||
|
bool DeleteNameIn(Directory &parent, const char *name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ClearDirectory(Directory &directory);
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -20,7 +20,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "InotifyQueue.hxx"
|
#include "InotifyQueue.hxx"
|
||||||
#include "InotifyDomain.hxx"
|
#include "InotifyDomain.hxx"
|
||||||
#include "UpdateGlue.hxx"
|
#include "Service.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -40,7 +40,7 @@ InotifyQueue::OnTimeout()
|
||||||
while (!queue.empty()) {
|
while (!queue.empty()) {
|
||||||
const char *uri_utf8 = queue.front().c_str();
|
const char *uri_utf8 = queue.front().c_str();
|
||||||
|
|
||||||
id = update_enqueue(uri_utf8, false);
|
id = update.Enqueue(uri_utf8, false);
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
/* retry later */
|
/* retry later */
|
||||||
ScheduleSeconds(INOTIFY_UPDATE_DELAY_S);
|
ScheduleSeconds(INOTIFY_UPDATE_DELAY_S);
|
||||||
|
|
|
@ -26,11 +26,16 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
class UpdateService;
|
||||||
|
|
||||||
class InotifyQueue final : private TimeoutMonitor {
|
class InotifyQueue final : private TimeoutMonitor {
|
||||||
|
UpdateService &update;
|
||||||
|
|
||||||
std::list<std::string> queue;
|
std::list<std::string> queue;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InotifyQueue(EventLoop &_loop):TimeoutMonitor(_loop) {}
|
InotifyQueue(EventLoop &_loop, UpdateService &_update)
|
||||||
|
:TimeoutMonitor(_loop), update(_update) {}
|
||||||
|
|
||||||
void Enqueue(const char *uri_utf8);
|
void Enqueue(const char *uri_utf8);
|
||||||
|
|
||||||
|
|
|
@ -285,7 +285,7 @@ mpd_inotify_callback(int wd, unsigned mask,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
mpd_inotify_init(EventLoop &loop, unsigned max_depth)
|
mpd_inotify_init(EventLoop &loop, UpdateService &update, unsigned max_depth)
|
||||||
{
|
{
|
||||||
LogDebug(inotify_domain, "initializing inotify");
|
LogDebug(inotify_domain, "initializing inotify");
|
||||||
|
|
||||||
|
@ -320,7 +320,7 @@ mpd_inotify_init(EventLoop &loop, unsigned max_depth)
|
||||||
|
|
||||||
recursive_watch_subdirectories(inotify_root, path, 0);
|
recursive_watch_subdirectories(inotify_root, path, 0);
|
||||||
|
|
||||||
inotify_queue = new InotifyQueue(loop);
|
inotify_queue = new InotifyQueue(loop, update);
|
||||||
|
|
||||||
LogDebug(inotify_domain, "watching music directory");
|
LogDebug(inotify_domain, "watching music directory");
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,12 @@
|
||||||
#include "Compiler.h"
|
#include "Compiler.h"
|
||||||
|
|
||||||
class EventLoop;
|
class EventLoop;
|
||||||
|
class UpdateService;
|
||||||
|
|
||||||
#ifdef HAVE_INOTIFY_INIT
|
#ifdef HAVE_INOTIFY_INIT
|
||||||
|
|
||||||
void
|
void
|
||||||
mpd_inotify_init(EventLoop &loop, unsigned max_depth);
|
mpd_inotify_init(EventLoop &loop, UpdateService &update, unsigned max_depth);
|
||||||
|
|
||||||
void
|
void
|
||||||
mpd_inotify_finish(void);
|
mpd_inotify_finish(void);
|
||||||
|
@ -36,7 +37,9 @@ mpd_inotify_finish(void);
|
||||||
#else /* !HAVE_INOTIFY_INIT */
|
#else /* !HAVE_INOTIFY_INIT */
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
mpd_inotify_init(gcc_unused EventLoop &loop, gcc_unused unsigned max_depth)
|
mpd_inotify_init(gcc_unused EventLoop &loop,
|
||||||
|
gcc_unused UpdateService &update,
|
||||||
|
gcc_unused unsigned max_depth)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,17 +18,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "UpdateQueue.hxx"
|
#include "Queue.hxx"
|
||||||
|
|
||||||
#include <queue>
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
static constexpr unsigned MAX_UPDATE_QUEUE_SIZE = 32;
|
|
||||||
|
|
||||||
static std::queue<UpdateQueueItem, std::list<UpdateQueueItem>> update_queue;
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
update_queue_push(const char *path, bool discard, unsigned id)
|
UpdateQueue::Push(const char *path, bool discard, unsigned id)
|
||||||
{
|
{
|
||||||
if (update_queue.size() >= MAX_UPDATE_QUEUE_SIZE)
|
if (update_queue.size() >= MAX_UPDATE_QUEUE_SIZE)
|
||||||
return false;
|
return false;
|
||||||
|
@ -38,7 +31,7 @@ update_queue_push(const char *path, bool discard, unsigned id)
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateQueueItem
|
UpdateQueueItem
|
||||||
update_queue_shift()
|
UpdateQueue::Pop()
|
||||||
{
|
{
|
||||||
if (update_queue.empty())
|
if (update_queue.empty())
|
||||||
return UpdateQueueItem();
|
return UpdateQueueItem();
|
|
@ -23,6 +23,8 @@
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <queue>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
struct UpdateQueueItem {
|
struct UpdateQueueItem {
|
||||||
std::string path_utf8;
|
std::string path_utf8;
|
||||||
|
@ -39,10 +41,15 @@ struct UpdateQueueItem {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
class UpdateQueue {
|
||||||
update_queue_push(const char *path, bool discard, unsigned id);
|
static constexpr unsigned MAX_UPDATE_QUEUE_SIZE = 32;
|
||||||
|
|
||||||
UpdateQueueItem
|
std::queue<UpdateQueueItem, std::list<UpdateQueueItem>> update_queue;
|
||||||
update_queue_shift();
|
|
||||||
|
public:
|
||||||
|
bool Push(const char *path, bool discard, unsigned id);
|
||||||
|
|
||||||
|
UpdateQueueItem Pop();
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h" /* must be first for large file support */
|
#include "config.h" /* must be first for large file support */
|
||||||
#include "UpdateRemove.hxx"
|
#include "Remove.hxx"
|
||||||
#include "UpdateDomain.hxx"
|
#include "UpdateDomain.hxx"
|
||||||
#include "GlobalEvents.hxx"
|
#include "GlobalEvents.hxx"
|
||||||
#include "thread/Mutex.hxx"
|
#include "thread/Mutex.hxx"
|
||||||
|
@ -36,17 +36,12 @@
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
static const Song *removed_song;
|
|
||||||
|
|
||||||
static Mutex remove_mutex;
|
|
||||||
static Cond remove_cond;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Safely remove a song from the database. This must be done in the
|
* Safely remove a song from the database. This must be done in the
|
||||||
* main task, to be sure that there is no pointer left to it.
|
* main task, to be sure that there is no pointer left to it.
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
song_remove_event(void)
|
UpdateRemoveService::RunDeferred()
|
||||||
{
|
{
|
||||||
assert(removed_song != nullptr);
|
assert(removed_song != nullptr);
|
||||||
|
|
||||||
|
@ -74,19 +69,13 @@ song_remove_event(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
update_remove_global_init(void)
|
UpdateRemoveService::Remove(const Song *song)
|
||||||
{
|
|
||||||
GlobalEvents::Register(GlobalEvents::DELETE, song_remove_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
update_remove_song(const Song *song)
|
|
||||||
{
|
{
|
||||||
assert(removed_song == nullptr);
|
assert(removed_song == nullptr);
|
||||||
|
|
||||||
removed_song = song;
|
removed_song = song;
|
||||||
|
|
||||||
GlobalEvents::Emit(GlobalEvents::DELETE);
|
DeferredMonitor::Schedule();
|
||||||
|
|
||||||
remove_mutex.lock();
|
remove_mutex.lock();
|
||||||
|
|
|
@ -21,18 +21,37 @@
|
||||||
#define MPD_UPDATE_REMOVE_HXX
|
#define MPD_UPDATE_REMOVE_HXX
|
||||||
|
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
|
#include "event/DeferredMonitor.hxx"
|
||||||
|
#include "thread/Mutex.hxx"
|
||||||
|
#include "thread/Cond.hxx"
|
||||||
|
|
||||||
struct Song;
|
struct Song;
|
||||||
|
|
||||||
void
|
|
||||||
update_remove_global_init(void);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a signal to the main thread which will in turn remove the
|
* This class handles #Song removal. It defers the action to the main
|
||||||
* song: from the sticker database and from the playlist. This
|
* thread to ensure that all references to the #Song are gone.
|
||||||
* serialized access is implemented to avoid excessive locking.
|
|
||||||
*/
|
*/
|
||||||
void
|
class UpdateRemoveService final : DeferredMonitor {
|
||||||
update_remove_song(const Song *song);
|
Mutex remove_mutex;
|
||||||
|
Cond remove_cond;
|
||||||
|
|
||||||
|
const Song *removed_song;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UpdateRemoveService(EventLoop &_loop)
|
||||||
|
:DeferredMonitor(_loop) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a signal to the main thread which will in turn remove
|
||||||
|
* the song: from the sticker database and from the playlist.
|
||||||
|
* This serialized access is implemented to avoid excessive
|
||||||
|
* locking.
|
||||||
|
*/
|
||||||
|
void Remove(const Song *song);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* virtual methods from class DeferredMonitor */
|
||||||
|
virtual void RunDeferred() override;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -17,12 +17,5 @@
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MPD_UPDATE_INTERNAL_H
|
#include "config.h"
|
||||||
#define MPD_UPDATE_INTERNAL_H
|
#include "Service.hxx"
|
||||||
|
|
||||||
#include "check.h"
|
|
||||||
|
|
||||||
extern bool walk_discard;
|
|
||||||
extern bool modified;
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* 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_UPDATE_SERVICE_HXX
|
||||||
|
#define MPD_UPDATE_SERVICE_HXX
|
||||||
|
|
||||||
|
#include "check.h"
|
||||||
|
#include "Queue.hxx"
|
||||||
|
#include "Walk.hxx"
|
||||||
|
#include "event/DeferredMonitor.hxx"
|
||||||
|
#include "thread/Thread.hxx"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class manages the update queue and runs the update thread.
|
||||||
|
*/
|
||||||
|
class UpdateService final : DeferredMonitor {
|
||||||
|
enum Progress {
|
||||||
|
UPDATE_PROGRESS_IDLE = 0,
|
||||||
|
UPDATE_PROGRESS_RUNNING = 1,
|
||||||
|
UPDATE_PROGRESS_DONE = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
Progress progress;
|
||||||
|
|
||||||
|
bool modified;
|
||||||
|
|
||||||
|
Thread update_thread;
|
||||||
|
|
||||||
|
static const unsigned update_task_id_max = 1 << 15;
|
||||||
|
|
||||||
|
unsigned update_task_id;
|
||||||
|
|
||||||
|
UpdateQueue queue;
|
||||||
|
|
||||||
|
UpdateQueueItem next;
|
||||||
|
|
||||||
|
UpdateWalk walk;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UpdateService(EventLoop &_loop);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a non-zero job id when we are currently updating
|
||||||
|
* the database.
|
||||||
|
*/
|
||||||
|
unsigned GetId() const {
|
||||||
|
return next.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add this path to the database update queue.
|
||||||
|
*
|
||||||
|
* @param path a path to update; if an empty string,
|
||||||
|
* the whole music directory is updated
|
||||||
|
* @return the job id, or 0 on error
|
||||||
|
*/
|
||||||
|
gcc_nonnull_all
|
||||||
|
unsigned Enqueue(const char *path, bool discard);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* virtual methods from class DeferredMonitor */
|
||||||
|
virtual void RunDeferred() override;
|
||||||
|
|
||||||
|
/* the update thread */
|
||||||
|
void Task();
|
||||||
|
static void Task(void *ctx);
|
||||||
|
|
||||||
|
void StartThread(UpdateQueueItem &&i);
|
||||||
|
|
||||||
|
unsigned GenerateId();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,50 +0,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_UPDATE_ARCHIVE_HXX
|
|
||||||
#define MPD_UPDATE_ARCHIVE_HXX
|
|
||||||
|
|
||||||
#include "check.h"
|
|
||||||
#include "Compiler.h"
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
struct Directory;
|
|
||||||
|
|
||||||
#ifdef ENABLE_ARCHIVE
|
|
||||||
|
|
||||||
bool
|
|
||||||
update_archive_file(Directory &directory,
|
|
||||||
const char *name, const char *suffix,
|
|
||||||
const struct stat *st);
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
update_archive_file(gcc_unused Directory &directory,
|
|
||||||
gcc_unused const char *name,
|
|
||||||
gcc_unused const char *suffix,
|
|
||||||
gcc_unused const struct stat *st)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,36 +0,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_UPDATE_CONTAINER_HXX
|
|
||||||
#define MPD_UPDATE_CONTAINER_HXX
|
|
||||||
|
|
||||||
#include "check.h"
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
struct Directory;
|
|
||||||
struct DecoderPlugin;
|
|
||||||
|
|
||||||
bool
|
|
||||||
update_container_file(Directory &directory,
|
|
||||||
const char *name,
|
|
||||||
const struct stat *st,
|
|
||||||
const char *suffix);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -18,15 +18,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "UpdateGlue.hxx"
|
#include "Service.hxx"
|
||||||
#include "UpdateQueue.hxx"
|
|
||||||
#include "UpdateWalk.hxx"
|
|
||||||
#include "UpdateRemove.hxx"
|
|
||||||
#include "UpdateDomain.hxx"
|
#include "UpdateDomain.hxx"
|
||||||
#include "Mapper.hxx"
|
#include "Mapper.hxx"
|
||||||
#include "db/DatabaseSimple.hxx"
|
#include "db/DatabaseSimple.hxx"
|
||||||
#include "Idle.hxx"
|
#include "Idle.hxx"
|
||||||
#include "GlobalEvents.hxx"
|
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
#include "Main.hxx"
|
#include "Main.hxx"
|
||||||
|
@ -38,30 +34,8 @@
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
static enum update_progress {
|
inline void
|
||||||
UPDATE_PROGRESS_IDLE = 0,
|
UpdateService::Task()
|
||||||
UPDATE_PROGRESS_RUNNING = 1,
|
|
||||||
UPDATE_PROGRESS_DONE = 2
|
|
||||||
} progress;
|
|
||||||
|
|
||||||
static bool modified;
|
|
||||||
|
|
||||||
static Thread update_thread;
|
|
||||||
|
|
||||||
static const unsigned update_task_id_max = 1 << 15;
|
|
||||||
|
|
||||||
static unsigned update_task_id;
|
|
||||||
|
|
||||||
static UpdateQueueItem next;
|
|
||||||
|
|
||||||
unsigned
|
|
||||||
isUpdatingDB(void)
|
|
||||||
{
|
|
||||||
return next.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
update_task(gcc_unused void *ctx)
|
|
||||||
{
|
{
|
||||||
if (!next.path_utf8.empty())
|
if (!next.path_utf8.empty())
|
||||||
FormatDebug(update_domain, "starting: %s",
|
FormatDebug(update_domain, "starting: %s",
|
||||||
|
@ -71,7 +45,7 @@ update_task(gcc_unused void *ctx)
|
||||||
|
|
||||||
SetThreadIdlePriority();
|
SetThreadIdlePriority();
|
||||||
|
|
||||||
modified = update_walk(next.path_utf8.c_str(), next.discard);
|
modified = walk.Walk(next.path_utf8.c_str(), next.discard);
|
||||||
|
|
||||||
if (modified || !db_exists()) {
|
if (modified || !db_exists()) {
|
||||||
Error error;
|
Error error;
|
||||||
|
@ -86,11 +60,18 @@ update_task(gcc_unused void *ctx)
|
||||||
LogDebug(update_domain, "finished");
|
LogDebug(update_domain, "finished");
|
||||||
|
|
||||||
progress = UPDATE_PROGRESS_DONE;
|
progress = UPDATE_PROGRESS_DONE;
|
||||||
GlobalEvents::Emit(GlobalEvents::UPDATE);
|
DeferredMonitor::Schedule();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
spawn_update_task(UpdateQueueItem &&i)
|
UpdateService::Task(void *ctx)
|
||||||
|
{
|
||||||
|
UpdateService &service = *(UpdateService *)ctx;
|
||||||
|
return service.Task();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UpdateService::StartThread(UpdateQueueItem &&i)
|
||||||
{
|
{
|
||||||
assert(main_thread.IsInside());
|
assert(main_thread.IsInside());
|
||||||
|
|
||||||
|
@ -100,15 +81,15 @@ spawn_update_task(UpdateQueueItem &&i)
|
||||||
next = std::move(i);
|
next = std::move(i);
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (!update_thread.Start(update_task, nullptr, error))
|
if (!update_thread.Start(Task, this, error))
|
||||||
FatalError(error);
|
FatalError(error);
|
||||||
|
|
||||||
FormatDebug(update_domain,
|
FormatDebug(update_domain,
|
||||||
"spawned thread for update job id %i", next.id);
|
"spawned thread for update job id %i", next.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned
|
unsigned
|
||||||
generate_update_id()
|
UpdateService::GenerateId()
|
||||||
{
|
{
|
||||||
unsigned id = update_task_id + 1;
|
unsigned id = update_task_id + 1;
|
||||||
if (id > update_task_id_max)
|
if (id > update_task_id_max)
|
||||||
|
@ -117,7 +98,7 @@ generate_update_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned
|
unsigned
|
||||||
update_enqueue(const char *path, bool discard)
|
UpdateService::Enqueue(const char *path, bool discard)
|
||||||
{
|
{
|
||||||
assert(main_thread.IsInside());
|
assert(main_thread.IsInside());
|
||||||
|
|
||||||
|
@ -125,16 +106,16 @@ update_enqueue(const char *path, bool discard)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (progress != UPDATE_PROGRESS_IDLE) {
|
if (progress != UPDATE_PROGRESS_IDLE) {
|
||||||
const unsigned id = generate_update_id();
|
const unsigned id = GenerateId();
|
||||||
if (!update_queue_push(path, discard, id))
|
if (!queue.Push(path, discard, id))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
update_task_id = id;
|
update_task_id = id;
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned id = update_task_id = generate_update_id();
|
const unsigned id = update_task_id = GenerateId();
|
||||||
spawn_update_task(UpdateQueueItem(path, discard, id));
|
StartThread(UpdateQueueItem(path, discard, id));
|
||||||
|
|
||||||
idle_add(IDLE_UPDATE);
|
idle_add(IDLE_UPDATE);
|
||||||
|
|
||||||
|
@ -144,7 +125,8 @@ update_enqueue(const char *path, bool discard)
|
||||||
/**
|
/**
|
||||||
* Called in the main thread after the database update is finished.
|
* Called in the main thread after the database update is finished.
|
||||||
*/
|
*/
|
||||||
static void update_finished_event(void)
|
void
|
||||||
|
UpdateService::RunDeferred()
|
||||||
{
|
{
|
||||||
assert(progress == UPDATE_PROGRESS_DONE);
|
assert(progress == UPDATE_PROGRESS_DONE);
|
||||||
assert(next.IsDefined());
|
assert(next.IsDefined());
|
||||||
|
@ -158,24 +140,16 @@ static void update_finished_event(void)
|
||||||
/* send "idle" events */
|
/* send "idle" events */
|
||||||
instance->DatabaseModified();
|
instance->DatabaseModified();
|
||||||
|
|
||||||
auto i = update_queue_shift();
|
auto i = queue.Pop();
|
||||||
if (i.IsDefined()) {
|
if (i.IsDefined()) {
|
||||||
/* schedule the next path */
|
/* schedule the next path */
|
||||||
spawn_update_task(std::move(i));
|
StartThread(std::move(i));
|
||||||
} else {
|
} else {
|
||||||
progress = UPDATE_PROGRESS_IDLE;
|
progress = UPDATE_PROGRESS_IDLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_global_init(void)
|
UpdateService::UpdateService(EventLoop &_loop)
|
||||||
|
:DeferredMonitor(_loop), walk(_loop)
|
||||||
{
|
{
|
||||||
GlobalEvents::Register(GlobalEvents::UPDATE, update_finished_event);
|
|
||||||
|
|
||||||
update_remove_global_init();
|
|
||||||
update_walk_global_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_global_finish(void)
|
|
||||||
{
|
|
||||||
update_walk_global_finish();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +0,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_UPDATE_GLUE_HXX
|
|
||||||
#define MPD_UPDATE_GLUE_HXX
|
|
||||||
|
|
||||||
#include "Compiler.h"
|
|
||||||
|
|
||||||
void update_global_init(void);
|
|
||||||
|
|
||||||
void update_global_finish(void);
|
|
||||||
|
|
||||||
unsigned
|
|
||||||
isUpdatingDB(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add this path to the database update queue.
|
|
||||||
*
|
|
||||||
* @param path a path to update; if an empty string,
|
|
||||||
* the whole music directory is updated
|
|
||||||
* @return the job id, or 0 on error
|
|
||||||
*/
|
|
||||||
gcc_nonnull_all
|
|
||||||
unsigned
|
|
||||||
update_enqueue(const char *path, bool discard);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -18,11 +18,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h" /* must be first for large file support */
|
#include "config.h" /* must be first for large file support */
|
||||||
#include "UpdateSong.hxx"
|
#include "Service.hxx"
|
||||||
#include "UpdateInternal.hxx"
|
|
||||||
#include "UpdateIO.hxx"
|
#include "UpdateIO.hxx"
|
||||||
#include "UpdateDatabase.hxx"
|
|
||||||
#include "UpdateContainer.hxx"
|
|
||||||
#include "UpdateDomain.hxx"
|
#include "UpdateDomain.hxx"
|
||||||
#include "db/DatabaseLock.hxx"
|
#include "db/DatabaseLock.hxx"
|
||||||
#include "db/Directory.hxx"
|
#include "db/Directory.hxx"
|
||||||
|
@ -32,10 +29,10 @@
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
static void
|
inline void
|
||||||
update_song_file2(Directory &directory,
|
UpdateWalk::UpdateSongFile2(Directory &directory,
|
||||||
const char *name, const struct stat *st,
|
const char *name, const char *suffix,
|
||||||
const char *suffix)
|
const struct stat *st)
|
||||||
{
|
{
|
||||||
db_lock();
|
db_lock();
|
||||||
Song *song = directory.FindSong(name);
|
Song *song = directory.FindSong(name);
|
||||||
|
@ -47,7 +44,7 @@ update_song_file2(Directory &directory,
|
||||||
directory.GetPath(), name);
|
directory.GetPath(), name);
|
||||||
if (song != nullptr) {
|
if (song != nullptr) {
|
||||||
db_lock();
|
db_lock();
|
||||||
delete_song(directory, song);
|
editor.DeleteSong(directory, song);
|
||||||
db_unlock();
|
db_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,10 +53,10 @@ update_song_file2(Directory &directory,
|
||||||
|
|
||||||
if (!(song != nullptr && st->st_mtime == song->mtime &&
|
if (!(song != nullptr && st->st_mtime == song->mtime &&
|
||||||
!walk_discard) &&
|
!walk_discard) &&
|
||||||
update_container_file(directory, name, st, suffix)) {
|
UpdateContainerFile(directory, name, suffix, st)) {
|
||||||
if (song != nullptr) {
|
if (song != nullptr) {
|
||||||
db_lock();
|
db_lock();
|
||||||
delete_song(directory, song);
|
editor.DeleteSong(directory, song);
|
||||||
db_unlock();
|
db_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +89,7 @@ update_song_file2(Directory &directory,
|
||||||
"deleting unrecognized file %s/%s",
|
"deleting unrecognized file %s/%s",
|
||||||
directory.GetPath(), name);
|
directory.GetPath(), name);
|
||||||
db_lock();
|
db_lock();
|
||||||
delete_song(directory, song);
|
editor.DeleteSong(directory, song);
|
||||||
db_unlock();
|
db_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,13 +98,13 @@ update_song_file2(Directory &directory,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
update_song_file(Directory &directory,
|
UpdateWalk::UpdateSongFile(Directory &directory,
|
||||||
const char *name, const char *suffix,
|
const char *name, const char *suffix,
|
||||||
const struct stat *st)
|
const struct stat *st)
|
||||||
{
|
{
|
||||||
if (!decoder_plugins_supports_suffix(suffix))
|
if (!decoder_plugins_supports_suffix(suffix))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
update_song_file2(directory, name, st, suffix);
|
UpdateSongFile2(directory, name, suffix, st);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +0,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_UPDATE_SONG_HXX
|
|
||||||
#define MPD_UPDATE_SONG_HXX
|
|
||||||
|
|
||||||
#include "check.h"
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
struct Directory;
|
|
||||||
|
|
||||||
bool
|
|
||||||
update_song_file(Directory &directory,
|
|
||||||
const char *name, const char *suffix,
|
|
||||||
const struct stat *st);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,37 +0,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_UPDATE_WALK_HXX
|
|
||||||
#define MPD_UPDATE_WALK_HXX
|
|
||||||
|
|
||||||
#include "check.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
update_walk_global_init(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
update_walk_global_finish(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the database was modified.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
update_walk(const char *path, bool discard);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -18,11 +18,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h" /* must be first for large file support */
|
#include "config.h" /* must be first for large file support */
|
||||||
#include "UpdateWalk.hxx"
|
#include "Walk.hxx"
|
||||||
#include "UpdateIO.hxx"
|
#include "UpdateIO.hxx"
|
||||||
#include "UpdateDatabase.hxx"
|
#include "Editor.hxx"
|
||||||
#include "UpdateSong.hxx"
|
|
||||||
#include "UpdateArchive.hxx"
|
|
||||||
#include "UpdateDomain.hxx"
|
#include "UpdateDomain.hxx"
|
||||||
#include "db/DatabaseLock.hxx"
|
#include "db/DatabaseLock.hxx"
|
||||||
#include "db/DatabaseSimple.hxx"
|
#include "db/DatabaseSimple.hxx"
|
||||||
|
@ -49,21 +47,8 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
bool walk_discard;
|
UpdateWalk::UpdateWalk(EventLoop &_loop)
|
||||||
bool modified;
|
:editor(_loop)
|
||||||
|
|
||||||
#ifndef WIN32
|
|
||||||
|
|
||||||
static constexpr bool DEFAULT_FOLLOW_INSIDE_SYMLINKS = true;
|
|
||||||
static constexpr bool DEFAULT_FOLLOW_OUTSIDE_SYMLINKS = true;
|
|
||||||
|
|
||||||
static bool follow_inside_symlinks;
|
|
||||||
static bool follow_outside_symlinks;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void
|
|
||||||
update_walk_global_init(void)
|
|
||||||
{
|
{
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
follow_inside_symlinks =
|
follow_inside_symlinks =
|
||||||
|
@ -76,11 +61,6 @@ update_walk_global_init(void)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
update_walk_global_finish(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
directory_set_stat(Directory &dir, const struct stat *st)
|
directory_set_stat(Directory &dir, const struct stat *st)
|
||||||
{
|
{
|
||||||
|
@ -89,9 +69,9 @@ directory_set_stat(Directory &dir, const struct stat *st)
|
||||||
dir.have_stat = true;
|
dir.have_stat = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
inline void
|
||||||
remove_excluded_from_directory(Directory &directory,
|
UpdateWalk::RemoveExcludedFromDirectory(Directory &directory,
|
||||||
const ExcludeList &exclude_list)
|
const ExcludeList &exclude_list)
|
||||||
{
|
{
|
||||||
db_lock();
|
db_lock();
|
||||||
|
|
||||||
|
@ -100,7 +80,7 @@ remove_excluded_from_directory(Directory &directory,
|
||||||
const auto name_fs = AllocatedPath::FromUTF8(child->GetName());
|
const auto name_fs = AllocatedPath::FromUTF8(child->GetName());
|
||||||
|
|
||||||
if (name_fs.IsNull() || exclude_list.Check(name_fs)) {
|
if (name_fs.IsNull() || exclude_list.Check(name_fs)) {
|
||||||
delete_directory(child);
|
editor.DeleteDirectory(child);
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,7 +91,7 @@ remove_excluded_from_directory(Directory &directory,
|
||||||
|
|
||||||
const auto name_fs = AllocatedPath::FromUTF8(song->uri);
|
const auto name_fs = AllocatedPath::FromUTF8(song->uri);
|
||||||
if (name_fs.IsNull() || exclude_list.Check(name_fs)) {
|
if (name_fs.IsNull() || exclude_list.Check(name_fs)) {
|
||||||
delete_song(directory, song);
|
editor.DeleteSong(directory, song);
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,8 +99,8 @@ remove_excluded_from_directory(Directory &directory,
|
||||||
db_unlock();
|
db_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
inline void
|
||||||
purge_deleted_from_directory(Directory &directory)
|
UpdateWalk::PurgeDeletedFromDirectory(Directory &directory)
|
||||||
{
|
{
|
||||||
Directory *child, *n;
|
Directory *child, *n;
|
||||||
directory_for_each_child_safe(child, n, directory) {
|
directory_for_each_child_safe(child, n, directory) {
|
||||||
|
@ -128,7 +108,7 @@ purge_deleted_from_directory(Directory &directory)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
db_lock();
|
db_lock();
|
||||||
delete_directory(child);
|
editor.DeleteDirectory(child);
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
modified = true;
|
modified = true;
|
||||||
|
@ -139,7 +119,7 @@ purge_deleted_from_directory(Directory &directory)
|
||||||
const auto path = map_song_fs(*song);
|
const auto path = map_song_fs(*song);
|
||||||
if (path.IsNull() || !FileExists(path)) {
|
if (path.IsNull() || !FileExists(path)) {
|
||||||
db_lock();
|
db_lock();
|
||||||
delete_song(directory, song);
|
editor.DeleteSong(directory, song);
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
|
||||||
modified = true;
|
modified = true;
|
||||||
|
@ -195,10 +175,10 @@ find_inode_ancestor(Directory *parent, ino_t inode, dev_t device)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
inline bool
|
||||||
update_playlist_file2(Directory &directory,
|
UpdateWalk::UpdatePlaylistFile(Directory &directory,
|
||||||
const char *name, const char *suffix,
|
const char *name, const char *suffix,
|
||||||
const struct stat *st)
|
const struct stat *st)
|
||||||
{
|
{
|
||||||
if (!playlist_suffix_supported(suffix))
|
if (!playlist_suffix_supported(suffix))
|
||||||
return false;
|
return false;
|
||||||
|
@ -212,30 +192,27 @@ update_playlist_file2(Directory &directory,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
inline bool
|
||||||
update_regular_file(Directory &directory,
|
UpdateWalk::UpdateRegularFile(Directory &directory,
|
||||||
const char *name, const struct stat *st)
|
const char *name, const struct stat *st)
|
||||||
{
|
{
|
||||||
const char *suffix = uri_get_suffix(name);
|
const char *suffix = uri_get_suffix(name);
|
||||||
if (suffix == nullptr)
|
if (suffix == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return update_song_file(directory, name, suffix, st) ||
|
return UpdateSongFile(directory, name, suffix, st) ||
|
||||||
update_archive_file(directory, name, suffix, st) ||
|
UpdateArchiveFile(directory, name, suffix, st) ||
|
||||||
update_playlist_file2(directory, name, suffix, st);
|
UpdatePlaylistFile(directory, name, suffix, st);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
void
|
||||||
update_directory(Directory &directory, const struct stat *st);
|
UpdateWalk::UpdateDirectoryChild(Directory &directory,
|
||||||
|
const char *name, const struct stat *st)
|
||||||
static void
|
|
||||||
update_directory_child(Directory &directory,
|
|
||||||
const char *name, const struct stat *st)
|
|
||||||
{
|
{
|
||||||
assert(strchr(name, '/') == nullptr);
|
assert(strchr(name, '/') == nullptr);
|
||||||
|
|
||||||
if (S_ISREG(st->st_mode)) {
|
if (S_ISREG(st->st_mode)) {
|
||||||
update_regular_file(directory, name, st);
|
UpdateRegularFile(directory, name, st);
|
||||||
} else if (S_ISDIR(st->st_mode)) {
|
} else if (S_ISDIR(st->st_mode)) {
|
||||||
if (find_inode_ancestor(&directory, st->st_ino, st->st_dev))
|
if (find_inode_ancestor(&directory, st->st_ino, st->st_dev))
|
||||||
return;
|
return;
|
||||||
|
@ -246,9 +223,9 @@ update_directory_child(Directory &directory,
|
||||||
|
|
||||||
assert(&directory == subdir->parent);
|
assert(&directory == subdir->parent);
|
||||||
|
|
||||||
if (!update_directory(*subdir, st)) {
|
if (!UpdateDirectory(*subdir, st)) {
|
||||||
db_lock();
|
db_lock();
|
||||||
delete_directory(subdir);
|
editor.DeleteDirectory(subdir);
|
||||||
db_unlock();
|
db_unlock();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -268,8 +245,9 @@ static bool skip_path(Path path_fs)
|
||||||
}
|
}
|
||||||
|
|
||||||
gcc_pure
|
gcc_pure
|
||||||
static bool
|
bool
|
||||||
skip_symlink(const Directory *directory, const char *utf8_name)
|
UpdateWalk::SkipSymlink(const Directory *directory,
|
||||||
|
const char *utf8_name) const
|
||||||
{
|
{
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
const auto path_fs = map_directory_child_fs(*directory, utf8_name);
|
const auto path_fs = map_directory_child_fs(*directory, utf8_name);
|
||||||
|
@ -333,8 +311,8 @@ skip_symlink(const Directory *directory, const char *utf8_name)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
bool
|
||||||
update_directory(Directory &directory, const struct stat *st)
|
UpdateWalk::UpdateDirectory(Directory &directory, const struct stat *st)
|
||||||
{
|
{
|
||||||
assert(S_ISDIR(st->st_mode));
|
assert(S_ISDIR(st->st_mode));
|
||||||
|
|
||||||
|
@ -358,9 +336,9 @@ update_directory(Directory &directory, const struct stat *st)
|
||||||
exclude_list.LoadFile(AllocatedPath::Build(path_fs, ".mpdignore"));
|
exclude_list.LoadFile(AllocatedPath::Build(path_fs, ".mpdignore"));
|
||||||
|
|
||||||
if (!exclude_list.IsEmpty())
|
if (!exclude_list.IsEmpty())
|
||||||
remove_excluded_from_directory(directory, exclude_list);
|
RemoveExcludedFromDirectory(directory, exclude_list);
|
||||||
|
|
||||||
purge_deleted_from_directory(directory);
|
PurgeDeletedFromDirectory(directory);
|
||||||
|
|
||||||
while (reader.ReadEntry()) {
|
while (reader.ReadEntry()) {
|
||||||
std::string utf8;
|
std::string utf8;
|
||||||
|
@ -375,15 +353,15 @@ update_directory(Directory &directory, const struct stat *st)
|
||||||
if (utf8.empty())
|
if (utf8.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (skip_symlink(&directory, utf8.c_str())) {
|
if (SkipSymlink(&directory, utf8.c_str())) {
|
||||||
modified |= delete_name_in(directory, utf8.c_str());
|
modified |= editor.DeleteNameIn(directory, utf8.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stat_directory_child(directory, utf8.c_str(), &st2) == 0)
|
if (stat_directory_child(directory, utf8.c_str(), &st2) == 0)
|
||||||
update_directory_child(directory, utf8.c_str(), &st2);
|
UpdateDirectoryChild(directory, utf8.c_str(), &st2);
|
||||||
else
|
else
|
||||||
modified |= delete_name_in(directory, utf8.c_str());
|
modified |= editor.DeleteNameIn(directory, utf8.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
directory.mtime = st->st_mtime;
|
directory.mtime = st->st_mtime;
|
||||||
|
@ -391,8 +369,8 @@ update_directory(Directory &directory, const struct stat *st)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Directory *
|
inline Directory *
|
||||||
directory_make_child_checked(Directory &parent, const char *name_utf8)
|
UpdateWalk::DirectoryMakeChildChecked(Directory &parent, const char *name_utf8)
|
||||||
{
|
{
|
||||||
db_lock();
|
db_lock();
|
||||||
Directory *directory = parent.FindChild(name_utf8);
|
Directory *directory = parent.FindChild(name_utf8);
|
||||||
|
@ -406,7 +384,7 @@ directory_make_child_checked(Directory &parent, const char *name_utf8)
|
||||||
find_inode_ancestor(&parent, st.st_ino, st.st_dev))
|
find_inode_ancestor(&parent, st.st_ino, st.st_dev))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (skip_symlink(&parent, name_utf8))
|
if (SkipSymlink(&parent, name_utf8))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
/* if we're adding directory paths, make sure to delete filenames
|
/* if we're adding directory paths, make sure to delete filenames
|
||||||
|
@ -414,7 +392,7 @@ directory_make_child_checked(Directory &parent, const char *name_utf8)
|
||||||
db_lock();
|
db_lock();
|
||||||
Song *conflicting = parent.FindSong(name_utf8);
|
Song *conflicting = parent.FindSong(name_utf8);
|
||||||
if (conflicting)
|
if (conflicting)
|
||||||
delete_song(parent, conflicting);
|
editor.DeleteSong(parent, conflicting);
|
||||||
|
|
||||||
directory = parent.CreateChild(name_utf8);
|
directory = parent.CreateChild(name_utf8);
|
||||||
db_unlock();
|
db_unlock();
|
||||||
|
@ -423,8 +401,8 @@ directory_make_child_checked(Directory &parent, const char *name_utf8)
|
||||||
return directory;
|
return directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Directory *
|
inline Directory *
|
||||||
directory_make_uri_parent_checked(const char *uri)
|
UpdateWalk::DirectoryMakeUriParentChecked(const char *uri)
|
||||||
{
|
{
|
||||||
Directory *directory = db_get_root();
|
Directory *directory = db_get_root();
|
||||||
char *duplicated = xstrdup(uri);
|
char *duplicated = xstrdup(uri);
|
||||||
|
@ -436,8 +414,8 @@ directory_make_uri_parent_checked(const char *uri)
|
||||||
if (*name_utf8 == 0)
|
if (*name_utf8 == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
directory = directory_make_child_checked(*directory,
|
directory = DirectoryMakeChildChecked(*directory,
|
||||||
name_utf8);
|
name_utf8);
|
||||||
if (directory == nullptr)
|
if (directory == nullptr)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -448,37 +426,37 @@ directory_make_uri_parent_checked(const char *uri)
|
||||||
return directory;
|
return directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
inline void
|
||||||
update_uri(const char *uri)
|
UpdateWalk::UpdateUri(const char *uri)
|
||||||
{
|
{
|
||||||
Directory *parent = directory_make_uri_parent_checked(uri);
|
Directory *parent = DirectoryMakeUriParentChecked(uri);
|
||||||
if (parent == nullptr)
|
if (parent == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const char *name = PathTraitsUTF8::GetBase(uri);
|
const char *name = PathTraitsUTF8::GetBase(uri);
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (!skip_symlink(parent, name) &&
|
if (!SkipSymlink(parent, name) &&
|
||||||
stat_directory_child(*parent, name, &st) == 0)
|
stat_directory_child(*parent, name, &st) == 0)
|
||||||
update_directory_child(*parent, name, &st);
|
UpdateDirectoryChild(*parent, name, &st);
|
||||||
else
|
else
|
||||||
modified |= delete_name_in(*parent, name);
|
modified |= editor.DeleteNameIn(*parent, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
update_walk(const char *path, bool discard)
|
UpdateWalk::Walk(const char *path, bool discard)
|
||||||
{
|
{
|
||||||
walk_discard = discard;
|
walk_discard = discard;
|
||||||
modified = false;
|
modified = false;
|
||||||
|
|
||||||
if (path != nullptr && !isRootDirectory(path)) {
|
if (path != nullptr && !isRootDirectory(path)) {
|
||||||
update_uri(path);
|
UpdateUri(path);
|
||||||
} else {
|
} else {
|
||||||
Directory *directory = db_get_root();
|
Directory *directory = db_get_root();
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
if (stat_directory(*directory, &st) == 0)
|
if (stat_directory(*directory, &st) == 0)
|
||||||
update_directory(*directory, &st);
|
UpdateDirectory(*directory, &st);
|
||||||
}
|
}
|
||||||
|
|
||||||
return modified;
|
return modified;
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* 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_UPDATE_WALK_HXX
|
||||||
|
#define MPD_UPDATE_WALK_HXX
|
||||||
|
|
||||||
|
#include "check.h"
|
||||||
|
#include "Editor.hxx"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
struct stat;
|
||||||
|
struct Directory;
|
||||||
|
struct archive_plugin;
|
||||||
|
class ExcludeList;
|
||||||
|
|
||||||
|
class UpdateWalk final {
|
||||||
|
#ifdef ENABLE_ARCHIVE
|
||||||
|
friend class UpdateArchiveVisitor;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
static constexpr bool DEFAULT_FOLLOW_INSIDE_SYMLINKS = true;
|
||||||
|
static constexpr bool DEFAULT_FOLLOW_OUTSIDE_SYMLINKS = true;
|
||||||
|
|
||||||
|
bool follow_inside_symlinks;
|
||||||
|
bool follow_outside_symlinks;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool walk_discard;
|
||||||
|
bool modified;
|
||||||
|
|
||||||
|
DatabaseEditor editor;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UpdateWalk(EventLoop &_loop);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the database was modified.
|
||||||
|
*/
|
||||||
|
bool Walk(const char *path, bool discard);
|
||||||
|
|
||||||
|
private:
|
||||||
|
gcc_pure
|
||||||
|
bool SkipSymlink(const Directory *directory,
|
||||||
|
const char *utf8_name) const;
|
||||||
|
|
||||||
|
void RemoveExcludedFromDirectory(Directory &directory,
|
||||||
|
const ExcludeList &exclude_list);
|
||||||
|
|
||||||
|
void PurgeDeletedFromDirectory(Directory &directory);
|
||||||
|
|
||||||
|
void UpdateSongFile2(Directory &directory,
|
||||||
|
const char *name, const char *suffix,
|
||||||
|
const struct stat *st);
|
||||||
|
|
||||||
|
bool UpdateSongFile(Directory &directory,
|
||||||
|
const char *name, const char *suffix,
|
||||||
|
const struct stat *st);
|
||||||
|
|
||||||
|
bool UpdateContainerFile(Directory &directory,
|
||||||
|
const char *name, const char *suffix,
|
||||||
|
const struct stat *st);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ENABLE_ARCHIVE
|
||||||
|
void UpdateArchiveTree(Directory &parent, const char *name);
|
||||||
|
|
||||||
|
bool UpdateArchiveFile(Directory &directory,
|
||||||
|
const char *name, const char *suffix,
|
||||||
|
const struct stat *st);
|
||||||
|
|
||||||
|
void UpdateArchiveFile(Directory &directory, const char *name,
|
||||||
|
const struct stat *st,
|
||||||
|
const archive_plugin &plugin);
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
bool UpdateArchiveFile(gcc_unused Directory &directory,
|
||||||
|
gcc_unused const char *name,
|
||||||
|
gcc_unused const char *suffix,
|
||||||
|
gcc_unused const struct stat *st) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool UpdatePlaylistFile(Directory &directory,
|
||||||
|
const char *name, const char *suffix,
|
||||||
|
const struct stat *st);
|
||||||
|
|
||||||
|
bool UpdateRegularFile(Directory &directory,
|
||||||
|
const char *name, const struct stat *st);
|
||||||
|
|
||||||
|
void UpdateDirectoryChild(Directory &directory,
|
||||||
|
const char *name, const struct stat *st);
|
||||||
|
|
||||||
|
bool UpdateDirectory(Directory &directory, const struct stat *st);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the specified directory object if it does not exist
|
||||||
|
* already or if the #stat object indicates that it has been
|
||||||
|
* modified since the last update. Returns nullptr when it
|
||||||
|
* exists already and is unmodified.
|
||||||
|
*
|
||||||
|
* The caller must lock the database.
|
||||||
|
*/
|
||||||
|
Directory *MakeDirectoryIfModified(Directory &parent, const char *name,
|
||||||
|
const struct stat *st);
|
||||||
|
|
||||||
|
Directory *DirectoryMakeChildChecked(Directory &parent,
|
||||||
|
const char *name_utf8);
|
||||||
|
|
||||||
|
Directory *DirectoryMakeUriParentChecked(const char *uri);
|
||||||
|
|
||||||
|
void UpdateUri(const char *uri);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue