unidl 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. unidl-2.0.0/MANIFEST.in +16 -0
  2. unidl-2.0.0/PKG-INFO +271 -0
  3. unidl-2.0.0/README.md +243 -0
  4. unidl-2.0.0/docs/CONTRIBUTING.md +105 -0
  5. unidl-2.0.0/docs/README.md +53 -0
  6. unidl-2.0.0/docs/SECURITY.md +61 -0
  7. unidl-2.0.0/docs/architecture.md +315 -0
  8. unidl-2.0.0/docs/audio.md +111 -0
  9. unidl-2.0.0/docs/availability.md +71 -0
  10. unidl-2.0.0/docs/batch.md +107 -0
  11. unidl-2.0.0/docs/cancelling.md +90 -0
  12. unidl-2.0.0/docs/chapters.md +104 -0
  13. unidl-2.0.0/docs/configuration.md +108 -0
  14. unidl-2.0.0/docs/downloader/audio-vivid.md +72 -0
  15. unidl-2.0.0/docs/downloader/ism-smooth-streaming.md +43 -0
  16. unidl-2.0.0/docs/downloader/json-manifest-format.md +243 -0
  17. unidl-2.0.0/docs/downloader/live-pipe-mux.md +26 -0
  18. unidl-2.0.0/docs/downloader/mp3-audio-format.md +379 -0
  19. unidl-2.0.0/docs/downloader-integration.md +100 -0
  20. unidl-2.0.0/docs/drm.md +542 -0
  21. unidl-2.0.0/docs/external-helpers.md +33 -0
  22. unidl-2.0.0/docs/i18n.md +105 -0
  23. unidl-2.0.0/docs/interface.md +210 -0
  24. unidl-2.0.0/docs/key-vault.md +371 -0
  25. unidl-2.0.0/docs/live.md +104 -0
  26. unidl-2.0.0/docs/partner-authorization.md +333 -0
  27. unidl-2.0.0/docs/playback-lifecycle.md +27 -0
  28. unidl-2.0.0/docs/publishing.md +83 -0
  29. unidl-2.0.0/docs/requirements.md +108 -0
  30. unidl-2.0.0/docs/settings.md +336 -0
  31. unidl-2.0.0/docs/testing.md +92 -0
  32. unidl-2.0.0/docs/troubleshooting.md +160 -0
  33. unidl-2.0.0/docs/ui-design.md +408 -0
  34. unidl-2.0.0/docs/unidl.example.yaml +21 -0
  35. unidl-2.0.0/docs/writing-a-service.md +401 -0
  36. unidl-2.0.0/pyproject.toml +71 -0
  37. unidl-2.0.0/requirements-dev.txt +7 -0
  38. unidl-2.0.0/requirements.txt +17 -0
  39. unidl-2.0.0/setup.cfg +4 -0
  40. unidl-2.0.0/src/unidl/__init__.py +3 -0
  41. unidl-2.0.0/src/unidl/__main__.py +180 -0
  42. unidl-2.0.0/src/unidl/core/__init__.py +70 -0
  43. unidl-2.0.0/src/unidl/core/appearance.py +199 -0
  44. unidl-2.0.0/src/unidl/core/brands.py +400 -0
  45. unidl-2.0.0/src/unidl/core/cache.py +231 -0
  46. unidl-2.0.0/src/unidl/core/cdm.py +123 -0
  47. unidl-2.0.0/src/unidl/core/cdmrules.py +354 -0
  48. unidl-2.0.0/src/unidl/core/chapters.py +207 -0
  49. unidl-2.0.0/src/unidl/core/config.py +1001 -0
  50. unidl-2.0.0/src/unidl/core/cookies.py +494 -0
  51. unidl-2.0.0/src/unidl/core/credentials.py +92 -0
  52. unidl-2.0.0/src/unidl/core/delivery.py +455 -0
  53. unidl-2.0.0/src/unidl/core/devreload.py +279 -0
  54. unidl-2.0.0/src/unidl/core/drm.py +928 -0
  55. unidl-2.0.0/src/unidl/core/engine.py +2971 -0
  56. unidl-2.0.0/src/unidl/core/exports.py +578 -0
  57. unidl-2.0.0/src/unidl/core/flow.py +677 -0
  58. unidl-2.0.0/src/unidl/core/helpers.py +562 -0
  59. unidl-2.0.0/src/unidl/core/i18n.py +475 -0
  60. unidl-2.0.0/src/unidl/core/justwatch.py +699 -0
  61. unidl-2.0.0/src/unidl/core/lyrics.py +310 -0
  62. unidl-2.0.0/src/unidl/core/monalisa.py +160 -0
  63. unidl-2.0.0/src/unidl/core/naming.py +564 -0
  64. unidl-2.0.0/src/unidl/core/partner.py +180 -0
  65. unidl-2.0.0/src/unidl/core/playback.py +323 -0
  66. unidl-2.0.0/src/unidl/core/playready.py +335 -0
  67. unidl-2.0.0/src/unidl/core/proxy.py +393 -0
  68. unidl-2.0.0/src/unidl/core/proxy_express.py +496 -0
  69. unidl-2.0.0/src/unidl/core/pssh.py +328 -0
  70. unidl-2.0.0/src/unidl/core/qr.py +438 -0
  71. unidl-2.0.0/src/unidl/core/ranges.py +277 -0
  72. unidl-2.0.0/src/unidl/core/readiness.py +242 -0
  73. unidl-2.0.0/src/unidl/core/redact.py +112 -0
  74. unidl-2.0.0/src/unidl/core/remotecdm.py +425 -0
  75. unidl-2.0.0/src/unidl/core/secureio.py +219 -0
  76. unidl-2.0.0/src/unidl/core/service.py +1623 -0
  77. unidl-2.0.0/src/unidl/core/settings.py +1409 -0
  78. unidl-2.0.0/src/unidl/core/template.py +136 -0
  79. unidl-2.0.0/src/unidl/core/titles.py +125 -0
  80. unidl-2.0.0/src/unidl/core/vault.py +693 -0
  81. unidl-2.0.0/src/unidl/core/vaults.py +1178 -0
  82. unidl-2.0.0/src/unidl/downloader/__init__.py +20 -0
  83. unidl-2.0.0/src/unidl/downloader/api.py +454 -0
  84. unidl-2.0.0/src/unidl/downloader/applemusic_decrypt.py +217 -0
  85. unidl-2.0.0/src/unidl/downloader/audio.py +294 -0
  86. unidl-2.0.0/src/unidl/downloader/avs3.py +965 -0
  87. unidl-2.0.0/src/unidl/downloader/backend.py +827 -0
  88. unidl-2.0.0/src/unidl/downloader/bbts.py +498 -0
  89. unidl-2.0.0/src/unidl/downloader/cenc_fragment.py +1349 -0
  90. unidl-2.0.0/src/unidl/downloader/chapters.py +89 -0
  91. unidl-2.0.0/src/unidl/downloader/cli.py +9008 -0
  92. unidl-2.0.0/src/unidl/downloader/console.py +202 -0
  93. unidl-2.0.0/src/unidl/downloader/deezer.py +202 -0
  94. unidl-2.0.0/src/unidl/downloader/detect.py +45 -0
  95. unidl-2.0.0/src/unidl/downloader/display.py +168 -0
  96. unidl-2.0.0/src/unidl/downloader/downloader.py +3982 -0
  97. unidl-2.0.0/src/unidl/downloader/embedding.py +336 -0
  98. unidl-2.0.0/src/unidl/downloader/http_client.py +875 -0
  99. unidl-2.0.0/src/unidl/downloader/ism_live.py +29 -0
  100. unidl-2.0.0/src/unidl/downloader/live.py +2356 -0
  101. unidl-2.0.0/src/unidl/downloader/live_rules.py +1325 -0
  102. unidl-2.0.0/src/unidl/downloader/loader.py +306 -0
  103. unidl-2.0.0/src/unidl/downloader/models.py +219 -0
  104. unidl-2.0.0/src/unidl/downloader/parser.py +170 -0
  105. unidl-2.0.0/src/unidl/downloader/parsers/__init__.py +7 -0
  106. unidl-2.0.0/src/unidl/downloader/parsers/dash.py +1224 -0
  107. unidl-2.0.0/src/unidl/downloader/parsers/direct.py +157 -0
  108. unidl-2.0.0/src/unidl/downloader/parsers/hls.py +1081 -0
  109. unidl-2.0.0/src/unidl/downloader/parsers/ism.py +560 -0
  110. unidl-2.0.0/src/unidl/downloader/parsers/ism_init.py +536 -0
  111. unidl-2.0.0/src/unidl/downloader/parsers/json_manifest.py +1153 -0
  112. unidl-2.0.0/src/unidl/downloader/parsers/sabr_ump.py +705 -0
  113. unidl-2.0.0/src/unidl/downloader/postprocess.py +1986 -0
  114. unidl-2.0.0/src/unidl/downloader/qobuz.py +143 -0
  115. unidl-2.0.0/src/unidl/downloader/sabr_ump.py +2604 -0
  116. unidl-2.0.0/src/unidl/downloader/selection.py +471 -0
  117. unidl-2.0.0/src/unidl/downloader/subtitles.py +721 -0
  118. unidl-2.0.0/src/unidl/downloader/utils.py +350 -0
  119. unidl-2.0.0/src/unidl/downloader/vgc.py +114 -0
  120. unidl-2.0.0/src/unidl/downloader/video_range.py +62 -0
  121. unidl-2.0.0/src/unidl/downloader/webm_decrypt.py +534 -0
  122. unidl-2.0.0/src/unidl/downloader/webm_live.py +527 -0
  123. unidl-2.0.0/src/unidl/downloader/youku.py +144 -0
  124. unidl-2.0.0/src/unidl/locales/en.json +998 -0
  125. unidl-2.0.0/src/unidl/locales/es.json +998 -0
  126. unidl-2.0.0/src/unidl/locales/fr.json +998 -0
  127. unidl-2.0.0/src/unidl/locales/pt.json +998 -0
  128. unidl-2.0.0/src/unidl/locales/zh-Hans.json +998 -0
  129. unidl-2.0.0/src/unidl/locales/zh-Hant.json +998 -0
  130. unidl-2.0.0/src/unidl/services/__init__.py +17 -0
  131. unidl-2.0.0/src/unidl/services/bbciplayer/__init__.py +402 -0
  132. unidl-2.0.0/src/unidl/services/bbciplayer/api.py +844 -0
  133. unidl-2.0.0/src/unidl/services/example/__init__.py +201 -0
  134. unidl-2.0.0/src/unidl/services/example/api.py +196 -0
  135. unidl-2.0.0/src/unidl/tui/__init__.py +1 -0
  136. unidl-2.0.0/src/unidl/tui/about_screen.py +98 -0
  137. unidl-2.0.0/src/unidl/tui/app.py +841 -0
  138. unidl-2.0.0/src/unidl/tui/askhost.py +1108 -0
  139. unidl-2.0.0/src/unidl/tui/asks.py +1004 -0
  140. unidl-2.0.0/src/unidl/tui/audio.py +494 -0
  141. unidl-2.0.0/src/unidl/tui/banner.py +366 -0
  142. unidl-2.0.0/src/unidl/tui/bidi.py +94 -0
  143. unidl-2.0.0/src/unidl/tui/cdm_rules_screen.py +283 -0
  144. unidl-2.0.0/src/unidl/tui/cdm_screen.py +421 -0
  145. unidl-2.0.0/src/unidl/tui/cells.py +46 -0
  146. unidl-2.0.0/src/unidl/tui/chapters_screen.py +111 -0
  147. unidl-2.0.0/src/unidl/tui/chrome.py +181 -0
  148. unidl-2.0.0/src/unidl/tui/download_screen.py +822 -0
  149. unidl-2.0.0/src/unidl/tui/filterbox.py +69 -0
  150. unidl-2.0.0/src/unidl/tui/flow_screen.py +106 -0
  151. unidl-2.0.0/src/unidl/tui/home.py +1036 -0
  152. unidl-2.0.0/src/unidl/tui/import_screen.py +226 -0
  153. unidl-2.0.0/src/unidl/tui/justwatch_screen.py +814 -0
  154. unidl-2.0.0/src/unidl/tui/justwatch_settings.py +112 -0
  155. unidl-2.0.0/src/unidl/tui/logline.py +201 -0
  156. unidl-2.0.0/src/unidl/tui/logpane.py +338 -0
  157. unidl-2.0.0/src/unidl/tui/lyrics_screen.py +96 -0
  158. unidl-2.0.0/src/unidl/tui/palette.py +69 -0
  159. unidl-2.0.0/src/unidl/tui/proxy_manager.py +764 -0
  160. unidl-2.0.0/src/unidl/tui/qr.py +389 -0
  161. unidl-2.0.0/src/unidl/tui/readiness_screen.py +179 -0
  162. unidl-2.0.0/src/unidl/tui/resource_manager.py +1360 -0
  163. unidl-2.0.0/src/unidl/tui/search.py +785 -0
  164. unidl-2.0.0/src/unidl/tui/service_screen.py +82 -0
  165. unidl-2.0.0/src/unidl/tui/session.py +2833 -0
  166. unidl-2.0.0/src/unidl/tui/settings_group.py +148 -0
  167. unidl-2.0.0/src/unidl/tui/settings_screen.py +740 -0
  168. unidl-2.0.0/src/unidl/tui/storage_manager.py +481 -0
  169. unidl-2.0.0/src/unidl/tui/theme.py +2245 -0
  170. unidl-2.0.0/src/unidl/tui/update_screen.py +67 -0
  171. unidl-2.0.0/src/unidl/tui/vault_screen.py +643 -0
  172. unidl-2.0.0/src/unidl/tui/vault_targets.py +349 -0
  173. unidl-2.0.0/src/unidl.egg-info/PKG-INFO +271 -0
  174. unidl-2.0.0/src/unidl.egg-info/SOURCES.txt +177 -0
  175. unidl-2.0.0/src/unidl.egg-info/dependency_links.txt +1 -0
  176. unidl-2.0.0/src/unidl.egg-info/entry_points.txt +2 -0
  177. unidl-2.0.0/src/unidl.egg-info/requires.txt +23 -0
  178. unidl-2.0.0/src/unidl.egg-info/top_level.txt +1 -0
  179. unidl-2.0.0/tests/test_framework.py +71 -0
@@ -0,0 +1,16 @@
1
+ # Public source archives include the user-facing documentation and install
2
+ # requirement lists, but never local runtime state or private media devices.
3
+ include README.md pyproject.toml requirements.txt requirements-dev.txt
4
+ recursive-include docs *.md *.yaml
5
+ recursive-exclude cdm *
6
+ recursive-exclude helpers *
7
+ recursive-exclude db *
8
+ recursive-exclude cookies *
9
+ recursive-exclude tokens *
10
+ recursive-exclude downloads *
11
+ recursive-exclude download_commands *
12
+ recursive-exclude exports *
13
+ recursive-exclude logs *
14
+ recursive-exclude cache *
15
+ global-exclude *.wvd *.prd *.mld *.sqlite *.sqlite3 *.db-shm *.db-wal *.db-journal
16
+ global-exclude *.pem
unidl-2.0.0/PKG-INFO ADDED
@@ -0,0 +1,271 @@
1
+ Metadata-Version: 2.4
2
+ Name: unidl
3
+ Version: 2.0.0
4
+ Summary: Integrated TUI for browsing streaming services and downloading selected media.
5
+ Author: unidl contributors
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: textual>=0.80
9
+ Requires-Dist: pywidevine>=1.8
10
+ Requires-Dist: pyplayready>=0.8.5
11
+ Requires-Dist: wasmtime>=20
12
+ Requires-Dist: requests[socks]>=2.31
13
+ Requires-Dist: qrcode[pil]>=7.4
14
+ Requires-Dist: textual-image[textual]>=0.13.2; python_version >= "3.12"
15
+ Requires-Dist: httpx[http2]>=0.27
16
+ Requires-Dist: cryptography>=42
17
+ Requires-Dist: PyYAML>=6.0
18
+ Requires-Dist: geonamescache>=2.0
19
+ Requires-Dist: pycryptodome>=3.20
20
+ Requires-Dist: python-bidi>=0.6
21
+ Requires-Dist: arabic-reshaper>=3.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: build>=1.2; extra == "dev"
24
+ Requires-Dist: pytest>=8.0; extra == "dev"
25
+ Requires-Dist: ruff>=0.6; extra == "dev"
26
+ Requires-Dist: setuptools>=68; extra == "dev"
27
+ Requires-Dist: wheel>=0.43; extra == "dev"
28
+
29
+ # UniDL
30
+
31
+ UniDL is a terminal-first media browser and native downloader. It connects
32
+ streaming services to one consistent workflow: find a title or channel, sign in
33
+ when needed, inspect the available media, choose the tracks you want, resolve
34
+ DRM keys through the service contract, and download or record the result.
35
+
36
+ The project is designed around three clear layers:
37
+
38
+ - **Services** handle login, catalogues, search, manifests, service settings and
39
+ service-owned licence requests.
40
+ - **Core/TUI** handles navigation, settings, credentials, CDM/vault selection,
41
+ track selection, progress, logs and the interactive workflow.
42
+ - **Native downloader** parses manifests, downloads segments, decrypts media,
43
+ writes subtitles and chapters, and muxes the final output in-process.
44
+
45
+ ## Features
46
+
47
+ ### Interactive TUI
48
+
49
+ - Keyboard and mouse navigation with predictable Back, Cancel and Quit behavior.
50
+ - Service home screens with URL, search, live, library and login entry points.
51
+ - Search results that continue through seasons and episodes without leaving the
52
+ service flow.
53
+ - JustWatch title and availability search with configurable regions and provider
54
+ mapping.
55
+ - Responsive log and progress panels, selectable text, copy actions, light and
56
+ dark themes, and localized interface strings.
57
+ - Explicit development reload for service packages without silently watching
58
+ files in the background.
59
+
60
+ ### VOD
61
+
62
+ - DASH/MPD, HLS, ISM/Smooth Streaming, JSON manifests and direct media URLs.
63
+ - Multiple video, audio and subtitle tracks with independent output selection.
64
+ - Resolution, codec, dynamic-range, language, channel-layout and subtitle
65
+ controls, including HDR/Dolby Vision and audio codec labels where available.
66
+ - Multi-manifest playback plans for services that expose separate ladders.
67
+ - Optional chapter retrieval and chapter embedding into the final container.
68
+ - Safe output naming with title, season/episode, resolution, audio and codec
69
+ tags based on the tracks the user actually downloads.
70
+ - Resumable segment downloads, cache-aware retries, post-processing and muxing.
71
+
72
+ ### Live recording
73
+
74
+ - Live HLS, DASH/fMP4 and other refreshable playlists.
75
+ - Track selection before recording, replay/DVR window inspection, recording
76
+ from the live edge or a chosen offset, and finite or unlimited duration.
77
+ - `00:00:00` means no duration limit; Stop, Back or Esc ends an active recording.
78
+ - Real-time merge and pipe-mux modes where the source and container support them.
79
+ - Rotating live keys with service-provided init data and an interactive fallback
80
+ for a genuinely new KID.
81
+ - Progress, replay-window information, segment counts, estimated size and
82
+ cancellation state in the TUI.
83
+
84
+ ### DRM, vaults and credentials
85
+
86
+ - Local Widevine, PlayReady and MonaLisa device contracts with strict system
87
+ matching.
88
+ - Optional remote CDM endpoints for systems that support remote challenge and
89
+ licence parsing.
90
+ - Local SQLite key vaults and compatible remote key vaults, with multi-vault
91
+ read/write policies, service scoping and manual KID:key entry.
92
+ - Service-local licence transport: the shared DRM layer creates challenges and
93
+ parses responses, while each service owns its endpoint, headers and request
94
+ format.
95
+ - Independent credential slots, cookie profiles, token stores and refresh
96
+ lifecycles per service and login method.
97
+
98
+ ### Audio and metadata
99
+
100
+ - Audio-only services and audio tracks use a dedicated presentation and naming
101
+ path.
102
+ - MP3 export with ID3v2 metadata, cover art, artist/album/title fields and
103
+ chapter-aware post-processing.
104
+ - Clear audio formats remain available when the source already matches the
105
+ requested container; other sources are converted through FFmpeg.
106
+
107
+ ### Helpers, proxy and storage
108
+
109
+ - Declared service helpers resolved from configuration, `PATH`, project helper
110
+ folders or package resources; no arbitrary filesystem scan.
111
+ - HTTP/HTTPS and SOCKS proxy support plus provider-specific VPN integrations
112
+ when configured by the user.
113
+ - Project-relative paths for tokens, cookies, CDMs, vaults, caches, commands,
114
+ subtitles and finished media.
115
+ - JSON command/export artifacts for automation and reproducible downloads.
116
+
117
+ ## Install
118
+
119
+ The minimum supported runtime is **Python 3.11** on Windows, macOS or Linux.
120
+ Use a 64-bit Python build and install FFmpeg (including `ffprobe`) for the full
121
+ download, conversion and muxing workflow. The Python dependency list is kept in
122
+ [`requirements.txt`](requirements.txt); development checks are in
123
+ [`requirements-dev.txt`](requirements-dev.txt).
124
+
125
+ From a published PyPI release:
126
+
127
+ ```console
128
+ python3 -m venv .venv
129
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
130
+ python -m pip install --upgrade pip
131
+ python -m pip install unidl
132
+ ```
133
+
134
+ The project is also installable directly from a source checkout:
135
+
136
+ ```console
137
+ python3 -m venv .venv
138
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
139
+ python -m pip install --upgrade pip
140
+ python -m pip install .
141
+ ```
142
+
143
+ For development and tests:
144
+
145
+ ```console
146
+ python -m pip install -e '.[dev]'
147
+ ```
148
+
149
+ The project also works with `uv`:
150
+
151
+ ```console
152
+ uv sync --extra dev
153
+ ```
154
+
155
+ `pip install unidl` is the shortest installation after a release has been
156
+ published to PyPI. Until then, use the source-checkout command above.
157
+
158
+ Place local CDM files under the configured `paths.cdm` tree and keep their
159
+ private material outside version control. Helpers, cookies, tokens and vault
160
+ databases are runtime data and should be stored in the configured project
161
+ directories.
162
+
163
+ See [Requirements and installation](docs/requirements.md) for external tools,
164
+ platform notes and a complete preflight checklist.
165
+
166
+ ## Use UniDL
167
+
168
+ Launch the installed TUI:
169
+
170
+ ```console
171
+ unidl
172
+ ```
173
+
174
+ `python -m unidl` is equivalent. A packaged install uses `unidl.yaml` from the
175
+ current directory when it exists; otherwise it starts with the safe built-in
176
+ path defaults. Pass an explicit configuration whenever the file lives elsewhere:
177
+
178
+ ```console
179
+ unidl --config ./unidl.yaml
180
+ ```
181
+
182
+ Useful read-only diagnostics:
183
+
184
+ ```console
185
+ unidl --config ./unidl.yaml --help
186
+ unidl --config ./unidl.yaml services
187
+ unidl --config ./unidl.yaml cdm --check
188
+ unidl --config ./unidl.yaml keys <kid> --service <service-id>
189
+ ```
190
+
191
+ The native downloader can also consume an exported JSON manifest or a direct
192
+ source URL:
193
+
194
+ ```console
195
+ unidl list <manifest-or-json>
196
+ unidl download <manifest-or-url> --save-name "Example.Title"
197
+ ```
198
+
199
+ In the TUI, choose a service, search or open a URL, select the title and tracks,
200
+ then choose whether to download now or save a command/export. A service's own
201
+ settings control provider API/profile choices; the shared track settings control
202
+ the final output tracks only. See [docs/settings.md](docs/settings.md).
203
+
204
+ For live channels, choose the tracks first, then choose recording, replay/DVR
205
+ behavior and duration. Leave the duration at `00:00:00` for an unlimited
206
+ recording and use Stop/Back/Esc to finish it.
207
+
208
+ ## Configuration and data
209
+
210
+ `unidl.yaml` is the static configuration surface. Relative paths are resolved
211
+ from the directory containing that file, so a checkout can be moved safely.
212
+ Interactive preferences are stored in `settings.json` under `paths.home`.
213
+ Credentials, cookies, tokens, CDMs, vaults, logs, command files and exports are
214
+ never required to be committed. Use a private override file for secrets:
215
+
216
+ ```console
217
+ python -m unidl --config ./unidl.private.yaml
218
+ ```
219
+
220
+ The application uses the explicitly selected configuration or the project-root
221
+ configuration when launched from a source checkout.
222
+
223
+ ## Project layout
224
+
225
+ ```text
226
+ src/unidl/core/ contracts, DRM, vaults, storage and flow engine
227
+ src/unidl/tui/ Textual interface and screens
228
+ src/unidl/downloader/ native parsers, transfer, decrypt and mux pipeline
229
+ src/unidl/services/ one package per service
230
+ helpers/ declared helper assets and modules
231
+ cdm/ local device files, kept private
232
+ docs/ architecture, service and downloader documentation
233
+ tests/ offline contract and integration tests
234
+ ```
235
+
236
+ ## Documentation
237
+
238
+ Start with [docs/README.md](docs/README.md). The most useful paths are:
239
+
240
+ - [Architecture](docs/architecture.md) — boundaries and data flow.
241
+ - [Requirements and installation](docs/requirements.md) — supported runtimes,
242
+ package installation and external tools.
243
+ - [Publishing](docs/publishing.md) — PyPI releases, token handling and GitHub
244
+ account switching.
245
+ - [Writing a service](docs/writing-a-service.md) — add a service and integrate
246
+ native Core capabilities.
247
+ - [Native downloader](docs/downloader-integration.md) — supported inputs,
248
+ delivery contracts and progress.
249
+ - [Configuration](docs/configuration.md) and [Settings](docs/settings.md) —
250
+ static configuration versus interactive preferences.
251
+ - [DRM](docs/drm.md) and [Key vault](docs/key-vault.md) — local/remote key
252
+ resolution and service-owned licensing.
253
+ - [Live channels](docs/live.md), [Audio](docs/audio.md) and
254
+ [Chapters](docs/chapters.md) — specialized playback paths.
255
+ - [Testing](docs/testing.md), [Troubleshooting](docs/troubleshooting.md) and
256
+ [Security](docs/SECURITY.md) — verification and safe operation.
257
+
258
+ ## Development checks
259
+
260
+ ```console
261
+ python -m pytest -q
262
+ python -m ruff check src tests
263
+ python -m compileall -q src
264
+ ```
265
+
266
+ When changing a service, run its offline checks and a real playback check with
267
+ authorized account, region and device data. Never include credentials, cookies,
268
+ tokens, CDM private material, vault keys or signed URLs in commits or bug
269
+ reports.
270
+
271
+ Copyright © 2026 Chris20
unidl-2.0.0/README.md ADDED
@@ -0,0 +1,243 @@
1
+ # UniDL
2
+
3
+ UniDL is a terminal-first media browser and native downloader. It connects
4
+ streaming services to one consistent workflow: find a title or channel, sign in
5
+ when needed, inspect the available media, choose the tracks you want, resolve
6
+ DRM keys through the service contract, and download or record the result.
7
+
8
+ The project is designed around three clear layers:
9
+
10
+ - **Services** handle login, catalogues, search, manifests, service settings and
11
+ service-owned licence requests.
12
+ - **Core/TUI** handles navigation, settings, credentials, CDM/vault selection,
13
+ track selection, progress, logs and the interactive workflow.
14
+ - **Native downloader** parses manifests, downloads segments, decrypts media,
15
+ writes subtitles and chapters, and muxes the final output in-process.
16
+
17
+ ## Features
18
+
19
+ ### Interactive TUI
20
+
21
+ - Keyboard and mouse navigation with predictable Back, Cancel and Quit behavior.
22
+ - Service home screens with URL, search, live, library and login entry points.
23
+ - Search results that continue through seasons and episodes without leaving the
24
+ service flow.
25
+ - JustWatch title and availability search with configurable regions and provider
26
+ mapping.
27
+ - Responsive log and progress panels, selectable text, copy actions, light and
28
+ dark themes, and localized interface strings.
29
+ - Explicit development reload for service packages without silently watching
30
+ files in the background.
31
+
32
+ ### VOD
33
+
34
+ - DASH/MPD, HLS, ISM/Smooth Streaming, JSON manifests and direct media URLs.
35
+ - Multiple video, audio and subtitle tracks with independent output selection.
36
+ - Resolution, codec, dynamic-range, language, channel-layout and subtitle
37
+ controls, including HDR/Dolby Vision and audio codec labels where available.
38
+ - Multi-manifest playback plans for services that expose separate ladders.
39
+ - Optional chapter retrieval and chapter embedding into the final container.
40
+ - Safe output naming with title, season/episode, resolution, audio and codec
41
+ tags based on the tracks the user actually downloads.
42
+ - Resumable segment downloads, cache-aware retries, post-processing and muxing.
43
+
44
+ ### Live recording
45
+
46
+ - Live HLS, DASH/fMP4 and other refreshable playlists.
47
+ - Track selection before recording, replay/DVR window inspection, recording
48
+ from the live edge or a chosen offset, and finite or unlimited duration.
49
+ - `00:00:00` means no duration limit; Stop, Back or Esc ends an active recording.
50
+ - Real-time merge and pipe-mux modes where the source and container support them.
51
+ - Rotating live keys with service-provided init data and an interactive fallback
52
+ for a genuinely new KID.
53
+ - Progress, replay-window information, segment counts, estimated size and
54
+ cancellation state in the TUI.
55
+
56
+ ### DRM, vaults and credentials
57
+
58
+ - Local Widevine, PlayReady and MonaLisa device contracts with strict system
59
+ matching.
60
+ - Optional remote CDM endpoints for systems that support remote challenge and
61
+ licence parsing.
62
+ - Local SQLite key vaults and compatible remote key vaults, with multi-vault
63
+ read/write policies, service scoping and manual KID:key entry.
64
+ - Service-local licence transport: the shared DRM layer creates challenges and
65
+ parses responses, while each service owns its endpoint, headers and request
66
+ format.
67
+ - Independent credential slots, cookie profiles, token stores and refresh
68
+ lifecycles per service and login method.
69
+
70
+ ### Audio and metadata
71
+
72
+ - Audio-only services and audio tracks use a dedicated presentation and naming
73
+ path.
74
+ - MP3 export with ID3v2 metadata, cover art, artist/album/title fields and
75
+ chapter-aware post-processing.
76
+ - Clear audio formats remain available when the source already matches the
77
+ requested container; other sources are converted through FFmpeg.
78
+
79
+ ### Helpers, proxy and storage
80
+
81
+ - Declared service helpers resolved from configuration, `PATH`, project helper
82
+ folders or package resources; no arbitrary filesystem scan.
83
+ - HTTP/HTTPS and SOCKS proxy support plus provider-specific VPN integrations
84
+ when configured by the user.
85
+ - Project-relative paths for tokens, cookies, CDMs, vaults, caches, commands,
86
+ subtitles and finished media.
87
+ - JSON command/export artifacts for automation and reproducible downloads.
88
+
89
+ ## Install
90
+
91
+ The minimum supported runtime is **Python 3.11** on Windows, macOS or Linux.
92
+ Use a 64-bit Python build and install FFmpeg (including `ffprobe`) for the full
93
+ download, conversion and muxing workflow. The Python dependency list is kept in
94
+ [`requirements.txt`](requirements.txt); development checks are in
95
+ [`requirements-dev.txt`](requirements-dev.txt).
96
+
97
+ From a published PyPI release:
98
+
99
+ ```console
100
+ python3 -m venv .venv
101
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
102
+ python -m pip install --upgrade pip
103
+ python -m pip install unidl
104
+ ```
105
+
106
+ The project is also installable directly from a source checkout:
107
+
108
+ ```console
109
+ python3 -m venv .venv
110
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
111
+ python -m pip install --upgrade pip
112
+ python -m pip install .
113
+ ```
114
+
115
+ For development and tests:
116
+
117
+ ```console
118
+ python -m pip install -e '.[dev]'
119
+ ```
120
+
121
+ The project also works with `uv`:
122
+
123
+ ```console
124
+ uv sync --extra dev
125
+ ```
126
+
127
+ `pip install unidl` is the shortest installation after a release has been
128
+ published to PyPI. Until then, use the source-checkout command above.
129
+
130
+ Place local CDM files under the configured `paths.cdm` tree and keep their
131
+ private material outside version control. Helpers, cookies, tokens and vault
132
+ databases are runtime data and should be stored in the configured project
133
+ directories.
134
+
135
+ See [Requirements and installation](docs/requirements.md) for external tools,
136
+ platform notes and a complete preflight checklist.
137
+
138
+ ## Use UniDL
139
+
140
+ Launch the installed TUI:
141
+
142
+ ```console
143
+ unidl
144
+ ```
145
+
146
+ `python -m unidl` is equivalent. A packaged install uses `unidl.yaml` from the
147
+ current directory when it exists; otherwise it starts with the safe built-in
148
+ path defaults. Pass an explicit configuration whenever the file lives elsewhere:
149
+
150
+ ```console
151
+ unidl --config ./unidl.yaml
152
+ ```
153
+
154
+ Useful read-only diagnostics:
155
+
156
+ ```console
157
+ unidl --config ./unidl.yaml --help
158
+ unidl --config ./unidl.yaml services
159
+ unidl --config ./unidl.yaml cdm --check
160
+ unidl --config ./unidl.yaml keys <kid> --service <service-id>
161
+ ```
162
+
163
+ The native downloader can also consume an exported JSON manifest or a direct
164
+ source URL:
165
+
166
+ ```console
167
+ unidl list <manifest-or-json>
168
+ unidl download <manifest-or-url> --save-name "Example.Title"
169
+ ```
170
+
171
+ In the TUI, choose a service, search or open a URL, select the title and tracks,
172
+ then choose whether to download now or save a command/export. A service's own
173
+ settings control provider API/profile choices; the shared track settings control
174
+ the final output tracks only. See [docs/settings.md](docs/settings.md).
175
+
176
+ For live channels, choose the tracks first, then choose recording, replay/DVR
177
+ behavior and duration. Leave the duration at `00:00:00` for an unlimited
178
+ recording and use Stop/Back/Esc to finish it.
179
+
180
+ ## Configuration and data
181
+
182
+ `unidl.yaml` is the static configuration surface. Relative paths are resolved
183
+ from the directory containing that file, so a checkout can be moved safely.
184
+ Interactive preferences are stored in `settings.json` under `paths.home`.
185
+ Credentials, cookies, tokens, CDMs, vaults, logs, command files and exports are
186
+ never required to be committed. Use a private override file for secrets:
187
+
188
+ ```console
189
+ python -m unidl --config ./unidl.private.yaml
190
+ ```
191
+
192
+ The application uses the explicitly selected configuration or the project-root
193
+ configuration when launched from a source checkout.
194
+
195
+ ## Project layout
196
+
197
+ ```text
198
+ src/unidl/core/ contracts, DRM, vaults, storage and flow engine
199
+ src/unidl/tui/ Textual interface and screens
200
+ src/unidl/downloader/ native parsers, transfer, decrypt and mux pipeline
201
+ src/unidl/services/ one package per service
202
+ helpers/ declared helper assets and modules
203
+ cdm/ local device files, kept private
204
+ docs/ architecture, service and downloader documentation
205
+ tests/ offline contract and integration tests
206
+ ```
207
+
208
+ ## Documentation
209
+
210
+ Start with [docs/README.md](docs/README.md). The most useful paths are:
211
+
212
+ - [Architecture](docs/architecture.md) — boundaries and data flow.
213
+ - [Requirements and installation](docs/requirements.md) — supported runtimes,
214
+ package installation and external tools.
215
+ - [Publishing](docs/publishing.md) — PyPI releases, token handling and GitHub
216
+ account switching.
217
+ - [Writing a service](docs/writing-a-service.md) — add a service and integrate
218
+ native Core capabilities.
219
+ - [Native downloader](docs/downloader-integration.md) — supported inputs,
220
+ delivery contracts and progress.
221
+ - [Configuration](docs/configuration.md) and [Settings](docs/settings.md) —
222
+ static configuration versus interactive preferences.
223
+ - [DRM](docs/drm.md) and [Key vault](docs/key-vault.md) — local/remote key
224
+ resolution and service-owned licensing.
225
+ - [Live channels](docs/live.md), [Audio](docs/audio.md) and
226
+ [Chapters](docs/chapters.md) — specialized playback paths.
227
+ - [Testing](docs/testing.md), [Troubleshooting](docs/troubleshooting.md) and
228
+ [Security](docs/SECURITY.md) — verification and safe operation.
229
+
230
+ ## Development checks
231
+
232
+ ```console
233
+ python -m pytest -q
234
+ python -m ruff check src tests
235
+ python -m compileall -q src
236
+ ```
237
+
238
+ When changing a service, run its offline checks and a real playback check with
239
+ authorized account, region and device data. Never include credentials, cookies,
240
+ tokens, CDM private material, vault keys or signed URLs in commits or bug
241
+ reports.
242
+
243
+ Copyright © 2026 Chris20
@@ -0,0 +1,105 @@
1
+ # Contributing to unidl
2
+
3
+ UniDL services are the account, catalogue and key-acquisition layer; the native
4
+ delivery core is part of the same application. Keep that boundary intact:
5
+ services may log in, discover playback and acquire keys; the native core owns
6
+ manifest parsing, track selection, download, decrypt, subtitles and muxing.
7
+
8
+ ## Before changing code
9
+
10
+ 1. Create a virtual environment and install UniDL plus the development extras
11
+ as described in [docs/testing.md](testing.md).
12
+ 2. Read [docs/architecture.md](architecture.md) and, for service work,
13
+ [docs/writing-a-service.md](writing-a-service.md).
14
+ 3. Keep credentials, cookies, tokens, device files, content keys, command
15
+ exports and debug logs out of version control. The visible project-local
16
+ runtime layout is supported, but its paths must remain ignored.
17
+
18
+ ## Project rules
19
+
20
+ - Service code never imports Textual, calls `print()` or calls `input()`. It
21
+ yields Flow asks and returns a `Playback`.
22
+ - Put HTTP clients and response parsing in the service's `api.py`; keep the flow
23
+ in `__init__.py`.
24
+ - Add each provider as one native package under `src/unidl/services/` and
25
+ register it in `src/unidl/services/__init__.py`; follow
26
+ [writing-a-service.md](writing-a-service.md).
27
+ - Do not duplicate manifest parsing, selection or download logic from the native delivery core.
28
+ - Do not hardcode credentials, local absolute paths or helper locations.
29
+ - Declare settings, credentials, helpers and `DRM_SYSTEMS`; do not make the UI
30
+ infer them from service internals.
31
+ - Shared Track output selection controls only what the native delivery core downloads and muxes.
32
+ Never reuse its quality/codec/range/audio/subtitle settings as licence-track
33
+ policy; configurable licence tracks/profiles/PSSH seeds require a separate
34
+ setting in that service's own section. The app-wide post-selection licence
35
+ compatibility mode is implemented by Core and is not permission for service
36
+ code to read those shared settings.
37
+ - Read and write session state only through `ctx.tokens`, which is rooted at this
38
+ service's own folder under `paths.tokens`. Name the file, never the directory:
39
+ path separators, absolute paths and `..` are rejected by `TokenStore`.
40
+ - If a service's file is renamed, move it rather than copy it: leaving the old
41
+ name behind leaves a session that signing out does not remove and the next read
42
+ adopts again.
43
+ - Keep narrative documentation free of fixed service totals. The porting record
44
+ may quote a measured snapshot; nothing else should.
45
+
46
+ ## Documentation with code changes
47
+
48
+ Update the matching reference in the same change:
49
+
50
+ - a config key: `docs/configuration.md` and `unidl.example.yaml`
51
+ - a global or track setting: `docs/settings.md`
52
+ - a Flow ask or service hook: `docs/writing-a-service.md` and the reference
53
+ service comments
54
+ - a screen, binding or snapshot target: `docs/interface.md` and
55
+ `docs/ui-design.md`
56
+ - DRM or vault behaviour: `docs/drm.md` or `docs/key-vault.md`
57
+ - a new verification command or fixture: `docs/testing.md`
58
+
59
+ Link new user-facing documents from `README.md` and use relative Markdown links
60
+ inside the repository.
61
+
62
+ ## Verification
63
+
64
+ Run `ruff` and `compileall`, then the smallest relevant offline checks. Run live
65
+ checks only when you have the required account, region, device and authority;
66
+ state exactly which commands ran and which were not run. Never paste live
67
+ credentials, full content keys, CDM secrets or unredacted debug logs into a bug
68
+ report or review.
69
+
70
+ For a service port, exercise every declared entry point against the real
71
+ service. Importing successfully is not proof that login, catalogue parsing,
72
+ licensing or the current third-party schema works.
73
+
74
+ ## Reloading service code during development
75
+
76
+ unidl does not watch source files. To adopt an edit without quitting, turn on
77
+ global **Debug mode**, return to the bare Home screen, highlight one service (or
78
+ filter to exactly one), and press `ctrl+r`. The action compiles the selected
79
+ service's loaded source first, reloads its multi-file package in dependency
80
+ order, and also reloads native service packages that share those modules. Core,
81
+ TUI and unrelated service modules are outside the reload graph.
82
+
83
+ The registry receives new classes, so only the **next** service session uses the
84
+ new code. No active service instance, download, licence exchange, heartbeat or
85
+ playback-session cleanup is patched. Reload is deliberately unavailable while a
86
+ service or overlay is on the screen.
87
+
88
+ This is a development convenience, not a transactional production hot patch.
89
+ A syntax error is rejected before any module is touched and the old registry is
90
+ restored after an import error, but Python cannot fully undo module globals that
91
+ an already-started import changed. Restart unidl after a failed reload, or after
92
+ adding/removing/renaming modules or symbols whose old names may remain in a
93
+ module namespace. Changing a service id also requires a restart.
94
+
95
+ ## Change description
96
+
97
+ Explain:
98
+
99
+ - what user-visible behaviour changed;
100
+ - which contract or bug motivated it;
101
+ - the checks run and their environment (offline, loopback or live);
102
+ - any account, region, helper, CDM or external-service limitation;
103
+ - any migration or configuration step users must take.
104
+
105
+ Use the software only with accounts and content you are authorised to access.
@@ -0,0 +1,53 @@
1
+ # UniDL documentation
2
+
3
+ This directory documents the public UniDL contracts and the workflow for using
4
+ and extending the project.
5
+
6
+ ## Start here
7
+
8
+ - [Architecture](architecture.md) — Core, TUI, services and native delivery.
9
+ - [Writing a service](writing-a-service.md) — package layout, registration,
10
+ Flow asks, playback, DRM, credentials and testing.
11
+ - [Configuration](configuration.md) — YAML, paths, devices and runtime state.
12
+ - [Settings](settings.md) — global, service and output-track preferences.
13
+ - [Requirements and installation](requirements.md) — supported Python versions,
14
+ external tools, package installs and runtime data.
15
+ - [Publishing](publishing.md) — PyPI tokens, trusted publishing and GitHub
16
+ account switching.
17
+ - [Native downloader](downloader-integration.md) — manifest inputs and delivery
18
+ integration.
19
+ - [Testing](testing.md) — offline checks, live checks and regression practice.
20
+
21
+ ## Playback and delivery
22
+
23
+ - [DRM systems](drm.md) — local/remote CDMs, PlayReady PSSH handling and key
24
+ acquisition boundaries.
25
+ - [Key vault](key-vault.md) — local and remote vaults, policies and manual keys.
26
+ - [Live channels](live.md) — recording, DVR/replay, duration and rotation.
27
+ - [Audio-only services](audio.md) — audio presentation, metadata and export.
28
+ - [Chapters](chapters.md) — chapter retrieval and embedding.
29
+ - [Downloader audio formats](downloader/mp3-audio-format.md) and
30
+ [JSON manifests](downloader/json-manifest-format.md).
31
+ - [Live pipe mux](downloader/live-pipe-mux.md), [ISM](downloader/ism-smooth-streaming.md)
32
+ and [Audio Vivid](downloader/audio-vivid.md) for specialized delivery paths.
33
+
34
+ ## Interface and integration
35
+
36
+ - [Interface](interface.md) and [UI design](ui-design.md) — navigation,
37
+ selection, logs, themes and accessibility.
38
+ - [Internationalization](i18n.md) — adding interface locales.
39
+ - [External helpers](external-helpers.md) — declaring and resolving helpers.
40
+ - [Partner authorization](partner-authorization.md) — service-to-service login
41
+ handoffs.
42
+ - [Playback lifecycle](playback-lifecycle.md) — session, heartbeat and cleanup
43
+ rules.
44
+ - [Availability](availability.md) — JustWatch search and regional offers.
45
+ - [Troubleshooting](troubleshooting.md) — common configuration and playback
46
+ issues.
47
+
48
+ ## Contributing
49
+
50
+ Read [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md) before
51
+ opening a change. Keep credentials and device/private material outside commits,
52
+ and update the matching documentation whenever a Core or service contract
53
+ changes.