All notable changes to this project will be documented in this file.
This project adheres to Semantic Versioning.
Versions 3.0.0 and older use a format based on Keep a Changelog, but versions thereafter use a custom changelog format using git-cliff.
3.3.3 - 2024-05-07
- dl: Automatically convert TTML Subs to WebVTT for MKV support
- Subtitle: Correct timestamps when merging fragmented WebVTT
- env: List all directories as table in info
- env: List possible config path locations when not found
- binaries: Move all binary definitions to core/binaries file
- curl-impersonate: Remove manual fix for curl proxy SSL
- curl-impersonate: Update the default browser to chrome124
- Config: Move possible config paths out of func to constant
- utilities: Remove get_binary_path, use binaries.find instead
- dl: Improve readability of download worker errors
- env: Shorten paths on Windows with env vars
3.3.2 - 2024-04-16
- Video: Ensure track is supported in change_color_range()
- Video: Optionalise constructor args, add doc-string & checks
- Audio: Optionalise constructor args, add doc-string & checks
- Subtitle: Optionalise constructor args, add doc-string & checks
- HLS: Ensure playlist.stream_info.codecs exists before use
- HLS: Ensure playlist.stream_info.resolution exists before use
- env: List used config path, otherwise the default path
- cfg: Use loaded config path instead of hardcoded default
- Basic: Return None not Exception if no proxy configured
- Video: Do not print "?"/"Unknown" values in str()
- Audio: Do not print "?"/"Unknown" values in str()
- Subtitle: Do not print "?"/"Unknown" values in str()
- Audio: List lang after codec for consistency with other Tracks
- Video: Return None if no m3u RANGE, not SDR
- env: Use -- to indicate no config found/loaded
3.3.1 - 2024-04-05
- dl: Add new --workers to set download threads/workers
- Chapter: Cast values to int prior to formatting
- requests: Fix multithreaded downloads
- Events: Dereference subscription store from ephemeral store
- dl: Change --workers to --downloads
3.3.0 - 2024-04-02
- Add support for MKV Attachments via Attachment class
- dl: Automatically attach fonts used within SSAv4 subs
- dl: Try find SSAv4 fonts in System OS fonts folder
- Basic: Allow single string URIs for countries
- Basic: Allow proxy selection by index (one-indexed)
- Events: Add new global Event Observer API
- curl-impersonate: Set Cert-Authority Bundle for HTTPS Proxies
- Basic: Make query case-insensitive
- WVD: Ensure WVDs dir exists before moving WVD file
- WVD: Fix empty path to WVDs folder check
- WVD: Move log out of loop to save performance
- WVD: Move log with path before Device load
- WVD: Add exists/empty checks to WVD folder dumps
- Basic: Fix variable typo regression
- Basic: Improve proxy format checks
- WVD: Print error if path to parse doesn't exist
- WVD: Seperate logs in loop for visual clarity
- Track: Move from OnXyz callables to Event observer
3.2.0 - 2024-03-25
- ClearKey: Pass session not proxy str in from_m3u_key method
- Track: Allow Track to choose downloader to use
- search: New Search command, Service method, SearchResult Class
- dl: Include chapters when muxing
- aria2c: Support aria2(c) 1.37.0 by handling upstream regression
- MultipleChoice: Simplify super() call and value types
- dl: Add single mux job if there's no video tracks
- Track: Compute Track ID from the
this
variable, notself
- DASH/HLS: Don't merge folders, skip final merge if only 1 segment
- dl: Use click.command() instead of click.group()
- HLS: Remove save dir even if final merge wasn't needed
- Track: Fix order of operation mistake in get_track_name
- requests: Set HTTP pool connections/maxsize to max workers
- Video: Delete original file after using change_color_range()
- Video: Delete original file after using remove_eia_cc()
- requests: Manually compute default max_workers or pool size is None
- requests: Block until connection freed if too many connections
- HLS: Delete subtitle segments as they are merged
- HLS: Delete video/audio segments after FFmpeg merge
- ClearKey: Only use User-Agent if none set in from_m3u_key
- Track: Remove TERRITORY_MAP constant, trim SAR China manually
- Track: Default the track name to it's lang's script/territory
- Service: Go back to the default pool_maxsize in Session
3.1.0 - 2024-03-05
- cli: Implement MultipleChoice click param based on Choice param
- dl: Skip video lang filter if --v-lang unused & only 1 video lang
- dl: Change --vcodec default to None, use any codec
- dl: Support multiple -r/--range and mux ranges separately
- Subtitle: Convert from fTTML->TTML & fVTT->WebVTT post-download
- Track: Make ID optional, Automatically compute one if not provided
- Track: Add a name property to use for the Track Name
- dl: Have --sub-format default to None to keep original sub format
- HLS: Use filtered out segment key info
- Track: Don't modify lang when getting name
- Track: Don't use fallback values "Zzzz"/"ZZ" for track name
- version: The
__version__
variable forgot to be updated
- Move dl command's download_track() to Track.download()
- dl: Remove unused
get_profiles()
method - DASH: Move data values from track url to track data property
- DASH: Change how Video FPS is gotten to remove FutureWarning log
- Track: Add type checks, improve typing
- Track: Remove swap() method and it's uses
- Track: Remove unused DRM enum
- Track: Rename Descriptor's M3U & MPD to HLS & DASH
- Track: Remove unnecessary bool casting
- Track: Move the path class instance variable with the rest
- Track: Return new path on move(), raise exceptions on errors
- Track: Move delete and move methods near start of Class
- Track: Rename extra to data, enforce type as dict
- Explicitly use marisa-trie==1.1.0 for Python 3.12 wheels
3.0.0 - 2024-03-01
- Support for Python 3.12.
- Audio track's Codec Enum now has FLAC defined.
- The Downloader to use can now be set in the config under the downloader key.
- New Multi-Threaded Downloader,
requests
, that makes HTTP(S) calls using Python-requests. - New Multi-Threaded Downloader,
curl_impersonate
, that makes HTTP(S) calls using Curl-Impersonate via Curl_CFFI. - HLS manifests specifying a Byte range value without starting offsets are now supported.
- HLS segments that use
EXT-X-DISCONTINUITY
are now supported. - DASH manifests with SegmentBase or only BaseURL are now supported.
- Subtitle tracks from DASH manifests now automatically marked as SDH if
urn:tva:metadata:cs:AudioPurposeCS:2007 = 2
. - The
--audio-only/--subs-only/--chapters-only
flags can now be used simultaneously. For example,--subs-only
with--chapters-only
will get just Subtitles and Chapters. - Added
--video-only
flag, which can also still be simultaneously used with the only "only" flags. Using all four of these flags will have the same effect as not using any of them. - Added
--no-proxy
flag, disabling all uses of proxies, even if--proxy
is set. - Added
--sub-format
option, which sets the wanted output subtitle format, defaulting to SubRip (SRT). - Added
Subtitle.reverse_rtl()
method to use SubtitleEdit's/ReverseRtlStartEnd
functionality. - Added
Subtitle.convert()
method to convert the loaded Subtitle to another format. Note that you cannot convert to fTTML or fVTT, but you can convert from them. SubtitleEdit will be used in precedence over pycaption if available. Converting to SubStationAlphav4 requires SubtitleEdit, but you may want to manually alter the Canvas resolution after the download. - Added support for SubRip (SRT) format subtitles in
Subtitle.parse()
via pycaption. - Added
API
Vault Client aiming for a RESTful like API. - Added
Chapters
Class to hold the new reworkedChapter
objects, automatically handling stuff like order of the Chapters, Chapter numbers, loading from a chapter file or string, and saving to a chapter file or string. - Added new
chapter_fallback_name
config option allowing you to set a Chapter Name Template used when muxing Chapters into an MKV Container with MKVMerge. Do note, it defaults to no Chapter Fallback Name at all, but MKVMerge will forceChapter {i:02}
at least for me on Windows with the program language set to English. You may want to instead useChapter {j:02}
which will doChapter 01, Intro, Chapter 02
instead ofChapter 01, Intro, Chapter 03
(an Intro is not a Chapter of story, but it is the 2nd Chapter marker, so It's up to you how you want to interpret it). - Added new
Track.OnSegmentDownloaded
Event, called any time one of the Track's segments were downloaded. - Added new
Subtitle.OnConverted
Event, called any time that Subtitle is converted. - Implemented
__add__
method toTracks
class, allowing you to add to the first Tracks object. For example, making it handy to merge HLS video tracks with DASH tracks,tracks = dash_tracks + hls_tracks.videos
, or for iterating:for track in dash.videos + hls.videos: ...
. - Added new utility
get_free_port()
to get a free local port to use, though it may be taken by the time it's used.
- Moved from my forked release of pymp4 (
rlaphoenix-pymp4
) back to the originalpymp4
release as it is now up-to-date with some of my needed fixes. - The DASH manifest is now stored in the Track
url
property to be reused byDASH.download_track()
. - Encrypted DASH streams are now downloaded in full and then decrypted, instead of downloading and decrypting each individual segment. Unlike HLS, DASH cannot dynamically switch out the DRM/Protection information. This brings both CPU and Disk IOPS improvements, as well as fixing rare weird decryption anomalies like broken or odd timestamps, decryption failures, or broken a/v continuity.
- When a track is being decrypted, it now displays "Decrypting" and afterward "Decrypted" in place of the download speed.
- When a track finishes downloaded, it now displays "Downloaded" in place of the download speed.
- When licensing is needed and fails, the track will display "FAILED" in place of the download speed. The track download will cancel and all other track downloads will be skipped/cancelled; downloading will end.
- The fancy smart quotes (
“
and”
) are now stripped from filenames. - All available services are now listed if you provide an invalid service tag/alias.
- If a WVD file fails to load and looks to be in the older unsupported v1 format, then instructions on migrating to v2 will be displayed.
- If Shaka-Packager prints an error (i.e.,
:ERROR:
log message) it will now raise asubprocess.CalledProcessError
exception, even if the process return code is 0. - The Video classes' Primaries, Transfer, and Matrix classes had changes to their enum names to better represent their values and uses. See the changed names in the commit.
- SubRip (SRT) Subtitles no longer have the
MULTI-LANGUAGE SRT
header forcefully removed. The root cause of the error was identified and fixed in this release. - Since
Range.Transfer.SDR_BT_601_625 = 5
has been removed,Range.from_cicp()
now internally remaps CICP transfer values of5
to6
(which is nowRange.Transfer.BT_601 = 6
). - Referer and User-Agent Header values passed to the aria2(c) downloader is now set via the dedicated
--referer
and--user-agent
options respectively, instead of--header
. - The aria2(c)
-j
,-x
, and-s
option values can now be set by the config under thearia2c
key in the options' full names. - The aria2(c)
-x
, and-s
option values now use aria2(c)'s own default values for them instead of16
. Thej
option value defaults to ThreadPoolExecutor's algorithm ofmin(32,(cpu_count+4))
. - The download progress bar now states
LICENSING
on the speed text when licensing DRM, andLICENSED
once finished. - The download progress bar now states
CANCELLING
/CANCELLED
on the speed text when cancelling downloads. This is to make it more clear that it didn't just stop, but stopped as it was cancelled. - The download cancel/skip events were moved to
constants.py
so it can be used across the codebase easier without argument drilling.DL_POOL_STOP
was renamed toDOWNLOAD_CANCELLED
andDL_POOL_SKIP
toDOWNLOAD_LICENCE_ONLY
. - The Cookie header is now calculated for each URL passed to the aria2(c) downloader based on the URL. Instead of passing every single cookie, which could have two cookies with the same name aimed for different host names, we now pass only cookies intended for the URL.
- The aria2(c) process no longer prints output to the terminal directly. Devine now only prints contents of the captured log messages to the terminal. This allows filtering out of errors and warnings that isn't a problem.
- DASH and HLS no longer download segments silencing errors on all but the last retry as the downloader rework makes this unnecessary. The errors will only be printed on the final retry regardless.
Track.repackage()
now saves as{name}_repack.{ext}
instead of{name}.repack.{ext}
.Video.change_color_range()
now saves as{name}_{limited|full}_range.{ext}
instead of{name}.range{0|1}.{ext}
.Widevine.decrypt()
now saves as{name}_decrypted.{ext}
instead of{name}.decrypted.{ext}
.- Files starting with the save path's name and using the save path's extension, but not the save path, are no longer deleted on download finish/stop/failure.
- The output container format is now explicitly specified as
MP4
when callingshaka-packager
. - The default downloader is now
requests
instead ofaria2c
to reduce required external dependencies. - Reworked the
Chapter
class to only hold a timestamp and name value with an ID automatically generated as a CRC32 of the Chapter representation. - The
--group
option has been renamed to--tag
. - The config file is now read from three more locations in the following order:
- The Devine Namespace Folder (e.g.,
%appdata%/Python/Python311/site-packages/devine/devine.yaml
). - The Parent Folder to the Devine Namespace Folder (e.g.,
%appdata%/Python/Python311/site-packages/devine.yaml
). - The AppDirs User Config Folder (e.g.,
%localappdata%/devine/devine.yaml
). Location 2 allows having a config at the root of a portable folder.
- The Devine Namespace Folder (e.g.,
- An empty config file is no longer created when no config file is found.
- You can now set a default cookie file for a Service, see README.
- You can now set a default credential for a Service, see config.
- Services are now auth-less by default and the error for not having at least a cookie or credential is removed.
Cookies/Credentials will only be loaded if a default one for the service is available, or if you use
-p/--profile
and the profile exists. - Subtitles when converting to SubRip (SRT) via SubtitleEdit will now use the
/ConvertColorsToDialog
option. - HLS segments are now merged by discontinuity instead of all at once. The merged discontinuities are then finally
merged to one file using
ffmpeg
. Doing the final merge by byte concatenation did not work for some playlists. - The Track is no longer passed through Event Callables. If you are able to set a function on an Even Callable, then you should have access to the track reference to call it directly if needed.
- The Track.OnDecrypted event callable is now passed the DRM and Segment objects used to Decrypt. The segment object is only passed from HLS downloads.
- The Track.OnDownloaded event callable is now called BEFORE decryption, right after downloading, not after decryption.
- All generated Track ID values across the codebase has moved from md5 to crc32 values as code processors complain about its use surrounding security, and it's length is too large for our use case anyway.
- HLS segments are now downloaded multi-threaded first and then processed in sequence thereafter.
- HLS segments are no longer decrypted one-by-one, requiring a lot of shaka-packager processes to run and close. They now merged and decrypt in groups based on their EXT-X-KEY, before being merged per discontinuity.
- The DASH and HLS downloaders now pass multiple URLs to the downloader instead of one-by-one, heavily increasing speed and reliability as connections are kept alive and re-used.
- Downloaders now yield back progress information in the same convention used by
rich
'sProgress.update()
method. DASH and HLS now pass the yielded information to their progress callable instead of passing the progress callable to the downloader. - The aria2(c) downloader now uses the aria2(c) JSON-RPC interface to query for download progress updates instead of parsing the stdout data in an extremely hacky way.
- The aria2(c) downloader now re-routes non-HTTP proxies via
pproxy
by a subprocess instead of the now-removedstart_pproxy
utility. This way has proven to be easier, more reliable, and prevents pproxy from messing with rich's terminal output in strange ways. - All downloader function's have an altered signature but ultimately similar.
uri
tourls
,out
(path) was removed, we now calculate the save path by passing anoutput_dir
andfilename
. Thesilent
,segmented
, andprogress
parameters were completely removed. - All downloader
urls
can now be a string or a dictionary containing extra URL-specific options to use like URL-specific headers. It can also be a list of the two types of URLs to downloading multi-threaded. - All downloader
filenames
can be a static string, or a filename string template with a few variables to use. The template system used is f-string, e.g.,"file_{i:03}{ext}"
(ext starts with.
if there's an extension). - DASH now updates the progress bar when merging segments.
- The
Widevine.decrypt()
method now also searches for shaka-packager as justpackager
as it is the default build name. (#74)
- The
devine auth
command and sub-commands due to lack of support, risk of data, and general quirks with it. - Removed
profiles
config, you must now specify which profile you wish to use each time with-p/--profile
. If you use a specific profile a lot more than others, you should make it the default. - The
saldl
downloader has been removed as their binary distribution is whack and development has seemed to stall. It was only used as an alternative to what was at the time the only downloader, aria2(c), as it did not support any form of Byte Range, butsaldl
did, which was crucial for resuming extremely large downloads or complex playlists. However, now we have the requests downloader which does support the Range header. - The
Track.needs_proxy
property was removed for a few design architectural reasons.- Design-wise it isn't valid to have --proxy (or via config/otherwise) set a proxy, then unpredictably have it
bypassed or disabled. If I specify
--proxy 127.0.0.1:8080
, I would expect it to use that proxy for all communication indefinitely, not switch in and out depending on the track or service. - With reason 1, it's also a security problem. The only reason I implemented it in the first place was so I could download faster on my home connection. This means I would authenticate and call APIs under a proxy, then suddenly download manifests and segments e.t.c under my home connection. A competent service could see that as an indicator of bad play and flag you.
- Maintaining this setup across the codebase is extremely annoying, especially because of how proxies are setup/used by Requests in the Session. There's no way to tell a request session to temporarily disable the proxy and turn it back on later, without having to get the proxy from the session (in an annoying way) store it, then remove it, make the calls, then assuming your still in the same function you can add it back. If you're not in the same function, well, time for some spaghetti code.
- Design-wise it isn't valid to have --proxy (or via config/otherwise) set a proxy, then unpredictably have it
bypassed or disabled. If I specify
- The
Range.Transfer.SDR_BT_601_625 = 5
key and value has been removed as I cannot find any official source to verify it as the correct use. However, usually atransfer
value of5
would be PAL SD material so it better matches6
, which is (now named)Range.Transfer.BT_601 = 6
. If you have something specifying transfer=5, just remap it to 6. - The warning log
There's no ... Audio Tracks, likely part of an invariant playlist, continuing...
message has been removed. So long as your playlist is expecting no audio tracks, or the audio is part of the video transport, then this wouldn't be a problem whatsoever. Therefore, having it log this annoying warning all the time is pointless. - The
--min-split-size
argument to the aria2(c) downloader as it was only used to disable splitting on segmented downloads, but the newer downloader system wouldn't really need or want this to be done. If aria2 has decided based on its other settings to have split a segment file, then it likely would benefit from doing so. - The
--remote-time
argument from the aria2(c) downloader as it may need to do a GET and a HEAD request to get the remote time information, slowing the download down. We don't need this information anyway as it will likely be repacked withffmpeg
or multiplexed withmkvmerge
, discarding/losing that information. - DASH and HLS's 5-attempt retry loop as the downloaders will retry for us.
- The
start_pproxy
utility has been removed as all uses of it now callpproxy
via subprocess instead. - The
LANGUAGE_MUX_MAP
constant and it's usage has been removed as it is no longer necessary as of MKVToolNix v54.
- Uses of
__ALL__
with Class objects have been correct to__all__
with string objects, following PEP8. - Fixed value of URL passed to
Track.get_key_id()
as it was a tuple rather than the URL string. - The
--skip-dl
flag now works again after breaking in v1.3.0. - Move WVD file to correct location on new installations in the
wvd add
command. - Cookie data is now passed to downloaders and use URLs based on the URI it will be used for, just like a browser.
- Failure to get FPS in DASH when SegmentBase isn't used.
- An error message is now returned if a WVD file fails to load instead of raising an exception.
- Track language information within M3U playlists are now validated with langcodes before use. Some manifests use the property for arbitrary data that their apps/players use for their own purposes.
- Attempt to fix non-UTF-8 and mixed-encoding Subtitle downloads by automatically converting to UTF-8. (#43) Decoding is attempted in the following order: UTF-8, CP-1252, then finally chardet detection. If it's neither UTF-8 nor CP-1252 and chardet could not detect the encoding, then it is left as-is. Conversion is done per-segment if the Subtitle is segmented, unless it's the fVTT or fTTML formats which are binary.
- Chapter Character Encoding is now explicitly set to UTF-8 when muxing to an MKV container as Windows seems to default to latin1 or something, breaking Chapter names with any sort of special character within.
- Subtitle passed through SubtitleEdit now explicitly use UTF-8 character encoding as it usually defaulted to UTF-8 with Byte Order Marks (aka UTF-8-SIG/UTF-8-BOM).
- Subtitles passed through SubtitleEdit now use the same output format as the subtitle being processed instead of SRT.
- Fixed rare infinite loop when the Server hosting the init/header data/segment file responds with a
Content-Length
header with a value of0
or smaller. - Removed empty caption lists/languages when parsing Subtitles with
Subtitle.parse()
. This stopped conversions to SRT containing theMULTI-LANGUAGE SRT
header when there was multiple caption lists, even though only one of them actually contained captions. - Text-based Subtitle formats now try to automatically convert to UTF-8 when run through
Subtitle.parse()
. - Text-based Subtitle formats now have
‎
and‏
HTML entities unescaped post-download as some rendering libraries seems to not decode them for us. SubtitleEdit also has problems with/ReverseRtlStartEnd
unless it's already decoded. - Fixed two concatenation errors surrounding DASH's BaseURL, sourceURL, and media values that start with or use
../
. - Fixed the number values in the
Newly added to x/y Vaults
log, which now statesCached n Key(s) to x/y Vaults
. - File write handler now flushes after appending a new segment to the final save path or checkpoint file, reducing memory usage by quite a bit in some scenarios.
2.2.0 - 2023-04-23
Since -q/--quality
has been reworked to support specifying multiple qualities, the type of this value is
no longer None|int
. It is now list[int]
and the list may be empty. It is no longer ever a None
value.
Please make sure any Service code that uses quality
via ctx.parent.params
reflects this change. You may
need to go from an if quality: ...
to for res in quality: ...
, or such. You may still use if quality
to check if it has 1 or more resolution specified, but make sure that the code within that if tree supports
more than 1 value in the quality
variable, which is now a list. Note that the list will always be in
descending order regardless of how the user specified them.
- Added the ability to specify and download multiple resolutions with
-q/--quality
. E.g.,-q 1080p,720p
. - Added support for DASH manifests that use SegmentList with range values on the Initialization definition (#47).
- Added a check for
uuid
mp4 boxes containingtenc
box data when getting the Track's Key ID to improve chances of finding a Key ID.
- The download path is no longer printed after each download. The simple reason is it felt unnecessary. It filled up a fair amount of vertical space for information you should already know.
- The logs after a download finishes has been split into two logs. One after the actual downloading process and the other after the multiplexing process. The downloading process has its own timer as well, so you can see how long the downloads itself took.
- I've switched from using the official pymp4 (for now) with my fork. At the time this change was made the original bearypig pymp4 repo was stagnant and the PyPI releases were old. I forked it, added some fixes by TrueDread and released my own update to PyPI, so it's no longer outdated. This was needed for some mp4 box parsing fixes. Since then the original repo is no longer stagnant, and a new release was made on PyPI. However, my repo still has some of TrueDread's fixes that is not yet on the original repository nor on PyPI.
- Removed the
with_resolution
method in the Tracks class. It has been replaced withby_resolutions
. The new replacement method supports getting all or n amount of tracks by resolution instead of the original always getting all tracks by resolution. - Removed the
select_per_language
method in the Tracks class. It has been replaced withby_language
. The new replacement method supports getting all or n amount of tracks by language instead of the original only able to get one track by language. It now defaults to getting all tracks by language.
- Prevented some duplicate Widevine tree logs under specific edge-cases.
- The Subtitle parse method no longer absorbs the syntax error message.
- Replaced all negative size values with 0 on TTML subtitles as a negative value would cause syntax errors.
- Fixed crash during decryption when shaka-packager skips decryption of a segment as it had no actual data and was just headers.
- Fixed CCExtractor crash in some scenarios by repacking the video stream prior to extraction.
- Fixed rare crash when calculating download speed of DASH and HLS downloads where a segment immediately finished after the previous segment. This seemed to only happen on the very last segment in rare situations.
- Fixed some failures parsing
tenc
mp4 boxes when obtaining the track's Key ID by using my own fork of pymp4 with up-to-date code and further fixes. - Fixed crashes when parsing some
tenc
mp4 boxes by simply skippingtenc
boxes that fail to parse. This happens because some services seem to mix up the data of thetenc
box with that of another type of box. - Fixed using invalid
tenc
boxes by skipping ones with a version number greater than 1.
2.1.0 - 2023-03-16
- The Track get_init_segment method has been re-written to be more controllable. A specific Byte-range, URL, and
maximum size can now be specified. A manually specified URL will override the Track's current URL. The Byte-range
will override the fallback value of
0-20000
(where 20000 is the defaultmaximum_size
). It now also checks if the server supports Byte-range, or it will otherwise stream the response. It also tries to get the file size length and uses that instead ofmaximum_size
unless it's bigger thanmaximum_size
. - Added new
get_key_id
method to Track to probe the track for a track-specific Encryption Key ID. This is similar to Widevine'sfrom_track
method but ignores allpssh
boxes and manifest information as the information within those could be for a wider range of tracks or not for that track at all. - Added a 5-attempt retry system to DASH and HLS downloads. URL downloads only uses aria2(c)'s built in retry system which has the same amount of tries and same delay between attempts. Any errors emitted when downloading segments will not be printed to console unless it occurred on the last attempt.
- Added a fallback way to obtain language information by taking it from the representation ID value, which may have the
language code within it. E.g.,
audio_en=128000
would be an English audio track at 128kb/s. We now take theen
from that ID where possible. - Added support for 13-char JS-style timestamp values to the Cacher system.
- Improved Forced Subtitle recognition by checking for both
forced-subtitle
andforced_subtitle
(#43).
- The
*
symbol is no longer spaced after the WidevineKID:KEY
when denoting that it is for this specific PSSH. This reduces wasted vertical space. - The "aria2 will resume download if the transfer is restarted" logs that occur when aria2(c) handles the CTRL+C break, and "If there are any errors, then see the log file" logs are now ignored and no longer logged to the console.
- DASH tracks will no longer prepare and license DRM unless it's just about to download. This is to reduce unnecessary preparation of DRM if the track had been converted to a URL download.
- For a fix listed below, we now use a fork of https://github.com/globocom/m3u8 that fixes a glaring problem with the EXT-X-KEY parsing system. See globocom/m3u8#313.
- The return code when mkvmerge returns an error is now logged with the error message.
- SubtitleEdit has been silenced when using it for SDH stripping.
- Fixed URL joining and Base URL calculations on DASH manifests that use multiple Base URL values.
- URL downloads will now store the chosen DRM before preparing and licensing with the DRM.
- URL downloads will now prepare and license with the DRM if the Track has pre-existing DRM information. Previously it would only prepare and license DRM if it did not pre-emptively have DRM information before downloading.
- The
*
symbol that indicates that the KID:KEY is for the track being downloaded now uses the newget_key_id
method of the track for a more accurate reading. - License check now ensures if a KEY was returned for the Track instead of all KIDs of the Track's PSSH. This prevents an issue where the PSSH may have Key IDs for a 720p and 1080p track, yet only a KEY for the 720p track was returned. It would have then raised an error and stopped the download, even though you are downloading the 720p track and not the 1080p track, therefore the error was irrelevant.
- Unnecessary duplicate license calls are now prevented in some scenarios where
--cdm-only
is used. - Fixed accuracy and speed of preparing and licensing DRM on HLS manifests where multiple EXT-X-KEY definitions appear in the manifest throughout the file. Using globocom/m3u8#313 we can now accurately get a list of EXT-X-KEYs mapped to each segment. This is a game changer for HLS manifests that use unique keys for every single (or most) segments as it would have otherwised needed to initialize (and possibly do network requests) for 100s of EXT-X-KEY information, per segment. This caused downloads of HLS manifests that used a unique key per segment to slow to a binding crawl, and still not even decrypt correctly as it wouldn't be able to map the correct initialized key to the correct segment.
- Fixed a regression that incorrectly implemented the OnMultiplex event for Audio and Subtitle tracks causing them to never trigger. It would instead accidentally have trigger the last Video track's OnMultiplex event instead of the Audio or Subtitle's event.
- The above fix also fixed the automatic SDH stripping subtitle. Any automatically created SDH->non-SDH subtitle from prior downloads would not have actually had SDH captions stripped, it would instead be a duplicate subtitle.
2.0.1 - 2023-03-07
- Re-added logging support for shaka-packager on errors and warnings. Do note that INFO logs and the 'Insufficient bits in bitstream for given AVC profile' warning logs are ignored and never printed.
- Added new exceptions to the Widevine DRM class,
CEKNotFound
andEmptyLicense
. - Added support for Byte-ranges on HLS init maps.
- Now lists the full 'Episode #' text when listing episode titles without an episode name.
- Subprocess exceptions from a download worker no longer prints a traceback. It now only logs the return code. This is because all subprocess errors during a download is now logged, therefore the full traceback is no longer necessary.
- Aria2(c) no longer pre-allocates file space if segmented. This is to reduce generally unnecessary upfront I/O usage.
- The Widevine DRM class's
get_content_keys
method now raises the newCEKNotFound
andEmptyLicense
exceptions notValueError
exceptions. - The prepare_drm code now raises exceptions where needed instead of
sys.exit(1)
. Callees do not need to make any changes. The exception should continue to go up the call stack and get handled by thedl
command.
- Fixed regression that broke support for pproxy. Do note that while pproxy has wheel's for Python 3.11+, it seems to be broken. I recommend using Python 3.10 or older for now. See qwj/python-proxy#161.
- Fixed regression and now store the chosen DRM object back to the track.drm field. Please note that using the track DRM field in Service code is not recommended, but for some services it's simply required.
- Fixed regression since v1.4.0 where the byte-range calculation was actually slightly off one on the right-side range. This was a one-indexed vs. zero-indexed problem. Please note that this could have affected the integrity of HLS downloads if they used EXT-X-BYTERANGE.
- Fixed possible soft-lock in HLS if the Queue for previous segment key and init data gets stuck in an empty state over an exception in a download thread. E.g., if a thread takes the previous segment key, throws an exception, and did not get the chance to give it back for the next thread.
- The prepare_drm function now handles unexpected exceptions raised in the Service's license method. This code would of otherwise been absorbed and the download would have soft-locked.
- Prevented a double-licensing call race-condition on HLS tracks by using a threading lock when preparing DRM information. This is not required in DASH, as it prepares DRM on the main thread, once, not per-segment.
- Fixed printing of aria2(c) logs when redirecting progress information to rich progress bars.
- Explicitly mark DASH and HLS aria2(c) downloads as segmented.
- Fixed listing of episode titles without an episode name.
- Fixed centering of the project URL in the ASCII banner.
- Removed the accidental double-newline after the ASCII banner.
2.0.0 - 2023-03-01
This release brings a huge change to the fundamentals of Devine's logging, UI, and UX.
- Add new dependency rich for advanced color and logging capabilities.
- Set rich console output color scheme to the Catppuccin Mocha theme.
- Add full download cancellation support by using CTRL+C. Track downloads will now be marked as STOPPED if you press CTRL+C to stop the download, or FAILED if any unexpected exception occurs during a download. The track will be marked as SKIPPED if the download stopped or failed before it got a chance to begin. It will print a download cancelled message if downloading was stopped, or a download error message if downloading failed. It will print the first download error traceback with rich before stopping.
- Downloads will now automatically cancel if any track or segment download fails.
- Implement sub-commands
add
anddelete
to thewvd
command for adding and deleting WVD (Widevine Device) files to and from the configured WVDs directory (#31). - Add new config option to disable the forced background color. You may want to disable the purple background if you're terminal isn't able to apply it correctly, or you prefer to use your own terminal's background color.
- Create
ComfyConsole
,ComfyLogRenderer
, andComfyRichHandler
. These are hacky classes to implement padding to the left and right of all rich console output. This gives devine a comfortable and freeing look-and-feel. - An ASCII banner is now displayed at the start of software execution with the version number.
- Add rich status output to various parts of the download process. It's also used when checking GEOFENCE within the base Service class. I encourage you to follow similar procedures where possible in Service code. This will result in cleaner log output, and overall less logs being made when finished.
- Add three rich horizontal rules to separate logs during the download process. The Service used, the Title received
from
get_titles()
, and then the Title being downloaded. This helps identify which logs are part of which process. - Add new
tree
methods toSeries
,Movies
, andAlbum
classes to list items within the objects with Rich Tree. This allows for more rich console output when displaying E.g., Seasons and Episodes within a Series, or Songs within an Album. - Add new
tree
method to theTracks
class to list the tracks received fromget_tracks()
with Rich Tree. Similar to the change just above, this allows for more rich console output. It has replaced theTracks.print()
method. - Add a rich progress bar to the track multiplexing operation.
- Add a log when a download finishes, how long it took, and where the final muxed file was moved to.
- Add a new track event,
OnMultiplex
. This event is run prior to multiplexing the finalized track data together. Use this to run code once a track has finished downloading and all the post-download operations. - Add support for mapping Netflix profiles beginning with
h264
to AVC. E.g., the new -QC profiles. - Download progress bars now display the download speed. It displays in decimal (^1024) size. E.g., MB/s.
- If a download stops or fails, any residual file that may have been downloaded in an incomplete OR complete state will now be deleted. Download continuation is not yet supported, and this will help to reduce leftover stale files.
- The logging base config now has
ComfyRichHandler
as its log handler for automatic rich console output when using the logging system. - The standard
traceback
module has been overridden withrich.traceback
for styled traceback output. - Only the rich console output is now saved when using
--log
. - All
tqdm
progress bars have been replaced with rich progress bars. The rich progress bars are now displayed under each track tree. - The titles are now only listed if
--list-titles
is used. Otherwise, only a brief explanation of what it received fromget_titles()
will be returned. E.g., for Series it will list how many seasons and episodes were received. - Similarly, all available tracks are now only listed if
--list
is used. This is to reduce unnecessary prints, and to separate confusion between listings of available tracks, and listings of tracks that are going to be downloaded. - Listing all available tracks with
--list
no longer continues execution. It now stops after the first list. If you want to list available tracks for a specific title, use-w
in combination with--list
. - The available tracks are now printed in a rich panel with a header denoting the tracks as such.
- The
Series
,Movies
, andAlbum
classes now have a much more simplified string representation. They now simply state the overarching content within them. E.g., Series says the title and year of the TV Show. - The final log when all titles are processed is now a rich log and states how long the entire process took.
- Widevine DRM license information is now printed below the tracks as a rich tree.
- The CCExtractor process, Subtitle Conversion process, and FFmpeg Repacking process were all moved out of the track download function (and therefore the thread) to be done on the main thread after downloading. This improves download speed as the threads can close and be freed quicker for the next track to begin.
- The CCExtractor process is now optional and will be skipped if the binary could not be found. An error is still logged in the cases where it would have run.
- The execution point of the
OnDownloaded
event has been moved to directly run after the stream has been downloaded. It used to run after all the post-download operations finished like CCExtractor, FFmpeg Repacking, and Subtitle Conversion. - The automatic SDH-stripped subtitle track now uses the new
OnMultiplex
event instead ofOnDownloaded
. This is to account for the previous change as it requires the subtitle to be first converted to SubRip to support SDH-stripping. - Logs during downloads now appear before the downloading track list. This way it isn't constantly interrupting view of the progress.
- Now running aria2(c) with normal subprocess instead of through asyncio. This removes the creation of yet another thread which is unnecessary as these calls would have already been under a non-main thread.
- Moved Widevine DRM licensing calls before the download process for normal URL track downloads.
- Segment Merging code for DASH and HLS downloads have been moved from the
dl
class to the HLS and DASH class.
- Remove explicit dependency on
coloredlogs
andcolorama
as they are no longer used by devine itself. - Remove dependency
tqdm
as it was replaced with rich progress bars. - Remove now-unused logging constants like the custom log formats.
- Remove
Tracks.print()
function as it was replaced with the newTracks.tree()
function. - Remove unnecessary sleep calls at the start of threads. This was believed to help with the download stop event check but that was not the case. It instead added an artificial delay with downloads.
- Fix another crash when using devine without a config file. It now creates the directory of the config file before making a new config file.
- Set the default aria2(c) file-allocation to
prealloc
like stated in the config documentation. It usesprealloc
as the default, asfalloc
is generally unsupported in most scenarios, so it's not a good default. - Correct the config documentation in regard to
proxies
now being calledproxy_providers
, andbasic
actually being adict
of lists, and not adict
of strings.
1.4.0 - 2023-02-25
- Add support for byte-ranged HLS and DASH segments, i.e., HLS EXT-X-BYTERANGE and DASH SegmentBase. Byte-ranged segments will be downloaded using python-requests as aria2(c) does not support byte ranges.
- Added support for data URI scheme in ClearKey DRM, including support for the base64 extension.
- Increase the urllib3 connection pool max size from the default 10 to 16 * 2. This is to accommodate up to 16 byte-ranged segment downloads while still giving enough room for a few other connections.
- The urllib3 connection pool now blocks and waits if it's full. This removes the Connection Pool Limit warnings when downloading more than one byte-ranged segmented track at a time.
- Moved
--log
from thedl
command to the entry command to allow logging of more than just the download command. With this change, the logs now include the initial root logs, including the version number. - Disable the urllib3 InsecureRequestWarnings as these seem to occur when using HTTP+S proxies when connecting to an HTTPS URL. While not ideal, we can't solve this problem, and the warning logs are quite annoying.
- Remove the
byte_range
parameter from the aria2(c) downloader that was added in v1.3.0 as it turns out it doesn't actually work. Theoretically it should, but it seems aria2(c) doesn't honor the Range header correctly and fails.
- Fix the JOC check on HLS playlists to check if audio channels are defined first.
- Fix decryption of AES-encrypted segments that are not pre-padded to AES-CBC boundary size (16 bytes).
- Fix the order of segment merging on Linux machines. On Windows, the
pathlib.iterdir()
function is always in order. However, on Linux, or at least some machines, this was not the case. - Fix printing of the traceback when a download worker raises an unexpected exception.
- Fix initial creation of the config file if none was created yet.
1.3.1 - 2023-02-23
- Fixed a regression where the
track.path
was only updated forDescriptor.URL
downloads if it had DRM. This caused downloads of subtitles or DRM-free tracks using theURL
descriptor to be broken (#33). - Fixed a regression where
title
andtrack
were not passed to the Service's functions for getting Widevine Service Certificates and Widevine Licenses. - Corrected the Cookie Path that was logged when adding cookies with
devine auth add
. - The Config data is now defaulted to an empty dictionary when completely empty or non-existent. This fixes a crash if
you try to use
devine auth add
without a config file.
1.3.0 - 2023-02-22
- Support for Python 3.8 has been dropped. Support for Windows 7 ended in January 2020.
- Although Python 3.8 is the last version with support for Windows 7, the decision was made to drop support because the number of affected users would be low.
- You may be interested in https://github.com/adang1345/PythonWin7, which has newer installers with patched support.
- Segmented HLS and DASH downloads now provide useful progress information using TQDM. Previously, aria2c would print progress information, but it was not very useful for segmented downloads due to how the information was presented.
- Segmented HLS and DASH downloads are now manually multi-threaded in a similar way to aria2c's
--j=16
. - A class-function was added to the Widevine DRM class to obtain PSSH and KID information from init data by looking for PSSH and TENC boxes. This is an alternative to the from_track class-function when you only have the init data and not a track object.
- Aria2c now has the ability to silence progress output and provide extra arguments.
- The downloading system for HLS and DASH has been completely reworked. It no longer downloads segments, merges them, and then decrypts. Instead, it now downloads and decrypts each individual segment. It dynamically switches DRM and Init Data per-segment where needed, fully supporting multiple EXT-X-KEY, EXT-X-MAP, and EXT-X-DISCONTINUITY tags in HLS. You can now download DRM-encrypted and DRM-free segments from within the same manifest, as well as manifests with unique DRM per-segment. None of this was possible with the old method of downloading.
- If a HLS manifest or segment uses an EXT-X-KEY with the method of NONE, it is assumed that the manifest or segment is DRM-free. This behavior applies even if the manifest or segment has other EXT-X-KEY methods specified, as that would be a mistake in the manifest.
- HLS now uses the proxy when loading AES-128 DRM as ClearKey objects, which is required for some services. It will
only be used if
Track.needs_proxy
is True. - The Widevine and ClearKey DRM classes decrypt functions no longer ask for a track. Instead, they ask for an input file path to which it will decrypt. It will automatically delete the input file and put the decrypted data in its place.
- The AtomicSQL utility was removed because it did not actually assist in making the SQL connections thread-safe. It helped, but in an almost backwards and over-thought approach.
- The Cacher expiration check now uses your local datetime timestamp over the UTC timestamp, which seems to have fixed early or late expiration if you are not at exactly UTC+00:00.
- The cookies file path is now checked to exist if supplied with the
--cookies
argument (#30). - An error is now logged, and execution will end if none of the DRM for a HLS manifest or segment is supported.
- HLS now only loads AES-128 EXT-X-KEY methods as ClearKey DRM because it currently only supports AES-128.
- AtomicSQL was replaced with connection factory systems using thread-safe storage for SQL connections. All Vault SQL calls are now fully thread-safe.
1.2.0 - 2023-02-13
- This release marks the end of support for Python 3.8.x.
- Although version 1.0.0 was intended to support Python 3.8.x, PyCharm failed to warn about a specific type annotation incompatibility. As a result, I was not aware that the support was not properly implemented.
- This release adds full support for Python 3.8.x, but it will be the only release with such support.
- The
dl
command CLI now includes Bitrate Selection options:-vb/--vbitrate
and-ab/--abitrate
. - The
dl
command CLI now includes an Audio Channels Selection option:-c/--channels
. - If a download worker fails abruptly, a full traceback will now be printed.
- The aria2c downloader has a new parameter for downloading a specific byte range.
- The usage of
Path.with_stem
withPath.with_suffix
has been simplified toPath.with_name
. - When printing audio track information, the assumption that the audio is
2.0ch
has been removed. - If audio channels were previously set as an integer value, they are no longer transformed as e.g.,
6ch
and now follow the normal behavior of being defined as a float value, e.g.,6.0
. - Audio channels are now explicitly parsed as float values, therefore parsing of values such as
16/JOC
(HLS) is no longer supported. The HLS manifest parser now assumes the track to be5.1ch
if the channels value is set to.../JOC
.
- Support for Python
>=3.8.6,<3.9.0
has been fixed. - The final fallback FPS value is now only obtained from the SegmentBase's timescale value if it exists.
- The FutureWarning that occurred when getting Segment URLs from SegmentTemplate DASH manifests has been removed.
- The HLS manifest parser now correctly sets the audio track's
joc
parameter. - Some Segmented WEBVTT streams may have included the WEBVTT header data when converting to SubRip SRT. This issue has been fixed by separating the header from any previous caption before conversion.
- The DASH manifest parser now uses the final redirected URL as the manifest URI (#25).
- File move operations from or to different drives (e.g., importing a cookie from another drive in
auth add
) (#27).
1.1.0 - 2023-02-07
- Added utility to change the video range flag between full(pc) and limited(tv).
- Added utility to test decoding of video and audio streams using FFmpeg.
- Added CHANGELOG.md
- The services and profiles listed by
auth list
are now sorted alphabetically. - An explicit error is now logged when adding a Cookie to a Service under a duplicate name.
- Corrected the organization name across the project from
devine
todevine-dl
asdevine
was taken. - Fixed startup crash if the config was not yet created or was blank.
- Fixed crash when using the
cfg
command to set a config option on new empty config files. - Fixed crash when loading key vaults during the
dl
command. - Fixed crash when using the
auth list
command when you do not have aCookies
data directory. - Fixed crash when adding a Cookie using
auth add
to a Service that has no directory yet. - Fixed crash when adding a Credential using
auth add
when it's the first ever credential, or first for the Service.
1.0.0 - 2023-02-06
Initial public release under the name Devine.