From fa8f7a2a570e0d38aef1222aabbf61270eb426e1 Mon Sep 17 00:00:00 2001 From: VineFeeder Date: Wed, 15 Oct 2025 11:35:58 +0100 Subject: [PATCH] 1.4.8; search from menu --- UPDATES.md | 4 + packages/envied/pyproject.toml | 5 +- packages/envied/src/envied/CHANGELOG.md | 459 ++++++++++++++++++ packages/envied/src/envied/commands/dl.py | 22 +- packages/envied/src/envied/commands/prd.py | 2 +- packages/envied/src/envied/core/__init__.py | 2 +- packages/envied/src/envied/core/__main__.py | 4 +- packages/envied/src/envied/core/constants.py | 1 + .../envied/src/envied/core/tracks/subtitle.py | 69 ++- .../envied/src/envied/core/tracks/tracks.py | 9 +- packages/envied/src/envied/core/utilities.py | 9 +- .../vinefeeder/src/vinefeeder/config.yaml | 2 +- .../src/vinefeeder/services/ALL4/__init__.py | 16 +- .../src/vinefeeder/services/BBC/__init__.py | 8 +- .../src/vinefeeder/services/ITVX/__init__.py | 17 +- .../src/vinefeeder/services/MY5/__init__.py | 3 + .../src/vinefeeder/services/PLEX/__init__.py | 18 +- .../src/vinefeeder/services/RTE/__init__.py | 18 +- .../src/vinefeeder/services/STV/__init__.py | 16 +- .../src/vinefeeder/services/TPTV/__init__.py | 16 +- .../src/vinefeeder/services/TVNZ/__init__.py | 9 +- .../src/vinefeeder/services/U/__init__.py | 12 +- uv.lock | 26 +- 23 files changed, 669 insertions(+), 78 deletions(-) create mode 100644 packages/envied/src/envied/CHANGELOG.md diff --git a/UPDATES.md b/UPDATES.md index 3668d73..65afa5b 100644 --- a/UPDATES.md +++ b/UPDATES.md @@ -13,4 +13,8 @@ **28 Sept 2025** - added HellYes GUI - added images to reflect GUI change + +**15 Oct 2025** + - corrected keyword search when using menu in terminal window. Previously that mode of search only took a url striing; now it takes keywords. + - code additions to reflect upstream envied changes. diff --git a/packages/envied/pyproject.toml b/packages/envied/pyproject.toml index 4e44c92..7d53f70 100644 --- a/packages/envied/pyproject.toml +++ b/packages/envied/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "uv_build" [project] name = "envied" -version = "1.4.7" +version = "1.4.8" description = "Modular Movie, TV, and Music Archival Software." authors = [{ name = "rlaphoenix and others" }] requires-python = ">=3.11,<3.15" @@ -60,9 +60,10 @@ dependencies = [ "subby", "webvtt-py>=0.5.1", "isodate>=0.7.2", - "beaupy>=3.10.1", + "beaupy>=3.10.2", "scrapy>=2.13.3", "mypy>=1.18.2,<2", + "pysubs2>=1.7.0,<2", ] [project.urls] diff --git a/packages/envied/src/envied/CHANGELOG.md b/packages/envied/src/envied/CHANGELOG.md new file mode 100644 index 0000000..82b9425 --- /dev/null +++ b/packages/envied/src/envied/CHANGELOG.md @@ -0,0 +1,459 @@ +# Changelog + +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/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.4.8] - 2025-10-08 + +### Added + +- **Exact Language Matching**: New `--exact-lang` flag for precise language matching + - Enables strict language code matching without fallbacks +- **No-Mux Flag**: New `--no-mux` flag to skip muxing tracks into container files + - Useful for keeping individual track files separate +- **AC4 Audio Codec Support**: Enhanced audio format handling + - Added AC4 codec support in Audio class with updated mime/profile handling +- **pysubs2 Subtitle Conversion**: Extended subtitle format support + - Added pysubs2 subtitle conversion with extended format support + - Configurable conversion method in configuration + +### Changed + +- **Audio Track Sorting**: Optimized audio track selection logic + - Improved audio track sorting by grouping descriptive tracks and sorting by bitrate + - Better identification of ATMOS and DD+ as highest quality for filenaming +- **pyplayready Update**: Upgraded to version 0.6.3 + - Updated import paths to resolve compatibility issues + - Fixed lxml constraints for better dependency management +- **pysubs2 Conversion Method**: Moved from auto to manual configuration + - pysubs2 no longer auto-selected during testing phase + +### Fixed + +- **Remote CDM**: Fixed curl_cffi compatibility + - Added curl_cffi to instance checks in RemoteCDM +- **Temporary File Handling**: Improved encoding handling + - Specified UTF-8 encoding when opening temporary files + +### Reverted + +- **tinycss SyntaxWarning Suppression**: Removed ineffective warning filter + - Reverted warnings filter that didn't work as expected for suppressing tinycss warnings + +## [1.4.7] - 2025-09-25 + +### Added + +- **curl_cffi Session Support**: Enhanced anti-bot protection with browser impersonation + - Added new session utility with curl_cffi support for bypassing anti-bot measures + - Browser impersonation support for Chrome, Firefox, and Safari user agents + - Full backward compatibility with requests.Session maintained + - Suppressed HTTPS proxy warnings for improved user experience +- **Download Retry Functionality**: Configurable retry mechanism for failed downloads + - Added retry count option to download function for improved reliability +- **Subtitle Requirements Options**: Enhanced subtitle download control + - Added options for required subtitles in download command + - Better control over subtitle track selection and requirements +- **Quality Selection Enhancement**: Improved quality selection options + - Added best available quality option in download command for optimal track selection +- **DecryptLabs API Integration**: Enhanced remote CDM configuration + - Added decrypt_labs_api_key to Config initialization for better API integration + +### Changed + +- **Manifest Parser Updates**: Enhanced compatibility across all parsers + - Updated DASH, HLS, ISM, and M3U8 parsers to accept curl_cffi sessions + - Improved cookie handling compatibility between requests and curl_cffi +- **Logging Improvements**: Reduced log verbosity for better user experience + - Changed duplicate track log level to debug to reduce console noise + - Dynamic CDM selection messages moved to debug-only output + +### Fixed + +- **Remote CDM Reuse**: Fixed KeyError in dynamic CDM selection + - Prevents KeyError when reusing remote CDMs in dynamic selection process + - Creates copy of CDM dictionary before modification to prevent configuration mutation + - Allows same CDM to be selected multiple times within session without errors + +## [1.4.6] - 2025-09-13 + +### Added + +- **Quality-Based CDM Selection**: Dynamic CDM selection based on video resolution + - Automatically selects appropriate CDM (L3/L1) based on video track quality + - Supports quality thresholds in configuration (>=, >, <=, <, exact match) + - Pre-selects optimal CDM based on highest quality across all video tracks + - Maintains backward compatibility with existing CDM configurations +- **Automatic Audio Language Metadata**: Intelligent embedded audio language detection + - Automatically sets audio language metadata when no separate audio tracks exist + - Smart video track selection based on title language with fallbacks + - Enhanced FFmpeg repackaging with audio stream metadata injection +- **Lazy DRM Loading**: Deferred DRM loading for multi-track key retrieval optimization + - Add deferred DRM loading to M3U8 parser to mark tracks for later processing + - Just-in-time DRM loading during download process for better performance + +### Changed + +- **Enhanced CDM Management**: Improved CDM switching logic for multi-quality downloads + - CDM selection now based on highest quality track to avoid inefficient switching + - Quality-based selection only within same DRM type (Widevine-to-Widevine, PlayReady-to-PlayReady) + - Single CDM used per session for better performance and reliability + +### Fixed + +- **Vault Caching Issues**: Fixed vault count display and NoneType iteration errors + - Fix 'NoneType' object is not iterable error in DecryptLabsRemoteCDM + - Fix vault count display showing 0/3 instead of actual successful vault count +- **Service Name Transmission**: Resolved DecryptLabsRemoteCDM service name issues + - Fixed DecryptLabsRemoteCDM sending 'generic' instead of proper service names + - Added case-insensitive vault lookups for SQLite/MySQL vaults + - Added local vault integration to DecryptLabsRemoteCDM +- **Import Organization**: Improved import ordering and code formatting + - Reorder imports in decrypt_labs_remote_cdm.py for better organization + - Clean up trailing whitespace in vault files + +### Configuration + +- **New CDM Configuration Format**: Extended `cdm:` section supports quality-based selection + ```yaml + cdm: + SERVICE_NAME: + "<=1080": l3_cdm_name + ">1080": l1_cdm_name + default: l3_cdm_name + ``` + +## [1.4.5] - 2025-09-09 + +### Added + +- **Enhanced CDM Key Caching**: Improved key caching and session management for L1/L2 devices + - Optimized `get_cached_keys_if_exists` functionality for better performance with L1/L2 devices + - Enhanced cached key retrieval logic with improved session handling +- **Widevine Common Certificate Fallback**: Added fallback to Widevine common certificate for L1 devices + - Improved compatibility for L1 devices when service certificates are unavailable +- **Enhanced Vault Loading**: Improved vault loading and key copying logic + - Better error handling and key management in vault operations +- **PSSH Display Optimization**: Truncated PSSH string display in non-debug mode for cleaner output +- **CDM Error Messaging**: Added error messages for missing service certificates in CDM sessions + +### Changed + +- **Dynamic Version Headers**: Updated User-Agent headers to use dynamic version strings + - DecryptLabsRemoteCDM now uses dynamic version import instead of hardcoded version +- **Intelligent CDM Caching**: Implemented intelligent caching system for CDM license requests + - Enhanced caching logic reduces redundant license requests and improves performance +- **Enhanced Tag Handling**: Improved tag handling for TV shows and movies from Simkl data + - Better metadata processing and formatting for improved media tagging + +### Fixed + +- **CDM Session Management**: Clean up session data when retrieving cached keys + - Remove decrypt_labs_session_id and challenge from session when cached keys exist but there are missing kids + - Ensures clean state for subsequent requests and prevents session conflicts +- **Tag Formatting**: Fixed formatting issues in tag processing +- **Import Order**: Fixed import order issues in tags module + +## [1.4.4] - 2025-09-02 + +### Added + +- **Enhanced DecryptLabs CDM Support**: Comprehensive remote CDM functionality + - Full support for Widevine, PlayReady, and ChromeCDM through DecryptLabsRemoteCDM + - Enhanced session management and caching support for remote WV/PR operations + - Support for cached keys and improved license handling + - New CDM configurations for Chrome and PlayReady devices with updated User-Agent and service certificate +- **Advanced Configuration Options**: New device and language preferences + - Added configuration options for device certificate status list + - Enhanced language preference settings + +### Changed + +- **DRM Decryption Enhancements**: Streamlined decryption process + - Simplified decrypt method by removing unused parameter and streamlined logic + - Improved DecryptLabs CDM configurations with better device support + +### Fixed + +- **Matroska Tag Compliance**: Enhanced media container compatibility + - Fixed Matroska tag compliance with official specification +- **Application Branding**: Cleaned up version display + - Removed old devine version reference from banner to avoid developer confusion + - Updated branding while maintaining original GNU license compliance +- **IP Information Handling**: Improved geolocation services + - Enhanced get_ip_info functionality with better failover handling + - Added support for 429 error handling and multiple API provider fallback + - Implemented cached IP info retrieval with fallback tester to avoid rate limiting +- **Dependencies**: Streamlined package requirements + - Removed unnecessary data extra requirement from langcodes + +### Removed + +- Deprecated version references in application banner for clarity + +## [1.4.3] - 2025-08-20 + +### Added + +- Cached IP info helper for region detection + - New `get_cached_ip_info()` with 24h cache and provider rotation (ipinfo/ipapi) with 429 handling. + - Reduces external calls and stabilizes non-proxy region lookups for caching/logging. + +### Changed + +- DRM decryption selection is fully configuration-driven + - Widevine and PlayReady now select the decrypter based solely on `decryption` in YAML (including per-service mapping). + - Shaka Packager remains the default decrypter when not specified. + - `dl.py` logs the chosen tool based on the resolved configuration. +- Geofencing and proxy verification improvements + - Safer geofence checks with error handling and clearer logs. + - Always verify proxy exit region via live IP lookup; fallback to proxy parsing on failure. +- Example config updated to default to Shaka + - `unshackle.yaml`/example now sets `decryption.default: shaka` (service overrides still supported). + +### Removed + +- Deprecated parameter `use_mp4decrypt` + - Removed from `Widevine.decrypt()` and `PlayReady.decrypt()` and all callsites. + - Internal naming switched from mp4decrypt-specific flags to generic `decrypter` selection. + +## [1.4.2] - 2025-08-14 + +### Added + +- **Session Management for API Requests**: Enhanced API reliability with retry logic + - Implemented session management for tags functionality with automatic retry mechanisms + - Improved API request stability and error handling +- **Series Year Configuration**: New `series_year` option for title naming control + - Added configurable `series_year` option to control year inclusion in series titles + - Enhanced YAML configuration with series year handling options +- **Audio Language Override**: New audio language selection option + - Added `audio_language` option to override default language selection for audio tracks + - Provides more granular control over audio track selection +- **Vault Key Reception Control**: Enhanced vault security options + - Added `no_push` option to Vault and its subclasses to control key reception + - Improved key management security and flexibility + +### Changed + +- **HLS Segment Processing**: Enhanced segment retrieval and merging capabilities + - Enhanced segment retrieval to allow all file types for better compatibility + - Improved segment merging with recursive file search and fallback to binary concatenation + - Fixed issues with VTT files from HLS not being found correctly due to format changes + - Added cleanup of empty segment directories after processing +- **Documentation**: Updated README.md with latest information + +### Fixed + +- **Audio Track Selection**: Improved per-language logic for audio tracks + - Adjusted `per_language` logic to ensure correct audio track selection + - Fixed issue where all tracks for selected language were being downloaded instead of just the intended ones + +## [1.4.1] - 2025-08-08 + +### Added + +- **Title Caching System**: Intelligent title caching to reduce redundant API calls + - Configurable title caching with 30-minute default cache duration + - 24-hour fallback cache on API failures for improved reliability + - Region-aware caching to handle geo-restricted content properly + - SHA256 hashing for cache keys to handle complex title IDs + - Added `--no-cache` CLI flag to bypass caching when needed + - Added `--reset-cache` CLI flag to clear existing cache data + - New cache configuration variables in config system + - Documented caching options in example configuration file + - Significantly improves performance when debugging or modifying CLI parameters +- **Enhanced Tagging Configuration**: New options for customizing tag behavior + - Added `tag_group_name` config option to control group name inclusion in tags + - Added `tag_imdb_tmdb` config option to control IMDB/TMDB details in tags + - Added Simkl API endpoint support as fallback when no TMDB API key is provided + - Enhanced tag_file function to prioritize provided TMDB ID when `--tmdb` flag is used + - Improved TMDB ID handling with better prioritization logic + +### Changed + +- **Language Selection Enhancement**: Improved default language handling + - Updated language option default to 'orig' when no `-l` flag is set + - Avoids hardcoded 'en' default and respects original content language +- **Tagging Logic Improvements**: Simplified and enhanced tagging functionality + - Simplified Simkl search logic with soft-fail when no results found + - Enhanced tag_file function with better TMDB ID prioritization + - Improved error handling in tagging operations + +### Fixed + +- **Subtitle Processing**: Enhanced subtitle filtering for edge cases + - Fixed ValueError in subtitle filtering for multiple colons in time references + - Improved handling of subtitles containing complex time formatting + - Better error handling for malformed subtitle timestamps + +### Removed + +- **Docker Support**: Removed Docker configuration from repository + - Removed Dockerfile and .dockerignore files + - Cleaned up README.md Docker-related documentation + - Focuses on direct installation methods + +## [1.4.0] - 2025-08-05 + +### Added + +- **HLG Transfer Characteristics Preservation**: Enhanced video muxing to preserve HLG color metadata + - Added automatic detection of HLG video tracks during muxing process + - Implemented `--color-transfer-characteristics 0:18` argument for mkvmerge when processing HLG content + - Prevents incorrect conversion from HLG (18) to BT.2020 (14) transfer characteristics + - Ensures proper HLG playback support on compatible hardware without manual editing +- **Original Language Support**: Enhanced language selection with 'orig' keyword support + - Added support for 'orig' language selector for both video and audio tracks + - Automatically detects and uses the title's original language when 'orig' is specified + - Improved language processing logic with better duplicate handling + - Enhanced help text to document original language selection usage +- **Forced Subtitle Support**: Added option to include forced subtitle tracks + - New functionality to download and include forced subtitle tracks alongside regular subtitles +- **WebVTT Subtitle Filtering**: Enhanced subtitle processing capabilities + - Added filtering for unwanted cues in WebVTT subtitles + - Improved subtitle quality by removing unnecessary metadata + +### Changed + +- **DRM Track Decryption**: Improved DRM decryption track selection logic + - Enhanced `get_drm_for_cdm()` method usage for better DRM-CDM matching + - Added warning messages when no matching DRM is found for tracks + - Improved error handling and logging for DRM decryption failures +- **Series Tree Representation**: Enhanced episode tree display formatting + - Updated series tree to show season breakdown with episode counts + - Improved visual representation with "S{season}({count})" format + - Better organization of series information in console output +- **Hybrid Processing UI**: Enhanced extraction and conversion processes + - Added dynamic spinning bars to follow the rest of the codebase design + - Improved visual feedback during hybrid HDR processing operations +- **Track Selection Logic**: Enhanced multi-track selection capabilities + - Fixed track selection to support combining -V, -A, -S flags properly + - Improved flexibility in selecting multiple track types simultaneously +- **Service Subtitle Support**: Added configuration for services without subtitle support + - Services can now indicate if they don't support subtitle downloads + - Prevents unnecessary subtitle download attempts for unsupported services +- **Update Checker**: Enhanced update checking logic and cache handling + - Improved rate limiting and caching mechanisms for update checks + - Better performance and reduced API calls to GitHub + +### Fixed + +- **PlayReady KID Extraction**: Enhanced KID extraction from PSSH data + - Added base64 support and XML parsing for better KID detection + - Fixed issue where only one KID was being extracted for certain services + - Improved multi-KID support for PlayReady protected content +- **Dolby Vision Detection**: Improved DV codec detection across all formats + - Fixed detection of dvhe.05.06 codec which was not being recognized correctly + - Enhanced detection logic in Episode and Movie title classes + - Better support for various Dolby Vision codec variants + +## [1.3.0] - 2025-08-03 + +### Added + +- **mp4decrypt Support**: Alternative DRM decryption method using mp4decrypt from Bento4 + - Added `mp4decrypt` binary detection and support in binaries module + - New `decryption` configuration option in unshackle.yaml for service-specific decryption methods + - Enhanced PlayReady and Widevine DRM classes with mp4decrypt decryption support + - Service-specific decryption mapping allows choosing between `shaka` and `mp4decrypt` per service + - Improved error handling and progress reporting for mp4decrypt operations +- **Scene Naming Configuration**: New `scene_naming` option for controlling file naming conventions + - Added scene naming logic to movie, episode, and song title classes + - Configurable through unshackle.yaml to enable/disable scene naming standards +- **Terminal Cleanup and Signal Handling**: Enhanced console management + - Implemented proper terminal cleanup on application exit + - Added signal handling for graceful shutdown in ComfyConsole +- **Configuration Template**: New `unshackle-example.yaml` template file + - Replaced main `unshackle.yaml` with example template to prevent git conflicts + - Users can now modify their local config without affecting repository updates +- **Enhanced Credential Management**: Improved CDM and vault configuration + - Expanded credential management documentation in configuration + - Enhanced CDM configuration examples and guidelines +- **Video Transfer Standards**: Added `Unspecified_Image` option to Transfer enum + - Implements ITU-T H.Sup19 standard value 2 for image characteristics + - Supports still image coding systems and unknown transfer characteristics +- **Update Check Rate Limiting**: Enhanced update checking system + - Added configurable update check intervals to prevent excessive API calls + - Improved rate limiting for GitHub API requests + +### Changed + +- **DRM Decryption Architecture**: Enhanced decryption system with dual method support + - Updated `dl.py` to handle service-specific decryption method selection + - Refactored `Config` class to manage decryption method mapping per service + - Enhanced DRM decrypt methods with `use_mp4decrypt` parameter for method selection +- **Error Handling**: Improved exception handling in Hybrid class + - Replaced log.exit calls with ValueError exceptions for better error propagation + - Enhanced error handling consistency across hybrid processing + +### Fixed + +- **Proxy Configuration**: Fixed proxy server mapping in configuration + - Renamed 'servers' to 'server_map' in proxy configuration to resolve Nord/Surfshark naming conflicts + - Updated configuration structure for better compatibility with proxy providers +- **HTTP Vault**: Improved URL handling and key retrieval logic + - Fixed URL processing issues in HTTP-based key vaults + - Enhanced key retrieval reliability and error handling + +## [1.2.0] - 2025-07-30 + +### Added + +- **Update Checker**: Automatic GitHub release version checking on startup + - Configurable update notifications via `update_checks` setting in unshackle.yaml + - Non-blocking HTTP requests with 5-second timeout for performance + - Smart semantic version comparison supporting all version formats (x.y.z, x.y, x) + - Graceful error handling for network issues and API failures + - User-friendly update notifications with current → latest version display + - Direct links to GitHub releases page for easy updates +- **HDR10+ Support**: Enhanced HDR10+ metadata processing for hybrid tracks + - HDR10+ tool binary support (`hdr10plus_tool`) added to binaries module + - HDR10+ to Dolby Vision conversion capabilities in hybrid processing + - Enhanced metadata extraction for HDR10+ content +- **Duration Fix Handling**: Added duration correction for video and hybrid tracks +- **Temporary Directory Management**: Automatic creation of temp directories for attachment downloads + +### Changed + +- Enhanced configuration system with new `update_checks` boolean option (defaults to true) +- Updated sample unshackle.yaml with update checker configuration documentation +- Improved console styling consistency using `bright_black` for dimmed text +- **Environment Dependency Check**: Complete overhaul with detailed categorization and status summary + - Organized dependencies by category (Core, HDR, Download, Subtitle, Player, Network) + - Enhanced status reporting with compact summary display + - Improved tool requirement tracking and missing dependency alerts +- **Hybrid Track Processing**: Significant improvements to HDR10+ and Dolby Vision handling + - Enhanced metadata extraction and processing workflows + - Better integration with HDR processing tools + +### Removed + +- **Docker Workflow**: Removed Docker build and publish GitHub Actions workflow for manual builds + +## [1.1.0] - 2025-07-29 + +### Added + +- **HDR10+DV Hybrid Processing**: New `-r HYBRID` command for processing HDR10 and Dolby Vision tracks + - Support for hybrid HDR processing and injection using dovi_tool + - New hybrid track processing module for seamless HDR10/DV conversion + - Automatic detection and handling of HDR10 and DV metadata +- Support for HDR10 and DV tracks in hybrid mode for EXAMPLE service +- Binary availability check for dovi_tool in hybrid mode operations +- Enhanced track processing capabilities for HDR content + +### Fixed + +- Import order issues and missing json import in hybrid processing +- UV installation process and error handling improvements +- Binary search functionality updated to use `binaries.find` + +### Changed + +- Updated package version from 1.0.2 to 1.1.0 +- Enhanced dl.py command processing for hybrid mode support +- Improved core titles (episode/movie) processing for HDR content +- Extended tracks module with hybrid processing capabilities \ No newline at end of file diff --git a/packages/envied/src/envied/commands/dl.py b/packages/envied/src/envied/commands/dl.py index d2fe608..5a73581 100644 --- a/packages/envied/src/envied/commands/dl.py +++ b/packages/envied/src/envied/commands/dl.py @@ -162,6 +162,12 @@ class dl: ) @click.option("-sl", "--s-lang", type=LANGUAGE_RANGE, default=["all"], help="Language wanted for Subtitles.") @click.option("-fs", "--forced-subs", is_flag=True, default=False, help="Include forced subtitle tracks.") + @click.option( + "--exact-lang", + is_flag=True, + default=False, + help="Use exact language matching (no variants). With this flag, -l es-419 matches ONLY es-419, not es-ES or other variants.", + ) @click.option( "--proxy", type=str, @@ -438,6 +444,7 @@ class dl: a_lang: list[str], s_lang: list[str], forced_subs: bool, + exact_lang: bool, sub_format: Optional[Subtitle.Codec], video_only: bool, audio_only: bool, @@ -709,7 +716,8 @@ class dl: else: if language not in processed_video_lang: processed_video_lang.append(language) - title.tracks.videos = title.tracks.by_language(title.tracks.videos, processed_video_lang) + title.tracks.videos = title.tracks.by_language( + title.tracks.videos, processed_video_lang, exact_match=exact_lang) if not title.tracks.videos: self.log.error(f"There's no {processed_video_lang} Video Track...") sys.exit(1) @@ -772,16 +780,18 @@ class dl: # filter subtitle tracks if s_lang and "all" not in s_lang: + from envied.core.utilities import is_exact_match + match_func = is_exact_match if exact_lang else is_close_match missing_langs = [ lang_ for lang_ in s_lang - if not any(is_close_match(lang_, [sub.language]) for sub in title.tracks.subtitles) + if not any(match_func(lang_, [sub.language]) for sub in title.tracks.subtitles) ] if missing_langs: self.log.error(", ".join(missing_langs) + " not found in tracks") sys.exit(1) - title.tracks.select_subtitles(lambda x: is_close_match(x.language, s_lang)) + title.tracks.select_subtitles(lambda x: match_func(x.language, s_lang)) if not title.tracks.subtitles: self.log.error(f"There's no {s_lang} Subtitle Track...") sys.exit(1) @@ -845,7 +855,7 @@ class dl: elif "all" not in processed_lang: per_language = 1 title.tracks.audio = title.tracks.by_language( - title.tracks.audio, processed_lang, per_language=per_language + title.tracks.audio, processed_lang, per_language=per_language, exact_match=exact_lang ) if not title.tracks.audio: self.log.error(f"There's no {processed_lang} Audio Track, cannot continue...") @@ -1040,11 +1050,11 @@ class dl: if family_dir.exists(): fonts = family_dir.glob("*.*tf") for font in fonts: - title.tracks.add(Attachment(font, f"{font_name} ({font.stem})")) + title.tracks.add(Attachment(path=font, name=f"{font_name} ({font.stem})")) font_count += 1 elif fonts_from_system: for font in fonts_from_system: - title.tracks.add(Attachment(font, f"{font_name} ({font.stem})")) + title.tracks.add(Attachment(path=font, name=f"{font_name} ({font.stem})")) font_count += 1 else: self.log.warning(f"Subtitle uses font [text2]{font_name}[/] but it could not be found...") diff --git a/packages/envied/src/envied/commands/prd.py b/packages/envied/src/envied/commands/prd.py index b656630..d9d6883 100644 --- a/packages/envied/src/envied/commands/prd.py +++ b/packages/envied/src/envied/commands/prd.py @@ -5,10 +5,10 @@ from typing import Optional import click import requests from Crypto.Random import get_random_bytes +from pyplayready import InvalidCertificateChain, OutdatedDevice from pyplayready.cdm import Cdm from pyplayready.crypto.ecc_key import ECCKey from pyplayready.device import Device -from pyplayready import InvalidCertificateChain, OutdatedDevice from pyplayready.system.bcert import Certificate, CertificateChain from pyplayready.system.pssh import PSSH diff --git a/packages/envied/src/envied/core/__init__.py b/packages/envied/src/envied/core/__init__.py index ac329c9..4963389 100644 --- a/packages/envied/src/envied/core/__init__.py +++ b/packages/envied/src/envied/core/__init__.py @@ -1 +1 @@ -__version__ = "1.4.7" +__version__ = "1.4.8" diff --git a/packages/envied/src/envied/core/__main__.py b/packages/envied/src/envied/core/__main__.py index 15dad5e..ad0d73f 100644 --- a/packages/envied/src/envied/core/__main__.py +++ b/packages/envied/src/envied/core/__main__.py @@ -1,6 +1,6 @@ import atexit import logging -import warnings +#import warnings from pathlib import Path import click @@ -8,7 +8,7 @@ import urllib3 from rich import traceback # Suppress SyntaxWarning from unmaintained tinycss package (dependency of subby) -warnings.filterwarnings("ignore", category=SyntaxWarning, module="tinycss") +#warnings.filterwarnings("ignore", category=SyntaxWarning, module="tinycss") from rich.console import Group from rich.padding import Padding diff --git a/packages/envied/src/envied/core/constants.py b/packages/envied/src/envied/core/constants.py index 609fcc3..6a14f7d 100644 --- a/packages/envied/src/envied/core/constants.py +++ b/packages/envied/src/envied/core/constants.py @@ -6,6 +6,7 @@ DOWNLOAD_LICENCE_ONLY = Event() DRM_SORT_MAP = ["ClearKey", "Widevine"] LANGUAGE_MAX_DISTANCE = 5 # this is max to be considered "same", e.g., en, en-US, en-AU +LANGUAGE_EXACT_DISTANCE = 0 # exact match only, no variants VIDEO_CODEC_MAP = {"AVC": "H.264", "HEVC": "H.265"} DYNAMIC_RANGE_MAP = {"HDR10": "HDR", "HDR10+": "HDR10P", "Dolby Vision": "DV", "HDR10 / HDR10+": "HDR10P", "HDR10 / HDR10": "HDR"} AUDIO_CODEC_MAP = {"E-AC-3": "DDP", "AC-3": "DD"} diff --git a/packages/envied/src/envied/core/tracks/subtitle.py b/packages/envied/src/envied/core/tracks/subtitle.py index 086a4c8..b520372 100644 --- a/packages/envied/src/envied/core/tracks/subtitle.py +++ b/packages/envied/src/envied/core/tracks/subtitle.py @@ -10,6 +10,7 @@ from pathlib import Path from typing import Any, Callable, Iterable, Optional, Union import pycaption +import pysubs2 import requests from construct import Container from pycaption import Caption, CaptionList, CaptionNode, WebVTTReader @@ -33,6 +34,9 @@ class Subtitle(Track): TimedTextMarkupLang = "TTML" # https://wikipedia.org/wiki/Timed_Text_Markup_Language WebVTT = "VTT" # https://wikipedia.org/wiki/WebVTT SAMI = "SMI" # https://wikipedia.org/wiki/SAMI + MicroDVD = "SUB" # https://wikipedia.org/wiki/MicroDVD + MPL2 = "MPL2" # MPL2 subtitle format + TMP = "TMP" # TMP subtitle format # MPEG-DASH box-encapsulated subtitle formats fTTML = "STPP" # https://www.w3.org/TR/2018/REC-ttml-imsc1.0.1-20180424 fVTT = "WVTT" # https://www.w3.org/TR/webvtt1 @@ -56,6 +60,12 @@ class Subtitle(Track): return Subtitle.Codec.WebVTT elif mime in ("smi", "sami"): return Subtitle.Codec.SAMI + elif mime in ("sub", "microdvd"): + return Subtitle.Codec.MicroDVD + elif mime == "mpl2": + return Subtitle.Codec.MPL2 + elif mime == "tmp": + return Subtitle.Codec.TMP elif mime == "stpp": return Subtitle.Codec.fTTML elif mime == "wvtt": @@ -391,6 +401,57 @@ class Subtitle(Track): # Fall back to existing conversion method on any error return self._convert_standard(codec) + def convert_with_pysubs2(self, codec: Subtitle.Codec) -> Path: + """ + Convert subtitle using pysubs2 library for broad format support. + + pysubs2 is a pure-Python library supporting SubRip (SRT), SubStation Alpha + (SSA/ASS), WebVTT, TTML, SAMI, MicroDVD, MPL2, and TMP formats. + """ + if not self.path or not self.path.exists(): + raise ValueError("You must download the subtitle track first.") + + if self.codec == codec: + return self.path + + output_path = self.path.with_suffix(f".{codec.value.lower()}") + original_path = self.path + + codec_to_pysubs2_format = { + Subtitle.Codec.SubRip: "srt", + Subtitle.Codec.SubStationAlpha: "ssa", + Subtitle.Codec.SubStationAlphav4: "ass", + Subtitle.Codec.WebVTT: "vtt", + Subtitle.Codec.TimedTextMarkupLang: "ttml", + Subtitle.Codec.SAMI: "sami", + Subtitle.Codec.MicroDVD: "microdvd", + Subtitle.Codec.MPL2: "mpl2", + Subtitle.Codec.TMP: "tmp", + } + + pysubs2_output_format = codec_to_pysubs2_format.get(codec) + if pysubs2_output_format is None: + return self._convert_standard(codec) + + try: + subs = pysubs2.load(str(self.path), encoding="utf-8") + + subs.save(str(output_path), format_=pysubs2_output_format, encoding="utf-8") + + if original_path.exists() and original_path != output_path: + original_path.unlink() + + self.path = output_path + self.codec = codec + + if callable(self.OnConverted): + self.OnConverted(codec) + + return output_path + + except Exception: + return self._convert_standard(codec) + def convert(self, codec: Subtitle.Codec) -> Path: """ Convert this Subtitle to another Format. @@ -400,6 +461,7 @@ class Subtitle(Track): - 'subby': Always uses subby with CommonIssuesFixer - 'subtitleedit': Uses SubtitleEdit when available, falls back to pycaption - 'pycaption': Uses only pycaption library + - 'pysubs2': Uses pysubs2 library """ # Check configuration for conversion method conversion_method = config.subtitle.get("conversion_method", "auto") @@ -407,11 +469,12 @@ class Subtitle(Track): if conversion_method == "subby": return self.convert_with_subby(codec) elif conversion_method == "subtitleedit": - return self._convert_standard(codec) # SubtitleEdit is used in standard conversion + return self._convert_standard(codec) elif conversion_method == "pycaption": return self._convert_pycaption_only(codec) + elif conversion_method == "pysubs2": + return self.convert_with_pysubs2(codec) elif conversion_method == "auto": - # Use subby for formats it handles better if self.codec in (Subtitle.Codec.WebVTT, Subtitle.Codec.SAMI): return self.convert_with_subby(codec) else: @@ -964,4 +1027,4 @@ class Subtitle(Track): ) -__all__ = ("Subtitle",) +__all__ = ("Subtitle",) \ No newline at end of file diff --git a/packages/envied/src/envied/core/tracks/tracks.py b/packages/envied/src/envied/core/tracks/tracks.py index 5f7e211..45bb160 100644 --- a/packages/envied/src/envied/core/tracks/tracks.py +++ b/packages/envied/src/envied/core/tracks/tracks.py @@ -14,7 +14,7 @@ from rich.tree import Tree from envied.core import binaries from envied.core.config import config from envied.core.console import console -from envied.core.constants import LANGUAGE_MAX_DISTANCE, AnyTrack, TrackT +from envied.core.constants import LANGUAGE_EXACT_DISTANCE, LANGUAGE_MAX_DISTANCE, AnyTrack, TrackT from envied.core.events import events from envied.core.tracks.attachment import Attachment from envied.core.tracks.audio import Audio @@ -295,11 +295,14 @@ class Tracks: self.videos = selected @staticmethod - def by_language(tracks: list[TrackT], languages: list[str], per_language: int = 0) -> list[TrackT]: + def by_language( + tracks: list[TrackT], languages: list[str], per_language: int = 0, exact_match: bool = False + ) -> list[TrackT]: + distance = LANGUAGE_EXACT_DISTANCE if exact_match else LANGUAGE_MAX_DISTANCE selected = [] for language in languages: selected.extend( - [x for x in tracks if closest_supported_match(x.language, [language], LANGUAGE_MAX_DISTANCE)][ + [x for x in tracks if closest_supported_match(str(x.language), [language], distance)][ : per_language or None ] ) diff --git a/packages/envied/src/envied/core/utilities.py b/packages/envied/src/envied/core/utilities.py index 22ab989..f0b9b52 100644 --- a/packages/envied/src/envied/core/utilities.py +++ b/packages/envied/src/envied/core/utilities.py @@ -24,7 +24,7 @@ from unidecode import unidecode from envied.core.cacher import Cacher from envied.core.config import config -from envied.core.constants import LANGUAGE_MAX_DISTANCE +from envied.core.constants import LANGUAGE_EXACT_DISTANCE, LANGUAGE_MAX_DISTANCE def rotate_log_file(log_path: Path, keep: int = 20) -> Path: @@ -113,6 +113,13 @@ def is_close_match(language: Union[str, Language], languages: Sequence[Union[str return False return closest_match(language, list(map(str, languages)))[1] <= LANGUAGE_MAX_DISTANCE +def is_exact_match(language: Union[str, Language], languages: Sequence[Union[str, Language, None]]) -> bool: + """Check if a language is an exact match to any of the provided languages.""" + languages = [x for x in languages if x] + if not languages: + return False + return closest_match(language, list(map(str, languages)))[1] <= LANGUAGE_EXACT_DISTANCE + def get_boxes(data: bytes, box_type: bytes, as_bytes: bool = False) -> Box: """ diff --git a/packages/vinefeeder/src/vinefeeder/config.yaml b/packages/vinefeeder/src/vinefeeder/config.yaml index 5f5698a..0057b81 100644 --- a/packages/vinefeeder/src/vinefeeder/config.yaml +++ b/packages/vinefeeder/src/vinefeeder/config.yaml @@ -1,2 +1,2 @@ -BATCH_DOWNLOAD: true +BATCH_DOWNLOAD: false TERMINAL_RESET: true diff --git a/packages/vinefeeder/src/vinefeeder/services/ALL4/__init__.py b/packages/vinefeeder/src/vinefeeder/services/ALL4/__init__.py index a907166..571a72d 100644 --- a/packages/vinefeeder/src/vinefeeder/services/ALL4/__init__.py +++ b/packages/vinefeeder/src/vinefeeder/services/ALL4/__init__.py @@ -90,15 +90,17 @@ class All4Loader(BaseLoader): elif inx == 0: # from greedy-search OR selecting Browse-category # example: https://www.channel4.com/programmes/the-great-british-bake-off/on-demand/75228-001 - - # need a search keyword(s) from url + if not 'https' in search_term: + return self.fetch_videos(search_term) + # need a search keyword(s) from category url # split and select series name - search_term = search_term.split("/")[4] - # fetch_videos_by_category search_term may have other params to remove - if "?" in search_term: - search_term = search_term.split("?")[0].replace("-", " ") + else: + search_term = search_term.split("/")[4] + # fetch_videos_by_category search_term may have other params to remove + if "?" in search_term: + search_term = search_term.split("?")[0].replace("-", " ") - return self.fetch_videos(search_term) + return self.fetch_videos(search_term) elif "http" in search_term and inx == 2: self.category = category diff --git a/packages/vinefeeder/src/vinefeeder/services/BBC/__init__.py b/packages/vinefeeder/src/vinefeeder/services/BBC/__init__.py index 39c2996..caaf154 100644 --- a/packages/vinefeeder/src/vinefeeder/services/BBC/__init__.py +++ b/packages/vinefeeder/src/vinefeeder/services/BBC/__init__.py @@ -185,12 +185,8 @@ class BbcLoader(BaseLoader): search_term.split("series")[0].replace("-", " ").strip() ) return self.fetch_videos(search_term) - - else: - print( - f"No search term found in {search_term}\nTry again with a series name in the url." - ) - return + else: + return self.fetch_videos(search_term) elif "http" in search_term and inx == 2: self.category = category diff --git a/packages/vinefeeder/src/vinefeeder/services/ITVX/__init__.py b/packages/vinefeeder/src/vinefeeder/services/ITVX/__init__.py index 5c357e9..b571bc1 100644 --- a/packages/vinefeeder/src/vinefeeder/services/ITVX/__init__.py +++ b/packages/vinefeeder/src/vinefeeder/services/ITVX/__init__.py @@ -62,7 +62,6 @@ class ItvxLoader(BaseLoader): e, "Is the package for vinefeeder/envied installed correctly ?", ) - return # keyword search @@ -73,14 +72,16 @@ class ItvxLoader(BaseLoader): # ALTERNATIVES BELOW FROM POP-UP MENU elif inx == 0: # from greedy-search OR selecting Browse-category - - # need a search keyword(s) from url + if not 'https' in search_term: + return self.fetch_videos(search_term) + # need a search keyword(s) from category url # split and select series name - search_term = search_term.split("/")[4].replace("-", " ") - # fetch_videos_by_category search_term may have other params to remove - if "?" in search_term: - search_term = search_term.split("?")[0].replace("-", " ") - return self.fetch_videos(search_term) + else: + search_term = search_term.split("/")[4].replace("-", " ") + # fetch_videos_by_category search_term may have other params to remove + if "?" in search_term: + search_term = search_term.split("?")[0].replace("-", " ") + return self.fetch_videos(search_term) elif "http" in search_term and inx == 2: self.category = category diff --git a/packages/vinefeeder/src/vinefeeder/services/MY5/__init__.py b/packages/vinefeeder/src/vinefeeder/services/MY5/__init__.py index ea83e0d..4a4dc6f 100644 --- a/packages/vinefeeder/src/vinefeeder/services/MY5/__init__.py +++ b/packages/vinefeeder/src/vinefeeder/services/MY5/__init__.py @@ -66,9 +66,12 @@ class My5Loader(BaseLoader): # ALTERNATIVES BELOW FROM POP-UP MENU elif inx == 0: # from greedy-search OR selecting Browse-category + if not 'https' in search_term: + return self.fetch_videos(search_term) # example: https://www.channel5.com/the-teacher/season-2/episode-2 # need a search keyword(s) from url # split and select series name + if "show" in search_term: match = re.search(r"\/show\/([a-z-]+)", search_term) diff --git a/packages/vinefeeder/src/vinefeeder/services/PLEX/__init__.py b/packages/vinefeeder/src/vinefeeder/services/PLEX/__init__.py index 037e9da..a105c9a 100644 --- a/packages/vinefeeder/src/vinefeeder/services/PLEX/__init__.py +++ b/packages/vinefeeder/src/vinefeeder/services/PLEX/__init__.py @@ -73,14 +73,18 @@ class PlexLoader(BaseLoader): elif inx == 0: # from greedy-search OR selecting Browse-category # example: https://www.channel4.com/programmes/the-great-british-bake-off/on-demand/75228-001 - - # need a search keyword(s) from url + if not 'https' in search_term: + return self.fetch_videos(search_term) + # need a search keyword(s) from category url # split and select series name - search_term = search_term.split("/")[4].replace("-", " ") - # fetch_videos_by_category search_term may have other params to remove - if "?" in search_term: - search_term = search_term.split("?")[0].replace("-", " ") - return self.fetch_videos(search_term) + else: + # need a search keyword(s) from url + # split and select series name + search_term = search_term.split("/")[4].replace("-", " ") + # fetch_videos_by_category search_term may have other params to remove + if "?" in search_term: + search_term = search_term.split("?")[0].replace("-", " ") + return self.fetch_videos(search_term) elif "http" in search_term and inx == 2: self.category = category diff --git a/packages/vinefeeder/src/vinefeeder/services/RTE/__init__.py b/packages/vinefeeder/src/vinefeeder/services/RTE/__init__.py index 254b171..9f01497 100644 --- a/packages/vinefeeder/src/vinefeeder/services/RTE/__init__.py +++ b/packages/vinefeeder/src/vinefeeder/services/RTE/__init__.py @@ -82,14 +82,18 @@ class RteLoader(BaseLoader): elif inx == 0: # from greedy-search OR selecting Browse-category # example: https://www.channel4.com/programmes/the-great-british-bake-off/on-demand/75228-001 - - # need a search keyword(s) from url + if not 'https' in search_term: + return self.fetch_videos(search_term) + # need a search keyword(s) from category url # split and select series name - search_term = search_term.split("/")[4].replace("-", " ") - # fetch_videos_by_category search_term may have other params to remove - if "?" in search_term: - search_term = search_term.split("?")[0].replace("-", " ") - return self.fetch_videos(search_term) + else: + # need a search keyword(s) from url + # split and select series name + search_term = search_term.split("/")[4].replace("-", " ") + # fetch_videos_by_category search_term may have other params to remove + if "?" in search_term: + search_term = search_term.split("?")[0].replace("-", " ") + return self.fetch_videos(search_term) elif "http" in search_term and inx == 2: self.category = category diff --git a/packages/vinefeeder/src/vinefeeder/services/STV/__init__.py b/packages/vinefeeder/src/vinefeeder/services/STV/__init__.py index 4248a0e..16bab4b 100644 --- a/packages/vinefeeder/src/vinefeeder/services/STV/__init__.py +++ b/packages/vinefeeder/src/vinefeeder/services/STV/__init__.py @@ -78,12 +78,16 @@ class StvLoader(BaseLoader): # https://player.stv.tv/summary/joan # need a search keyword(s) from url # split and select series name - - search_term = search_term.split("/")[-1] - # fetch_videos_by_category search_term may have other params to remove - if "?" in search_term: - search_term = search_term.split("?")[0].replace("-", " ") - return self.fetch_videos(search_term) + if not 'https' in search_term: + return self.fetch_videos(search_term) + # need a search keyword(s) from category url + # split and select series name + else: + search_term = search_term.split("/")[-1] + # fetch_videos_by_category search_term may have other params to remove + if "?" in search_term: + search_term = search_term.split("?")[0].replace("-", " ") + return self.fetch_videos(search_term) elif "http" in search_term and inx == 2: self.category = category diff --git a/packages/vinefeeder/src/vinefeeder/services/TPTV/__init__.py b/packages/vinefeeder/src/vinefeeder/services/TPTV/__init__.py index 06f8526..2145a43 100644 --- a/packages/vinefeeder/src/vinefeeder/services/TPTV/__init__.py +++ b/packages/vinefeeder/src/vinefeeder/services/TPTV/__init__.py @@ -110,12 +110,16 @@ class TptvLoader(BaseLoader): # ALTERNATIVES BELOW FROM POP-UP MENU elif inx == 0: # from greedy-search OR selecting Browse-category - - search_term = search_term.split("/")[-1] - # fetch_videos_by_category search_term may have other params to remove - if "?" in search_term: - search_term = search_term.split("?")[0].replace("-", " ") - return self.fetch_videos(search_term) + if not 'https' in search_term: + return self.fetch_videos(search_term) + # need a search keyword(s) from category url + # split and select series name + else: + search_term = search_term.split("/")[-1] + # fetch_videos_by_category search_term may have other params to remove + if "?" in search_term: + search_term = search_term.split("?")[0].replace("-", " ") + return self.fetch_videos(search_term) elif "http" in search_term and inx == 2: self.category = category diff --git a/packages/vinefeeder/src/vinefeeder/services/TVNZ/__init__.py b/packages/vinefeeder/src/vinefeeder/services/TVNZ/__init__.py index 0899d24..579b4ee 100644 --- a/packages/vinefeeder/src/vinefeeder/services/TVNZ/__init__.py +++ b/packages/vinefeeder/src/vinefeeder/services/TVNZ/__init__.py @@ -94,9 +94,14 @@ class TvnzLoader(BaseLoader): # from greedy-search OR selecting Browse-category # need a search keyword(s) from url # split and select series name - search_term = search_term.split("/")[4] + if not 'https' in search_term: + return self.fetch_videos(search_term) + # need a search keyword(s) from category url + # split and select series name + else: + search_term = search_term.split("/")[4] - return self.fetch_videos(search_term) + return self.fetch_videos(search_term) elif "http" in search_term and inx == 2: self.category = category diff --git a/packages/vinefeeder/src/vinefeeder/services/U/__init__.py b/packages/vinefeeder/src/vinefeeder/services/U/__init__.py index 07e5a25..5d3e9eb 100644 --- a/packages/vinefeeder/src/vinefeeder/services/U/__init__.py +++ b/packages/vinefeeder/src/vinefeeder/services/U/__init__.py @@ -72,11 +72,15 @@ class ULoader(BaseLoader): elif inx == 0: # from greedy-search OR selecting Browse-category # example https://u.co.uk/shows/worlds-most-dangerous-roads/series-6/episode-1/6363704256112 - - # need a search keyword(s) from url + if not 'https' in search_term: + return self.fetch_videos(search_term) + # need a search keyword(s) from category url # split and select series name - search_term = search_term.split("/")[4].replace("-", " ") - return self.fetch_videos(search_term) + else: + # need a search keyword(s) from url + # split and select series name + search_term = search_term.split("/")[4].replace("-", " ") + return self.fetch_videos(search_term) elif "http" in search_term and inx == 2: self.category = category diff --git a/uv.lock b/uv.lock index 3670e6f..bb48cdc 100644 --- a/uv.lock +++ b/uv.lock @@ -150,7 +150,7 @@ wheels = [ [[package]] name = "beaupy" -version = "3.10.1" +version = "3.10.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "emoji" }, @@ -158,9 +158,9 @@ dependencies = [ { name = "questo" }, { name = "rich" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/24/60/3b3b4d23e135c6828792f84d46be86c126fe72771121723c5775c43a6812/beaupy-3.10.1.tar.gz", hash = "sha256:8d8a1cde212264a0402a672eed322488fb212f2e11c80c08ed011a70b489ef12", size = 16417, upload-time = "2025-01-14T18:19:27.888Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/ce/81f6aa40d2c779a3f0a0ac11d130a9c14c6b4f53700cd279980f3b9ecdf0/beaupy-3.10.2.tar.gz", hash = "sha256:da1a644f71e60fddc599084a5ef8bd41118b9c779527ce501289729e60e7db90", size = 16535, upload-time = "2025-10-06T18:47:50.063Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/a8/0787af1185f990023d69240a61c46b853bad35d040206b9704179bac8dac/beaupy-3.10.1-py3-none-any.whl", hash = "sha256:b31337581fc445fc81b07701d97de6409d6b13a4d0b620ca461475c2756c45ca", size = 14675, upload-time = "2025-01-14T18:19:25.533Z" }, + { url = "https://files.pythonhosted.org/packages/d6/bf/42536b458a7108a3b880bbfc99783e73b1c3b4c24d4ede3057bb22c44e07/beaupy-3.10.2-py3-none-any.whl", hash = "sha256:c5398416b6a9dfed25d56be4caf4b995c41c9d18aa132203ef69fa3d8953bcc5", size = 14842, upload-time = "2025-10-06T18:47:48.8Z" }, ] [[package]] @@ -547,7 +547,7 @@ wheels = [ [[package]] name = "envied" -version = "1.4.7" +version = "1.4.8" source = { editable = "packages/envied" } dependencies = [ { name = "appdirs" }, @@ -574,6 +574,7 @@ dependencies = [ { name = "pymp4" }, { name = "pymysql" }, { name = "pyplayready" }, + { name = "pysubs2" }, { name = "pywidevine", extra = ["serve"] }, { name = "pyyaml" }, { name = "requests", extra = ["socks"] }, @@ -604,7 +605,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "appdirs", specifier = ">=1.4.4,<2" }, - { name = "beaupy", specifier = ">=3.10.1" }, + { name = "beaupy", specifier = ">=3.10.2" }, { name = "brotli", specifier = ">=1.1.0,<2" }, { name = "chardet", specifier = ">=5.2.0,<6" }, { name = "click", specifier = ">=8.1.8,<9" }, @@ -627,6 +628,7 @@ requires-dist = [ { name = "pymp4", specifier = ">=1.4.0,<2" }, { name = "pymysql", specifier = ">=1.1.0,<2" }, { name = "pyplayready", specifier = ">=0.6.3,<0.7" }, + { name = "pysubs2", specifier = ">=1.7.0,<2" }, { name = "pywidevine", extras = ["serve"], specifier = ">=1.8.0,<2" }, { name = "pyyaml", specifier = ">=6.0.1,<7" }, { name = "requests", extras = ["socks"], specifier = ">=2.31.0,<3" }, @@ -1683,6 +1685,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b0/1e/c6a28a142f14e735088534cc92951c3f48cccd77cdd4f3b10d7996be420f/pyqt6_sip-13.10.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3dde8024d055f496eba7d44061c5a1ba4eb72fc95e5a9d7a0dbc908317e0888b", size = 303833, upload-time = "2025-05-23T12:26:41.075Z" }, { url = "https://files.pythonhosted.org/packages/89/63/e5adf350c1c3123d4865c013f164c5265512fa79f09ad464fb2fdf9f9e61/pyqt6_sip-13.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:0b097eb58b4df936c4a2a88a2f367c8bb5c20ff049a45a7917ad75d698e3b277", size = 53527, upload-time = "2025-05-23T12:26:42.625Z" }, { url = "https://files.pythonhosted.org/packages/58/74/2df4195306d050fbf4963fb5636108a66e5afa6dc05fd9e81e51ec96c384/pyqt6_sip-13.10.2-cp313-cp313-win_arm64.whl", hash = "sha256:cc6a1dfdf324efaac6e7b890a608385205e652845c62130de919fd73a6326244", size = 45373, upload-time = "2025-05-23T12:26:43.536Z" }, + { url = "https://files.pythonhosted.org/packages/23/57/74b4eb7a51b9133958daa8409b55de95e44feb694d4e2e3eba81a070ca20/pyqt6_sip-13.10.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8a76a06a8e5c5b1f17a3f6f3c834ca324877e07b960b18b8b9bbfd9c536ec658", size = 112354, upload-time = "2025-10-08T08:44:00.22Z" }, + { url = "https://files.pythonhosted.org/packages/f2/cb/fdef02e0d6ee8443a9683a43650d61c6474b634b6ae6e1c6f097da6310bf/pyqt6_sip-13.10.2-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9128d770a611200529468397d710bc972f1dcfe12bfcbb09a3ccddcd4d54fa5b", size = 323488, upload-time = "2025-10-08T08:44:01.965Z" }, + { url = "https://files.pythonhosted.org/packages/8c/5b/8ede8d6234c3ea884cbd097d7d47ff9910fb114efe041af62b4453acd23b/pyqt6_sip-13.10.2-cp314-cp314-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d820a0fae7315932c08f27dc0a7e33e0f50fe351001601a8eb9cf6f22b04562e", size = 303881, upload-time = "2025-10-08T08:44:04.086Z" }, + { url = "https://files.pythonhosted.org/packages/be/44/b5e78b072d1594643b0f1ff348f2bf54d4adb5a3f9b9f0989c54e33238d6/pyqt6_sip-13.10.2-cp314-cp314-win_amd64.whl", hash = "sha256:3213bb6e102d3842a3bb7e59d5f6e55f176c80880ff0b39d0dac0cfe58313fb3", size = 55098, upload-time = "2025-10-08T08:44:08.943Z" }, + { url = "https://files.pythonhosted.org/packages/e2/91/357e9fcef5d830c3d50503d35e0357818aca3540f78748cc214dfa015d00/pyqt6_sip-13.10.2-cp314-cp314-win_arm64.whl", hash = "sha256:ce33ff1f94960ad4b08035e39fa0c3c9a67070bec39ffe3e435c792721504726", size = 46088, upload-time = "2025-10-08T08:44:10.014Z" }, ] [[package]] @@ -1694,6 +1701,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725, upload-time = "2019-09-20T02:06:22.938Z" }, ] +[[package]] +name = "pysubs2" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/4a/becf78d9d3df56e6c4a9c50b83794e5436b6c5ab6dd8a3f934e94c89338c/pysubs2-1.8.0.tar.gz", hash = "sha256:3397bb58a4a15b1325ba2ae3fd4d7c214e2c0ddb9f33190d6280d783bb433b20", size = 1130048, upload-time = "2024-12-24T12:39:47.769Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/09/0fc0719162e5ad723f71d41cf336f18b6b5054d70dc0fe42ace6b4d2bdc9/pysubs2-1.8.0-py3-none-any.whl", hash = "sha256:05716f5039a9ebe32cd4d7673f923cf36204f3a3e99987f823ab83610b7035a0", size = 43516, upload-time = "2024-12-24T12:39:44.469Z" }, +] + [[package]] name = "pytest" version = "8.4.2"