From 01682c62c2c9ea2f7f498544ee3aaa299c0c2423 Mon Sep 17 00:00:00 2001 From: Caine Date: Sat, 7 Mar 2026 12:52:55 +0000 Subject: Initial commit: Radio Susan scripts, configs, and SFX --- generate_site.py | 602 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 602 insertions(+) create mode 100755 generate_site.py (limited to 'generate_site.py') diff --git a/generate_site.py b/generate_site.py new file mode 100755 index 0000000..4930374 --- /dev/null +++ b/generate_site.py @@ -0,0 +1,602 @@ +#!/usr/bin/env python3 +"""Generate the static site for Radio Susan from playlist.json. +Outputs to /var/www/radio/. Run after generate_daily_playlist.py. +""" + +import grp +import json +import os +import sys +from datetime import datetime +from pathlib import Path + +PLAYLIST_JSON = Path("/var/lib/radio/playlist.json") +OUTPUT_DIR = Path("/var/www/radio") +STREAM_URL = "https://radio.jihakuz.xyz/stream" + + +def set_mediaserver_perms(path): + try: + gid = grp.getgrnam("mediaserver").gr_gid + os.chown(str(path), -1, gid) + os.chmod(str(path), 0o664) + except Exception: + pass + + +def generate_html(playlist): + generated = playlist["generated_at"][:10] + total_tracks = playlist["total_tracks"] + duration = playlist["total_duration_hours"] + shows = playlist["shows"] + entries = playlist["entries"] + + # Build tracks-only list for the schedule + tracks = [e for e in entries if e["type"] == "track"] + + # Group tracks by show + show_tracks = {} + for t in tracks: + show_name = t.get("show", "Unknown") + show_tracks.setdefault(show_name, []).append(t) + + # Mode stats + mode_counts = {} + for t in tracks: + m = t.get("mode", "unknown") + mode_counts[m] = mode_counts.get(m, 0) + 1 + + # Build show cards HTML + show_cards = "" + for show in shows: + name = show["name"] + trks = show_tracks.get(name, []) + show_cards += f""" +
+
+
{show['start']} — {show['end']}
+

{name}

+
{show['description']} · Energy {show['energy']} · {len(trks)} tracks
+
+
+ {''.join(f'
' + f'{t["start_formatted"][:5]}' + f'{_esc(t["artist"])}' + f'' + f'{_esc(t["title"])}' + f'{_esc(t.get("album", ""))}' + f'{t["mode"]}' + f'
' for t in trks)} +
+
+""" + + html = f""" + + + + + Radio Susan — 99.0 + + + +
+
+

RADIO SUSAN

+
99.0 FM
+
Generated {generated} · {total_tracks} tracks · {duration}h
+
+ +
+
NOW PLAYING
+
Loading...
+
+
+ +
+ +
+

Up Next

+
+
+ +
+
{total_tracks}
Tracks
+
{len(shows)}
Shows
+
{mode_counts.get('full_album', 0)}
Album Tracks
+
{mode_counts.get('deep_cuts', 0)}
Deep Cuts
+
{mode_counts.get('new_to_library', 0)}
New
+
+ +

TODAY'S SCHEDULE

+ + {show_cards} + + +
+ + + + + +""" + + return html + + +def _esc(s): + """Escape HTML entities.""" + return (s or "").replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """) + + +def main(): + if not PLAYLIST_JSON.exists(): + print(f"ERROR: {PLAYLIST_JSON} not found. Run generate_daily_playlist.py first.") + sys.exit(1) + + playlist = json.load(open(PLAYLIST_JSON)) + print(f"Generating site from playlist ({playlist['total_tracks']} tracks)...") + + OUTPUT_DIR.mkdir(parents=True, exist_ok=True) + + # Generate index.html + html = generate_html(playlist) + index_path = OUTPUT_DIR / "index.html" + index_path.write_text(html) + set_mediaserver_perms(index_path) + + # Copy playlist.json for API access + api_path = OUTPUT_DIR / "playlist.json" + api_path.write_text(json.dumps(playlist, separators=(",", ":"))) + set_mediaserver_perms(api_path) + + print(f"Site generated at {OUTPUT_DIR}") + print(f" index.html: {index_path.stat().st_size / 1024:.1f}KB") + print(f" playlist.json: {api_path.stat().st_size / 1024:.1f}KB") + + +if __name__ == "__main__": + main() -- cgit v1.2.3