Commit Graph
219 Commits
Author SHA1 Message Date
imSp4rky 9f4625d971 fix(proxy): do not warn about requests to a configured remote CDM 2026-10-16 17:58:38 -06:00
imSp4rky bfda31ce4a fix(proxy): keep service traffic on --proxy and warn about requests that bypass it
--proxy now overrides HTTP(S)_PROXY and the Windows system proxy while keeping the env CA bundle. Core fallback sessions use the run's proxy, and a warn-only audit hook names service code that sends a request or opens a raw socket outside the proxy; a session with proxy_exempt = True opts out.
2026-10-16 16:12:42 -06:00
imSp4rky dc169f81e1 fix(vaults): stop querying sibling KIDs and skip unavailable vaults
The server CDM stub names only the manifest's KIDs; sibling keys stay content keys. API and HTTP vaults retry a 429, and a vault that is down or still rate limited costs one timeout per run.
2026-10-16 14:49:37 -06:00
imSp4rky 614a8c5ae4 fix(subtitles): sort cues by start time when converting from SSA/ASS
Convert a time-sorted copy of the SSA/ASS source, so that SubtitleEdit and pysubs2 write SRT cues in order. SSA/ASS targets keep the file order.
2026-10-15 16:39:19 -06:00
imSp4rky a4ce70a53c feat(tracks)!: match a language tag to the variant it means
A tag with a region (en-US, es-419) matches only that variant and reports a miss instead of taking another region. A bare tag takes the variant it means (pt -> pt-BR, es -> es-ES) and falls back to any variant only when that one is missing. `orig` prefers the title's own tag, `-sl orig` resolves, forced and SDH subtitles are picked from their own pools, and the track name no longer strips the region from the cached language.

BREAKING CHANGE: -l es-419 no longer selects es-ES, and language_priority, language_tags and muxing.default_language treat a regional tag as exact.
2026-10-15 12:26:39 -06:00
imSp4rky 8a1fbf2de1 feat(music): embed plain lyrics and save timed lyrics from Song.synced_lyrics 2026-10-14 12:37:43 -06:00
imSp4rky b107bca168 feat(music): save lyrics beside the audio and keep song fields over remote
Song.lyrics_suffix writes the lyrics file; song fields set in get_tracks now reach a remote client.
2026-10-14 11:04:29 -06:00
imSp4rky d41bb73d9e feat(remote): decrypt service-owned cbcs samples on the server
The client downloads the file and sends binary sample frames to a new samples route.
2026-10-14 11:03:14 -06:00
imSp4rky 5ffa9a4e2b feat(drm): add in-process cbcs decrypter with an external sample decrypter hook 2026-10-14 09:42:07 -06:00
imSp4rky 6b04d7b137 fix(vaults): lengthen the whole-file key check and steady its HEVC test
Wrong-key HEVC noise can decode clean for seconds in small frames, so the tail check now decodes 8 s. The test clips are 640x360 with a fixed IV.
2026-10-13 17:32:29 -06:00
imSp4rky 4fbaf2a61a feat(api): forward exact_lang to the service context
The remote-dl client sends exact_lang in dl_params, and REST download jobs set it in the service ctx, so a service can skip the manifests of close-match languages.
2026-10-13 15:07:01 -06:00
imSp4rky 3e64a7d684 fix(api): follow the cdm config key order for the server CDM 2026-10-13 13:40:38 -06:00
imSp4rky 2ba442f50e feat(dl): load the DRM system a cdm entry lists first
The first of the widevine / playready config keys in a cdm entry sets which device loads first and which DRM system licenses a track that offers the two. An entry that lists playready first changes from Widevine to PlayReady.
2026-10-13 12:44:11 -06:00
imSp4rky b4136e1cf5 perf(sample): download only the subtitle segments around the window
A segmented subtitle track took every segment in a --sample run. It now takes the window plus one segment on each side.
2026-10-11 19:14:14 -06:00
imSp4rky ecdfb667c9 feat(dl): add --sample and --include-sample
--sample downloads only a time window of a title (default 120-180 s) and cuts the mux to it. --include-sample cuts a 60 s sample from a finished file. Both write to a Sample folder with a SAMPLE tag on the filename
2026-10-11 14:53:50 -06:00
imSp4rky 0a7c76561c feat(search): add ANONYMOUS_SEARCH to skip the login for a service
A service that sets the flag gets no cookie load, no authenticate() call and no cookie save on a search, on the CLI and in the REST search handler. /api/services reports it as anonymous_search.
2026-10-10 13:21:14 -06:00
imSp4rky 09cae2ec3f feat(serve): relay approval messages to jobs and proxy remote downloads
A REST job shows a wait_for_approval message as its notice field. search, list-titles and list-tracks fail at once on a login that needs the user. A remote client downloads through the --proxy it sends.
2026-10-07 11:15:16 -06:00
imSp4rky 5b0369e63a feat(service): add wait_for_approval and return the cache for a login with no prompt
A pair code shown in a log line never answers a prompt, so the server read that login as anonymous and kept the session cache.
2026-10-07 09:03:18 -06:00
imSp4rky 3fd69e8039 feat(serve): add --cdm-only for a CDM backend with no REST API
Mounts only the pywidevine and pyplayready routes plus /api/health and the dashboard; no session store, service refresh or Swagger.
2026-10-06 18:24:21 -06:00
imSp4rky f86359de5d feat(serve): reload unshackle.yaml on SIGHUP without a restart
Extract the serve validation and app config builder so startup and reload share them. A bad file or a restart-only change keeps the running config; open CDM sessions survive.
2026-10-06 17:56:47 -06:00
imSp4rky eea4b41b70 feat(serve): log CDM calls for the dashboard
Add GET /api/dashboard/cdm-logs: one record per pywidevine/pyplayready call plus open sessions per device, with no key or licence material.
2026-10-06 14:58:08 -06:00
imSp4rky 94e5264396 fix(cdm): send a PlayReady licence without an XML declaration as XML
A remote CDM tried base64 first on any licence not starting with "<?xml", which can decode XML text into garbage. Also lists L3 as a device_name.
2026-10-06 13:59:40 -06:00
imSp4rky 3477d09cc2 feat(dl): add --audio-description-only and --forced-subs-only
Each keeps one kind of track inside its track type. A run of more than one title skips a title that has no such track. The REST API rejects a non-boolean value for a boolean dl param, and the DASH parser reads audio description from Role elements.
2026-10-06 12:45:52 -06:00
imSp4rky 9fc25e53d5 fix(hybrid): read the container file for a base that had the DV fixup
The fixup replaces a track with a raw HEVC file, which has no duration and cannot be seeked, so Level 5 crop detection raised KeyError.
2026-10-05 17:32:30 -06:00
imSp4rky 1d2cb3a102 fix(dl): read the vaults first under --all-drm
A DRM system sends a challenge only for a KID with no content key. --cdm-only keeps one challenge per system for each set of KIDs.
2026-10-05 16:43:54 -06:00
imSp4rky ee38477b11 feat(dl): add --all-drm to license Widevine and PlayReady per track
Each DRM system sends one challenge per KID set in a run, also when a vault has the content keys. Needs a CDM for each system in the cdm config.
2026-10-05 15:22:01 -06:00
imSp4rky b530d96e9f fix(docs): hide the release version in the header repo widget 2026-10-04 21:52:38 -06:00
imSp4rky 34eb94b55d fix(sanitize): normalise filenames to NFC and keep combining marks
Remove only control characters in unicode mode. A mark after a zero width joiner or a digit was still dropped (#161). In ASCII mode a decomposed title now transliterates like the composed one.

Names change for titles with combining marks, compatibility ideographs or decomposed spellings, so an existing library can get a second folder.
2026-10-02 13:22:04 -06:00
imSp4rky 9a1abd23b6 feat(remote): license with the client's device when the user picked one
A remote client that passes --cdm, has a cdm entry for the service that named the loaded device, or sets server_cdm: false now declines the server CDM at session create, and warns when the server keeps it. A service option listed in SERVER_DEVICE_OPTIONS keeps the server CDM.
2026-10-01 13:21:53 -06:00
imSp4rky c7ab04028a feat(setup): add unshackle setup command and cross-platform install scripts
setup downloads the external tools, writes a first config and imports CDM devices; install.sh and install.bat bootstrap uv and run it.
2026-10-01 12:21:24 -06:00
imSp4rky fdcc653b66 fix(api): keep the worker's error on failed jobs, gate tracebacks behind --debug-api
Failed jobs now keep the worker's traceback, error_details and error_code instead of the "Worker exited" wrapper. error_traceback and worker_stderr only reach API clients under --debug-api.
2026-09-29 15:53:10 -06:00
imSp4rky 37e2b59bce feat(dl): add --latest-episodes N to download the N newest episodes
Split episodes count once. Exposed as latest_episodes on the REST API. Closes #159.
2026-09-28 09:01:40 -06:00
imSp4rky 4f188b866f fix(cdm): skip the server header probe for pywidevine and pyplayready remote CDMs
A reverse proxy or CDN rewrites the Server header, so upstream refused a working serve API.
2026-09-25 22:54:08 -06:00
imSp4rky 746bf184d2 refactor(proxies): share proxy dispatch between dl, search and resolve_proxy
A bare query skips a failing provider and errors when none answers; an unknown prefix such as us:seattle fails with a hint. The REST path now loads WindscribeVPN.
2026-09-25 18:14:26 -06:00
imSp4rky 80c0dc4ebf fix(proxies): return None for unservable queries and repair live providers
Basic, NordVPN, Windscribe, Gluetun and Hola no longer raise on a query they cannot serve. Hola parses stdout only and reports a ban; NordVPN cities use the API filter; uk maps to gb.
2026-09-25 18:13:31 -06:00
imSp4rky 40281660f1 fix(proxies): skip a proxy provider that fails to load
A bare --proxy query moves past it; naming it (--proxy nordvpn:us) fails with the load error. dl, search and the REST path share one loader.
2026-09-25 17:28:29 -06:00
imSp4rky 34c04e0587 fix(proxies): match surfshark's current server api
Server names replace numeric server IDs; unknown queries return None so the next proxy provider can answer.
2026-09-25 17:27:36 -06:00
imSp4rky 69360d1b4c feat(remote): let a remote client license with its own device identity
Relay the service's CDM calls to the client, accept the service's allowed identity config (for example ESN/Kpe/Kph), add the server_vault grant and a lent server-device mode that reads keys only from vaults and returns no login cache. Also fixes the proxied certificate fetch, Base64 Widevine licences, an answered call that stayed pending and a missing default quality.
2026-09-25 12:32:49 -06:00
imSp4rky 174192882f feat(serve): relay service prompts from download jobs to the REST client
A job's input_prompt carries a service's request_input() prompt; the client answers it with POST /api/download/jobs/{job_id}/input.
2026-09-25 09:32:35 -06:00
imSp4rky 0e4e6f2ec6 feat(remote): list only configured services on the dashboard
/api/dashboard/services and the /status count now use serve.services, else the union of serve.users services lists; ?all=1 lists every installed service.
2026-09-24 17:14:18 -06:00
imSp4rky 7d31153c7f feat(remote): cap server CDM licensing by height per API key and service
A session above the cap licenses with the client's own device, and a track above it in an uncapped -q run is handed back to the client. Also names the fallback in the ccextractor crash warning.
2026-09-24 16:50:26 -06:00
imSp4rky 91db1fa0b1 feat(remote): keep slow remote session requests alive with a heartbeat
Titles, tracks and licence stream a newline every 30s until the body is ready, so a slow service outlasts the client timeout and proxy idle limits; add a per-server timeout.
2026-09-24 10:18:03 -06:00
imSp4rky c06b00ec63 fix(import): read an HLS export that names no title language or master key
Probe each media playlist for its KIDs and refuse a manifest unshackle cannot read.
2026-09-23 16:20:49 -06:00
imSp4rky d014c69d00 feat(export): separate export file name parts with dots
Titles, scope and quality parts use dots and never follow output_template.
2026-09-23 15:26:46 -06:00
imSp4rky a19c3e3879 fix(export): name the file from the first write 2026-09-23 15:11:15 -06:00
imSp4rky 347668eafa docs(export): describe the export file name and its messages 2026-09-23 15:11:15 -06:00
imSp4rky 80088fe4f6 feat(import): build tracks from direct-file export rows
Imports unidl json_manifest exports made of whole files.
2026-09-23 15:04:22 -06:00
imSp4rky 06866eb12e feat(export): record each track's KIDs
An import reads them first, so a track gets its own keys without a probe.
2026-09-23 14:35:58 -06:00
imSp4rky 84bb07f589 fix(import): stop clearly when no export or vault key exists
The bad-key retry now requests each dropped KID.
2026-09-23 14:33:34 -06:00
imSp4rky 8a942e7795 fix(import): read export files through mediaexport
The command still required the old v2 format.
2026-09-23 13:30:35 -06:00