diff --git a/fetch_podcasts.py b/fetch_podcasts.py index 44df923..e92be1b 100755 --- a/fetch_podcasts.py +++ b/fetch_podcasts.py @@ -413,4 +413,118 @@ def set_archive(slug, value): db = open_subs_db() row = db.execute("SELECT name FROM shows WHERE slug = ?", (slug,)).fetchone() if row is None: - log_error(f"No + log_error(f"No show found with slug '{slug}'.") + return + db.execute("UPDATE shows SET archived = ? WHERE slug = ?", (value, slug)) + db.commit() + db.close() + state = "archived" if value == 1 else "non-archived (live)" + log.info("Show '%s' (%s) is now %s.", row["name"], slug, state) + +def remove_show_data(slug): + pod_dir = PODCASTS_DIR / slug + if pod_dir.exists(): + shutil.rmtree(pod_dir) + txt = PLAYLISTS_DIR / f"{slug}.txt" + if txt.exists(): + txt.unlink() + +def delete_show(slug): + db = open_subs_db() + row = db.execute("SELECT name FROM shows WHERE slug = ?", (slug,)).fetchone() + if row is None: + log_error(f"No show found with slug '{slug}'.") + return + remove_show_data(slug) + db.execute("DELETE FROM shows WHERE slug = ?", (slug,)) + db.commit() + db.close() + played_db = open_played_db() + played_db.execute("DELETE FROM episodes WHERE show_slug = ?", (slug,)) + played_db.commit() + played_db.close() + log.info("Deleted show: %s (%s)", row["name"], slug) + +def import_opml(path): + try: + with open(path) as f: + content = f.read() + except OSError as e: + log_error(f"Cannot read OPML file: {e}") + return + shows = parse_opml(content) + db = open_subs_db() + added = 0 + skipped_video = 0 + for show in shows: + slug = slugify(show["name"]) + existing = db.execute("SELECT slug FROM shows WHERE slug = ?", (slug,)).fetchone() + if existing is not None: + continue + parsed = fetch_feed(show["feed_url"]) + if parsed is None: + log.warning("OPML import: skipping '%s', could not fetch feed.", show["name"]) + continue + if not is_audio_feed(parsed): + log.info("OPML import: skipping '%s' (%s): video podcast.", show["name"], slug) + skipped_video += 1 + continue + guid = show["guid"] or gen_uuid() + db.execute( + "INSERT INTO shows (slug, guid, name, feed_url, source, opml_import, archived) VALUES (?, ?, ?, ?, 'opml', 1, 1)", + (slug, guid, show["name"], show["feed_url"]), + ) + added += 1 + db.commit() + db.close() + log.info("OPML import: %d added, %d video shows filtered out.", added, skipped_video) + +def run_fetch(config): + g = config["gpodder"] + if g.get("enable") is True: + remote = gpodder_sync(config) + if not remote: + log.warning("No subscriptions retrieved from gPodder; using local registry only.") + else: + added = register_remote_shows(remote) + pruned = prune_stale_shows(remote) + log.info("Sync: %d added, %d pruned.", added, pruned) + fetch_all_episodes() + +def main(): + parser = argparse.ArgumentParser(description="Podcast fetcher for radio automation") + parser.add_argument("--list-shows", action="store_true", help="List registered shows") + parser.add_argument("--detail", action="store_true", help="With --list-shows, show feed URLs") + parser.add_argument("--add-show", metavar="FEED_URL", help="Add a show from a feed URL") + parser.add_argument("--delete-show", metavar="SLUG", help="Delete a show and its data") + parser.add_argument("--archive", metavar="SLUG", help="Mark a show as archived (download episodes)") + parser.add_argument("--unarchive", metavar="SLUG", help="Mark a show as non-archived (stream live)") + parser.add_argument("--import-opml", metavar="FILE", help="Import shows from an OPML file") + args = parser.parse_args() + + init_paths() + STATE_DIR.mkdir(parents=True, exist_ok=True) + LOGS_DIR.mkdir(parents=True, exist_ok=True) + PODCASTS_DIR.mkdir(parents=True, exist_ok=True) + PLAYLISTS_DIR.mkdir(parents=True, exist_ok=True) + _setup_logging() + + config = load_config() + + if args.archive: + set_archive(args.archive, 1) + elif args.unarchive: + set_archive(args.unarchive, 0) + elif args.list_shows: + list_shows(detail=args.detail) + elif args.add_show: + add_show(args.add_show) + elif args.delete_show: + delete_show(args.delete_show) + elif args.import_opml: + import_opml(args.import_opml) + else: + run_fetch(config) + +if __name__ == "__main__": + main()