@@ -12,6 +12,7 @@ import pickle
|
||||
import re
|
||||
import sqlite3
|
||||
import time
|
||||
import urllib.parse
|
||||
|
||||
from google.auth.transport.requests import Request
|
||||
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):
|
||||
"""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)
|
||||
if not match:
|
||||
return 0
|
||||
hours = int(match.group(1)) if match.group(1) else 0
|
||||
minutes = int(match.group(2)) if match.group(2) else 0
|
||||
seconds = int(match.group(3)) if match.group(3) else 0
|
||||
return hours * 3600 + minutes * 60 + seconds
|
||||
weeks = int(match.group(1)) if match.group(1) else 0
|
||||
days = int(match.group(2)) if match.group(2) else 0
|
||||
hours = int(match.group(3)) if match.group(3) else 0
|
||||
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):
|
||||
@@ -347,10 +350,13 @@ async def message_callback(conn, cursor, youtube, client, room, event):
|
||||
"msgtype": "m.text",
|
||||
"body": warning_text,
|
||||
"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:
|
||||
message_id = record_message(conn, cursor, sender, link, timestamp)
|
||||
if in_playlist(cursor, video_id, playlist_id):
|
||||
@@ -368,10 +374,13 @@ async def message_callback(conn, cursor, youtube, client, room, event):
|
||||
)
|
||||
print(f"Added track to this week's playlist: {link}")
|
||||
if recent:
|
||||
if duration <= 1200:
|
||||
if 0 < duration <= 1200:
|
||||
asyncio.create_task(process_radio_track(link, video_id, title))
|
||||
else:
|
||||
print(f"📻 Skipping radio upload for {title} (duration: {duration}s > 1200s)")
|
||||
if duration == 0:
|
||||
print(f"📻 Skipping radio upload for {title} (duration unknown or live stream)")
|
||||
else:
|
||||
print(f"📻 Skipping radio upload for {title} (duration: {duration}s > 1200s)")
|
||||
|
||||
|
||||
def in_playlist(cursor, video_id, playlist_id):
|
||||
@@ -500,17 +509,16 @@ async def process_radio_track(video_link, video_id, title):
|
||||
|
||||
print(f"📻 Uploading to AzuraCast: {title}")
|
||||
|
||||
encoded_path = urllib.parse.quote(filename)
|
||||
upload_cmd = [
|
||||
"curl",
|
||||
"-s",
|
||||
"-X",
|
||||
"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",
|
||||
f"X-API-Key: {AZURACAST_API_KEY}",
|
||||
"-F",
|
||||
f"path={filename}",
|
||||
"-F",
|
||||
f"file=@{filepath}",
|
||||
]
|
||||
|
||||
@@ -621,36 +629,50 @@ async def process_radio_track(video_link, video_id, title):
|
||||
|
||||
# 2. Issue the Play Next Request SECOND
|
||||
# Now that the file is legally in a playlist, AzuraCast will accept the request.
|
||||
if unique_id:
|
||||
req_cmd = [
|
||||
if target_path:
|
||||
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",
|
||||
"-s",
|
||||
"-X",
|
||||
"POST",
|
||||
f"{base_url}/api/station/{AZURACAST_STATION_ID}/request/{unique_id}",
|
||||
"PUT",
|
||||
f"{base_url}/api/station/{AZURACAST_STATION_ID}/files/batch",
|
||||
"-H",
|
||||
f"X-API-Key: {AZURACAST_API_KEY}",
|
||||
"-H",
|
||||
"Content-Type: application/json",
|
||||
"-d",
|
||||
f"@{q_json_path}",
|
||||
]
|
||||
req_proc = await asyncio.create_subprocess_exec(
|
||||
*req_cmd,
|
||||
q_proc = await asyncio.create_subprocess_exec(
|
||||
*q_batch_cmd,
|
||||
stdout=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:
|
||||
req_resp = json.loads(req_stdout.decode())
|
||||
# Check the JSON response directly so we don't lie about success
|
||||
if req_resp.get("success") or req_resp.get("code") == 200:
|
||||
q_resp = json.loads(q_stdout.decode())
|
||||
if q_resp.get("success") or q_resp.get("code") == 200:
|
||||
print(f"✅ Queued on radio to play next: {title}")
|
||||
else:
|
||||
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:
|
||||
print(f"⚠️ Failed to queue {title}. API returned: {req_stdout.decode()}")
|
||||
print(f"⚠️ Failed to queue {title}. API returned: {q_stdout.decode()}")
|
||||
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):
|
||||
os.remove(filepath)
|
||||
|
||||
Reference in New Issue
Block a user