diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index fc9a9363..c0297a0c 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -2182,6 +2182,7 @@ queue_tracks_add_playlist(const char *id, int pos) static int queue_tracks_add_byuris(const char *param, int pos, int *total_count) { + struct player_status status; char *uris; char *uri; char *ptr; @@ -2227,7 +2228,9 @@ queue_tracks_add_byuris(const char *param, int pos, int *total_count) } else { - ret = library_queue_add(uri, pos, &count, NULL); + player_get_status(&status); + + ret = library_queue_item_add(uri, pos, status.shuffle, status.item_id, &count, NULL); if (ret != LIBRARY_OK) { DPRINTF(E_LOG, L_WEB, "Invalid uri '%s'\n", uri); diff --git a/src/library.c b/src/library.c index 515ece84..78bdc49a 100644 --- a/src/library.c +++ b/src/library.c @@ -48,12 +48,22 @@ #include "listener.h" #include "player.h" -struct playlist_add_param +struct playlist_item_add_param { const char *vp_playlist; const char *vp_item; }; +struct queue_item_add_param +{ + const char *path; + int position; + char reshuffle; + uint32_t item_id; + int *count; + int *new_item_id; +}; + static struct commands_base *cmdbase; static pthread_t tid_library; @@ -95,27 +105,11 @@ static struct event *updateev; static unsigned int deferred_update_notifications; static short deferred_update_events; -static bool -handle_deferred_update_notifications(void) -{ - time_t update_time; - bool ret = (deferred_update_notifications > 0); - if (ret) - { - DPRINTF(E_DBG, L_LIB, "Database changed (%d changes)\n", deferred_update_notifications); - - deferred_update_notifications = 0; - update_time = time(NULL); - db_admin_setint64(DB_ADMIN_DB_UPDATE, (int64_t) update_time); - db_admin_setint64(DB_ADMIN_DB_MODIFIED, (int64_t) update_time); - } - - return ret; -} +/* ------------------- CALLED BY LIBRARY SOURCE MODULES -------------------- */ void -library_add_media(struct media_file_info *mfi) +library_media_save(struct media_file_info *mfi) { if (!mfi->path || !mfi->fname) { @@ -138,106 +132,48 @@ library_add_media(struct media_file_info *mfi) } int -library_queue_add(const char *path, int position, int *count, int *new_item_id) +library_playlist_save(struct playlist_info *pli) { - struct player_status status; - int i; - int ret; - - DPRINTF(E_DBG, L_LIB, "Add items for path '%s' to the queue\n", path); - - player_get_status(&status); - - ret = LIBRARY_PATH_INVALID; - for (i = 0; sources[i] && ret == LIBRARY_PATH_INVALID; i++) + if (!pli->path) { - if (sources[i]->disabled || !sources[i]->queue_add) - { - DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support queue_add\n", sources[i]->name); - continue; - } - - ret = sources[i]->queue_add(path, position, status.shuffle, status.item_id, count, new_item_id); - - if (ret == LIBRARY_OK) - { - DPRINTF(E_DBG, L_LIB, "Items for path '%s' from library source '%s' added to the queue\n", path, sources[i]->name); - break; - } + DPRINTF(E_LOG, L_LIB, "Ignoring playlist file with missing path\n"); + return -1; } - if (ret != LIBRARY_OK) - DPRINTF(E_LOG, L_LIB, "Failed to add items for path '%s' to the queue (%d)\n", path, ret); + if (!pli->directory_id || !pli->virtual_path) + { + // Missing informations for virtual_path and directory_id (may) lead to misplaced appearance in mpd clients + DPRINTF(E_WARN, L_LIB, "Playlist with missing values (path='%s', directory='%d', virtual_path='%s')\n", + pli->path, pli->directory_id, pli->virtual_path); + } - return ret; + if (pli->id == 0) + return db_pl_add(pli, NULL); + else + return db_pl_update(pli); } -int -library_add_playlist_info(const char *path, const char *title, const char *virtual_path, enum pl_type type, int parent_pl_id, int dir_id) + +/* ---------------------- LIBRARY ABSTRACTION --------------------- */ +/* thread: library */ + +static bool +handle_deferred_update_notifications(void) { - struct playlist_info *pli; - int plid; - int ret; + time_t update_time; + bool ret = (deferred_update_notifications > 0); - pli = db_pl_fetch_bypath(path); - if (pli) + if (ret) { - DPRINTF(E_DBG, L_LIB, "Playlist found ('%s', link %s), updating\n", title, path); + DPRINTF(E_DBG, L_LIB, "Database changed (%d changes)\n", deferred_update_notifications); - plid = pli->id; - - pli->type = type; - free(pli->title); - pli->title = strdup(title); - if (pli->virtual_path) - free(pli->virtual_path); - pli->virtual_path = safe_strdup(virtual_path); - pli->directory_id = dir_id; - - ret = db_pl_update(pli); - if (ret < 0) - { - DPRINTF(E_LOG, L_LIB, "Error updating playlist ('%s', link %s)\n", title, path); - - free_pli(pli, 0); - return -1; - } - - db_pl_clear_items(plid); - } - else - { - DPRINTF(E_DBG, L_LIB, "Adding playlist ('%s', link %s)\n", title, path); - - pli = (struct playlist_info *)malloc(sizeof(struct playlist_info)); - if (!pli) - { - DPRINTF(E_LOG, L_LIB, "Out of memory\n"); - - return -1; - } - - memset(pli, 0, sizeof(struct playlist_info)); - - pli->type = type; - pli->title = strdup(title); - pli->path = strdup(path); - pli->virtual_path = safe_strdup(virtual_path); - pli->parent_id = parent_pl_id; - pli->directory_id = dir_id; - - ret = db_pl_add(pli, &plid); - if ((ret < 0) || (plid < 1)) - { - DPRINTF(E_LOG, L_LIB, "Error adding playlist ('%s', link %s, ret %d, plid %d)\n", title, path, ret, plid); - - free_pli(pli, 0); - return -1; - } + deferred_update_notifications = 0; + update_time = time(NULL); + db_admin_setint64(DB_ADMIN_DB_UPDATE, (int64_t) update_time); + db_admin_setint64(DB_ADMIN_DB_MODIFIED, (int64_t) update_time); } - free_pli(pli, 0); - return plid; + return ret; } static void @@ -378,9 +314,135 @@ fullrescan(void *arg, int *ret) return COMMAND_END; } -/* - * Callback to notify listeners of database changes - */ +static enum command_state +playlist_item_add(void *arg, int *retval) +{ + struct playlist_item_add_param *param = arg; + int i; + int ret = LIBRARY_ERROR; + + DPRINTF(E_DBG, L_LIB, "Adding item '%s' to playlist '%s'\n", param->vp_item, param->vp_playlist); + + for (i = 0; sources[i]; i++) + { + if (sources[i]->disabled || !sources[i]->playlist_item_add) + { + DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support playlist_item_add\n", sources[i]->name); + continue; + } + + ret = sources[i]->playlist_item_add(param->vp_playlist, param->vp_item); + + if (ret == LIBRARY_OK) + { + DPRINTF(E_DBG, L_LIB, "Adding item '%s' to playlist '%s' with library source '%s'\n", param->vp_item, param->vp_playlist, sources[i]->name); + listener_notify(LISTENER_STORED_PLAYLIST); + break; + } + } + + *retval = ret; + return COMMAND_END; +} + +static enum command_state +playlist_remove(void *arg, int *retval) +{ + const char *virtual_path = arg; + int i; + int ret = LIBRARY_ERROR; + + DPRINTF(E_DBG, L_LIB, "Removing playlist at path '%s'\n", virtual_path); + + for (i = 0; sources[i]; i++) + { + if (sources[i]->disabled || !sources[i]->playlist_remove) + { + DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support playlist_remove\n", sources[i]->name); + continue; + } + + ret = sources[i]->playlist_remove(virtual_path); + + if (ret == LIBRARY_OK) + { + DPRINTF(E_DBG, L_LIB, "Removing playlist '%s' with library source '%s'\n", virtual_path, sources[i]->name); + listener_notify(LISTENER_STORED_PLAYLIST); + break; + } + } + + *retval = ret; + return COMMAND_END; +} + +static enum command_state +queue_item_add(void *arg, int *retval) +{ + struct queue_item_add_param *param = arg; + int i; + int ret; + + DPRINTF(E_DBG, L_LIB, "Add items for path '%s' to the queue\n", param->path); + + ret = LIBRARY_PATH_INVALID; + for (i = 0; sources[i] && ret == LIBRARY_PATH_INVALID; i++) + { + if (sources[i]->disabled || !sources[i]->queue_item_add) + { + DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support queue_add\n", sources[i]->name); + continue; + } + + ret = sources[i]->queue_item_add(param->path, param->position, param->reshuffle, param->item_id, param->count, param->new_item_id); + + if (ret == LIBRARY_OK) + { + DPRINTF(E_DBG, L_LIB, "Items for path '%s' from library source '%s' added to the queue\n", param->path, sources[i]->name); + break; + } + } + + if (ret != LIBRARY_OK) + DPRINTF(E_LOG, L_LIB, "Failed to add items for path '%s' to the queue (%d)\n", param->path, ret); + + *retval = ret; + return COMMAND_END; +} + +static enum command_state +queue_save(void *arg, int *retval) +{ + const char *virtual_path = arg; + int i; + int ret = LIBRARY_ERROR; + + DPRINTF(E_DBG, L_LIB, "Saving queue to path '%s'\n", virtual_path); + + for (i = 0; sources[i]; i++) + { + if (sources[i]->disabled || !sources[i]->queue_save) + { + DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support queue_save\n", sources[i]->name); + continue; + } + + ret = sources[i]->queue_save(virtual_path); + + if (ret == LIBRARY_OK) + { + DPRINTF(E_DBG, L_LIB, "Saving queue to path '%s' with library source '%s'\n", virtual_path, sources[i]->name); + listener_notify(LISTENER_STORED_PLAYLIST); + break; + } + } + + *retval = ret; + return COMMAND_END; +} + + +// Callback to notify listeners of database changes static void update_trigger_cb(int fd, short what, void *arg) { @@ -410,7 +472,7 @@ update_trigger(void *arg, int *retval) } -/* --------------------------- LIBRARY INTERFACE -------------------------- */ +/* ----------------------- LIBRARY EXTERNAL INTERFACE ---------------------- */ void library_rescan() @@ -437,6 +499,7 @@ library_metarescan() scanning = true; // TODO Guard "scanning" with a mutex commands_exec_async(cmdbase, metarescan, NULL); } + void library_fullrescan() { @@ -494,39 +557,24 @@ initscan() listener_notify(LISTENER_UPDATE); } -/* - * @return true if scan is running, otherwise false - */ bool library_is_scanning() { return scanning; } -/* - * @param is_scanning true if scan is running, otherwise false - */ void library_set_scanning(bool is_scanning) { scanning = is_scanning; } -/* - * @return true if a running scan should be aborted due to imminent shutdown, otherwise false - */ bool library_is_exiting() { return scan_exit; } -/* - * Trigger for sending the DATABASE event - * - * Needs to be called, if an update to the database (library tables) occurred. The DATABASE event - * is emitted with the delay 'library_update_wait'. It is safe to call this function from any thread. - */ void library_update_trigger(short update_events) { @@ -547,81 +595,17 @@ library_update_trigger(short update_events) } } -static enum command_state -playlist_add(void *arg, int *retval) -{ - struct playlist_add_param *param = arg; - int i; - int ret = LIBRARY_ERROR; - - DPRINTF(E_DBG, L_LIB, "Adding item '%s' to playlist '%s'\n", param->vp_item, param->vp_playlist); - - for (i = 0; sources[i]; i++) - { - if (sources[i]->disabled || !sources[i]->playlist_add) - { - DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support playlist_add\n", sources[i]->name); - continue; - } - - ret = sources[i]->playlist_add(param->vp_playlist, param->vp_item); - - if (ret == LIBRARY_OK) - { - DPRINTF(E_DBG, L_LIB, "Adding item '%s' to playlist '%s' with library source '%s'\n", param->vp_item, param->vp_playlist, sources[i]->name); - listener_notify(LISTENER_STORED_PLAYLIST); - break; - } - } - - *retval = ret; - return COMMAND_END; -} - int -library_playlist_add(const char *vp_playlist, const char *vp_item) +library_playlist_item_add(const char *vp_playlist, const char *vp_item) { - struct playlist_add_param param; + struct playlist_item_add_param param; if (library_is_scanning()) return -1; param.vp_playlist = vp_playlist; param.vp_item = vp_item; - return commands_exec_sync(cmdbase, playlist_add, NULL, ¶m); -} - -static enum command_state -playlist_remove(void *arg, int *retval) -{ - const char *virtual_path; - int i; - int ret = LIBRARY_ERROR; - - virtual_path = arg; - - DPRINTF(E_DBG, L_LIB, "Removing playlist at path '%s'\n", virtual_path); - - for (i = 0; sources[i]; i++) - { - if (sources[i]->disabled || !sources[i]->playlist_remove) - { - DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support playlist_remove\n", sources[i]->name); - continue; - } - - ret = sources[i]->playlist_remove(virtual_path); - - if (ret == LIBRARY_OK) - { - DPRINTF(E_DBG, L_LIB, "Removing playlist '%s' with library source '%s'\n", virtual_path, sources[i]->name); - listener_notify(LISTENER_STORED_PLAYLIST); - break; - } - } - - *retval = ret; - return COMMAND_END; + return commands_exec_sync(cmdbase, playlist_item_add, NULL, ¶m); } int @@ -633,39 +617,6 @@ library_playlist_remove(char *virtual_path) return commands_exec_sync(cmdbase, playlist_remove, NULL, virtual_path); } -static enum command_state -queue_save(void *arg, int *retval) -{ - const char *virtual_path; - int i; - int ret = LIBRARY_ERROR; - - virtual_path = arg; - - DPRINTF(E_DBG, L_LIB, "Saving queue to path '%s'\n", virtual_path); - - for (i = 0; sources[i]; i++) - { - if (sources[i]->disabled || !sources[i]->queue_save) - { - DPRINTF(E_DBG, L_LIB, "Library source '%s' is disabled or does not support queue_save\n", sources[i]->name); - continue; - } - - ret = sources[i]->queue_save(virtual_path); - - if (ret == LIBRARY_OK) - { - DPRINTF(E_DBG, L_LIB, "Saving queue to path '%s' with library source '%s'\n", virtual_path, sources[i]->name); - listener_notify(LISTENER_STORED_PLAYLIST); - break; - } - } - - *retval = ret; - return COMMAND_END; -} - int library_queue_save(char *path) { @@ -675,15 +626,24 @@ library_queue_save(char *path) return commands_exec_sync(cmdbase, queue_save, NULL, path); } -/* - * Execute the function 'func' with the given argument 'arg' in the library thread. - * - * The pointer passed as argument is freed in the library thread after func returned. - * - * @param func The function to be executed - * @param arg Argument passed to func - * @return 0 if triggering the function execution succeeded, -1 on failure. - */ +int +library_queue_item_add(const char *path, int position, char reshuffle, uint32_t item_id, int *count, int *new_item_id) +{ + struct queue_item_add_param param; + + if (library_is_scanning()) + return -1; + + param.path = path; + param.position = position; + param.reshuffle = reshuffle; + param.item_id = item_id; + param.count = count; + param.new_item_id = new_item_id; + + return commands_exec_sync(cmdbase, queue_item_add, NULL, ¶m); +} + int library_exec_async(command_function func, void *arg) { diff --git a/src/library.h b/src/library.h index 1d2bd8f3..b8780339 100644 --- a/src/library.h +++ b/src/library.h @@ -72,9 +72,9 @@ struct library_source int (*fullrescan)(void); /* - * Save queue as a new playlist under the given virtual path + * Add item to playlist */ - int (*playlist_add)(const char *vp_playlist, const char *vp_item); + int (*playlist_item_add)(const char *vp_playlist, const char *vp_item); /* * Removes the playlist under the given virtual path @@ -89,17 +89,18 @@ struct library_source /* * Add item for the given path to the current queue */ - int (*queue_add)(const char *path, int position, char reshuffle, uint32_t item_id, int *count, int *new_item_id); + int (*queue_item_add)(const char *path, int position, char reshuffle, uint32_t item_id, int *count, int *new_item_id); }; +/* --------------------- Interface towards source backends ----------------- */ + void -library_add_media(struct media_file_info *mfi); +library_media_save(struct media_file_info *mfi); int -library_add_playlist_info(const char *path, const char *title, const char *virtual_path, enum pl_type type, int parent_pl_id, int dir_id); +library_playlist_save(struct playlist_info *pli); -int -library_queue_add(const char *path, int position, int *count, int *new_item_id); +/* ------------------------ Library external interface --------------------- */ void library_rescan(); @@ -110,20 +111,35 @@ library_metarescan(); void library_fullrescan(); +/* + * @return true if scan is running, otherwise false + */ bool library_is_scanning(); +/* + * @param is_scanning true if scan is running, otherwise false + */ void library_set_scanning(bool is_scanning); +/* + * @return true if a running scan should be aborted due to imminent shutdown, otherwise false + */ bool library_is_exiting(); +/* + * Trigger for sending the DATABASE event + * + * Needs to be called, if an update to the database (library tables) occurred. The DATABASE event + * is emitted with the delay 'library_update_wait'. It is safe to call this function from any thread. + */ void library_update_trigger(short update_events); int -library_playlist_add(const char *vp_playlist, const char *vp_item); +library_playlist_item_add(const char *vp_playlist, const char *vp_item); int library_playlist_remove(char *virtual_path); @@ -131,6 +147,18 @@ library_playlist_remove(char *virtual_path); int library_queue_save(char *path); +int +library_queue_item_add(const char *path, int position, char reshuffle, uint32_t item_id, int *count, int *new_item_id); + +/* + * Execute the function 'func' with the given argument 'arg' in the library thread. + * + * The pointer passed as argument is freed in the library thread after func returned. + * + * @param func The function to be executed + * @param arg Argument passed to func + * @return 0 if triggering the function execution succeeded, -1 on failure. + */ int library_exec_async(command_function func, void *arg); diff --git a/src/library/filescanner.c b/src/library/filescanner.c index 995f0908..a84ce2fd 100644 --- a/src/library/filescanner.c +++ b/src/library/filescanner.c @@ -538,7 +538,7 @@ process_regular_file(const char *file, struct stat *sb, int type, int flags, int } } - library_add_media(&mfi); + library_media_save(&mfi); cache_artwork_ping(file, sb->st_mtime, !is_bulkscan); // TODO [artworkcache] If entry in artwork cache exists for no artwork available, delete the entry if media file has embedded artwork @@ -676,13 +676,14 @@ check_speciallib(char *path, const char *libtype) /* Thread: scan */ static int -create_virtual_path(char *path, char *virtual_path, int virtual_path_len) +virtual_path_make(char *virtual_path, int virtual_path_len, const char *path) { int ret; + ret = snprintf(virtual_path, virtual_path_len, "/file:%s", path); if ((ret < 0) || (ret >= virtual_path_len)) { - DPRINTF(E_LOG, L_SCAN, "Virtual path /file:%s, PATH_MAX exceeded\n", path); + DPRINTF(E_LOG, L_SCAN, "Virtual path '/file:%s', virtual_path_len exceeded (%d/%d)\n", path, ret, virtual_path_len); return -1; } @@ -766,7 +767,7 @@ process_directory(char *path, int parent_id, int flags) /* Add/update directories table */ - ret = create_virtual_path(path, virtual_path, sizeof(virtual_path)); + ret = virtual_path_make(virtual_path, sizeof(virtual_path), path); if (ret < 0) return; @@ -893,7 +894,7 @@ process_parent_directories(char *path) strncpy(buf, path, (ptr - path)); buf[(ptr - path)] = '\0'; - ret = create_virtual_path(buf, virtual_path, sizeof(virtual_path)); + ret = virtual_path_make(virtual_path, sizeof(virtual_path), buf); if (ret < 0) return 0; @@ -1028,7 +1029,7 @@ get_parent_dir_id(const char *path) pathcopy = strdup(path); parent_dir = dirname(pathcopy); - ret = create_virtual_path(parent_dir, virtual_path, sizeof(virtual_path)); + ret = virtual_path_make(virtual_path, sizeof(virtual_path), parent_dir); if (ret == 0) parent_id = db_directory_id_byvirtualpath(virtual_path); else @@ -1222,7 +1223,6 @@ process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie uint32_t path_hash; char *file = path; char resolved_path[PATH_MAX]; - char *dir; char dir_vpath[PATH_MAX]; int type; int i; @@ -1291,14 +1291,12 @@ process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie if (ret > 0) { - // If file was successfully enabled, update the directory id - dir = strdup(path); - ptr = strrchr(dir, '/'); - dir[(ptr - dir)] = '\0'; - - ret = create_virtual_path(dir, dir_vpath, sizeof(dir_vpath)); + ret = virtual_path_make(dir_vpath, sizeof(dir_vpath), path); if (ret >= 0) { + ptr = strrchr(dir_vpath, '/'); + *ptr = '\0'; + dir_id = db_directory_id_byvirtualpath(dir_vpath); if (dir_id > 0) { @@ -1307,8 +1305,6 @@ process_inotify_file(struct watch_info *wi, char *path, struct inotify_event *ie DPRINTF(E_LOG, L_SCAN, "Error updating directory id for file: %s\n", path); } } - - free(dir); } else { @@ -1719,7 +1715,7 @@ map_media_file_to_queue_item(struct db_queue_item *queue_item, struct media_file } static int -queue_add_stream(const char *path, int position, char reshuffle, uint32_t item_id, int *count, int *new_item_id) +queue_item_stream_add(const char *path, int position, char reshuffle, uint32_t item_id, int *count, int *new_item_id) { struct media_file_info mfi; struct db_queue_item item; @@ -1753,11 +1749,11 @@ queue_add_stream(const char *path, int position, char reshuffle, uint32_t item_i } static int -queue_add(const char *uri, int position, char reshuffle, uint32_t item_id, int *count, int *new_item_id) +queue_item_add(const char *uri, int position, char reshuffle, uint32_t item_id, int *count, int *new_item_id) { if (strncasecmp(uri, "http://", strlen("http://")) == 0 || strncasecmp(uri, "https://", strlen("https://")) == 0) { - queue_add_stream(uri, position, reshuffle, item_id, count, new_item_id); + queue_item_stream_add(uri, position, reshuffle, item_id, count, new_item_id); return LIBRARY_OK; } @@ -1828,7 +1824,7 @@ has_suffix(const char *file, const char *suffix) * Returns NULL on error and a new allocated path on success. */ static char * -get_playlist_path(const char *vp_playlist) +playlist_path_create(const char *vp_playlist) { const char *path; char *pl_path; @@ -1863,27 +1859,6 @@ get_playlist_path(const char *vp_playlist) return pl_path; } -static int -get_playlist_id(const char *pl_path, const char *vp_playlist) -{ - const char *filename; - char *title; - int dir_id; - int pl_id; - - pl_id = db_pl_id_bypath(pl_path); - if (pl_id < 0) - { - dir_id = get_parent_dir_id(pl_path); - filename = filename_from_path(pl_path); - title = strip_extension(filename); - pl_id = library_add_playlist_info(pl_path, title, vp_playlist, PL_PLAIN, 0, dir_id); - free(title); - } - - return pl_id; -} - static int playlist_add_path(FILE *fp, int pl_id, const char *path) { @@ -1950,7 +1925,7 @@ playlist_add_files(FILE *fp, int pl_id, const char *virtual_path) memset(&mfi, 0, sizeof(struct media_file_info)); scan_metadata_stream(path, &mfi); - library_add_media(&mfi); + library_media_save(&mfi); free_mfi(&mfi, 1); ret = playlist_add_path(fp, pl_id, path); @@ -1967,15 +1942,60 @@ playlist_add_files(FILE *fp, int pl_id, const char *virtual_path) return ret; } +// Fills a basic pli template based on a path. Caller can then modify content +// before saving the playlist. +int +playlist_template_fill(struct playlist_info *pli, const char *path) +{ + const char *filename; + char virtual_path[PATH_MAX]; + int ret; + + filename = filename_from_path(path); + + memset(pli, 0, sizeof(struct playlist_info)); + + pli->type = PL_PLAIN; + pli->path = strdup(path); + pli->title = strip_extension(filename); // Will alloc + + ret = virtual_path_make(virtual_path, sizeof(virtual_path), path); + if (ret < 0) + return -1; + pli->virtual_path = strip_extension(virtual_path); // Will alloc + + pli->directory_id = get_parent_dir_id(path); + + return 0; +} + static int -playlist_add(const char *vp_playlist, const char *vp_item) +playlist_create(const char *path) +{ + struct playlist_info pli; + int ret; + + ret = playlist_template_fill(&pli, path); + if (ret < 0) + return -1; + + ret = library_playlist_save(&pli); + free_pli(&pli, 1); + if (ret < 0) + return -1; + + return db_pl_id_bypath(path); +} + +static int +playlist_item_add(const char *vp_playlist, const char *vp_item) { char *pl_path; FILE *fp; int pl_id; int ret; - pl_path = get_playlist_path(vp_playlist); + pl_path = playlist_path_create(vp_playlist); if (!pl_path) return LIBRARY_PATH_INVALID; @@ -1983,31 +2003,36 @@ playlist_add(const char *vp_playlist, const char *vp_item) if (!fp) { DPRINTF(E_LOG, L_SCAN, "Error opening file '%s' for writing: %d\n", pl_path, errno); - free(pl_path); - return LIBRARY_ERROR; + goto error; } - pl_id = get_playlist_id(pl_path, vp_playlist); - free(pl_path); + pl_id = db_pl_id_bypath(pl_path); if (pl_id < 0) { - DPRINTF(E_LOG, L_SCAN, "Could not get playlist id for %s\n", vp_playlist); - fclose(fp); - return LIBRARY_ERROR; + pl_id = playlist_create(pl_path); + if (pl_id < 0) + goto error; } ret = playlist_add_files(fp, pl_id, vp_item); - fclose(fp); - if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "Could not add %s to playlist\n", vp_item); - return LIBRARY_ERROR; + goto error; } + fclose(fp); + free(pl_path); + db_pl_ping(pl_id); return LIBRARY_OK; + + error: + if (fp) + fclose(fp); + free(pl_path); + return LIBRARY_ERROR; } static int @@ -2018,7 +2043,7 @@ playlist_remove(const char *vp_playlist) int pl_id; int ret; - pl_path = get_playlist_path(vp_playlist); + pl_path = playlist_path_create(vp_playlist); if (!pl_path) { DPRINTF(E_LOG, L_SCAN, "Unsupported virtual path '%s'\n", vp_playlist); @@ -2059,7 +2084,7 @@ queue_save(const char *virtual_path) int pl_id; int ret; - pl_path = get_playlist_path(virtual_path); + pl_path = playlist_path_create(virtual_path); if (!pl_path) return LIBRARY_PATH_INVALID; @@ -2067,17 +2092,15 @@ queue_save(const char *virtual_path) if (!fp) { DPRINTF(E_LOG, L_SCAN, "Error opening file '%s' for writing: %d\n", pl_path, errno); - free(pl_path); - return LIBRARY_ERROR; + goto error; } - pl_id = get_playlist_id(pl_path, virtual_path); - free(pl_path); + pl_id = db_pl_id_bypath(pl_path); if (pl_id < 0) { - DPRINTF(E_LOG, L_SCAN, "Could not get playlist id for %s\n", virtual_path); - fclose(fp); - return LIBRARY_ERROR; + pl_id = playlist_create(pl_path); + if (pl_id < 0) + goto error; } memset(&query_params, 0, sizeof(struct query_params)); @@ -2085,8 +2108,7 @@ queue_save(const char *virtual_path) if (ret < 0) { DPRINTF(E_LOG, L_SCAN, "Failed to start queue enum\n"); - fclose(fp); - return LIBRARY_ERROR; + goto error; } while ((ret = db_queue_enum_fetch(&query_params, &queue_item)) == 0 && queue_item.id > 0) @@ -2106,7 +2128,7 @@ queue_save(const char *virtual_path) memset(&mfi, 0, sizeof(struct media_file_info)); scan_metadata_stream(queue_item.path, &mfi); - library_add_media(&mfi); + library_media_save(&mfi); free_mfi(&mfi, 1); } else @@ -2131,7 +2153,9 @@ queue_save(const char *virtual_path) } db_queue_enum_end(&query_params); + fclose(fp); + free(pl_path); db_pl_ping(pl_id); @@ -2139,6 +2163,12 @@ queue_save(const char *virtual_path) return LIBRARY_ERROR; return LIBRARY_OK; + + error: + if (fp) + fclose(fp); + free(pl_path); + return LIBRARY_ERROR; } /* Thread: main */ @@ -2174,8 +2204,8 @@ struct library_source filescanner = .rescan = filescanner_rescan, .metarescan = filescanner_metarescan, .fullrescan = filescanner_fullrescan, - .playlist_add = playlist_add, + .playlist_item_add = playlist_item_add, .playlist_remove = playlist_remove, .queue_save = queue_save, - .queue_add = queue_add, + .queue_item_add = queue_item_add, }; diff --git a/src/library/filescanner_playlist.c b/src/library/filescanner_playlist.c index a94148f9..ba548ca8 100644 --- a/src/library/filescanner_playlist.c +++ b/src/library/filescanner_playlist.c @@ -109,7 +109,7 @@ process_url(int pl_id, const char *path, struct media_file_info *mfi) { mfi->id = db_file_id_bypath(path); scan_metadata_stream(path, mfi); - library_add_media(mfi); + library_media_save(mfi); return db_pl_add_item_bypath(pl_id, path); } diff --git a/src/mpd.c b/src/mpd.c index 29e9a85c..cfbf5aad 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -1695,6 +1695,7 @@ mpd_queue_add(char *path, bool exact_match, int position) static int mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, struct mpd_client_ctx *ctx) { + struct player_status status; int ret; ret = mpd_queue_add(argv[1], false, -1); @@ -1707,8 +1708,10 @@ mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, st if (ret == 0) { + player_get_status(&status); + // Given path is not in the library, check if it is possible to add as a non-library queue item - ret = library_queue_add(argv[1], -1, NULL, NULL); + ret = library_queue_item_add(argv[1], -1, status.shuffle, status.item_id, NULL, NULL); if (ret != LIBRARY_OK) { *errmsg = safe_asprintf("Failed to add song '%s' to playlist (unkown path)", argv[1]); @@ -1728,6 +1731,7 @@ mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, st static int mpd_command_addid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, struct mpd_client_ctx *ctx) { + struct player_status status; int to_pos = -1; int ret; @@ -1745,8 +1749,10 @@ mpd_command_addid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, if (ret == 0) { + player_get_status(&status); + // Given path is not in the library, directly add it as a new queue item - ret = library_queue_add(argv[1], to_pos, NULL, NULL); + ret = library_queue_item_add(argv[1], to_pos, status.shuffle, status.item_id, NULL, NULL); if (ret != LIBRARY_OK) { *errmsg = safe_asprintf("Failed to add song '%s' to playlist (unkown path)", argv[1]); @@ -2510,7 +2516,7 @@ mpd_command_playlistadd(struct evbuffer *evbuf, int argc, char **argv, char **er vp_item = prepend_slash(argv[2]); - ret = library_playlist_add(vp_playlist, vp_item); + ret = library_playlist_item_add(vp_playlist, vp_item); free(vp_playlist); free(vp_item); if (ret < 0) diff --git a/src/spotify_webapi.c b/src/spotify_webapi.c index 07160b70..a04d427a 100644 --- a/src/spotify_webapi.c +++ b/src/spotify_webapi.c @@ -1239,7 +1239,7 @@ queue_add_playlist(const char *uri, int position, char reshuffle, uint32_t item_ } static int -queue_add(const char *uri, int position, char reshuffle, uint32_t item_id, int *count, int *new_item_id) +queue_item_add(const char *uri, int position, char reshuffle, uint32_t item_id, int *count, int *new_item_id) { if (strncasecmp(uri, "spotify:track:", strlen("spotify:track:")) == 0) { @@ -1423,7 +1423,7 @@ track_add(struct spotify_track *track, struct spotify_album *album, const char * map_track_to_mfi(&mfi, track, album, pl_name); - library_add_media(&mfi); + library_media_save(&mfi); free_mfi(&mfi, 1); } @@ -1571,6 +1571,23 @@ scan_playlist_tracks(const char *playlist_tracks_endpoint_uri, int plid) return ret; } +static void +map_playlist_to_pli(struct playlist_info *pli, struct spotify_playlist *playlist) +{ + + pli->type = PL_PLAIN; + pli->path = strdup(playlist->uri); + pli->title = safe_strdup(playlist->name); + + pli->parent_id = spotify_base_plid; + pli->directory_id = DIR_SPOTIFY; + + if (playlist->owner) + pli->virtual_path = safe_asprintf("/spotify:/%s (%s)", playlist->name, playlist->owner); + else + pli->virtual_path = safe_asprintf("/spotify:/%s", playlist->name); +} + /* * Add a saved playlist to the library */ @@ -1578,8 +1595,8 @@ static int saved_playlist_add(json_object *item, int index, int total, void *arg) { struct spotify_playlist playlist; - char virtual_path[PATH_MAX]; - int plid; + struct playlist_info pli; + int pl_id; // Map playlist information parse_metadata_playlist(item, &playlist); @@ -1592,21 +1609,17 @@ saved_playlist_add(json_object *item, int index, int total, void *arg) return -1; } - if (playlist.owner) - { - snprintf(virtual_path, PATH_MAX, "/spotify:/%s (%s)", playlist.name, playlist.owner); - } - else - { - snprintf(virtual_path, PATH_MAX, "/spotify:/%s", playlist.name); - } + memset(&pli, 0, sizeof(struct playlist_info)); - db_transaction_begin(); - plid = library_add_playlist_info(playlist.uri, playlist.name, virtual_path, PL_PLAIN, spotify_base_plid, DIR_SPOTIFY); - db_transaction_end(); + map_playlist_to_pli(&pli, &playlist); - if (plid > 0) - scan_playlist_tracks(playlist.tracks_href, plid); + library_playlist_save(&pli); + pl_id = db_pl_id_bypath(pli.path); + + free_pli(&pli, 1); + + if (pl_id > 0) + scan_playlist_tracks(playlist.tracks_href, pl_id); else DPRINTF(E_LOG, L_SPOTIFY, "Error adding playlist: '%s' (%s) \n", playlist.name, playlist.uri); @@ -1633,8 +1646,19 @@ scan_playlists() static void create_saved_tracks_playlist() { - spotify_saved_plid = library_add_playlist_info("spotify:savedtracks", "Spotify Saved", "/spotify:/Spotify Saved", PL_PLAIN, spotify_base_plid, DIR_SPOTIFY); + struct playlist_info pli = + { + .path = "spotify:savedtracks", + .title = "Spotify Saved", + .virtual_path = "/spotify:/Spotify Saved", + .type = PL_PLAIN, + .parent_id = spotify_base_plid, + .directory_id = DIR_SPOTIFY, + }; + library_playlist_save(&pli); + + spotify_saved_plid = db_pl_id_bypath(pli.path); if (spotify_saved_plid <= 0) { DPRINTF(E_LOG, L_SPOTIFY, "Error adding playlist for saved tracks\n"); @@ -1649,17 +1673,25 @@ static void create_base_playlist() { cfg_t *spotify_cfg; - int ret; + struct playlist_info pli = + { + .path = "spotify:playlistfolder", + .title = "Spotify", + .type = PL_FOLDER, + }; spotify_base_plid = 0; spotify_cfg = cfg_getsec(cfg, "spotify"); - if (!cfg_getbool(spotify_cfg, "base_playlist_disable")) + if (cfg_getbool(spotify_cfg, "base_playlist_disable")) + return; + + library_playlist_save(&pli); + + spotify_base_plid = db_pl_id_bypath(pli.path); + if (spotify_base_plid < 0) { - ret = library_add_playlist_info("spotify:playlistfolder", "Spotify", NULL, PL_FOLDER, 0, 0); - if (ret < 0) - DPRINTF(E_LOG, L_SPOTIFY, "Error adding base playlist\n"); - else - spotify_base_plid = ret; + DPRINTF(E_LOG, L_SPOTIFY, "Error adding base playlist\n"); + spotify_base_plid = 0; } } @@ -1968,6 +2000,6 @@ struct library_source spotifyscanner = .metarescan = rescan, .initscan = initscan, .fullrescan = fullrescan, - .queue_add = queue_add, + .queue_item_add = queue_item_add, };