Commit Graph
138 Commits
Author SHA1 Message Date
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 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 9bc6f5fb58 fix(dl): keep the --enrich title and year after get_tracks
A service that sets its title again in get_tracks, as a remote session does, replaced the metadata name and year, so the file took the service's title. A song takes only the language.
2026-10-04 15:57:51 -06:00
imSp4rky 73bdb98d3e fix(console): write UTF-8 to redirected streams on Windows 2026-10-02 14:07:05 -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
Chaloemchai 7b3794333f fix(sanitize): keep nonspacing marks on letters in unicode filenames (#162) 2026-10-02 11:56:56 -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 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 5838f2aed6 fix(tracks): keep the Dolby Vision record when remuxing video with ffmpeg
The MP4 muxer drops dvcC/dvvC unless -strict unofficial is set, so a repacked DV track muxed as plain HEVC and lost its DV tag
2026-09-25 16:55:51 -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 1af7d86b88 test(import): assert on the ClickException, not the wrapped error panel
A long tmp path on macOS and Windows runners made rich-click split the message across lines.
2026-09-23 16:07:33 -06:00
imSp4rky bc50167791 fix(import): give no title keys to a track that is clear
A subtitle, or a track the export lists no KID for while it lists others, gets no DRM.
2026-09-23 15:52:57 -06:00
imSp4rky 28ddd08917 fix(import): say an import uses the export's keys, not a server CDM
The CDM type stub now follows the exported DRM system.
2026-09-23 15:48:50 -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 ce42cd245b feat(export): name export files after their titles and tracks
Log what each title and the run exported.
2026-09-23 15:11:14 -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 fc26297ff0 fix(import): give each track only the keys it declares
The whole title pool stays the fallback for a track that declares no KID.
2026-09-23 14:30:17 -06:00
imSp4rky 47f30668ee fix(drm): give the zero-KID fallback only the track's own key
mp4decrypt and shaka use the last key for a repeated KID.
2026-09-23 14:27:30 -06:00
imSp4rky f470eed703 fix(import): report refused titles and fail when none is usable 2026-09-23 13:30:36 -06:00
imSp4rky 224e066adb fix(export): skip bad keys and failed writes without stopping the download 2026-09-23 13:30:36 -06:00
imSp4rky 4c8c630dd1 test(export): cover header stripping and key merging 2026-09-23 13:30:35 -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
imSp4rkyandAntCardFr 783c8ae1c2 feat(proxies): add Control D proxy provider
Runs a loopback forwarder per region-named profile (unshackle-<region>), so
--proxy and --proxy-download can exit in different countries

Thanks to Orni_ for the original code and the test account.

Co-authored-by: AntCardFr <24798811+AntCardFR@users.noreply.github.com>
2026-09-23 13:23:50 -06:00
imSp4rky ba819aa11e fix(export): strip session headers from exports and normalise merged keys 2026-09-23 11:19:26 -06:00
imSp4rky 30ea001cb3 Merge branch 'feat/new-export' into dev 2026-09-23 11:12:19 -06:00
imSp4rky b36db97ffd fix(serve): stop repeating server vault pushes and retry failed ones 2026-09-22 20:23:22 -06:00
imSp4rky c7c19ae06e fix(dl): skip empty vault pushes and count cached keys once
A licence that repeats vault keys no longer sends an empty InsertKeys, --cdm-only no longer logs "Cached N Keys to 0/0 Vaults", and a key two tracks both licensed counts once.
2026-09-22 19:54:18 -06:00
imSp4rky b988951e17 test(vaults): widen the timing margin in the parallel retry test 2026-09-22 10:59:11 -06:00
imSp4rky d239964f0b fix(serve): apply staged service reloads when sessions end, with a max staging age
A staged tag was only retried on the refresh tick or a job completion, so a service with steady remote sessions never swapped in. Session end, expiry and eviction now trigger a single-flight apply, the tick applies after the pull, and serve.services_staged_max_age swaps an overdue tag in even while busy.
2026-09-22 10:40:49 -06:00
imSp4rky 6e681af513 feat(manifests): extend merge_segments rolling merge to HLS and ISM
RollingMerge takes the segment path and an append hook. ISM synthesises the init from segment 0 and converts PIFF per fragment; HLS qualifies only with one init, one key and no discontinuity. Both parsers check the merge reached every segment, and resume is decided per track.
2026-09-21 20:53:37 -06:00
imSp4rky 79d109d548 feat(dash): add merge_segments to append segments during the download
Opt-in rolling merge for DASH video and audio: each contiguous segment is appended to the output as it lands, so the post-download "Merging" stage goes away. Decrypts the init segment eagerly when decrypt_segments is on, disables resume for the track, and skips subtitle tracks.
2026-09-21 19:13:39 -06:00
imSp4rky a254830121 fix(export): import side-loaded subtitles and keep conflicting keys apart
The importer builds subtitle tracks from track rows that carry a url, which the format promised and no reader did. The writer keeps a conflicting KID instead of overwriting it, identifies a manifest by its whole URL, writes subtitle rows as srt or vtt rather than enum names, and survives an export file it cannot read.
2026-09-20 10:39:13 -06:00
imSp4rky 746385101b feat(export): write mediaexport files and read legacy exports through the package
--export writes the mediaexport format with DRM, content keys and manifests at title level, and unshackle's own track dicts under x-unshackle. unshackle import reads mediaexport, unidl v1 and unshackle v2 through the shared reader, applies the export's request headers to the import session, and rebuilds DRM from the title-level pool. mediaexport is a git dependency on unshackle-dl/mediaexport.
2026-09-20 00:07:04 -06:00
imSp4rky 9c9e059e2d fix(remote): forward nested service cache files to and from the server
Cache keys are now the path relative to the service cache dir without .json, so Cacher keys like session_web/<sha1> reach the remote session. The credential filter runs on every path segment, the suffix is appended rather than substituted, and both sides cap the map at 64 keys.
2026-09-19 21:42:01 -06:00
imSp4rky af866e0c95 fix(vaults): harden the KID window check and clear a stale flag on licence 2026-09-18 17:57:00 -06:00
imSp4rky b30e6d7cbe fix(vaults): verify vault content keys per KID from the fragment map
Map each KID to its fragments from the ciphertext moov/moof (tenc, seig, ISM duration clock) and decode up to three windows per KID that never cross into another KID, so a clear lead cannot hide a wrong key and only the failing KID is flagged. Decode every frame (wrong-key HEVC keyframes decode clean), re-judge mid-GOP starts on keyframes, time out every FFmpeg run, and refuse to store a flagged pair again in SQLite.
2026-09-18 01:10:14 -06:00
imSp4rky cf2906f120 fix(cli): make the click param types the one source of codec spellings
--acodec now lists its codecs in --help, --vcodec drops the duplicate entries, and the REST API derives its allowlists from the same types, so AC-4, DDP, VORB and sub_format "original" stop being rejected and H264 stops being silently dropped. Adds shell completion for --acodec and repairs MultipleChoice completion. Raises the click floor to 8.2.
2026-09-17 19:22:19 -06:00