[vlc-devel] [PATCH 2/4] ytdl: stream filter module for YoutubeDL
Rémi Denis-Courmont
remi at remlab.net
Wed Sep 23 17:43:07 CEST 2020
This passes every HTTP(S) URL through YoutubeDL to extract playlists
or media, unless the underlying stream is already a playlist.
---
NEWS | 1 +
modules/stream_filter/Makefile.am | 7 +
modules/stream_filter/ytdl.c | 204 ++++++++++++++++++++++++++++++
po/POTFILES.in | 1 +
4 files changed, 213 insertions(+)
create mode 100644 modules/stream_filter/ytdl.c
diff --git a/NEWS b/NEWS
index 9af6a77b8f..7eb1a72567 100644
--- a/NEWS
+++ b/NEWS
@@ -65,6 +65,7 @@ Access:
* Audio CD data tracks are now correctly detected and skipped
* Deprecates Audio CD CDDB lookups in favor of more accurate Musicbrainz
* Improved CD-TEXT and added Shift-JIS encoding support
+ * Support for YoutubeDL (where available).
Access output:
* Added support for the RIST (Reliable Internet Stream Transport) Protocol
diff --git a/modules/stream_filter/Makefile.am b/modules/stream_filter/Makefile.am
index 11f86a4a63..164c30bec6 100644
--- a/modules/stream_filter/Makefile.am
+++ b/modules/stream_filter/Makefile.am
@@ -51,3 +51,10 @@ stream_filter_LTLIBRARIES += libadf_plugin.la
libskiptags_plugin_la_SOURCES = stream_filter/skiptags.c
stream_filter_LTLIBRARIES += libskiptags_plugin.la
+
+libytdl_plugin_la_SOURCES = stream_filter/ytdl.c
+if !HAVE_WIN32
+if !HAVE_ANDROID
+stream_filter_LTLIBRARIES += libytdl_plugin.la
+endif
+endif
diff --git a/modules/stream_filter/ytdl.c b/modules/stream_filter/ytdl.c
new file mode 100644
index 0000000000..6da83e0c08
--- /dev/null
+++ b/modules/stream_filter/ytdl.c
@@ -0,0 +1,204 @@
+/*****************************************************************************
+ * 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
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+
+#include <vlc_common.h>
+#include <vlc_stream.h>
+#include <vlc_fs.h>
+#include <vlc_input_item.h>
+#include <vlc_interrupt.h>
+#include <vlc_plugin.h>
+#include <vlc_spawn.h>
+
+
+struct ytdl_playlist {
+ pid_t pid;
+ int fd;
+ int 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_playlist *sys = s->p_sys;
+
+ if (unlikely(sys->first_byte != EOF)) {
+ /* Relay the first byte that was dequeued while probing. */
+ if (likely(len > 0)) {
+ *(unsigned char *)buf = sys->first_byte;
+ sys->first_byte = EOF;
+ }
+
+ return len > 0;
+ }
+
+ 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 void Close(vlc_object_t *obj)
+{
+ stream_t *s = (stream_t *)obj;
+ struct ytdl_playlist *sys = s->p_sys;
+
+ msg_Dbg(s, "terminating PID %u", (unsigned)sys->pid);
+ kill(sys->pid, SIGTERM);
+ vlc_close(sys->fd);
+ vlc_waitpid(sys->pid);
+}
+
+static int OpenFilter(vlc_object_t *obj)
+{
+ stream_t *s = (stream_t *)obj;
+ ssize_t val;
+
+ if (s->s->pf_readdir != NULL || s->psz_url == NULL)
+ return VLC_EGENERIC;
+ if (strncasecmp(s->psz_url, "http:", 5)
+ && strncasecmp(s->psz_url, "https:", 6))
+ return VLC_EGENERIC;
+ if (!var_InheritBool(s, "ytdl"))
+ return VLC_EGENERIC;
+
+ struct ytdl_playlist *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);
+
+ unsigned char first_byte;
+ if (readall(sys->fd, &first_byte, 1) <= 0) {
+ /* Location not handled */
+ msg_Dbg(s, "cannot extract infos");
+ Close(obj);
+ return VLC_EGENERIC;
+ }
+
+ sys->first_byte = first_byte;
+ s->pf_read = Read;
+ 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("stream_filter", 305)
+ set_callbacks(OpenFilter, 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..7b9af4bbc8 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1151,6 +1151,7 @@ modules/stream_filter/inflate.c
modules/stream_filter/prefetch.c
modules/stream_filter/record.c
modules/stream_filter/skiptags.c
+modules/stream_filter/ytdl.c
modules/stream_out/autodel.c
modules/stream_out/bridge.c
modules/stream_out/chromaprint.c
--
2.28.0
More information about the vlc-devel
mailing list