[FEATURE] Override variable support in ytdl_options (#1087)
Adds the ability to use override variables in the [ytdl_options](https://ytdl-sub.readthedocs.io/en/latest/config_reference/plugins.html#ytdl-options) section of the config.
This commit is contained in:
parent
28c29683cb
commit
1499d7486c
6 changed files with 93 additions and 36 deletions
|
|
@ -69,19 +69,34 @@ Supports the following override variables:
|
|||
- "To Catch a Smuggler"
|
||||
|
||||
|
||||
Chunk Initial Download
|
||||
----------------------
|
||||
Chunk Downloads
|
||||
---------------
|
||||
|
||||
If you are archiving a large channel, ``ytdl-sub`` will try pulling each video's metadata from newest to oldest before starting any downloads. It is a long process and not ideal. A better method is to chunk the process by using the following preset:
|
||||
If you are archiving a large channel, ``ytdl-sub`` will try pulling each video's metadata from newest to oldest before
|
||||
starting any downloads. It is a long process and not ideal. A better method is to chunk the process by using the
|
||||
following preset:
|
||||
|
||||
``chunk_initial_download``
|
||||
``Chunk Downloads``
|
||||
|
||||
It will download videos starting from the oldest one, and only download 20 at a time. You can
|
||||
change this number by setting:
|
||||
It will download videos starting from the oldest one, and only download 20 at a time by default. You can
|
||||
change this number by setting the override variable ``chunk_max_downloads``.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
ytdl_options:
|
||||
max_downloads: 30 # Desired number to download per invocation
|
||||
__preset__:
|
||||
overrides:
|
||||
chunk_max_downloads: 20
|
||||
|
||||
Once the entire channel is downloaded, remove this preset. Then it will pull metadata from newest to oldest again, and stop pulling additional metadata once it reaches a video that has already been downloaded.
|
||||
Plex TV Show by Date:
|
||||
|
||||
# Chunk these ones
|
||||
= Documentaries | Chunk Downloads:
|
||||
"NOVA PBS": "https://www.youtube.com/@novapbs"
|
||||
"National Geographic": "https://www.youtube.com/@NatGeo"
|
||||
|
||||
# But not these ones
|
||||
= Documentaries:
|
||||
"Cosmos - What If": "https://www.youtube.com/playlist?list=PLZdXRHYAVxTJno6oFF9nLGuwXNGYHmE8U"
|
||||
|
||||
Once the entire channel is downloaded, remove the usage of this preset. It will then pull metadata from newest to
|
||||
oldest again, and stop once it reaches a video that has already been downloaded.
|
||||
|
|
@ -11,12 +11,14 @@ from ytdl_sub.entries.variables.override_variables import REQUIRED_OVERRIDE_VARI
|
|||
from ytdl_sub.entries.variables.override_variables import OverrideHelpers
|
||||
from ytdl_sub.script.parser import parse
|
||||
from ytdl_sub.script.script import Script
|
||||
from ytdl_sub.script.types.resolvable import Resolvable
|
||||
from ytdl_sub.script.utils.exceptions import ScriptVariableNotResolved
|
||||
from ytdl_sub.utils.exceptions import InvalidVariableNameException
|
||||
from ytdl_sub.utils.exceptions import StringFormattingException
|
||||
from ytdl_sub.utils.exceptions import ValidationException
|
||||
from ytdl_sub.utils.script import ScriptUtils
|
||||
from ytdl_sub.utils.scriptable import Scriptable
|
||||
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 UnstructuredDictFormatterValidator
|
||||
|
||||
|
|
@ -144,11 +146,36 @@ class Overrides(UnstructuredDictFormatterValidator, Scriptable):
|
|||
self.update_script()
|
||||
return self
|
||||
|
||||
def _apply_to_resolvable(
|
||||
self,
|
||||
formatter: StringFormatterValidator,
|
||||
entry: Optional[Entry],
|
||||
function_overrides: Optional[Dict[str, str]],
|
||||
) -> Resolvable:
|
||||
script: Script = self.script
|
||||
unresolvable: Set[str] = self.unresolvable
|
||||
if entry:
|
||||
script = entry.script
|
||||
unresolvable = entry.unresolvable
|
||||
|
||||
try:
|
||||
return script.resolve_once(
|
||||
dict({"tmp_var": formatter.format_string}, **(function_overrides or {})),
|
||||
unresolvable=unresolvable,
|
||||
)["tmp_var"]
|
||||
except ScriptVariableNotResolved as exc:
|
||||
raise StringFormattingException(
|
||||
"Tried to resolve the following script, but could not due to unresolved "
|
||||
f"variables:\n {formatter.format_string}\n"
|
||||
"This is most likely due to circular dependencies in variables. "
|
||||
"If you think otherwise, please file a bug on GitHub and post your config. Thanks!"
|
||||
) from exc
|
||||
|
||||
def apply_formatter(
|
||||
self,
|
||||
formatter: StringFormatterValidator,
|
||||
entry: Optional[Entry] = None,
|
||||
function_overrides: Dict[str, str] = None,
|
||||
function_overrides: Optional[Dict[str, str]] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Parameters
|
||||
|
|
@ -169,25 +196,28 @@ class Overrides(UnstructuredDictFormatterValidator, Scriptable):
|
|||
StringFormattingException
|
||||
If the formatter that is trying to be resolved cannot
|
||||
"""
|
||||
script: Script = self.script
|
||||
unresolvable: Set[str] = self.unresolvable
|
||||
if entry:
|
||||
script = entry.script
|
||||
unresolvable = entry.unresolvable
|
||||
|
||||
try:
|
||||
return formatter.post_process(
|
||||
str(
|
||||
script.resolve_once(
|
||||
dict({"tmp_var": formatter.format_string}, **(function_overrides or {})),
|
||||
unresolvable=unresolvable,
|
||||
)["tmp_var"]
|
||||
return formatter.post_process(
|
||||
str(
|
||||
self._apply_to_resolvable(
|
||||
formatter=formatter, entry=entry, function_overrides=function_overrides
|
||||
)
|
||||
)
|
||||
except ScriptVariableNotResolved as exc:
|
||||
raise StringFormattingException(
|
||||
"Tried to resolve the following script, but could not due to unresolved "
|
||||
f"variables:\n {formatter.format_string}\n"
|
||||
"This is most likely due to circular dependencies in variables. "
|
||||
"If you think otherwise, please file a bug on GitHub and post your config. Thanks!"
|
||||
) from exc
|
||||
)
|
||||
|
||||
def apply_overrides_formatter_to_native(
|
||||
self,
|
||||
formatter: OverridesStringFormatterValidator,
|
||||
) -> Any:
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
formatter
|
||||
Overrides formatter to apply
|
||||
|
||||
Returns
|
||||
-------
|
||||
The native python form of the resolved variable
|
||||
"""
|
||||
return self._apply_to_resolvable(
|
||||
formatter=formatter, entry=None, function_overrides=None
|
||||
).native
|
||||
|
|
|
|||
|
|
@ -9,11 +9,13 @@ from ytdl_sub.validators.string_datetime import StringDatetimeValidator
|
|||
from ytdl_sub.validators.string_formatter_validators import OverridesIntegerFormatterValidator
|
||||
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 (
|
||||
UnstructuredOverridesDictFormatterValidator,
|
||||
)
|
||||
from ytdl_sub.validators.validators import BoolValidator
|
||||
from ytdl_sub.validators.validators import LiteralDictValidator
|
||||
|
||||
|
||||
class YTDLOptions(LiteralDictValidator):
|
||||
class YTDLOptions(UnstructuredOverridesDictFormatterValidator):
|
||||
"""
|
||||
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
|
||||
|
|
|
|||
|
|
@ -37,10 +37,12 @@ presets:
|
|||
|
||||
chunk_initial_download: # legacy preset name
|
||||
ytdl_options:
|
||||
max_downloads: 20
|
||||
max_downloads: "{chunk_max_downloads}"
|
||||
playlistreverse: True
|
||||
break_on_existing: False
|
||||
overrides:
|
||||
chunk_max_downloads: 20
|
||||
|
||||
"Download in Chunks":
|
||||
"Chunk Downloads":
|
||||
preset:
|
||||
- chunk_initial_download
|
||||
|
|
@ -110,7 +110,11 @@ class SubscriptionYTDLOptions:
|
|||
|
||||
@property
|
||||
def _user_ytdl_options(self) -> Dict:
|
||||
return self._preset.ytdl_options.dict
|
||||
native_ytdl_options = {
|
||||
key: self._overrides.apply_overrides_formatter_to_native(val)
|
||||
for key, val in self._preset.ytdl_options.dict.items()
|
||||
}
|
||||
return native_ytdl_options
|
||||
|
||||
@property
|
||||
def _plugin_match_filters(self) -> Dict:
|
||||
|
|
|
|||
|
|
@ -12,7 +12,10 @@ def subscription_dict(output_directory):
|
|||
return {
|
||||
"preset": "Bandcamp",
|
||||
"ytdl_options": {
|
||||
"max_downloads": 15,
|
||||
# Test that ytdl-options can handle overrides
|
||||
# TODO: Move this to a local test
|
||||
"max_downloads": "{max_downloads}",
|
||||
"extractor_args": {"youtube": {"lang": ["en"]}},
|
||||
},
|
||||
"audio_extract": {"codec": "mp3", "quality": 320},
|
||||
"date_range": {"after": "20210110"},
|
||||
|
|
@ -20,6 +23,7 @@ def subscription_dict(output_directory):
|
|||
"subscription_value": "https://sithuayemusic.bandcamp.com/",
|
||||
"subscription_indent_1": "Progressive Metal",
|
||||
"music_directory": output_directory,
|
||||
"max_downloads": 15,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue