Revert "no more entry script, maybe bad idea???"

This reverts commit 4f08b5300e932392ee0a6252a112202c018831ad.
This commit is contained in:
Jesse Bannon 2023-12-10 09:15:18 -08:00
parent 8cd6ed12e0
commit 148e73fdde
16 changed files with 292 additions and 285 deletions

View file

@ -1,29 +1,22 @@
import copy
from typing import Any
from typing import Dict
from typing import Optional
from typing import Set
from typing import Type
from typing import TypeVar
from typing import final
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 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.script.parser import parse
from ytdl_sub.script.script import Script
from ytdl_sub.utils.script import ScriptUtils
from ytdl_sub.utils.scriptable import Scriptable
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
TType = TypeVar("TType")
class Overrides(DictFormatterValidator):
class Overrides(DictFormatterValidator, Scriptable):
"""
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
@ -69,7 +62,8 @@ class Overrides(DictFormatterValidator):
)
def __init__(self, name, value):
super().__init__(name, value)
DictFormatterValidator.__init__(self, name, value)
Scriptable.__init__(self)
# Add sanitized overrides
for key in self._keys:
@ -87,53 +81,14 @@ class Overrides(DictFormatterValidator):
sanitize=sanitized,
)
self.script = Script(copy.deepcopy(VARIABLE_SCRIPTS))
self.unresolvable: Set[str] = copy.deepcopy(UNRESOLVED_VARIABLES)
self.unresolvable.add(VARIABLES.entry_metadata.variable_name)
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()
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
def subscription_name(self) -> str:
"""
@ -143,18 +98,10 @@ class Overrides(DictFormatterValidator):
"""
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(
self,
formatter: StringFormatterValidator,
entry: Optional[Entry] = None,
function_overrides: Dict[str, str] = None,
) -> str:
"""
@ -171,15 +118,17 @@ class Overrides(DictFormatterValidator):
-------
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(
str(
self.script.resolve_once(
script.resolve_once(
dict({"tmp_var": formatter.format_string}, **(function_overrides or {})),
unresolvable=self.unresolvable.union(
VARIABLES.entry_metadata.variable_name
if isinstance(formatter, OverridesStringFormatterValidator)
else set()
),
unresolvable=unresolvable,
)["tmp_var"]
)
)

View file

@ -361,7 +361,12 @@ class Preset(_PresetShell):
# values from multiple validators
self.__recursive_preset_validate()
self.overrides.initialize_script(unresolved_variables=set(list(self._added_variables.keys())))
self.overrides.initialize_script(
unresolved_variables={
var_name: f"{{%throw('Plugin variable {var_name} has not been created yet')}}"
for var_name in self._added_variables
}
)
@property
def name(self) -> str:

View file

@ -109,17 +109,17 @@ class InfoJsonDownloader(SourcePlugin[InfoJsonDownloaderOptions]):
prior_variables = entry.kwargs(YTDL_SUB_ENTRY_VARIABLES_KWARG_KEY)
del entry._kwargs[YTDL_SUB_ENTRY_VARIABLES_KWARG_KEY]
self.overrides.add(
dict(
entry._kwargs,
**{
inj.variable_name: prior_variables.get(
inj.variable_name,
VARIABLE_SCRIPTS[inj.variable_name],
)
for inj in ENTRY_INJECTED_VARIABLES
},
)
entry.initialize_script(
override_variables=self.overrides.dict_with_format_strings,
unresolvable=self.overrides.unresolvable,
).add(
{
inj.variable_name: prior_variables.get(
inj.variable_name,
VARIABLE_SCRIPTS[inj.variable_name],
)
for inj in ENTRY_INJECTED_VARIABLES
}
)
entries.append(entry)

View file

@ -71,7 +71,7 @@ class UrlDownloaderThumbnailPlugin(SourcePluginExtension):
Downloads and moves channel avatar and banner images to the output directory.
"""
for thumbnail_info in thumbnail_list_info.list:
thumbnail_name = self.overrides.apply_formatter(thumbnail_info.name)
thumbnail_name = self.overrides.apply_formatter(thumbnail_info.name, entry=entry)
thumbnail_id = self.overrides.apply_formatter(thumbnail_info.uid)
# 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.
# Try to use source_webpage_url if it does not exist
entry_collection_url = self.overrides.get_str(v.ytdl_sub_input_url)
entry_collection_url = entry.get_str(v.ytdl_sub_input_url)
# If the collection URL cannot find its mapping, use the last URL
collection_url = (
@ -459,16 +459,15 @@ class MultiUrlDownloader(SourcePlugin[MultiUrlValidator]):
for entry in self._iterate_entries(
url_validator=collection_url, parents=parents, orphans=orphan_entries
):
self.overrides.add(
dict(
entry._kwargs,
**{
v.ytdl_sub_input_url.variable_name: self.overrides.apply_formatter(
collection_url.url
)
},
)
entry.initialize_script(
override_variables=self.overrides.dict_with_format_strings,
unresolvable=self.overrides.unresolvable,
).add(
{
v.ytdl_sub_input_url.variable_name: self.overrides.apply_formatter(
collection_url.url
)
}
)
yield entry

View file

@ -2,13 +2,17 @@ import copy
import json
import os
from pathlib import Path
from typing import Dict
from typing import Optional
from typing import Set
from typing import Type
from typing import TypeVar
from typing import final
from ytdl_sub.config.overrides import Overrides
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 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 VIDEO_CODEC_EXTS
@ -17,11 +21,49 @@ YTDL_SUB_ENTRY_VARIABLES_KWARG_KEY: str = "ytdl_sub_entry_variables"
TType = TypeVar("TType")
class Entry(BaseEntry):
class Entry(BaseEntry, Scriptable):
"""
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
def ext(self) -> str:
"""
@ -29,7 +71,7 @@ class Entry(BaseEntry):
This is not reflected in the entry. See if the mkv file exists and return "mkv" if so,
otherwise, return the original extension.
"""
ext = self.kwargs(VARIABLES.ext.metadata_key)
ext = self.get_str(VARIABLES.ext)
for possible_ext in [ext, "mkv"]:
file_path = str(Path(self.working_directory()) / f"{self.uid}.{possible_ext}")
if os.path.isfile(file_path):
@ -55,7 +97,7 @@ class Entry(BaseEntry):
-------
The download thumbnail's file name
"""
return f"{self.kwargs(VARIABLES.uid.metadata_key)}.jpg"
return f"{self.get_str(VARIABLES.uid)}.{self.get_str(VARIABLES.thumbnail_ext)}"
def get_download_thumbnail_path(self) -> str:
"""Returns the entry's thumbnail's file path to where it was downloaded"""
@ -79,12 +121,12 @@ class Entry(BaseEntry):
return None
def write_info_json(self, overrides: Overrides) -> None:
def write_info_json(self) -> None:
"""
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["ytdl_sub_entry_variables"] = overrides.to_dict()
kwargs_dict["ytdl_sub_entry_variables"] = self.to_dict()
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:
@ -126,3 +168,12 @@ class Entry(BaseEntry):
break
return file_exists
@final
def to_dict(self) -> Dict[str, str]:
"""
Returns
-------
Dictionary containing all variables
"""
return self.script.resolve().as_native()

View file

@ -118,7 +118,7 @@ ENTRY_HARDCODED_VARIABLES: Dict[Variable, str] = {
ENTRY_RELATIVE_VARIABLES: Dict[MetadataVariable, str] = {
v.playlist_metadata: entry_get(v.playlist_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] = {
@ -279,11 +279,6 @@ mergedeep.merge(
VARIABLE_SCRIPTS: Dict[str, str] = {
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()

View file

@ -58,7 +58,7 @@ class ViewPlugin(Plugin[ViewOptions]):
"""
Adds all source variables to the entry
"""
source_var_dict = self.overrides.to_dict()
source_var_dict = copy.deepcopy(entry.to_dict())
for key in source_var_dict.keys():
source_var_dict[key] = self._truncate_value(source_var_dict[key])

View file

@ -157,7 +157,7 @@ class MusicTagsPlugin(Plugin[MusicTagsOptions]):
tags_to_write: Dict[str, List[str]] = defaultdict(list)
for tag_name, tag_formatters in self.plugin_options.tags.as_lists.items():
for tag_formatter in tag_formatters:
tag_value = self.overrides.apply_formatter(formatter=tag_formatter)
tag_value = self.overrides.apply_formatter(formatter=tag_formatter, entry=entry)
tags_to_write[tag_name].append(tag_value)
# write the actual tags if its not a dry run

View file

@ -99,7 +99,7 @@ class SharedNfoTagsPlugin(Plugin[SharedNfoTagsOptions], ABC):
for key, string_tags in self.plugin_options.tags.string_tags.items():
tags = [
XmlElement(
text=self.overrides.apply_formatter(formatter=string_tag),
text=self.overrides.apply_formatter(formatter=string_tag, entry=entry),
attributes={},
)
for string_tag in string_tags
@ -110,9 +110,11 @@ class SharedNfoTagsPlugin(Plugin[SharedNfoTagsOptions], ABC):
for key, attribute_tags in self.plugin_options.tags.attribute_tags.items():
tags = [
XmlElement(
text=self.overrides.apply_formatter(formatter=attribute_tag.tag),
text=self.overrides.apply_formatter(formatter=attribute_tag.tag, entry=entry),
attributes={
attr_name: self.overrides.apply_formatter(formatter=attr_formatter)
attr_name: self.overrides.apply_formatter(
formatter=attr_formatter, entry=entry
)
for attr_name, attr_formatter in attribute_tag.attributes.dict.items()
},
)
@ -126,7 +128,9 @@ class SharedNfoTagsPlugin(Plugin[SharedNfoTagsOptions], ABC):
def _create_nfo(self, entry: Entry, save_to_entry: bool = True) -> None:
# Write the nfo tags to XML with the nfo_root
nfo_root = self.overrides.apply_formatter(formatter=self.plugin_options.nfo_root)
nfo_root = self.overrides.apply_formatter(
formatter=self.plugin_options.nfo_root, entry=entry
)
nfo_tags = self._get_xml_element_dict(entry=entry)
# If the nfo tags are empty, then stop continuing
@ -148,7 +152,9 @@ class SharedNfoTagsPlugin(Plugin[SharedNfoTagsOptions], ABC):
xml = to_xml(nfo_dict=nfo_tags, nfo_root=nfo_root)
nfo_file_name = self.overrides.apply_formatter(formatter=self.plugin_options.nfo_name)
nfo_file_name = self.overrides.apply_formatter(
formatter=self.plugin_options.nfo_name, entry=entry
)
# Save the nfo's XML to file
nfo_file_path = Path(self.working_directory) / nfo_file_name

View file

@ -288,16 +288,34 @@ class RegexPlugin(Plugin[RegexOptions]):
# Otherwise, error
raise RegexNoMatchException(f"Regex failed to match '{variable_name}' from '{entry.title}'")
def _can_process_at_metadata_stage(self, variable_name: str) -> bool:
# Try to see if it can resolve
try:
self.overrides.apply_formatter(formatter=self.overrides.dict[variable_name])
# If it can not from missing variables (from post-metadata stage), return False
except StringFormattingVariableNotFoundException:
def _can_process_at_metadata_stage(self, entry: Entry, variable_name: str) -> bool:
# If the variable is an override...
if variable_name in self.overrides.dict:
# Try to see if it can resolve
try:
self.overrides.apply_formatter(
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 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]:
"""
Parameters
@ -330,13 +348,16 @@ class RegexPlugin(Plugin[RegexOptions]):
# 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(
variable_name=variable_name
entry=entry, variable_name=variable_name
):
continue
self._add_processed_regex_variable_name(entry, variable_name)
regex_input_str = self.overrides.get_str(variable_name)
regex_input_str = self._get_regex_input_string(
entry=entry,
variable_name=variable_name,
)
if (
regex_options.exclude is not None
@ -354,22 +375,49 @@ class RegexPlugin(Plugin[RegexOptions]):
if not regex_options.has_defaults:
return self._try_skip_entry(entry=entry, variable_name=variable_name)
# otherwise, use defaults
self.overrides.add(
{
regex_options.capture_group_names[i]: default
for i, default in enumerate(regex_options.capture_group_defaults)
}
# otherwise, use defaults (apply them using the original entry source dict)
source_variables_and_overrides_dict = dict(
entry.to_dict(), **self.overrides.dict_with_format_strings
)
# 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
# {source_var}_capture_1, {source_var}_capture_2, ...
else:
self.overrides.add(
{
# Add the value...
entry.add_variables(
variables_to_add={
regex_options.capture_group_names[i]: 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

View file

@ -196,6 +196,7 @@ class SubtitlesPlugin(Plugin[SubtitleOptions]):
subtitle_file_name = f"{entry.uid}.{lang}.{self.plugin_options.subtitles_type}"
output_subtitle_file_name = self.overrides.apply_formatter(
formatter=self.plugin_options.subtitles_name,
entry=entry,
function_overrides={"lang": lang},
)

View file

@ -76,7 +76,7 @@ class VideoTagsPlugin(Plugin[VideoTagsOptions]):
tags_to_write: Dict[str, str] = {}
for tag_name, tag_formatter in self.plugin_options.tags.dict.items():
tag_value = self.overrides.apply_formatter(formatter=tag_formatter)
tag_value = self.overrides.apply_formatter(formatter=tag_formatter, entry=entry)
tags_to_write[tag_name] = tag_value
# write the actual tags if its not a dry run

View file

@ -62,7 +62,9 @@ class SubscriptionDownload(BaseSubscription, ABC):
Optional. Metadata to record to the transaction log for this entry
"""
# Move the file after all direct file modifications are complete
output_file_name = self.overrides.apply_formatter(formatter=self.output_options.file_name)
output_file_name = self.overrides.apply_formatter(
formatter=self.output_options.file_name, entry=entry
)
self._enhanced_download_archive.save_file_to_output_directory(
file_name=entry.get_download_file_name(),
file_metadata=entry_metadata,
@ -73,7 +75,7 @@ class SubscriptionDownload(BaseSubscription, ABC):
# Always pretend to include the thumbnail in a dry-run
if self.output_options.thumbnail_name and (dry_run or entry.is_thumbnail_downloaded()):
output_thumbnail_name = self.overrides.apply_formatter(
formatter=self.output_options.thumbnail_name
formatter=self.output_options.thumbnail_name, entry=entry
)
# Copy the thumbnails since they could be used later for other things
@ -90,7 +92,7 @@ class SubscriptionDownload(BaseSubscription, ABC):
if self.output_options.info_json_name:
output_info_json_name = self.overrides.apply_formatter(
formatter=self.output_options.info_json_name
formatter=self.output_options.info_json_name, entry=entry
)
# if not dry-run, write the info json

View file

@ -36,8 +36,6 @@ class ScriptUtils:
out = f"{{%float({value})}}"
elif isinstance(value, bool):
out = f"{{%bool({value})}}"
elif isinstance(value, dict):
out = f"{{ {json.dumps(value)} }}"
else:
out = json.dumps(value)

View file

@ -50,128 +50,112 @@ def download_file_name(uid, ext):
@pytest.fixture
def mock_entry_to_dict():
def mock_entry_to_dict(
uid,
title,
ext,
extractor,
upload_date,
thumbnail_ext,
webpage_url,
):
return {
"channel": "abc123",
"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",
},
"uid": uid,
"uid_sanitized": uid,
"epoch": 1596878400,
"epoch_date": "20200808",
"epoch_hour": "09",
"ext": "mp5",
"extractor": "xtract",
"title": "entry title",
"title_sanitized": "entry title",
"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",
"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",
"playlist_count": 1,
"playlist_description": "",
"webpage_url": webpage_url,
"playlist_index": 1,
"playlist_index_padded": "01",
"playlist_index_padded6": "000001",
"playlist_index_reversed": 1,
"playlist_index_reversed_padded": "01",
"playlist_index_reversed_padded6": "000001",
"playlist_metadata": {},
"playlist_metadata_sanitized": "{}",
"playlist_count": 1,
"playlist_max_upload_year": 2021,
"playlist_max_upload_year_truncated": 21,
"playlist_title": "entry title",
"playlist_title_sanitized": "entry title",
"playlist_description": "",
"playlist_webpage_url": "https://yourname.here",
"playlist_uid": "abc123",
"playlist_uploader": "abc123",
"playlist_uploader_id": "abc123",
"playlist_uploader_sanitized": "abc123",
"playlist_uploader_id": "abc123",
"playlist_uploader_url": "https://yourname.here",
"playlist_webpage_url": "https://yourname.here",
"release_date": "20210112",
"release_date_standardized": "2021-01-12",
"release_day": 12,
"release_day_of_year": 12,
"release_day_of_year_padded": "012",
"release_day_of_year_reversed": 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_webpage_url": "https://yourname.here",
"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": "abc",
"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": "httpsyourname.here",
"title_sanitized_plex": "entry title",
"release_date": upload_date,
"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_padded": "12",
"release_day_reversed": 20,
"release_day_reversed_padded": "20",
"release_day_of_year": 12,
"release_day_of_year_padded": "012",
"release_day_of_year_reversed": 354,
"release_day_of_year_reversed_padded": "354",
}
@ -194,4 +178,7 @@ def mock_entry_kwargs(
@pytest.fixture
def mock_entry(mock_entry_kwargs):
return Entry(entry_dict=mock_entry_kwargs, working_directory=".")
return Entry(entry_dict=mock_entry_kwargs, working_directory=".").initialize_script(
override_variables={},
unresolvable=set(),
)

View file

@ -1,39 +1,13 @@
from typing import Callable
import pytest
from ytdl_sub.config.overrides import Overrides
from ytdl_sub.entries.entry import Entry
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):
def test_entry_to_dict(self, mock_overrides_factory, mock_entry, mock_entry_to_dict):
out = mock_overrides_factory(mock_entry).to_dict()
def test_entry_to_dict(self, mock_entry, mock_entry_to_dict):
out = mock_entry.to_dict()
del out["entry_metadata"]
assert out == mock_entry_to_dict
def test_entry_missing_kwarg(self, mock_entry):
@ -53,7 +27,6 @@ class TestEntry(object):
)
def test_entry_reverse_variables(
self,
mock_overrides_factory,
mock_entry_kwargs,
upload_date,
year_rev,
@ -62,16 +35,16 @@ class TestEntry(object):
month_rev_pad,
day_rev_pad,
):
mock_entry_kwargs["upload_date"] = upload_date
overrides = mock_overrides_factory(
Entry(entry_dict=mock_entry_kwargs, working_directory=".")
)
assert overrides.get_int(v.upload_year_truncated_reversed) == year_rev
assert overrides.get_int(v.upload_month_reversed) == month_rev
assert overrides.get_int(v.upload_day_reversed) == day_rev
assert overrides.get_str(v.upload_month_reversed_padded) == month_rev_pad
assert overrides.get_str(v.upload_day_reversed_padded) == day_rev_pad
mock_entry_kwargs["upload_date"] = upload_date
entry = Entry(entry_dict=mock_entry_kwargs, working_directory=".").initialize_script(
override_variables={}, unresolvable=set()
)
assert entry.get_int(v.upload_year_truncated_reversed) == year_rev
assert entry.get_int(v.upload_month_reversed) == month_rev
assert entry.get_int(v.upload_day_reversed) == day_rev
assert entry.get_str(v.upload_month_reversed_padded) == month_rev_pad
assert entry.get_str(v.upload_day_reversed_padded) == day_rev_pad
@pytest.mark.parametrize(
"upload_date, day_year, day_year_rev, day_year_pad, day_year_rev_pad",
@ -81,21 +54,14 @@ class TestEntry(object):
],
)
def test_entry_upload_day_of_year_variables(
self,
mock_overrides_factory,
mock_entry_kwargs,
upload_date,
day_year,
day_year_rev,
day_year_pad,
day_year_rev_pad,
self, mock_entry_kwargs, upload_date, day_year, day_year_rev, day_year_pad, day_year_rev_pad
):
mock_entry_kwargs["upload_date"] = upload_date
overrides = mock_overrides_factory(
Entry(entry_dict=mock_entry_kwargs, working_directory=".")
entry = Entry(entry_dict=mock_entry_kwargs, working_directory=".").initialize_script(
override_variables={}, unresolvable=set()
)
assert overrides.get_int(v.upload_day_of_year) == day_year
assert overrides.get_int(v.upload_day_of_year_reversed) == day_year_rev
assert overrides.get_str(v.upload_day_of_year_padded) == day_year_pad
assert overrides.get_str(v.upload_day_of_year_reversed_padded) == day_year_rev_pad
assert entry.get_int(v.upload_day_of_year) == day_year
assert entry.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 entry.get_str(v.upload_day_of_year_reversed_padded) == day_year_rev_pad