Fix radio.

Signed-off-by: Abdulkadir Furkan Şanlı <me@afk.pm>
This commit is contained in:
afk
2026-07-19 22:53:22 +02:00
parent fac762c84d
commit ad828c3605
+45 -23
View File
@@ -12,6 +12,7 @@ import pickle
import re import re
import sqlite3 import sqlite3
import time import time
import urllib.parse
from google.auth.transport.requests import Request from google.auth.transport.requests import Request
from google_auth_oauthlib.flow import InstalledAppFlow from google_auth_oauthlib.flow import InstalledAppFlow
@@ -184,14 +185,16 @@ def add_video_to_playlist(youtube, playlist_id, video_id, retry_count=6):
def parse_iso8601_duration(duration_str): def parse_iso8601_duration(duration_str):
"""Parse ISO 8601 duration string into seconds.""" """Parse ISO 8601 duration string into seconds."""
pattern = re.compile(r'PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?') pattern = re.compile(r'^P(?:(\d+)W)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$')
match = pattern.match(duration_str) match = pattern.match(duration_str)
if not match: if not match:
return 0 return 0
hours = int(match.group(1)) if match.group(1) else 0 weeks = int(match.group(1)) if match.group(1) else 0
minutes = int(match.group(2)) if match.group(2) else 0 days = int(match.group(2)) if match.group(2) else 0
seconds = int(match.group(3)) if match.group(3) else 0 hours = int(match.group(3)) if match.group(3) else 0
return hours * 3600 + minutes * 60 + seconds minutes = int(match.group(4)) if match.group(4) else 0
seconds = int(match.group(5)) if match.group(5) else 0
return weeks * 604800 + days * 86400 + hours * 3600 + minutes * 60 + seconds
def get_video_info(youtube, video_id): def get_video_info(youtube, video_id):
@@ -347,10 +350,13 @@ async def message_callback(conn, cursor, youtube, client, room, event):
"msgtype": "m.text", "msgtype": "m.text",
"body": warning_text, "body": warning_text,
"format": "org.matrix.custom.html", "format": "org.matrix.custom.html",
"formatted_body": f"<em><span data-mx-color='#ff0000'>{html.escape(warning_text)}</span></em>", "formatted_body": f"<em><span data-mx-color='#808080'>{html.escape(warning_text)}</span></em>",
}, },
) )
elif not is_music_vid:
print(f"📻 Skipping radio upload for {title} (Not categorized as Music/Entertainment)")
if is_music_vid: if is_music_vid:
message_id = record_message(conn, cursor, sender, link, timestamp) message_id = record_message(conn, cursor, sender, link, timestamp)
if in_playlist(cursor, video_id, playlist_id): if in_playlist(cursor, video_id, playlist_id):
@@ -368,8 +374,11 @@ async def message_callback(conn, cursor, youtube, client, room, event):
) )
print(f"Added track to this week's playlist: {link}") print(f"Added track to this week's playlist: {link}")
if recent: if recent:
if duration <= 1200: if 0 < duration <= 1200:
asyncio.create_task(process_radio_track(link, video_id, title)) asyncio.create_task(process_radio_track(link, video_id, title))
else:
if duration == 0:
print(f"📻 Skipping radio upload for {title} (duration unknown or live stream)")
else: else:
print(f"📻 Skipping radio upload for {title} (duration: {duration}s > 1200s)") print(f"📻 Skipping radio upload for {title} (duration: {duration}s > 1200s)")
@@ -500,17 +509,16 @@ async def process_radio_track(video_link, video_id, title):
print(f"📻 Uploading to AzuraCast: {title}") print(f"📻 Uploading to AzuraCast: {title}")
encoded_path = urllib.parse.quote(filename)
upload_cmd = [ upload_cmd = [
"curl", "curl",
"-s", "-s",
"-X", "-X",
"POST", "POST",
f"{base_url}/api/station/{AZURACAST_STATION_ID}/files/upload", f"{base_url}/api/station/{AZURACAST_STATION_ID}/files/upload?path={encoded_path}",
"-H", "-H",
f"X-API-Key: {AZURACAST_API_KEY}", f"X-API-Key: {AZURACAST_API_KEY}",
"-F", "-F",
f"path={filename}",
"-F",
f"file=@{filepath}", f"file=@{filepath}",
] ]
@@ -621,36 +629,50 @@ async def process_radio_track(video_link, video_id, title):
# 2. Issue the Play Next Request SECOND # 2. Issue the Play Next Request SECOND
# Now that the file is legally in a playlist, AzuraCast will accept the request. # Now that the file is legally in a playlist, AzuraCast will accept the request.
if unique_id: if target_path:
req_cmd = [ q_payload = {
"do": "queue",
"files": [target_path],
}
q_json_path = os.path.join(DATA_DIR, f"queue_{filename}.json")
with open(q_json_path, "w") as f:
json.dump(q_payload, f)
q_batch_cmd = [
"curl", "curl",
"-s", "-s",
"-X", "-X",
"POST", "PUT",
f"{base_url}/api/station/{AZURACAST_STATION_ID}/request/{unique_id}", f"{base_url}/api/station/{AZURACAST_STATION_ID}/files/batch",
"-H", "-H",
f"X-API-Key: {AZURACAST_API_KEY}", f"X-API-Key: {AZURACAST_API_KEY}",
"-H",
"Content-Type: application/json",
"-d",
f"@{q_json_path}",
] ]
req_proc = await asyncio.create_subprocess_exec( q_proc = await asyncio.create_subprocess_exec(
*req_cmd, *q_batch_cmd,
stdout=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
) )
req_stdout, _ = await req_proc.communicate() q_stdout, _ = await q_proc.communicate()
if os.path.exists(q_json_path):
os.remove(q_json_path)
try: try:
req_resp = json.loads(req_stdout.decode()) q_resp = json.loads(q_stdout.decode())
# Check the JSON response directly so we don't lie about success if q_resp.get("success") or q_resp.get("code") == 200:
if req_resp.get("success") or req_resp.get("code") == 200:
print(f"✅ Queued on radio to play next: {title}") print(f"✅ Queued on radio to play next: {title}")
else: else:
print( print(
f"⚠️ AzuraCast rejected the request for {title}: {req_stdout.decode()}" f"⚠️ AzuraCast rejected the queue request for {title}: {q_stdout.decode()}"
) )
except json.JSONDecodeError: except json.JSONDecodeError:
print(f"⚠️ Failed to queue {title}. API returned: {req_stdout.decode()}") print(f"⚠️ Failed to queue {title}. API returned: {q_stdout.decode()}")
else: else:
print("⚠️ Uploaded, but no unique_id returned to make the request.") print("⚠️ Uploaded, but no path returned to make the request.")
if os.path.exists(filepath): if os.path.exists(filepath):
os.remove(filepath) os.remove(filepath)