From e598922133d11cc59994f35f62fe0f454ceb4173 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 21 Jul 2010 08:58:15 +0200 Subject: update: store playlist files in database Don't open the music directory for each "lsinfo" call. Get the list of playlist files from the memory database. --- src/directory.c | 2 + src/directory.h | 4 ++ src/directory_print.c | 34 ++------------ src/directory_save.c | 9 ++++ src/playlist_database.c | 76 ++++++++++++++++++++++++++++++ src/playlist_database.h | 40 ++++++++++++++++ src/playlist_vector.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++ src/playlist_vector.h | 68 +++++++++++++++++++++++++++ src/update_walk.c | 31 +++++++++++++ 9 files changed, 353 insertions(+), 31 deletions(-) create mode 100644 src/playlist_database.c create mode 100644 src/playlist_database.h create mode 100644 src/playlist_vector.c create mode 100644 src/playlist_vector.h (limited to 'src') diff --git a/src/directory.c b/src/directory.c index 0ace67dde..0a466a517 100644 --- a/src/directory.c +++ b/src/directory.c @@ -42,6 +42,8 @@ directory_new(const char *path, struct directory *parent) directory->parent = parent; memcpy(directory->path, path, pathlen + 1); + playlist_vector_init(&directory->playlists); + return directory; } diff --git a/src/directory.h b/src/directory.h index 4a137b3a5..4f7470a22 100644 --- a/src/directory.h +++ b/src/directory.h @@ -23,6 +23,7 @@ #include "check.h" #include "dirvec.h" #include "songvec.h" +#include "playlist_vector.h" #include #include @@ -35,6 +36,9 @@ struct directory { struct dirvec children; struct songvec songs; + + struct playlist_vector playlists; + struct directory *parent; time_t mtime; ino_t inode; diff --git a/src/directory_print.c b/src/directory_print.c index ef4738e45..74ff26cb3 100644 --- a/src/directory_print.c +++ b/src/directory_print.c @@ -23,7 +23,6 @@ #include "client.h" #include "song_print.h" #include "mapper.h" -#include "playlist_list.h" #include "decoder_list.h" #include "path.h" #include "uri.h" @@ -61,36 +60,9 @@ static void directory_print_playlists(struct client *client, const struct directory *directory) { - char *path_fs = map_directory_fs(directory); - if (path_fs == NULL) - return; - - DIR *dir = opendir(path_fs); - g_free(path_fs); - if (dir == NULL) - return; - - struct dirent *ent; - while ((ent = readdir(dir))) { - char *name_utf8 = fs_charset_to_utf8(ent->d_name); - if (name_utf8 == NULL) - continue; - - const char *suffix = uri_get_suffix(name_utf8); - if (suffix != NULL && - /* ignore files which are handled by a decoder for - now, too expensive to probe them all, and most - of them probably don't contain a playlist - (e.g. FLAC files without embedded cue sheet) */ - decoder_plugin_from_suffix(suffix, NULL) == NULL && - playlist_suffix_supported(suffix)) - print_playlist_in_directory(client, directory, - name_utf8); - - g_free(name_utf8); - } - - closedir(dir); + for (const struct playlist_metadata *pm = directory->playlists.head; + pm != NULL; pm = pm->next) + print_playlist_in_directory(client, directory, pm->name); } void diff --git a/src/directory_save.c b/src/directory_save.c index 18472db98..af134d9a2 100644 --- a/src/directory_save.c +++ b/src/directory_save.c @@ -23,6 +23,7 @@ #include "song.h" #include "text_file.h" #include "song_save.h" +#include "playlist_database.h" #include #include @@ -69,6 +70,8 @@ directory_save(FILE *fp, struct directory *directory) songvec_save(fp, &directory->songs); + playlist_vector_save(fp, &directory->playlists); + if (!directory_is_root(directory)) fprintf(fp, DIRECTORY_END "%s\n", directory_get_path(directory)); @@ -168,6 +171,12 @@ directory_load(FILE *fp, struct directory *directory, return false; songvec_add(&directory->songs, song); + } else if (g_str_has_prefix(line, PLAYLIST_META_BEGIN)) { + const char *name = line + sizeof(PLAYLIST_META_BEGIN) - 1; + + if (!playlist_metadata_load(fp, &directory->playlists, + name, buffer, error)) + return false; } else { g_set_error(error, directory_quark(), 0, "Malformed line: %s", line); diff --git a/src/playlist_database.c b/src/playlist_database.c new file mode 100644 index 000000000..f74406277 --- /dev/null +++ b/src/playlist_database.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2003-2010 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. + */ + +#include "config.h" +#include "playlist_database.h" +#include "playlist_vector.h" +#include "text_file.h" + +#include +#include + +static GQuark +playlist_database_quark(void) +{ + return g_quark_from_static_string("playlist_database"); +} + +void +playlist_vector_save(FILE *fp, const struct playlist_vector *pv) +{ + for (const struct playlist_metadata *pm = pv->head; + pm != NULL; pm = pm->next) + fprintf(fp, PLAYLIST_META_BEGIN "%s\n" + "mtime: %li\n" + "playlist_end\n", + pm->name, (long)pm->mtime); +} + +bool +playlist_metadata_load(FILE *fp, struct playlist_vector *pv, const char *name, + GString *buffer, GError **error_r) +{ + struct playlist_metadata pm; + char *line, *colon; + const char *value; + + while ((line = read_text_line(fp, buffer)) != NULL && + strcmp(line, "playlist_end") != 0) { + colon = strchr(line, ':'); + if (colon == NULL || colon == line) { + g_set_error(error_r, playlist_database_quark(), 0, + "unknown line in db: %s", line); + return false; + } + + *colon++ = 0; + value = g_strchug(colon); + + if (strcmp(line, "mtime") == 0) + pm.mtime = strtol(value, NULL, 10); + else { + g_set_error(error_r, playlist_database_quark(), 0, + "unknown line in db: %s", line); + return false; + } + } + + playlist_vector_update_or_add(pv, name, pm.mtime); + return true; +} diff --git a/src/playlist_database.h b/src/playlist_database.h new file mode 100644 index 000000000..7e114abdd --- /dev/null +++ b/src/playlist_database.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2003-2010 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_PLAYLIST_DATABASE_H +#define MPD_PLAYLIST_DATABASE_H + +#include "check.h" + +#include +#include +#include + +#define PLAYLIST_META_BEGIN "playlist_begin: " + +struct playlist_vector; + +void +playlist_vector_save(FILE *fp, const struct playlist_vector *pv); + +bool +playlist_metadata_load(FILE *fp, struct playlist_vector *pv, const char *name, + GString *buffer, GError **error_r); + +#endif diff --git a/src/playlist_vector.c b/src/playlist_vector.c new file mode 100644 index 000000000..80b70a96c --- /dev/null +++ b/src/playlist_vector.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2003-2010 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. + */ + +#include "config.h" +#include "playlist_vector.h" + +#include +#include +#include + +static struct playlist_metadata * +playlist_metadata_new(const char *name, time_t mtime) +{ + assert(name != NULL); + + struct playlist_metadata *pm = g_slice_new(struct playlist_metadata); + pm->name = g_strdup(name); + pm->mtime = mtime; + return pm; +} + +static void +playlist_metadata_free(struct playlist_metadata *pm) +{ + assert(pm != NULL); + assert(pm->name != NULL); + + g_free(pm->name); + g_slice_free(struct playlist_metadata, pm); +} + +void +playlist_vector_deinit(struct playlist_vector *pv) +{ + assert(pv != NULL); + + while (pv->head != NULL) { + struct playlist_metadata *pm = pv->head; + pv->head = pm->next; + playlist_metadata_free(pm); + } +} + +static struct playlist_metadata ** +playlist_vector_find_p(struct playlist_vector *pv, const char *name) +{ + assert(pv != NULL); + assert(name != NULL); + + struct playlist_metadata **pmp = &pv->head; + + for (;;) { + struct playlist_metadata *pm = *pmp; + if (pm == NULL) + return NULL; + + if (strcmp(pm->name, name) == 0) + return pmp; + + pmp = &pm->next; + } +} + +struct playlist_metadata * +playlist_vector_find(struct playlist_vector *pv, const char *name) +{ + struct playlist_metadata **pmp = playlist_vector_find_p(pv, name); + return pmp != NULL ? *pmp : NULL; +} + +void +playlist_vector_add(struct playlist_vector *pv, + const char *name, time_t mtime) +{ + struct playlist_metadata *pm = playlist_metadata_new(name, mtime); + pm->next = pv->head; + pv->head = pm; +} + +void +playlist_vector_update_or_add(struct playlist_vector *pv, + const char *name, time_t mtime) +{ + struct playlist_metadata **pmp = playlist_vector_find_p(pv, name); + if (pmp != NULL) { + struct playlist_metadata *pm = *pmp; + pm->mtime = mtime; + } else + playlist_vector_add(pv, name, mtime); +} + +bool +playlist_vector_remove(struct playlist_vector *pv, const char *name) +{ + struct playlist_metadata **pmp = playlist_vector_find_p(pv, name); + if (pmp == NULL) + return false; + + struct playlist_metadata *pm = *pmp; + *pmp = pm->next; + + playlist_metadata_free(pm); + return true; +} diff --git a/src/playlist_vector.h b/src/playlist_vector.h new file mode 100644 index 000000000..a2ba306d3 --- /dev/null +++ b/src/playlist_vector.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2003-2010 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_PLAYLIST_VECTOR_H +#define MPD_PLAYLIST_VECTOR_H + +#include +#include +#include + +/** + * A directory entry pointing to a playlist file. + */ +struct playlist_metadata { + struct playlist_metadata *next; + + /** + * The UTF-8 encoded name of the playlist file. + */ + char *name; + + time_t mtime; +}; + +struct playlist_vector { + struct playlist_metadata *head; +}; + +static inline void +playlist_vector_init(struct playlist_vector *pv) +{ + pv->head = NULL; +} + +void +playlist_vector_deinit(struct playlist_vector *pv); + +struct playlist_metadata * +playlist_vector_find(struct playlist_vector *pv, const char *name); + +void +playlist_vector_add(struct playlist_vector *pv, + const char *name, time_t mtime); + +void +playlist_vector_update_or_add(struct playlist_vector *pv, + const char *name, time_t mtime); + +bool +playlist_vector_remove(struct playlist_vector *pv, const char *name); + +#endif /* SONGVEC_H */ diff --git a/src/update_walk.c b/src/update_walk.c index b8c740ae0..bd5714def 100644 --- a/src/update_walk.c +++ b/src/update_walk.c @@ -28,6 +28,7 @@ #include "path.h" #include "decoder_list.h" #include "decoder_plugin.h" +#include "playlist_list.h" #include "conf.h" #ifdef ENABLE_ARCHIVE @@ -159,6 +160,8 @@ delete_name_in(struct directory *parent, const char *name) delete_song(parent, song); modified = true; } + + playlist_vector_remove(&parent->playlists, name); } /* passed to songvec_for_each */ @@ -244,6 +247,21 @@ directory_exists(const struct directory *directory) return exists; } +static bool +directory_child_is_regular(const struct directory *directory, + const char *name_utf8) +{ + char *path_fs = map_directory_child_fs(directory, name_utf8); + if (path_fs == NULL) + return false; + + struct stat st; + bool is_regular = stat(path_fs, &st) == 0 && S_ISREG(st.st_mode); + g_free(path_fs); + + return is_regular; +} + static void removeDeletedFromDirectory(struct directory *directory) { @@ -260,6 +278,16 @@ removeDeletedFromDirectory(struct directory *directory) } songvec_for_each(&directory->songs, delete_song_if_removed, directory); + + for (const struct playlist_metadata *pm = directory->playlists.head; + pm != NULL;) { + const struct playlist_metadata *next = pm->next; + + if (!directory_child_is_regular(directory, pm->name)) + playlist_vector_remove(&directory->playlists, pm->name); + + pm = next; + } } static int @@ -574,6 +602,9 @@ update_regular_file(struct directory *directory, } else if ((archive = archive_plugin_from_suffix(suffix))) { update_archive_file(directory, name, st, archive); #endif + + } else if (playlist_suffix_supported(suffix)) { + playlist_vector_add(&directory->playlists, name, st->st_mtime); } } -- cgit v1.2.3