Commit Graph
81 Commits
Author SHA1 Message Date
imSp4rky d8a4950b99 fix(import): keep only exported dash content periods on a re-fetch
The export writes mediaexport content_periods per manifest; an import that re-fetches the MPD (no saved snapshot) drops the periods a service filtered.
2026-11-02 20:58:49 -07:00
imSp4rky 2bbb2d65b8 ci: block on mypy and bandit and run hooks from the lock
Pre-commit runs ruff, isort and bandit through uv run, so it uses the versions in uv.lock. Bandit moves from runtime dependencies to the dev group.
2026-11-01 19:32:34 -07:00
imSp4rky 7898ff6dd6 refactor(types): bring mypy to zero errors across unshackle
Fix the bugs the type errors exposed (RnetSession.send body, HLS proxy session, title cache fallback, mux progress) and gate mypy in pre-commit, warn-only in CI.
2026-10-30 20:51:21 -06:00
imSp4rky 9f82777394 ci: run the unit suite in parallel and keep it off the network
Test servers shut down without the 0.5s poll wait, unit tests refuse non-loopback lookups, and three load-sensitive tests are steadied. Pushes to dev test Python 3.11 only; the docs job builds with uv.
2026-10-13 19:31:23 -06:00
imSp4rky 83eef01fad chore(release): bump version to 6.0.0 2026-10-13 16:32:19 -06:00
imSp4rky 878838de30 build(deps): lock mediaexport at fa9357e 2026-09-23 15:17:30 -06:00
imSp4rky ed3159378f build(deps): pull mediaexport from github in the package metadata 2026-09-23 11:29:37 -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 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 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
imSp4rky a6cb0231d5 chore: bump dev version to 5.5.0 2026-08-15 16:00:14 -06:00
imSp4rky 0576d9d03c chore(release): 5.4.0 2026-08-15 15:54:07 -06:00
imSp4rky db9ec3e334 feat(cli): add named color themes and rich-formatted help
Render all click help through rich-click, themed by a new `theme` config key (9 palettes + aliases, previewable via `unshackle env theme`), and show service docstrings in local and remote `dl <TAG> -h`.
2026-08-13 16:25:14 -06:00
imSp4rky 72581ba5d2 feat(metadata)!: replace animeapi with a first-class anilist provider
--anilist supersedes --animeapi (REST: anilist_id); the keyless AniList
GraphQL provider joins the registry with title search, mal: ID lookup,
an ANILIST mkv tag, and the anilist_title_language config option.
Services mark anime via Service.ANIME / Title.anime for anilist-first
metadata resolution with fall-through.
2026-08-11 16:42:05 -06:00
imSp4rky d853ef9600 ci: add multi-os test, lint, and wheel build workflows
Also fixes the sdist leaving wheels empty, pins subby to its git
source in wheel metadata, and fixes docstrings that broke mkdocs --strict.
2026-07-15 14:49:19 -06:00
imSp4rky f42af571a7 build(deps): cap pycaption below 2.2.27
2.2.27's WebVTT reader rewrite removes _decode and adds strict header
validation, breaking WebVTTReaderExt and HLS X-TIMESTAMP-MAP segments.
2026-07-11 18:47:35 -06:00
imSp4rky d4a58267d4 chore(release): bump version to 5.3.0 2026-06-30 20:51:17 -06:00
imSp4rky a2f670b140 feat: support Python 3.14 and widen range to 3.11-3.14
Move the supported interpreter range to >=3.11,<3.15, dropping the EOL 3.10 floor and unblocking installs on 3.13/3.14.

- pyproject: requires-python ">=3.10,<3.13" -> ">=3.11,<3.15"
- update_checker: replace deprecated asyncio.get_event_loop() with asyncio.to_thread() (warns on 3.12+, slated for removal)
- test_compression: simplify _run() to plain asyncio.run()
- README: bump documented Python range to 3.11 - 3.14
- uv.lock: regenerate for the new interpreter floor
2026-06-20 15:25:42 -06:00
imSp4rky ba69bc7d61 feat(dl): add --merge-video to merge video language variants
Group selected videos by (resolution, range, codec) and mux each group into one MKV; only language is collapsed, so ranges/codecs/resolutions stay in separate files. Adds --merge-video flag + muxing.merge_video config (global and per-service), docs, and tests.
2026-06-17 13:31:21 -06:00
sp4rk.yandMrMovies-Dev 78a6a97fcf feat(music): native music core - shared helpers, album folder template, display cleanup (#125)
* Add native Music core workflow

* feat(music): add shared music core helpers, album folder template, and UX cleanup

Consolidate duplicated music-service logic into core and extend the native music workflow.

- add core/music/extract.py: shared stateless helpers (first_text, first_number, year/duration/name formatting, classify_release_kind, dedupe_track_options, build_music_from_songs) so services stop carrying their own copies
- add core/music/display.py: shared rich rendering (render_track_panel, render_album_header, render_artwork_preview) with TrackRow / MusicHeaderInfo data holders
- export the new helpers from core/music/__init__.py
- add dedicated `albums` folder template kind (output_template.folder.albums) resolving albums -> songs -> "{artist} - {album} ({year})"; whitelist music template variables (album_artist, track_total, disc_total, release_type, genre, explicit, isrc, upc, label)
- fix song filename crash: config.get_output_template(...) did not exist; use config.output_template.get("songs") with a sane default
- strip emojis from music output (renderer, dl.py music branch) to match unshackle UX; remove dead MusicSongPlan import and music_icon logic
- document the albums folder key in unshackle-example.yaml
- add tests for extract, display, and the folder template

---------

Co-authored-by: MrMovies-Dev <MrMovies-Dev@users.noreply.github.com>
2026-06-15 13:34:34 -06:00
imSp4rky 7064d261c2 fix(security): sanitize logs, redact secrets, harden XML parsing
Address actionable CodeQL security-extended findings and extend the same redaction to the debug JSONL logger
2026-06-09 19:08:41 -06:00
imSp4rky 2d43c2601e chore(deps): bump subby to 0.3.30 and pycaption to 2.2.22
- subby pin 1ea6a52 -> a057280: improved missing-line-split handling, duplicate frontal hyphen removal, UTF-8 without BOM output, Python <=3.11 syntax fix
- pycaption 2.2.20 -> 2.2.22: SCC-only fixes, no impact on our SRT/DFXP/WebVTT usage
- pytest: filter third-party warnings (aiohttp NotAppKeyWarning required by pywidevine/pyplayready string app keys, pycaption bs4 deprecations)
2026-06-09 18:49:16 -06:00
dependabot[bot] a79cf2b5ee chore(deps): bump aiohttp from 3.13.5 to 3.14.0
---
updated-dependencies:
- dependency-name: aiohttp
  dependency-version: 3.14.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-04 00:21:47 +00:00
imSp4rky f1febe7e43 chore(release): bump version to 5.1.0 2026-05-26 10:54:14 -06:00
imSp4rky 7654e91ebc feat(dl): gate s_lang/a_lang miss behind --best-available
Missing requested subtitle and audio languages now warn and continue when --best-available is set instead of hard-exiting. Without the flag, missing languages still produce an error and exit, matching the prior strict behavior. Audio missing-lang detection is now symmetric with subtitles.

- add find_missing_langs helper in core/utilities for reuse between s_lang and a_lang paths (skips all/best/orig sentinels)
- refactor dl.py s_lang/a_lang checks to share the helper
- add tests/lang_selection covering match primitives, helper output, and tricky langcodes corners (zh-Hans/zh-Hant/zh-CN/zh-TW/zh-HK, cmn/yue, fil/tl/tgl)
- clean up unused-var ruff F841 in tests/remote/unit/
2026-05-22 13:52:35 -06:00
imSp4rky b1158099d1 fix(dash): expand CICP enums to full H.273 range
Video.Range.from_cicp() crashed with ValueError on any CICP primaries, transfer, or matrix value outside the small subset previously enumerated (e.g. TransferCharacteristics=4 / BT.470M gamma 2.2 seen in SCTE-stitched live MPDs). Extend the three inner enums to cover all documented ITU-T H.273 / ISO/IEC 23001-8 values and fall back to Unspecified on unknown codes so SDR content no longer fails to parse.

Source: https://raw.githubusercontent.com/FFmpeg/FFmpeg/master/libavutil/pixfmt.h (authoritative implementation of ITU-T H.273 / ISO/IEC 23001-8 tables). ITU spec itself: https://www.itu.int/rec/T-REC-H.273
2026-04-24 11:50:27 -06:00
imSp4rky 8f4f947d0d fix(api): sync REST API download endpoint with updated dl command (#98)
The REST API download endpoint was broken after recent dl command changes.

- Add missing vbitrate_range, abitrate_range, and worst parameters to the API call and DEFAULT_DOWNLOAD_PARAMS
- Convert wanted episode strings (S01E01) to internal SxE format via SeasonRange so episode filtering works correctly
- Track completed output files via dl.completed_files instead of returning an empty list

Closes #98
2026-04-08 23:04:58 -06:00
imSp4rky 0ebf9278d1 fix(deps): bump aiohttp and pygments to resolve 11 security vulnerabilities 2026-04-02 11:44:54 -06:00
imSp4rky ccc494be06 Merge branch 'feat/unified-downloader' into dev
# Conflicts:
#	unshackle/core/manifests/hls.py
2026-03-30 05:24:09 +00:00
Andy 7358619a40 fix(deps): bump PyJWT minimum to 2.12.0 for CVE-2026-32597
PyJWT <= 2.11.0 accepts unknown `crit` header extensions in violation of RFC 7515 §4.1.11. Bump lower bound to 2.12.0 which includes the fix.
2026-03-25 15:16:21 -06:00
Andy 99be88dc08 feat(session): replace curl_cffi with rnet for TLS-fingerprinted HTTP
Replace CurlSession (curl_cffi) with RnetSession powered by rnet (Rust/BoringSSL). Benchmarks show 3.5x faster segmented downloads (1.06 GB/s vs 304 MB/s) and 16% faster single-file downloads with near-zero TLS fingerprinting overhead.

- Add RnetSession wrapper with requests-compatible API (headers, cookies, proxies, retry logic, prepared requests)
- Add RnetResponse wrapper normalizing rnet quirks (status_code as int, text as property, bytes-to-str headers, iter_content re-chunking)
- Replace CurlSession isinstance checks across manifests, tracks, DRM
- Update downloader with rnet native streaming path and byte-based progress tracking for accurate Rich progress bars
- Add speed display column to Rich progress bar (DASH/HLS/URL prefix)
- Add rnet dependency, services use exact preset names (e.g. OkHttp4_12)
2026-03-24 10:08:17 -06:00
Andy 732709d3a9 feat(remote): interactive auth handshake, server CDM, cache round-trip, and serve remote-only mode
Add InputBridge for interactive client-server authentication (OTP, device codes, PINs) with async auth via asyncio.to_thread, prompt polling endpoints, and cancellation support.
Server CDM mode detects CDM type from config.cdm per-service, resolves keys server-side, and returns DRM type to client for correct display. Cache files round-trip between client and server on session create/delete. Vault loading fixed for server-side key caching.
HLS/ISM/DASH DRM extraction in license handler. Serve --remote-only mode exposes only session endpoints. Clean connection error handling for unreachable servers.
2026-03-22 22:44:36 -06:00
Andy 572a894620 feat(dl): add --animeapi and --enrich options for anime metadata and tagging
Add AnimeAPI integration to resolve anime database IDs (MAL, AniList, Kitsu, etc.) to TMDB/IMDB/TVDB for MKV tagging. The --enrich flag overrides show title and fills in year when missing from the service.

- Add animeapi-py dependency for cross-platform anime ID resolution
- Add --animeapi option (e.g. mal:12345, anilist:98765, defaults to MAL)
- Add --enrich flag to override title/year from external sources
- Remove --tmdb-name and --tmdb-year in favor of unified --enrich
- Update REST API params and docs to match
2026-02-28 12:51:14 -07:00
Andy 08269bef60 feat: update unshackle version to 4.0.0 2026-02-26 22:39:25 -07:00
Andy 798ce95042 fix(dependencies): update pyplayready version to 0.8.3 and adjust dependencies 2026-02-26 14:12:38 -07:00
Andy b11b23e529 chore(release): bump version to 3.1.0 2026-02-23 16:27:05 -07:00
Andy bf9087a1ce chore(release): bump version to 3.0.0
BREAKING CHANGE: PlayReady users without explicit playready_devices no longer get access to all devices by default.

Key changes:
- feat(drm): add MonaLisa DRM support to core infrastructure
- feat(cdm): add remote PlayReady CDM support via pyplayready RemoteCdm
- feat(serve): add PlayReady CDM support alongside Widevine
- feat(gluetun): Gluetun VPN integration and Windscribe support
- feat(audio): codec lists and split muxing
- feat(tracks): prioritize Atmos audio tracks over higher bitrate non-Atmos
- feat(video): detect interlaced scan type from MPD manifests
- feat(cdm): normalize CDM detection for local and remote implementations
- fix(serve)!: make PlayReady users config consistently a mapping
- 50+ additional bug fixes across HLS/DASH, proxies, subtitles, and more
2026-02-15 13:04:42 -07:00
Andy 62aa85c666 chore(release): bump version to 2.4.0 2026-02-07 14:52:54 -07:00
Andy 3fcad1aa01 feat(drm): add MonaLisa DRM support to core infrastructure
- Add MonaLisaCDM class wrapping wasmtime for key extraction
- Add MonaLisa DRM class with decrypt_segment() for per-segment decryption
- Display Content ID and keys in download output (matching Widevine/PlayReady)
- Add wasmtime dependency for WASM module execution
2026-01-31 22:05:44 -07:00
Andy 4b30090d87 feat(gluetun): improve VPN connection display and Windscribe support 2026-01-24 13:36:04 -07:00
Andy e77f000494 feat: Gluetun VPN integration and remote service enhancements
Major features:
- Native Docker-based Gluetun VPN proxy provider with multi-provider support
  (NordVPN, Windscribe, Surfshark, ExpressVPN, and 50+ more)
- Stateless remote service architecture with local session caching
- Client-side authentication for remote services (browser, 2FA, OAuth support)

Key changes:
- core/proxies/windscribevpn.py: Enhanced proxy handling
- core/crypto.py: Cryptographic utilities
- docs/VPN_PROXY_SETUP.md: Comprehensive VPN/proxy documentation
2026-01-24 11:23:13 -07:00
Andy af49560345 Merge branch 'main' into dev 2026-01-22 14:47:25 -07:00
Sp5rky 73a4bf8764 Merge pull request #53 from CodeName393/main
Merging after code review - fixes binary path handling
2026-01-22 13:53:23 -07:00
stabbedbybrick a60247f485 add WVTT (WebVTT in MP4) subtitle converter for subby (#61)
* chore(deps): update subby to 0.3.27

* fix(subs): add WVTT (WebVTT in MP4) subtitle converter for subby

- Use WVTTConverter if self.codec == Subtitle.Codec.fVTT
- Read or save files as Paths instead of strings to avoid AttributeErrors
- Add CommonIssuesFixer when stripping SDH
- Set Subtitle.Codec.fVTT to use subby if conversion_method == "auto"
- Silence the underlying srt library logging used by subby to avoid info messages being printed to console
2026-01-22 12:41:41 -07:00
Andy eaad0e3cc2 chore(release): bump version to 2.3.1 2026-01-22 00:39:41 +00:00
Andy 71bbb27d47 fix(deps): upgrade vulnerable dependencies for security alerts
- urllib3: 2.5.0 -> 2.6.3 (CVE-2025-66418, CVE-2025-66471, CVE-2026-21441)
- aiohttp: 3.13.2 -> 3.13.3 (8 CVEs including CVE-2025-69223, CVE-2025-69227)
- fonttools: 4.60.1 -> 4.61.1 (CVE-2025-66034)
- filelock: 3.19.1 -> 3.20.3 (CVE-2025-68146, CVE-2026-22701)
- virtualenv: 20.34.0 -> 20.36.1 (CVE-2026-22702)
2026-01-21 15:09:51 +00:00
Andy abd8fc2eb9 chore(release): bump version to 2.3.0 2026-01-18 19:03:14 +00:00
Andy d1d3daf750 chore(release): bump version to 2.2.0 2026-01-15 03:07:18 +00:00
Andy dc9823cd28 chore(release): bump version to 2.1.0 2025-11-27 23:35:56 +00:00