This commit is contained in:
jbannon 2022-04-30 06:53:45 +00:00
parent fd5c48b897
commit 9a8ca7fc28
4 changed files with 261 additions and 0 deletions

View file

@ -0,0 +1,67 @@
# This example shows how to download and format a Youtube video OR playlist
# to display in Kodi as a music video. Kodi requires music videos to be in
# a shared directory, so we will configure this to make the output directory
# formatted as:
#
# /path/to/Music Videos
# Elton John - Rocketman.jpg
# Elton John - Rocketman.mp4
# Elton John - Rocketman.nfo
# System of a Down - Chop Suey.jpg
# System of a Down - Chop Suey.mp4
# System of a Down - Chop Suey.nfo
# ...
#
configuration:
working_directory: '.ytdl-sub-downloads'
presets:
yt_music_video:
# Youtube playlists are our source/download strategy. However, this
# can be overwritten to download music videos from a 'channel' or a
# single 'video'
youtube:
download_strategy: "playlist"
# For advanced YTDL users only.
# It is wise to leave ignoreerrors=True to avoid errors for things
# like age-restricted videos in case you have not set your cookie.
ytdl_options:
ignoreerrors: True
# For each video downloaded, set the file and thumbnail name here.
# We set both with {music_video_name}, which is a variable we define in
# the overrides section further below to represent consistent naming format.
#
# Another field worth mentioning is maintain_download_archive=True. This
# is generally a good thing to enable with playlists because it will
# store previously downloaded video ids to tell YTDL not to re-download
# them on a successive invocation.
output_options:
output_directory: "path/to/Music Videos"
file_name: "{music_video_name}.{ext}"
thumbnail_name: "{music_video_name}.jpg"
maintain_download_archive: True
# Always convert the video thumbnail to jpg
# TODO: always convert all thumbnails to jpg in code
convert_thumbnail:
to: "jpg"
# For each video downloaded, add a music video NFO file for it. Populate it
# with tags that Kodi will read and use to display it in the music or music
# videos section.
nfo_tags:
nfo_name: "{music_video_name}.nfo"
nfo_root: "musicvideo"
tags:
artist: "{artist}"
title: "{title}"
album: "Music Videos"
year: "{upload_year}"
# Overrides is a section where we can define our own variables, and use them in
# any other section. We define our episode file name here, which gets reused above
# for the video, thumbnail, and NFO file.
overrides:
music_video_name: "{sanitized_artist} - {sanitized_title}"

View file

@ -0,0 +1,84 @@
# This example shows how we can use the `kodi_music_videos_config.yaml` preset
# to download music videos in a few different ways. We will use made-up artists
# in each example
###############################################################################
# LEVEL 1 - DOWNLOAD MUSIC VIDEO PLAYLIST
# Subscription names are defined by you. We will call this one john_smith
# for simplicity, and it will download every single video in john_smith's music
# video playlist. Many artists maintain a playlist of all their music videos,
# which makes this an easy way to grab all of them.
john_smith:
# We must define a preset to use from our config. We named the one in the
# config example "yt_music_video", so set that here.
preset: "yt_music_video"
# Since our preset download strategy is set to 'playlist', set the playlist id.
youtube:
playlist_id: "UCsvn_Po0SmunchJYtttWpOxMg"
# Overrides can be defined per-subscription. If you noticed, we used {artist}
# and {sanitized_artist} in our "yt_music_video" preset. We intended to reserve
# that variable to be defined for each individual subscription. Each override
# defined here will create a 'sanitized_' version that is safe for file systems.
#
# A note for Kodi music videos, it is important to make sure your artist name
# exactly matches how it is formatted in Kodi itself, otherwise it will be
# read in as a new artist
overrides:
artist: "John Smith and the Instrument Players"
###############################################################################
# LEVEL 2 - DOWNLOAD SINGLE MUSIC VIDEO
# It is not always ideal to download all of an artist's music videos.
# Maybe you only like one song of theirs. We can reuse our preset
# to download a single video instead.
#
# The only difference between this example and the one above is
# - youtube.after
# This is saying 'only download videos in the last 14 days'
# - ytdl_options.break_on_reject
# This is getting into YTDL voodoo. By default, YTDL will try to
# download all channel videos beginning with the most recent one.
# By setting break_on_reject, we will break this full-download on
# the first video that gets rejected. Since we have youtube.after
# defined, all videos after today-14days will be rejected. Therefore,
# the first video that is out of range that it tries to download, it
# will stop there, and save a significant amount of time.
john_smith_one_hit_wonder:
preset: "yt_channel_as_tv"
youtube:
download_strategy: "video"
video_id: "QhY6r6oAErg"
overrides:
tv_show_name: "John /\ Smith"
ytdl_options:
break_on_reject: True
###############################################################################
# LEVEL 3 - ROLLING ARCHIVE
# If you put the recent archive example in a cron job, then in a year or so
# you will basically be datahoarding that channel unless you manually delete
# old videos. We automate because we are lazy. This example shows how to
# only keep the last 14-days worth of videos, and delete the rest.
#
# The only difference between this example and the one above is
# - output_options.keep_files.after
# This is saying "only keep files if they were uploaded in the last 14 days".
# All other files that this subscription had previously downloaded will be
# deleted.
john_smith_rolling_archive:
preset: "yt_channel_as_tv"
youtube:
channel_id: "UCsvn_Po0SmunchJYtttWpOxMg"
after: today-14days
overrides:
tv_show_name: "John /\ Smith"
ytdl_options:
break_on_reject: True
output_options:
keep_files:
after: today-14days

View file

@ -18,3 +18,22 @@ class YoutubeVideoVariables(EntryVariables):
return self.kwargs("track")
return super().title
@property
def playlist_index(self) -> int:
"""
Returns
-------
The index of the video in the playlist. For non-playlist download strategies, this will
always return 1.
"""
return 1
@property
def playlist_size(self) -> int:
"""
Returns
-------
The size of the playlist. For non-playlist download strategies, this will always return 1.
"""
return 1

View file

@ -1,6 +1,8 @@
import os.path
from pathlib import Path
from typing import List, Dict
from ytdl_sub.entries.base_entry import PlaylistMetadata
from ytdl_sub.entries.entry import Entry
from ytdl_sub.entries.variables.youtube_variables import YoutubeVideoVariables
@ -31,3 +33,92 @@ class YoutubeVideo(YoutubeVideoVariables, Entry):
return possible_thumbnail_path
return super().get_download_thumbnail_path()
class YoutubePlaylistVideo(YoutubeVideo):
def __init__(
self,
entry_dict: Dict,
working_directory: str,
playlist_metadata: PlaylistMetadata,
):
"""
Initialize the playlist video with playlist metadata
"""
super().__init__(entry_dict=entry_dict, working_directory=working_directory)
self._playlist_metadata = playlist_metadata
@classmethod
def from_youtube_video(
cls,
youtube_video: YoutubeVideo,
playlist_metadata: PlaylistMetadata,
) -> "YoutubePlaylistVideo":
"""
Parameters
----------
youtube_video:
Video to convert to an playlist video
playlist_metadata:
Metadata for playlist ordering
Returns
-------
YoutubeVideo converted to a YoutubePlaylistVideo
"""
return YoutubePlaylistVideo(
entry_dict=youtube_video._kwargs, # pylint: disable=protected-access
working_directory=youtube_video.working_directory(),
playlist_metadata=playlist_metadata,
)
@property
def playlist_index(self) -> int:
"""
Returns
-------
The playlist index
"""
return self._playlist_metadata.playlist_index
@property
def playlist_size(self) -> int:
"""
Returns
-------
The size of the playlist
"""
return self._playlist_metadata.playlist_count
class YoutubePlaylist(Entry):
@property
def _videos(self) -> List[YoutubeVideo]:
"""
Returns all videos in the playlist represented by non-playlist Videos. Use this to fetch any
data needed from the videos before representing it as a playlist video.
"""
return [
YoutubeVideo(entry_dict=entry, working_directory=self._working_directory)
for entry in self.kwargs("entries")
]
def playlist_videos(self) -> List[YoutubePlaylistVideo]:
"""
Returns
-------
All videos in the playlist represented as YoutubePlaylistVideos. This updates
playlist-specific fields like playlist_index and playlist_size with its actual value.
"""
return [
YoutubePlaylistVideo.from_youtube_video(
youtube_video=video,
playlist_metadata=PlaylistMetadata(
playlist_id=self.uid,
playlist_extractor=self.extractor,
playlist_index=video.kwargs("playlist_index"),
playlist_count=self.kwargs('playlist_count'),
),
)
for video in self._videos
]