[vlc-devel] [PATCH 1/2] share: add wrapper script for the YoutubeDL Python module

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


This script generates a M3U playlist from a given URL, providing a
simple serial format that can be read and parsed by another process.

There are in principles two other alternative ways to access it:

1) Calling the YoutubeDL module directly in-process through CPython.
   This poses a number of problems:
   - CPython must be loaded by the main executable. Python modules will
     fail to resolve their CPython symbols otherwise.
   - Multiple CPython interpreters are still very immature; GIL behaves
     weirdly. CPython is really not meant for multithread.
   - The GIL prevents concurrent uses (that's the whole point of it).
   - CPython network I/O cannot be interrupted by VLC interruptions, so
     the calling thread may get stuck inside CPython.
   - A build-time dependency on CPython is added.

2) Parsing the output of the youtube-dl program. This is what MPV does.
   But this requires a whole new parser for the Python syntax.

With a custom Python script, we can decide on the serialisation format
that most suits the usage.
---
 share/Makefile.am     |   2 +
 share/ytdl-extract.py | 103 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 105 insertions(+)
 create mode 100755 share/ytdl-extract.py

diff --git a/share/Makefile.am b/share/Makefile.am
index 2373ffbe91..8a92f04360 100644
--- a/share/Makefile.am
+++ b/share/Makefile.am
@@ -49,6 +49,8 @@ nobase_dist_pkgdata_SCRIPTS = \
 	utils/audio-vlc-default.sh \
 	utils/video-vlc-default.sh \
 	$(NULL)
+
+dist_pkgdata_SCRIPTS = ytdl-extract.py
 endif
 
 EXTRA_DIST += \
diff --git a/share/ytdl-extract.py b/share/ytdl-extract.py
new file mode 100755
index 0000000000..2d784c5090
--- /dev/null
+++ b/share/ytdl-extract.py
@@ -0,0 +1,103 @@
+#! /usr/bin/python3
+#
+# Copyright (C) 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.
+
+import sys
+import youtube_dl
+import urllib.parse
+
+class logger(object):
+    def debug(self, msg):
+        pass
+
+    def warning(self, msg):
+        pass
+
+    def error(self, msg):
+        sys.stderr.write(msg)
+
+def formats_choose_best(fmts):
+    # Pick our preferred format out of many
+    best_abr = -1
+    best_height = -1
+    best_format = None
+
+    for f in fmts:
+        if 'height' in f and f['height']:
+            height = f['height']
+        else:
+            height = 0
+
+        if height < best_height:
+            continue
+
+        if 'abr' in f and f['abr']:
+            abr = f['abr']
+        else:
+            abr = 0
+
+        if height == best_height or abr < best_abr:
+            continue
+
+        best_abr = abr
+        best_height = height
+        best_format = f
+
+    return best_format
+
+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:
+        print('#EXTINF:,,' + entry['title'])
+
+    if 'url' in fmt:
+        print('#EXTVLCOPT:no-ytdl') # don't parse recursively
+        print(fmt['url'])
+    else:
+        print('vlc://nop')
+
+def url_extract(url):
+    opts = {
+        'logger': logger(),
+    }
+
+    dl = youtube_dl.YoutubeDL(opts)
+    #dl.add_default_info_extractors()
+
+    # Process a given URL
+    # TODO: set process=False to handle playlist item one at a time
+    infos = dl.extract_info(url, process=True, download=False)
+
+    print('#EXTM3U')
+
+    if 'entries' in infos:
+        # URL is a playlist: iterate over entries
+
+        for entry in infos['entries']:
+            entry_extract(entry)
+
+    else:
+        # URL is a single media
+        entry_extract(infos)
+
+
+url_extract(sys.argv[1])
-- 
2.28.0



More information about the vlc-devel mailing list