[vlc-devel] [PATCH 2/2] ytdl: access module for YoutubeDL

Rémi Denis-Courmont remi at remlab.net
Sun Sep 20 20:57:22 CEST 2020


This passes every HTTP(S) URL through YoutubeDL to extract playlists
or media. If YoutubeDL fails, it falls back to the normal HTTP(S)
access. This includes if YoutubeDL or Python 3 are not found.
---
 modules/access/Makefile.am |   5 +
 modules/access/ytdl.c      | 200 +++++++++++++++++++++++++++++++++++++
 po/POTFILES.in             |   1 +
 3 files changed, 206 insertions(+)
 create mode 100644 modules/access/ytdl.c

diff --git a/modules/access/Makefile.am b/modules/access/Makefile.am
index 9cd3098fc2..f7dc76d6d3 100644
--- a/modules/access/Makefile.am
+++ b/modules/access/Makefile.am
@@ -436,6 +436,11 @@ libaccess_mtp_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)'
 access_LTLIBRARIES += $(LTLIBaccess_mtp)
 EXTRA_LTLIBRARIES += libaccess_mtp_plugin.la
 
+libaccess_ytdl_plugin_la_SOURCES = access/ytdl.c
+if !HAVE_WIN32
+access_LTLIBRARIES += libaccess_ytdl_plugin.la
+endif
+
 ### SRT ###
 
 libaccess_srt_plugin_la_SOURCES = access/srt.c access/srt_common.c access/srt_common.h dummy.cpp
diff --git a/modules/access/ytdl.c b/modules/access/ytdl.c
new file mode 100644
index 0000000000..4a47f4491e
--- /dev/null
+++ b/modules/access/ytdl.c
@@ -0,0 +1,200 @@
+/*****************************************************************************
+ * ytdl.c:
+ *****************************************************************************
+ * Copyright (C) 2019-2020 Rémi Denis-Courmont
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+
+#define PY_SSIZE_T_CLEAN
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+
+#include <vlc_common.h>
+#include <vlc_access.h>
+#include <vlc_fs.h>
+#include <vlc_input_item.h>
+#include <vlc_interrupt.h>
+#include <vlc_plugin.h>
+#include <vlc_spawn.h>
+
+
+struct ytdl_sys {
+    pid_t pid;
+    int fd;
+    char first_byte;
+};
+
+static size_t readall(int fd, void *buf, size_t len)
+{
+    size_t total = 0;
+
+    while (len > 0 && !vlc_killed()) {
+        ssize_t val = vlc_read_i11e(fd, buf, len);
+
+        if (val == 0)
+            break; /* EOS */
+
+        if (val < 0) {
+            if (errno != EINTR)
+                break; /* fatal error */
+            continue;
+        }
+
+        buf = (char *)buf + val;
+        len -= val;
+        total += val;
+    }
+
+    return total;
+}
+
+static ssize_t Read(stream_t *s, void *buf, size_t len)
+{
+    struct ytdl_sys *sys = s->p_sys;
+    ssize_t val = vlc_read_i11e(sys->fd, buf, len);
+
+    if (val < 0) {
+        if (errno == EINTR || errno == EAGAIN)
+            return -1;
+
+        msg_Err(s, "read error: %s", vlc_strerror_c(errno));
+        val = 0;
+    }
+
+    return val;
+}
+
+static int Control(stream_t *s, int query, va_list args)
+{
+    switch (query)
+    {
+        case STREAM_CAN_SEEK:
+        case STREAM_CAN_FASTSEEK:
+        case STREAM_CAN_PAUSE:
+        case STREAM_CAN_CONTROL_PACE:
+            *va_arg(args, bool *) = false;
+            break;
+
+        case STREAM_GET_TYPE:
+            *va_arg(args, int *) = ITEM_TYPE_PLAYLIST;
+            break;
+
+        case STREAM_GET_PTS_DELAY:
+            *va_arg(args, vlc_tick_t *) =
+                 VLC_TICK_FROM_MS(var_InheritInteger(s, "network-caching"));
+            break;
+
+        default:
+            return VLC_EGENERIC;
+
+    }
+
+    return VLC_SUCCESS;
+}
+
+static ssize_t ReadFirstByte(stream_t *s, void *buf, size_t len)
+{
+    struct ytdl_sys *sys = s->p_sys;
+
+    if (unlikely(len == 0))
+        return 0;
+
+    *(char *)buf = sys->first_byte;
+    s->pf_read = Read;
+    return 1;
+}
+
+static void Close(vlc_object_t *obj)
+{
+    stream_t *s = (stream_t *)obj;
+    struct ytdl_sys *sys = s->p_sys;
+
+    kill(sys->pid, SIGTERM);
+    vlc_close(sys->fd);
+    vlc_waitpid(sys->pid);
+}
+
+static int Open(vlc_object_t *obj)
+{
+    stream_t *s = (stream_t *)obj;
+    ssize_t val;
+
+    if (!var_InheritBool(s, "ytdl"))
+        return VLC_EGENERIC;
+
+    struct ytdl_sys *sys = vlc_obj_malloc(obj, sizeof (*sys));
+    if (unlikely(sys == NULL))
+        return VLC_EGENERIC;
+
+    char *path = config_GetSysPath(VLC_PKG_DATA_DIR, "ytdl-extract.py");
+    if (unlikely(path == NULL))
+        return VLC_EGENERIC;
+
+    int fds[2];
+
+    if (vlc_pipe(fds)) {
+        free(path);
+        return VLC_EGENERIC;
+    }
+
+    s->p_sys = sys;
+    sys->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);
+    vlc_close(fds[1]);
+
+    if (val) {
+        msg_Dbg(obj, "cannot start %s: %s", path, vlc_strerror_c(val));
+        free(path);
+        vlc_close(fds[0]);
+        return VLC_EGENERIC;
+    }
+
+    free(path);
+
+    if (readall(sys->fd, &sys->first_byte, 1) <= 0) {
+        /* Location not handled */
+        msg_Dbg(s, "cannot extract infos");
+        Close(obj);
+        return VLC_EGENERIC;
+    }
+
+    s->pf_read = ReadFirstByte;
+    s->pf_control = Control;
+    return VLC_SUCCESS;
+}
+
+vlc_module_begin()
+    set_shortname("YT-DL")
+    set_description("YoutubeDL")
+    set_category(CAT_INPUT)
+    set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
+    set_capability("access", 10)
+    add_shortcut("http", "https")
+    set_callbacks(Open, Close)
+    add_bool("ytdl", true, N_("Enable YT-DL"), N_("Enable YT-DL"), true)
+        change_safe()
+vlc_module_end()
diff --git a/po/POTFILES.in b/po/POTFILES.in
index d5ce933408..9d49cb59d8 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -212,6 +212,7 @@ modules/access/vcd/vcd.c
 modules/access/vdr.c
 modules/access/vnc.c
 modules/access/wasapi.c
+modules/access/ytdl.c
 modules/access_output/dummy.c
 modules/access_output/file.c
 modules/access_output/http.c
-- 
2.28.0



More information about the vlc-devel mailing list