forked from kenzuya/unshackle
Compare commits
4 Commits
3d384b8e3e
...
2.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc9823cd28 | ||
|
|
6fa3554b70 | ||
|
|
2d4bf140fa | ||
|
|
d0816787ce |
41
CHANGELOG.md
41
CHANGELOG.md
@@ -5,6 +5,47 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [2.1.0] - 2025-11-27
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Per-Track Quality-Based CDM Selection**: Dynamic CDM switching during runtime DRM operations
|
||||||
|
- Enables quality-based CDM selection during runtime DRM switching
|
||||||
|
- Different CDMs can be used for different video quality levels within the same download session
|
||||||
|
- Example: Use Widevine L3 for SD/HD and PlayReady SL3 for 4K content
|
||||||
|
- **Enhanced Track Export**: Improved export functionality with additional metadata
|
||||||
|
- Added URL field to track export for easier identification
|
||||||
|
- Added descriptor information in export output
|
||||||
|
- Keys now exported in hex-formatted strings
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **Dependencies**: Upgraded to latest compatible versions
|
||||||
|
- Updated various dependencies to their latest versions
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Attachment Preservation**: Fixed attachments being dropped during track filtering
|
||||||
|
- Attachments (screenshots, fonts) were being lost when track list was rebuilt
|
||||||
|
- Fixes image files remaining in temp directory after muxing
|
||||||
|
- **DASH BaseURL Resolution**: Added AdaptationSet-level BaseURL support per DASH spec
|
||||||
|
- URL resolution chain now properly follows: MPD → Period → AdaptationSet → Representation
|
||||||
|
- **WindscribeVPN Region Support**: Restricted to supported regions with proper error handling
|
||||||
|
- Added error handling for unsupported regions in get_proxy method
|
||||||
|
- Prevents cryptic errors when using unsupported region codes
|
||||||
|
- **Filename Sanitization**: Fixed space-hyphen-space handling in filenames
|
||||||
|
- Pre-process space-hyphen-space patterns (e.g., "Title - Episode") before other replacements
|
||||||
|
- Made space-hyphen-space handling conditional on scene_naming setting
|
||||||
|
- Addresses PR #44 by fixing the root cause
|
||||||
|
- **CICP Enum Values**: Corrected values to match ITU-T H.273 specification
|
||||||
|
- Added Primaries.Unspecified (value 2) per H.273 spec
|
||||||
|
- Renamed Primaries/Transfer value 0 from Unspecified to Reserved for spec accuracy
|
||||||
|
- Simplified Transfer value 2 from Unspecified_Image to Unspecified
|
||||||
|
- Verified against ITU-T H.273, ISO/IEC 23091-2, H.264/H.265 specs, and FFmpeg enums
|
||||||
|
- **HLS Byte Range Parsing**: Fixed TypeError in range_offset conversion
|
||||||
|
- Converted range_offset to int to prevent TypeError in calculate_byte_range
|
||||||
|
- **pyplayready Compatibility**: Pinned to <0.7 to avoid KID extraction bug
|
||||||
|
|
||||||
## [2.0.0] - 2025-11-10
|
## [2.0.0] - 2025-11-10
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "unshackle"
|
name = "unshackle"
|
||||||
version = "2.0.0"
|
version = "2.1.0"
|
||||||
description = "Modular Movie, TV, and Music Archival Software."
|
description = "Modular Movie, TV, and Music Archival Software."
|
||||||
authors = [{ name = "unshackle team" }]
|
authors = [{ name = "unshackle team" }]
|
||||||
requires-python = ">=3.10,<3.13"
|
requires-python = ">=3.10,<3.13"
|
||||||
|
|||||||
@@ -1368,6 +1368,7 @@ class dl:
|
|||||||
kept_tracks.extend(title.tracks.subtitles)
|
kept_tracks.extend(title.tracks.subtitles)
|
||||||
if keep_chapters:
|
if keep_chapters:
|
||||||
kept_tracks.extend(title.tracks.chapters)
|
kept_tracks.extend(title.tracks.chapters)
|
||||||
|
kept_tracks.extend(title.tracks.attachments)
|
||||||
|
|
||||||
title.tracks = Tracks(kept_tracks)
|
title.tracks = Tracks(kept_tracks)
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "2.0.0"
|
__version__ = "2.1.0"
|
||||||
|
|||||||
@@ -297,8 +297,9 @@ class DASH:
|
|||||||
manifest_base_url = track.url
|
manifest_base_url = track.url
|
||||||
elif not re.match("^https?://", manifest_base_url, re.IGNORECASE):
|
elif not re.match("^https?://", manifest_base_url, re.IGNORECASE):
|
||||||
manifest_base_url = urljoin(track.url, f"./{manifest_base_url}")
|
manifest_base_url = urljoin(track.url, f"./{manifest_base_url}")
|
||||||
period_base_url = urljoin(manifest_base_url, period.findtext("BaseURL"))
|
period_base_url = urljoin(manifest_base_url, period.findtext("BaseURL") or "")
|
||||||
rep_base_url = urljoin(period_base_url, representation.findtext("BaseURL"))
|
adaptation_set_base_url = urljoin(period_base_url, adaptation_set.findtext("BaseURL") or "")
|
||||||
|
rep_base_url = urljoin(adaptation_set_base_url, representation.findtext("BaseURL") or "")
|
||||||
|
|
||||||
period_duration = period.get("duration") or manifest.get("mediaPresentationDuration")
|
period_duration = period.get("duration") or manifest.get("mediaPresentationDuration")
|
||||||
init_data: Optional[bytes] = None
|
init_data: Optional[bytes] = None
|
||||||
|
|||||||
@@ -45,14 +45,15 @@ class WindscribeVPN(Proxy):
|
|||||||
"""
|
"""
|
||||||
Get an HTTPS proxy URI for a WindscribeVPN server.
|
Get an HTTPS proxy URI for a WindscribeVPN server.
|
||||||
|
|
||||||
Note: Windscribe's static OpenVPN credentials only work on US servers.
|
Note: Windscribe's static OpenVPN credentials work reliably on US, AU, and NZ servers.
|
||||||
"""
|
"""
|
||||||
query = query.lower()
|
query = query.lower()
|
||||||
|
supported_regions = {"us", "au", "nz"}
|
||||||
|
|
||||||
if query != "us" and query not in self.server_map:
|
if query not in supported_regions and query not in self.server_map:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Windscribe proxy does not currently support the '{query.upper()}' region. "
|
f"Windscribe proxy does not currently support the '{query.upper()}' region. "
|
||||||
"Only US servers are supported with static OpenVPN credentials. "
|
f"Supported regions with reliable credentials: {', '.join(sorted(supported_regions))}. "
|
||||||
)
|
)
|
||||||
|
|
||||||
if query in self.server_map:
|
if query in self.server_map:
|
||||||
@@ -66,6 +67,7 @@ class WindscribeVPN(Proxy):
|
|||||||
if not hostname:
|
if not hostname:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
hostname = hostname.split(':')[0]
|
||||||
return f"https://{self.username}:{self.password}@{hostname}:443"
|
return f"https://{self.username}:{self.password}@{hostname}:443"
|
||||||
|
|
||||||
def get_random_server(self, country_code: str) -> Optional[str]:
|
def get_random_server(self, country_code: str) -> Optional[str]:
|
||||||
|
|||||||
2
uv.lock
generated
2
uv.lock
generated
@@ -1565,7 +1565,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unshackle"
|
name = "unshackle"
|
||||||
version = "2.0.0"
|
version = "2.1.0"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiohttp-swagger3" },
|
{ name = "aiohttp-swagger3" },
|
||||||
|
|||||||
Reference in New Issue
Block a user