Auto-download new podcast episodes from an RSS feed

A show folder under Kinderpodcasts/ opts in by containing a feed.txt marker
naming its RSS feed. A new PodcastFeedService polls every such feed every 6
hours (and once at startup), downloads any episode not already on disk using
the existing YYYYMMDD - Title.ext convention, and triggers the same
rescan-and-broadcast sequence "Bibliothek neu einlesen" already uses - now
shared via App.rescan_library() instead of duplicated. A show with no
feed.txt is untouched, so there is no new config section for this.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-10 22:43:50 +02:00
parent 2e0e6ad199
commit 57afc32f4a
9 changed files with 652 additions and 8 deletions

View File

@@ -21,6 +21,8 @@ from collections.abc import Awaitable, Callable, Coroutine
from pathlib import Path
from typing import Any
import httpx2
from musicmouse import __version__
from musicmouse.app import App
from musicmouse.bus import EventBus
@@ -35,6 +37,7 @@ from musicmouse.library.analysis import build_analyzer
from musicmouse.reactions import register_all
from musicmouse.services.base import Service
from musicmouse.services.mqtt import MqttService, build_entities
from musicmouse.services.podcasts import PodcastFeedService
from musicmouse.services.web import WebService
_log = logging.getLogger("musicmouse")
@@ -327,6 +330,19 @@ def _build_services(
else:
services.append(WebService(app, web_config, config_path))
# Unconditional: a show only starts downloading once someone drops a `feed.txt`
# into its folder, so there is nothing to gate here with its own config section.
web_service = next((s for s in services if isinstance(s, WebService)), None)
services.append(
PodcastFeedService(
app.library,
client=httpx2.AsyncClient(timeout=30.0),
on_change=lambda: app.rescan_library(
broadcast=web_service.hub.broadcast_library if web_service else None
),
)
)
return services