[vlc-devel] [PATCH 4/4] ytdl: avoid intermediate playlist for single media
RĂ©mi Denis-Courmont
remi at remlab.net
Wed Sep 23 17:43:09 CEST 2020
Rather than exposing a playlist with a single item, this wraps the
media at the found URL and expose it as a regular byte stream. This
allows the actual media URL to be resolved and possibly refreshed
everytime.
---
modules/stream_filter/ytdl.c | 114 ++++++++++++++++++++++++++++++-----
share/ytdl-extract.py | 22 +++----
2 files changed, 109 insertions(+), 27 deletions(-)
diff --git a/modules/stream_filter/ytdl.c b/modules/stream_filter/ytdl.c
index 10af6c23f7..f13b5d13fd 100644
--- a/modules/stream_filter/ytdl.c
+++ b/modules/stream_filter/ytdl.c
@@ -37,9 +37,10 @@
struct ytdl_playlist {
+ void (*close)(stream_t *s);
pid_t pid;
int fd;
- int first_byte;
+ bool first_byte;
};
static size_t readall(int fd, void *buf, size_t len)
@@ -70,11 +71,11 @@ static ssize_t Read(stream_t *s, void *buf, size_t len)
{
struct ytdl_playlist *sys = s->p_sys;
- if (unlikely(sys->first_byte != EOF)) {
+ if (unlikely(sys->first_byte)) {
/* Relay the first byte that was dequeued while probing. */
if (likely(len > 0)) {
- *(unsigned char *)buf = sys->first_byte;
- sys->first_byte = EOF;
+ *(unsigned char *)buf = '#';
+ sys->first_byte = false;
}
return len > 0;
@@ -121,9 +122,8 @@ static int Control(stream_t *s, int query, va_list args)
return VLC_SUCCESS;
}
-static void Close(vlc_object_t *obj)
+static void ClosePlaylist(stream_t *s)
{
- stream_t *s = (stream_t *)obj;
struct ytdl_playlist *sys = s->p_sys;
msg_Dbg(s, "terminating PID %u", (unsigned)sys->pid);
@@ -132,12 +132,63 @@ static void Close(vlc_object_t *obj)
vlc_waitpid(sys->pid);
}
+struct ytdl_stream {
+ void (*close)(stream_t *s);
+ stream_t *source;
+};
+
+static ssize_t ReadNested(stream_t *s, void *buf, size_t len)
+{
+ struct ytdl_stream *sys = s->p_sys;
+
+ return vlc_stream_ReadPartial(sys->source, buf, len);
+}
+
+static int SeekNested(stream_t *s, uint64_t offset)
+{
+ struct ytdl_stream *sys = s->p_sys;
+
+ return vlc_stream_Seek(sys->source, offset);
+}
+
+static int ControlNested(stream_t *s, int query, va_list args)
+{
+ struct ytdl_stream *sys = s->p_sys;
+
+ return vlc_stream_vaControl(sys->source, query, args);
+}
+
+static void CloseNested(stream_t *s)
+{
+ struct ytdl_stream *sys = s->p_sys;
+
+ vlc_stream_Delete(sys->source);
+}
+
+/* Must be struct to ensure equal pointer representation. */
+struct ytdl_common {
+ union {
+ void (*close)(stream_t *s);
+ struct ytdl_playlist playlist;
+ struct ytdl_stream stream;
+ };
+};
+
+/* TODO: remove this and union whence vlc_stream_operations comes to exist. */
+static void Close(vlc_object_t *obj)
+{
+ stream_t *s = (stream_t *)obj;
+ struct ytdl_common *sys = s->p_sys;
+
+ return sys->close(s);
+}
+
static int OpenCommon(vlc_object_t *obj)
{
stream_t *s = (stream_t *)obj;
ssize_t val;
- struct ytdl_playlist *sys = vlc_obj_malloc(obj, sizeof (*sys));
+ struct ytdl_common *sys = vlc_obj_malloc(obj, sizeof (*sys));
if (unlikely(sys == NULL))
return VLC_EGENERIC;
@@ -153,12 +204,12 @@ static int OpenCommon(vlc_object_t *obj)
}
s->p_sys = sys;
- sys->fd = fds[0];
+ sys->playlist.fd = fds[0];
int fdv[] = { -1, fds[1], 2, -1 };
const char *argv[] = { path, s->psz_url, NULL };
- val = vlc_spawn(&sys->pid, path, fdv, argv);
+ val = vlc_spawn(&sys->playlist.pid, path, fdv, argv);
vlc_close(fds[1]);
if (val) {
@@ -170,17 +221,48 @@ static int OpenCommon(vlc_object_t *obj)
free(path);
- unsigned char first_byte;
- if (readall(sys->fd, &first_byte, 1) <= 0) {
+ char first_byte;
+
+ if (readall(sys->playlist.fd, &first_byte, 1) <= 0) {
/* Location not handled */
msg_Dbg(s, "cannot extract infos");
- Close(obj);
+ ClosePlaylist(s);
+ return VLC_EGENERIC;
+ }
+
+ if (first_byte == '#') {
+ /* Playlist */
+ msg_Dbg(s, "extracting playlist");
+ s->pf_read = Read;
+ s->pf_control = Control;
+ sys->close = ClosePlaylist;
+ sys->playlist.first_byte = true;
+ return VLC_SUCCESS;
+ }
+
+ /* Redirect if there is a single URL, so that we can refresh it every
+ * time it is opened.
+ */
+ char url[65536];
+
+ url[0] = first_byte;
+ val = readall(sys->playlist.fd, url + 1, sizeof (url) - 2);
+ url[val] = '\0';
+
+ ClosePlaylist(s);
+ var_Create(obj, "ytdl", VLC_VAR_BOOL);
+ sys->stream.source = vlc_stream_NewURL(obj, url);
+
+ if (sys->stream.source == NULL) {
+ msg_Err(s, "cannot open URL: %s", url);
return VLC_EGENERIC;
}
- sys->first_byte = first_byte;
- s->pf_read = Read;
- s->pf_control = Control;
+ msg_Dbg(s, "redirecting to: %s", url);
+ s->pf_read = ReadNested;
+ s->pf_seek = SeekNested;
+ s->pf_control = ControlNested;
+ sys->close = CloseNested;
return VLC_SUCCESS;
}
@@ -193,7 +275,7 @@ static int OpenFilter(vlc_object_t *obj)
if (strncasecmp(s->psz_url, "http:", 5)
&& strncasecmp(s->psz_url, "https:", 6))
return VLC_EGENERIC;
- if (!var_InheritBool(obj, "ytdl"))
+ if (!var_InheritBool(s->s, "ytdl"))
return VLC_EGENERIC;
return OpenCommon(obj);
diff --git a/share/ytdl-extract.py b/share/ytdl-extract.py
index f166384b60..ce1e9ba355 100755
--- a/share/ytdl-extract.py
+++ b/share/ytdl-extract.py
@@ -59,14 +59,14 @@ def formats_choose_best(fmts):
return best_format
-def entry_extract(entry):
+def entry_extract(entry, meta=True):
# Process a given entry of a playlist
if 'formats' in entry:
fmt = formats_choose_best(entry['formats'])
else:
fmt = entry
- if 'title' in entry:
+ if meta and 'title' in entry:
print('#EXTINF:,,' + entry['title'].splitlines()[0])
if 'ie_key' in fmt and fmt['ie_key']:
@@ -75,7 +75,8 @@ def entry_extract(entry):
return
if 'url' in fmt:
- print('#EXTVLCOPT:no-ytdl') # don't parse recursively
+ if meta:
+ print('#EXTVLCOPT:no-ytdl') # don't parse recursively
print(fmt['url'])
else:
print('vlc://nop')
@@ -92,18 +93,18 @@ def url_extract(url):
# Process a given URL
infos = dl.extract_info(url, download=False)
- print('#EXTM3U')
-
- if 'title' in infos:
- print('#PLAYLIST:' + infos['title'].splitlines()[0])
-
if 'entries' in infos:
# URL is a playlist: iterate over entries
+ print('#EXTM3U')
+
+ if 'title' in infos:
+ print('#PLAYLIST:' + infos['title'].splitlines()[0])
+
for entry in infos['entries']:
entry_extract(entry)
else:
# URL is a single media
- entry_extract(infos)
+ entry_extract(infos, meta=False)
def url_process(ie_url):
opts = {
@@ -120,8 +121,7 @@ def url_process(ie_url):
entry[p[0]] = p[1]
entry = dl.process_ie_result(entry, download=False)
- print('#EXTM3U')
- entry_extract(entry)
+ entry_extract(entry, meta=False)
url = sys.argv[1]
--
2.28.0
More information about the vlc-devel
mailing list