no more entry script, maybe bad idea???
This commit is contained in:
parent
a4920c03c3
commit
8cd6ed12e0
16 changed files with 289 additions and 296 deletions
|
|
@ -1,22 +1,29 @@
|
||||||
import copy
|
import copy
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import Optional
|
|
||||||
from typing import Set
|
from typing import Set
|
||||||
|
from typing import Type
|
||||||
|
from typing import TypeVar
|
||||||
|
from typing import final
|
||||||
|
|
||||||
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.script.variable_definitions import VARIABLES
|
from ytdl_sub.entries.script.variable_definitions import VARIABLES
|
||||||
|
from ytdl_sub.entries.script.variable_definitions import Variable
|
||||||
|
from ytdl_sub.entries.script.variable_scripts import UNRESOLVED_VARIABLES
|
||||||
|
from ytdl_sub.entries.script.variable_scripts import VARIABLE_SCRIPTS
|
||||||
from ytdl_sub.entries.variables.override_variables import SUBSCRIPTION_NAME
|
from ytdl_sub.entries.variables.override_variables import SUBSCRIPTION_NAME
|
||||||
from ytdl_sub.script.parser import parse
|
from ytdl_sub.script.parser import parse
|
||||||
from ytdl_sub.script.script import Script
|
from ytdl_sub.script.script import Script
|
||||||
from ytdl_sub.utils.scriptable import Scriptable
|
from ytdl_sub.utils.script import ScriptUtils
|
||||||
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 StringFormatterValidator
|
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
|
||||||
|
|
||||||
|
TType = TypeVar("TType")
|
||||||
|
|
||||||
class Overrides(DictFormatterValidator, Scriptable):
|
|
||||||
|
class Overrides(DictFormatterValidator):
|
||||||
"""
|
"""
|
||||||
Optional. This section allows you to define variables that can be used in any string formatter.
|
Optional. This section allows you to define variables that can be used in any string formatter.
|
||||||
For example, if you want your file and thumbnail files to match without copy-pasting a large
|
For example, if you want your file and thumbnail files to match without copy-pasting a large
|
||||||
|
|
@ -62,8 +69,7 @@ class Overrides(DictFormatterValidator, Scriptable):
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
DictFormatterValidator.__init__(self, name, value)
|
super().__init__(name, value)
|
||||||
Scriptable.__init__(self)
|
|
||||||
|
|
||||||
# Add sanitized overrides
|
# Add sanitized overrides
|
||||||
for key in self._keys:
|
for key in self._keys:
|
||||||
|
|
@ -81,14 +87,53 @@ class Overrides(DictFormatterValidator, Scriptable):
|
||||||
sanitize=sanitized,
|
sanitize=sanitized,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.unresolvable.add(VARIABLES.entry_metadata.variable_name)
|
self.script = Script(copy.deepcopy(VARIABLE_SCRIPTS))
|
||||||
|
self.unresolvable: Set[str] = copy.deepcopy(UNRESOLVED_VARIABLES)
|
||||||
def initialize_script(self, unresolved_variables: Dict[str, str]) -> None:
|
|
||||||
self.script.add(dict(self.dict_with_format_strings, **unresolved_variables))
|
|
||||||
self.unresolvable.update(set(unresolved_variables.keys()))
|
|
||||||
|
|
||||||
|
def initialize_script(self, unresolved_variables: Set[str]) -> None:
|
||||||
|
self.unresolvable |= unresolved_variables
|
||||||
|
self.script.add(
|
||||||
|
dict(
|
||||||
|
self.dict_with_format_strings,
|
||||||
|
**{
|
||||||
|
unresolved: f"{{%throw('Variable {unresolved} has not been resolved yet')}}"
|
||||||
|
for unresolved in self.unresolvable
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
self.update_script()
|
self.update_script()
|
||||||
|
|
||||||
|
def add_entry_kwargs(self, entry_kwargs: Dict[str, Any]) -> "Overrides":
|
||||||
|
self.unresolvable.remove(VARIABLES.entry_metadata.variable_name)
|
||||||
|
self.script.add(
|
||||||
|
{VARIABLES.entry_metadata.variable_name: ScriptUtils.to_script(entry_kwargs)}
|
||||||
|
)
|
||||||
|
self.update_script()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def add(self, values: Dict[str, Any]) -> None:
|
||||||
|
self.unresolvable -= set(list(values.keys()))
|
||||||
|
self.script.add(
|
||||||
|
ScriptUtils.add_sanitized_variables(
|
||||||
|
{name: ScriptUtils.to_script(value) for name, value in values.items()}
|
||||||
|
),
|
||||||
|
unresolvable=self.unresolvable,
|
||||||
|
)
|
||||||
|
self.update_script()
|
||||||
|
|
||||||
|
def update_script(self) -> None:
|
||||||
|
self.script.resolve(unresolvable=self.unresolvable, update=True)
|
||||||
|
|
||||||
|
def get(self, variable: Variable | str, expected_type: Type[TType]) -> TType:
|
||||||
|
out = self.script.resolve(unresolvable=self.unresolvable).get_native(variable.variable_name)
|
||||||
|
return expected_type(out)
|
||||||
|
|
||||||
|
def get_str(self, variable: Variable) -> str:
|
||||||
|
return self.get(variable, str)
|
||||||
|
|
||||||
|
def get_int(self, variable: Variable) -> int:
|
||||||
|
return self.get(variable, int)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def subscription_name(self) -> str:
|
def subscription_name(self) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
@ -98,10 +143,18 @@ class Overrides(DictFormatterValidator, Scriptable):
|
||||||
"""
|
"""
|
||||||
return self._root_name
|
return self._root_name
|
||||||
|
|
||||||
|
@final
|
||||||
|
def to_dict(self) -> Dict[str, str]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Dictionary containing all variables
|
||||||
|
"""
|
||||||
|
return self.script.resolve().as_native()
|
||||||
|
|
||||||
def apply_formatter(
|
def apply_formatter(
|
||||||
self,
|
self,
|
||||||
formatter: StringFormatterValidator,
|
formatter: StringFormatterValidator,
|
||||||
entry: Optional[Entry] = None,
|
|
||||||
function_overrides: Dict[str, str] = None,
|
function_overrides: Dict[str, str] = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
@ -118,17 +171,15 @@ class Overrides(DictFormatterValidator, Scriptable):
|
||||||
-------
|
-------
|
||||||
The format_string after .format has been called
|
The format_string after .format has been called
|
||||||
"""
|
"""
|
||||||
script: Script = self.script
|
|
||||||
unresolvable: Set[str] = self.unresolvable
|
|
||||||
if entry:
|
|
||||||
script = entry.script
|
|
||||||
unresolvable = entry.unresolvable
|
|
||||||
|
|
||||||
return formatter.post_process(
|
return formatter.post_process(
|
||||||
str(
|
str(
|
||||||
script.resolve_once(
|
self.script.resolve_once(
|
||||||
dict({"tmp_var": formatter.format_string}, **(function_overrides or {})),
|
dict({"tmp_var": formatter.format_string}, **(function_overrides or {})),
|
||||||
unresolvable=unresolvable,
|
unresolvable=self.unresolvable.union(
|
||||||
|
VARIABLES.entry_metadata.variable_name
|
||||||
|
if isinstance(formatter, OverridesStringFormatterValidator)
|
||||||
|
else set()
|
||||||
|
),
|
||||||
)["tmp_var"]
|
)["tmp_var"]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -361,12 +361,7 @@ class Preset(_PresetShell):
|
||||||
# values from multiple validators
|
# values from multiple validators
|
||||||
self.__recursive_preset_validate()
|
self.__recursive_preset_validate()
|
||||||
|
|
||||||
self.overrides.initialize_script(
|
self.overrides.initialize_script(unresolved_variables=set(list(self._added_variables.keys())))
|
||||||
unresolved_variables={
|
|
||||||
var_name: f"{{%throw('Plugin variable {var_name} has not been created yet')}}"
|
|
||||||
for var_name in self._added_variables
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
|
|
||||||
|
|
@ -109,17 +109,17 @@ class InfoJsonDownloader(SourcePlugin[InfoJsonDownloaderOptions]):
|
||||||
prior_variables = entry.kwargs(YTDL_SUB_ENTRY_VARIABLES_KWARG_KEY)
|
prior_variables = entry.kwargs(YTDL_SUB_ENTRY_VARIABLES_KWARG_KEY)
|
||||||
del entry._kwargs[YTDL_SUB_ENTRY_VARIABLES_KWARG_KEY]
|
del entry._kwargs[YTDL_SUB_ENTRY_VARIABLES_KWARG_KEY]
|
||||||
|
|
||||||
entry.initialize_script(
|
self.overrides.add(
|
||||||
override_variables=self.overrides.dict_with_format_strings,
|
dict(
|
||||||
unresolvable=self.overrides.unresolvable,
|
entry._kwargs,
|
||||||
).add(
|
**{
|
||||||
{
|
inj.variable_name: prior_variables.get(
|
||||||
inj.variable_name: prior_variables.get(
|
inj.variable_name,
|
||||||
inj.variable_name,
|
VARIABLE_SCRIPTS[inj.variable_name],
|
||||||
VARIABLE_SCRIPTS[inj.variable_name],
|
)
|
||||||
)
|
for inj in ENTRY_INJECTED_VARIABLES
|
||||||
for inj in ENTRY_INJECTED_VARIABLES
|
},
|
||||||
}
|
)
|
||||||
)
|
)
|
||||||
entries.append(entry)
|
entries.append(entry)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ class UrlDownloaderThumbnailPlugin(SourcePluginExtension):
|
||||||
Downloads and moves channel avatar and banner images to the output directory.
|
Downloads and moves channel avatar and banner images to the output directory.
|
||||||
"""
|
"""
|
||||||
for thumbnail_info in thumbnail_list_info.list:
|
for thumbnail_info in thumbnail_list_info.list:
|
||||||
thumbnail_name = self.overrides.apply_formatter(thumbnail_info.name, entry=entry)
|
thumbnail_name = self.overrides.apply_formatter(thumbnail_info.name)
|
||||||
thumbnail_id = self.overrides.apply_formatter(thumbnail_info.uid)
|
thumbnail_id = self.overrides.apply_formatter(thumbnail_info.uid)
|
||||||
|
|
||||||
# If the thumbnail name is an empty string, completely ignore trying to download it
|
# If the thumbnail name is an empty string, completely ignore trying to download it
|
||||||
|
|
@ -176,7 +176,7 @@ class UrlDownloaderCollectionVariablePlugin(SourcePluginExtension):
|
||||||
"""
|
"""
|
||||||
# COLLECTION_URL is a recent variable that may not exist for old entries when updating.
|
# COLLECTION_URL is a recent variable that may not exist for old entries when updating.
|
||||||
# Try to use source_webpage_url if it does not exist
|
# Try to use source_webpage_url if it does not exist
|
||||||
entry_collection_url = entry.get_str(v.ytdl_sub_input_url)
|
entry_collection_url = self.overrides.get_str(v.ytdl_sub_input_url)
|
||||||
|
|
||||||
# If the collection URL cannot find its mapping, use the last URL
|
# If the collection URL cannot find its mapping, use the last URL
|
||||||
collection_url = (
|
collection_url = (
|
||||||
|
|
@ -459,15 +459,16 @@ class MultiUrlDownloader(SourcePlugin[MultiUrlValidator]):
|
||||||
for entry in self._iterate_entries(
|
for entry in self._iterate_entries(
|
||||||
url_validator=collection_url, parents=parents, orphans=orphan_entries
|
url_validator=collection_url, parents=parents, orphans=orphan_entries
|
||||||
):
|
):
|
||||||
entry.initialize_script(
|
|
||||||
override_variables=self.overrides.dict_with_format_strings,
|
self.overrides.add(
|
||||||
unresolvable=self.overrides.unresolvable,
|
dict(
|
||||||
).add(
|
entry._kwargs,
|
||||||
{
|
**{
|
||||||
v.ytdl_sub_input_url.variable_name: self.overrides.apply_formatter(
|
v.ytdl_sub_input_url.variable_name: self.overrides.apply_formatter(
|
||||||
collection_url.url
|
collection_url.url
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
)
|
||||||
)
|
)
|
||||||
yield entry
|
yield entry
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,13 @@ import copy
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Set
|
|
||||||
from typing import Type
|
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
from typing import final
|
from typing import final
|
||||||
|
|
||||||
|
from ytdl_sub.config.overrides import Overrides
|
||||||
from ytdl_sub.entries.base_entry import BaseEntry
|
from ytdl_sub.entries.base_entry import BaseEntry
|
||||||
from ytdl_sub.entries.script.variable_definitions import VARIABLES
|
from ytdl_sub.entries.script.variable_definitions import VARIABLES
|
||||||
from ytdl_sub.entries.script.variable_definitions import Variable
|
|
||||||
from ytdl_sub.utils.scriptable import Scriptable
|
|
||||||
from ytdl_sub.validators.audo_codec_validator import AUDIO_CODEC_EXTS
|
from ytdl_sub.validators.audo_codec_validator import AUDIO_CODEC_EXTS
|
||||||
from ytdl_sub.validators.audo_codec_validator import VIDEO_CODEC_EXTS
|
from ytdl_sub.validators.audo_codec_validator import VIDEO_CODEC_EXTS
|
||||||
|
|
||||||
|
|
@ -21,49 +17,11 @@ YTDL_SUB_ENTRY_VARIABLES_KWARG_KEY: str = "ytdl_sub_entry_variables"
|
||||||
TType = TypeVar("TType")
|
TType = TypeVar("TType")
|
||||||
|
|
||||||
|
|
||||||
class Entry(BaseEntry, Scriptable):
|
class Entry(BaseEntry):
|
||||||
"""
|
"""
|
||||||
Entry object to represent a single media object returned from yt-dlp.
|
Entry object to represent a single media object returned from yt-dlp.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, entry_dict: Dict, working_directory: str):
|
|
||||||
BaseEntry.__init__(self, entry_dict=entry_dict, working_directory=working_directory)
|
|
||||||
Scriptable.__init__(self)
|
|
||||||
|
|
||||||
def initialize_script(
|
|
||||||
self, override_variables: Dict[str, str], unresolvable: Set[str]
|
|
||||||
) -> "Entry":
|
|
||||||
# TODO: CLEAN THIS SHIT UP
|
|
||||||
# Overrides contains added variables that are unresolvable, add them here
|
|
||||||
self.unresolvable |= unresolvable
|
|
||||||
|
|
||||||
# Remove the entry variable
|
|
||||||
self.unresolvable.remove(VARIABLES.entry_metadata.variable_name)
|
|
||||||
|
|
||||||
# Add entry metadata, but avoid the `.add()` helper since it also adds sanitized
|
|
||||||
self.script.add({VARIABLES.entry_metadata.variable_name: f"{{{json.dumps(self._kwargs)}}}"})
|
|
||||||
self.script.add(
|
|
||||||
{
|
|
||||||
unresolved: f"{{%throw('Variable {unresolved} has not been resolved yet')}}"
|
|
||||||
for unresolved in self.unresolvable
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# use .add here to get sanitized
|
|
||||||
self.add(override_variables)
|
|
||||||
self.update_script()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def get(self, variable: Variable, expected_type: Type[TType]) -> TType:
|
|
||||||
out = self.script.resolve(unresolvable=self.unresolvable).get_native(variable.variable_name)
|
|
||||||
return expected_type(out)
|
|
||||||
|
|
||||||
def get_str(self, variable: Variable) -> str:
|
|
||||||
return self.get(variable, str)
|
|
||||||
|
|
||||||
def get_int(self, variable: Variable) -> int:
|
|
||||||
return self.get(variable, int)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ext(self) -> str:
|
def ext(self) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
@ -71,7 +29,7 @@ class Entry(BaseEntry, Scriptable):
|
||||||
This is not reflected in the entry. See if the mkv file exists and return "mkv" if so,
|
This is not reflected in the entry. See if the mkv file exists and return "mkv" if so,
|
||||||
otherwise, return the original extension.
|
otherwise, return the original extension.
|
||||||
"""
|
"""
|
||||||
ext = self.get_str(VARIABLES.ext)
|
ext = self.kwargs(VARIABLES.ext.metadata_key)
|
||||||
for possible_ext in [ext, "mkv"]:
|
for possible_ext in [ext, "mkv"]:
|
||||||
file_path = str(Path(self.working_directory()) / f"{self.uid}.{possible_ext}")
|
file_path = str(Path(self.working_directory()) / f"{self.uid}.{possible_ext}")
|
||||||
if os.path.isfile(file_path):
|
if os.path.isfile(file_path):
|
||||||
|
|
@ -97,7 +55,7 @@ class Entry(BaseEntry, Scriptable):
|
||||||
-------
|
-------
|
||||||
The download thumbnail's file name
|
The download thumbnail's file name
|
||||||
"""
|
"""
|
||||||
return f"{self.get_str(VARIABLES.uid)}.{self.get_str(VARIABLES.thumbnail_ext)}"
|
return f"{self.kwargs(VARIABLES.uid.metadata_key)}.jpg"
|
||||||
|
|
||||||
def get_download_thumbnail_path(self) -> str:
|
def get_download_thumbnail_path(self) -> str:
|
||||||
"""Returns the entry's thumbnail's file path to where it was downloaded"""
|
"""Returns the entry's thumbnail's file path to where it was downloaded"""
|
||||||
|
|
@ -121,12 +79,12 @@ class Entry(BaseEntry, Scriptable):
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def write_info_json(self) -> None:
|
def write_info_json(self, overrides: Overrides) -> None:
|
||||||
"""
|
"""
|
||||||
Write the entry's _kwargs back into the info.json file as well as its source variables
|
Write the entry's _kwargs back into the info.json file as well as its source variables
|
||||||
"""
|
"""
|
||||||
kwargs_dict = copy.deepcopy(self._kwargs)
|
kwargs_dict = copy.deepcopy(self._kwargs)
|
||||||
kwargs_dict["ytdl_sub_entry_variables"] = self.to_dict()
|
kwargs_dict["ytdl_sub_entry_variables"] = overrides.to_dict()
|
||||||
kwargs_json = json.dumps(kwargs_dict, ensure_ascii=False, sort_keys=True, indent=2)
|
kwargs_json = json.dumps(kwargs_dict, ensure_ascii=False, sort_keys=True, indent=2)
|
||||||
|
|
||||||
with open(self.get_download_info_json_path(), "w", encoding="utf-8") as file:
|
with open(self.get_download_info_json_path(), "w", encoding="utf-8") as file:
|
||||||
|
|
@ -168,12 +126,3 @@ class Entry(BaseEntry, Scriptable):
|
||||||
break
|
break
|
||||||
|
|
||||||
return file_exists
|
return file_exists
|
||||||
|
|
||||||
@final
|
|
||||||
def to_dict(self) -> Dict[str, str]:
|
|
||||||
"""
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Dictionary containing all variables
|
|
||||||
"""
|
|
||||||
return self.script.resolve().as_native()
|
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ ENTRY_HARDCODED_VARIABLES: Dict[Variable, str] = {
|
||||||
ENTRY_RELATIVE_VARIABLES: Dict[MetadataVariable, str] = {
|
ENTRY_RELATIVE_VARIABLES: Dict[MetadataVariable, str] = {
|
||||||
v.playlist_metadata: entry_get(v.playlist_metadata, {}),
|
v.playlist_metadata: entry_get(v.playlist_metadata, {}),
|
||||||
v.source_metadata: entry_get(v.source_metadata, {}),
|
v.source_metadata: entry_get(v.source_metadata, {}),
|
||||||
v.sibling_entry_metadata: entry_get(v.sibling_entry_metadata, "{ [] }"),
|
v.sibling_entry_metadata: entry_get(v.sibling_entry_metadata, []),
|
||||||
}
|
}
|
||||||
|
|
||||||
ENTRY_REQUIRED_VARIABLES: Dict[MetadataVariable, str] = {
|
ENTRY_REQUIRED_VARIABLES: Dict[MetadataVariable, str] = {
|
||||||
|
|
@ -279,6 +279,11 @@ mergedeep.merge(
|
||||||
VARIABLE_SCRIPTS: Dict[str, str] = {
|
VARIABLE_SCRIPTS: Dict[str, str] = {
|
||||||
var.variable_name: script for var, script in _VARIABLE_SCRIPTS.items()
|
var.variable_name: script for var, script in _VARIABLE_SCRIPTS.items()
|
||||||
}
|
}
|
||||||
UNRESOLVED_VARIABLES: Set[str] = {var.variable_name for var in ENTRY_INJECTED_VARIABLES}
|
|
||||||
|
|
||||||
|
UNRESOLVED_VARIABLES: Set[str] = {
|
||||||
|
var.variable_name
|
||||||
|
for var in list(ENTRY_EMPTY_METADATA.keys())
|
||||||
|
+ list(ENTRY_INJECTED_VARIABLES.keys())
|
||||||
|
+ list(ENTRY_RELATIVE_VARIABLES.keys())
|
||||||
|
}
|
||||||
CustomFunctions.register()
|
CustomFunctions.register()
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ class ViewPlugin(Plugin[ViewOptions]):
|
||||||
"""
|
"""
|
||||||
Adds all source variables to the entry
|
Adds all source variables to the entry
|
||||||
"""
|
"""
|
||||||
source_var_dict = copy.deepcopy(entry.to_dict())
|
source_var_dict = self.overrides.to_dict()
|
||||||
for key in source_var_dict.keys():
|
for key in source_var_dict.keys():
|
||||||
source_var_dict[key] = self._truncate_value(source_var_dict[key])
|
source_var_dict[key] = self._truncate_value(source_var_dict[key])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ class MusicTagsPlugin(Plugin[MusicTagsOptions]):
|
||||||
tags_to_write: Dict[str, List[str]] = defaultdict(list)
|
tags_to_write: Dict[str, List[str]] = defaultdict(list)
|
||||||
for tag_name, tag_formatters in self.plugin_options.tags.as_lists.items():
|
for tag_name, tag_formatters in self.plugin_options.tags.as_lists.items():
|
||||||
for tag_formatter in tag_formatters:
|
for tag_formatter in tag_formatters:
|
||||||
tag_value = self.overrides.apply_formatter(formatter=tag_formatter, entry=entry)
|
tag_value = self.overrides.apply_formatter(formatter=tag_formatter)
|
||||||
tags_to_write[tag_name].append(tag_value)
|
tags_to_write[tag_name].append(tag_value)
|
||||||
|
|
||||||
# write the actual tags if its not a dry run
|
# write the actual tags if its not a dry run
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ class SharedNfoTagsPlugin(Plugin[SharedNfoTagsOptions], ABC):
|
||||||
for key, string_tags in self.plugin_options.tags.string_tags.items():
|
for key, string_tags in self.plugin_options.tags.string_tags.items():
|
||||||
tags = [
|
tags = [
|
||||||
XmlElement(
|
XmlElement(
|
||||||
text=self.overrides.apply_formatter(formatter=string_tag, entry=entry),
|
text=self.overrides.apply_formatter(formatter=string_tag),
|
||||||
attributes={},
|
attributes={},
|
||||||
)
|
)
|
||||||
for string_tag in string_tags
|
for string_tag in string_tags
|
||||||
|
|
@ -110,11 +110,9 @@ class SharedNfoTagsPlugin(Plugin[SharedNfoTagsOptions], ABC):
|
||||||
for key, attribute_tags in self.plugin_options.tags.attribute_tags.items():
|
for key, attribute_tags in self.plugin_options.tags.attribute_tags.items():
|
||||||
tags = [
|
tags = [
|
||||||
XmlElement(
|
XmlElement(
|
||||||
text=self.overrides.apply_formatter(formatter=attribute_tag.tag, entry=entry),
|
text=self.overrides.apply_formatter(formatter=attribute_tag.tag),
|
||||||
attributes={
|
attributes={
|
||||||
attr_name: self.overrides.apply_formatter(
|
attr_name: self.overrides.apply_formatter(formatter=attr_formatter)
|
||||||
formatter=attr_formatter, entry=entry
|
|
||||||
)
|
|
||||||
for attr_name, attr_formatter in attribute_tag.attributes.dict.items()
|
for attr_name, attr_formatter in attribute_tag.attributes.dict.items()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -128,9 +126,7 @@ class SharedNfoTagsPlugin(Plugin[SharedNfoTagsOptions], ABC):
|
||||||
|
|
||||||
def _create_nfo(self, entry: Entry, save_to_entry: bool = True) -> None:
|
def _create_nfo(self, entry: Entry, save_to_entry: bool = True) -> None:
|
||||||
# Write the nfo tags to XML with the nfo_root
|
# Write the nfo tags to XML with the nfo_root
|
||||||
nfo_root = self.overrides.apply_formatter(
|
nfo_root = self.overrides.apply_formatter(formatter=self.plugin_options.nfo_root)
|
||||||
formatter=self.plugin_options.nfo_root, entry=entry
|
|
||||||
)
|
|
||||||
nfo_tags = self._get_xml_element_dict(entry=entry)
|
nfo_tags = self._get_xml_element_dict(entry=entry)
|
||||||
|
|
||||||
# If the nfo tags are empty, then stop continuing
|
# If the nfo tags are empty, then stop continuing
|
||||||
|
|
@ -152,9 +148,7 @@ class SharedNfoTagsPlugin(Plugin[SharedNfoTagsOptions], ABC):
|
||||||
|
|
||||||
xml = to_xml(nfo_dict=nfo_tags, nfo_root=nfo_root)
|
xml = to_xml(nfo_dict=nfo_tags, nfo_root=nfo_root)
|
||||||
|
|
||||||
nfo_file_name = self.overrides.apply_formatter(
|
nfo_file_name = self.overrides.apply_formatter(formatter=self.plugin_options.nfo_name)
|
||||||
formatter=self.plugin_options.nfo_name, entry=entry
|
|
||||||
)
|
|
||||||
|
|
||||||
# Save the nfo's XML to file
|
# Save the nfo's XML to file
|
||||||
nfo_file_path = Path(self.working_directory) / nfo_file_name
|
nfo_file_path = Path(self.working_directory) / nfo_file_name
|
||||||
|
|
|
||||||
|
|
@ -288,34 +288,16 @@ class RegexPlugin(Plugin[RegexOptions]):
|
||||||
# Otherwise, error
|
# Otherwise, error
|
||||||
raise RegexNoMatchException(f"Regex failed to match '{variable_name}' from '{entry.title}'")
|
raise RegexNoMatchException(f"Regex failed to match '{variable_name}' from '{entry.title}'")
|
||||||
|
|
||||||
def _can_process_at_metadata_stage(self, entry: Entry, variable_name: str) -> bool:
|
def _can_process_at_metadata_stage(self, variable_name: str) -> bool:
|
||||||
# If the variable is an override...
|
# Try to see if it can resolve
|
||||||
if variable_name in self.overrides.dict:
|
try:
|
||||||
# Try to see if it can resolve
|
self.overrides.apply_formatter(formatter=self.overrides.dict[variable_name])
|
||||||
try:
|
# If it can not from missing variables (from post-metadata stage), return False
|
||||||
self.overrides.apply_formatter(
|
except StringFormattingVariableNotFoundException:
|
||||||
formatter=self.overrides.dict[variable_name],
|
|
||||||
entry=entry,
|
|
||||||
)
|
|
||||||
# If it can not from missing variables (from post-metadata stage), return False
|
|
||||||
except StringFormattingVariableNotFoundException:
|
|
||||||
return False
|
|
||||||
# If it is a source variable and not present, return false
|
|
||||||
elif variable_name not in entry.to_dict():
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _get_regex_input_string(self, entry: Entry, variable_name: str) -> str:
|
|
||||||
# Apply override formatter if it's an override
|
|
||||||
if variable_name in self.overrides.dict:
|
|
||||||
return self.overrides.apply_formatter(
|
|
||||||
formatter=self.overrides.dict[variable_name],
|
|
||||||
entry=entry,
|
|
||||||
)
|
|
||||||
# Otherwise pluck from the entry's source variable
|
|
||||||
return entry.to_dict()[variable_name]
|
|
||||||
|
|
||||||
def _modify_entry_metadata(self, entry: Entry, is_metadata_stage: bool) -> Optional[Entry]:
|
def _modify_entry_metadata(self, entry: Entry, is_metadata_stage: bool) -> Optional[Entry]:
|
||||||
"""
|
"""
|
||||||
Parameters
|
Parameters
|
||||||
|
|
@ -348,16 +330,13 @@ class RegexPlugin(Plugin[RegexOptions]):
|
||||||
|
|
||||||
# If it's the metadata stage, and it can't be processed, skip until post-metadata
|
# If it's the metadata stage, and it can't be processed, skip until post-metadata
|
||||||
if is_metadata_stage and not self._can_process_at_metadata_stage(
|
if is_metadata_stage and not self._can_process_at_metadata_stage(
|
||||||
entry=entry, variable_name=variable_name
|
variable_name=variable_name
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self._add_processed_regex_variable_name(entry, variable_name)
|
self._add_processed_regex_variable_name(entry, variable_name)
|
||||||
|
|
||||||
regex_input_str = self._get_regex_input_string(
|
regex_input_str = self.overrides.get_str(variable_name)
|
||||||
entry=entry,
|
|
||||||
variable_name=variable_name,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
regex_options.exclude is not None
|
regex_options.exclude is not None
|
||||||
|
|
@ -375,49 +354,22 @@ class RegexPlugin(Plugin[RegexOptions]):
|
||||||
if not regex_options.has_defaults:
|
if not regex_options.has_defaults:
|
||||||
return self._try_skip_entry(entry=entry, variable_name=variable_name)
|
return self._try_skip_entry(entry=entry, variable_name=variable_name)
|
||||||
|
|
||||||
# otherwise, use defaults (apply them using the original entry source dict)
|
# otherwise, use defaults
|
||||||
source_variables_and_overrides_dict = dict(
|
self.overrides.add(
|
||||||
entry.to_dict(), **self.overrides.dict_with_format_strings
|
{
|
||||||
|
regex_options.capture_group_names[i]: default
|
||||||
|
for i, default in enumerate(regex_options.capture_group_defaults)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# add both the default...
|
|
||||||
entry.add_variables(
|
|
||||||
variables_to_add={
|
|
||||||
regex_options.capture_group_names[i]: default.apply_formatter(
|
|
||||||
variable_dict=source_variables_and_overrides_dict
|
|
||||||
)
|
|
||||||
for i, default in enumerate(regex_options.capture_group_defaults)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
# and sanitized default
|
|
||||||
entry.add_variables(
|
|
||||||
variables_to_add={
|
|
||||||
f"{regex_options.capture_group_names[i]}_sanitized": sanitize_filename(
|
|
||||||
default.apply_formatter(
|
|
||||||
variable_dict=source_variables_and_overrides_dict
|
|
||||||
)
|
|
||||||
)
|
|
||||||
for i, default in enumerate(regex_options.capture_group_defaults)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
# There is a capture, add the source variables to the entry as
|
# There is a capture, add the source variables to the entry as
|
||||||
# {source_var}_capture_1, {source_var}_capture_2, ...
|
# {source_var}_capture_1, {source_var}_capture_2, ...
|
||||||
else:
|
else:
|
||||||
# Add the value...
|
self.overrides.add(
|
||||||
entry.add_variables(
|
{
|
||||||
variables_to_add={
|
|
||||||
regex_options.capture_group_names[i]: capture
|
regex_options.capture_group_names[i]: capture
|
||||||
for i, capture in enumerate(maybe_capture)
|
for i, capture in enumerate(maybe_capture)
|
||||||
},
|
}
|
||||||
)
|
|
||||||
# And the sanitized value
|
|
||||||
entry.add_variables(
|
|
||||||
variables_to_add={
|
|
||||||
f"{regex_options.capture_group_names[i]}_sanitized": sanitize_filename(
|
|
||||||
capture
|
|
||||||
)
|
|
||||||
for i, capture in enumerate(maybe_capture)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return entry
|
return entry
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,6 @@ class SubtitlesPlugin(Plugin[SubtitleOptions]):
|
||||||
subtitle_file_name = f"{entry.uid}.{lang}.{self.plugin_options.subtitles_type}"
|
subtitle_file_name = f"{entry.uid}.{lang}.{self.plugin_options.subtitles_type}"
|
||||||
output_subtitle_file_name = self.overrides.apply_formatter(
|
output_subtitle_file_name = self.overrides.apply_formatter(
|
||||||
formatter=self.plugin_options.subtitles_name,
|
formatter=self.plugin_options.subtitles_name,
|
||||||
entry=entry,
|
|
||||||
function_overrides={"lang": lang},
|
function_overrides={"lang": lang},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ class VideoTagsPlugin(Plugin[VideoTagsOptions]):
|
||||||
|
|
||||||
tags_to_write: Dict[str, str] = {}
|
tags_to_write: Dict[str, str] = {}
|
||||||
for tag_name, tag_formatter in self.plugin_options.tags.dict.items():
|
for tag_name, tag_formatter in self.plugin_options.tags.dict.items():
|
||||||
tag_value = self.overrides.apply_formatter(formatter=tag_formatter, entry=entry)
|
tag_value = self.overrides.apply_formatter(formatter=tag_formatter)
|
||||||
tags_to_write[tag_name] = tag_value
|
tags_to_write[tag_name] = tag_value
|
||||||
|
|
||||||
# write the actual tags if its not a dry run
|
# write the actual tags if its not a dry run
|
||||||
|
|
|
||||||
|
|
@ -62,9 +62,7 @@ class SubscriptionDownload(BaseSubscription, ABC):
|
||||||
Optional. Metadata to record to the transaction log for this entry
|
Optional. Metadata to record to the transaction log for this entry
|
||||||
"""
|
"""
|
||||||
# Move the file after all direct file modifications are complete
|
# Move the file after all direct file modifications are complete
|
||||||
output_file_name = self.overrides.apply_formatter(
|
output_file_name = self.overrides.apply_formatter(formatter=self.output_options.file_name)
|
||||||
formatter=self.output_options.file_name, entry=entry
|
|
||||||
)
|
|
||||||
self._enhanced_download_archive.save_file_to_output_directory(
|
self._enhanced_download_archive.save_file_to_output_directory(
|
||||||
file_name=entry.get_download_file_name(),
|
file_name=entry.get_download_file_name(),
|
||||||
file_metadata=entry_metadata,
|
file_metadata=entry_metadata,
|
||||||
|
|
@ -75,7 +73,7 @@ class SubscriptionDownload(BaseSubscription, ABC):
|
||||||
# Always pretend to include the thumbnail in a dry-run
|
# Always pretend to include the thumbnail in a dry-run
|
||||||
if self.output_options.thumbnail_name and (dry_run or entry.is_thumbnail_downloaded()):
|
if self.output_options.thumbnail_name and (dry_run or entry.is_thumbnail_downloaded()):
|
||||||
output_thumbnail_name = self.overrides.apply_formatter(
|
output_thumbnail_name = self.overrides.apply_formatter(
|
||||||
formatter=self.output_options.thumbnail_name, entry=entry
|
formatter=self.output_options.thumbnail_name
|
||||||
)
|
)
|
||||||
|
|
||||||
# Copy the thumbnails since they could be used later for other things
|
# Copy the thumbnails since they could be used later for other things
|
||||||
|
|
@ -92,7 +90,7 @@ class SubscriptionDownload(BaseSubscription, ABC):
|
||||||
|
|
||||||
if self.output_options.info_json_name:
|
if self.output_options.info_json_name:
|
||||||
output_info_json_name = self.overrides.apply_formatter(
|
output_info_json_name = self.overrides.apply_formatter(
|
||||||
formatter=self.output_options.info_json_name, entry=entry
|
formatter=self.output_options.info_json_name
|
||||||
)
|
)
|
||||||
|
|
||||||
# if not dry-run, write the info json
|
# if not dry-run, write the info json
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ class ScriptUtils:
|
||||||
out = f"{{%float({value})}}"
|
out = f"{{%float({value})}}"
|
||||||
elif isinstance(value, bool):
|
elif isinstance(value, bool):
|
||||||
out = f"{{%bool({value})}}"
|
out = f"{{%bool({value})}}"
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
out = f"{{ {json.dumps(value)} }}"
|
||||||
else:
|
else:
|
||||||
out = json.dumps(value)
|
out = json.dumps(value)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,112 +50,128 @@ def download_file_name(uid, ext):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_entry_to_dict(
|
def mock_entry_to_dict():
|
||||||
uid,
|
|
||||||
title,
|
|
||||||
ext,
|
|
||||||
extractor,
|
|
||||||
upload_date,
|
|
||||||
thumbnail_ext,
|
|
||||||
webpage_url,
|
|
||||||
):
|
|
||||||
return {
|
return {
|
||||||
"uid": uid,
|
"channel": "abc123",
|
||||||
"uid_sanitized": uid,
|
"channel_id": "abc123",
|
||||||
|
"channel_sanitized": "abc123",
|
||||||
|
"comments": "",
|
||||||
|
"comments_sanitized": "",
|
||||||
|
"creator": "abc123",
|
||||||
|
"creator_sanitized": "abc123",
|
||||||
|
"description": "",
|
||||||
|
"download_index": 1,
|
||||||
|
"download_index_padded6": "000001",
|
||||||
|
"download_index_sanitized": "1",
|
||||||
|
"entry_metadata": {
|
||||||
|
"epoch": 1596878400,
|
||||||
|
"ext": "mp5",
|
||||||
|
"extractor": "xtract",
|
||||||
|
"extractor_key": "test_extractor_key",
|
||||||
|
"id": "abc123",
|
||||||
|
"thumbnail": "abc123.jpg",
|
||||||
|
"title": "entry {title}",
|
||||||
|
"upload_date": "20210112",
|
||||||
|
"webpage_url": "https://yourname.here",
|
||||||
|
},
|
||||||
"epoch": 1596878400,
|
"epoch": 1596878400,
|
||||||
"epoch_date": "20200808",
|
"epoch_date": "20200808",
|
||||||
"epoch_hour": "09",
|
"epoch_hour": "09",
|
||||||
"title": "entry {title}",
|
"ext": "mp5",
|
||||||
"title_sanitized": "entry {title}",
|
"extractor": "xtract",
|
||||||
"ext": ext,
|
|
||||||
"description": "",
|
|
||||||
"comments": "",
|
|
||||||
"requested_subtitles": "",
|
|
||||||
"sponsorblock_chapters": "",
|
|
||||||
"creator": "abc123",
|
|
||||||
"creator_sanitized": "abc123",
|
|
||||||
"channel": "abc123",
|
|
||||||
"channel_sanitized": "abc123",
|
|
||||||
"channel_id": uid,
|
|
||||||
"extractor": extractor,
|
|
||||||
"extractor_key": "test_extractor_key",
|
"extractor_key": "test_extractor_key",
|
||||||
"uploader": "abc123",
|
|
||||||
"uploader_id": "abc123",
|
|
||||||
"uploader_url": "https://yourname.here",
|
|
||||||
"download_index": 1,
|
|
||||||
"download_index_padded6": "000001",
|
|
||||||
"upload_date_index": 1,
|
|
||||||
"upload_date_index_padded": "01",
|
|
||||||
"upload_date_index_reversed": 99,
|
|
||||||
"upload_date_index_reversed_padded": "99",
|
|
||||||
"upload_date": upload_date,
|
|
||||||
"upload_date_standardized": "2021-01-12",
|
|
||||||
"upload_year": 2021,
|
|
||||||
"upload_year_truncated": 21,
|
|
||||||
"upload_year_truncated_reversed": 79,
|
|
||||||
"upload_month": 1,
|
|
||||||
"upload_month_padded": "01",
|
|
||||||
"upload_month_reversed": 12,
|
|
||||||
"upload_month_reversed_padded": "12",
|
|
||||||
"upload_day": 12,
|
|
||||||
"upload_day_padded": "12",
|
|
||||||
"upload_day_reversed": 20,
|
|
||||||
"upload_day_reversed_padded": "20",
|
|
||||||
"upload_day_of_year": 12,
|
|
||||||
"upload_day_of_year_padded": "012",
|
|
||||||
"upload_day_of_year_reversed": 354,
|
|
||||||
"upload_day_of_year_reversed_padded": "354",
|
|
||||||
"thumbnail_ext": thumbnail_ext,
|
|
||||||
"info_json_ext": "info.json",
|
"info_json_ext": "info.json",
|
||||||
"webpage_url": webpage_url,
|
"playlist_count": 1,
|
||||||
|
"playlist_description": "",
|
||||||
"playlist_index": 1,
|
"playlist_index": 1,
|
||||||
"playlist_index_padded": "01",
|
"playlist_index_padded": "01",
|
||||||
"playlist_index_padded6": "000001",
|
"playlist_index_padded6": "000001",
|
||||||
"playlist_index_reversed": 1,
|
"playlist_index_reversed": 1,
|
||||||
"playlist_index_reversed_padded": "01",
|
"playlist_index_reversed_padded": "01",
|
||||||
"playlist_index_reversed_padded6": "000001",
|
"playlist_index_reversed_padded6": "000001",
|
||||||
"playlist_count": 1,
|
"playlist_metadata": {},
|
||||||
"playlist_max_upload_year": 2021,
|
"playlist_metadata_sanitized": "{}",
|
||||||
"playlist_max_upload_year_truncated": 21,
|
|
||||||
"playlist_title": "entry {title}",
|
"playlist_title": "entry {title}",
|
||||||
"playlist_title_sanitized": "entry {title}",
|
"playlist_title_sanitized": "entry {title}",
|
||||||
"playlist_description": "",
|
|
||||||
"playlist_webpage_url": "https://yourname.here",
|
|
||||||
"playlist_uid": "abc123",
|
"playlist_uid": "abc123",
|
||||||
"playlist_uploader": "abc123",
|
"playlist_uploader": "abc123",
|
||||||
"playlist_uploader_sanitized": "abc123",
|
|
||||||
"playlist_uploader_id": "abc123",
|
"playlist_uploader_id": "abc123",
|
||||||
|
"playlist_uploader_sanitized": "abc123",
|
||||||
"playlist_uploader_url": "https://yourname.here",
|
"playlist_uploader_url": "https://yourname.here",
|
||||||
"source_count": 1,
|
"playlist_webpage_url": "https://yourname.here",
|
||||||
"source_description": "",
|
"release_date": "20210112",
|
||||||
"source_index": 1,
|
|
||||||
"source_index_padded": "01",
|
|
||||||
"source_title": "entry {title}",
|
|
||||||
"source_title_sanitized": "entry {title}",
|
|
||||||
"source_webpage_url": "https://yourname.here",
|
|
||||||
"source_uid": "abc123",
|
|
||||||
"source_uploader": "abc123",
|
|
||||||
"source_uploader_id": "abc123",
|
|
||||||
"source_uploader_url": "https://yourname.here",
|
|
||||||
"uid_sanitized_plex": "abc123",
|
|
||||||
"title_sanitized_plex": "entry {title}",
|
|
||||||
"release_date": upload_date,
|
|
||||||
"release_date_standardized": "2021-01-12",
|
"release_date_standardized": "2021-01-12",
|
||||||
"release_year": 2021,
|
|
||||||
"release_year_truncated": 21,
|
|
||||||
"release_year_truncated_reversed": 79,
|
|
||||||
"release_month": 1,
|
|
||||||
"release_month_padded": "01",
|
|
||||||
"release_month_reversed": 12,
|
|
||||||
"release_month_reversed_padded": "12",
|
|
||||||
"release_day": 12,
|
"release_day": 12,
|
||||||
"release_day_padded": "12",
|
|
||||||
"release_day_reversed": 20,
|
|
||||||
"release_day_reversed_padded": "20",
|
|
||||||
"release_day_of_year": 12,
|
"release_day_of_year": 12,
|
||||||
"release_day_of_year_padded": "012",
|
"release_day_of_year_padded": "012",
|
||||||
"release_day_of_year_reversed": 354,
|
"release_day_of_year_reversed": 354,
|
||||||
"release_day_of_year_reversed_padded": "354",
|
"release_day_of_year_reversed_padded": "354",
|
||||||
|
"release_day_padded": "12",
|
||||||
|
"release_day_reversed": 20,
|
||||||
|
"release_day_reversed_padded": "20",
|
||||||
|
"release_month": 1,
|
||||||
|
"release_month_padded": "01",
|
||||||
|
"release_month_reversed": 12,
|
||||||
|
"release_month_reversed_padded": "12",
|
||||||
|
"release_year": 2021,
|
||||||
|
"release_year_truncated": 21,
|
||||||
|
"release_year_truncated_reversed": 79,
|
||||||
|
"requested_subtitles": "",
|
||||||
|
"requested_subtitles_sanitized": "",
|
||||||
|
"sibling_entry_metadata": [],
|
||||||
|
"sibling_entry_metadata_sanitized": "[]",
|
||||||
|
"source_count": 1,
|
||||||
|
"source_description": "",
|
||||||
|
"source_index": 1,
|
||||||
|
"source_index_padded": "01",
|
||||||
|
"source_metadata": {},
|
||||||
|
"source_metadata_sanitized": "{}",
|
||||||
|
"source_title": "entry {title}",
|
||||||
|
"source_title_sanitized": "entry {title}",
|
||||||
|
"source_uid": "abc123",
|
||||||
|
"source_uploader": "abc123",
|
||||||
|
"source_uploader_id": "abc123",
|
||||||
|
"source_uploader_url": "https://yourname.here",
|
||||||
|
"source_webpage_url": "https://yourname.here",
|
||||||
|
"sponsorblock_chapters": "",
|
||||||
|
"sponsorblock_chapters_sanitized": "",
|
||||||
|
"subscription_name": "test",
|
||||||
|
"subscription_name_sanitized": "test",
|
||||||
|
"thumbnail_ext": "jpg",
|
||||||
|
"title": "entry {title}",
|
||||||
|
"title_sanitized": "entry {title}",
|
||||||
|
"title_sanitized_plex": "entry {title}",
|
||||||
|
"uid": "abc123",
|
||||||
|
"uid_sanitized": "abc123",
|
||||||
|
"uid_sanitized_plex": "abc123",
|
||||||
|
"upload_date": "20210112",
|
||||||
|
"upload_date_index": 1,
|
||||||
|
"upload_date_index_padded": "01",
|
||||||
|
"upload_date_index_reversed": 99,
|
||||||
|
"upload_date_index_reversed_padded": "99",
|
||||||
|
"upload_date_index_sanitized": "1",
|
||||||
|
"upload_date_standardized": "2021-01-12",
|
||||||
|
"upload_day": 12,
|
||||||
|
"upload_day_of_year": 12,
|
||||||
|
"upload_day_of_year_padded": "012",
|
||||||
|
"upload_day_of_year_reversed": 354,
|
||||||
|
"upload_day_of_year_reversed_padded": "354",
|
||||||
|
"upload_day_padded": "12",
|
||||||
|
"upload_day_reversed": 20,
|
||||||
|
"upload_day_reversed_padded": "20",
|
||||||
|
"upload_month": 1,
|
||||||
|
"upload_month_padded": "01",
|
||||||
|
"upload_month_reversed": 12,
|
||||||
|
"upload_month_reversed_padded": "12",
|
||||||
|
"upload_year": 2021,
|
||||||
|
"upload_year_truncated": 21,
|
||||||
|
"upload_year_truncated_reversed": 79,
|
||||||
|
"uploader": "abc123",
|
||||||
|
"uploader_id": "abc123",
|
||||||
|
"uploader_url": "https://yourname.here",
|
||||||
|
"webpage_url": "https://yourname.here",
|
||||||
|
"ytdl_sub_input_url": "https://yourname.here",
|
||||||
|
"ytdl_sub_input_url_sanitized": "https:⧸⧸yourname.here",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -178,7 +194,4 @@ def mock_entry_kwargs(
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_entry(mock_entry_kwargs):
|
def mock_entry(mock_entry_kwargs):
|
||||||
return Entry(entry_dict=mock_entry_kwargs, working_directory=".").initialize_script(
|
return Entry(entry_dict=mock_entry_kwargs, working_directory=".")
|
||||||
override_variables={},
|
|
||||||
unresolvable=set(),
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,39 @@
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from ytdl_sub.config.overrides import Overrides
|
||||||
from ytdl_sub.entries.entry import Entry
|
from ytdl_sub.entries.entry import Entry
|
||||||
from ytdl_sub.entries.script.variable_definitions import VARIABLES as v
|
from ytdl_sub.entries.script.variable_definitions import VARIABLES as v
|
||||||
|
from ytdl_sub.entries.script.variable_scripts import ENTRY_INJECTED_VARIABLES
|
||||||
|
from ytdl_sub.entries.script.variable_scripts import ENTRY_RELATIVE_VARIABLES
|
||||||
|
from ytdl_sub.entries.script.variable_scripts import UNRESOLVED_VARIABLES
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_overrides_factory() -> Callable[[Entry], Overrides]:
|
||||||
|
def _mock_overrides_factory(entry: Entry) -> Overrides:
|
||||||
|
overrides = Overrides(name="test", value={})
|
||||||
|
overrides.initialize_script(unresolved_variables=set())
|
||||||
|
|
||||||
|
overrides.add(
|
||||||
|
{
|
||||||
|
var.variable_name: format_string
|
||||||
|
for var, format_string in (
|
||||||
|
list(ENTRY_INJECTED_VARIABLES.items()) + list(ENTRY_RELATIVE_VARIABLES.items())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
overrides.add_entry_kwargs(entry._kwargs)
|
||||||
|
return overrides
|
||||||
|
|
||||||
|
return _mock_overrides_factory
|
||||||
|
|
||||||
|
|
||||||
class TestEntry(object):
|
class TestEntry(object):
|
||||||
def test_entry_to_dict(self, mock_entry, mock_entry_to_dict):
|
def test_entry_to_dict(self, mock_overrides_factory, mock_entry, mock_entry_to_dict):
|
||||||
out = mock_entry.to_dict()
|
out = mock_overrides_factory(mock_entry).to_dict()
|
||||||
del out["entry_metadata"]
|
|
||||||
assert out == mock_entry_to_dict
|
assert out == mock_entry_to_dict
|
||||||
|
|
||||||
def test_entry_missing_kwarg(self, mock_entry):
|
def test_entry_missing_kwarg(self, mock_entry):
|
||||||
|
|
@ -27,6 +53,7 @@ class TestEntry(object):
|
||||||
)
|
)
|
||||||
def test_entry_reverse_variables(
|
def test_entry_reverse_variables(
|
||||||
self,
|
self,
|
||||||
|
mock_overrides_factory,
|
||||||
mock_entry_kwargs,
|
mock_entry_kwargs,
|
||||||
upload_date,
|
upload_date,
|
||||||
year_rev,
|
year_rev,
|
||||||
|
|
@ -35,16 +62,16 @@ class TestEntry(object):
|
||||||
month_rev_pad,
|
month_rev_pad,
|
||||||
day_rev_pad,
|
day_rev_pad,
|
||||||
):
|
):
|
||||||
|
|
||||||
mock_entry_kwargs["upload_date"] = upload_date
|
mock_entry_kwargs["upload_date"] = upload_date
|
||||||
entry = Entry(entry_dict=mock_entry_kwargs, working_directory=".").initialize_script(
|
overrides = mock_overrides_factory(
|
||||||
override_variables={}, unresolvable=set()
|
Entry(entry_dict=mock_entry_kwargs, working_directory=".")
|
||||||
)
|
)
|
||||||
assert entry.get_int(v.upload_year_truncated_reversed) == year_rev
|
|
||||||
assert entry.get_int(v.upload_month_reversed) == month_rev
|
assert overrides.get_int(v.upload_year_truncated_reversed) == year_rev
|
||||||
assert entry.get_int(v.upload_day_reversed) == day_rev
|
assert overrides.get_int(v.upload_month_reversed) == month_rev
|
||||||
assert entry.get_str(v.upload_month_reversed_padded) == month_rev_pad
|
assert overrides.get_int(v.upload_day_reversed) == day_rev
|
||||||
assert entry.get_str(v.upload_day_reversed_padded) == day_rev_pad
|
assert overrides.get_str(v.upload_month_reversed_padded) == month_rev_pad
|
||||||
|
assert overrides.get_str(v.upload_day_reversed_padded) == day_rev_pad
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"upload_date, day_year, day_year_rev, day_year_pad, day_year_rev_pad",
|
"upload_date, day_year, day_year_rev, day_year_pad, day_year_rev_pad",
|
||||||
|
|
@ -54,14 +81,21 @@ class TestEntry(object):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_entry_upload_day_of_year_variables(
|
def test_entry_upload_day_of_year_variables(
|
||||||
self, mock_entry_kwargs, upload_date, day_year, day_year_rev, day_year_pad, day_year_rev_pad
|
self,
|
||||||
|
mock_overrides_factory,
|
||||||
|
mock_entry_kwargs,
|
||||||
|
upload_date,
|
||||||
|
day_year,
|
||||||
|
day_year_rev,
|
||||||
|
day_year_pad,
|
||||||
|
day_year_rev_pad,
|
||||||
):
|
):
|
||||||
mock_entry_kwargs["upload_date"] = upload_date
|
mock_entry_kwargs["upload_date"] = upload_date
|
||||||
entry = Entry(entry_dict=mock_entry_kwargs, working_directory=".").initialize_script(
|
overrides = mock_overrides_factory(
|
||||||
override_variables={}, unresolvable=set()
|
Entry(entry_dict=mock_entry_kwargs, working_directory=".")
|
||||||
)
|
)
|
||||||
|
|
||||||
assert entry.get_int(v.upload_day_of_year) == day_year
|
assert overrides.get_int(v.upload_day_of_year) == day_year
|
||||||
assert entry.get_int(v.upload_day_of_year_reversed) == day_year_rev
|
assert overrides.get_int(v.upload_day_of_year_reversed) == day_year_rev
|
||||||
assert entry.get_str(v.upload_day_of_year_padded) == day_year_pad
|
assert overrides.get_str(v.upload_day_of_year_padded) == day_year_pad
|
||||||
assert entry.get_str(v.upload_day_of_year_reversed_padded) == day_year_rev_pad
|
assert overrides.get_str(v.upload_day_of_year_reversed_padded) == day_year_rev_pad
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue