Add documentation for plugins + improve docs overall (#23)
This commit is contained in:
parent
f67a4dc7dc
commit
29ef377cc7
16 changed files with 434 additions and 175 deletions
10
README.md
10
README.md
|
|
@ -8,18 +8,18 @@
|
||||||
|
|
||||||
Automate downloading and adding metadata with YoutubeDL.
|
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)
|
[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),
|
([Kodi](https://github.com/xbmc/xbmc),
|
||||||
[Jellyfin](https://github.com/jellyfin/jellyfin),
|
[Jellyfin](https://github.com/jellyfin/jellyfin),
|
||||||
[Plex](https://github.com/plexinc/pms-docker),
|
[Plex](https://github.com/plexinc/pms-docker),
|
||||||
[Emby](https://github.com/plexinc/pms-docker),
|
[Emby](https://github.com/plexinc/pms-docker),
|
||||||
modern music players)
|
modern music players).
|
||||||
in the most hassle-free way possible. We recognize that everyone stores their
|
We recognize that everyone stores their
|
||||||
media differently. Our approach for file and metadata formatting is to provide
|
media differently. Our approach for file and metadata formatting is to provide
|
||||||
maximum flexibility while maintaining simplicity. Read more about it
|
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
|
## Supported Features
|
||||||
Below lists supported download schemes. You can see our
|
Below lists supported download schemes. You can see our
|
||||||
|
|
|
||||||
|
|
@ -61,3 +61,7 @@ add_module_names = False
|
||||||
python_use_unqualified_type_names = False
|
python_use_unqualified_type_names = False
|
||||||
napoleon_numpy_docstrings = True
|
napoleon_numpy_docstrings = True
|
||||||
napoleon_use_rtype = False
|
napoleon_use_rtype = False
|
||||||
|
|
||||||
|
# -- Options for autodocs -------------------------------------------------
|
||||||
|
|
||||||
|
autodoc_default_options = {"autodoc_typehints_format": "short"}
|
||||||
|
|
|
||||||
187
docs/config.rst
187
docs/config.rst
|
|
@ -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
|
:doc:`examples <examples>` and read detailed documentation for every configurable
|
||||||
field below.
|
field below.
|
||||||
|
|
||||||
|
config.yaml
|
||||||
|
-----------
|
||||||
|
|
||||||
The ``config.yaml`` is made up of two sections:
|
The ``config.yaml`` is made up of two sections:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. 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.
|
section to the left.
|
||||||
|
|
||||||
configuration
|
configuration
|
||||||
-------------
|
^^^^^^^^^^^^^
|
||||||
The ``configuration`` section contains app-wide configs applied to all presets
|
The ``configuration`` section contains app-wide configs applied to all presets
|
||||||
and subscriptions.
|
and subscriptions.
|
||||||
|
|
||||||
presets
|
presets
|
||||||
-------
|
^^^^^^^
|
||||||
``presets`` define a `formula` for how to format downloaded media and metadata.
|
``presets`` define a `formula` for how to format downloaded media and metadata.
|
||||||
|
|
||||||
download_strategy
|
download_strategy
|
||||||
^^^^^^^^^^^^^^^^^
|
"""""""""""""""""
|
||||||
Download strategies dictate what is getting downloaded from a source. Each
|
Download strategies dictate what is getting downloaded from a source. Each
|
||||||
download strategy has its own set of parameters.
|
download strategy has its own set of parameters.
|
||||||
|
|
||||||
youtube
|
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
|
channel
|
||||||
'''''''
|
_______
|
||||||
.. code-block:: yaml
|
.. autoclass:: ytdl_sub.downloaders.youtube_downloader.YoutubeChannelDownloaderOptions()
|
||||||
|
:members:
|
||||||
presets:
|
:member-order: bysource
|
||||||
my_example_preset:
|
:inherited-members:
|
||||||
youtube:
|
:exclude-members: get_date_range
|
||||||
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
|
playlist
|
||||||
''''''''
|
________
|
||||||
.. code-block:: yaml
|
.. autoclass:: ytdl_sub.downloaders.youtube_downloader.YoutubePlaylistDownloaderOptions()
|
||||||
|
:members:
|
||||||
presets:
|
:member-order: bysource
|
||||||
my_example_preset:
|
:inherited-members:
|
||||||
youtube:
|
|
||||||
download_strategy: "playlist"
|
|
||||||
|
|
||||||
playlist_id
|
|
||||||
___________
|
|
||||||
.. autoproperty:: ytdl_sub.downloaders.youtube_downloader.YoutubePlaylistDownloaderOptions.playlist_id
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
video
|
video
|
||||||
'''''
|
_____
|
||||||
.. code-block:: yaml
|
.. autoclass:: ytdl_sub.downloaders.youtube_downloader.YoutubeVideoDownloaderOptions()
|
||||||
|
:members:
|
||||||
presets:
|
:member-order: bysource
|
||||||
my_example_preset:
|
:inherited-members:
|
||||||
youtube:
|
|
||||||
download_strategy: "video"
|
|
||||||
|
|
||||||
video_id
|
|
||||||
________
|
|
||||||
.. autoproperty:: ytdl_sub.downloaders.youtube_downloader.YoutubeVideoDownloaderOptions.video_id
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
soundcloud
|
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
|
albums_and_singles
|
||||||
''''''''''''''''''
|
__________________
|
||||||
.. code-block:: yaml
|
.. autoclass:: ytdl_sub.downloaders.soundcloud_downloader.SoundcloudAlbumsAndSinglesDownloadOptions()
|
||||||
|
:members:
|
||||||
presets:
|
:member-order: bysource
|
||||||
my_example_preset:
|
:inherited-members:
|
||||||
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_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
|
.. autoclass:: ytdl_sub.config.preset_options.OutputOptions()
|
||||||
"""""""""""""""""""""""""
|
:members:
|
||||||
.. autoproperty:: ytdl_sub.config.preset_options.OutputOptions.maintain_download_archive
|
:member-order: bysource
|
||||||
|
:exclude-members: get_upload_date_range_to_keep
|
||||||
keep_files
|
|
||||||
""""""""""
|
|
||||||
.. autoproperty:: ytdl_sub.config.preset_options.OutputOptions.keep_files
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
ytdl_options
|
ytdl_options
|
||||||
^^^^^^^^^^^^
|
""""""""""""
|
||||||
.. autoclass:: ytdl_sub.config.preset_options.YTDLOptions()
|
.. autoclass:: ytdl_sub.config.preset_options.YTDLOptions()
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
.. _overrides:
|
||||||
|
|
||||||
overrides
|
overrides
|
||||||
^^^^^^^^^
|
"""""""""
|
||||||
.. autoclass:: ytdl_sub.config.preset_options.Overrides()
|
.. autoclass:: ytdl_sub.config.preset_options.Overrides()
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
Plugins
|
Plugins
|
||||||
^^^^^^^
|
"""""""
|
||||||
|
Plugins are used to perform any type of post-processing to the already downloaded files.
|
||||||
|
|
||||||
music_tags
|
music_tags
|
||||||
""""""""""
|
''''''''''
|
||||||
.. autoclass:: ytdl_sub.plugins.music_tags.MusicTagsOptions()
|
.. autoclass:: ytdl_sub.plugins.music_tags.MusicTagsOptions()
|
||||||
|
:members:
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
nfo
|
nfo
|
||||||
"""
|
'''
|
||||||
.. autoclass:: ytdl_sub.plugins.nfo_tags.NfoTagsOptions()
|
.. autoclass:: ytdl_sub.plugins.nfo_tags.NfoTagsOptions()
|
||||||
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
nfo_output_directory
|
nfo_output_directory
|
||||||
""""""""""""""""""""
|
''''''''''''''''''''
|
||||||
.. autoclass:: ytdl_sub.plugins.output_directory_nfo_tags.OutputDirectoryNfoTagsOptions()
|
.. autoclass:: ytdl_sub.plugins.output_directory_nfo_tags.OutputDirectoryNfoTagsOptions()
|
||||||
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
.. _source-variables:
|
||||||
|
|
||||||
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
|
Youtube Variables
|
||||||
^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -176,6 +149,8 @@ Youtube Variables
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
|
|
||||||
|
.. _soundcloud-variables:
|
||||||
|
|
||||||
Soundcloud Variables
|
Soundcloud Variables
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
.. automodule:: ytdl_sub.entries.variables.soundcloud_variables
|
.. automodule:: ytdl_sub.entries.variables.soundcloud_variables
|
||||||
|
|
@ -183,16 +158,18 @@ Soundcloud Variables
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
|
|
||||||
Formatters
|
-------------------------------------------------------------------------------
|
||||||
----------
|
|
||||||
Formatters are strings that can contain ``{variables}`` that are overwritten at
|
Config Types
|
||||||
run-time with values assigned to that variable. There are two different types of
|
------------
|
||||||
formatters.
|
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()
|
.. autoclass:: ytdl_sub.validators.string_formatter_validators.StringFormatterValidator()
|
||||||
|
|
||||||
Overrides Formatter
|
|
||||||
^^^^^^^^^^^^^^^^^^^
|
|
||||||
.. autoclass:: ytdl_sub.validators.string_formatter_validators.OverridesStringFormatterValidator()
|
.. 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()
|
||||||
|
|
@ -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>`_
|
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
|
consumption in your favorite media player in the most hassle-free way
|
||||||
possible.
|
possible.
|
||||||
|
|
||||||
If you are new to ytdl-sub, head over to the
|
If you are new to ytdl-sub, head over to the
|
||||||
:doc:`Getting Started <getting_started>`
|
: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.
|
various types of media, and start downloading.
|
||||||
|
|
||||||
Contents
|
Contents
|
||||||
========
|
========
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 10
|
:maxdepth: 2
|
||||||
|
|
||||||
getting_started
|
getting_started
|
||||||
config
|
config
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ john_smith_recent_archive:
|
||||||
# only keep the last 14-days worth of videos, and delete the rest.
|
# only keep the last 14-days worth of videos, and delete the rest.
|
||||||
#
|
#
|
||||||
# The only difference between this example and the one above is
|
# 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".
|
# 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
|
# All other files that this subscription had previously downloaded will be
|
||||||
# deleted.
|
# deleted.
|
||||||
|
|
@ -85,5 +85,4 @@ john_smith_rolling_archive:
|
||||||
break_on_reject: True
|
break_on_reject: True
|
||||||
break_on_existing: True
|
break_on_existing: True
|
||||||
output_options:
|
output_options:
|
||||||
keep_files:
|
keep_files_after: today-2weeks
|
||||||
after: today-2weeks
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from yt_dlp.utils import DateRange
|
||||||
from yt_dlp.utils import sanitize_filename
|
from yt_dlp.utils import sanitize_filename
|
||||||
|
|
||||||
from ytdl_sub.entries.entry import Entry
|
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.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 DictFormatterValidator
|
||||||
from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator
|
from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator
|
||||||
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
|
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
|
||||||
|
|
@ -86,13 +87,32 @@ class Overrides(DictFormatterValidator):
|
||||||
|
|
||||||
|
|
||||||
class OutputOptions(StrictDictValidator):
|
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"}
|
_required_keys = {"output_directory", "file_name"}
|
||||||
_optional_keys = {
|
_optional_keys = {
|
||||||
"thumbnail_name",
|
"thumbnail_name",
|
||||||
"maintain_download_archive",
|
"maintain_download_archive",
|
||||||
"keep_files",
|
"keep_files_before",
|
||||||
|
"keep_files_after",
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
|
|
@ -115,11 +135,17 @@ class OutputOptions(StrictDictValidator):
|
||||||
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
|
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(
|
raise self._validation_exception(
|
||||||
"keep_files requires maintain_download_archive set to True"
|
"keep_files requires maintain_download_archive set to True"
|
||||||
)
|
)
|
||||||
|
|
@ -164,34 +190,34 @@ class OutputOptions(StrictDictValidator):
|
||||||
return self._maintain_download_archive.value
|
return self._maintain_download_archive.value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def keep_files(self) -> DateRangeValidator:
|
def keep_files_before(self) -> Optional[StringDatetimeValidator]:
|
||||||
"""
|
"""
|
||||||
Optional. Requires ``maintain_download_archive`` set to True.
|
Optional. Requires ``maintain_download_archive`` set to True.
|
||||||
|
|
||||||
Only keeps files that are uploaded in the defined range. Should be formatted as:
|
Only keeps files that are uploaded before this datetime. By default, ytdl-sub will keep
|
||||||
|
files before ``now``, which implies all files.
|
||||||
.. 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
|
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
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,25 @@ class SoundcloudDownloader(
|
||||||
|
|
||||||
|
|
||||||
class SoundcloudAlbumsAndSinglesDownloadOptions(SoundcloudDownloaderOptions):
|
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"}
|
_required_keys = {"username"}
|
||||||
|
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,29 @@ class YoutubeDownloader(
|
||||||
|
|
||||||
|
|
||||||
class YoutubeVideoDownloaderOptions(YoutubeDownloaderOptions):
|
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"}
|
_required_keys = {"video_id"}
|
||||||
|
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
|
|
@ -64,7 +87,7 @@ class YoutubeVideoDownloaderOptions(YoutubeDownloaderOptions):
|
||||||
@property
|
@property
|
||||||
def video_id(self) -> str:
|
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``.
|
``youtube.com/watch?v=VMAPTo7RVDo``.
|
||||||
"""
|
"""
|
||||||
return self._video_id.value
|
return self._video_id.value
|
||||||
|
|
@ -92,6 +115,21 @@ class YoutubeVideoDownloader(YoutubeDownloader[YoutubeVideoDownloaderOptions, Yo
|
||||||
|
|
||||||
|
|
||||||
class YoutubePlaylistDownloaderOptions(YoutubeDownloaderOptions):
|
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"}
|
_required_keys = {"playlist_id"}
|
||||||
|
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
|
|
@ -101,7 +139,8 @@ class YoutubePlaylistDownloaderOptions(YoutubeDownloaderOptions):
|
||||||
@property
|
@property
|
||||||
def playlist_id(self) -> str:
|
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
|
return self._playlist_id.value
|
||||||
|
|
||||||
|
|
@ -141,6 +180,26 @@ class YoutubePlaylistDownloader(
|
||||||
|
|
||||||
|
|
||||||
class YoutubeChannelDownloaderOptions(YoutubeDownloaderOptions, DateRangeValidator):
|
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"}
|
_required_keys = {"channel_id"}
|
||||||
_optional_keys = {"before", "after", "channel_avatar_path", "channel_banner_path"}
|
_optional_keys = {"before", "after", "channel_avatar_path", "channel_banner_path"}
|
||||||
|
|
||||||
|
|
@ -159,8 +218,10 @@ class YoutubeChannelDownloaderOptions(YoutubeDownloaderOptions, DateRangeValidat
|
||||||
def channel_id(self) -> str:
|
def channel_id(self) -> str:
|
||||||
"""
|
"""
|
||||||
Required. The channel's ID. Not to be confused with the username. It should look something
|
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
|
like ``UCsvn_Po0SmunchJYOWpOxMg`` from
|
||||||
channel's avatar image to take you to their channel, then check the url.
|
``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
|
return self._channel_id.value
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,13 @@ from ytdl_sub.entries.base_entry import BaseEntry
|
||||||
# pylint: disable=no-member
|
# 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
|
@property
|
||||||
|
|
@ -45,7 +49,7 @@ class BaseEntryVariables:
|
||||||
return {property_name: getattr(self, property_name) for property_name in property_names}
|
return {property_name: getattr(self, property_name) for property_name in property_names}
|
||||||
|
|
||||||
|
|
||||||
class EntryVariables(BaseEntryVariables):
|
class EntryVariables(SourceVariables):
|
||||||
@property
|
@property
|
||||||
def title(self: BaseEntry) -> str:
|
def title(self: BaseEntry) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,47 @@ from ytdl_sub.validators.validators import StringValidator
|
||||||
|
|
||||||
|
|
||||||
class MusicTagsOptions(PluginOptions):
|
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"}
|
_required_keys = {"tags"}
|
||||||
_optional_keys = {"multi_value_separator"}
|
_optional_keys = {"multi_value_separator"}
|
||||||
|
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
super().__init__(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(
|
self.multi_value_separator = self._validate_key_if_present(
|
||||||
key="multi_value_separator", validator=StringValidator
|
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]):
|
class MusicTagsPlugin(Plugin[MusicTagsOptions]):
|
||||||
plugin_options_type = MusicTagsOptions
|
plugin_options_type = MusicTagsOptions
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,69 @@ from ytdl_sub.validators.string_formatter_validators import StringFormatterValid
|
||||||
|
|
||||||
|
|
||||||
class NfoTagsOptions(PluginOptions):
|
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"}
|
_required_keys = {"nfo_name", "nfo_root", "tags"}
|
||||||
|
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
super().__init__(name, value)
|
super().__init__(name, value)
|
||||||
|
|
||||||
self.nfo_name = self._validate_key(key="nfo_name", validator=StringFormatterValidator)
|
self._nfo_name = self._validate_key(key="nfo_name", validator=StringFormatterValidator)
|
||||||
self.nfo_root = self._validate_key(key="nfo_root", validator=StringFormatterValidator)
|
self._nfo_root = self._validate_key(key="nfo_root", validator=StringFormatterValidator)
|
||||||
self.tags = self._validate_key(key="tags", validator=DictFormatterValidator)
|
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]):
|
class NfoTagsPlugin(Plugin[NfoTagsOptions]):
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,20 @@ from ytdl_sub.validators.string_formatter_validators import OverridesStringForma
|
||||||
|
|
||||||
class OutputDirectoryNfoTagsOptions(PluginOptions):
|
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"}
|
_required_keys = {"nfo_name", "nfo_root", "tags"}
|
||||||
|
|
@ -20,13 +33,47 @@ class OutputDirectoryNfoTagsOptions(PluginOptions):
|
||||||
super().__init__(name, value)
|
super().__init__(name, value)
|
||||||
# Since this does not create NFOs for entries, we must use the overrides formatter classes
|
# 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
|
# 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
|
key="nfo_name", validator=OverridesStringFormatterValidator
|
||||||
)
|
)
|
||||||
self.nfo_root = self._validate_key(
|
self._nfo_root = self._validate_key(
|
||||||
key="nfo_root", validator=OverridesStringFormatterValidator
|
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]):
|
class OutputDirectoryNfoTagsPlugin(Plugin[OutputDirectoryNfoTagsOptions]):
|
||||||
|
|
|
||||||
|
|
@ -202,10 +202,9 @@ class Subscription:
|
||||||
# If output options maintains stale file deletion, perform the delete here prior to saving
|
# If output options maintains stale file deletion, perform the delete here prior to saving
|
||||||
# the download archive
|
# the download archive
|
||||||
if self.output_options.maintain_download_archive:
|
if self.output_options.maintain_download_archive:
|
||||||
if self.output_options.keep_files:
|
date_range_to_keep = self.output_options.get_upload_date_range_to_keep()
|
||||||
self._enhanced_download_archive.remove_stale_files(
|
if date_range_to_keep:
|
||||||
date_range=self.output_options.keep_files.get_date_range()
|
self._enhanced_download_archive.remove_stale_files(date_range=date_range_to_keep)
|
||||||
)
|
|
||||||
|
|
||||||
self._enhanced_download_archive.save_download_mappings()
|
self._enhanced_download_archive.save_download_mappings()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,14 @@ from ytdl_sub.validators.validators import Validator
|
||||||
|
|
||||||
class StringDatetimeValidator(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
|
_expected_value_type = str
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,28 @@ from ytdl_sub.validators.validators import Validator
|
||||||
|
|
||||||
class StringFormatterValidator(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
|
_expected_value_type = str
|
||||||
|
|
@ -132,15 +153,26 @@ class StringFormatterValidator(Validator):
|
||||||
return formatter.format_string
|
return formatter.format_string
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=line-too-long
|
||||||
class OverridesStringFormatterValidator(StringFormatterValidator):
|
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):
|
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
|
_key_validator = StringFormatterValidator
|
||||||
|
|
@ -164,8 +196,8 @@ class DictFormatterValidator(LiteralDictValidator):
|
||||||
|
|
||||||
class OverridesDictFormatterValidator(DictFormatterValidator):
|
class OverridesDictFormatterValidator(DictFormatterValidator):
|
||||||
"""
|
"""
|
||||||
Validates a dictionary made up of key: string_formatters, that must be resolved by overrides
|
A dict made up of
|
||||||
only.
|
:class:`~ytdl_sub.validators.string_formatter_validators.OverridesStringFormatterValidator`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_key_validator = OverridesStringFormatterValidator
|
_key_validator = OverridesStringFormatterValidator
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ def expected_recent_channel_download():
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def rolling_recent_channel_subscription_dict(recent_channel_subscription_dict):
|
def rolling_recent_channel_subscription_dict(recent_channel_subscription_dict):
|
||||||
return mergedeep.merge(
|
return mergedeep.merge(
|
||||||
recent_channel_subscription_dict, {"output_options": {"keep_files": {"after": "20181101"}}}
|
recent_channel_subscription_dict, {"output_options": {"keep_files_after": "20181101"}}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue