Files
Tom FluxandClaude Opus 5 18bb2e420b Initial implementation of youtube-automate
A DVR for YouTube subscriptions, laid out so Jellyfin presents each channel
as a show and each video as an episode. Cron-driven, idempotent, with a
public admin UI for a non-operator.

Verified end to end on susan against three real channels: PO tokens, h264
downloads, Jellyfin resolution from local NFOs with all providers disabled,
retention and tombstones.

Corrections to the original design handover (specs.md documents each with
the evidence, and specs.handover-original.md preserves the original):

- The format sort selected 360p. Ranking acodec above res makes `bv*` prefer
  the combined 360p stream, which carries AAC, over the 720p video-only
  stream whose acodec is none. vcodec now leads, so a video without h264 at
  720p yields h264 lower down rather than VP9 this hardware cannot transcode.
- yt-dlp now requires a JS runtime and the yt-dlp-ejs solver scripts, which
  only ship with the [default] extra. Without them the n challenge fails and
  the mweb formats disappear entirely.
- --flat-playlist carries no upload dates, so the specced client-side date
  filter for backfill was impossible. Backfill is RSS-first.
- skipped_old was terminal, so raising a channel's retention appeared to do
  nothing. Added an explicit rescan.
- is_upcoming premieres now defer and retry instead of being skipped forever.
- TubeArchivist is gone, so the media root and the tube.jihakuz.xyz vhost
  were both reclaimed; the latter still pointed at its dead port.

240 offline tests, no network and no real yt-dlp invocation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 21:42:48 +01:00

33 lines
855 B
TOML

[project]
name = "youtube-automate"
version = "1.0.0"
description = "A DVR for YouTube subscriptions, laid out for Jellyfin"
requires-python = ">=3.11"
# Pins that matter are documented in specs.md §3:
# - the [default] extra is what ships yt-dlp-ejs (the JS challenge solver)
# - curl-cffi must stay below 0.16 or yt-dlp rejects it as unsupported
# - the bgutil plugin must match the container tag
dependencies = [
"yt-dlp[default]",
"bgutil-ytdlp-pot-provider==1.3.1",
"curl-cffi<0.16",
]
[project.optional-dependencies]
dev = ["pytest>=8"]
[project.scripts]
youtube-automate = "youtube_automate.cli:main"
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[tool.setuptools]
packages = ["youtube_automate", "youtube_automate.web"]
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-q"