mirror of
https://github.com/mpv-player/mpv
synced 2025-04-07 18:14:33 +00:00
core: add libquvi 0.9 support
This adds support for libquvi 0.9.x, and these features: - start time (part of youtube URL) - youtube subtitles - alternative source switching ('l' and 'L' keys) - youtube playlists Note that libquvi 0.9 is still in development. Although this seems to be API stable now, it looks like there will be a 1.0 release, which is supposed to be the next stable release and the actual successor of libquvi 0.4.x.
This commit is contained in:
parent
ac79eb7337
commit
5f664d78e6
@ -399,6 +399,7 @@ tv-hue x
|
||||
track-list list of audio/video/sub tracks, cur. entr. marked
|
||||
chapter-list list of chapters, current entry marked
|
||||
playlist playlist, current entry marked
|
||||
quvi-format x see ``--quvi-format``
|
||||
=========================== = ==================================================
|
||||
|
||||
.. _property_expansion:
|
||||
|
@ -1567,12 +1567,25 @@
|
||||
Video format/quality that is directly passed to libquvi (default: ``best``).
|
||||
This is used when opening links to streaming sites like YouTube. The
|
||||
interpretation of this value is highly specific to the streaming site and
|
||||
the video. The only well defined values that work on all sites are ``best``
|
||||
the video.
|
||||
|
||||
libquvi 0.4.x:
|
||||
|
||||
The only well defined values that work on all sites are ``best``
|
||||
(best quality/highest bandwidth, default), and ``default`` (lowest quality).
|
||||
|
||||
The quvi command line tool can be used to find out which formats are
|
||||
supported for a given URL: ``quvi --query-formats URL``.
|
||||
|
||||
libquvi 0.9.x:
|
||||
|
||||
The following explanations are relevant:
|
||||
``http://quvi.sourceforge.net/doc/0.9/glossary_termino.html#m_stream_id``
|
||||
|
||||
With 0.9.x, the ``quvi-format`` property can be used at runtime to cycle
|
||||
through the list of formats. Unfortunately, this resets the playback
|
||||
position and is slow too.
|
||||
|
||||
--radio=<option1:option2:...>
|
||||
These options set various parameters of the radio capture module. For
|
||||
listening to radio with mpv use ``radio://<frequency>`` (if channels
|
||||
|
1
Makefile
1
Makefile
@ -108,6 +108,7 @@ SOURCES-$(GL_WAYLAND) += video/out/wayland_common.c \
|
||||
SOURCES-$(JACK) += audio/out/ao_jack.c
|
||||
SOURCES-$(JOYSTICK) += core/input/joystick.c
|
||||
SOURCES-$(LIBQUVI) += core/resolve_quvi.c
|
||||
SOURCES-$(LIBQUVI9) += core/resolve_quvi9.c
|
||||
SOURCES-$(LIRC) += core/input/lirc.c
|
||||
SOURCES-$(OPENAL) += audio/out/ao_openal.c
|
||||
SOURCES-$(OSS) += audio/out/ao_oss.c
|
||||
|
25
configure
vendored
25
configure
vendored
@ -311,6 +311,7 @@ Optional features:
|
||||
--enable-winsock2_h enable winsock2_h [autodetect]
|
||||
--enable-smb enable Samba (SMB) input [autodetect]
|
||||
--enable-libquvi enable libquvi [autodetect]
|
||||
--disable-libquvi9 disable libquvi 0.9.x [autodetect]
|
||||
--enable-lcms2 enable LCMS2 support [autodetect]
|
||||
--disable-vcd disable VCD support [autodetect]
|
||||
--disable-bluray disable Blu-ray support [autodetect]
|
||||
@ -466,6 +467,7 @@ networking=yes
|
||||
_winsock2_h=auto
|
||||
_smb=auto
|
||||
_libquvi=auto
|
||||
_libquvi9=auto
|
||||
_libguess=auto
|
||||
_joystick=no
|
||||
_lirc=auto
|
||||
@ -668,6 +670,8 @@ for ac_option do
|
||||
--disable-smb) _smb=no ;;
|
||||
--enable-libquvi) _libquvi=yes ;;
|
||||
--disable-libquvi) _libquvi=no ;;
|
||||
--enable-libquvi9) _libquvi9=yes ;;
|
||||
--disable-libquvi9) _libquvi9=no ;;
|
||||
--enable-libguess) _libguess=yes ;;
|
||||
--disable-libguess) _libguess=no ;;
|
||||
--enable-joystick) _joystick=yes ;;
|
||||
@ -1727,7 +1731,26 @@ else
|
||||
fi
|
||||
echores "$_smb"
|
||||
|
||||
|
||||
echocheck "libquvi 0.9.0 support"
|
||||
if test "$_libquvi9" = auto ; then
|
||||
_libquvi9=no
|
||||
if pkg_config_add 'libquvi-0.9 >= 0.9.0' ; then
|
||||
_libquvi9=yes
|
||||
fi
|
||||
fi
|
||||
if test "$_libquvi9" = yes; then
|
||||
def_libquvi9="#define CONFIG_LIBQUVI9 1"
|
||||
else
|
||||
def_libquvi9="#undef CONFIG_LIBQUVI9"
|
||||
fi
|
||||
echores "$_libquvi9"
|
||||
|
||||
echocheck "libquvi support"
|
||||
if test "$_libquvi9" = yes ; then
|
||||
_libquvi=no
|
||||
res_comment="using libquvi 0.9.x"
|
||||
fi
|
||||
if test "$_libquvi" = auto ; then
|
||||
_libquvi=no
|
||||
if pkg_config_add 'libquvi >= 0.4.1' ; then
|
||||
@ -3190,6 +3213,7 @@ VF_LAVFI = $vf_lavfi
|
||||
AF_LAVFI = $af_lavfi
|
||||
LIBSMBCLIENT = $_smb
|
||||
LIBQUVI = $_libquvi
|
||||
LIBQUVI9 = $_libquvi9
|
||||
LIBGUESS = $_libguess
|
||||
LIBTHEORA = $_theora
|
||||
LIRC = $_lirc
|
||||
@ -3389,6 +3413,7 @@ $def_inet_pton
|
||||
$def_networking
|
||||
$def_smb
|
||||
$def_libquvi
|
||||
$def_libquvi9
|
||||
$def_libguess
|
||||
$def_socklen_t
|
||||
$def_vstream
|
||||
|
@ -24,6 +24,9 @@
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <libavutil/avstring.h>
|
||||
#include <libavutil/common.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "talloc.h"
|
||||
#include "command.h"
|
||||
@ -67,7 +70,6 @@
|
||||
|
||||
#include "core/mp_core.h"
|
||||
#include "mp_fifo.h"
|
||||
#include "libavutil/avstring.h"
|
||||
|
||||
static void change_video_filters(MPContext *mpctx, const char *cmd,
|
||||
const char *arg);
|
||||
@ -464,6 +466,65 @@ static int mp_property_edition(m_option_t *prop, int action, void *arg,
|
||||
return M_PROPERTY_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static struct mp_resolve_src *find_source(struct mp_resolve_result *res,
|
||||
char *url)
|
||||
{
|
||||
if (res->num_srcs == 0)
|
||||
return NULL;
|
||||
|
||||
int src = 0;
|
||||
for (int n = 0; n < res->num_srcs; n++) {
|
||||
if (strcmp(res->srcs[n]->url, res->url) == 0) {
|
||||
src = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res->srcs[src];
|
||||
}
|
||||
|
||||
static int mp_property_quvi_format(m_option_t *prop, int action, void *arg,
|
||||
MPContext *mpctx)
|
||||
{
|
||||
struct mp_resolve_result *res = mpctx->resolve_result;
|
||||
if (!res || !res->num_srcs)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
struct mp_resolve_src *cur = find_source(res, res->url);
|
||||
if (!cur)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
switch (action) {
|
||||
case M_PROPERTY_GET:
|
||||
*(char **)arg = talloc_strdup(NULL, cur->encid);
|
||||
return M_PROPERTY_OK;
|
||||
case M_PROPERTY_SET: {
|
||||
mpctx->stop_play = PT_RESTART;
|
||||
break;
|
||||
}
|
||||
case M_PROPERTY_SWITCH: {
|
||||
struct m_property_switch_arg *sarg = arg;
|
||||
int pos = 0;
|
||||
for (int n = 0; n < res->num_srcs; n++) {
|
||||
if (res->srcs[n] == cur) {
|
||||
pos = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pos += sarg->inc;
|
||||
if (pos < 0 || pos >= res->num_srcs) {
|
||||
if (sarg->wrap) {
|
||||
pos = (res->num_srcs + pos) % res->num_srcs;
|
||||
} else {
|
||||
pos = av_clip(pos, 0, res->num_srcs);
|
||||
}
|
||||
}
|
||||
char *arg = res->srcs[pos]->encid;
|
||||
return mp_property_quvi_format(prop, M_PROPERTY_SET, &arg, mpctx);
|
||||
}
|
||||
}
|
||||
return mp_property_generic_option(prop, action, arg, mpctx);
|
||||
}
|
||||
|
||||
/// Number of titles in file
|
||||
static int mp_property_titles(m_option_t *prop, int action, void *arg,
|
||||
MPContext *mpctx)
|
||||
@ -1523,6 +1584,7 @@ static const m_option_t mp_properties[] = {
|
||||
{ "chapter", mp_property_chapter, CONF_TYPE_INT,
|
||||
M_OPT_MIN, 0, 0, NULL },
|
||||
M_OPTION_PROPERTY_CUSTOM("edition", mp_property_edition),
|
||||
M_OPTION_PROPERTY_CUSTOM("quvi-format", mp_property_quvi_format),
|
||||
{ "titles", mp_property_titles, CONF_TYPE_INT,
|
||||
0, 0, 0, NULL },
|
||||
{ "chapters", mp_property_chapters, CONF_TYPE_INT,
|
||||
|
@ -3890,6 +3890,9 @@ static struct track *open_external_file(struct MPContext *mpctx, char *filename,
|
||||
if (!filename)
|
||||
return NULL;
|
||||
int format = 0;
|
||||
char *disp_filename = filename;
|
||||
if (strncmp(disp_filename, "memory://", 9) == 0)
|
||||
disp_filename = "memory://"; // avoid noise
|
||||
struct stream *stream = open_stream(filename, &mpctx->opts, &format);
|
||||
if (!stream)
|
||||
goto err_out;
|
||||
@ -3920,7 +3923,7 @@ static struct track *open_external_file(struct MPContext *mpctx, char *filename,
|
||||
if (stream->type == filter) {
|
||||
struct track *t = add_stream_track(mpctx, stream, false);
|
||||
t->is_external = true;
|
||||
t->title = talloc_strdup(t, filename);
|
||||
t->title = talloc_strdup(t, disp_filename);
|
||||
t->external_filename = talloc_strdup(t, filename);
|
||||
first = t;
|
||||
}
|
||||
@ -3928,7 +3931,7 @@ static struct track *open_external_file(struct MPContext *mpctx, char *filename,
|
||||
if (!first) {
|
||||
free_demuxer(demuxer);
|
||||
mp_msg(MSGT_CPLAYER, MSGL_WARN, "No streams added from file %s.\n",
|
||||
filename);
|
||||
disp_filename);
|
||||
goto err_out;
|
||||
}
|
||||
MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, demuxer);
|
||||
@ -3936,7 +3939,7 @@ static struct track *open_external_file(struct MPContext *mpctx, char *filename,
|
||||
|
||||
err_out:
|
||||
mp_msg(MSGT_CPLAYER, MSGL_ERR, "Can not open external file %s.\n",
|
||||
filename);
|
||||
disp_filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3954,6 +3957,25 @@ struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename, int noer
|
||||
STREAM_SUB);
|
||||
}
|
||||
|
||||
static void open_subtitles_from_resolve(struct MPContext *mpctx)
|
||||
{
|
||||
struct MPOpts *opts = &mpctx->opts;
|
||||
struct mp_resolve_result *res = mpctx->resolve_result;
|
||||
if (!res)
|
||||
return;
|
||||
for (int n = 0; n < res->num_subs; n++) {
|
||||
struct mp_resolve_sub *sub = res->subs[n];
|
||||
char *s = talloc_strdup(NULL, sub->url);
|
||||
if (!s)
|
||||
s = talloc_asprintf(NULL, "memory://%s", sub->data);
|
||||
struct track *t =
|
||||
open_external_file(mpctx, s, opts->sub_demuxer_name, 0, STREAM_SUB);
|
||||
talloc_free(s);
|
||||
if (t)
|
||||
t->lang = talloc_strdup(t, sub->lang);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_timeline(struct MPContext *mpctx)
|
||||
{
|
||||
if (mpctx->timeline) {
|
||||
@ -4005,7 +4027,7 @@ static void add_subtitle_fonts_from_sources(struct MPContext *mpctx)
|
||||
static struct mp_resolve_result *resolve_url(const char *filename,
|
||||
struct MPOpts *opts)
|
||||
{
|
||||
#ifdef CONFIG_LIBQUVI
|
||||
#if defined(CONFIG_LIBQUVI) || defined(CONFIG_LIBQUVI9)
|
||||
return mp_resolve_quvi(filename, opts);
|
||||
#else
|
||||
return NULL;
|
||||
@ -4138,8 +4160,17 @@ static void play_current_file(struct MPContext *mpctx)
|
||||
|
||||
char *stream_filename = mpctx->filename;
|
||||
mpctx->resolve_result = resolve_url(stream_filename, opts);
|
||||
if (mpctx->resolve_result)
|
||||
if (mpctx->resolve_result) {
|
||||
if (mpctx->resolve_result->playlist) {
|
||||
// Replace entry with playlist contents
|
||||
playlist_transfer_entries(mpctx->playlist,
|
||||
mpctx->resolve_result->playlist);
|
||||
if (mpctx->playlist->current)
|
||||
playlist_remove(mpctx->playlist, mpctx->playlist->current);
|
||||
goto terminate_playback;
|
||||
}
|
||||
stream_filename = mpctx->resolve_result->url;
|
||||
}
|
||||
int file_format = DEMUXER_TYPE_UNKNOWN;
|
||||
mpctx->stream = open_stream(stream_filename, opts, &file_format);
|
||||
if (!mpctx->stream) { // error...
|
||||
@ -4253,6 +4284,7 @@ goto_reopen_demuxer: ;
|
||||
add_subtitle_fonts_from_sources(mpctx);
|
||||
|
||||
open_subtitles_from_options(mpctx);
|
||||
open_subtitles_from_resolve(mpctx);
|
||||
open_audiofiles_from_options(mpctx);
|
||||
|
||||
check_previous_track_selection(mpctx);
|
||||
@ -4366,6 +4398,12 @@ goto_reopen_demuxer: ;
|
||||
queue_seek(mpctx, MPSEEK_ABSOLUTE, startpos, 0);
|
||||
execute_queued_seek(mpctx);
|
||||
}
|
||||
if (startpos == -1 && mpctx->resolve_result &&
|
||||
mpctx->resolve_result->start_time > 0)
|
||||
{
|
||||
queue_seek(mpctx, MPSEEK_ABSOLUTE, mpctx->resolve_result->start_time, 0);
|
||||
execute_queued_seek(mpctx);
|
||||
}
|
||||
if (opts->chapterrange[0] > 0) {
|
||||
if (mp_seek_chapter(mpctx, opts->chapterrange[0] - 1))
|
||||
execute_queued_seek(mpctx);
|
||||
|
@ -180,13 +180,19 @@ void playlist_add_base_path(struct playlist *pl, bstr base_path)
|
||||
}
|
||||
}
|
||||
|
||||
// Move all entries from source_pl to pl, appending them at the end of pl.
|
||||
// source_pl will be empty, and all entries have changed ownership to pl.
|
||||
// Move all entries from source_pl to pl, appending them after the current entry
|
||||
// of pl. source_pl will be empty, and all entries have changed ownership to pl.
|
||||
void playlist_transfer_entries(struct playlist *pl, struct playlist *source_pl)
|
||||
{
|
||||
struct playlist_entry *add_after = pl->current;
|
||||
if (pl->current && pl->current_was_replaced)
|
||||
add_after = pl->current->next;
|
||||
if (!add_after)
|
||||
add_after = pl->last;
|
||||
|
||||
while (source_pl->first) {
|
||||
struct playlist_entry *e = source_pl->first;
|
||||
playlist_unlink(source_pl, e);
|
||||
playlist_add(pl, e);
|
||||
playlist_insert(pl, add_after, e);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,27 @@ struct MPOpts;
|
||||
struct mp_resolve_result {
|
||||
char *url;
|
||||
char *title;
|
||||
|
||||
struct mp_resolve_src **srcs;
|
||||
int num_srcs;
|
||||
|
||||
double start_time;
|
||||
|
||||
struct mp_resolve_sub **subs;
|
||||
int num_subs;
|
||||
|
||||
struct playlist *playlist;
|
||||
};
|
||||
|
||||
struct mp_resolve_src {
|
||||
char *url;
|
||||
char *encid; // indicates quality level, contents are libquvi specific
|
||||
};
|
||||
|
||||
struct mp_resolve_sub {
|
||||
char *url;
|
||||
char *data;
|
||||
char *lang;
|
||||
};
|
||||
|
||||
struct mp_resolve_result *mp_resolve_quvi(const char *url, struct MPOpts *opts);
|
||||
|
150
core/resolve_quvi9.c
Normal file
150
core/resolve_quvi9.c
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv 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.
|
||||
*
|
||||
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <quvi.h>
|
||||
|
||||
#include "talloc.h"
|
||||
#include "core/mp_msg.h"
|
||||
#include "core/options.h"
|
||||
#include "core/playlist.h"
|
||||
#include "resolve.h"
|
||||
|
||||
static bool mp_quvi_ok(quvi_t q)
|
||||
{
|
||||
if (!quvi_ok(q)) {
|
||||
mp_msg(MSGT_OPEN, MSGL_ERR, "[quvi] %s\n", quvi_errmsg(q));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct mp_resolve_result *mp_resolve_quvi(const char *url, struct MPOpts *opts)
|
||||
{
|
||||
int mode = QUVI_SUPPORTS_MODE_OFFLINE;
|
||||
|
||||
quvi_t q = quvi_new();
|
||||
if (!quvi_ok(q)) {
|
||||
mp_msg(MSGT_OPEN, MSGL_ERR, "[quvi] %s\n", quvi_errmsg(q));
|
||||
|
||||
quvi_free(q);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mp_resolve_result *res = talloc_zero(NULL, struct mp_resolve_result);
|
||||
|
||||
if (quvi_supports(q, url, mode, QUVI_SUPPORTS_TYPE_PLAYLIST)) {
|
||||
mp_msg(MSGT_OPEN, MSGL_INFO, "[quvi] Checking playlist...\n");
|
||||
quvi_playlist_t qp = quvi_playlist_new(q, url);
|
||||
if (mp_quvi_ok(q)) {
|
||||
res->playlist = talloc_zero(res, struct playlist);
|
||||
while (quvi_playlist_media_next(qp)) {
|
||||
char *entry = NULL;
|
||||
quvi_playlist_get(qp, QUVI_PLAYLIST_MEDIA_PROPERTY_URL, &entry);
|
||||
if (entry)
|
||||
playlist_add_file(res->playlist, entry);
|
||||
}
|
||||
}
|
||||
quvi_playlist_free(qp);
|
||||
}
|
||||
|
||||
if (quvi_supports(q, url, mode, QUVI_SUPPORTS_TYPE_MEDIA)) {
|
||||
mp_msg(MSGT_OPEN, MSGL_INFO, "[quvi] Checking URL...\n");
|
||||
quvi_media_t media = quvi_media_new(q, url);
|
||||
if (mp_quvi_ok(q)) {
|
||||
char *format = opts->quvi_format ? opts->quvi_format : "best";
|
||||
bool use_default = strcmp(format, "default") == 0;
|
||||
if (!use_default)
|
||||
quvi_media_stream_select(media, format);
|
||||
|
||||
char *val = NULL;
|
||||
quvi_media_get(media, QUVI_MEDIA_STREAM_PROPERTY_URL, &val);
|
||||
res->url = talloc_strdup(res, val);
|
||||
|
||||
val = NULL;
|
||||
quvi_media_get(media, QUVI_MEDIA_PROPERTY_TITLE, &val);
|
||||
res->title = talloc_strdup(res, val);
|
||||
|
||||
double start = 0;
|
||||
quvi_media_get(media, QUVI_MEDIA_PROPERTY_START_TIME_MS, &start);
|
||||
res->start_time = start / 1000.0;
|
||||
|
||||
quvi_media_stream_reset(media);
|
||||
while (quvi_media_stream_next(media)) {
|
||||
char *entry = NULL, *id = NULL;
|
||||
quvi_media_get(media, QUVI_MEDIA_STREAM_PROPERTY_URL, &entry);
|
||||
quvi_media_get(media, QUVI_MEDIA_STREAM_PROPERTY_ID, &id);
|
||||
if (entry) {
|
||||
struct mp_resolve_src *src = talloc_ptrtype(res, src);
|
||||
*src = (struct mp_resolve_src) {
|
||||
.url = talloc_strdup(src, entry),
|
||||
.encid = talloc_strdup(src, id),
|
||||
};
|
||||
MP_TARRAY_APPEND(res, res->srcs, res->num_srcs, src);
|
||||
talloc_steal(res->srcs, src);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
quvi_media_free(media);
|
||||
}
|
||||
|
||||
if (quvi_supports(q, url, mode, QUVI_SUPPORTS_TYPE_SUBTITLE)) {
|
||||
mp_msg(MSGT_OPEN, MSGL_INFO, "[quvi] Getting subtitles...\n");
|
||||
quvi_subtitle_t qsub = quvi_subtitle_new(q, url);
|
||||
if (mp_quvi_ok(q)) {
|
||||
while (1) {
|
||||
quvi_subtitle_type_t qst = quvi_subtitle_type_next(qsub);
|
||||
if (!qst)
|
||||
break;
|
||||
while (1) {
|
||||
quvi_subtitle_lang_t qsl = quvi_subtitle_lang_next(qst);
|
||||
if (!qsl)
|
||||
break;
|
||||
char *lang;
|
||||
quvi_subtitle_lang_get(qsl, QUVI_SUBTITLE_LANG_PROPERTY_ID,
|
||||
&lang);
|
||||
// Let quvi convert the subtitle to SRT.
|
||||
quvi_subtitle_export_t qse =
|
||||
quvi_subtitle_export_new(qsl, "srt");
|
||||
if (mp_quvi_ok(q)) {
|
||||
const char *subdata = quvi_subtitle_export_data(qse);
|
||||
struct mp_resolve_sub *sub = talloc_ptrtype(res, sub);
|
||||
*sub = (struct mp_resolve_sub) {
|
||||
.lang = talloc_strdup(sub, lang),
|
||||
.data = talloc_strdup(sub, subdata),
|
||||
};
|
||||
MP_TARRAY_APPEND(res, res->subs, res->num_subs, sub);
|
||||
talloc_steal(res->subs, sub);
|
||||
}
|
||||
quvi_subtitle_export_free(qse);
|
||||
}
|
||||
}
|
||||
}
|
||||
quvi_subtitle_free(qsub);
|
||||
}
|
||||
|
||||
quvi_free(q);
|
||||
|
||||
if (!res->url && (!res->playlist || !res->playlist->first)) {
|
||||
talloc_free(res);
|
||||
res = NULL;
|
||||
}
|
||||
return res;
|
||||
}
|
@ -130,6 +130,9 @@ E cycle edition # next edition
|
||||
A cycle angle
|
||||
U stop
|
||||
|
||||
l cycle quvi-format 1
|
||||
L cycle quvi-format -1
|
||||
|
||||
# TV
|
||||
h tv_step_channel 1
|
||||
k tv_step_channel -1
|
||||
|
Loading…
Reference in New Issue
Block a user