| 1 | #!/usr/bin/env bash |
| 2 | # Desc: Download YouTube videos |
| 3 | # Usage: $ ./bkytpldl-generic |
| 4 | # Version: 4.1.0 |
| 5 | |
| 6 | declare -a args; # array for yt-dlp arguments |
| 7 | declare -a urls urls_rand; # array for YouTube playlist URLs |
| 8 | |
| 9 | # Settings |
| 10 | dir_out="~/Videos/"; |
| 11 | urls+=("https://www.youtube.com/playlist?list=PLxxx"); # adjust me |
| 12 | urls+=("https://www.youtube.com/playlist?list=PLxxx"); # adjust me |
| 13 | urls+=("https://www.youtube.com/playlist?list=PLxxx"); # adjust me |
| 14 | |
| 15 | yell() { echo "$0: $*" >&2; } # print script path and all args to stderr |
| 16 | die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status |
| 17 | must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails |
| 18 | |
| 19 | # check dependencies |
| 20 | if ! command -v yt-dlp 1>/dev/random 2>&1; then die "FATAL:yt-dlp not found."; fi; |
| 21 | |
| 22 | # Donʼt run multiple yt-dlp instances |
| 23 | if pgrep "^yt-dlp$" 1>/dev/random 2>&1; then die "FATAL:yt-dlp already running."; fi; |
| 24 | |
| 25 | # Check directories |
| 26 | if [[ ! -d $dir_out ]]; then mkdir -p "$dir_out"; fi; |
| 27 | |
| 28 | # == Assemble options == |
| 29 | |
| 30 | # yt-dlp output options |
| 31 | ## Restrict file name character set |
| 32 | #args+=("--restrict-filenames"); # Remove non-ASCII characters |
| 33 | args+=("--trim-filenames=120"); # Use in tandem with `%(title).120B` |
| 34 | |
| 35 | ## Request to write accompanying files |
| 36 | args+=("--write-subs"); # Write subtitles file |
| 37 | args+=("--write-auto-subs"); # Write subtitles file |
| 38 | #args+=("--all-subs"); # Download all available subtitles (causes many requests) |
| 39 | #subLangs="en.*,ja.*,id.*,es.*,zh-Hans.*,zh-Hant.*,sv.*,el.*,hi.*,ru.*,bn.*,fr.*,ko.*,ar.*,nv.*"; # custom language list |
| 40 | subLangs="en,en-orig,en.*"; # custom language list |
| 41 | args+=("--sub-langs" "$subLangs"); |
| 42 | args+=("--write-info-json"); # Write accompanying json file |
| 43 | args+=("--no-overwrites"); # Don't overwrite files |
| 44 | args+=("--write-thumbnail"); # Write thumbnail |
| 45 | |
| 46 | ## Only download metadata |
| 47 | #args+=("--no-download"); # Don't download video file. |
| 48 | |
| 49 | ## Save meta-data |
| 50 | args+=("--write-comments"); # Get comments |
| 51 | ### Limit comments |
| 52 | ### comment_sort values: |
| 53 | ### top : use YouTube top comment algorithm |
| 54 | ### new : get newest comments (default) |
| 55 | ### max_comments values: |
| 56 | ### max-comments : max number of parent comments or replies |
| 57 | ### max-parents : max number of comment threads |
| 58 | ### max-replies : max number of replies across all threads |
| 59 | ### max-replies-per-thread : max number of replies per thread |
| 60 | args+=("--extractor-args" "youtube:comment_sort=top;max_comments=10000,100,10000,100"); |
| 61 | |
| 62 | ## Randomize order in which playlist items are downloaded |
| 63 | args+=("--playlist-random"); |
| 64 | |
| 65 | ## Delay between downloads |
| 66 | minSleep="30"; |
| 67 | maxSleep="$(( minSleep + (RANDOM + RANDOM + RANDOM) / ( 3 * 400) ))"; # roughly 60 seconds |
| 68 | args+=("--min-sleep-interval" "$minSleep"); |
| 69 | args+=("--max-sleep-interval" "$maxSleep"); |
| 70 | args+=("--sleep-requests" "2"); # delay on metadata requests |
| 71 | args+=("--sleep-subtitles" "10"); # delay for subtitles |
| 72 | |
| 73 | ## Remember downloaded videos to avoid redownload attempts |
| 74 | pathDA="$dir_out"/.bkytpldl_history.txt; |
| 75 | args+=("--download-archive" "$pathDA"); |
| 76 | |
| 77 | ## Use firefox 'default-release' profile cookies |
| 78 | ## Example: Linux: from ~/.mozilla/firefox/deadbeef.default-release/ |
| 79 | #args+=("--cookies-from-browser"); |
| 80 | #args+=("firefox:deadbeef.default-release"); Default Firefox profile name |
| 81 | |
| 82 | ## Specify output filename format |
| 83 | ## Note: `$(title).120B` shortens title to 120 bytes (useful for |
| 84 | ## titles with UTF-8 characters. |
| 85 | args+=("-o"); |
| 86 | args+=("%(playlist)s/%(upload_date)s.%(channel)s.%(channel_id)s.%(title).120B.%(id)s.%(ext)s"); |
| 87 | |
| 88 | ## Limit download resolution to 1080p |
| 89 | args+=("-S" "res:1080"); |
| 90 | |
| 91 | ## Specify playlist URLs to download |
| 92 | ### Shuffle playlist download order |
| 93 | mapfile -t urls_rand < <(printf "%s\n" "${urls[@]}" | shuf); |
| 94 | for url in "${urls_rand[@]}"; do |
| 95 | args+=("$url"); |
| 96 | done; |
| 97 | |
| 98 | # Change working directory to output dir |
| 99 | pushd "$dir_out" || die "FATAL:Failed to change pwd to:dir_out:$dir_out"; |
| 100 | |
| 101 | # == Download videos == |
| 102 | |
| 103 | #yell "DEBUG:args:$(declare -p args)"; # debug command |
| 104 | must yt-dlp "${args[@]}"; # execute command |
| 105 | popd || die "FATAL:Failed to return from dir_out:$dir_out"; |
| 106 | |
| 107 | # Author: Steven Baltakatei Sandoval |
| 108 | # License; GPLv3+ |