mirror of
https://github.com/mistergibson/radio.git
synced 2026-09-08 22:09:51 -07:00
storage dir setup
This commit is contained in:
parent
952c9ae5a9
commit
1b191b868d
9 changed files with 418 additions and 402 deletions
|
|
@ -1,37 +1,57 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
update_playlists.py - Regenerate per-show .pls playlists based on playback history.
|
||||
Runs via cron hourly at :30.
|
||||
Runs via cron hourly at :30. All data under the storage path from config.json.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import sqlite3
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
STATE_DIR = ROOT / "state"
|
||||
SUBS_DB = STATE_DIR / "subscriptions.db"
|
||||
PLAYED_DB = STATE_DIR / "played.db"
|
||||
PODCASTS_DIR = ROOT / "podcasts"
|
||||
PLAYLISTS_DIR = ROOT / "playlists"
|
||||
LOGS_DIR = ROOT / "logs"
|
||||
CONFIG_PATH = ROOT / "config.json"
|
||||
|
||||
AUDIO_EXTS = {".mp3", ".m4a"}
|
||||
|
||||
STATE_DIR = None
|
||||
SUBS_DB = None
|
||||
PLAYED_DB = None
|
||||
PODCASTS_DIR = None
|
||||
PLAYLISTS_DIR = None
|
||||
LOGS_DIR = None
|
||||
|
||||
def load_config():
|
||||
with open(CONFIG_PATH) as f:
|
||||
return json.load(f)
|
||||
|
||||
def init_paths():
|
||||
global STATE_DIR, SUBS_DB, PLAYED_DB, PODCASTS_DIR, PLAYLISTS_DIR, LOGS_DIR
|
||||
cfg = load_config()
|
||||
storage = Path(cfg["storage"]).expanduser().resolve()
|
||||
STATE_DIR = storage / "state"
|
||||
SUBS_DB = STATE_DIR / "subscriptions.db"
|
||||
PLAYED_DB = STATE_DIR / "played.db"
|
||||
PODCASTS_DIR = storage / "podcasts"
|
||||
PLAYLISTS_DIR = storage / "playlists"
|
||||
LOGS_DIR = storage / "logs"
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
handlers=[
|
||||
logging.FileHandler(LOGS_DIR / "update.log"),
|
||||
logging.StreamHandler(sys.stdout),
|
||||
],
|
||||
)
|
||||
log = logging.getLogger("update_playlists")
|
||||
|
||||
def _setup_logging():
|
||||
fh = logging.FileHandler(LOGS_DIR / "update.log")
|
||||
fh.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
|
||||
log.addHandler(fh)
|
||||
|
||||
def open_subs_db():
|
||||
conn = sqlite3.connect(SUBS_DB)
|
||||
conn.row_factory = sqlite3.Row
|
||||
|
|
@ -56,7 +76,6 @@ def open_played_db():
|
|||
return conn
|
||||
|
||||
def find_audio_files(directory):
|
||||
"""Recursively scan for .mp3/.m4a files."""
|
||||
results = []
|
||||
if not directory.exists():
|
||||
return results
|
||||
|
|
@ -66,34 +85,28 @@ def find_audio_files(directory):
|
|||
return results
|
||||
|
||||
def select_unplayed_episode(slug, played_db):
|
||||
"""Pick one unplayed episode for the show, falling back to least-recently-played."""
|
||||
files = find_audio_files(PODCASTS_DIR / slug)
|
||||
if not files:
|
||||
return None
|
||||
|
||||
played_rows = played_db.execute(
|
||||
"SELECT file_path, played_at FROM episodes WHERE show_slug = ? AND played_at IS NOT NULL",
|
||||
(slug,),
|
||||
).fetchall()
|
||||
played_paths = {row["file_path"]: row["played_at"] for row in played_rows}
|
||||
|
||||
unplayed = [f for f in files if f not in played_paths]
|
||||
if unplayed:
|
||||
return unplayed[0]
|
||||
|
||||
# All played: pick least recently played
|
||||
if played_paths:
|
||||
return min(played_paths.items(), key=lambda kv: (kv[1] or ""))[0]
|
||||
return files[0]
|
||||
|
||||
def write_pls(filepath, out_path):
|
||||
"""Write a .pls playlist pointing at a single file."""
|
||||
abs_path = str(Path(filepath).resolve())
|
||||
content = f"[playlist]\nFile1={abs_path}\nTitle1=Radio Episode\nLength1=-1\nNumberOfEntries=1\nVersion=2\n"
|
||||
out_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
out_path.write_text(content)
|
||||
|
||||
def mark_as_played(slug, filepath, duration, played_db):
|
||||
def mark_as_played(slug, filepath, played_db):
|
||||
played_db.execute(
|
||||
"UPDATE episodes SET played_at = datetime('now') WHERE show_slug = ? AND file_path = ?",
|
||||
(slug, filepath),
|
||||
|
|
@ -104,25 +117,16 @@ def update_all():
|
|||
subs_db = open_subs_db()
|
||||
played_db = open_played_db()
|
||||
shows = subs_db.execute("SELECT slug, name FROM shows ORDER BY name").fetchall()
|
||||
|
||||
for show in shows:
|
||||
slug = show["slug"]
|
||||
selected = select_unplayed_episode(slug, played_db)
|
||||
if selected is None:
|
||||
log.info("%s: no audio files found, skipping.", slug)
|
||||
continue
|
||||
|
||||
duration_row = played_db.execute(
|
||||
"SELECT duration_seconds FROM episodes WHERE show_slug = ? AND file_path = ?",
|
||||
(slug, selected),
|
||||
).fetchone()
|
||||
duration = duration_row["duration_seconds"] if duration_row else None
|
||||
|
||||
out_pls = PLAYLISTS_DIR / f"{slug}.pls"
|
||||
write_pls(selected, out_pls)
|
||||
mark_as_played(slug, selected, duration, played_db)
|
||||
mark_as_played(slug, selected, played_db)
|
||||
log.info("%s: queued %s", slug, Path(selected).name)
|
||||
|
||||
subs_db.close()
|
||||
played_db.close()
|
||||
|
||||
|
|
@ -148,9 +152,11 @@ def main():
|
|||
parser.add_argument("--json", action="store_true", help="Emit JSON summary and exit")
|
||||
args = parser.parse_args()
|
||||
|
||||
STATE_DIR.mkdir(exist_ok=True)
|
||||
LOGS_DIR.mkdir(exist_ok=True)
|
||||
PLAYLISTS_DIR.mkdir(exist_ok=True)
|
||||
init_paths()
|
||||
STATE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
LOGS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
PLAYLISTS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
_setup_logging()
|
||||
|
||||
if args.json:
|
||||
json_summary()
|
||||
|
|
@ -159,4 +165,3 @@ def main():
|
|||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue