From 879a318e549782265f668b1365ab86ea10efd98a Mon Sep 17 00:00:00 2001 From: ymir Date: Sat, 31 Jan 2026 11:10:33 +0000 Subject: [PATCH] update yt-dlp and fix some bugs with TPE2. Fixes #3 --- .env/share/bash-completion/completions/yt-dlp | 2 +- .env/share/doc/yt_dlp/README.txt | 117 +++++++++++------- .../fish/vendor_completions.d/yt-dlp.fish | 3 +- .env/share/man/man1/yt-dlp.1 | 103 ++++++++++----- .env/share/zsh/site-functions/_yt-dlp | 2 +- make_folders.py | 38 ++++-- 6 files changed, 174 insertions(+), 91 deletions(-) diff --git a/.env/share/bash-completion/completions/yt-dlp b/.env/share/bash-completion/completions/yt-dlp index 2fb4447..1c215e6 100644 --- a/.env/share/bash-completion/completions/yt-dlp +++ b/.env/share/bash-completion/completions/yt-dlp @@ -4,7 +4,7 @@ __yt_dlp() COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" - opts="--help --version --update --no-update --update-to --ignore-errors --no-abort-on-error --abort-on-error --list-extractors --extractor-descriptions --use-extractors --force-generic-extractor --default-search --ignore-config --no-config-locations --config-locations --plugin-dirs --no-plugin-dirs --js-runtimes --no-js-runtimes --remote-components --no-remote-components --flat-playlist --no-flat-playlist --live-from-start --no-live-from-start --wait-for-video --no-wait-for-video --mark-watched --no-mark-watched --no-colors --color --compat-options --alias --preset-alias --proxy --socket-timeout --source-address --impersonate --list-impersonate-targets --force-ipv4 --force-ipv6 --enable-file-urls --geo-verification-proxy --xff --geo-bypass --no-geo-bypass --geo-bypass-country --geo-bypass-ip-block --playlist-start --playlist-end --playlist-items --match-title --reject-title --min-filesize --max-filesize --date --datebefore --dateafter --min-views --max-views --match-filters --no-match-filters --break-match-filters --no-break-match-filters --no-playlist --yes-playlist --age-limit --download-archive --no-download-archive --max-downloads --break-on-existing --no-break-on-existing --break-on-reject --break-per-input --no-break-per-input --skip-playlist-after-errors --concurrent-fragments --limit-rate --throttled-rate --retries --file-access-retries --fragment-retries --retry-sleep --skip-unavailable-fragments --abort-on-unavailable-fragments --keep-fragments --no-keep-fragments --buffer-size --resize-buffer --no-resize-buffer --http-chunk-size --test --playlist-reverse --no-playlist-reverse --playlist-random --lazy-playlist --no-lazy-playlist --hls-prefer-native --hls-prefer-ffmpeg --hls-use-mpegts --no-hls-use-mpegts --download-sections --downloader --downloader-args --batch-file --no-batch-file --id --paths --output --output-na-placeholder --autonumber-size --autonumber-start --restrict-filenames --no-restrict-filenames --windows-filenames --no-windows-filenames --trim-filenames --no-overwrites --force-overwrites --no-force-overwrites --continue --no-continue --part --no-part --mtime --no-mtime --write-description --no-write-description --write-info-json --no-write-info-json --write-playlist-metafiles --no-write-playlist-metafiles --clean-info-json --no-clean-info-json --write-comments --no-write-comments --load-info-json --cookies --no-cookies --cookies-from-browser --no-cookies-from-browser --cache-dir --no-cache-dir --rm-cache-dir --write-thumbnail --no-write-thumbnail --write-all-thumbnails --list-thumbnails --write-link --write-url-link --write-webloc-link --write-desktop-link --quiet --no-quiet --no-warnings --simulate --no-simulate --ignore-no-formats-error --no-ignore-no-formats-error --skip-download --print --print-to-file --get-url --get-title --get-id --get-thumbnail --get-description --get-duration --get-filename --get-format --dump-json --dump-single-json --print-json --force-write-archive --newline --no-progress --progress --console-title --progress-template --progress-delta --verbose --dump-pages --write-pages --load-pages --print-traffic --encoding --legacy-server-connect --no-check-certificates --prefer-insecure --user-agent --referer --add-headers --bidi-workaround --sleep-requests --sleep-interval --max-sleep-interval --sleep-subtitles --format --format-sort --format-sort-force --no-format-sort-force --video-multistreams --no-video-multistreams --audio-multistreams --no-audio-multistreams --all-formats --prefer-free-formats --no-prefer-free-formats --check-formats --check-all-formats --no-check-formats --list-formats --list-formats-as-table --list-formats-old --merge-output-format --allow-unplayable-formats --no-allow-unplayable-formats --write-subs --no-write-subs --write-auto-subs --no-write-auto-subs --all-subs --list-subs --sub-format --sub-langs --username --password --twofactor --netrc --netrc-location --netrc-cmd --video-password --ap-mso --ap-username --ap-password --ap-list-mso --client-certificate --client-certificate-key --client-certificate-password --extract-audio --audio-format --audio-quality --remux-video --recode-video --postprocessor-args --keep-video --no-keep-video --post-overwrites --no-post-overwrites --embed-subs --no-embed-subs --embed-thumbnail --no-embed-thumbnail --embed-metadata --no-embed-metadata --embed-chapters --no-embed-chapters --embed-info-json --no-embed-info-json --metadata-from-title --parse-metadata --replace-in-metadata --xattrs --concat-playlist --fixup --ffmpeg-location --exec --no-exec --exec-before-download --no-exec-before-download --convert-subs --convert-thumbnails --split-chapters --no-split-chapters --remove-chapters --no-remove-chapters --force-keyframes-at-cuts --no-force-keyframes-at-cuts --use-postprocessor --sponsorblock-mark --sponsorblock-remove --sponsorblock-chapter-title --no-sponsorblock --sponsorblock-api --extractor-retries --allow-dynamic-mpd --ignore-dynamic-mpd --hls-split-discontinuity --no-hls-split-discontinuity --extractor-args" + opts="--help --version --update --no-update --update-to --ignore-errors --no-abort-on-error --abort-on-error --list-extractors --extractor-descriptions --use-extractors --force-generic-extractor --default-search --ignore-config --no-config-locations --config-locations --plugin-dirs --no-plugin-dirs --js-runtimes --no-js-runtimes --remote-components --no-remote-components --flat-playlist --no-flat-playlist --live-from-start --no-live-from-start --wait-for-video --no-wait-for-video --mark-watched --no-mark-watched --no-colors --color --compat-options --alias --preset-alias --proxy --socket-timeout --source-address --impersonate --list-impersonate-targets --force-ipv4 --force-ipv6 --enable-file-urls --geo-verification-proxy --xff --geo-bypass --no-geo-bypass --geo-bypass-country --geo-bypass-ip-block --playlist-start --playlist-end --playlist-items --match-title --reject-title --min-filesize --max-filesize --date --datebefore --dateafter --min-views --max-views --match-filters --no-match-filters --break-match-filters --no-break-match-filters --no-playlist --yes-playlist --age-limit --download-archive --no-download-archive --max-downloads --break-on-existing --no-break-on-existing --break-on-reject --break-per-input --no-break-per-input --skip-playlist-after-errors --concurrent-fragments --limit-rate --throttled-rate --retries --file-access-retries --fragment-retries --retry-sleep --skip-unavailable-fragments --abort-on-unavailable-fragments --keep-fragments --no-keep-fragments --buffer-size --resize-buffer --no-resize-buffer --http-chunk-size --test --playlist-reverse --no-playlist-reverse --playlist-random --lazy-playlist --no-lazy-playlist --hls-prefer-native --hls-prefer-ffmpeg --hls-use-mpegts --no-hls-use-mpegts --download-sections --downloader --downloader-args --batch-file --no-batch-file --id --paths --output --output-na-placeholder --autonumber-size --autonumber-start --restrict-filenames --no-restrict-filenames --windows-filenames --no-windows-filenames --trim-filenames --no-overwrites --force-overwrites --no-force-overwrites --continue --no-continue --part --no-part --mtime --no-mtime --write-description --no-write-description --write-info-json --no-write-info-json --write-playlist-metafiles --no-write-playlist-metafiles --clean-info-json --no-clean-info-json --write-comments --no-write-comments --load-info-json --cookies --no-cookies --cookies-from-browser --no-cookies-from-browser --cache-dir --no-cache-dir --rm-cache-dir --write-thumbnail --no-write-thumbnail --write-all-thumbnails --list-thumbnails --write-link --write-url-link --write-webloc-link --write-desktop-link --quiet --no-quiet --no-warnings --simulate --no-simulate --ignore-no-formats-error --no-ignore-no-formats-error --skip-download --print --print-to-file --get-url --get-title --get-id --get-thumbnail --get-description --get-duration --get-filename --get-format --dump-json --dump-single-json --print-json --force-write-archive --newline --no-progress --progress --console-title --progress-template --progress-delta --verbose --dump-pages --write-pages --load-pages --print-traffic --encoding --legacy-server-connect --no-check-certificates --prefer-insecure --user-agent --referer --add-headers --bidi-workaround --sleep-requests --sleep-interval --max-sleep-interval --sleep-subtitles --format --format-sort --format-sort-reset --format-sort-force --no-format-sort-force --video-multistreams --no-video-multistreams --audio-multistreams --no-audio-multistreams --all-formats --prefer-free-formats --no-prefer-free-formats --check-formats --check-all-formats --no-check-formats --list-formats --list-formats-as-table --list-formats-old --merge-output-format --allow-unplayable-formats --no-allow-unplayable-formats --write-subs --no-write-subs --write-auto-subs --no-write-auto-subs --all-subs --list-subs --sub-format --sub-langs --username --password --twofactor --netrc --netrc-location --netrc-cmd --video-password --ap-mso --ap-username --ap-password --ap-list-mso --client-certificate --client-certificate-key --client-certificate-password --extract-audio --audio-format --audio-quality --remux-video --recode-video --postprocessor-args --keep-video --no-keep-video --post-overwrites --no-post-overwrites --embed-subs --no-embed-subs --embed-thumbnail --no-embed-thumbnail --embed-metadata --no-embed-metadata --embed-chapters --no-embed-chapters --embed-info-json --no-embed-info-json --metadata-from-title --parse-metadata --replace-in-metadata --xattrs --concat-playlist --fixup --ffmpeg-location --exec --no-exec --exec-before-download --no-exec-before-download --convert-subs --convert-thumbnails --split-chapters --no-split-chapters --remove-chapters --no-remove-chapters --force-keyframes-at-cuts --no-force-keyframes-at-cuts --use-postprocessor --sponsorblock-mark --sponsorblock-remove --sponsorblock-chapter-title --no-sponsorblock --sponsorblock-api --extractor-retries --allow-dynamic-mpd --ignore-dynamic-mpd --hls-split-discontinuity --no-hls-split-discontinuity --extractor-args" keywords=":ytfavorites :ytrecommended :ytsubscriptions :ytwatchlater :ythistory" fileopts="-a|--batch-file|--download-archive|--cookies|--load-info-json" diropts="--cache-dir" diff --git a/.env/share/doc/yt_dlp/README.txt b/.env/share/doc/yt_dlp/README.txt index c4274e8..642b7dc 100644 --- a/.env/share/doc/yt_dlp/README.txt +++ b/.env/share/doc/yt_dlp/README.txt @@ -247,7 +247,8 @@ Python versions 3.10+ (CPython) and 3.11+ (PyPy) are supported. Other versions and implementations may or may not work correctly. While all the other dependencies are optional, ffmpeg, ffprobe, -yt-dlp-ejs and a JavaScript runtime are highly recommended +yt-dlp-ejs and a supported JavaScript runtime/engine are highly +recommended Strongly recommended @@ -264,11 +265,11 @@ Strongly recommended Important: What you need is ffmpeg binary, NOT the Python package of the same name -- yt-dlp-ejs - Required for deciphering YouTube n/sig values. Licensed - under Unlicense, bundles MIT and ISC components. +- yt-dlp-ejs - Required for full YouTube support. Licensed under + Unlicense, bundles MIT and ISC components. - A JavaScript runtime like deno (recommended), node.js, bun, or - QuickJS is also required to run yt-dlp-ejs. See the wiki. + A JavaScript runtime/engine like deno (recommended), node.js, bun, + or QuickJS is also required to run yt-dlp-ejs. See the wiki. Networking @@ -289,7 +290,7 @@ may be required for some sites that employ TLS fingerprinting. - curl_cffi (recommended) - Python binding for curl-impersonate. Provides impersonation targets for Chrome, Edge and Safari. Licensed under MIT - - Can be installed with the curl-cffi group, e.g. + - Can be installed with the curl-cffi extra, e.g. pip install "yt-dlp[default,curl-cffi]" - Currently included in most builds except yt-dlp (Unix zipimport binary), yt-dlp_x86 (Windows 32-bit) and @@ -343,7 +344,7 @@ will be built for the same CPU architecture as the Python used. You can run the following commands: - python devscripts/install_deps.py --include-group pyinstaller + python devscripts/install_deps.py --include-extra pyinstaller python devscripts/make_lazy_extractors.py python -m bundle.pyinstaller @@ -580,7 +581,7 @@ Geo-restriction: Video Selection: - -I, --playlist-items ITEM_SPEC Comma separated playlist_index of the items + -I, --playlist-items ITEM_SPEC Comma-separated playlist_index of the items to download. You can specify a range using "[START]:[STOP][:STEP]". For backward compatibility, START-STOP is also supported. @@ -962,6 +963,8 @@ Video Format Options: for more details -S, --format-sort SORTORDER Sort the formats by the fields given, see "Sorting Formats" for more details + --format-sort-reset Disregard previous user specified sort order + and reset to the default --format-sort-force Force user specified sort order to have precedence over all fields, see "Sorting Formats" for more details (Alias: --S-force) @@ -1481,7 +1484,7 @@ have some special formatting: 7. More Conversions: In addition to the normal format types diouxXeEfFgGcrs, yt-dlp additionally supports converting to B = Bytes, j = json (flag # for pretty-printing, + for Unicode), h = - HTML escaping, l = a comma separated list (flag # for \n + HTML escaping, l = a comma-separated list (flag # for \n newline-separated), q = a string quoted for the terminal (flag # to split a list into different arguments), D = add Decimal suffixes (e.g. 10M) (flag # to use 1024 as factor), and S = Sanitize as @@ -1562,6 +1565,8 @@ The available fields are: - comment_count (numeric): Number of comments on the video (For some extractors, comments are only downloaded at the end, and so this field cannot be used) +- save_count (numeric): Number of times the video has been saved or + bookmarked - age_limit (numeric): Age restriction for the video (years) - live_status (string): One of "not_live", "is_live", "is_upcoming", "was_live", "post_live" (was live, but VOD is not yet processed) @@ -2035,6 +2040,15 @@ respects. Most of the time, what you actually want is the video with the smallest filesize instead. So it is generally better to use -f best -S +size,+br,+res,+fps. +If you use the -S/--format-sort option multiple times, each subsequent +sorting argument will be prepended to the previous one, and only the +highest priority entry of any duplicated field will be preserved. E.g. +-S proto -S res is equivalent to -S res,proto, and +-S res:720,fps -S vcodec,res:1080 is equivalent to +-S vcodec,res:1080,fps. You can use --format-sort-reset to disregard any +previously passed -S/--format-sort arguments and reset to the default +order. + Tip: You can use the -v -F to see how the formats have been sorted (worst to best). @@ -2223,9 +2237,9 @@ metadata: composer composer or composers - genre genre or genres + genre genre, genres, categories or tags - album album + album album or series album_artist album_artist or album_artists @@ -2252,6 +2266,9 @@ Modifying metadata examples # Regex example $ yt-dlp --parse-metadata "description:Artist - (?P.+)" + # Copy the episode field to the title field (with FROM and TO as single fields) + $ yt-dlp --parse-metadata "episode:title" + # Set title as "Series name S01E05" $ yt-dlp --parse-metadata "%(series)s S%(season_number)02dE%(episode_number)02d:%(title)s" @@ -2295,20 +2312,20 @@ youtube respectively - player_client: Clients to extract video data from. The currently available clients are web, web_safari, web_embedded, web_music, - web_creator, mweb, ios, android, android_sdkless, android_vr, tv, + web_creator, mweb, ios, ios_downgraded, android, android_vr, tv, tv_simply, tv_downgraded, and tv_embedded. By default, - tv,android_sdkless,web is used. If no JavaScript runtime is - available, then android_sdkless,web_safari,web is used. If logged-in - cookies are passed to yt-dlp, then tv_downgraded,web_safari,web is - used for free accounts and tv_downgraded,web_creator,web is used for - premium accounts. The web_music client is added for - music.youtube.com URLs when logged-in cookies are used. The - web_embedded client is added for age-restricted videos but only - works if the video is embeddable. The tv_embedded and web_creator - clients are added for age-restricted videos if account - age-verification is required. Some clients, such as web and - web_music, require a po_token for their formats to be downloadable. - Some clients, such as web_creator, will only work with + android_vr,ios_downgraded,web,web_safari is used. If no JavaScript + runtime/engine is available, then android_vr,ios_downgraded is used. + If logged-in cookies are passed to yt-dlp, then + tv_downgraded,web,web_safari is used for free accounts and + tv_downgraded,web_creator,web is used for premium accounts. The + web_music client is added for music.youtube.com URLs when logged-in + cookies are used. The web_embedded client is added for + age-restricted videos but only works if the video is embeddable. The + tv_embedded and web_creator clients are added for age-restricted + videos if account age-verification is required. Some clients, such + as web and web_music, require a po_token for their formats to be + downloadable. Some clients, such as web_creator, will only work with authentication. Not all clients support authentication via cookies. You can use default for the default clients, or you can use all for all clients (not recommended). You can prefix a client with - to @@ -2338,11 +2355,15 @@ youtube YouTube's side) - max_comments: Limit the amount of comments to gather. Comma-separated list of integers representing - max-comments,max-parents,max-replies,max-replies-per-thread. Default - is all,all,all,all - - E.g. all,all,1000,10 will get a maximum of 1000 replies total, - with up to 10 replies per thread. 1000,all,100 will get a - maximum of 1000 comments, with a maximum of 100 replies total + max-comments,max-parents,max-replies,max-replies-per-thread,max-depth. + Default is all,all,all,all,all + - A max-depth value of 1 will discard all replies, regardless of + the max-replies or max-replies-per-thread values given + - E.g. all,all,1000,10,2 will get a maximum of 1000 replies total, + with up to 10 replies per thread, and only 2 levels of depth + (i.e. top-level comments plus their immediate replies). + 1000,all,100 will get a maximum of 1000 comments, with a maximum + of 100 replies total - formats: Change the types of formats to return. dashy (convert HTTP to DASH), duplicate (identical content but different URLs or protocol; includes dashy), incomplete (cannot be downloaded @@ -2363,7 +2384,7 @@ youtube without cookies. Note: this may have adverse effects if used improperly. If a session from a browser is wanted, you should pass cookies instead (which contain the Visitor ID) -- po_token: Proof of Origin (PO) Token(s) to use. Comma seperated list +- po_token: Proof of Origin (PO) Token(s) to use. Comma-separated list of PO Tokens in the format CLIENT.CONTEXT+PO_TOKEN, e.g. youtube:po_token=web.gvs+XXX,web.player=XXX,web_safari.gvs+YYY. Context can be any of gvs (Google Video Server URLs), player @@ -2375,18 +2396,20 @@ youtube requires one for the given context), never (never fetch a PO Token), or auto (default; only fetch a PO Token if the client requires one for the given context) -- playback_wait: Duration (in seconds) to wait inbetween the - extraction and download stages in order to ensure the formats are - available. The default is 6 seconds - jsc_trace: Enable debug logging for JS Challenge fetching. Either true or false (default) +- use_ad_playback_context: Skip preroll ads to eliminate the mandatory + wait period before download. Do NOT use this when passing premium + account cookies to yt-dlp, as it will result in a loss of premium + formats. Only effective with the web, web_safari, web_music and mweb + player clients. Either true or false (default) youtube-ejs -- jitless: Run suported Javascript engines in JIT-less mode. Supported - runtimes are deno, node and bun. Provides better security at the - cost of performance/speed. Do note that node and bun are still - considered unsecure. Either true or false (default) +- jitless: Run supported Javascript engines in JIT-less mode. + Supported runtimes are deno, node and bun. Provides better security + at the cost of performance/speed. Do note that node and bun are + still considered insecure. Either true or false (default) youtubepot-webpo @@ -2568,10 +2591,11 @@ tver vimeo - client: Client to extract video data from. The currently available - clients are android, ios, and web. Only one client can be used. The - web client is used by default. The web client only works with - account cookies or login credentials. The android and ios clients - only work with previously cached OAuth tokens + clients are android, ios, macos and web. Only one client can be + used. The macos client is used by default, but the web client is + used when logged-in. The web client only works with account cookies + or login credentials. The android and ios clients only work with + previously cached OAuth tokens - original_format_policy: Policy for when to try extracting original formats. One of always, never, or auto. The default auto policy tries to avoid exceeding the web client's API rate-limit by only @@ -3085,7 +3109,7 @@ and youtube-dlc: files by default. Use --mtime or --compat-options mtime-by-default to revert this. -For ease of use, a few more compat options are available: +For convenience, there are some compat option aliases available to use: - --compat-options all: Use all compat options (Do NOT use this!) - --compat-options youtube-dl: Same as @@ -3097,8 +3121,13 @@ For ease of use, a few more compat options are available: - --compat-options 2022: Same as --compat-options 2023,playlist-match-filter,no-external-downloader-progress,prefer-legacy-http-handler,manifest-filesize-approx - --compat-options 2023: Same as --compat-options 2024,prefer-vp9-sort -- --compat-options 2024: Same as --compat-options mtime-by-default. - Use this to enable all future compat options +- --compat-options 2024: Same as + --compat-options 2025,mtime-by-default +- --compat-options 2025: Currently does nothing. Use this to enable + all future compat options + +Using one of the yearly compat option aliases will pin yt-dlp's default +behavior to what it was at the end of that calendar year. The following compat options restore vulnerable behavior from before security patches: diff --git a/.env/share/fish/vendor_completions.d/yt-dlp.fish b/.env/share/fish/vendor_completions.d/yt-dlp.fish index 8750971..d015f7d 100644 --- a/.env/share/fish/vendor_completions.d/yt-dlp.fish +++ b/.env/share/fish/vendor_completions.d/yt-dlp.fish @@ -50,7 +50,7 @@ complete --command yt-dlp --long-option geo-bypass-country complete --command yt-dlp --long-option geo-bypass-ip-block complete --command yt-dlp --long-option playlist-start complete --command yt-dlp --long-option playlist-end -complete --command yt-dlp --long-option playlist-items --short-option I --description 'Comma separated playlist_index of the items to download. You can specify a range using "[START]:[STOP][:STEP]". For backward compatibility, START-STOP is also supported. Use negative indices to count from the right and negative STEP to download in reverse order. E.g. "-I 1:3,7,-5::2" used on a playlist of size 15 will download the items at index 1,2,3,7,11,13,15' +complete --command yt-dlp --long-option playlist-items --short-option I --description 'Comma-separated playlist_index of the items to download. You can specify a range using "[START]:[STOP][:STEP]". For backward compatibility, START-STOP is also supported. Use negative indices to count from the right and negative STEP to download in reverse order. E.g. "-I 1:3,7,-5::2" used on a playlist of size 15 will download the items at index 1,2,3,7,11,13,15' complete --command yt-dlp --long-option match-title complete --command yt-dlp --long-option reject-title complete --command yt-dlp --long-option min-filesize --description 'Abort download if filesize is smaller than SIZE, e.g. 50k or 44.6M' @@ -199,6 +199,7 @@ complete --command yt-dlp --long-option max-sleep-interval --description 'Maximu complete --command yt-dlp --long-option sleep-subtitles --description 'Number of seconds to sleep before each subtitle download' complete --command yt-dlp --long-option format --short-option f --description 'Video format code, see "FORMAT SELECTION" for more details' complete --command yt-dlp --long-option format-sort --short-option S --description 'Sort the formats by the fields given, see "Sorting Formats" for more details' +complete --command yt-dlp --long-option format-sort-reset --description 'Disregard previous user specified sort order and reset to the default' complete --command yt-dlp --long-option format-sort-force --description 'Force user specified sort order to have precedence over all fields, see "Sorting Formats" for more details (Alias: --S-force)' complete --command yt-dlp --long-option no-format-sort-force --description 'Some fields have precedence over the user specified sort order (default)' complete --command yt-dlp --long-option video-multistreams --description 'Allow multiple video streams to be merged into a single file' diff --git a/.env/share/man/man1/yt-dlp.1 b/.env/share/man/man1/yt-dlp.1 index 3592aa7..ea048a2 100644 --- a/.env/share/man/man1/yt-dlp.1 +++ b/.env/share/man/man1/yt-dlp.1 @@ -265,7 +265,7 @@ One of \[dq]default\[dq] (only when known to be useful), .SS Video Selection: .TP -I, --playlist-items \f[I]ITEM_SPEC\f[R] -Comma separated playlist_index of the items to download. +Comma-separated playlist_index of the items to download. You can specify a range using \[dq][START]:[STOP][:STEP]\[dq]. For backward compatibility, START-STOP is also supported. Use negative indices to count from the right and negative STEP to @@ -811,6 +811,9 @@ Video format code, see \[dq]FORMAT SELECTION\[dq] for more details Sort the formats by the fields given, see \[dq]Sorting Formats\[dq] for more details .TP +--format-sort-reset +Disregard previous user specified sort order and reset to the default +.TP --format-sort-force Force user specified sort order to have precedence over all fields, see \[dq]Sorting Formats\[dq] for more details (Alias: --S-force) @@ -1513,7 +1516,7 @@ E.g. \f[V]diouxXeEfFgGcrs\f[R], yt-dlp additionally supports converting to \f[V]B\f[R] = \f[B]B\f[R]ytes, \f[V]j\f[R] = \f[B]j\f[R]son (flag \f[V]#\f[R] for pretty-printing, \f[V]+\f[R] for Unicode), \f[V]h\f[R] = -HTML escaping, \f[V]l\f[R] = a comma separated \f[B]l\f[R]ist (flag +HTML escaping, \f[V]l\f[R] = a comma-separated \f[B]l\f[R]ist (flag \f[V]#\f[R] for \f[V]\[rs]n\f[R] newline-separated), \f[V]q\f[R] = a string \f[B]q\f[R]uoted for the terminal (flag \f[V]#\f[R] to split a list into different arguments), \f[V]D\f[R] = add \f[B]D\f[R]ecimal @@ -1650,6 +1653,9 @@ scale used depends on the webpage some extractors, comments are only downloaded at the end, and so this field cannot be used) .IP \[bu] 2 +\f[V]save_count\f[R] (numeric): Number of times the video has been saved +or bookmarked +.IP \[bu] 2 \f[V]age_limit\f[R] (numeric): Age restriction for the video (years) .IP \[bu] 2 \f[V]live_status\f[R] (string): One of \[dq]not_live\[dq], @@ -2349,6 +2355,18 @@ filesize instead. So it is generally better to use \f[V]-f best -S +size,+br,+res,+fps\f[R]. .PP +If you use the \f[V]-S\f[R]/\f[V]--format-sort\f[R] option multiple +times, each subsequent sorting argument will be prepended to the +previous one, and only the highest priority entry of any duplicated +field will be preserved. +E.g. +\f[V]-S proto -S res\f[R] is equivalent to \f[V]-S res,proto\f[R], and +\f[V]-S res:720,fps -S vcodec,res:1080\f[R] is equivalent to +\f[V]-S vcodec,res:1080,fps\f[R]. +You can use \f[V]--format-sort-reset\f[R] to disregard any previously +passed \f[V]-S\f[R]/\f[V]--format-sort\f[R] arguments and reset to the +default order. +.PP \f[B]Tip\f[R]: You can use the \f[V]-v -F\f[R] to see how the formats have been sorted (worst to best). .SS Format Selection examples @@ -2582,12 +2600,13 @@ T} T{ \f[V]genre\f[R] T}@T{ -\f[V]genre\f[R] or \f[V]genres\f[R] +\f[V]genre\f[R], \f[V]genres\f[R], \f[V]categories\f[R] or +\f[V]tags\f[R] T} T{ \f[V]album\f[R] T}@T{ -\f[V]album\f[R] +\f[V]album\f[R] or \f[V]series\f[R] T} T{ \f[V]album_artist\f[R] @@ -2637,6 +2656,9 @@ $ yt-dlp --parse-metadata \[dq]title:%(artist)s - %(title)s\[dq] # Regex example $ yt-dlp --parse-metadata \[dq]description:Artist - (?P.+)\[dq] +# Copy the episode field to the title field (with FROM and TO as single fields) +$ yt-dlp --parse-metadata \[dq]episode:title\[dq] + # Set title as \[dq]Series name S01E05\[dq] $ yt-dlp --parse-metadata \[dq]%(series)s S%(season_number)02dE%(episode_number)02d:%(title)s\[dq] @@ -2691,14 +2713,14 @@ respectively \f[V]player_client\f[R]: Clients to extract video data from. The currently available clients are \f[V]web\f[R], \f[V]web_safari\f[R], \f[V]web_embedded\f[R], \f[V]web_music\f[R], \f[V]web_creator\f[R], -\f[V]mweb\f[R], \f[V]ios\f[R], \f[V]android\f[R], -\f[V]android_sdkless\f[R], \f[V]android_vr\f[R], \f[V]tv\f[R], +\f[V]mweb\f[R], \f[V]ios\f[R], \f[V]ios_downgraded\f[R], +\f[V]android\f[R], \f[V]android_vr\f[R], \f[V]tv\f[R], \f[V]tv_simply\f[R], \f[V]tv_downgraded\f[R], and \f[V]tv_embedded\f[R]. -By default, \f[V]tv,android_sdkless,web\f[R] is used. -If no JavaScript runtime is available, then -\f[V]android_sdkless,web_safari,web\f[R] is used. +By default, \f[V]android_vr,ios_downgraded,web,web_safari\f[R] is used. +If no JavaScript runtime/engine is available, then +\f[V]android_vr,ios_downgraded\f[R] is used. If logged-in cookies are passed to yt-dlp, then -\f[V]tv_downgraded,web_safari,web\f[R] is used for free accounts and +\f[V]tv_downgraded,web,web_safari\f[R] is used for free accounts and \f[V]tv_downgraded,web_creator,web\f[R] is used for premium accounts. The \f[V]web_music\f[R] client is added for \f[V]music.youtube.com\f[R] URLs when logged-in cookies are used. @@ -2757,13 +2779,18 @@ choose comment sorting mode (on YouTube\[aq]s side) .IP \[bu] 2 \f[V]max_comments\f[R]: Limit the amount of comments to gather. Comma-separated list of integers representing -\f[V]max-comments,max-parents,max-replies,max-replies-per-thread\f[R]. -Default is \f[V]all,all,all,all\f[R] +\f[V]max-comments,max-parents,max-replies,max-replies-per-thread,max-depth\f[R]. +Default is \f[V]all,all,all,all,all\f[R] .RS 2 .IP \[bu] 2 +A \f[V]max-depth\f[R] value of \f[V]1\f[R] will discard all replies, +regardless of the \f[V]max-replies\f[R] or +\f[V]max-replies-per-thread\f[R] values given +.IP \[bu] 2 E.g. -\f[V]all,all,1000,10\f[R] will get a maximum of 1000 replies total, with -up to 10 replies per thread. +\f[V]all,all,1000,10,2\f[R] will get a maximum of 1000 replies total, +with up to 10 replies per thread, and only 2 levels of depth (i.e. +top-level comments plus their immediate replies). \f[V]1000,all,100\f[R] will get a maximum of 1000 comments, with a maximum of 100 replies total .RE @@ -2801,7 +2828,7 @@ If a session from a browser is wanted, you should pass cookies instead (which contain the Visitor ID) .IP \[bu] 2 \f[V]po_token\f[R]: Proof of Origin (PO) Token(s) to use. -Comma seperated list of PO Tokens in the format +Comma-separated list of PO Tokens in the format \f[V]CLIENT.CONTEXT+PO_TOKEN\f[R], e.g. \f[V]youtube:po_token=web.gvs+XXX,web.player=XXX,web_safari.gvs+YYY\f[R]. Context can be any of \f[V]gvs\f[R] (Google Video Server URLs), @@ -2818,20 +2845,23 @@ client requires one for the given context), \f[V]never\f[R] (never fetch a PO Token), or \f[V]auto\f[R] (default; only fetch a PO Token if the client requires one for the given context) .IP \[bu] 2 -\f[V]playback_wait\f[R]: Duration (in seconds) to wait inbetween the -extraction and download stages in order to ensure the formats are -available. -The default is \f[V]6\f[R] seconds -.IP \[bu] 2 \f[V]jsc_trace\f[R]: Enable debug logging for JS Challenge fetching. Either \f[V]true\f[R] or \f[V]false\f[R] (default) +.IP \[bu] 2 +\f[V]use_ad_playback_context\f[R]: Skip preroll ads to eliminate the +mandatory wait period before download. +Do NOT use this when passing premium account cookies to yt-dlp, as it +will result in a loss of premium formats. +Only effective with the \f[V]web\f[R], \f[V]web_safari\f[R], +\f[V]web_music\f[R] and \f[V]mweb\f[R] player clients. +Either \f[V]true\f[R] or \f[V]false\f[R] (default) .SS youtube-ejs .IP \[bu] 2 -\f[V]jitless\f[R]: Run suported Javascript engines in JIT-less mode. +\f[V]jitless\f[R]: Run supported Javascript engines in JIT-less mode. Supported runtimes are \f[V]deno\f[R], \f[V]node\f[R] and \f[V]bun\f[R]. Provides better security at the cost of performance/speed. Do note that \f[V]node\f[R] and \f[V]bun\f[R] are still considered -unsecure. +insecure. Either \f[V]true\f[R] or \f[V]false\f[R] (default) .SS youtubepot-webpo .IP \[bu] 2 @@ -3045,9 +3075,10 @@ Default is \f[V]asc\f[R] .IP \[bu] 2 \f[V]client\f[R]: Client to extract video data from. The currently available clients are \f[V]android\f[R], \f[V]ios\f[R], -and \f[V]web\f[R]. +\f[V]macos\f[R] and \f[V]web\f[R]. Only one client can be used. -The \f[V]web\f[R] client is used by default. +The \f[V]macos\f[R] client is used by default, but the \f[V]web\f[R] +client is used when logged-in. The \f[V]web\f[R] client only works with account cookies or login credentials. The \f[V]android\f[R] and \f[V]ios\f[R] clients only work with @@ -3161,8 +3192,8 @@ Python versions 3.10+ (CPython) and 3.11+ (PyPy) are supported. Other versions and implementations may or may not work correctly. .PP While all the other dependencies are optional, \f[V]ffmpeg\f[R], -\f[V]ffprobe\f[R], \f[V]yt-dlp-ejs\f[R] and a JavaScript runtime are -highly recommended +\f[V]ffprobe\f[R], \f[V]yt-dlp-ejs\f[R] and a supported JavaScript +runtime/engine are highly recommended .SS Strongly recommended .IP \[bu] 2 \f[B]ffmpeg\f[R] and \f[B]ffprobe\f[R] (https://www.ffmpeg.org) - @@ -3185,8 +3216,8 @@ for details on the specific issues solved by these builds name (https://pypi.org/project/ffmpeg) .RE .IP \[bu] 2 -\f[B]yt-dlp-ejs\f[R] (https://github.com/yt-dlp/ejs) - Required for -deciphering YouTube n/sig values. +\f[B]yt-dlp-ejs\f[R] (https://github.com/yt-dlp/ejs) - Required for full +YouTube support. Licensed under Unlicense (https://github.com/yt-dlp/ejs/blob/main/LICENSE), bundles MIT (https://github.com/davidbonnet/astring/blob/main/LICENSE) and @@ -3194,7 +3225,7 @@ ISC (https://github.com/meriyah/meriyah/blob/main/LICENSE.md) components. .RS 2 .PP -A JavaScript runtime like \f[B]deno\f[R] (https://deno.land) +A JavaScript runtime/engine like \f[B]deno\f[R] (https://deno.land) (recommended), \f[B]node.js\f[R] (https://nodejs.org), \f[B]bun\f[R] (https://bun.sh), or \f[B]QuickJS\f[R] (https://bellard.org/quickjs/) is also required to run @@ -3237,7 +3268,7 @@ Licensed under MIT (https://github.com/lexiforest/curl_cffi/blob/main/LICENSE) .RS 2 .IP \[bu] 2 -Can be installed with the \f[V]curl-cffi\f[R] group, e.g. +Can be installed with the \f[V]curl-cffi\f[R] extra, e.g. \f[V]pip install \[dq]yt-dlp[default,curl-cffi]\[dq]\f[R] .IP \[bu] 2 Currently included in most builds \f[I]except\f[R] \f[V]yt-dlp\f[R] @@ -3326,7 +3357,7 @@ You can run the following commands: .IP .nf \f[C] -python devscripts/install_deps.py --include-group pyinstaller +python devscripts/install_deps.py --include-extra pyinstaller python devscripts/make_lazy_extractors.py python -m bundle.pyinstaller \f[R] @@ -4056,7 +4087,7 @@ default. Use \f[V]--mtime\f[R] or \f[V]--compat-options mtime-by-default\f[R] to revert this. .PP -For ease of use, a few more compat options are available: +For convenience, there are some compat option aliases available to use: .IP \[bu] 2 \f[V]--compat-options all\f[R]: Use all compat options (\f[B]Do NOT use this!\f[R]) @@ -4077,9 +4108,15 @@ this!\f[R]) \f[V]--compat-options 2024,prefer-vp9-sort\f[R] .IP \[bu] 2 \f[V]--compat-options 2024\f[R]: Same as -\f[V]--compat-options mtime-by-default\f[R]. +\f[V]--compat-options 2025,mtime-by-default\f[R] +.IP \[bu] 2 +\f[V]--compat-options 2025\f[R]: Currently does nothing. Use this to enable all future compat options .PP +Using one of the yearly compat option aliases will pin yt-dlp\[aq]s +default behavior to what it was at the \f[I]end\f[R] of that calendar +year. +.PP The following compat options restore vulnerable behavior from before security patches: .IP \[bu] 2 diff --git a/.env/share/zsh/site-functions/_yt-dlp b/.env/share/zsh/site-functions/_yt-dlp index ca2468e..787b69d 100644 --- a/.env/share/zsh/site-functions/_yt-dlp +++ b/.env/share/zsh/site-functions/_yt-dlp @@ -21,7 +21,7 @@ __yt_dlp() { elif [[ ${prev} == "--recode-video" ]]; then _arguments '*: :(mp4 flv ogg webm mkv)' else - _arguments '*: :(--help --version --update --no-update --update-to --ignore-errors --no-abort-on-error --abort-on-error --list-extractors --extractor-descriptions --use-extractors --force-generic-extractor --default-search --ignore-config --no-config-locations --config-locations --plugin-dirs --no-plugin-dirs --js-runtimes --no-js-runtimes --remote-components --no-remote-components --flat-playlist --no-flat-playlist --live-from-start --no-live-from-start --wait-for-video --no-wait-for-video --mark-watched --no-mark-watched --no-colors --color --compat-options --alias --preset-alias --proxy --socket-timeout --source-address --impersonate --list-impersonate-targets --force-ipv4 --force-ipv6 --enable-file-urls --geo-verification-proxy --xff --geo-bypass --no-geo-bypass --geo-bypass-country --geo-bypass-ip-block --playlist-start --playlist-end --playlist-items --match-title --reject-title --min-filesize --max-filesize --date --datebefore --dateafter --min-views --max-views --match-filters --no-match-filters --break-match-filters --no-break-match-filters --no-playlist --yes-playlist --age-limit --download-archive --no-download-archive --max-downloads --break-on-existing --no-break-on-existing --break-on-reject --break-per-input --no-break-per-input --skip-playlist-after-errors --concurrent-fragments --limit-rate --throttled-rate --retries --file-access-retries --fragment-retries --retry-sleep --skip-unavailable-fragments --abort-on-unavailable-fragments --keep-fragments --no-keep-fragments --buffer-size --resize-buffer --no-resize-buffer --http-chunk-size --test --playlist-reverse --no-playlist-reverse --playlist-random --lazy-playlist --no-lazy-playlist --hls-prefer-native --hls-prefer-ffmpeg --hls-use-mpegts --no-hls-use-mpegts --download-sections --downloader --downloader-args --batch-file --no-batch-file --id --paths --output --output-na-placeholder --autonumber-size --autonumber-start --restrict-filenames --no-restrict-filenames --windows-filenames --no-windows-filenames --trim-filenames --no-overwrites --force-overwrites --no-force-overwrites --continue --no-continue --part --no-part --mtime --no-mtime --write-description --no-write-description --write-info-json --no-write-info-json --write-playlist-metafiles --no-write-playlist-metafiles --clean-info-json --no-clean-info-json --write-comments --no-write-comments --load-info-json --cookies --no-cookies --cookies-from-browser --no-cookies-from-browser --cache-dir --no-cache-dir --rm-cache-dir --write-thumbnail --no-write-thumbnail --write-all-thumbnails --list-thumbnails --write-link --write-url-link --write-webloc-link --write-desktop-link --quiet --no-quiet --no-warnings --simulate --no-simulate --ignore-no-formats-error --no-ignore-no-formats-error --skip-download --print --print-to-file --get-url --get-title --get-id --get-thumbnail --get-description --get-duration --get-filename --get-format --dump-json --dump-single-json --print-json --force-write-archive --newline --no-progress --progress --console-title --progress-template --progress-delta --verbose --dump-pages --write-pages --load-pages --print-traffic --encoding --legacy-server-connect --no-check-certificates --prefer-insecure --user-agent --referer --add-headers --bidi-workaround --sleep-requests --sleep-interval --max-sleep-interval --sleep-subtitles --format --format-sort --format-sort-force --no-format-sort-force --video-multistreams --no-video-multistreams --audio-multistreams --no-audio-multistreams --all-formats --prefer-free-formats --no-prefer-free-formats --check-formats --check-all-formats --no-check-formats --list-formats --list-formats-as-table --list-formats-old --merge-output-format --allow-unplayable-formats --no-allow-unplayable-formats --write-subs --no-write-subs --write-auto-subs --no-write-auto-subs --all-subs --list-subs --sub-format --sub-langs --username --password --twofactor --netrc --netrc-location --netrc-cmd --video-password --ap-mso --ap-username --ap-password --ap-list-mso --client-certificate --client-certificate-key --client-certificate-password --extract-audio --audio-format --audio-quality --remux-video --recode-video --postprocessor-args --keep-video --no-keep-video --post-overwrites --no-post-overwrites --embed-subs --no-embed-subs --embed-thumbnail --no-embed-thumbnail --embed-metadata --no-embed-metadata --embed-chapters --no-embed-chapters --embed-info-json --no-embed-info-json --metadata-from-title --parse-metadata --replace-in-metadata --xattrs --concat-playlist --fixup --ffmpeg-location --exec --no-exec --exec-before-download --no-exec-before-download --convert-subs --convert-thumbnails --split-chapters --no-split-chapters --remove-chapters --no-remove-chapters --force-keyframes-at-cuts --no-force-keyframes-at-cuts --use-postprocessor --sponsorblock-mark --sponsorblock-remove --sponsorblock-chapter-title --no-sponsorblock --sponsorblock-api --extractor-retries --allow-dynamic-mpd --ignore-dynamic-mpd --hls-split-discontinuity --no-hls-split-discontinuity --extractor-args)' + _arguments '*: :(--help --version --update --no-update --update-to --ignore-errors --no-abort-on-error --abort-on-error --list-extractors --extractor-descriptions --use-extractors --force-generic-extractor --default-search --ignore-config --no-config-locations --config-locations --plugin-dirs --no-plugin-dirs --js-runtimes --no-js-runtimes --remote-components --no-remote-components --flat-playlist --no-flat-playlist --live-from-start --no-live-from-start --wait-for-video --no-wait-for-video --mark-watched --no-mark-watched --no-colors --color --compat-options --alias --preset-alias --proxy --socket-timeout --source-address --impersonate --list-impersonate-targets --force-ipv4 --force-ipv6 --enable-file-urls --geo-verification-proxy --xff --geo-bypass --no-geo-bypass --geo-bypass-country --geo-bypass-ip-block --playlist-start --playlist-end --playlist-items --match-title --reject-title --min-filesize --max-filesize --date --datebefore --dateafter --min-views --max-views --match-filters --no-match-filters --break-match-filters --no-break-match-filters --no-playlist --yes-playlist --age-limit --download-archive --no-download-archive --max-downloads --break-on-existing --no-break-on-existing --break-on-reject --break-per-input --no-break-per-input --skip-playlist-after-errors --concurrent-fragments --limit-rate --throttled-rate --retries --file-access-retries --fragment-retries --retry-sleep --skip-unavailable-fragments --abort-on-unavailable-fragments --keep-fragments --no-keep-fragments --buffer-size --resize-buffer --no-resize-buffer --http-chunk-size --test --playlist-reverse --no-playlist-reverse --playlist-random --lazy-playlist --no-lazy-playlist --hls-prefer-native --hls-prefer-ffmpeg --hls-use-mpegts --no-hls-use-mpegts --download-sections --downloader --downloader-args --batch-file --no-batch-file --id --paths --output --output-na-placeholder --autonumber-size --autonumber-start --restrict-filenames --no-restrict-filenames --windows-filenames --no-windows-filenames --trim-filenames --no-overwrites --force-overwrites --no-force-overwrites --continue --no-continue --part --no-part --mtime --no-mtime --write-description --no-write-description --write-info-json --no-write-info-json --write-playlist-metafiles --no-write-playlist-metafiles --clean-info-json --no-clean-info-json --write-comments --no-write-comments --load-info-json --cookies --no-cookies --cookies-from-browser --no-cookies-from-browser --cache-dir --no-cache-dir --rm-cache-dir --write-thumbnail --no-write-thumbnail --write-all-thumbnails --list-thumbnails --write-link --write-url-link --write-webloc-link --write-desktop-link --quiet --no-quiet --no-warnings --simulate --no-simulate --ignore-no-formats-error --no-ignore-no-formats-error --skip-download --print --print-to-file --get-url --get-title --get-id --get-thumbnail --get-description --get-duration --get-filename --get-format --dump-json --dump-single-json --print-json --force-write-archive --newline --no-progress --progress --console-title --progress-template --progress-delta --verbose --dump-pages --write-pages --load-pages --print-traffic --encoding --legacy-server-connect --no-check-certificates --prefer-insecure --user-agent --referer --add-headers --bidi-workaround --sleep-requests --sleep-interval --max-sleep-interval --sleep-subtitles --format --format-sort --format-sort-reset --format-sort-force --no-format-sort-force --video-multistreams --no-video-multistreams --audio-multistreams --no-audio-multistreams --all-formats --prefer-free-formats --no-prefer-free-formats --check-formats --check-all-formats --no-check-formats --list-formats --list-formats-as-table --list-formats-old --merge-output-format --allow-unplayable-formats --no-allow-unplayable-formats --write-subs --no-write-subs --write-auto-subs --no-write-auto-subs --all-subs --list-subs --sub-format --sub-langs --username --password --twofactor --netrc --netrc-location --netrc-cmd --video-password --ap-mso --ap-username --ap-password --ap-list-mso --client-certificate --client-certificate-key --client-certificate-password --extract-audio --audio-format --audio-quality --remux-video --recode-video --postprocessor-args --keep-video --no-keep-video --post-overwrites --no-post-overwrites --embed-subs --no-embed-subs --embed-thumbnail --no-embed-thumbnail --embed-metadata --no-embed-metadata --embed-chapters --no-embed-chapters --embed-info-json --no-embed-info-json --metadata-from-title --parse-metadata --replace-in-metadata --xattrs --concat-playlist --fixup --ffmpeg-location --exec --no-exec --exec-before-download --no-exec-before-download --convert-subs --convert-thumbnails --split-chapters --no-split-chapters --remove-chapters --no-remove-chapters --force-keyframes-at-cuts --no-force-keyframes-at-cuts --use-postprocessor --sponsorblock-mark --sponsorblock-remove --sponsorblock-chapter-title --no-sponsorblock --sponsorblock-api --extractor-retries --allow-dynamic-mpd --ignore-dynamic-mpd --hls-split-discontinuity --no-hls-split-discontinuity --extractor-args)' fi ;; esac diff --git a/make_folders.py b/make_folders.py index 7d7ce5a..f207e68 100755 --- a/make_folders.py +++ b/make_folders.py @@ -91,7 +91,9 @@ def search_google_images_and_save(x: str, audio): if x.endswith(".flac"): songpath = join(".",str(audio["artist"]),str(audio["album"])) else: - songpath = join(".",str(audio["TPE2"]),str(audio["TALB"])) + # Use TPE2 if available, otherwise fallback to TPE1 + artist_folder = str(audio.get("TPE2", audio.get("TPE1", "Unknown Artist"))) + songpath = join(".", artist_folder, str(audio["TALB"])) if "\x00" in songpath: # having null bytes in os.replace will throw an error songpath = songpath.replace("\x00",", ") @@ -189,7 +191,7 @@ def create_ID3_tag(audio, tagname: str, textvalue: str): def check_tag(audio, filename: str, ID3_tag: str, normal_tag) -> bool: res = False - # check if the ID3 artist tag exists + # check if the ID3 tag exists if (ID3_tag in audio.keys() and len(str(audio[ID3_tag])) != 0): logging.info(ID3_tag + " ID3 tag found! " + str(audio[ID3_tag])) @@ -201,7 +203,7 @@ def check_tag(audio, filename: str, ID3_tag: str, normal_tag) -> bool: logging.info("Set " + normal_tag + " to " + str(audio[normal_tag])) res = True - # check if general album tag exists + # check if general tag exists elif (normal_tag in audio.keys() and len(str(audio[normal_tag])) != 0): logging.info(normal_tag + " normal tag found! " + str(audio[normal_tag])) if audio[normal_tag] is not str: @@ -228,6 +230,11 @@ def check_title_songname(x: str, audio): if (" - " in x): items = x.split(" - ") + # If the format is 'artist - Topic - title', remove 'Topic' + if len(items) > 2 and items[1].strip().lower() == "topic": + logging.info("Detected ' - Topic - ' in name, removing 'Topic'.") + # Rebuild items without 'Topic' + items = [items[0]] + items[2:] if (len(items) > 2): logging.info("song title has more than 1 part after the -: " + str(items)) if (items[1].count(".mp3") >= 1): @@ -266,7 +273,7 @@ def check_for_multiple_artists(audio, filename: str, name: str): artists = name.split(",") elif ("/" in name): artists = name.split("/") - elif ("\x00" in name): + elif ("\x00" in name): artists = name.split("\x00") if (len(artists) > 0): @@ -276,7 +283,11 @@ def check_for_multiple_artists(audio, filename: str, name: str): else: audio["TPE1"] = TPE2(encoding=3,text=["\0".join(artists)]) else: - logging.info("no multiple artists found in name " + name) + logging.info("no multiple artists found in name " + name + ", setting artist to " + name) + if filename.endswith(".flac"): + audio["artist"] = TPE2(encoding=3,text=name) + else: + audio["TPE1"] = TPE2(encoding=3,text=name) # checks for any artist from the song name. If it exists it sets the properties of the file @@ -297,6 +308,9 @@ def check_artist(audio, filename: str) -> bool: res = False # check if the ID3 artist tag exists + check_tag(audio, filename, "TPE1","artist") + check_tag(audio, filename, "TPE2","artist") + if ("TPE1" in audio.keys()): if (len(str(audio["TPE1"])) != 0): logging.info("TPE1 tag was found! " + str(audio["TPE1"])) @@ -445,9 +459,9 @@ def check_spotify_album_and_save(spotify, audio,x: str) -> bool: comment ="Spotify ID: {0}. Release date precision: {1}, total tracks in album: {2}. This album has {3} version(s)".format(album["id"],album["release_date_precision"], album["total_tracks"],len(results["albums"]["items"])) logging.info("Comment: " + comment) if x.endswith(".flac"): - audio["comment"] = comment + audio["comment"] = audio["comment"] + comment else: - audio["COMM"] = COMM(encoding=3,text=comment) + audio["COMM"] = COMM(encoding=3,text=comment + audio["COMM"]) if x.endswith(".flac"): remove_flac_ID3_tags(audio,x) @@ -748,10 +762,12 @@ def main(): logging.info("valid artist found. making folder for artist " + str(audio["artist"][0])) make_folder(join(".",str(audio["artist"][0]))) else: - logging.info("valid artist found. making folder for artist " + str(audio["TPE1"])) - if "/" in audio["TPE2"]: - audio["TPE2"] = audio["TPE2"].replace("/","") - make_folder(join(".",str(audio["TPE2"]))) + # Use TPE2 if available, otherwise fallback to TPE1 + artist_folder = str(audio.get("TPE2", audio.get("TPE1", "Unknown Artist"))) + logging.info("valid artist found. making folder for artist " + artist_folder) + if "/" in artist_folder: + artist_folder = artist_folder.replace("/","") + make_folder(join(".", artist_folder)) if (has_valid_album): if (x.endswith(".flac")):