Using Absolute Series Scanner, YouTube Agent Bundle and yt-dlp a system can be created for automatically downloading videos from YouTube.
The following naming convention seems to work in making all the Absolute Series Scanner and YouTube-Agent sort YouTube videos by year using folders named after the year that the video has been released:
/Channel Name [Channel ID]/YYYYmmdd - Channel Name - Title [Video ID].webm
with a generated filesystem layout such as:
+-----+ Channel Name [Channel ID] + | +----------------+ YYYYmmdd - Channel Name - Video Title [Video ID].webm | +----------------+ YYYYmmdd - Channel Name - Video Title [Video ID].webm | . . .
In yt-dlp terminology, the following string must be passed to the output
parameter corresponding to the layout described above:
/%(uploader)s [%(channel_id)s]/%(upload_date>%Y%m%d)s - %(uploader)s - %(title)s [%(id)s].%(ext)s
Or, script-wise, the following script seems suitable to generate the required layout for every channel or playlist:
#!/bin/sh ########################################################################### ## Copyright (C) Wizardry and Steamworks 2024 - License: MIT ## ########################################################################### # Acquire a lock. LOCK_FILE="/tmp/lock" if mkdir $LOCK_FILE 2>&1 >/dev/null; then trap '{ rm -rf $LOCK_FILE; }' KILL QUIT TERM EXIT INT HUP else exit 0 fi FULL_PATH="/mnt/storage/YouTube/%(uploader)s [%(channel_id)s]/%(upload_date>%Y%m%d)s - %(uploader)s - %(title)s [%(id)s].%(ext)s" mkdir -p /archives yt-dlp \ --output "${FULL_PATH}" \ --replace-in-metadata "title" "[^\x20\x30-\x39\x40-\x5a\x61-\x7a\x2f\x5c\x5b\x5d]" "" \ -S "height:480" \ --username oauth2 --password '' \ --continue \ --download-archive "/archives/$0.txt" \ --write-info-json \ --write-subs \ YOUTUBE_PLAYLIST_OR_CHANNEL_URL 2>&1 | logger
where:
YOUTUBE_PLAYLIST_OR_CHANNEL_URL
is a placeholder for the YouTube channel or playlistThe result is that when Plex Media Server is made to scan the folder using the plugins above, with the settings:
Select type
→TV Shows
,Advanced
→Scanner
→Absolute Series Scanner
,Advanced
→Agent
→YouTubeSeries
Advanced
→Seasons
→Show
as pictured in the screenshot below:
then browsing the folder will result in all videos being organized underneath season folders labeled by year.
The script below is ready to be used with a schedule such as cron in order to download a list of channels and playlists from YouTube. The script is made or the sake of automation, by periodically checking whether new content is available and then downloading the content to local storage in order to be viewed with a PVR such as Plex Media Server or Jellyfin.
#!/usr/bin/env bash ########################################################################### ## Copyright (C) Wizardry and Steamworks 2024 - License: GNU GPLv3 ## ########################################################################### # This is a script meant to be used on a schedule, for example, by # # placing it within a crontab, that will use yt-dlp in order to download # # a list of youtube videos from specified channels or playlists. # # # # Requirements: # # * ''yt-dlp'' # # # # Note that the script requires some folders to be created as specified # # within the "CONFIGURATION" section of the script. Please ensure that # # the script is properly configured before using it. # ########################################################################### ########################################################################### # CONFIGURATION # ########################################################################### # the top-level part of the path to where all YouTube videos will be saved PATH_PREFIX="/mnt/storage/YouTube" # the sub-path after the path-prefix created by yt-dlp OUTPUT_PATH="%(uploader)s [%(channel_id)s]/%(upload_date>%Y%m%d)s - %(uploader)s - %(title)s [%(id)s].%(ext)s" ARCHIVE_DIRECTORY=/archives/ # this is an array consisting of YouTube videos; it may contain channel # links, playlists or URLs to a channel's videos collection YOUTUBE_DOWNLOADS=( "https://www.youtube.com/channel/ZOq89SAAXCYN20LWEMKP0Yix/videos" "https://www.youtube.com/playlist?list=XOQKliawOIIswzxxkovUHLDbndqwsapwv" "https://www.youtube.com/@NamedChannel/videos" ) ########################################################################### # INTERNALS # ########################################################################### # acquire a lock LOCK_FILE="/tmp/"`echo ${0##*/} | md5sum | cut -d ' ' -f 1` if mkdir $LOCK_FILE 2>&1 >/dev/null; then trap '{ rm -rf "${LOCK_FILE}"; }' KILL QUIT TERM EXIT INT HUP else exit 0 fi mkdir -p "${ARCHIVE_DIRECTORY}" # start downloading all scheduled YouTube content for URL in "${YOUTUBE_DOWNLOADS[@]}"; do echo "Downloading: "${URL} | logger yt-dlp \ --output "${PATH_PREFIX}/${OUTPUT_PATH}" \ --replace-in-metadata "title" "[^\x20\x30-\x39\x40-\x5a\x61-\x7a\x2f\x5c\x5b\x5d]" "" \ -S "height:480" \ --continue \ --download-archive "${ARCHIVE_DIRECTORY}/$0.txt" \ --embed-metadata \ --embed-subs \ --write-info-json \ --write-subs \ --write-auto-sub \ --sub-lang "en.*" \ --playlist-reverse \ "${URL}" 2>&1 | logger done
The script uses some assumptions when downloading content from YouTube, namely the following statements can be made:
480p
(or better), on the assumption that just viewing is desired, with perhaps the least used up storage and bandwidth in the process.The script will require some initial seeding in order to obtain the backfill necessary for all specified YouTube content such that running the script in foreground on the first run is recommended. After all content has been downloaded, perhaps a good suggestion is to run the script on a daily cron schedule, given that the script should sift fast enough through any content already downloaded and will only download new videos.
In case Plex Media Server is the PVR of choice, this script can be ran before the Plex Media Server library refresh script such that Plex Media Server will refresh the YouTube library containing the media that is being downloaded. In order to achieve the proper script precedence, the comments on the cron FUSS page regarding the order of execution of scripts should help.
Using the following combination of options, a given timeframe with the first date into the past can be downloaded exclusively without having yt-dlp
go through the entire videos or playlist. The best way seems to be to just attempt and download the last videos within a playlist because yt-dlp will not overwrite he files, but only continue them if they are found to be partial downloads. For example, the options:
--playlist-end 10 \ --playlist-reverse \
will download the last 10
videos from the top of the list (most recent).
While yt-dlp
does support downloading videos after a given date, it seems that the filters are applied on each video, such that running a script to pull videos with a date match will result in yt-dlp
running through the entire playlist video-by-video which might be annoyingly slow for large playlists.