4 Commits

Author SHA1 Message Date
Andy
dc9823cd28 chore(release): bump version to 2.1.0 2025-11-27 23:35:56 +00:00
Andy
6fa3554b70 fix(dl): preserve attachments when rebuilding track list
Attachments (screenshots, fonts) were being dropped when title.tracks was rebuilt from kept_tracks, causing image files to remain in temp directory after muxing. The cleanup code iterated over an empty attachments list since they were orphaned during track filtering.
2025-11-27 23:03:53 +00:00
Andy
2d4bf140fa fix(dash): add AdaptationSet-level BaseURL resolution
Add support for BaseURL elements at the AdaptationSet level per DASH spec. The URL resolution chain now properly follows: MPD → Period → AdaptationSet → Representation.
2025-11-25 16:09:28 +00:00
Andy
d0816787ce fix: restrict WindscribeVPN to supported regions 2025-11-24 18:17:00 +00:00
7 changed files with 53 additions and 8 deletions

View File

@@ -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

View File

@@ -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"

View File

@@ -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)

View File

@@ -1 +1 @@
__version__ = "2.0.0" __version__ = "2.1.0"

View File

@@ -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

View File

@@ -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
View File

@@ -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" },