aboutsummaryrefslogtreecommitdiffstats
path: root/yt_radio.py
diff options
context:
space:
mode:
Diffstat (limited to 'yt_radio.py')
-rw-r--r--yt_radio.py118
1 files changed, 118 insertions, 0 deletions
diff --git a/yt_radio.py b/yt_radio.py
new file mode 100644
index 0000000..3897899
--- /dev/null
+++ b/yt_radio.py
@@ -0,0 +1,118 @@
+from flask import Flask, Response, stream_with_context, abort
+from yt_dlp import YoutubeDL
+import subprocess
+import json
+import threading
+import random
+import os
+import sys
+from dotenv import load_dotenv
+
+load_dotenv()
+
+app = Flask(__name__)
+
+PLAYLIST = [
+]
+
+BASE_URL = os.environ.get("BASE_URL")
+RANDOMIZE_PLAYLIST = os.environ.get("RANDOMIZE_PLAYLIST")
+METADATA = {}
+
+def convert_playlist_to_links(link: str):
+ ydl_opts = {
+ "quiet": True,
+ "extract_flat": True, # don't download, just metadata
+ }
+ urls = []
+ with YoutubeDL(ydl_opts) as ydl:
+ info = ydl.extract_info(link, download=False)
+ for entry in info["entries"]:
+ urls.append(f"https://www.youtube.com/watch?v={entry['id']}")
+ if bool(RANDOMIZE_PLAYLIST) == True:
+ random.shuffle(urls)
+ return urls
+
+def fetch_metadata(index, url):
+ try:
+ result = subprocess.run(
+ ["yt-dlp", "--dump-json", url],
+ capture_output=True,
+ text=True,
+ timeout=20
+ )
+ data = json.loads(result.stdout)
+ METADATA[index] = {
+ "title": data.get("title", f"Track {index+1}"),
+ "artist": data.get("uploader", "Unknown"),
+ "duration": data.get("duration", -1)
+ }
+ except Exception:
+ METADATA[index] = {
+ "title": f"Track {index+1}",
+ "artist": "Unknown",
+ "duration": -1
+ }
+
+
+for i, url in enumerate(PLAYLIST):
+ threading.Thread(target=fetch_metadata, args=(i, url), daemon=True).start()
+
+
+@app.route("/playlist.m3u")
+def playlist():
+ lines = ["#EXTM3U"]
+
+ indices = list(range(len(PLAYLIST)))
+
+ for i in indices:
+ meta = METADATA.get(i, {
+ "title": f"Track {i+1}",
+ "artist": "Unknown",
+ "duration": -1
+ })
+
+ lines.append(
+ f"#EXTINF:{meta['duration']},{meta['artist']} - {meta['title']}"
+ )
+ lines.append(f"{BASE_URL}/track/{i}")
+
+ return Response("\n".join(lines),
+ mimetype="audio/x-mpegurl")
+
+@app.route("/track/<int:index>")
+def track(index):
+ if index < 0 or index >= len(PLAYLIST):
+ abort(404)
+
+ process = subprocess.Popen(
+ ["yt-dlp", "-f", "bestaudio", "-o", "-", PLAYLIST[index]],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.DEVNULL
+ )
+
+ def stream():
+ try:
+ while True:
+ chunk = process.stdout.read(8192)
+ if not chunk:
+ break
+ yield chunk
+ finally:
+ process.kill()
+
+ return Response(
+ stream_with_context(stream()),
+ mimetype="audio/mpeg"
+ )
+
+
+if __name__ == "__main__":
+ if len(sys.argv) <= 1:
+ print("ERROR: Specify link to YouTube playlist")
+ playlist_url = sys.argv[1]
+ print("Converting Playlist to YouTube Links")
+ PLAYLIST = convert_playlist_to_links(playlist_url)
+ print(PLAYLIST)
+ print(f"OK. Now serving, you can access the m3u at {BASE_URL}/playlist.m3u")
+ app.run(host="0.0.0.0", port=8000, threaded=True)
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage