This is the Python 3.14 + uv rewrite foundation for Crunchy Downloader. It is intentionally CLI-first and Linux/Docker-first, with the browser UI served by the same Python app so the downloader engine remains the source of truth.
crd doctorverifies Python version, app directories, and external tools.crd doctor --jsonprints the same local diagnostics as stable JSON for scripts.crd versionprints the package version and Python runtime target.crd web servestarts the local Python-backed web UI on127.0.0.1:7860by default. The Vue 3 + Vite UI is served at/;/vue/remains a compatibility alias. The UI includes dashboard, add-download preview/queueing, queue inspection/actions, account status/actions including credentials-file login, subscription/profile details, validated settings editing, local history review/marking/queue-missing with saved sort/filter views, and calendar views with week navigation, filtering, visible episode queueing, Sonarr status/search/manual matching/sync controls, release checklist/update-status review, redacted log viewing, and debug summary/bundle support. The web surface has every-page render smoke coverage, redaction-sensitive API smoke coverage, and a local browser smoke checklist indocs/web_browser_smoke.md.crd web desktopandcrd web desktop-file --output PATHare deprecated compatibility wrappers around the browser server. They do not create separate queue, history, config, token, or download schemas, and they are no longer the primary UI path.- All non-
versioncommands write redacted JSONL lifecycle logs tologs/crd.log.jsonlby default; use global--log-file PATH,--verbose, or--quietto adjust logging. - Known CLI failures include stable
CRD-*error codes in stderr and structured logs. crd debug bundle --output PATHwrites a redacted diagnostic zip with local settings, queue, token status, doctor output, and optional history.crd config import-legacy <old-config-dir>imports old CRD settings into a v1 schema.crd config import-legacy <old-config-dir> --include-secretsalso imports legacy token files into Python-ownedtokens.v1.json.crd auth import-legacy-tokens <old-config-dir>imports only old CRD token files when settings import is not desired or a legacy settings value needs manual cleanup first.crd config show|get|set|pathinspects and updates validated Python-owned settings.- By default, local JSON app files live under
./config/; use--app-dir,CRD_HOME, or the Docker/crd-datamount to redirect the full app data directory. crd history import-legacy <history.json>imports old history JSON without modifying it.crd search <query>lists Crunchyroll series metadata, with--jsonfor scripts.crd download <url-or-id>resolves Crunchyroll episode, season, series, movie, music video, concert, artist, and featured-music metadata and queues normalized item(s). Artist and featured-music sources require--all.crd calendar crunchyroll|anilist|upcominglists Crunchyroll simulcast calendar entries and Crunchyroll-linked AniList seasonal/upcoming entries with cached mocked-test-friendly HTTP.crd sonarr status|series search|match-series|match-episode|syncchecks Sonarr, searches existing Sonarr series, stores Sonarr IDs/file flags in history, and can queue monitored Sonarr-missing Crunchyroll history episodes.crd notify test --event EVENT --provider PROVIDERsends a configured test notification through webhook, execute-command, local-file, or sound providers. Queue runs publish download-finished, download-failed, and queue-finished events as best-effort notifications.crd protected tools status [--json]reports optional Bento4mp4decryptand Shaka Packager helper availability for diagnostics.crd protected encrypted plan ITEM_ID --track MEDIA:TRACK_ID:PATH [--json]prepares a protected/encrypted media handoff plan from already-downloaded track files.crd protected encrypted handoff ITEM_ID --track MEDIA:TRACK_ID:PATH [--json]routes that handoff through the protected/encrypted handler shell.- Protected-media helper paths are configured with
protected_media.mp4decrypt_path,protected_media.shaka_packager_path, andprotected_media.widevine_device_path. User-owned device files should live outside version control, for example under the ignored localconfig/directory. The default device path isconfig/widevine/device.wvd. - Browser-auth inputs are read from user-provided files under the ignored local
config/auth/directory by default. Seeconfig/README.mdfor the local file layout andconfig/examples/for placeholdercredentials.json,cookies.json, andbrowser-token.jsonformats. - HTTP challenge pages can be classified on every shared HTTP path. When
network.use_flare_solverris enabled, the client uses the configurednetwork.flare_solverr_urlFlareSolverr-compatible/v1endpoint to resolve the challenged request and keeps solved cookies/user-agent only in memory. crd queue add|list|remove|clear|runmanages a persistent v1 queue;queue runorchestrates planning, segment/sidecar download, muxing, and v1 history marking for pending items. Queue runs can retry failed work, select failed batches with--failed, cap work with--limit, preview with--dry-run, and skip history updates with--no-history. Whencrunchyroll.mark_as_watchedis enabled, queue-run marks the episode watched after a successful local output only; the profile action is best-effort and secret-free in output. Setdownload.debug_logging=truefor append-only queue-run debug lines on stderr and redacteddownload_debugJSONL events in the active log file.crd queue work list|cleaninspects queue-run work directories and cleans selected failed, known, or orphaned work files without touching final output directories.crd queue plan <item-id> [--segments]inspects playback artifacts and transient manifest plans.crd queue download-segments <item-id> --output PATHwrites selected manifest track bytes to deterministic per-track files with resume support.crd queue download-artifacts <item-id> --output PATHwrites raw subtitles, captions, BIFs, images, and description sidecars without conversion.crd queue convert-subtitles <item-id> --input PATH --output PATHconverts raw ASS/VTT/SRT sidecars into mux-ready subtitle files while preserving the raw downloads.crd queue generate-chapters <item-id> --input PATH --output PATHconverts raw BIF/storyboard sidecars into mux-ready.ffmetadatachapters while preserving the raw BIF files.crd queue sync-timing-plan <item-id> --output PATH --delay audio:ja-JP=120builds a dry-run timing sidecar, andcrd queue sync-timingwrites it for mux planning.crd queue sync-video-plan <item-id> --base-video BASE --compare-video DUB --language ja-JPmeasures two local video tracks and previews an automatic timing sidecar;crd queue sync-videowrites the same sidecar format for mux planning.crd queue mux-plan <item-id> --input PATH --output PATHbuilds a dry-run ffmpeg/mkvmerge command from downloaded tracks and sidecars, including generated chapters, MKV font attachments, sync timing sidecars, and audio-only MP3 output when configured.crd queue mux <item-id> --input PATH --output PATHexecutes the planned ffmpeg/mkvmerge command and writes the final container file.crd queue encode-plan <item-id> --input PATH --output PATHbuilds a dry-run post-mux ffmpeg encode command from the selected preset.crd queue encode <item-id> --input PATH --output PATHexecutes that post-mux encode;queue runuses the same step whenmuxing.encode_enabledis set.crd auth status|browser import|device start|device poll|diagnose|login --anonymous|login --credentials-file PATH|refresh|profile|profiles|use-profile|endpoint|logoutmanages browser-session auth, device-code probing, anonymous/token auth, legacy credentials-file login, and redacted endpoint client-setting overrides.- The root
Dockerfilebuilds the Python CLI runtime with Python 3.14, uv, ffmpeg, mkvmerge, non-root UID/GID10001:10001,/crd-dataapp storage, and/downloadsoutput mounts. crd version --jsonreportscli_contract_version=1; script-facing JSON output is documented indocs/v1_cli_json_contracts.md.
Live auth/search/metadata calls are available, but tests use mocked transports by default.
The living parity checklist is maintained in docs/python_rewrite_parity_checklist.md.
The production beta gate is maintained in docs/beta_release_checklist.md.
The current error-code catalog is documented in docs/error_codes.md.
uv run crd doctor
uv run crd --log-file /crd-data/logs/crd.log.jsonl doctor --json
uv run crd version
uv run crd web serve
uv run crd debug bundle --output /crd-data/logs/debug.zip
uv run crd search "Frieren"
uv run crd download EPISODE_ID --dub ja-JP --subs en-US
uv run crd download movie:MOVIE_ID --dub ja-JP --subs none
uv run crd download artist:ARTIST_ID --all --subs none
uv run crd calendar crunchyroll --week 2026-05-18 --json
uv run crd calendar anilist --season SPRING --year 2026
uv run crd calendar upcoming --from 2026-05-18 --days 7
uv run crd sonarr status --json
uv run crd sonarr series search "Frieren"
uv run crd sonarr sync --queue-missing --json
uv run crd notify test --event queue-finished --provider local-file --json
uv run crd protected tools status --json
uv run crd config set protected_media.mp4decrypt_path /opt/homebrew/bin/mp4decrypt
uv run crd auth browser import-token --authorization "Basic paste-browser-client-authorization-header" --user-agent "Mozilla/5.0 ..."
uv run crd protected encrypted plan ITEM_ID --track video:v720:/downloads/ITEM_ID-video-v720.bin --json
uv run crd protected encrypted handoff ITEM_ID --track video:v720:/downloads/ITEM_ID-video-v720.bin --tool shaka-packager --timeout 120 --json
uv run crd config set network.use_flare_solverr true
uv run crd config set network.flare_solverr_url http://localhost:8191
uv run crd auth diagnose --operation credentials --credentials-file credentials.json --endpoint android --json
uv run crd auth browser import --cookies-file crunchyroll-cookies.json --endpoint browser
uv run crd auth browser import-token --token-file browser-token.json --cookies-file crunchyroll-cookies.json --endpoint browser --auth-url https://www.crunchyroll.com/auth/v1/token
uv run crd auth browser import-token --endpoint browser
uv run crd auth refresh --endpoint browser
uv run crd auth device start --endpoint device
uv run crd auth device poll --endpoint device --device-code DEVICE_CODE
uv run crd auth login --credentials-file credentials.json --solve-challenge
uv run crd --app-dir /crd-data queue list
uv run crd --app-dir /crd-data queue run
uv run crd --app-dir /crd-data queue work list
uv run crd --app-dir /crd-data queue work clean --failed
uv run crd --app-dir /crd-data queue download-segments ITEM_ID --output /downloads
uv run crd --app-dir /crd-data queue download-artifacts ITEM_ID --output /downloads
uv run crd --app-dir /crd-data queue convert-subtitles ITEM_ID --input /downloads --output /downloads
uv run crd --app-dir /crd-data queue generate-chapters ITEM_ID --input /downloads --output /downloads
uv run crd --app-dir /crd-data queue sync-timing ITEM_ID --output /downloads --delay audio:ja-JP=120
uv run crd --app-dir /crd-data queue sync-video ITEM_ID --base-video /downloads/base.bin --compare-video /downloads/dub.bin --language ja-JP --output /downloads
uv run crd --app-dir /crd-data queue mux-plan ITEM_ID --input /downloads --output /downloads/out.mkv
uv run crd --app-dir /crd-data queue mux ITEM_ID --input /downloads --output /downloads/out.mkv
uv run crd --app-dir /crd-data queue encode-plan ITEM_ID --input /downloads/out.mkv --output /downloads/out-encoded.mkv
uv run crd --app-dir /crd-data queue encode ITEM_ID --input /downloads/out.mkv --output /downloads/out-encoded.mkv
uv run crd --app-dir /crd-data auth login --credentials-file /run/secrets/crd-credentials.json
uv run pytest
uv run ruff check
uv run ty checkdocker build -t crunchy-downloader-python:local .
mkdir -p .crd-data/{config,cache,temp,video,logs} downloads
docker run --rm crunchy-downloader-python:local version
docker run --rm -v "$PWD/.crd-data:/crd-data" -v "$PWD/downloads:/downloads" crunchy-downloader-python:local doctor --json
docker run --rm -v "$PWD/.crd-data:/crd-data" -v "$PWD/downloads:/downloads" crunchy-downloader-python:local queue run --output /downloadsOn Linux hosts, make the mounted paths writable by the image user with
sudo chown -R 10001:10001 .crd-data downloads. Full Docker notes are in docs/docker.md.
Live Crunchyroll auth currently works best through a user-owned browser session. Keep real auth
material under ignored local paths such as config/auth/ and config/widevine/; do not commit
cookies, bearer tokens, Authorization headers, credentials, WVD files, or downloaded media. The
expected local layout is documented in config/README.md, and the config/examples/ directory
contains placeholder formats:
config/examples/cookies.example.jsonshows the JSON cookie export shape accepted bycrd auth browser importandimport-token. Netscape cookie exports are also accepted.config/examples/browser-token.example.jsonshows the browser token response fields expected bycrd auth browser import-token.config/examples/credentials.example.jsonshows the legacy username/password credentials-file shape.
For browser auth, provide:
- A cookie export from your own logged-in Crunchyroll browser session.
- A token JSON copied from your own browser token response, containing at least
access_token,refresh_token, andexpires_in. - Optional one-off browser client details, such as the browser request's
Authorizationheader value or user-agent, ascrd auth browser import-tokenflags when needed. mp4decryptand a user-owned Widevine device file if you plan to process protected media.
Place the user-owned auth files in the ignored local config directory:
mkdir -p config/auth config/widevine
cp config/examples/credentials.example.json config/auth/credentials.json
cp config/examples/cookies.example.json config/auth/cookies.json
cp config/examples/browser-token.example.json config/auth/browser-token.json
uv run crd config set protected_media.mp4decrypt_path /opt/homebrew/bin/mp4decryptThen import and refresh with short commands:
uv run crd auth browser import-token --endpoint browser
uv run crd auth profile --endpoint browser
uv run crd auth refresh --endpoint browserFlags override conventional files. For one-off imports, pass --token-file, --cookies-file,
--authorization, --auth-url, --playback-endpoint, and --user-agent directly.
Browser-session import stores cookies in sessions.v1.json with redacted debug-bundle output.
Browser token import stores token metadata in tokens.v1.json; refresh uses stored endpoint
metadata and browser cookies so the short-lived access token does not need to be pasted repeatedly.
Device-code login is probed through standard OAuth device-code
endpoints and works only if Crunchyroll exposes a compatible response for the configured
endpoint/client.
Credentials-file login expects JSON such as
{"username":"name@example.test","password":"account-password"}. The file is read once, not
copied into app storage, and credentials are redacted from logs. Crunchyroll may reject this legacy
OAuth grant/client combination; prefer browser-session import or device-code login when available.
Run live account tests only in a throwaway app directory.
Use crd auth endpoint set NAME --authorization "Basic ..." --user-agent "..." only for
user-supplied Crunchyroll client settings you are allowed to use; show and list always redact
the stored Authorization header.
Release notes and smoke-test prompts live in docs/release_notes_template.md,
docs/smoke_checklist.md, docs/web_browser_smoke.md, and docs/desktop_wrapper.md.
Deprecated desktop compatibility commands are documented in docs/desktop_wrapper.md for existing
launcher users, but new workflows should use crd web serve and open the browser UI directly.
If Python 3.14 is not installed locally, use python3 -m unittest discover for syntax-level
development feedback until the target interpreter is available.