Add documentation for plugins + improve docs overall (#23)

This commit is contained in:
Jesse Bannon 2022-05-10 22:22:03 -07:00 committed by GitHub
parent f67a4dc7dc
commit 29ef377cc7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 434 additions and 175 deletions

View file

@ -8,18 +8,18 @@
Automate downloading and adding metadata with YoutubeDL.
This package strives to download media via
This package downloads media via
[yt-dlp](https://github.com/yt-dlp/yt-dlp)
and prepare it for consumption in your favorite media player
and prepares it for consumption in your favorite media player
([Kodi](https://github.com/xbmc/xbmc),
[Jellyfin](https://github.com/jellyfin/jellyfin),
[Plex](https://github.com/plexinc/pms-docker),
[Emby](https://github.com/plexinc/pms-docker),
modern music players)
in the most hassle-free way possible. We recognize that everyone stores their
modern music players).
We recognize that everyone stores their
media differently. Our approach for file and metadata formatting is to provide
maximum flexibility while maintaining simplicity. Read more about it
[here](https://ytdl-sub.readthedocs.io/en/latest/).
[here](https://ytdl-sub.readthedocs.io/en/latest/getting_started.html).
## Supported Features
Below lists supported download schemes. You can see our

View file

@ -61,3 +61,7 @@ add_module_names = False
python_use_unqualified_type_names = False
napoleon_numpy_docstrings = True
napoleon_use_rtype = False
# -- Options for autodocs -------------------------------------------------
autodoc_default_options = {"autodoc_typehints_format": "short"}

View file

@ -4,6 +4,9 @@ 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.
config.yaml
-----------
The ``config.yaml`` is made up of two sections:
.. code-block:: yaml
@ -15,159 +18,129 @@ 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
"""""""
'''''''
Download strategies for downloading videos (or audio if you configure `ytdl_options`_ correctly) from Youtube. See
Download strategies for downloading music from Soundcloud. See
:class:`Youtube Variables <ytdl_sub.entries.variables.youtube_variables>`
for available source variables to use.
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
_______
.. autoclass:: ytdl_sub.downloaders.youtube_downloader.YoutubeChannelDownloaderOptions()
:members:
:member-order: bysource
:inherited-members:
:exclude-members: get_date_range
-------------------------------------------------------------------------------
playlist
''''''''
.. code-block:: yaml
presets:
my_example_preset:
youtube:
download_strategy: "playlist"
playlist_id
___________
.. autoproperty:: ytdl_sub.downloaders.youtube_downloader.YoutubePlaylistDownloaderOptions.playlist_id
________
.. autoclass:: ytdl_sub.downloaders.youtube_downloader.YoutubePlaylistDownloaderOptions()
:members:
:member-order: bysource
:inherited-members:
-------------------------------------------------------------------------------
video
'''''
.. code-block:: yaml
presets:
my_example_preset:
youtube:
download_strategy: "video"
video_id
________
.. autoproperty:: ytdl_sub.downloaders.youtube_downloader.YoutubeVideoDownloaderOptions.video_id
_____
.. autoclass:: ytdl_sub.downloaders.youtube_downloader.YoutubeVideoDownloaderOptions()
:members:
:member-order: bysource
:inherited-members:
-------------------------------------------------------------------------------
soundcloud
""""""""""
''''''''''
Download strategies for downloading music from Soundcloud. See
:class:`Soundcloud Variables <ytdl_sub.entries.variables.soundcloud_variables>`
for available source variables to use.
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
__________________
.. autoclass:: ytdl_sub.downloaders.soundcloud_downloader.SoundcloudAlbumsAndSinglesDownloadOptions()
:members:
:member-order: bysource
:inherited-members:
-------------------------------------------------------------------------------
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
.. autoclass:: ytdl_sub.config.preset_options.OutputOptions()
:members:
:member-order: bysource
:exclude-members: get_upload_date_range_to_keep
-------------------------------------------------------------------------------
ytdl_options
^^^^^^^^^^^^
""""""""""""
.. autoclass:: ytdl_sub.config.preset_options.YTDLOptions()
-------------------------------------------------------------------------------
.. _overrides:
overrides
^^^^^^^^^
"""""""""
.. autoclass:: ytdl_sub.config.preset_options.Overrides()
-------------------------------------------------------------------------------
Plugins
^^^^^^^
"""""""
Plugins are used to perform any type of post-processing to the already downloaded files.
music_tags
""""""""""
''''''''''
.. autoclass:: ytdl_sub.plugins.music_tags.MusicTagsOptions()
:members:
-------------------------------------------------------------------------------
nfo
"""
'''
.. autoclass:: ytdl_sub.plugins.nfo_tags.NfoTagsOptions()
:members:
:member-order: bysource
-------------------------------------------------------------------------------
nfo_output_directory
""""""""""""""""""""
''''''''''''''''''''
.. autoclass:: ytdl_sub.plugins.output_directory_nfo_tags.OutputDirectoryNfoTagsOptions()
:members:
:member-order: bysource
-------------------------------------------------------------------------------
.. _source-variables:
Source Variables
----------------
Source variables are ``{variables}`` that contain metadata from downloaded media.
These variables can be used in StringFormatters, but not OverrideFormatters.
.. autoclass:: ytdl_sub.entries.variables.entry_variables.SourceVariables
.. _youtube-variables:
Youtube Variables
^^^^^^^^^^^^^^^^^
@ -176,6 +149,8 @@ Youtube Variables
:inherited-members:
:undoc-members:
.. _soundcloud-variables:
Soundcloud Variables
^^^^^^^^^^^^^^^^^^^^
.. automodule:: ytdl_sub.entries.variables.soundcloud_variables
@ -183,16 +158,18 @@ Soundcloud Variables
: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.
-------------------------------------------------------------------------------
Config Types
------------
The `config.yaml`_ uses various types for its configurable fields. Below is a definition for each type.
String Formatter
^^^^^^^^^^^^^^^^
.. autoclass:: ytdl_sub.validators.string_formatter_validators.StringFormatterValidator()
Overrides Formatter
^^^^^^^^^^^^^^^^^^^
.. autoclass:: ytdl_sub.validators.string_formatter_validators.OverridesStringFormatterValidator()
.. autoclass:: ytdl_sub.validators.string_datetime.StringDatetimeValidator()
.. autoclass:: ytdl_sub.validators.string_formatter_validators.DictFormatterValidator()
.. autoclass:: ytdl_sub.validators.string_formatter_validators.OverridesDictFormatterValidator()

View file

@ -2,20 +2,20 @@ ytdl-sub: to enjoy media in your favorite player
================================================
ytdl-sub is an `open-source <https://github.com/jmbannon/ytdl-sub>`_
python app that strives to automate downloading media and preparing it for
python app that automates 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
:doc:`Getting Started <getting_started>`
page. This guide shows how you can install ytdl-sub, configure it to download
page. This guide explains how you can use ytdl-sub, install, configure it to download
various types of media, and start downloading.
Contents
========
.. toctree::
:maxdepth: 10
:maxdepth: 2
getting_started
config

View file

@ -70,7 +70,7 @@ john_smith_recent_archive:
# 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
# - 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.
@ -85,5 +85,4 @@ john_smith_rolling_archive:
break_on_reject: True
break_on_existing: True
output_options:
keep_files:
after: today-2weeks
keep_files_after: today-2weeks

View file

@ -1,10 +1,11 @@
from typing import Optional
from yt_dlp.utils import DateRange
from yt_dlp.utils import sanitize_filename
from ytdl_sub.entries.entry import Entry
from ytdl_sub.validators.date_range_validator import DateRangeValidator
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
from ytdl_sub.validators.string_datetime import StringDatetimeValidator
from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator
from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
@ -86,13 +87,32 @@ class Overrides(DictFormatterValidator):
class OutputOptions(StrictDictValidator):
"""Where to output the final files and thumbnails"""
"""
Defines where to output files and thumbnails after all post-processing has completed.
Usage:
.. code-block:: yaml
presets:
my_example_preset:
output_options:
# required
output_directory: "/path/to/videos_or_music"
file_name: "{sanitized_title}.{ext}"
# optional
thumbnail_name: "{sanitized_title}.{thumbnail_ext}"
maintain_download_archive: True
keep_files_before: now
keep_files_after: 19000101
"""
_required_keys = {"output_directory", "file_name"}
_optional_keys = {
"thumbnail_name",
"maintain_download_archive",
"keep_files",
"keep_files_before",
"keep_files_after",
}
def __init__(self, name, value):
@ -115,11 +135,17 @@ class OutputOptions(StrictDictValidator):
self._maintain_download_archive = self._validate_key_if_present(
key="maintain_download_archive", validator=BoolValidator, default=False
)
self._keep_files = self._validate_key_if_present(
key="keep_files", validator=DateRangeValidator
self._keep_files_before = self._validate_key_if_present(
"keep_files_before", StringDatetimeValidator
)
self._keep_files_after = self._validate_key_if_present(
"keep_files_after", StringDatetimeValidator
)
if self._keep_files and not self.maintain_download_archive:
if (
self._keep_files_before or self._keep_files_after
) and not self.maintain_download_archive:
raise self._validation_exception(
"keep_files requires maintain_download_archive set to True"
)
@ -164,34 +190,34 @@ class OutputOptions(StrictDictValidator):
return self._maintain_download_archive.value
@property
def keep_files(self) -> DateRangeValidator:
def keep_files_before(self) -> Optional[StringDatetimeValidator]:
"""
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.
Only keeps files that are uploaded before this datetime. By default, ytdl-sub will keep
files before ``now``, which implies all files.
"""
return self._keep_files
return self._keep_files_before
@property
def keep_files_after(self) -> Optional[StringDatetimeValidator]:
"""
Optional. Requires ``maintain_download_archive`` set to True.
Only keeps files that are uploaded after this datetime. By default, ytdl-sub will keep
files after ``19000101``, which implies all files.
"""
return self._keep_files_after
def get_upload_date_range_to_keep(self) -> Optional[DateRange]:
"""
Returns
-------
Date range if the 'before' or 'after' is defined. None otherwise.
"""
if self.keep_files_before or self.keep_files_after:
return DateRange(
start=self.keep_files_after.datetime_str if self.keep_files_after else None,
end=self.keep_files_before.datetime_str if self.keep_files_before else None,
)
return None

View file

@ -71,6 +71,25 @@ class SoundcloudDownloader(
class SoundcloudAlbumsAndSinglesDownloadOptions(SoundcloudDownloaderOptions):
"""
Downloads a soundcloud user's entire discography. Groups together album tracks and considers
any track not in an album as a single. Also includes any collaboration tracks.
Usage:
.. code-block:: yaml
presets:
my_example_preset:
soundcloud:
# required
download_strategy: "albums_and_singles"
username: soundcloud_username_from_url
# optional
skip_premiere_tracks: True
"""
_required_keys = {"username"}
def __init__(self, name, value):

View file

@ -55,6 +55,29 @@ class YoutubeDownloader(
class YoutubeVideoDownloaderOptions(YoutubeDownloaderOptions):
"""
Downloads a single youtube video. This download strategy is intended for CLI usage performing
a one-time download of a video, not a subscription.
Usage:
.. code-block:: yaml
presets:
my_example_preset:
youtube:
# required
download_strategy: "video"
video_id: "VMAPTo7RVDo"
CLI usage:
.. code-block:: bash
ytdl-sub dl --preset "my_example_preset" --youtube.video_id "VMAPTo7RVDo"
"""
_required_keys = {"video_id"}
def __init__(self, name, value):
@ -64,7 +87,7 @@ class YoutubeVideoDownloaderOptions(YoutubeDownloaderOptions):
@property
def video_id(self) -> str:
"""
Required. The ID of the video. Looks like the ``VMAPTo7RVDo`` in
Required. The ID of the video. Looks like the ``VMAPTo7RVDo`` from
``youtube.com/watch?v=VMAPTo7RVDo``.
"""
return self._video_id.value
@ -92,6 +115,21 @@ class YoutubeVideoDownloader(YoutubeDownloader[YoutubeVideoDownloaderOptions, Yo
class YoutubePlaylistDownloaderOptions(YoutubeDownloaderOptions):
"""
Downloads all videos from a youtube playlist.
Usage:
.. code-block:: yaml
presets:
my_example_preset:
youtube:
# required
download_strategy: "playlist"
playlist_id: "UCsvn_Po0SmunchJYtttWpOxMg"
"""
_required_keys = {"playlist_id"}
def __init__(self, name, value):
@ -101,7 +139,8 @@ class YoutubePlaylistDownloaderOptions(YoutubeDownloaderOptions):
@property
def playlist_id(self) -> str:
"""
Required. The playlist's ID.
Required. The playlist's ID. Looks like "UCsvn_Po0SmunchJYtttWpOxMg"
from ``https://www.youtube.com/playlist?list=UCsvn_Po0SmunchJYtttWpOxMg``.
"""
return self._playlist_id.value
@ -141,6 +180,26 @@ class YoutubePlaylistDownloader(
class YoutubeChannelDownloaderOptions(YoutubeDownloaderOptions, DateRangeValidator):
"""
Downloads all videos from a youtube channel.
Usage:
.. code-block:: yaml
presets:
my_example_preset:
youtube:
# required
download_strategy: "channel"
channel_id: "UCsvn_Po0SmunchJYtttWpOxMg"
# optional
channel_avatar_path: "poster.jpg"
channel_banner_path: "fanart.jpg"
before: "now"
after: "today-2weeks"
"""
_required_keys = {"channel_id"}
_optional_keys = {"before", "after", "channel_avatar_path", "channel_banner_path"}
@ -159,8 +218,10 @@ class YoutubeChannelDownloaderOptions(YoutubeDownloaderOptions, DateRangeValidat
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.
like ``UCsvn_Po0SmunchJYOWpOxMg`` from
``https://www.youtube.com/channel/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

View file

@ -9,9 +9,13 @@ from ytdl_sub.entries.base_entry import BaseEntry
# pylint: disable=no-member
class BaseEntryVariables:
class SourceVariables:
"""
Abstract entry object to represent usable variables for formatting presets and subscriptions
Source variables are ``{variables}`` that contain metadata from downloaded media.
These variables can be used with fields that expect
:class:`~ytdl_sub.validators.string_formatter_validators.StringFormatterValidator`,
but not
:class:`~ytdl_sub.validators.string_formatter_validators.OverridesStringFormatterValidator`.
"""
@property
@ -45,7 +49,7 @@ class BaseEntryVariables:
return {property_name: getattr(self, property_name) for property_name in property_names}
class EntryVariables(BaseEntryVariables):
class EntryVariables(SourceVariables):
@property
def title(self: BaseEntry) -> str:
"""

View file

@ -8,18 +8,47 @@ from ytdl_sub.validators.validators import StringValidator
class MusicTagsOptions(PluginOptions):
"""
Adds tags to every download audio file using
`MediaFile <https://mediafile.readthedocs.io/en/latest/>`_,
the same audio file tagging package used by
`beets <https://beets.readthedocs.io/en/stable/>`_.
It supports basic tags like ``title``, ``album``, ``artist`` and ``albumartist``. You can find
a full list of tags for various file types in MediaFile's
`source code <https://github.com/beetbox/mediafile/blob/v0.9.0/mediafile.py#L1770>`_.
Usage:
.. code-block:: yaml
presets:
my_example_preset:
music_tags:
tags:
artist: "{artist}"
album: "{album}"
genre: "ytdl downloaded music"
"""
_required_keys = {"tags"}
_optional_keys = {"multi_value_separator"}
def __init__(self, name, value):
super().__init__(name, value)
self.tags = self._validate_key(key="tags", validator=DictFormatterValidator)
self._tags = self._validate_key(key="tags", validator=DictFormatterValidator)
self.multi_value_separator = self._validate_key_if_present(
key="multi_value_separator", validator=StringValidator
)
@property
def tags(self) -> DictFormatterValidator:
"""
Key/values of tag names/tag values. Supports source and override variables.
"""
return self._tags
class MusicTagsPlugin(Plugin[MusicTagsOptions]):
plugin_options_type = MusicTagsOptions

View file

@ -11,14 +11,69 @@ from ytdl_sub.validators.string_formatter_validators import StringFormatterValid
class NfoTagsOptions(PluginOptions):
"""
Adds an NFO file for every download file. An NFO file is simply an XML file
with a ``.nfo`` extension. You can add any values into the NFO.
Usage:
.. code-block:: yaml
presets:
my_example_preset:
nfo:
nfo_name: "{sanitized_title}.nfo"
nfo_root: "episodedetails"
tags:
title: "{title}"
season: "{upload_year}"
episode: "{upload_month}{upload_day_padded}"
"""
_required_keys = {"nfo_name", "nfo_root", "tags"}
def __init__(self, name, value):
super().__init__(name, value)
self.nfo_name = self._validate_key(key="nfo_name", validator=StringFormatterValidator)
self.nfo_root = self._validate_key(key="nfo_root", validator=StringFormatterValidator)
self.tags = self._validate_key(key="tags", validator=DictFormatterValidator)
self._nfo_name = self._validate_key(key="nfo_name", validator=StringFormatterValidator)
self._nfo_root = self._validate_key(key="nfo_root", validator=StringFormatterValidator)
self._tags = self._validate_key(key="tags", validator=DictFormatterValidator)
@property
def nfo_name(self) -> StringFormatterValidator:
"""
The NFO file name.
"""
return self._nfo_name
@property
def nfo_root(self) -> StringFormatterValidator:
"""
The root tag of the NFO's XML. In the usage above, it would look like
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<episodedetails>
</episodedetails>
"""
return self._nfo_root
@property
def tags(self) -> DictFormatterValidator:
"""
Tags within the nfo_root tag. In the usage above, it would look like
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<episodedetails>
<title>Awesome Youtube Video</title>
<season>2022</season>
<episode>502</episode>
</episodedetails>
"""
return self._tags
class NfoTagsPlugin(Plugin[NfoTagsOptions]):

View file

@ -11,7 +11,20 @@ from ytdl_sub.validators.string_formatter_validators import OverridesStringForma
class OutputDirectoryNfoTagsOptions(PluginOptions):
"""
Validates config values for the output directory nfo tags plugin.
Adds a single NFO file in the output directory. An NFO file is simply an XML file with a
``.nfo`` extension. You can add any values into the NFO.
Usage:
.. code-block:: yaml
presets:
my_example_preset:
output_directory_nfo_tags:
nfo_name: "tvshow.nfo"
nfo_root: "tvshow"
tags:
title: "Sweet youtube TV show"
"""
_required_keys = {"nfo_name", "nfo_root", "tags"}
@ -20,13 +33,47 @@ class OutputDirectoryNfoTagsOptions(PluginOptions):
super().__init__(name, value)
# Since this does not create NFOs for entries, we must use the overrides formatter classes
# to ensure we are only using values defined as string literals or in overrides
self.nfo_name = self._validate_key(
self._nfo_name = self._validate_key(
key="nfo_name", validator=OverridesStringFormatterValidator
)
self.nfo_root = self._validate_key(
self._nfo_root = self._validate_key(
key="nfo_root", validator=OverridesStringFormatterValidator
)
self.tags = self._validate_key(key="tags", validator=OverridesDictFormatterValidator)
self._tags = self._validate_key(key="tags", validator=OverridesDictFormatterValidator)
@property
def nfo_name(self) -> OverridesStringFormatterValidator:
"""
The NFO file name.
"""
return self._nfo_name
@property
def nfo_root(self) -> OverridesStringFormatterValidator:
"""
The root tag of the NFO's XML. In the usage above, it would look like
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<tvshow>
</tvshow>
"""
return self._nfo_root
@property
def tags(self) -> OverridesDictFormatterValidator:
"""
Tags within the nfo_root tag. In the usage above, it would look like
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<tvshow>
<title>Sweet youtube TV show</title>
</tvshow>
"""
return self._tags
class OutputDirectoryNfoTagsPlugin(Plugin[OutputDirectoryNfoTagsOptions]):

View file

@ -202,10 +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.keep_files:
self._enhanced_download_archive.remove_stale_files(
date_range=self.output_options.keep_files.get_date_range()
)
date_range_to_keep = self.output_options.get_upload_date_range_to_keep()
if date_range_to_keep:
self._enhanced_download_archive.remove_stale_files(date_range=date_range_to_keep)
self._enhanced_download_archive.save_download_mappings()

View file

@ -5,7 +5,14 @@ from ytdl_sub.validators.validators import Validator
class StringDatetimeValidator(Validator):
"""
Validates a ytdl datetime string value
String that contains a yt-dlp datetime. From their docs:
.. code-block:: Markdown
A string in the format YYYYMMDD or
(now|today|yesterday|date)[+-][0-9](microsecond|second|minute|hour|day|week|month|year)(s)
Valid examples are ``now-2weeks`` or ``20200101``.
"""
_expected_value_type = str

View file

@ -13,7 +13,28 @@ from ytdl_sub.validators.validators import Validator
class StringFormatterValidator(Validator):
"""
Ensures user-created formatter strings are valid
String that can use
:class:`source variables <ytdl_sub.entries.variables.entry_variables.SourceVariables>`
and
:class:`overrides <ytdl_sub.config.preset_options.Overrides>`
for populating things like file paths and metadata.
.. code-block:: python
"{tv_show_file_name}.s{upload_year}.e{upload_month}{upload_day_padded}.{ext}"
is valid when using
:class:`youtube variables <ytdl_sub.entries.variables.youtube_variables.YoutubeVideoVariables>`
with the following overrides:
.. code-block:: yaml
presets:
my_example_preset:
overrides:
tv_show_file_name: "sweet_tv_show"
and would resolve to something like ``sweet_tv_show.s2022.e502.mp4``.
"""
_expected_value_type = str
@ -132,15 +153,26 @@ class StringFormatterValidator(Validator):
return formatter.format_string
# pylint: disable=line-too-long
class OverridesStringFormatterValidator(StringFormatterValidator):
"""
A string formatter that should strictly use overrides that resolve without any entry variables.
String that can `only` use :class:`overrides <ytdl_sub.config.preset_options.Overrides>`.
Used in fields that do not touch the downloaded files themselves, but instead, `single`
things like
:func:`output_directory <ytdl_sub.config.preset_options.OutputOptions.output_directory>`
or the fields in
:class:`nfo_output_directory <ytdl_sub.plugins.output_directory_nfo_tags.OutputDirectoryNfoTagsOptions>`
"""
# pylint: enable=line-too-long
class DictFormatterValidator(LiteralDictValidator):
"""
Validates a dictionary made up of key: string_formatters
A dict made up of
:class:`~ytdl_sub.validators.string_formatter_validators.StringFormatterValidator`.
"""
_key_validator = StringFormatterValidator
@ -164,8 +196,8 @@ class DictFormatterValidator(LiteralDictValidator):
class OverridesDictFormatterValidator(DictFormatterValidator):
"""
Validates a dictionary made up of key: string_formatters, that must be resolved by overrides
only.
A dict made up of
:class:`~ytdl_sub.validators.string_formatter_validators.OverridesStringFormatterValidator`.
"""
_key_validator = OverridesStringFormatterValidator

View file

@ -183,7 +183,7 @@ def expected_recent_channel_download():
@pytest.fixture
def rolling_recent_channel_subscription_dict(recent_channel_subscription_dict):
return mergedeep.merge(
recent_channel_subscription_dict, {"output_options": {"keep_files": {"after": "20181101"}}}
recent_channel_subscription_dict, {"output_options": {"keep_files_after": "20181101"}}
)