[library] Some refactoring of the library module

Misc refactoring, e.g. alignment of how modules save tracks and playlists, incl
use of mfi and pli. Also try to avoid direct calls between library and player.
This commit is contained in:
ejurgensen 2020-01-20 14:13:47 -08:00
parent 08e89ffd4f
commit abdc0d6d27
7 changed files with 399 additions and 340 deletions

View File

@ -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);

View File

@ -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, &param);
}
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, &param);
}
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, &param);
}
int
library_exec_async(command_function func, void *arg)
{

View File

@ -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);

View File

@ -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,
};

View File

@ -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);
}

View File

@ -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)

View File

@ -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,
};