Overhaul docs for readthedocs (#20)

This commit is contained in:
Jesse Bannon 2022-05-06 22:25:29 -07:00 committed by GitHub
parent b37a138f65
commit ab2df88299
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 520 additions and 157 deletions

View file

@ -49,6 +49,8 @@ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
#
html_theme = "sphinx_rtd_theme"
html_theme_options = {"navigation_depth": 10}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
@ -56,6 +58,6 @@ html_theme = "sphinx_rtd_theme"
# Do not show full module path in api docs
add_module_names = False
python_use_unqualified_type_names = True
python_use_unqualified_type_names = False
napoleon_numpy_docstrings = True
napoleon_use_rtype = False

198
docs/config.rst Normal file
View file

@ -0,0 +1,198 @@
Config
======
ytdl-sub is configured using a ``config.yaml`` file. You can view our
:doc:`examples <examples>` and read detailed documentation for every configurable
field below.
The ``config.yaml`` is made up of two sections:
.. code-block:: yaml
configuration:
presets:
You can jump to any section and subsection of the config using the navigation
section to the left.
configuration
-------------
The ``configuration`` section contains app-wide configs applied to all presets
and subscriptions.
presets
-------
``presets`` define a `formula` for how to format downloaded media and metadata.
download_strategy
^^^^^^^^^^^^^^^^^
Download strategies dictate what is getting downloaded from a source. Each
download strategy has its own set of parameters.
youtube
"""""""
channel
'''''''
.. code-block:: yaml
presets:
my_example_preset:
youtube:
download_strategy: "channel"
channel_id
__________
.. autoproperty:: ytdl_sub.downloaders.youtube_downloader.YoutubeChannelDownloaderOptions.channel_id
channel_avatar_path
___________________
.. autoproperty:: ytdl_sub.downloaders.youtube_downloader.YoutubeChannelDownloaderOptions.channel_avatar_path
channel_banner_path
___________________
.. autoproperty:: ytdl_sub.downloaders.youtube_downloader.YoutubeChannelDownloaderOptions.channel_banner_path
before
______
.. autoproperty:: ytdl_sub.downloaders.youtube_downloader.YoutubeChannelDownloaderOptions.before
after
_____
.. autoproperty:: ytdl_sub.downloaders.youtube_downloader.YoutubeChannelDownloaderOptions.after
-------------------------------------------------------------------------------
playlist
''''''''
.. code-block:: yaml
presets:
my_example_preset:
youtube:
download_strategy: "playlist"
playlist_id
___________
.. autoproperty:: ytdl_sub.downloaders.youtube_downloader.YoutubePlaylistDownloaderOptions.playlist_id
-------------------------------------------------------------------------------
video
'''''
.. code-block:: yaml
presets:
my_example_preset:
youtube:
download_strategy: "video"
video_id
________
.. autoproperty:: ytdl_sub.downloaders.youtube_downloader.YoutubeVideoDownloaderOptions.video_id
-------------------------------------------------------------------------------
soundcloud
""""""""""
albums_and_singles
''''''''''''''''''
.. code-block:: yaml
presets:
my_example_preset:
soundcloud:
download_strategy: "albums_and_singles"
username
________
.. autoproperty:: ytdl_sub.downloaders.soundcloud_downloader.SoundcloudAlbumsAndSinglesDownloadOptions.username
skip_premiere_tracks
____________________
.. autoproperty:: ytdl_sub.downloaders.soundcloud_downloader.SoundcloudAlbumsAndSinglesDownloadOptions.skip_premiere_tracks
-------------------------------------------------------------------------------
output_options
^^^^^^^^^^^^^^
output_directory
""""""""""""""""
.. autoproperty:: ytdl_sub.config.preset_options.OutputOptions.output_directory
file_name
"""""""""
.. autoproperty:: ytdl_sub.config.preset_options.OutputOptions.file_name
thumbnail_name
""""""""""""""
.. autoproperty:: ytdl_sub.config.preset_options.OutputOptions.thumbnail_name
maintain_download_archive
"""""""""""""""""""""""""
.. autoproperty:: ytdl_sub.config.preset_options.OutputOptions.maintain_download_archive
keep_files
""""""""""
.. autoproperty:: ytdl_sub.config.preset_options.OutputOptions.keep_files
-------------------------------------------------------------------------------
ytdl_options
^^^^^^^^^^^^
.. autoclass:: ytdl_sub.config.preset_options.YTDLOptions()
-------------------------------------------------------------------------------
overrides
^^^^^^^^^
.. autoclass:: ytdl_sub.config.preset_options.Overrides()
Plugins
^^^^^^^
music_tags
""""""""""
.. autoclass:: ytdl_sub.plugins.music_tags.MusicTagsOptions()
nfo
"""
.. autoclass:: ytdl_sub.plugins.nfo_tags.NfoTagsOptions()
nfo_output_directory
""""""""""""""""""""
.. autoclass:: ytdl_sub.plugins.output_directory_nfo_tags.OutputDirectoryNfoTagsOptions()
Source Variables
----------------
Source variables are ``{variables}`` that contain metadata from downloaded media.
These variables can be used in StringFormatters, but not OverrideFormatters.
Youtube Variables
^^^^^^^^^^^^^^^^^
.. automodule:: ytdl_sub.entries.variables.youtube_variables
:members:
:inherited-members:
:undoc-members:
Soundcloud Variables
^^^^^^^^^^^^^^^^^^^^
.. automodule:: ytdl_sub.entries.variables.soundcloud_variables
:members:
:inherited-members:
:undoc-members:
Formatters
----------
Formatters are strings that can contain ``{variables}`` that are overwritten at
run-time with values assigned to that variable. There are two different types of
formatters.
String Formatter
^^^^^^^^^^^^^^^^
.. autoclass:: ytdl_sub.validators.string_formatter_validators.StringFormatterValidator()
Overrides Formatter
^^^^^^^^^^^^^^^^^^^
.. autoclass:: ytdl_sub.validators.string_formatter_validators.OverridesStringFormatterValidator()

View file

@ -1,10 +0,0 @@
Examples
========
Hello, WIP
.. toctree::
:titlesonly:
:maxdepth: 2
kodi_tv_shows

View file

@ -1,12 +0,0 @@
Kodi/Jellyfin TV Shows
======================
Config
.. include:: ../../../examples/kodi_tv_shows_config.yaml
:literal:
.. include:: ../../../examples/kodi_tv_shows_subscriptions.yaml
:literal:

View file

@ -1,11 +0,0 @@
Format Variables
================
Hello, WIP
.. toctree::
:titlesonly:
:maxdepth: 2
soundcloud
youtube

View file

@ -1,7 +0,0 @@
Soundcloud Track
----------------
.. automodule:: ytdl_sub.entries.variables.soundcloud_variables
:members:
:inherited-members:
:undoc-members:

View file

@ -1,7 +0,0 @@
Youtube Video
-------------
.. automodule:: ytdl_sub.entries.variables.youtube_variables
:members:
:inherited-members:
:undoc-members:

View file

@ -1,11 +0,0 @@
Configuration
=============
Hello, WIP
.. toctree::
:titlesonly:
:maxdepth: 2
format_variables/index
examples/index

48
docs/examples.rst Normal file
View file

@ -0,0 +1,48 @@
Examples
========
This page shows how you can use ytdl-sub for various use cases. These
are the configs I personally use and have incorporated as part of the e2e tests.
Each example has a ``config.yaml`` and ``subscription.yaml``. The config defines
`how` you format your media, whereas the subscription defines `what` you
download plus some additional configuring if needed.
Also note that ``kodi`` examples are applicable for ``jellyfin``, ``emby``, and
``plex`` with the
`XBMC Movies <https://github.com/gboudreau/XBMCnfoMoviesImporter.bundle>`_
or
`XBMC TV Show <https://github.com/gboudreau/XBMCnfoTVImporter.bundle>`_
Plex importer. We would like to improve Plex support, please chime in
`here <https://github.com/jmbannon/ytdl-sub/issues/6>`_
if you have experience with importing custom videos with metadata.
Kodi/Jellyfin TV Shows
----------------------
config.yaml
^^^^^^^^^^^
.. include:: ../examples/kodi_tv_shows_config.yaml
:literal:
subscriptions.yaml
^^^^^^^^^^^^^^^^^^
.. include:: ../examples/kodi_tv_shows_subscriptions.yaml
:literal:
Kodi/Jellyfin Music Videos
--------------------------
config.yaml
^^^^^^^^^^^
.. include:: ../examples/kodi_music_videos_config.yaml
:literal:
subscriptions.yaml
^^^^^^^^^^^^^^^^^^
.. include:: ../examples/kodi_music_videos_subscriptions.yaml
:literal:

58
docs/getting_started.rst Normal file
View file

@ -0,0 +1,58 @@
Getting Started
===============
The goal of this app is automate the downloading and metadata creation of audio and video files, then place the files
in a directory that gets read by your media player/server. Everyone stores and watches their media differently, so
we strive for comprehensive and simplistic customization to fit all self-hosting needs.
Audio metadata is just a matter of adding tags to the audio file. The backend plugin we use supports practically every
common audio file type, so there should (hopefully) be no issues getting audio recognized by your media player.
Video metadata on the other hand, is currently geared toward generating Kodi/Jellyfin/Emby NFO files. Plex uses metadata
written within MP4 containers - we do not support that currently, but could be added as a plugin in the future.
Usage Ideas
-----------
Below is a list ways you can use ytdl-sub to download and consume different kinds of media in the representation you
prefer.
* Download a Youtube channel
* Store the channel as its own TV Show
* Use the channel's avatar as the TV Show poster, banner as the fanart
* Format the season and episodes as ``Season YYYY/sYYYY.eMMDD - Video Title.mp4`` to easily navigate videos by upload date
* Store the channel as a single season under a TV Show shared with other channels
* Only download audio, store it as a Podcast
* Only keep video/audio uploaded in the last `N` days
* Great for news or podcast based channels
* Download a Youtube playlist
* Download an artist's music videos playlist, store each video as a Kodi/Jellyfin/Emby Music Video
* Only download the audio, store the playlist as an album
* Manually download a single Youtube video
* Store it as a Movie
* Download a one-hit wonder and store it as a Kodi/Jellyfin/Emby Music Video
* Download a soundcloud artist's discography
* Add tags and album cover images so it shows up nicely in your music player
If you want to jump the gun to see how ytdl-sub can be configured to do these things, head over to the
:doc:`examples <examples>`.
Install
-------
Once we are ready for our first release, we will add this package to pypi. Then,
we plan to create a docker image that uses the
`LinuxServer.io <https://www.linuxserver.io/>`_
base image, and hopefully become a part of their fleet someday.
Until then, you will have to clone this repo and run it using python 3.10
.. code-block:: Bash
git clone https://github.com/jmbannon/ytdl-sub.git
cd ytdl-sub
pip install -e .

View file

@ -1,19 +0,0 @@
Getting Started
===============
The goal of this app is automate the downloading and metadata creation of audio and video files, then place the files
in a directory that gets read by your media player/server. Everyone stores and watches their media differently, so
we strive for comprehensive and simplistic customization to fit all self-hosting needs.
Audio metadata is just a matter of adding tags to the audio file. The backend plugin we use supports practically every
common audio file type, so there should (hopefully) be no issues getting audio recognized by your media player.
Video metadata on the other hand, is currently geared toward generating Kodi/Jellyfin/Emby NFO files. Plex uses metadata
written within MP4 containers - we do not support that currently, but could be added as a plugin in the future.
.. toctree::
:titlesonly:
:maxdepth: 2
usage_ideas
install

View file

@ -1,2 +0,0 @@
Install
=======

View file

@ -1,28 +0,0 @@
Usage Ideas
===========
Below is a list ways you can use ytdl-sub to download and consume different kinds of media in the representation you
prefer.
* Download a Youtube channel
* Store the channel as its own TV Show
* Use the channel's avatar as the TV Show poster, banner as the fanart
* Format the season and episodes as ``Season YYYY/sYYYY.eMMDD - Video Title.mp4`` to easily navigate videos by upload date
* Store the channel as a single season under a TV Show shared with other channels
* Only download audio, store it as a Podcast
* Only keep video/audio uploaded in the last `N` days
* Great for news or podcast based channels
* Download a Youtube playlist
* Download an artist's music videos playlist, store each video as a Kodi/Jellyfin/Emby Music Video
* Only download the audio, store the playlist as an album
* Manually download a single Youtube video
* Store it as a Movie
* Download a one-hit wonder and store it as a Kodi/Jellyfin/Emby Music Video
* Download a soundcloud artist's discography
* Add tags and album cover images so it shows up nicely in your music player
If you want to jump the gun to see how ytdl-sub can be configured to do these things, head over to the
:doc:`examples <../config/examples/index>`.

View file

@ -6,16 +6,17 @@ python app that strives to automate downloading media and preparing it for
consumption in your favorite media player in the most hassle-free way
possible.
If you are new to ytdl-sub, head over to the Getting Started page. This guide
shows how you can install ytdl-sub, configure it to download various types
of media, and start downloading.
If you are new to ytdl-sub, head over to the
:doc:`Getting Started <getting_started>`
page. This guide shows how you can install ytdl-sub, configure it to download
various types of media, and start downloading.
Contents
========
.. toctree::
:titlesonly:
:maxdepth: 2
:maxdepth: 10
getting_started/index
config/index
getting_started
config
examples

View file

@ -13,12 +13,52 @@ from ytdl_sub.validators.validators import LiteralDictValidator
class YTDLOptions(LiteralDictValidator):
"""Ensures `ytdl_options` is a dict"""
"""
Optional. This section allows you to add any ytdl argument to ytdl-sub's downloader.
The argument names can differ slightly from the command-line argument names. See
`this docstring <https://github.com/yt-dlp/yt-dlp/blob/2022.04.08/yt_dlp/YoutubeDL.py#L197>`_
for more details.
ytdl_options should be formatted like:
.. code-block:: yaml
presets:
my_example_preset:
ytdl_options:
ignoreerrors: True
where each key is a ytdl argument.
"""
# Disable for proper docstring formatting
# pylint: disable=line-too-long
class Overrides(DictFormatterValidator):
"""Ensures `overrides` is a dict"""
"""
Optional. This section allows you to define variables that can be used in any string formatter.
For example, if you want your file and thumbnail files to match without copy-pasting a large
format string, you can define something like:
.. code-block:: yaml
presets:
my_example_preset:
overrides:
output_directory: "/path/to/media"
custom_file_name: "{upload_year}.{upload_month_padded}.{upload_day_padded}.{sanitized_title}"
# Then use the override variables in the output options
output_options:
output_directory: "{output_directory}"
file_name: "{custom_file_name}.{ext}"
thumbnail_name: "{custom_file_name}.{thumbnail_ext}"
Override variables can contain explicit values and other variables, including both override
and source variables.
"""
# pylint: enable=line-too-long
def __init__(self, name, value):
super().__init__(name, value)
for key in self._keys:
@ -60,26 +100,98 @@ class OutputOptions(StrictDictValidator):
# Output directory should resolve without any entry variables.
# This is to check the directory for any download-archives before any downloads begin
self.output_directory: OverridesStringFormatterValidator = self._validate_key(
self._output_directory: OverridesStringFormatterValidator = self._validate_key(
key="output_directory", validator=OverridesStringFormatterValidator
)
# file name and thumbnails however can use entry variables
self.file_name: StringFormatterValidator = self._validate_key(
self._file_name: StringFormatterValidator = self._validate_key(
key="file_name", validator=StringFormatterValidator
)
self.thumbnail_name = self._validate_key_if_present(
self._thumbnail_name = self._validate_key_if_present(
key="thumbnail_name", validator=StringFormatterValidator
)
self.maintain_download_archive = self._validate_key_if_present(
self._maintain_download_archive = self._validate_key_if_present(
key="maintain_download_archive", validator=BoolValidator, default=False
)
self.delete_stale_files = self._validate_key_if_present(
self._keep_files = self._validate_key_if_present(
key="keep_files", validator=DateRangeValidator
)
if self.delete_stale_files and not self.maintain_download_archive:
if self._keep_files and not self.maintain_download_archive:
raise self._validation_exception(
"keep_files requires maintain_download_archive set to True"
)
@property
def output_directory(self) -> OverridesStringFormatterValidator:
"""
Required. The output directory to store all media files downloaded.
"""
return self._output_directory
@property
def file_name(self) -> StringFormatterValidator:
"""
Required. The file name for the media file. This can include directories such as
``"Season {upload_year}/{title}.{ext}"``, and will be placed in the output directory.
"""
return self._file_name
@property
def thumbnail_name(self) -> Optional[StringFormatterValidator]:
"""
Optional. The file name for the media's thumbnail image. This can include directories such
as ``"Season {upload_year}/{title}.{thumbnail_ext}"``, and will be placed in the output
directory.
"""
return self._thumbnail_name
@property
def maintain_download_archive(self) -> bool:
"""
Optional. Maintains a download archive file in the output directory for a subscription.
It is named ``.ytdl-sub-{subscription_name}-download-archive.json``, stored in the
output directory.
The download archive contains a mapping of ytdl IDs to downloaded files. This is used to
create a ytdl download-archive file when invoking a download on a subscription. This will
prevent ytdl from redownloading media already downloaded.
Defaults to False.
"""
return self._maintain_download_archive.value
@property
def keep_files(self) -> DateRangeValidator:
"""
Optional. Requires ``maintain_download_archive`` set to True.
Only keeps files that are uploaded in the defined range. Should be formatted as:
.. code-block:: yaml
presets:
my_example_preset:
output_options:
keep_files:
before:
after:
where ``before`` and ``after`` are date-times. A common usage of this option is to only
fill in the after, such as:
.. code-block:: yaml
presets:
my_example_preset:
output_options:
keep_files:
after: today-2weeks
Which translates to 'keep files uploaded in the last two weeks'.
By default, ytdl-sub will keep all files.
"""
return self._keep_files

View file

@ -24,10 +24,17 @@ class SoundcloudDownloaderOptions(DownloaderValidator, ABC):
def __init__(self, name: str, value: dict):
super().__init__(name=name, value=value)
self.skip_premiere_tracks = self._validate_key(
self._skip_premiere_tracks = self._validate_key(
"skip_premiere_tracks", BoolValidator, default=True
)
@property
def skip_premiere_tracks(self) -> bool:
"""
Optional. True to skip tracks that require purchasing. False otherwise. Defaults to True.
"""
return self._skip_premiere_tracks.value
SoundcloudDownloaderOptionsT = TypeVar(
"SoundcloudDownloaderOptionsT", bound=SoundcloudDownloaderOptions
@ -68,7 +75,14 @@ class SoundcloudAlbumsAndSinglesDownloadOptions(SoundcloudDownloaderOptions):
def __init__(self, name, value):
super().__init__(name, value)
self.username = self._validate_key(key="username", validator=StringValidator)
self._username = self._validate_key(key="username", validator=StringValidator)
@property
def username(self) -> str:
"""
Required. The Soundcloud username found in the url of their page.
"""
return self._username.value
class SoundcloudAlbumsAndSinglesDownloader(
@ -132,7 +146,7 @@ class SoundcloudAlbumsAndSinglesDownloader(
"""
Soundcloud subscription to download albums and tracks as singles.
"""
artist_url = self.artist_url(artist_name=self.download_options.username.value)
artist_url = self.artist_url(artist_name=self.download_options.username)
entry_dicts = self.extract_info_via_info_json(url=artist_url)
# Get all of the artist's albums
@ -146,7 +160,7 @@ class SoundcloudAlbumsAndSinglesDownloader(
tracks += album.album_tracks()
# Filter any premiere tracks if specified
if self.download_options.skip_premiere_tracks.value:
if self.download_options.skip_premiere_tracks:
tracks = [track for track in tracks if not track.is_premiere()]
return tracks

View file

@ -59,7 +59,15 @@ class YoutubeVideoDownloaderOptions(YoutubeDownloaderOptions):
def __init__(self, name, value):
super().__init__(name, value)
self.video_id = self._validate_key("video_id", StringValidator)
self._video_id = self._validate_key("video_id", StringValidator)
@property
def video_id(self) -> str:
"""
Required. The ID of the video. Looks like the ``VMAPTo7RVDo`` in
``youtube.com/watch?v=VMAPTo7RVDo``.
"""
return self._video_id.value
class YoutubeVideoDownloader(YoutubeDownloader[YoutubeVideoDownloaderOptions, YoutubeVideo]):
@ -73,8 +81,7 @@ class YoutubeVideoDownloader(YoutubeDownloader[YoutubeVideoDownloaderOptions, Yo
def download(self) -> List[YoutubeVideo]:
"""Download a single Youtube video"""
video_id = self.download_options.video_id.value
video_url = self.video_url(video_id=video_id)
video_url = self.video_url(video_id=self.download_options.video_id)
entry_dict = self.extract_info(url=video_url)
return [YoutubeVideo(entry_dict=entry_dict, working_directory=self.working_directory)]
@ -89,7 +96,14 @@ class YoutubePlaylistDownloaderOptions(YoutubeDownloaderOptions):
def __init__(self, name, value):
super().__init__(name, value)
self.playlist_id = self._validate_key("playlist_id", StringValidator)
self._playlist_id = self._validate_key("playlist_id", StringValidator)
@property
def playlist_id(self) -> str:
"""
Required. The playlist's ID.
"""
return self._playlist_id.value
class YoutubePlaylistDownloader(
@ -107,8 +121,7 @@ class YoutubePlaylistDownloader(
"""
Downloads all videos in a Youtube playlist
"""
playlist_id = self.download_options.playlist_id.value
playlist_url = self.playlist_url(playlist_id=playlist_id)
playlist_url = self.playlist_url(playlist_id=self.download_options.playlist_id)
playlist_videos: List[YoutubePlaylistVideo] = []
entry_dicts = self.extract_info_via_info_json(url=playlist_url)
@ -134,14 +147,37 @@ class YoutubeChannelDownloaderOptions(YoutubeDownloaderOptions, DateRangeValidat
def __init__(self, name, value):
YoutubeDownloaderOptions.__init__(self, name, value)
DateRangeValidator.__init__(self, name, value)
self.channel_id = self._validate_key("channel_id", StringValidator)
self.channel_avatar_path = self._validate_key_if_present(
self._channel_id = self._validate_key("channel_id", StringValidator)
self._channel_avatar_path = self._validate_key_if_present(
"channel_avatar_path", OverridesStringFormatterValidator
)
self.channel_banner_path = self._validate_key_if_present(
self._channel_banner_path = self._validate_key_if_present(
"channel_banner_path", OverridesStringFormatterValidator
)
@property
def channel_id(self) -> str:
"""
Required. The channel's ID. Not to be confused with the username. It should look something
like `UCsvn_Po0SmunchJYOWpOxMg`. You can get this by opening a video and clicking on the
channel's avatar image to take you to their channel, then check the url.
"""
return self._channel_id.value
@property
def channel_avatar_path(self) -> Optional[OverridesStringFormatterValidator]:
"""
Optional. Path to store the channel's avatar thumbnail image to.
"""
return self._channel_avatar_path
@property
def channel_banner_path(self) -> Optional[OverridesStringFormatterValidator]:
"""
Optional. Path to store the channel's banner image to.
"""
return self._channel_banner_path
class YoutubeChannelDownloader(YoutubeDownloader[YoutubeChannelDownloaderOptions, YoutubeVideo]):
downloader_options_type = YoutubeChannelDownloaderOptions
@ -167,20 +203,11 @@ class YoutubeChannelDownloader(YoutubeDownloader[YoutubeChannelDownloaderOptions
"""Returns full channel url"""
return f"https://youtube.com/channel/{channel_id}"
@property
def channel_id(self) -> str:
"""
Returns
-------
Channel ID
"""
return self.download_options.channel_id.value
def download(self) -> List[YoutubeVideo]:
"""
Downloads all videos from a channel
"""
channel_url = self.channel_url(channel_id=self.channel_id)
channel_url = self.channel_url(channel_id=self.download_options.channel_id)
channel_videos: List[YoutubeVideo] = []
ytdl_options_overrides = {}

View file

@ -202,9 +202,9 @@ class Subscription:
# If output options maintains stale file deletion, perform the delete here prior to saving
# the download archive
if self.output_options.maintain_download_archive:
if self.output_options.delete_stale_files:
if self.output_options.keep_files:
self._enhanced_download_archive.remove_stale_files(
date_range=self.output_options.delete_stale_files.get_date_range()
date_range=self.output_options.keep_files.get_date_range()
)
self._enhanced_download_archive.save_download_mappings()

View file

@ -11,8 +11,18 @@ class DateRangeValidator(StrictDictValidator):
def __init__(self, name, value):
super().__init__(name, value)
self.before = self._validate_key_if_present("before", StringDatetimeValidator)
self.after = self._validate_key_if_present("after", StringDatetimeValidator)
self._before = self._validate_key_if_present("before", StringDatetimeValidator)
self._after = self._validate_key_if_present("after", StringDatetimeValidator)
@property
def before(self) -> Optional[StringDatetimeValidator]:
"""Optional. Only download videos before this datetime."""
return self._before
@property
def after(self) -> Optional[StringDatetimeValidator]:
"""Optional. Only download videos after this datetime."""
return self._after
def get_date_range(self) -> Optional[DateRange]:
"""
@ -20,9 +30,9 @@ class DateRangeValidator(StrictDictValidator):
-------
Date range if the 'before' or 'after' is defined. None otherwise.
"""
if self.before or self.after:
if self._before or self._after:
return DateRange(
start=self.after.datetime_str if self.after else None,
end=self.before.datetime_str if self.before else None,
start=self._after.datetime_str if self._after else None,
end=self._before.datetime_str if self._before else None,
)
return None

View file

@ -355,7 +355,7 @@ class EnhancedDownloadArchive:
-------
The download mapping's file name (no path)
"""
return f".ytdl-subscribe-{self.subscription_name}-download-mapping.json"
return f".ytdl-sub-{self.subscription_name}-download-archive.json"
@property
def _mapping_output_file_path(self):

View file

@ -66,7 +66,7 @@ def expected_full_channel_download():
return ExpectedDownload(
expected_md5_file_hashes={
# Download mapping
Path("pz/.ytdl-subscribe-pz-download-mapping.json"): "add71021318bf87a3facb965fd38bd7f",
Path("pz/.ytdl-sub-pz-download-archive.json"): "add71021318bf87a3facb965fd38bd7f",
# Output directory files
Path("pz/fanart.jpg"): "e6e323373c8902568e96e374817179cf",
@ -158,7 +158,7 @@ def expected_recent_channel_download():
return ExpectedDownload(
expected_md5_file_hashes={
# Download mapping
Path("pz/.ytdl-subscribe-pz-download-mapping.json"): "a133d9ea8a63e239cd41b799b9031fd5",
Path("pz/.ytdl-sub-pz-download-archive.json"): "a133d9ea8a63e239cd41b799b9031fd5",
# Output directory files
Path("pz/fanart.jpg"): "e6e323373c8902568e96e374817179cf",
@ -205,7 +205,7 @@ def expected_rolling_recent_channel_download():
return ExpectedDownload(
expected_md5_file_hashes={
# Download mapping
Path("pz/.ytdl-subscribe-pz-download-mapping.json"): "8013b4d2ba6921c9347c014ac915e3f6",
Path("pz/.ytdl-sub-pz-download-archive.json"): "8013b4d2ba6921c9347c014ac915e3f6",
# Output directory files
Path("pz/fanart.jpg"): "e6e323373c8902568e96e374817179cf",