mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-26 23:25:56 -05:00
[mpd/conf] Add support for stored playlist commands (add/save/delete)
This also introduces a new config option to define a default playlist folder. The default playlist folder is used by forked-daapd if the playlist is not a full virtual path (simpifies creation of playlists with mpd clients).
This commit is contained in:
parent
d10c3672c6
commit
dc3f9443e7
@ -264,6 +264,11 @@ mpd {
|
|||||||
# the playlist like MPD does. Note that some dacp clients do not show
|
# the playlist like MPD does. Note that some dacp clients do not show
|
||||||
# the playqueue if playback is stopped.
|
# the playqueue if playback is stopped.
|
||||||
# clear_queue_on_stop_disable = false
|
# clear_queue_on_stop_disable = false
|
||||||
|
|
||||||
|
# A directory in one of the library directories that will be used as the default
|
||||||
|
# playlist directory. forked-dapd creates new playlists in this directory if only
|
||||||
|
# a playlist name is provided by the mpd client.
|
||||||
|
# default_playlist_directory = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# SQLite configuration (allows to modify the operation of the SQLite databases)
|
# SQLite configuration (allows to modify the operation of the SQLite databases)
|
||||||
|
@ -156,6 +156,7 @@ static cfg_opt_t sec_mpd[] =
|
|||||||
CFG_INT("port", 6600, CFGF_NONE),
|
CFG_INT("port", 6600, CFGF_NONE),
|
||||||
CFG_INT("http_port", 0, CFGF_NONE),
|
CFG_INT("http_port", 0, CFGF_NONE),
|
||||||
CFG_BOOL("clear_queue_on_stop_disable", cfg_false, CFGF_NONE),
|
CFG_BOOL("clear_queue_on_stop_disable", cfg_false, CFGF_NONE),
|
||||||
|
CFG_STR("default_playlist_directory", NULL, CFGF_NONE),
|
||||||
CFG_END()
|
CFG_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
147
src/mpd.c
147
src/mpd.c
@ -70,6 +70,8 @@ static struct evhttp *evhttpd;
|
|||||||
|
|
||||||
struct evconnlistener *listener;
|
struct evconnlistener *listener;
|
||||||
|
|
||||||
|
// Virtual path to the default playlist directory
|
||||||
|
static char *default_pl_dir;
|
||||||
|
|
||||||
#define COMMAND_ARGV_MAX 37
|
#define COMMAND_ARGV_MAX 37
|
||||||
|
|
||||||
@ -185,6 +187,26 @@ struct idle_client
|
|||||||
struct idle_client *idle_clients;
|
struct idle_client *idle_clients;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates a new string for the given path that starts with a '/'.
|
||||||
|
* If 'path' already starts with a '/' the returned string is a duplicate
|
||||||
|
* of 'path'.
|
||||||
|
*
|
||||||
|
* The returned string needs to be freed by the caller.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
prepend_slash(const char *path)
|
||||||
|
{
|
||||||
|
char *result;
|
||||||
|
|
||||||
|
if (path[0] == '/')
|
||||||
|
result = strdup(path);
|
||||||
|
else
|
||||||
|
result = safe_asprintf("/%s", path);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Thread: mpd */
|
/* Thread: mpd */
|
||||||
static void *
|
static void *
|
||||||
@ -604,6 +626,10 @@ mpd_command_idle(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||||||
{
|
{
|
||||||
client->events |= LISTENER_OPTIONS;
|
client->events |= LISTENER_OPTIONS;
|
||||||
}
|
}
|
||||||
|
else if (0 == strcmp(argv[i], "stored_playlist"))
|
||||||
|
{
|
||||||
|
client->events |= LISTENER_STORED_PLAYLIST;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DPRINTF(E_DBG, L_MPD, "Idle command for '%s' not supported\n", argv[i]);
|
DPRINTF(E_DBG, L_MPD, "Idle command for '%s' not supported\n", argv[i]);
|
||||||
@ -2172,6 +2198,112 @@ mpd_command_load(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mpd_command_playlistadd(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||||
|
{
|
||||||
|
char *vp_playlist;
|
||||||
|
char *vp_item;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (argc < 3)
|
||||||
|
{
|
||||||
|
*errmsg = safe_asprintf("Missing argument for command 'playlistadd'");
|
||||||
|
return ACK_ERROR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!default_pl_dir || strstr(argv[1], ":/"))
|
||||||
|
{
|
||||||
|
// Argument is a virtual path, make sure it starts with a '/'
|
||||||
|
vp_playlist = prepend_slash(argv[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Argument is a playlist name, prepend default playlist directory
|
||||||
|
vp_playlist = safe_asprintf("%s/%s", default_pl_dir, argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
vp_item = prepend_slash(argv[2]);
|
||||||
|
|
||||||
|
ret = library_playlist_add(vp_playlist, vp_item);
|
||||||
|
free(vp_playlist);
|
||||||
|
free(vp_item);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
*errmsg = safe_asprintf("Error saving queue to file '%s'", argv[1]);
|
||||||
|
return ACK_ERROR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mpd_command_rm(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||||
|
{
|
||||||
|
char *virtual_path;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
{
|
||||||
|
*errmsg = safe_asprintf("Missing argument for command 'rm'");
|
||||||
|
return ACK_ERROR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!default_pl_dir || strstr(argv[1], ":/"))
|
||||||
|
{
|
||||||
|
// Argument is a virtual path, make sure it starts with a '/'
|
||||||
|
virtual_path = prepend_slash(argv[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Argument is a playlist name, prepend default playlist directory
|
||||||
|
virtual_path = safe_asprintf("%s/%s", default_pl_dir, argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = library_playlist_remove(virtual_path);
|
||||||
|
free(virtual_path);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
*errmsg = safe_asprintf("Error removing playlist '%s'", argv[1]);
|
||||||
|
return ACK_ERROR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mpd_command_save(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||||
|
{
|
||||||
|
char *virtual_path;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
{
|
||||||
|
*errmsg = safe_asprintf("Missing argument for command 'save'");
|
||||||
|
return ACK_ERROR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!default_pl_dir || strstr(argv[1], ":/"))
|
||||||
|
{
|
||||||
|
// Argument is a virtual path, make sure it starts with a '/'
|
||||||
|
virtual_path = prepend_slash(argv[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Argument is a playlist name, prepend default playlist directory
|
||||||
|
virtual_path = safe_asprintf("%s/%s", default_pl_dir, argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = library_queue_save(virtual_path);
|
||||||
|
free(virtual_path);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
*errmsg = safe_asprintf("Error saving queue to file '%s'", argv[1]);
|
||||||
|
return ACK_ERROR_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
mpd_get_query_params_find(int argc, char **argv, struct query_params *qp)
|
mpd_get_query_params_find(int argc, char **argv, struct query_params *qp)
|
||||||
{
|
{
|
||||||
@ -3944,11 +4076,11 @@ static struct mpd_command mpd_handlers[] =
|
|||||||
.mpdcommand = "load",
|
.mpdcommand = "load",
|
||||||
.handler = mpd_command_load
|
.handler = mpd_command_load
|
||||||
},
|
},
|
||||||
/*
|
|
||||||
{
|
{
|
||||||
.mpdcommand = "playlistadd",
|
.mpdcommand = "playlistadd",
|
||||||
.handler = mpd_command_playlistadd
|
.handler = mpd_command_playlistadd
|
||||||
},
|
},
|
||||||
|
/*
|
||||||
{
|
{
|
||||||
.mpdcommand = "playlistclear",
|
.mpdcommand = "playlistclear",
|
||||||
.handler = mpd_command_playlistclear
|
.handler = mpd_command_playlistclear
|
||||||
@ -3965,6 +4097,7 @@ static struct mpd_command mpd_handlers[] =
|
|||||||
.mpdcommand = "rename",
|
.mpdcommand = "rename",
|
||||||
.handler = mpd_command_rename
|
.handler = mpd_command_rename
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
.mpdcommand = "rm",
|
.mpdcommand = "rm",
|
||||||
.handler = mpd_command_rm
|
.handler = mpd_command_rm
|
||||||
@ -3973,7 +4106,6 @@ static struct mpd_command mpd_handlers[] =
|
|||||||
.mpdcommand = "save",
|
.mpdcommand = "save",
|
||||||
.handler = mpd_command_save
|
.handler = mpd_command_save
|
||||||
},
|
},
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The music database
|
* The music database
|
||||||
@ -4512,6 +4644,10 @@ mpd_notify_idle_client(struct idle_client *client, enum listener_event_type type
|
|||||||
evbuffer_add(client->evbuffer, "changed: options\n", 17);
|
evbuffer_add(client->evbuffer, "changed: options\n", 17);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case LISTENER_STORED_PLAYLIST:
|
||||||
|
evbuffer_add(client->evbuffer, "changed: stored_playlist\n", 25);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DPRINTF(E_WARN, L_MPD, "Unsupported event type (%d) in notify idle clients.\n", type);
|
DPRINTF(E_WARN, L_MPD, "Unsupported event type (%d) in notify idle clients.\n", type);
|
||||||
return -1;
|
return -1;
|
||||||
@ -4706,6 +4842,7 @@ int mpd_init(void)
|
|||||||
unsigned short http_port;
|
unsigned short http_port;
|
||||||
const char *http_addr;
|
const char *http_addr;
|
||||||
int v6enabled;
|
int v6enabled;
|
||||||
|
const char *pl_dir;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
|
||||||
@ -4806,6 +4943,10 @@ int mpd_init(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pl_dir = cfg_getstr(cfg_getsec(cfg, "mpd"), "default_playlist_directory");
|
||||||
|
if (pl_dir)
|
||||||
|
default_pl_dir = safe_asprintf("/file:%s", pl_dir);
|
||||||
|
|
||||||
DPRINTF(E_INFO, L_MPD, "mpd thread init\n");
|
DPRINTF(E_INFO, L_MPD, "mpd thread init\n");
|
||||||
|
|
||||||
ret = pthread_create(&tid_mpd, NULL, mpd, NULL);
|
ret = pthread_create(&tid_mpd, NULL, mpd, NULL);
|
||||||
@ -4884,4 +5025,6 @@ void mpd_deinit(void)
|
|||||||
|
|
||||||
// Free event base (should free events too)
|
// Free event base (should free events too)
|
||||||
event_base_free(evbase_mpd);
|
event_base_free(evbase_mpd);
|
||||||
|
|
||||||
|
free(default_pl_dir);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user