Ostatnio aktywny 3 days ago

A YouTube downloader created by ChatGPT. See the docblock for more information

channels.txt Surowy
1#https://piped.video/channel/UCRiYVwfoEnKfweISfKytuQw # Akimbo
2https://piped.video/channel/UCxXlxVmarXu3n340ah5xwqA # Astrobiscuit
3https://piped.video/channel/UC-9b7aDP6ZN0coj9-xFnrtw # Astrum
4#https://piped.video/channel/UCTEEa35OCDcJDh-lwbXehdg # CantrellCaving
5#https://piped.video/channel/UCbW17b5g1Eb90M457eOykPg # Chinese cuisine
6https://piped.video/channel/UCg6gPGh8HU2U01vaFCAsvmQ # Chris Titus Tech
7#https://piped.video/channel/UCvfqpaehdaqtkXPNhvJRyGA # Dash Cam Owners Australia
8https://piped.video/channel/UCdC0An4ZPNr_YiFiYoVbwaw # Daily Dose of Internet
9https://piped.video/channel/UCmdmZp1K_kjXYBKgyqg_5LA # Daily Dose of Gaming
10https://piped.video/channel/UCk6_V9EQQ7c8fuWzFKcCNzQ # Daily Dose of Pets
11https://piped.video/channel/UCTIa8uo_aisNdqQpMf4wKTg # DailyDoseOfInternetCats
12#https://piped.video/channel/UCnetiRHPnIMBCm-yY4m69qA # Dashcam Lessons
13#https://piped.video/channel/UCOS7jdle9zw_HnKW4ytZnDw # Dashcam Nation
14#https://piped.video/channel/UCqOoboPm3uhY_YXhvhmL-WA # Discovery
15https://piped.video/channel/UCxByVUuLdxxiqQVmbOnDEzw # Erik Granqvist
16#https://piped.video/channel/UCPq2ETz4aAGo2Z-8JisDPIA # ESL Counter-Strike
17#https://piped.video/channel/UCwF3VyalTHzL0L-GDlwtbRw # EVE Online
18#https://piped.video/channel/UC73dVtWf9mpjiWYkXyIlm7A # Exploring the Unbeaten Path
19https://piped.video/channel/UCPDis9pjXuqyI7RYLJ-TTSA # FailArmy
20https://piped.video/channel/UCt8CdzMEoTie3iix3KmvV7A # GameSprout
21#https://piped.video/channel/UC5SlWFFu-YvbujLKa0zoNGw # Idiot Drivers
22#https://piped.video/channel/UCXE0IwEN5HkDohBq2ebv5Bw # Idiots In Cars
23https://piped.video/channel/UCw7FkXsC00lH2v2yB5LQoYA # jackfrags
24https://piped.video/channel/UCWFKCr40YwOZQx8FHU_ZqqQ # JerryRigEverything
25https://piped.video/channel/UCla1P6TzCetvMHJ6ZHyXxgA # Kanal 5 Sverige
26https://piped.video/channel/UCsXVk37bltHxD1rDPwtNM8Q # Kurzgesagt - In a Nutshell
27#https://piped.video/channel/UCpJmBQ8iNHXeQ7jQWDyGe3A # Life Noggin
28#https://piped.video/channel/UCXuqSBlHAE6Xw-yeJA0Tunw # Linus Tech Tips
29https://piped.video/channel/UCS1-E3fjfJmY5O2HqhXM5CQ # Learn Japanese Channel
30#https://piped.video/channel/UCKIrZr7WuPFgFy7OfkAuHOw # MegaDrivingSchool
31#https://piped.video/channel/UCS67mNnpfnHsU3IQYNHLToA # Most Dangerous
32#https://piped.video/channel/UCpVm7bg6pXKo1Pr6k5kxG9A # National Geographic
33#https://piped.video/channel/UCDPk9MG2RexnOMGTD-YnSnA # Nat Geo Wild
34#https://piped.video/channel/UCL-g3eGJi1omSDSz48AML-g # NVIDIA GeForce
35https://piped.video/channel/UC-ihxmkocezGSm9JcKg1rfw # OperatorDrewski
36https://piped.video/channel/UCfpCQ89W9wjkHc8J_6eTbBg # Outdoor Boys
37#https://piped.video/channel/UCKGe7fZ_S788Jaspxg-_5Sg # PC Security Channel
38https://piped.video/channel/UCwdsaWtA70AiVeALp2ux6RA # RamenStyle
39#https://piped.video/channel/UCs5QhJQF9I1m81bCJy0WO-w # Real History
40#https://piped.video/channel/UCB1qBlpuLMi2ydz8zxFfLfw # Ribecka
41https://piped.video/channel/UC8kQrh-1JyFZ3RiTHHKMt7A # Ruby Dashcam Academy
42https://piped.video/channel/UCRtsZ5Iak9wSLsQLQ3XOAeA # SciManDan
43#https://piped.video/channel/UCZpZTSdvEgVf0BqTqf9NjNg # Sean Dalton
44https://piped.video/channel/UCpXwMqnXfJzazKS5fJ8nrVw # shiey
45#https://piped.video/channel/UCUMwsAsbK-SsW5GHufJJFUA # SHL
46https://piped.video/channel/UCpB959t8iPrxQWj7G6n0ctQ # SSSniperWolf
47https://piped.video/channel/UCQD3awTLw9i8Xzh85FKsuJA # SovietWomble
48#https://piped.video/channel/UCOt5hVyS2-nbcJ3_FP41Ajg # Tomographic
49#https://piped.video/channel/UCHnyfMqiRRG1u-2MsSQLbXA # Veritasium
50https://piped.video/channel/UCYfnmhHA2O-q1JHPJDWpaOQ # Volvo Dashcam
51#https://piped.video/channel/UCvz84_Q0BbvZThy75mbd-Dg # Zack D. Films
52#https://piped.video/channel/UC4Tklxku1yPcRIH0VVCKoeA # Quantum Tech HD
crontab Surowy
1# Add this line to crontab -e as sudo (sudo su) to automate the whole thing
2# As it is right now, videos will be downloaded from YouTube every 6 hour (00:00 > 06:00 > 12:00 > 18:00 > repeat)
30 */6 * * * /bin/bash /dir/to/dlyt.sh >> /dir/to/dlyt.log 2>&1
dlyt.sh Surowy
1#!/bin/bash
2###############################################################################
3# dlyt.sh
4#
5# YouTube → Jellyfin Downloader & Organizer
6#
7# Downloads recent YouTube videos from channels listed in channels.txt,
8# organizes them into a Jellyfin-friendly folder structure, generates NFO files,
9# converts thumbnails from WEBP to JPG, and cleans up unnecessary files.
10#
11# Features:
12# - Downloads up to 5 videos per channel (configurable via --playlist-end)
13# - Skips live, upcoming, long (>2h) videos, or titles containing "WAN"
14# - Sorts episodes by upload_date
15# - Creates Season 01 and Specials (shorts)
16# - Generates tvshow.nfo with channel description
17# - Converts thumbnails from WEBP → JPG
18# - Cleans up info.json and description files after NFO creation
19# - Removes orphaned or leftover files
20# - Changes ownership to jellyfin for proper library access
21#
22# Dependencies:
23# - yt-dlp
24# - jq
25# - ImageMagick (magick command)
26# - Bash ≥4
27###############################################################################
28
29set -euo pipefail # Abort on errors, unset variables are errors, pipeline failures propagate
30
31# BASE DIRECTORIES
32BASE="/dir/to/folder" # Root for all downloads
33YTDLP="$BASE/yt-dlp" # Folder for downloads
34CHANNELS="$BASE/channels.txt" # List of channels
35
36mkdir -p "$YTDLP" # Create download folder if missing
37
38sudo chown -R user:user "${YTDLP}" # Temporarily give ownership to the user to allow writing
39
40
41
42echo "===== yt-dlp download ====="
43
44# Download videos with yt-dlp
45yt-dlp \
46 --ignore-errors \
47 --no-overwrites \
48 --continue \
49 --restrict-filenames \
50 --windows-filenames \
51 --playlist-end 5 \
52 \
53 --dateafter now-10days \
54 \
55 --match-filter "!is_live & live_status!=is_upcoming & duration<7200 & title!*=WAN" \
56 \
57 --format "(bestvideo[ext=mp4][height<=1080]/bestvideo[height<=1080])+(bestaudio[ext=m4a]/bestaudio)/best[ext=mp4]/best" \
58 --merge-output-format mp4 \
59 \
60 --write-info-json \
61 --write-description \
62 --write-thumbnail \
63 --embed-chapters \
64 --sponsorblock-mark all,-preview,-filler,-interaction \
65 --download-archive "$YTDLP/downloaded.txt" \
66 \
67 --output "$YTDLP/%(uploader)s/%(title)s [%(id)s].%(ext)s" \
68 \
69 -a "$CHANNELS" || true
70
71
72
73echo "===== Organizing channels ====="
74
75# Folder layout for each channel:
76# SHOW/ <- Root folder for channel (e.g., "SciManDan")
77# ├─ Season 01/ <- Regular episodes (longer than 240s)
78# │ ├─ S01E01 - Title1 [ID].mp4
79# │ ├─ S01E02 - Title2 [ID].mp4
80# │ ├─ ...
81# │ ├─ S01E01 - Title1 [ID].webp <- optional, converted later to .jpg
82# │ └─ S01E02 - Title2 [ID].webp
83# ├─ Specials/ <- Shorts (<241s (=4min))
84# │ ├─ S00E01 - Short1 [ID].mp4
85# │ ├─ S00E02 - Short2 [ID].mp4
86# │ └─ ...
87# ├─ tvshow.nfo <- Generated channel NFO
88# ├─ poster.jpg <- First JPG in channel, copied as poster
89# └─ other leftover files <- cleaned up by the script
90
91# Iterate over each channel folder
92find "$YTDLP" -mindepth 1 -maxdepth 1 -type d | while read -r SHOW; do
93 mkdir -p "$SHOW/Season 01" "$SHOW/Specials" # Create Season/Specials folders
94
95 ep1=1 # Season 01 episode counter
96 ep0=1 # Specials (shorts) episode counter
97
98 # Sort episodes by upload_date: get date|jsonfile, sort by date, then cut jsonfile
99 find "$SHOW" -maxdepth 1 -name "*.info.json" | while read -r JSON; do
100 DATE=$(jq -r '.upload_date // "00000000"' "$JSON") # Use default 00000000 if missing
101 echo "$DATE|$JSON" # Format: YYYYMMDD|jsonfile
102 done | sort | cut -d'|' -f2 | while read -r JSON; do
103 # Explanation of pipe:
104 # 1. `jq` extracts upload_date from each .info.json
105 # 2. `echo "$DATE|$JSON"` pairs date with filename
106 # 3. `sort` sorts alphabetically → chronological because YYYYMMDD
107 # 4. `cut -d'|' -f2` returns only the filename, in sorted order
108 BASEFILE="${JSON%.info.json}" # Base filename without extension
109 MP4="$BASEFILE.mp4" # Corresponding video file
110 [ ! -f "$MP4" ] && continue # Skip if video missing
111
112 TITLE=$(jq -r '.title' "$JSON") # Video title
113 DESC=$(jq -r '.description // ""' "$JSON") # Video description (empty if missing)
114 DATE=$(jq -r '.upload_date' "$JSON") # Upload date
115 ID=$(jq -r '.id' "$JSON") # YouTube video ID
116 DURATION=$(jq -r '.duration // 0' "$JSON") # Duration in seconds
117
118 AIRED="${DATE:0:4}-${DATE:4:2}-${DATE:6:2}" # Convert YYYYMMDD → YYYY-MM-DD
119
120 # Determine Season / Specials based on duration
121 if [ "$DURATION" -lt 241 ]; then
122 SEASON=0
123 EP=$(printf "%02d" "$ep0") # Zero-padded episode
124 SAFE_TITLE=$(echo "$TITLE" | cut -c1-120) # Truncate long titles to 120 chars
125 DEST="$SHOW/Specials/S00E$EP - $SAFE_TITLE [$ID]"
126 ep0=$((ep0+1)) # Increment specials counter
127 else
128 SEASON=1
129 EP=$(printf "%02d" "$ep1")
130 SAFE_TITLE=$(echo "$TITLE" | cut -c1-120)
131 DEST="$SHOW/Season 01/S01E$EP - $SAFE_TITLE [$ID]"
132 ep1=$((ep1+1)) # Increment season counter
133 fi
134
135 mkdir -p "$(dirname "$DEST")" # Ensure destination folder exists
136
137 mv "$MP4" "$DEST.mp4" # Move video to organized folder
138
139 # Move WEBP thumbnail to match episode name
140 if [ -f "$BASEFILE.webp" ]; then
141 mv "$BASEFILE.webp" "$DEST.webp"
142 fi
143
144 # Create episode NFO
145cat > "$DEST.nfo" <<EOF
146# Mapping of episode files:
147# For each episode DEST:
148# DEST.mp4 <- main video
149# DEST.webp <- original thumbnail (moved)
150# DEST.jpg <- converted thumbnail via ImageMagick
151# DEST.nfo <- episode metadata
152#
153# This ensures Jellyfin recognizes the episode with proper thumbnail and metadata.
154
155<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
156<episodedetails>
157 <title>$TITLE</title>
158 <season>$SEASON</season>
159 <episode>$((10#$EP))</episode> # Convert to decimal, handle leading zeros
160 <airikred>$AIRED</airikred>
161 <premiered>$AIRED</premiered>
162 <plot><![CDATA[$DESC]]></plot>
163 <uniqueid type="youtube">$ID</uniqueid>
164</episodedetails>
165EOF
166
167 rm -f "$BASEFILE.info.json" "$BASEFILE.description" # Cleanup metadata
168done
169
170
171
172echo "===== Generating tvshow.nfo for $(basename "$SHOW") ====="
173
174# tvshow.nfo & poster.jpg:
175# - tvshow.nfo contains channel title, studio, and channel description (plot)
176# - poster.jpg is copied from the first .jpg found in channel (episode thumbnail)
177#
178# Jellyfin reads this for series overview in the library.
179
180if [ ! -f "$SHOW/tvshow.nfo" ]; then
181
182 # Attempt to grab channel description from first info.json
183 CHANNEL_JSON=$(find "$SHOW" -maxdepth 1 -name "*[[]*.info.json" | head -n 1)
184 if [ -n "$CHANNEL_JSON" ]; then
185 SHOW_DESC=$(jq -r '.description // ""' "$CHANNEL_JSON")
186 else
187 SHOW_DESC=""
188 fi
189
190cat > "$SHOW/tvshow.nfo" <<EOF
191<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
192<tvshow>
193<title>$(basename "$SHOW")</title>
194<studio>YouTube</studio>
195<plot><![CDATA[$SHOW_DESC]]></plot>
196</tvshow>
197EOF
198fi
199
200# Ensure poster.jpg exists for the channel
201if [ ! -f "$SHOW/poster.jpg" ]; then
202 THUMB=$(find "$SHOW" -type f -iname "*.jpg" | head -n 1)
203 [ -n "$THUMB" ] && cp "$THUMB" "$SHOW/poster.jpg"
204fi
205done
206
207
208
209echo "===== Removing old JPGs in channel root ====="
210
211# Remove unnecessary JPGs from root (except poster.jpg)
212for CHANNEL in "$YTDLP"/*; do
213 [ -d "$CHANNEL" ] || continue
214 find "$CHANNEL" -maxdepth 1 -type f -name "*.jpg" ! -name "poster.jpg" -exec rm -f {} +
215done
216
217
218
219echo "===== Converting WEBP thumbnails to JPG with ImageMagick ====="
220
221# Conversion process:
222# - Each *.webp file in the channel folder tree is converted to *.jpg
223# - Original *.webp is deleted later
224# - JPG filename matches video DEST, so Jellyfin can use it automatically
225# Example:
226# /SciManDan/Season 01/S01E01 - Title1 [ID].webp -> S01E01 - Title1 [ID].jpg
227
228# Convert WEBP thumbnails to JPG
229find "$YTDLP" -type f -name "*.webp" | while read -r WEBP; do
230 JPG="${WEBP%.webp}.jpg"
231 magick "$WEBP" "$JPG" # magick CLI converts webp → jpg
232done
233
234
235
236echo "===== Removing WEBP files ====="
237
238find "$YTDLP" -type f -name "*.webp" -delete # Remove WEBP after conversion
239
240
241
242echo "===== Cleaning up leftover channel metadata ====="
243
244# Remove any leftover .info.json and .description files
245for CHANNEL in "$YTDLP"/*; do
246 [ -d "$CHANNEL" ] || continue
247 find "$CHANNEL" -maxdepth 1 -type f \( \
248 -name "*.info.json" -o \
249 -name "*.description" \
250 \) -exec rm -f {} +
251done
252
253
254
255echo "===== Cleaning orphaned NFO and JPG files ====="
256
257# Delete any leftover orphaned NFO/JPG if MP4 is missing
258for CHANNEL in "$YTDLP"/*; do
259 [ -d "$CHANNEL" ] || continue
260 find "$CHANNEL" -type f \( -name "*.info.json" -o -name "*.description" \) -exec rm -f {} +
261done
262
263
264
265echo "===== Change owner rights to Jellyfin ====="
266
267sudo chown -R jellyfin:jellyfin "${YTDLP}" # Final ownership for Jellyfin access
268
269echo "===== ALL DONE ====="
jellyfin-example-1.jpg Surowy
jellyfin-example-1.jpg
jellyfin-example-2.jpg Surowy
jellyfin-example-2.jpg
jellyfin-example-3.jpg Surowy
jellyfin-example-3.jpg
jellyfin-example-4.jpg Surowy
jellyfin-example-4.jpg
jellyfin-example-5.jpg Surowy
jellyfin-example-5.jpg