← yt-playlist-export

yt-playlist

9a10b0f07a5a8df696aa54a9ced6c0c7598b186c · Alexander Tabakov · May 1, 2023 14:38

+107 · 1 file changed

  • yt-playlist.py +107 −0
diff --git a/yt-playlist.py b/yt-playlist.py
new file mode 100755
index 0000000..5ca9759
--- /dev/null
+++ b/yt-playlist.py
@@ -0,0 +1,107 @@
+#!/bin/env python3
+
+import argparse
+import csv
+import sys
+import json
+import yt_dlp
+
+parser = argparse.ArgumentParser(description='yt-playlist exports YouTube playlists to JSON or CSV')
+parser.add_argument('--browser', type=str, required=False, default='firefox', help='browser where you logged in to YouTube')
+parser.add_argument('-f', '--format', type=str, choices=['csv', 'json'], required=False, default='csv', help='output\'s format')
+parser.add_argument('-o', '--output', type=str, required=False, help='filename to store output, by default it\'s printed to stdout')
+parser.add_argument('-v', '--verbose', action='store_true', help='verbose')
+parser.add_argument('playlist', nargs='+', help='playlist URL')
+args = parser.parse_args()
+
+
+# print(args)
+# exit(0)
+
+playlist = args.playlist[0]
+format = args.format
+output = args.output
+verbose = args.verbose
+
+id = 'id'
+url = 'url'
+title = 'title'
+duration = 'duration'
+channel_id = 'channel id'
+channel_name = 'channel name'
+channel_url = 'channel url'
+duration = 'duration (seconds)'
+view_count = 'view count'
+thumbnail_xs = 'thumbnail xs'
+thumbnail_s = 'thumbnail s'
+thumbnail_m = 'thumbnail m'
+thumbnail_l = 'thumbnail l'
+
+header = [
+    id,
+    url,
+    title,
+    duration,
+    channel_id,
+    channel_name,
+    channel_url,
+    view_count,
+    thumbnail_xs,
+    thumbnail_s,
+    thumbnail_m,
+    thumbnail_l,
+]
+
+# Converts ytl JSON structure to yt-playlist, in particular removing all not populated field
+def convert_to_csv(info: any) -> any:
+    for entry in info['entries']:
+        yield {
+            id: entry['id'],
+            url: entry['url'],
+            title: entry['title'],
+            duration: entry['duration'],
+            channel_id: entry['channel_id'],
+            channel_name: entry['channel'],
+            channel_url: entry['channel_url'],
+            view_count: entry['view_count'],
+            thumbnail_xs: entry['thumbnails'][0]['url'],
+            thumbnail_s: entry['thumbnails'][1]['url'],
+            thumbnail_m: entry['thumbnails'][2]['url'],
+            thumbnail_l: entry['thumbnails'][3]['url'],
+        }
+
+def print_csv(info: any):
+    global output
+    if output is None:
+        csv_file = sys.stdout
+    else:
+        csv_file = open(output, mode='w', newline='')
+    writer = csv.DictWriter(csv_file, fieldnames=header)
+    writer.writeheader()
+    for row in convert_to_csv(info):
+        writer.writerow(row)
+
+def print_json(info: any):
+    global output
+    if output is None:
+        print(json.dumps(info))
+    else:
+        with open(output, 'w') as f:
+            f.write(json.dumps(info))
+
+# See help(yt_dlp.YoutubeDL) for a list of available options and public functions
+ydl_opts = {
+    'cookiesfrombrowser': (args.browser,),
+    'ignoreerrors': 'only_download',
+    'extract_flat': True,
+    'quiet': not verbose
+}
+
+with yt_dlp.YoutubeDL(ydl_opts) as ydl:
+    info = ydl.extract_info(playlist, download=False)
+    # ydl.sanitize_info makes the info json-serializable
+    info_sanitized = ydl.sanitize_info(info)
+    if format == 'json':
+        print_json(info_sanitized)
+    else:
+        print_csv(info_sanitized)