plugins done
This commit is contained in:
parent
724faaac9e
commit
0669ac3e32
30 changed files with 860 additions and 750 deletions
|
|
@ -53,51 +53,7 @@ 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
|
This section is work-in-progress!
|
||||||
"""""""""""""""""
|
|
||||||
Download strategies dictate what is getting downloaded from a source. Each
|
|
||||||
download strategy has its own set of parameters.
|
|
||||||
|
|
||||||
.. _url:
|
|
||||||
|
|
||||||
url
|
|
||||||
'''
|
|
||||||
.. autoclass:: ytdl_sub.downloaders.url.url.UrlDownloadOptions()
|
|
||||||
:members: url, playlist_thumbnails, source_thumbnails, download_reverse
|
|
||||||
:member-order: bysource
|
|
||||||
|
|
||||||
multi_url
|
|
||||||
'''''''''
|
|
||||||
.. autoclass:: ytdl_sub.downloaders.url.multi_url.MultiUrlDownloadOptions()
|
|
||||||
:members: urls, variables
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
output_options
|
|
||||||
""""""""""""""
|
|
||||||
|
|
||||||
.. autoclass:: ytdl_sub.config.preset_options.OutputOptions()
|
|
||||||
:members:
|
|
||||||
:member-order: bysource
|
|
||||||
:exclude-members: get_upload_date_range_to_keep, partial_validate
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
.. _ytdl_options:
|
|
||||||
|
|
||||||
ytdl_options
|
|
||||||
""""""""""""
|
|
||||||
.. autoclass:: ytdl_sub.config.preset_options.YTDLOptions()
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
.. _overrides:
|
|
||||||
|
|
||||||
overrides
|
|
||||||
"""""""""
|
|
||||||
.. autoclass:: ytdl_sub.config.overrides.Overrides()
|
|
||||||
|
|
||||||
.. _parent preset:
|
|
||||||
|
|
||||||
preset
|
preset
|
||||||
""""""
|
""""""
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,17 +0,0 @@
|
||||||
==================
|
|
||||||
Config Field Types
|
|
||||||
==================
|
|
||||||
|
|
||||||
The ``config.yaml`` uses various types for its configurable fields. Below is a definition for each type.
|
|
||||||
|
|
||||||
.. autoclass:: ytdl_sub.validators.string_formatter_validators.StringFormatterValidator()
|
|
||||||
|
|
||||||
.. autoclass:: ytdl_sub.validators.string_formatter_validators.OverridesStringFormatterValidator()
|
|
||||||
|
|
||||||
.. autoclass:: ytdl_sub.validators.file_path_validators.StringFormatterFileNameValidator()
|
|
||||||
|
|
||||||
.. autoclass:: ytdl_sub.validators.string_datetime.StringDatetimeValidator()
|
|
||||||
|
|
||||||
.. autoclass:: ytdl_sub.validators.string_formatter_validators.DictFormatterValidator()
|
|
||||||
|
|
||||||
.. autoclass:: ytdl_sub.validators.string_formatter_validators.OverridesDictFormatterValidator()
|
|
||||||
|
|
@ -2,8 +2,11 @@
|
||||||
Scripting
|
Scripting
|
||||||
=========
|
=========
|
||||||
|
|
||||||
.. toctree::
|
Work in progress! Explanation of how to define scripts/variables will be added here.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
entry_variables
|
entry_variables
|
||||||
override_variables
|
override_variables
|
||||||
scripting_functions
|
scripting_functions
|
||||||
config_types
|
|
||||||
|
|
@ -23,9 +23,9 @@ from ytdl_sub.validators.string_formatter_validators import StringFormatterValid
|
||||||
|
|
||||||
class Overrides(DictFormatterValidator, Scriptable):
|
class Overrides(DictFormatterValidator, Scriptable):
|
||||||
"""
|
"""
|
||||||
Optional. This section allows you to define variables that can be used in any string formatter.
|
Allows you to define variables that can be used in any EntryFormatter or OverridesFormatter.
|
||||||
For example, if you want your file and thumbnail files to match without copy-pasting a large
|
|
||||||
format string, you can define something like:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,12 @@ from ytdl_sub.validators.validators import LiteralDictValidator
|
||||||
|
|
||||||
class YTDLOptions(LiteralDictValidator):
|
class YTDLOptions(LiteralDictValidator):
|
||||||
"""
|
"""
|
||||||
Optional. This section allows you to add any ytdl argument to ytdl-sub's downloader.
|
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
|
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>`_
|
`this docstring <https://github.com/yt-dlp/yt-dlp/blob/2022.04.08/yt_dlp/YoutubeDL.py#L197>`_
|
||||||
for more details.
|
for more details.
|
||||||
|
|
||||||
ytdl_options should be formatted like:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
|
@ -58,7 +58,7 @@ class OutputOptions(StrictDictValidator):
|
||||||
"""
|
"""
|
||||||
Defines where to output files and thumbnails after all post-processing has completed.
|
Defines where to output files and thumbnails after all post-processing has completed.
|
||||||
|
|
||||||
Usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
|
@ -156,97 +156,119 @@ class OutputOptions(StrictDictValidator):
|
||||||
@property
|
@property
|
||||||
def output_directory(self) -> OverridesStringFormatterValidator:
|
def output_directory(self) -> OverridesStringFormatterValidator:
|
||||||
"""
|
"""
|
||||||
Required. The output directory to store all media files downloaded.
|
:expected type: OverridesFormatter
|
||||||
|
:description:
|
||||||
|
The output directory to store all media files downloaded.
|
||||||
"""
|
"""
|
||||||
return self._output_directory
|
return self._output_directory
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def file_name(self) -> StringFormatterValidator:
|
def file_name(self) -> StringFormatterValidator:
|
||||||
"""
|
"""
|
||||||
Required. The file name for the media file. This can include directories such as
|
:expected type: EntryFormatter
|
||||||
``"Season {upload_year}/{title}.{ext}"``, and will be placed in the output directory.
|
:description:
|
||||||
|
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
|
return self._file_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def thumbnail_name(self) -> Optional[StringFormatterValidator]:
|
def thumbnail_name(self) -> Optional[StringFormatterValidator]:
|
||||||
"""
|
"""
|
||||||
Optional. The file name for the media's thumbnail image. This can include directories such
|
:expected type: Optional[EntryFormatter]
|
||||||
as ``"Season {upload_year}/{title}.{thumbnail_ext}"``, and will be placed in the output
|
:description:
|
||||||
directory. Can be set to empty string or `null` to disable thumbnail writes.
|
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. Can be set to empty string or `null` to disable thumbnail writes.
|
||||||
"""
|
"""
|
||||||
return self._thumbnail_name
|
return self._thumbnail_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def info_json_name(self) -> Optional[StringFormatterValidator]:
|
def info_json_name(self) -> Optional[StringFormatterValidator]:
|
||||||
"""
|
"""
|
||||||
Optional. The file name for the media's info json file. This can include directories such
|
:expected type: Optional[EntryFormatter]
|
||||||
as ``"Season {upload_year}/{title}.{info_json_ext}"``, and will be placed in the output
|
:description:
|
||||||
directory. Can be set to empty string or `null` to disable info json writes.
|
The file name for the media's info json file. This can include directories such
|
||||||
|
as ``"Season {upload_year}/{title}.{info_json_ext}"``, and will be placed in the output
|
||||||
|
directory. Can be set to empty string or `null` to disable info json writes.
|
||||||
"""
|
"""
|
||||||
return self._info_json_name
|
return self._info_json_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def download_archive_name(self) -> Optional[OverridesStringFormatterValidator]:
|
def download_archive_name(self) -> Optional[OverridesStringFormatterValidator]:
|
||||||
"""
|
"""
|
||||||
Optional. The file name to store a subscriptions download archive placed relative to
|
:expected type: Optional[OverridesFormatter]
|
||||||
the output directory. Defaults to ``.ytdl-sub-{subscription_name}-download-archive.json``
|
:description:
|
||||||
|
The file name to store a subscriptions download archive placed relative to
|
||||||
|
the output directory. Defaults to ``.ytdl-sub-{subscription_name}-download-archive.json``
|
||||||
"""
|
"""
|
||||||
return self._download_archive_name
|
return self._download_archive_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def migrated_download_archive_name(self) -> Optional[OverridesStringFormatterValidator]:
|
def migrated_download_archive_name(self) -> Optional[OverridesStringFormatterValidator]:
|
||||||
"""
|
"""
|
||||||
Optional. Intended to be used if you are migrating a subscription with either a new
|
:expected type: Optional[OverridesFormatter]
|
||||||
subscription name or output directory. It will try to load the archive file using this name
|
:description:
|
||||||
first, and fallback to ``download_archive_name``. It will always save to this file
|
Intended to be used if you are migrating a subscription with either a new
|
||||||
and remove the original ``download_archive_name``.
|
subscription name or output directory. It will try to load the archive file using this
|
||||||
|
name first, and fallback to ``download_archive_name``. It will always save to this file
|
||||||
|
and remove the original ``download_archive_name``.
|
||||||
"""
|
"""
|
||||||
return self._migrated_download_archive_name
|
return self._migrated_download_archive_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def maintain_download_archive(self) -> bool:
|
def maintain_download_archive(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Optional. Maintains a download archive file in the output directory for a subscription.
|
:expected type: Optional[Boolean]
|
||||||
It is named ``.ytdl-sub-{subscription_name}-download-archive.json``, stored in the
|
:description:
|
||||||
output directory.
|
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
|
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
|
create a ytdl download-archive file when invoking a download on a subscription. This will
|
||||||
prevent ytdl from redownloading media already downloaded.
|
prevent ytdl from redownloading media already downloaded.
|
||||||
|
|
||||||
Defaults to False.
|
Defaults to False.
|
||||||
"""
|
"""
|
||||||
return self._maintain_download_archive.value
|
return self._maintain_download_archive.value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def keep_files_before(self) -> Optional[StringDatetimeValidator]:
|
def keep_files_before(self) -> Optional[StringDatetimeValidator]:
|
||||||
"""
|
"""
|
||||||
Optional. Requires ``maintain_download_archive`` set to True.
|
:expected type: Optional[OverridesFormatter]
|
||||||
|
:description:
|
||||||
|
Requires ``maintain_download_archive`` set to True. Uses the same syntax as the
|
||||||
|
``date_range`` plugin.
|
||||||
|
|
||||||
Only keeps files that are uploaded before this datetime. By default, ytdl-sub will keep
|
Only keeps files that are uploaded before this datetime. By default, ytdl-sub will keep
|
||||||
files before ``now``, which implies all files. Can be used in conjunction with
|
files before ``now``, which implies all files. Can be used in conjunction with
|
||||||
``keep_max_files``.
|
``keep_max_files``.
|
||||||
"""
|
"""
|
||||||
return self._keep_files_before
|
return self._keep_files_before
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def keep_files_after(self) -> Optional[StringDatetimeValidator]:
|
def keep_files_after(self) -> Optional[StringDatetimeValidator]:
|
||||||
"""
|
"""
|
||||||
Optional. Requires ``maintain_download_archive`` set to True.
|
:expected type: Optional[OverridesFormatter]
|
||||||
|
:description:
|
||||||
|
Requires ``maintain_download_archive`` set to True. Uses the same syntax as the
|
||||||
|
``date_range`` plugin.
|
||||||
|
|
||||||
Only keeps files that are uploaded after this datetime. By default, ytdl-sub will keep
|
Only keeps files that are uploaded after this datetime. By default, ytdl-sub will keep
|
||||||
files after ``19000101``, which implies all files. Can be used in conjunction with
|
files after ``19000101``, which implies all files. Can be used in conjunction with
|
||||||
``keep_max_files``.
|
``keep_max_files``.
|
||||||
"""
|
"""
|
||||||
return self._keep_files_after
|
return self._keep_files_after
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def keep_max_files(self) -> Optional[OverridesIntegerFormatterValidator]:
|
def keep_max_files(self) -> Optional[OverridesIntegerFormatterValidator]:
|
||||||
"""
|
"""
|
||||||
Optional. Requires ``maintain_download_archive`` set to True.
|
:expected type: Optional[OverridesFormatter]
|
||||||
|
:description:
|
||||||
|
Requires ``maintain_download_archive`` set to True.
|
||||||
|
|
||||||
Only keeps N most recently uploaded videos. If set to <= 0, ``keep_max_files`` will not be
|
Only keeps N most recently uploaded videos. If set to <= 0, ``keep_max_files`` will not be
|
||||||
applied. Can be used in conjunction with ``keep_files_before`` and ``keep_files_after``.
|
applied. Can be used in conjunction with ``keep_files_before`` and ``keep_files_after``.
|
||||||
"""
|
"""
|
||||||
return self._keep_max_files
|
return self._keep_max_files
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Remove later - keep for docstring
|
|
||||||
class MultiUrlDownloadOptions(MultiUrlValidator):
|
|
||||||
"""
|
|
||||||
Downloads from multiple URLs. If an entry is returned from more than one URL, it will
|
|
||||||
resolve to the bottom-most URL settings.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
presets:
|
|
||||||
my_example_preset:
|
|
||||||
download:
|
|
||||||
# required
|
|
||||||
urls:
|
|
||||||
- url: "youtube.com/channel/UCsvn_Po0SmunchJYtttWpOxMg"
|
|
||||||
variables:
|
|
||||||
season_index: "1"
|
|
||||||
season_name: "Uploads"
|
|
||||||
playlist_thumbnails:
|
|
||||||
- name: "poster.jpg"
|
|
||||||
uid: "avatar_uncropped"
|
|
||||||
- name: "fanart.jpg"
|
|
||||||
uid: "banner_uncropped"
|
|
||||||
- name: "season{season_index}-poster.jpg"
|
|
||||||
uid: "latest_entry"
|
|
||||||
- url: "https://www.youtube.com/playlist?list=UCsvn_Po0SmunchJYtttWpOxMg"
|
|
||||||
variables:
|
|
||||||
season_index: "2"
|
|
||||||
season_name: "Playlist as Season"
|
|
||||||
playlist_thumbnails:
|
|
||||||
- name: "season{season_index}-poster.jpg"
|
|
||||||
uid: "latest_entry"
|
|
||||||
"""
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
from ytdl_sub.downloaders.url.validators import UrlValidator
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Remove later - keep for docstring
|
|
||||||
class UrlDownloadOptions(UrlValidator):
|
|
||||||
"""
|
|
||||||
Downloads from a single URL supported by yt-dlp.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
presets:
|
|
||||||
my_example_preset:
|
|
||||||
download:
|
|
||||||
# required
|
|
||||||
url: "youtube.com/channel/UCsvn_Po0SmunchJYtttWpOxMg"
|
|
||||||
# optional
|
|
||||||
playlist_thumbnails:
|
|
||||||
- name: "poster.jpg"
|
|
||||||
uid: "avatar_uncropped"
|
|
||||||
- name: "fanart.jpg"
|
|
||||||
uid: "banner_uncropped"
|
|
||||||
download_reverse: True
|
|
||||||
"""
|
|
||||||
|
|
@ -215,8 +215,53 @@ class UrlListValidator(ListValidator[UrlStringOrDictValidator]):
|
||||||
|
|
||||||
class MultiUrlValidator(OptionsValidator):
|
class MultiUrlValidator(OptionsValidator):
|
||||||
"""
|
"""
|
||||||
Downloads from multiple URLs. If an entry is returned from more than one URL, it will
|
Sets the URL(s) to download from. Can be used in many forms, including
|
||||||
resolve to the bottom-most URL settings.
|
|
||||||
|
:Single URL:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
download: "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
|
||||||
|
|
||||||
|
:Multi URL:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
download:
|
||||||
|
- "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
|
||||||
|
- "https://www.youtube.com/watch?v=3BFTio5296w"
|
||||||
|
|
||||||
|
:Thumbnails + Variables:
|
||||||
|
|
||||||
|
All variables must be defined for the top-most url. All subsequent URL variables can be either
|
||||||
|
overwritten or default to the top-most value.
|
||||||
|
|
||||||
|
If an entry is returned from more than one URL, it will use the variables in the bottom-most
|
||||||
|
URL.
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
download:
|
||||||
|
# required
|
||||||
|
urls:
|
||||||
|
- url: "youtube.com/channel/UCsvn_Po0SmunchJYtttWpOxMg"
|
||||||
|
variables:
|
||||||
|
season_index: "1"
|
||||||
|
season_name: "Uploads"
|
||||||
|
playlist_thumbnails:
|
||||||
|
- name: "poster.jpg"
|
||||||
|
uid: "avatar_uncropped"
|
||||||
|
- name: "fanart.jpg"
|
||||||
|
uid: "banner_uncropped"
|
||||||
|
- name: "season{season_index}-poster.jpg"
|
||||||
|
uid: "latest_entry"
|
||||||
|
- url: "https://www.youtube.com/playlist?list=UCsvn_Po0SmunchJYtttWpOxMg"
|
||||||
|
variables:
|
||||||
|
season_index: "2"
|
||||||
|
season_name: "Playlist as Season"
|
||||||
|
playlist_thumbnails:
|
||||||
|
- name: "season{season_index}-poster.jpg"
|
||||||
|
uid: "latest_entry"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ class ChaptersOptions(OptionsDictValidator):
|
||||||
Embeds chapters to video files if they are present. Additional options to add SponsorBlock
|
Embeds chapters to video files if they are present. Additional options to add SponsorBlock
|
||||||
chapters and remove specific ones. Can also remove chapters using regex.
|
chapters and remove specific ones. Can also remove chapters using regex.
|
||||||
|
|
||||||
:usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class DateRangeOptions(OptionsDictValidator):
|
||||||
Note that yt-dlp will round times to the closest day, meaning that `day` is the lowest
|
Note that yt-dlp will round times to the closest day, meaning that `day` is the lowest
|
||||||
granularity possible.
|
granularity possible.
|
||||||
|
|
||||||
:usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,11 @@ class EmbedThumbnailOptions(BoolValidator, OptionsValidator):
|
||||||
"""
|
"""
|
||||||
Whether to embed thumbnails to the audio/video file or not.
|
Whether to embed thumbnails to the audio/video file or not.
|
||||||
|
|
||||||
Usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
presets:
|
embed_thumbnail: True
|
||||||
my_example_preset:
|
|
||||||
embed_thumbnail: True
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,29 +32,27 @@ class FileConvertOptions(OptionsDictValidator):
|
||||||
"""
|
"""
|
||||||
Converts video files from one extension to another.
|
Converts video files from one extension to another.
|
||||||
|
|
||||||
Usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
presets:
|
file_convert:
|
||||||
my_example_preset:
|
convert_to: "mp4"
|
||||||
file_convert:
|
|
||||||
convert_to: "mp4"
|
|
||||||
|
|
||||||
Supports custom ffmpeg conversions:
|
Also supports custom ffmpeg conversions:
|
||||||
|
|
||||||
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
presets:
|
file_convert:
|
||||||
my_example_preset:
|
convert_to: "mkv"
|
||||||
file_convert:
|
convert_with: "ffmpeg"
|
||||||
convert_to: "mkv"
|
ffmpeg_post_process_args: >
|
||||||
convert_with: "ffmpeg"
|
-bitexact
|
||||||
ffmpeg_post_process_args: >
|
-vcodec copy
|
||||||
-bitexact
|
-acodec copy
|
||||||
-vcodec copy
|
-scodec mov_text
|
||||||
-acodec copy
|
|
||||||
-scodec mov_text
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_required_keys = {"convert_to"}
|
_required_keys = {"convert_to"}
|
||||||
|
|
@ -89,35 +87,38 @@ class FileConvertOptions(OptionsDictValidator):
|
||||||
@property
|
@property
|
||||||
def convert_to(self) -> str:
|
def convert_to(self) -> str:
|
||||||
"""
|
"""
|
||||||
Convert to a desired file type. Supports:
|
:expected type: String
|
||||||
|
:description:
|
||||||
|
Convert to a desired file type. Supports
|
||||||
|
|
||||||
* Video: avi, flv, mkv, mov, mp4, webm
|
- Video: avi, flv, mkv, mov, mp4, webm
|
||||||
* Audio: aac, flac, mp3, m4a, opus, vorbis, wav
|
- Audio: aac, flac, mp3, m4a, opus, vorbis, wav
|
||||||
"""
|
"""
|
||||||
return self._convert_to
|
return self._convert_to
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def convert_with(self) -> Optional[str]:
|
def convert_with(self) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Optional. Supports ``yt-dlp`` and ``ffmpeg``. ``yt-dlp`` will convert files within
|
:expected type: Optional[String]
|
||||||
yt-dlp whereas ``ffmpeg`` specifies it will be converted using a custom command specified
|
:description:
|
||||||
with ``ffmpeg_post_process_args``. Defaults to ``yt-dlp``.
|
Supports ``yt-dlp`` and ``ffmpeg``. ``yt-dlp`` will convert files within
|
||||||
|
yt-dlp whereas ``ffmpeg`` specifies it will be converted using a custom command specified
|
||||||
|
with ``ffmpeg_post_process_args``. Defaults to ``yt-dlp``.
|
||||||
"""
|
"""
|
||||||
return self._convert_with
|
return self._convert_with
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ffmpeg_post_process_args(self) -> Optional[OverridesStringFormatterValidator]:
|
def ffmpeg_post_process_args(self) -> Optional[OverridesStringFormatterValidator]:
|
||||||
"""
|
"""
|
||||||
Optional. ffmpeg args to post-process an entry file with. The args will be inserted in the
|
:expected type: Optional[OverridesFormatter]
|
||||||
form of:
|
:description:
|
||||||
|
ffmpeg args to post-process an entry file with. The args will be inserted in the
|
||||||
|
form of
|
||||||
|
|
||||||
.. code-block:: bash
|
``ffmpeg -i input_file.ext {ffmpeg_post_process_args) output_file.output_ext``.
|
||||||
|
|
||||||
ffmpeg -i input_file.ext {ffmpeg_post_process_args) output_file.output_ext
|
The output file will use the extension specified in ``convert_to``. Post-processing args
|
||||||
|
can still be set with ``convert_with`` set to ``yt-dlp``.
|
||||||
The output file will use the extension specified in ``convert_to``. Post-processing args
|
|
||||||
can still be set with ``convert_with`` set to ``yt-dlp``.
|
|
||||||
"""
|
"""
|
||||||
return self._ffmpeg_post_process_args
|
return self._ffmpeg_post_process_args
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,17 +19,15 @@ class FilterExcludeOptions(ListFormatterValidator, OptionsValidator):
|
||||||
Applies a conditional OR on any number of filters comprised of either variables or scripts.
|
Applies a conditional OR on any number of filters comprised of either variables or scripts.
|
||||||
If any filter evaluates to True, the entry will be excluded.
|
If any filter evaluates to True, the entry will be excluded.
|
||||||
|
|
||||||
Usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
presets:
|
filter_exclude:
|
||||||
my_example_preset:
|
- >-
|
||||||
filter_exclude:
|
{ %contains( %lower(title), '#short' ) }
|
||||||
- >-
|
- >-
|
||||||
{ %contains( %lower(title), '#short' ) }
|
{ %contains( %lower(description), '#short' ) }
|
||||||
- >-
|
|
||||||
{ %contains( %lower(description), '#short' ) }
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,25 +19,23 @@ class FilterIncludeOptions(ListFormatterValidator, OptionsValidator):
|
||||||
Applies a conditional AND on any number of filters comprised of either variables or scripts.
|
Applies a conditional AND on any number of filters comprised of either variables or scripts.
|
||||||
If all filters evaluate to True, the entry will be included.
|
If all filters evaluate to True, the entry will be included.
|
||||||
|
|
||||||
Usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
presets:
|
filter_include:
|
||||||
my_example_preset:
|
- >-
|
||||||
filter_include:
|
{description}
|
||||||
- >-
|
- >-
|
||||||
{description}
|
{
|
||||||
- >-
|
%regex_search_any(
|
||||||
{
|
title,
|
||||||
%regex_search_any(
|
[
|
||||||
title,
|
"Full Episode",
|
||||||
[
|
"FULL",
|
||||||
"Full Episode",
|
]
|
||||||
"FULL",
|
)
|
||||||
]
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,7 @@ class FormatOptions(OptionsValidator):
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
presets:
|
format: "(bv*[height<=1080]+bestaudio/best[height<=1080])"
|
||||||
my_example_preset:
|
|
||||||
format: "(bv*[height<=1080]+bestaudio/best[height<=1080])"
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
|
|
|
||||||
|
|
@ -58,32 +58,20 @@ def combine_filters(filters: List[str], to_combine: List[str]) -> List[str]:
|
||||||
|
|
||||||
class MatchFiltersOptions(OptionsDictValidator):
|
class MatchFiltersOptions(OptionsDictValidator):
|
||||||
"""
|
"""
|
||||||
Set ``--match-filters``` to pass into yt-dlp to filter entries from being downloaded.
|
Set ``--match-filters`` to pass into yt-dlp to filter entries from being downloaded.
|
||||||
Uses the same syntax as yt-dlp.
|
Uses the same syntax as yt-dlp. An entry will be downloaded if any one of the filters are met.
|
||||||
|
For logical AND's between match filters, use the ``&`` operator in a single match filter.
|
||||||
|
|
||||||
Usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
presets:
|
match_filters:
|
||||||
my_example_preset:
|
filters:
|
||||||
match_filters:
|
- "age_limit<?18 & like_count>?100"
|
||||||
filters: "original_url!*=/shorts/"
|
# Other common match-filters
|
||||||
|
# - "original_url!*=/shorts/ & !is_live"
|
||||||
Supports one or multiple filters:
|
# - "availability=?public"
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
presets:
|
|
||||||
my_example_preset:
|
|
||||||
match_filters:
|
|
||||||
filters:
|
|
||||||
- "age_limit<?18"
|
|
||||||
- "like_count>?100"
|
|
||||||
# Other common match-filters
|
|
||||||
# - "original_url!*=/shorts/ & !is_live"
|
|
||||||
# - "age_limit<?18"
|
|
||||||
# - "availability=?public"
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_optional_keys = {"filters"}
|
_optional_keys = {"filters"}
|
||||||
|
|
|
||||||
|
|
@ -75,23 +75,22 @@ class MusicTagsOptions(OptionsDictValidator):
|
||||||
a full list of tags for various file types in MediaFile's
|
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>`_.
|
`source code <https://github.com/beetbox/mediafile/blob/v0.9.0/mediafile.py#L1770>`_.
|
||||||
|
|
||||||
Usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
presets:
|
presets:
|
||||||
my_example_preset:
|
my_example_preset:
|
||||||
music_tags:
|
music_tags:
|
||||||
tags:
|
artist: "{artist}"
|
||||||
artist: "{artist}"
|
album: "{album}"
|
||||||
album: "{album}"
|
# Supports id3v2.4 multi-tags
|
||||||
# Supports id3v2.4 multi-tags
|
genres:
|
||||||
genres:
|
- "{genre}"
|
||||||
- "{genre}"
|
- "ytdl-sub"
|
||||||
- "ytdl-sub"
|
albumartists:
|
||||||
albumartists:
|
- "{artist}"
|
||||||
- "{artist}"
|
- "ytdl-sub"
|
||||||
- "ytdl-sub"
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_optional_keys = {"tags", "embed_thumbnail"}
|
_optional_keys = {"tags", "embed_thumbnail"}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,9 @@ class SharedNfoTagsOptions(OptionsDictValidator):
|
||||||
@property
|
@property
|
||||||
def nfo_name(self) -> StringFormatterFileNameValidator:
|
def nfo_name(self) -> StringFormatterFileNameValidator:
|
||||||
"""
|
"""
|
||||||
The NFO file name.
|
:expected type: EntryFormatter
|
||||||
|
:description:
|
||||||
|
The NFO file name.
|
||||||
"""
|
"""
|
||||||
return self._nfo_name
|
return self._nfo_name
|
||||||
|
|
||||||
|
|
@ -81,9 +83,11 @@ class SharedNfoTagsOptions(OptionsDictValidator):
|
||||||
@property
|
@property
|
||||||
def kodi_safe(self) -> Optional[bool]:
|
def kodi_safe(self) -> Optional[bool]:
|
||||||
"""
|
"""
|
||||||
Optional. Kodi does not support > 3-byte unicode characters, which include emojis and some
|
:expected type: Optional[Boolean]
|
||||||
foreign language characters. Setting this to True will replace those characters with '□'.
|
:description:
|
||||||
Defaults to False.
|
Defaults to False. Kodi does not support > 3-byte unicode characters, which include
|
||||||
|
emojis and some foreign language characters. Setting this to True will replace those
|
||||||
|
characters with '□'.
|
||||||
"""
|
"""
|
||||||
return self._kodi_safe
|
return self._kodi_safe
|
||||||
|
|
||||||
|
|
@ -190,22 +194,18 @@ class NfoTagsOptions(SharedNfoTagsOptions):
|
||||||
Adds an NFO file for every download file. An NFO file is simply an XML file
|
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.
|
with a ``.nfo`` extension. You can add any values into the NFO.
|
||||||
|
|
||||||
Usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
presets:
|
nfo_tags:
|
||||||
my_example_preset:
|
nfo_name: "{title_sanitized}.nfo"
|
||||||
nfo_tags:
|
nfo_root: "episodedetails"
|
||||||
# required
|
tags:
|
||||||
nfo_name: "{title_sanitized}.nfo"
|
title: "{title}"
|
||||||
nfo_root: "episodedetails"
|
season: "{upload_year}"
|
||||||
tags:
|
episode: "{upload_month}{upload_day_padded}"
|
||||||
title: "{title}"
|
kodi_safe: False
|
||||||
season: "{upload_year}"
|
|
||||||
episode: "{upload_month}{upload_day_padded}"
|
|
||||||
# optional
|
|
||||||
kodi_safe: False
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_formatter_validator = StringFormatterValidator
|
_formatter_validator = StringFormatterValidator
|
||||||
|
|
@ -215,50 +215,54 @@ class NfoTagsOptions(SharedNfoTagsOptions):
|
||||||
@property
|
@property
|
||||||
def nfo_root(self) -> StringFormatterValidator:
|
def nfo_root(self) -> StringFormatterValidator:
|
||||||
"""
|
"""
|
||||||
The root tag of the NFO's XML. In the usage above, it would look like
|
:expected type: EntryFormatter
|
||||||
|
:description:
|
||||||
|
The root tag of the NFO's XML. In the usage above, it would look like
|
||||||
|
|
||||||
.. code-block:: xml
|
.. code-block:: xml
|
||||||
|
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<episodedetails>
|
<episodedetails>
|
||||||
</episodedetails>
|
</episodedetails>
|
||||||
"""
|
"""
|
||||||
return self._nfo_root
|
return self._nfo_root
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tags(self) -> NfoTagsValidator:
|
def tags(self) -> NfoTagsValidator:
|
||||||
"""
|
"""
|
||||||
Tags within the nfo_root tag. In the usage above, it would look like
|
:expected type: NfoTags
|
||||||
|
:description:
|
||||||
|
Tags within the nfo_root tag. In the usage above, it would look like
|
||||||
|
|
||||||
.. code-block:: xml
|
.. code-block:: xml
|
||||||
|
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<episodedetails>
|
<episodedetails>
|
||||||
<title>Awesome Youtube Video</title>
|
<title>Awesome Youtube Video</title>
|
||||||
<season>2022</season>
|
<season>2022</season>
|
||||||
<episode>502</episode>
|
<episode>502</episode>
|
||||||
</episodedetails>
|
</episodedetails>
|
||||||
|
|
||||||
Also supports xml attributes and duplicate keys:
|
Also supports xml attributes and duplicate keys:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
season:
|
season:
|
||||||
attributes:
|
attributes:
|
||||||
name: "Best Year"
|
name: "Best Year"
|
||||||
tag: "{upload_year}"
|
tag: "{upload_year}"
|
||||||
genre:
|
genre:
|
||||||
- "Comedy"
|
- "Comedy"
|
||||||
- "Drama"
|
- "Drama"
|
||||||
|
|
||||||
Which translates to
|
Which translates to
|
||||||
|
|
||||||
.. code-block:: xml
|
.. code-block:: xml
|
||||||
|
|
||||||
<season name="Best Year">2022</season>
|
<season name="Best Year">2022</season>
|
||||||
<genre>Comedy</genre>
|
<genre>Comedy</genre>
|
||||||
<genre>Drama</genre>
|
<genre>Drama</genre>
|
||||||
"""
|
"""
|
||||||
return self._tags
|
return self._tags
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,48 +37,52 @@ class OutputDirectoryNfoTagsOptions(SharedNfoTagsOptions):
|
||||||
@property
|
@property
|
||||||
def nfo_root(self) -> StringFormatterValidator:
|
def nfo_root(self) -> StringFormatterValidator:
|
||||||
"""
|
"""
|
||||||
The root tag of the NFO's XML. In the usage above, it would look like
|
:expected type: EntryFormatter
|
||||||
|
:description:
|
||||||
|
The root tag of the NFO's XML. In the usage above, it would look like
|
||||||
|
|
||||||
.. code-block:: xml
|
.. code-block:: xml
|
||||||
|
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<tvshow>
|
<tvshow>
|
||||||
</tvshow>
|
</tvshow>
|
||||||
"""
|
"""
|
||||||
return self._nfo_root
|
return self._nfo_root
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tags(self) -> NfoTagsValidator:
|
def tags(self) -> NfoTagsValidator:
|
||||||
"""
|
"""
|
||||||
Tags within the nfo_root tag. In the usage above, it would look like
|
:expected type: NfoTags
|
||||||
|
:description:
|
||||||
|
Tags within the nfo_root tag. In the usage above, it would look like
|
||||||
|
|
||||||
.. code-block:: xml
|
.. code-block:: xml
|
||||||
|
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<tvshow>
|
<tvshow>
|
||||||
<title>Sweet youtube TV show</title>
|
<title>Sweet youtube TV show</title>
|
||||||
</tvshow>
|
</tvshow>
|
||||||
|
|
||||||
Also supports xml attributes and duplicate keys:
|
Also supports xml attributes and duplicate keys:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
named_season:
|
named_season:
|
||||||
- tag: "{source_title}"
|
- tag: "{source_title}"
|
||||||
attributes:
|
attributes:
|
||||||
number: "{collection_index}"
|
number: "{collection_index}"
|
||||||
genre:
|
genre:
|
||||||
- "Comedy"
|
- "Comedy"
|
||||||
- "Drama"
|
- "Drama"
|
||||||
|
|
||||||
Which translates to
|
Which translates to
|
||||||
|
|
||||||
.. code-block:: xml
|
.. code-block:: xml
|
||||||
|
|
||||||
<title year="2022">Sweet youtube TV show</season>
|
<title year="2022">Sweet youtube TV show</season>
|
||||||
<genre>Comedy</genre>
|
<genre>Comedy</genre>
|
||||||
<genre>Drama</genre>
|
<genre>Drama</genre>
|
||||||
"""
|
"""
|
||||||
return self._tags
|
return self._tags
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,41 @@ class FromSourceVariablesRegex(DictValidator):
|
||||||
|
|
||||||
class RegexOptions(OptionsDictValidator):
|
class RegexOptions(OptionsDictValidator):
|
||||||
r"""
|
r"""
|
||||||
|
.. attention::
|
||||||
|
|
||||||
|
This plugin will eventually be deprecated and replaced by scripting functions.
|
||||||
|
You can replicate the example below using the following.
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
# Only includes videos with 'Official Video'
|
||||||
|
filter_include:
|
||||||
|
- >-
|
||||||
|
{ %contains( %lower(title), "official video" ) }
|
||||||
|
|
||||||
|
# Excludes videos with '#short' in its description
|
||||||
|
filter_exclude:
|
||||||
|
- >-
|
||||||
|
{ %contains( %lower(description), '#short' ) }
|
||||||
|
|
||||||
|
# Creates a capture array with defaults, and assigns
|
||||||
|
# each capture group to its own variable
|
||||||
|
overrides:
|
||||||
|
description_date_capture: >-
|
||||||
|
{
|
||||||
|
%regex_capture_many_with_defaults(
|
||||||
|
description,
|
||||||
|
[ "([0-9]{4})-([0-9]{2})-([0-9]{2})" ],
|
||||||
|
[ upload_year, upload_month, upload_day ]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
captured_upload_year: >-
|
||||||
|
{ %array_at(description_date_capture, 1) }
|
||||||
|
captured_upload_month: >-
|
||||||
|
{ %array_at(description_date_capture, 2) }
|
||||||
|
captured_upload_day: >-
|
||||||
|
{ %array_at(description_date_capture, 3) }
|
||||||
|
|
||||||
Performs regex matching on an entry's source or override variables. Regex can be used to filter
|
Performs regex matching on an entry's source or override variables. Regex can be used to filter
|
||||||
entries from proceeding with download or capture groups to create new source variables.
|
entries from proceeding with download or capture groups to create new source variables.
|
||||||
|
|
||||||
|
|
@ -137,51 +172,49 @@ class RegexOptions(OptionsDictValidator):
|
||||||
and using ``title_and_description`` can regex match/exclude from either ``title`` or
|
and using ``title_and_description`` can regex match/exclude from either ``title`` or
|
||||||
``description``.
|
``description``.
|
||||||
|
|
||||||
Usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
presets:
|
regex:
|
||||||
my_example_preset:
|
# By default, if any match fails and has no defaults, the entry will
|
||||||
regex:
|
# be skipped. If False, ytdl-sub will error and stop all downloads
|
||||||
# By default, if any match fails and has no defaults, the entry will
|
# from proceeding.
|
||||||
# be skipped. If False, ytdl-sub will error and stop all downloads
|
skip_if_match_fails: True
|
||||||
# from proceeding.
|
|
||||||
skip_if_match_fails: True
|
|
||||||
|
|
||||||
from:
|
from:
|
||||||
# For each entry's `title` value...
|
# For each entry's `title` value...
|
||||||
title:
|
title:
|
||||||
# Perform this regex match on it to act as a filter.
|
# Perform this regex match on it to act as a filter.
|
||||||
# This will only download videos with "[Official Video]" in it. Note that we
|
# This will only download videos with "[Official Video]" in it. Note that we
|
||||||
# double backslash to make YAML happy
|
# double backslash to make YAML happy
|
||||||
match:
|
match:
|
||||||
- '\\[Official Video\\]'
|
- '\\[Official Video\\]'
|
||||||
|
|
||||||
# For each entry's `description` value...
|
# For each entry's `description` value...
|
||||||
description:
|
description:
|
||||||
# Match with capture groups and defaults.
|
# Match with capture groups and defaults.
|
||||||
# This tries to scrape a date from the description and produce new
|
# This tries to scrape a date from the description and produce new
|
||||||
# source variables
|
# source variables
|
||||||
match:
|
match:
|
||||||
- '([0-9]{4})-([0-9]{2})-([0-9]{2})'
|
- '([0-9]{4})-([0-9]{2})-([0-9]{2})'
|
||||||
# Exclude any entry where the description contains #short
|
# Exclude any entry where the description contains #short
|
||||||
exclude:
|
exclude:
|
||||||
- '#short'
|
- '#short'
|
||||||
|
|
||||||
# Each capture group creates these new source variables, respectively,
|
# Each capture group creates these new source variables, respectively,
|
||||||
# as well a sanitized version, i.e. `captured_upload_year_sanitized`
|
# as well a sanitized version, i.e. `captured_upload_year_sanitized`
|
||||||
capture_group_names:
|
capture_group_names:
|
||||||
- "captured_upload_year"
|
- "captured_upload_year"
|
||||||
- "captured_upload_month"
|
- "captured_upload_month"
|
||||||
- "captured_upload_day"
|
- "captured_upload_day"
|
||||||
|
|
||||||
# And if the string does not match, use these as respective default
|
# And if the string does not match, use these as respective default
|
||||||
# values for the new source variables.
|
# values for the new source variables.
|
||||||
capture_group_defaults:
|
capture_group_defaults:
|
||||||
- "{upload_year}"
|
- "{upload_year}"
|
||||||
- "{upload_month}"
|
- "{upload_month}"
|
||||||
- "{upload_day}"
|
- "{upload_day}"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_required_keys = {"from"}
|
_required_keys = {"from"}
|
||||||
|
|
@ -209,8 +242,10 @@ class RegexOptions(OptionsDictValidator):
|
||||||
@property
|
@property
|
||||||
def skip_if_match_fails(self) -> Optional[bool]:
|
def skip_if_match_fails(self) -> Optional[bool]:
|
||||||
"""
|
"""
|
||||||
Defaults to True. If True, when any match fails and has no defaults, the entry will be
|
:expected type: Optional[Boolean]
|
||||||
skipped. If False, ytdl-sub will error and all downloads will not proceed.
|
:description:
|
||||||
|
Defaults to True. If True, when any match fails and has no defaults, the entry will be
|
||||||
|
skipped. If False, ytdl-sub will error and all downloads will not proceed.
|
||||||
"""
|
"""
|
||||||
return self._skip_if_match_fails
|
return self._skip_if_match_fails
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,23 +48,22 @@ class WhenNoChaptersValidator(StringSelectValidator):
|
||||||
class SplitByChaptersOptions(OptionsDictValidator):
|
class SplitByChaptersOptions(OptionsDictValidator):
|
||||||
"""
|
"""
|
||||||
Splits a file by chapters into multiple files. Each file becomes its own entry with the
|
Splits a file by chapters into multiple files. Each file becomes its own entry with the
|
||||||
new source variables ``chapter_title``, ``chapter_title_sanitized``, ``chapter_index``,
|
new variables
|
||||||
``chapter_index_padded``, ``chapter_count``.
|
|
||||||
|
|
||||||
If a file has no chapters, and ``when_no_chapters`` is set to "pass", then ``chapter_title`` is
|
- ``chapter_title``
|
||||||
set to the entry's title and ``chapter_index``, ``chapter_count`` are both set to 1.
|
- ``chapter_index``
|
||||||
|
- ``chapter_index_padded``
|
||||||
|
- ``chapter_count``
|
||||||
|
|
||||||
Note that when using this plugin and performing dry-run, it assumes embedded chapters are being
|
Note that when using this plugin and performing dry-run, it assumes embedded chapters are being
|
||||||
used with no modifications.
|
used with no modifications.
|
||||||
|
|
||||||
Usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
presets:
|
split_by_chapters:
|
||||||
my_example_preset:
|
when_no_chapters: "pass"
|
||||||
split_by_chapters:
|
|
||||||
when_no_chapters: "pass"
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_required_keys = {"when_no_chapters"}
|
_required_keys = {"when_no_chapters"}
|
||||||
|
|
@ -100,8 +99,16 @@ class SplitByChaptersOptions(OptionsDictValidator):
|
||||||
@property
|
@property
|
||||||
def when_no_chapters(self) -> str:
|
def when_no_chapters(self) -> str:
|
||||||
"""
|
"""
|
||||||
Behavior to perform when no chapters are present. Supports "pass" (continue processing),
|
:expected type: String
|
||||||
"drop" (exclude it from output), and "error" (stop processing for everything).
|
:description:
|
||||||
|
Behavior to perform when no chapters are present. Supports
|
||||||
|
|
||||||
|
- "pass" (continue processing),
|
||||||
|
- "drop" (exclude it from output)
|
||||||
|
- "error" (stop processing for everything).
|
||||||
|
|
||||||
|
If a file has no chapters and is set to "pass", then ``chapter_title`` is
|
||||||
|
set to the entry's title and ``chapter_index``, ``chapter_count`` are both set to 1.
|
||||||
"""
|
"""
|
||||||
return self._when_no_chapters
|
return self._when_no_chapters
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,18 +37,18 @@ class SubtitleOptions(OptionsDictValidator):
|
||||||
``lang`` and ``subtitles_ext``. ``lang`` is dynamic since you can download multiple subtitles.
|
``lang`` and ``subtitles_ext``. ``lang`` is dynamic since you can download multiple subtitles.
|
||||||
It will set the respective language to the correct subtitle file.
|
It will set the respective language to the correct subtitle file.
|
||||||
|
|
||||||
Usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
presets:
|
subtitles:
|
||||||
my_example_preset:
|
subtitles_name: "{title_sanitized}.{lang}.{subtitles_ext}"
|
||||||
subtitles:
|
subtitles_type: "srt"
|
||||||
subtitles_name: "{title_sanitized}.{lang}.{subtitles_ext}"
|
embed_subtitles: False
|
||||||
subtitles_type: "srt"
|
languages:
|
||||||
embed_subtitles: False
|
- "en" # supports multiple languages
|
||||||
languages: "en" # supports list of multiple languages
|
- "de"
|
||||||
allow_auto_generated_subtitles: False
|
allow_auto_generated_subtitles: False
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_optional_keys = {
|
_optional_keys = {
|
||||||
|
|
@ -82,40 +82,50 @@ class SubtitleOptions(OptionsDictValidator):
|
||||||
@property
|
@property
|
||||||
def subtitles_name(self) -> Optional[StringFormatterValidator]:
|
def subtitles_name(self) -> Optional[StringFormatterValidator]:
|
||||||
"""
|
"""
|
||||||
Optional. The file name for the media's subtitles if they are present. This can include
|
:expected type: Optional[EntryFormatter]
|
||||||
directories such as ``"Season {upload_year}/{title_sanitized}.{lang}.{subtitles_ext}"``, and
|
:description:
|
||||||
will be placed in the output directory. ``lang`` is dynamic since you can download multiple
|
The file name for the media's subtitles if they are present. This can include
|
||||||
subtitles. It will set the respective language to the correct subtitle file.
|
directories such as ``"Season {upload_year}/{title_sanitized}.{lang}.{subtitles_ext}"``,
|
||||||
|
and will be placed in the output directory. ``lang`` is dynamic since you can download
|
||||||
|
multiple subtitles. It will set the respective language to the correct subtitle file.
|
||||||
"""
|
"""
|
||||||
return self._subtitles_name
|
return self._subtitles_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def subtitles_type(self) -> Optional[str]:
|
def subtitles_type(self) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Optional. One of the subtitle file types "srt", "vtt", "ass", "lrc". Defaults to "srt"
|
:expected type: Optional[String]
|
||||||
|
:description:
|
||||||
|
Defaults to "srt". One of the subtitle file types "srt", "vtt", "ass", "lrc".
|
||||||
"""
|
"""
|
||||||
return self._subtitles_type
|
return self._subtitles_type
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def embed_subtitles(self) -> Optional[bool]:
|
def embed_subtitles(self) -> Optional[bool]:
|
||||||
"""
|
"""
|
||||||
Optional. Whether to embed the subtitles into the video file. Defaults to False.
|
:expected type: Optional[Boolean]
|
||||||
NOTE: webm files can only embed "vtt" subtitle types.
|
:description:
|
||||||
|
Defaults to False. Whether to embed the subtitles into the video file. Note that
|
||||||
|
webm files can only embed "vtt" subtitle types.
|
||||||
"""
|
"""
|
||||||
return self._embed_subtitles
|
return self._embed_subtitles
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def languages(self) -> Optional[List[str]]:
|
def languages(self) -> Optional[List[str]]:
|
||||||
"""
|
"""
|
||||||
Optional. Language code(s) to download for subtitles. Supports a single or list of multiple
|
:expected type: Optional[List[String]]
|
||||||
language codes. Defaults to "en".
|
:description:
|
||||||
|
Language code(s) to download for subtitles. Supports a single or list of multiple
|
||||||
|
language codes. Defaults to only "en".
|
||||||
"""
|
"""
|
||||||
return [lang.value for lang in self._languages]
|
return [lang.value for lang in self._languages]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def allow_auto_generated_subtitles(self) -> Optional[bool]:
|
def allow_auto_generated_subtitles(self) -> Optional[bool]:
|
||||||
"""
|
"""
|
||||||
Optional. Whether to allow auto generated subtitles. Defaults to False.
|
:expected type: Optional[Boolean]
|
||||||
|
:description:
|
||||||
|
Defaults to False. Whether to allow auto generated subtitles.
|
||||||
"""
|
"""
|
||||||
return self._allow_auto_generated_subtitles
|
return self._allow_auto_generated_subtitles
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ class ThrottleProtectionOptions(OptionsDictValidator):
|
||||||
range-based values, a random number will be chosen within the range to avoid sleeps looking
|
range-based values, a random number will be chosen within the range to avoid sleeps looking
|
||||||
scripted.
|
scripted.
|
||||||
|
|
||||||
Usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
|
@ -110,31 +110,39 @@ class ThrottleProtectionOptions(OptionsDictValidator):
|
||||||
@property
|
@property
|
||||||
def sleep_per_download_s(self) -> Optional[RandomizedRangeValidator]:
|
def sleep_per_download_s(self) -> Optional[RandomizedRangeValidator]:
|
||||||
"""
|
"""
|
||||||
Number in seconds to sleep between each download. Does not include time it takes for
|
:expected type: Optional[Range]
|
||||||
ytdl-sub to perform post-processing.
|
:description:
|
||||||
|
Number in seconds to sleep between each download. Does not include time it takes for
|
||||||
|
ytdl-sub to perform post-processing.
|
||||||
"""
|
"""
|
||||||
return self._sleep_per_download_s
|
return self._sleep_per_download_s
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sleep_per_subscription_s(self) -> Optional[RandomizedRangeValidator]:
|
def sleep_per_subscription_s(self) -> Optional[RandomizedRangeValidator]:
|
||||||
"""
|
"""
|
||||||
Number in seconds to sleep between each subscription.
|
:expected type: Optional[Range]
|
||||||
|
:description:
|
||||||
|
Number in seconds to sleep between each subscription.
|
||||||
"""
|
"""
|
||||||
return self._sleep_per_subscription_s
|
return self._sleep_per_subscription_s
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def max_downloads_per_subscription(self) -> Optional[RandomizedRangeValidator]:
|
def max_downloads_per_subscription(self) -> Optional[RandomizedRangeValidator]:
|
||||||
"""
|
"""
|
||||||
Number of downloads to perform per subscription.
|
:expected type: Optional[Range]
|
||||||
|
:description:
|
||||||
|
Number of downloads to perform per subscription.
|
||||||
"""
|
"""
|
||||||
return self._max_downloads_per_subscription
|
return self._max_downloads_per_subscription
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def subscription_download_probability(self) -> Optional[ProbabilityValidator]:
|
def subscription_download_probability(self) -> Optional[ProbabilityValidator]:
|
||||||
"""
|
"""
|
||||||
Probability to perform any downloads, recomputed for each subscription. This is only
|
:expected type: Optional[Float]
|
||||||
recommended to set if you run ytdl-sub in a cron-job, that way you are statistically
|
:description:
|
||||||
guaranteed over time to eventually download the subscription.
|
Probability to perform any downloads, recomputed for each subscription. This is only
|
||||||
|
recommended to set if you run ytdl-sub in a cron-job, that way you are statistically
|
||||||
|
guaranteed over time to eventually download the subscription.
|
||||||
"""
|
"""
|
||||||
return self._subscription_download_probability
|
return self._subscription_download_probability
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,14 @@ class VideoTagsOptions(OptionsDictValidator):
|
||||||
"""
|
"""
|
||||||
Adds tags to every downloaded video file using ffmpeg ``-metadata key=value`` args.
|
Adds tags to every downloaded video file using ffmpeg ``-metadata key=value`` args.
|
||||||
|
|
||||||
Usage:
|
:Usage:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
presets:
|
video_tags:
|
||||||
my_example_preset:
|
title: "{title}"
|
||||||
video_tags:
|
date: "{upload_date}"
|
||||||
title: "{title}"
|
description: "{description}"
|
||||||
date: "{upload_date}"
|
|
||||||
description: "{description}"
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_optional_keys = {"tags"}
|
_optional_keys = {"tags"}
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,4 @@ from tools.docgen.docgen import REGENERATE_DOCS
|
||||||
|
|
||||||
|
|
||||||
def test_docgen_regenerate_disabled():
|
def test_docgen_regenerate_disabled():
|
||||||
assert REGENERATE_DOCS is False
|
assert REGENERATE_DOCS is False
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
REGENERATE_DOCS: bool = True
|
REGENERATE_DOCS: bool = False
|
||||||
|
|
||||||
|
|
||||||
class DocGen:
|
class DocGen:
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@ from typing import Dict
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
from tools.docgen.docgen import DocGen
|
from tools.docgen.docgen import DocGen
|
||||||
from tools.docgen.utils import camel_case_to_human, line_section
|
from tools.docgen.utils import camel_case_to_human
|
||||||
from tools.docgen.utils import get_function_docs
|
from tools.docgen.utils import get_function_docs
|
||||||
|
from tools.docgen.utils import line_section
|
||||||
from tools.docgen.utils import properties
|
from tools.docgen.utils import properties
|
||||||
from tools.docgen.utils import section
|
from tools.docgen.utils import section
|
||||||
from ytdl_sub.entries.script.variable_definitions import VariableDefinitions
|
from ytdl_sub.entries.script.variable_definitions import VariableDefinitions
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import inspect
|
import inspect
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, Optional
|
from typing import Any
|
||||||
|
from typing import Dict
|
||||||
|
from typing import Optional
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
from tools.docgen.docgen import DocGen
|
from tools.docgen.docgen import DocGen
|
||||||
|
|
@ -12,6 +14,20 @@ from ytdl_sub.config.plugin.plugin_mapping import PluginMapping
|
||||||
from ytdl_sub.config.preset_options import OutputOptions
|
from ytdl_sub.config.preset_options import OutputOptions
|
||||||
from ytdl_sub.config.preset_options import YTDLOptions
|
from ytdl_sub.config.preset_options import YTDLOptions
|
||||||
from ytdl_sub.config.validators.options import OptionsValidator
|
from ytdl_sub.config.validators.options import OptionsValidator
|
||||||
|
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
|
||||||
|
|
||||||
|
|
||||||
|
def should_filter_all_properties(plugin_name: str) -> bool:
|
||||||
|
return plugin_name in (
|
||||||
|
"format",
|
||||||
|
"match_filters",
|
||||||
|
"music_tags",
|
||||||
|
"filter_include",
|
||||||
|
"filter_exclude",
|
||||||
|
"embed_thumbnail",
|
||||||
|
"video_tags",
|
||||||
|
"download",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def should_filter_property(property_name: str) -> bool:
|
def should_filter_property(property_name: str) -> bool:
|
||||||
|
|
@ -26,9 +42,7 @@ def should_filter_property(property_name: str) -> bool:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_function_docs(
|
def get_function_docs(function_name: str, obj: Any, level: int) -> str:
|
||||||
function_name: str, obj: Any, level: int
|
|
||||||
) -> str:
|
|
||||||
docs = f"\n``{function_name}``\n\n"
|
docs = f"\n``{function_name}``\n\n"
|
||||||
docs += inspect.cleandoc(getattr(obj, function_name).__doc__)
|
docs += inspect.cleandoc(getattr(obj, function_name).__doc__)
|
||||||
docs += "\n\n"
|
docs += "\n\n"
|
||||||
|
|
@ -42,6 +56,9 @@ def generate_plugin_docs(name: str, options: Type[OptionsValidator], offset: int
|
||||||
docs += inspect.cleandoc(options.__doc__)
|
docs += inspect.cleandoc(options.__doc__)
|
||||||
docs += "\n"
|
docs += "\n"
|
||||||
|
|
||||||
|
if should_filter_all_properties(name):
|
||||||
|
return docs
|
||||||
|
|
||||||
property_names = [prop for prop in properties(options) if not should_filter_property(prop)]
|
property_names = [prop for prop in properties(options) if not should_filter_property(prop)]
|
||||||
for property_name in sorted(property_names):
|
for property_name in sorted(property_names):
|
||||||
docs += get_function_docs(function_name=property_name, obj=options, level=offset + 1)
|
docs += get_function_docs(function_name=property_name, obj=options, level=offset + 1)
|
||||||
|
|
@ -59,6 +76,7 @@ class PluginsDocGen(DocGen):
|
||||||
"output_options": OutputOptions,
|
"output_options": OutputOptions,
|
||||||
"ytdl_options": YTDLOptions,
|
"ytdl_options": YTDLOptions,
|
||||||
"overrides": Overrides,
|
"overrides": Overrides,
|
||||||
|
"download": MultiUrlValidator,
|
||||||
}
|
}
|
||||||
for plugin_name, plugin_type in PluginMapping._MAPPING.items():
|
for plugin_name, plugin_type in PluginMapping._MAPPING.items():
|
||||||
if plugin_name.startswith("_"):
|
if plugin_name.startswith("_"):
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ from typing import Optional
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
from tools.docgen.docgen import DocGen
|
from tools.docgen.docgen import DocGen
|
||||||
from tools.docgen.utils import camel_case_to_human, line_section
|
from tools.docgen.utils import camel_case_to_human
|
||||||
|
from tools.docgen.utils import line_section
|
||||||
from tools.docgen.utils import section
|
from tools.docgen.utils import section
|
||||||
from tools.docgen.utils import static_methods
|
from tools.docgen.utils import static_methods
|
||||||
from ytdl_sub.entries.script.custom_functions import CustomFunctions
|
from ytdl_sub.entries.script.custom_functions import CustomFunctions
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue