You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
514 lines
14 KiB
514 lines
14 KiB
/*****************************************************************************
|
|
* playlist.c : remote control stdin/stdout module for vlc
|
|
*****************************************************************************
|
|
* Copyright (C) 2004-2009 the VideoLAN team
|
|
*
|
|
* Author: Peter Surda <shurdeek@panorama.sth.ac.at>
|
|
* Jean-Paul Saman <jpsaman #_at_# m2x _replaceWith#dot_ nl>
|
|
*
|
|
* 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.
|
|
*****************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <vlc_common.h>
|
|
#include <vlc_interface.h>
|
|
#include <vlc_input_item.h>
|
|
#include <vlc_playlist.h>
|
|
#include <vlc_url.h>
|
|
|
|
#include "cli.h"
|
|
|
|
#ifndef HAVE_WORDEXP
|
|
/*****************************************************************************
|
|
* parse_MRL: build a input item from a full mrl
|
|
*****************************************************************************
|
|
* MRL format: "simplified-mrl [:option-name[=option-value]]"
|
|
* We don't check for '"' or '\'', we just assume that a ':' that follows a
|
|
* space is a new option. Should be good enough for our purpose.
|
|
*****************************************************************************/
|
|
static input_item_t *parse_MRL(const char *mrl)
|
|
{
|
|
#define SKIPSPACE( p ) { while( *p == ' ' || *p == '\t' ) p++; }
|
|
#define SKIPTRAILINGSPACE( p, d ) \
|
|
{ char *e = d; while (e > p && (*(e-1)==' ' || *(e-1)=='\t')) {e--; *e=0 ;} }
|
|
|
|
input_item_t *p_item = NULL;
|
|
char *psz_item = NULL, *psz_item_mrl = NULL, *psz_orig, *psz_mrl;
|
|
char **ppsz_options = NULL;
|
|
int i_options = 0;
|
|
|
|
if (mrl == NULL)
|
|
return 0;
|
|
|
|
psz_mrl = psz_orig = strdup( mrl );
|
|
if (psz_mrl == NULL)
|
|
return NULL;
|
|
|
|
while (*psz_mrl)
|
|
{
|
|
SKIPSPACE(psz_mrl);
|
|
psz_item = psz_mrl;
|
|
|
|
for (; *psz_mrl; psz_mrl++)
|
|
{
|
|
if ((*psz_mrl == ' ' || *psz_mrl == '\t') && psz_mrl[1] == ':')
|
|
{
|
|
/* We have a complete item */
|
|
break;
|
|
}
|
|
if ((*psz_mrl == ' ' || *psz_mrl == '\t') &&
|
|
(psz_mrl[1] == '"' || psz_mrl[1] == '\'') && psz_mrl[2] == ':')
|
|
{
|
|
/* We have a complete item */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (*psz_mrl)
|
|
{
|
|
*psz_mrl = 0;
|
|
psz_mrl++;
|
|
}
|
|
SKIPTRAILINGSPACE(psz_item, psz_item + strlen(psz_item));
|
|
|
|
/* Remove '"' and '\'' if necessary */
|
|
if (*psz_item == '"' && psz_item[strlen(psz_item)-1] == '"')
|
|
{
|
|
psz_item++;
|
|
psz_item[strlen(psz_item) - 1] = 0;
|
|
}
|
|
if (*psz_item == '\'' && psz_item[strlen(psz_item)-1] == '\'')
|
|
{
|
|
psz_item++;
|
|
psz_item[strlen(psz_item)-1] = 0;
|
|
}
|
|
|
|
if (psz_item_mrl == NULL)
|
|
{
|
|
if (strstr( psz_item, "://" ) != NULL)
|
|
psz_item_mrl = strdup(psz_item);
|
|
else
|
|
psz_item_mrl = vlc_path2uri(psz_item, NULL);
|
|
if (psz_item_mrl == NULL)
|
|
{
|
|
free(psz_orig);
|
|
return NULL;
|
|
}
|
|
}
|
|
else if (*psz_item)
|
|
{
|
|
i_options++;
|
|
ppsz_options = xrealloc(ppsz_options, i_options * sizeof(char *));
|
|
ppsz_options[i_options - 1] = &psz_item[1];
|
|
}
|
|
|
|
if (*psz_mrl)
|
|
SKIPSPACE(psz_mrl);
|
|
}
|
|
|
|
/* Now create a playlist item */
|
|
if (psz_item_mrl != NULL)
|
|
{
|
|
p_item = input_item_New(psz_item_mrl, NULL);
|
|
for (int i = 0; i < i_options; i++)
|
|
input_item_AddOption(p_item, ppsz_options[i],
|
|
VLC_INPUT_OPTION_TRUSTED);
|
|
free(psz_item_mrl);
|
|
}
|
|
|
|
if (i_options)
|
|
free(ppsz_options);
|
|
free(psz_orig);
|
|
|
|
return p_item;
|
|
}
|
|
#endif
|
|
|
|
static void print_playlist(struct cli_client *cl, vlc_playlist_t *playlist)
|
|
{
|
|
size_t count = vlc_playlist_Count(playlist);
|
|
size_t current = vlc_playlist_GetCurrentIndex(playlist);
|
|
|
|
for (size_t i = 0; i < count; ++i)
|
|
{
|
|
vlc_playlist_item_t *plitem = vlc_playlist_Get(playlist, i);
|
|
input_item_t *item = vlc_playlist_item_GetMedia(plitem);
|
|
vlc_tick_t len = item->i_duration;
|
|
char selected = (i == current) ? '*' : ' ';
|
|
|
|
if (len != INPUT_DURATION_INDEFINITE && len != VLC_TICK_INVALID)
|
|
{
|
|
char buf[MSTRTIME_MAX_SIZE];
|
|
secstotimestr(buf, len);
|
|
cli_printf(cl, "| %c%zu %s (%s)", selected, i, item->psz_name, buf);
|
|
}
|
|
else
|
|
cli_printf(cl, "| %c%zu %s", selected, i, item->psz_name);
|
|
}
|
|
}
|
|
|
|
static int PlaylistDoVoid(struct cli_client *cl, void *data,
|
|
int (*cb)(vlc_playlist_t *))
|
|
{
|
|
vlc_playlist_t *playlist = data;
|
|
int ret;
|
|
|
|
vlc_playlist_Lock(playlist);
|
|
ret = cb(playlist);
|
|
vlc_playlist_Unlock(playlist);
|
|
(void) cl;
|
|
return ret;
|
|
}
|
|
|
|
static int PlaylistPrev(struct cli_client *cl, const char *const *args,
|
|
size_t count, void *data)
|
|
{
|
|
(void) args; (void) count;
|
|
return PlaylistDoVoid(cl, data, vlc_playlist_Prev);
|
|
}
|
|
|
|
static int PlaylistNext(struct cli_client *cl, const char *const *args,
|
|
size_t count, void *data)
|
|
{
|
|
(void) args; (void) count;
|
|
return PlaylistDoVoid(cl, data, vlc_playlist_Next);
|
|
}
|
|
|
|
static int PlaylistPlay(struct cli_client *cl, const char *const *args,
|
|
size_t count, void *data)
|
|
{
|
|
(void) args; (void) count;
|
|
return PlaylistDoVoid(cl, data, vlc_playlist_Start);
|
|
}
|
|
|
|
static int PlaylistDoStop(vlc_playlist_t *playlist)
|
|
{
|
|
vlc_playlist_Stop(playlist);
|
|
return 0;
|
|
}
|
|
|
|
static int PlaylistStop(struct cli_client *cl, const char *const *args,
|
|
size_t count, void *data)
|
|
{
|
|
(void) args; (void) count;
|
|
return PlaylistDoVoid(cl, data, PlaylistDoStop);
|
|
}
|
|
|
|
static int PlaylistDoClear(vlc_playlist_t *playlist)
|
|
{
|
|
vlc_playlist_Stop(playlist);
|
|
vlc_playlist_Clear(playlist);
|
|
return 0;
|
|
}
|
|
|
|
static int PlaylistClear(struct cli_client *cl, const char *const *args,
|
|
size_t count, void *data)
|
|
{
|
|
(void) args; (void) count;
|
|
return PlaylistDoVoid(cl, data, PlaylistDoClear);
|
|
}
|
|
|
|
static int PlaylistDoSort(vlc_playlist_t *playlist)
|
|
{
|
|
struct vlc_playlist_sort_criterion criteria =
|
|
{
|
|
.key = VLC_PLAYLIST_SORT_KEY_ARTIST,
|
|
.order = VLC_PLAYLIST_SORT_ORDER_ASCENDING
|
|
};
|
|
|
|
return vlc_playlist_Sort(playlist, &criteria, 1);
|
|
}
|
|
|
|
static int PlaylistSort(struct cli_client *cl, const char *const *args,
|
|
size_t count, void *data)
|
|
{
|
|
(void) args; (void) count;
|
|
return PlaylistDoVoid(cl, data, PlaylistDoSort);
|
|
}
|
|
|
|
static int PlaylistList(struct cli_client *cl, const char *const *args,
|
|
size_t count, void *data)
|
|
{
|
|
vlc_playlist_t *playlist = data;
|
|
|
|
cli_printf(cl, "+----[ Playlist ]");
|
|
vlc_playlist_Lock(playlist);
|
|
print_playlist(cl, playlist);
|
|
vlc_playlist_Unlock(playlist);
|
|
cli_printf(cl, "+----[ End of playlist ]");
|
|
(void) args; (void) count;
|
|
return 0;
|
|
}
|
|
|
|
static int PlaylistRepeatCommon(struct cli_client *cl, const char *const *args,
|
|
size_t count, void *data,
|
|
enum vlc_playlist_playback_repeat on_mode)
|
|
|
|
{
|
|
vlc_playlist_t *playlist = data;
|
|
|
|
vlc_playlist_Lock(playlist);
|
|
|
|
enum vlc_playlist_playback_repeat cur_mode =
|
|
vlc_playlist_GetPlaybackRepeat(playlist);
|
|
enum vlc_playlist_playback_repeat new_mode;
|
|
|
|
if (cur_mode == on_mode)
|
|
new_mode = VLC_PLAYLIST_PLAYBACK_REPEAT_NONE;
|
|
else
|
|
new_mode = on_mode;
|
|
|
|
if (count > 1)
|
|
{
|
|
if (strcmp(args[1], "on") == 0)
|
|
new_mode = on_mode;
|
|
if (strcmp(args[1], "off") == 0)
|
|
new_mode = VLC_PLAYLIST_PLAYBACK_REPEAT_NONE;
|
|
}
|
|
|
|
if (new_mode != cur_mode)
|
|
vlc_playlist_SetPlaybackRepeat(playlist, new_mode);
|
|
|
|
vlc_playlist_Unlock(playlist);
|
|
(void) cl;
|
|
return 0;
|
|
}
|
|
|
|
static int PlaylistRepeat(struct cli_client *cl, const char *const *args,
|
|
size_t count, void *data)
|
|
{
|
|
return PlaylistRepeatCommon(cl, args, count, data,
|
|
VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT);
|
|
}
|
|
|
|
static int PlaylistLoop(struct cli_client *cl, const char *const *args,
|
|
size_t count, void *data)
|
|
{
|
|
return PlaylistRepeatCommon(cl, args, count, data,
|
|
VLC_PLAYLIST_PLAYBACK_REPEAT_ALL);
|
|
}
|
|
|
|
static int PlaylistRandom(struct cli_client *cl, const char *const *args,
|
|
size_t count, void *data)
|
|
{
|
|
vlc_playlist_t *playlist = data;
|
|
|
|
vlc_playlist_Lock(playlist);
|
|
|
|
enum vlc_playlist_playback_order cur_mode =
|
|
vlc_playlist_GetPlaybackOrder(playlist);
|
|
enum vlc_playlist_playback_order new_mode;
|
|
|
|
if (cur_mode == VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM)
|
|
new_mode = VLC_PLAYLIST_PLAYBACK_ORDER_NORMAL;
|
|
else
|
|
new_mode = VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM;
|
|
|
|
if (count > 1)
|
|
{
|
|
if (strcmp(args[1], "on") == 0)
|
|
new_mode = VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM;
|
|
if (strcmp(args[1], "off") == 0)
|
|
new_mode = VLC_PLAYLIST_PLAYBACK_ORDER_NORMAL;
|
|
}
|
|
|
|
if (new_mode != cur_mode)
|
|
vlc_playlist_SetPlaybackOrder(playlist, new_mode);
|
|
|
|
vlc_playlist_Unlock(playlist);
|
|
(void) cl;
|
|
return 0;
|
|
}
|
|
|
|
static int PlaylistGoto(struct cli_client *cl, const char *const *args,
|
|
size_t n_args, void *data)
|
|
{
|
|
vlc_playlist_t *playlist = data;
|
|
const char *arg = n_args > 1 ? args[1] : "";
|
|
unsigned long long index = atoll(arg);
|
|
|
|
vlc_playlist_Lock(playlist);
|
|
|
|
int ret = vlc_playlist_PlayAt(playlist, index);
|
|
if (ret) {
|
|
size_t count = vlc_playlist_Count(playlist);
|
|
|
|
cli_printf(cl,
|
|
vlc_ngettext("Playlist has only %zu element",
|
|
"Playlist has only %zu elements", count),
|
|
count);
|
|
}
|
|
|
|
vlc_playlist_Unlock(playlist);
|
|
return ret;
|
|
}
|
|
|
|
static int PlaylistAddCommon(struct cli_client *cl, const char *const *args,
|
|
size_t n_args, void *data, bool play)
|
|
{
|
|
vlc_playlist_t *playlist = data;
|
|
size_t count;
|
|
int ret = 0;
|
|
|
|
vlc_playlist_Lock(playlist);
|
|
count = vlc_playlist_Count(playlist);
|
|
#ifdef HAVE_WORDEXP
|
|
|
|
for (size_t i = 1; i < n_args;)
|
|
{
|
|
input_item_t *item;
|
|
|
|
if (strstr(args[i], "://" ) != NULL)
|
|
item = input_item_New(args[i], NULL);
|
|
else
|
|
{
|
|
char *url = vlc_path2uri(args[i], NULL);
|
|
|
|
if (url != NULL)
|
|
{
|
|
item = input_item_New(url, NULL);
|
|
free(url);
|
|
}
|
|
else
|
|
item = NULL;
|
|
}
|
|
|
|
i++;
|
|
|
|
/* Check if following argument(s) are input item options prefixed with
|
|
* a colon.
|
|
*/
|
|
while (i < n_args && args[i][0] == ':')
|
|
{
|
|
if (likely(item != NULL)
|
|
&& input_item_AddOption(item, args[i] + 1,
|
|
VLC_INPUT_OPTION_TRUSTED))
|
|
{
|
|
input_item_Release(item);
|
|
item = NULL;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (unlikely(item == NULL))
|
|
{
|
|
ret = VLC_ENOMEM;
|
|
continue;
|
|
}
|
|
|
|
if (vlc_playlist_InsertOne(playlist, count, item) == VLC_SUCCESS)
|
|
{
|
|
if (play)
|
|
vlc_playlist_PlayAt(playlist, count);
|
|
|
|
count++;
|
|
}
|
|
|
|
input_item_Release(item);
|
|
}
|
|
(void) cl;
|
|
#else
|
|
const char *arg = n_args > 1 ? args[1] : "";
|
|
|
|
input_item_t *item = parse_MRL( arg );
|
|
|
|
if (item != NULL)
|
|
{
|
|
cli_printf(cl, "Trying to %s %s to playlist.",
|
|
play ? "add" : "enqueue", arg);
|
|
|
|
if (vlc_playlist_InsertOne(playlist, count, item) == VLC_SUCCESS
|
|
&& play)
|
|
vlc_playlist_PlayAt(playlist, count);
|
|
|
|
input_item_Release(item);
|
|
}
|
|
#endif
|
|
vlc_playlist_Unlock(playlist);
|
|
return ret;
|
|
}
|
|
|
|
static int PlaylistAdd(struct cli_client *cl, const char *const *args,
|
|
size_t count, void *data)
|
|
{
|
|
return PlaylistAddCommon(cl, args, count, data, true);
|
|
}
|
|
|
|
static int PlaylistEnqueue(struct cli_client *cl, const char *const *args,
|
|
size_t count, void *data)
|
|
{
|
|
return PlaylistAddCommon(cl, args, count, data, false);
|
|
}
|
|
|
|
static int PlaylistMove(struct cli_client *cl, const char *const *args,
|
|
size_t count, void *data)
|
|
{
|
|
vlc_playlist_t *playlist = data;
|
|
int ret;
|
|
|
|
if (count != 3)
|
|
{
|
|
cli_printf(cl, "%s expects two parameters", args[0]);
|
|
return VLC_EGENERIC /*EINVAL*/;
|
|
}
|
|
|
|
size_t from = strtoul(args[1], NULL, 0);
|
|
size_t to = strtoul(args[2], NULL, 0);
|
|
|
|
vlc_playlist_Lock(playlist);
|
|
size_t size = vlc_playlist_Count(playlist);
|
|
|
|
if (from < size && to < size)
|
|
{
|
|
vlc_playlist_Move(playlist, from, 1, to);
|
|
ret = 0;
|
|
}
|
|
else
|
|
{
|
|
cli_printf(cl, vlc_ngettext("Playlist has only %zu element",
|
|
"Playlist has only %zu elements", size),
|
|
size);
|
|
ret = VLC_ENOENT;
|
|
}
|
|
vlc_playlist_Unlock(playlist);
|
|
return ret;
|
|
}
|
|
|
|
static const struct cli_handler cmds[] =
|
|
{
|
|
{ "playlist", PlaylistList },
|
|
{ "sort", PlaylistSort },
|
|
{ "play", PlaylistPlay },
|
|
{ "stop", PlaylistStop },
|
|
{ "clear", PlaylistClear },
|
|
{ "prev", PlaylistPrev },
|
|
{ "next", PlaylistNext },
|
|
{ "add", PlaylistAdd },
|
|
{ "repeat", PlaylistRepeat },
|
|
{ "loop", PlaylistLoop },
|
|
{ "random", PlaylistRandom },
|
|
{ "enqueue", PlaylistEnqueue },
|
|
{ "goto", PlaylistGoto },
|
|
{ "move", PlaylistMove },
|
|
};
|
|
|
|
void RegisterPlaylist(intf_thread_t *intf)
|
|
{
|
|
RegisterHandlers(intf, cmds, ARRAY_SIZE(cmds),
|
|
vlc_intf_GetMainPlaylist(intf));
|
|
}
|
|
|