From 56bae3f39af32306b5e520fce5c7a24b6babb1a8 Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Tue, 6 Sep 2022 16:59:58 -0700 Subject: [PATCH] multi tag value for NFOs WIP --- src/ytdl_sub/plugins/nfo_tags.py | 61 ++++++++++++------- src/ytdl_sub/validators/nfo_validators.py | 41 ++++++++----- .../validators/string_formatter_validators.py | 1 + tests/e2e/plugins/test_nfo_tags.py | 23 +++++++ 4 files changed, 91 insertions(+), 35 deletions(-) diff --git a/src/ytdl_sub/plugins/nfo_tags.py b/src/ytdl_sub/plugins/nfo_tags.py index 86a100bf..c7996f0e 100644 --- a/src/ytdl_sub/plugins/nfo_tags.py +++ b/src/ytdl_sub/plugins/nfo_tags.py @@ -3,6 +3,7 @@ from abc import ABC from pathlib import Path from typing import Dict from typing import Generic +from typing import List from typing import Optional from typing import Type from typing import TypeVar @@ -95,23 +96,31 @@ class SharedNfoTagsPlugin( Shared code between NFO tags and Ouptut Directory NFO Tags """ - def _get_xml_element_dict(self, entry: Optional[Entry]) -> Dict[str, XmlElement]: - nfo_tags: Dict[str, XmlElement] = {} + def _get_xml_element_dict(self, entry: Optional[Entry]) -> Dict[str, List[XmlElement]]: + nfo_tags: Dict[str, List[XmlElement]] = {} - for key, string_tag in self.plugin_options.tags.string_tags.items(): - nfo_tags[key] = XmlElement( - text=self.overrides.apply_formatter(formatter=string_tag, entry=entry), - attributes={}, - ) + for key, string_tags in self.plugin_options.tags.string_tags.items(): + nfo_tags[key] = [ + XmlElement( + text=self.overrides.apply_formatter(formatter=string_tag, entry=entry), + attributes={}, + ) + for string_tag in string_tags + ] - for key, attribute_tag in self.plugin_options.tags.attribute_tags.items(): - nfo_tags[key] = XmlElement( - text=self.overrides.apply_formatter(formatter=attribute_tag.tag, entry=entry), - attributes={ - attr_name: self.overrides.apply_formatter(formatter=attr_formatter, entry=entry) - for attr_name, attr_formatter in attribute_tag.attributes.dict.items() - }, - ) + for key, attribute_tags in self.plugin_options.tags.attribute_tags.items(): + nfo_tags[key] = [ + XmlElement( + text=self.overrides.apply_formatter(formatter=attribute_tag.tag, entry=entry), + attributes={ + attr_name: self.overrides.apply_formatter( + formatter=attr_formatter, entry=entry + ) + for attr_name, attr_formatter in attribute_tag.attributes.dict.items() + }, + ) + for attribute_tag in attribute_tags + ] return nfo_tags @@ -125,11 +134,14 @@ class SharedNfoTagsPlugin( if self.plugin_options.kodi_safe: nfo_root = to_max_3_byte_utf8_string(nfo_root) nfo_tags = { - to_max_3_byte_utf8_string(key): XmlElement( - text=to_max_3_byte_utf8_string(xml_elem.text), - attributes=to_max_3_byte_utf8_dict(xml_elem.attributes), - ) - for key, xml_elem in nfo_tags.items() + to_max_3_byte_utf8_string(key): [ + XmlElement( + text=to_max_3_byte_utf8_string(xml_elem.text), + attributes=to_max_3_byte_utf8_dict(xml_elem.attributes), + ) + for xml_elem in xml_elems + ] + for key, xml_elems in nfo_tags.items() } xml = to_xml(nfo_dict=nfo_tags, nfo_root=nfo_root) @@ -147,7 +159,14 @@ class SharedNfoTagsPlugin( # Save the nfo file and log its metadata nfo_metadata = FileMetadata.from_dict( value_dict={ - nfo_root: {key: xml_elem.to_dict_value() for key, xml_elem in nfo_tags.items()} + nfo_root: { + key: ( + xml_elems[0].to_dict_value() + if len(xml_elems) == 1 + else [xml_elem.to_dict_value() for xml_elem in xml_elems] + ) + for key, xml_elems in nfo_tags.items() + } }, title="NFO tags:", ) diff --git a/src/ytdl_sub/validators/nfo_validators.py b/src/ytdl_sub/validators/nfo_validators.py index 36cb360e..850753b9 100644 --- a/src/ytdl_sub/validators/nfo_validators.py +++ b/src/ytdl_sub/validators/nfo_validators.py @@ -1,6 +1,8 @@ from abc import ABC +from collections import defaultdict from typing import Dict from typing import Generic +from typing import List from typing import Type from typing import TypeVar @@ -80,23 +82,34 @@ class SharedNfoTagsValidator( def __init__(self, name, value): super().__init__(name, value) - self._string_tags: Dict[str, StringFormatterValidator] = {} - self._attribute_tags: Dict[str, TNfoTagsWithAttributesValidator] = {} + self._string_tags: Dict[str, List[StringFormatterValidator]] = defaultdict(list) + self._attribute_tags: Dict[str, List[TNfoTagsWithAttributesValidator]] = defaultdict(list) for key, tag_value in self._dict.items(): - if isinstance(tag_value, str): - self._string_tags[key] = self._validate_key( - key=key, validator=self._tags_with_attributes_validator.formatter_validator - ) - elif isinstance(tag_value, dict): - self._attribute_tags[key] = self._validate_key( - key=key, validator=self._tags_with_attributes_validator - ) - else: - raise self._validation_exception("must either be a string or attributes object") + # Turn each value into a list if it's not + if not isinstance(tag_value, list): + tag_value = [tag_value] + + # iterate each list, validate accordingly if it is a string tag or attribute tag + for tag_value_i in tag_value: + if isinstance(tag_value_i, str): + self._string_tags[key].append( + self._validate_key( + key=key, + validator=self._tags_with_attributes_validator.formatter_validator, + ) + ) + elif isinstance(tag_value_i, dict): + self._attribute_tags[key].append( + self._validate_key(key=key, validator=self._tags_with_attributes_validator) + ) + else: + raise self._validation_exception( + "must either be a single or list of string/attribute object" + ) @property - def string_tags(self) -> Dict[str, StringFormatterValidator]: + def string_tags(self) -> Dict[str, List[StringFormatterValidator]]: """ Returns ------- @@ -105,7 +118,7 @@ class SharedNfoTagsValidator( return self._string_tags @property - def attribute_tags(self) -> Dict[str, TNfoTagsWithAttributesValidator]: + def attribute_tags(self) -> Dict[str, List[TNfoTagsWithAttributesValidator]]: """ Returns ------- diff --git a/src/ytdl_sub/validators/string_formatter_validators.py b/src/ytdl_sub/validators/string_formatter_validators.py index 97c61a84..65fc07ec 100644 --- a/src/ytdl_sub/validators/string_formatter_validators.py +++ b/src/ytdl_sub/validators/string_formatter_validators.py @@ -241,3 +241,4 @@ class OverridesDictFormatterValidator(DictFormatterValidator): """ _key_validator = OverridesStringFormatterValidator + diff --git a/tests/e2e/plugins/test_nfo_tags.py b/tests/e2e/plugins/test_nfo_tags.py index 11363a09..cf3621e4 100644 --- a/tests/e2e/plugins/test_nfo_tags.py +++ b/tests/e2e/plugins/test_nfo_tags.py @@ -23,6 +23,17 @@ def subscription_dict(output_directory): "attributes": {"🎸?": "value\nnewlines 🎸"}, "tag": "the \n tag 🎸🎸", }, + "kodi_safe_multi_title 🎸": ["value 1 🎸", "value 2 🎸"], + "kodi_safe_multi_title_with_attrs": [ + { + "attributes": {"🎸?": "value\nnewlines 🎸"}, + "tag": "the \n tag 1 🎸🎸", + }, + { + "attributes": {"🎸?": "value\nnewlines 🎸"}, + "tag": "the \n tag 2 🎸🎸", + }, + ] }, }, "output_directory_nfo_tags": { @@ -34,6 +45,17 @@ def subscription_dict(output_directory): "attributes": {"🎸?": "value\nnewlines 🎸"}, "tag": "the \n tag 🎸🎸", }, + "kodi_safe_multi_title 🎸": ["value 1 🎸", "value 2 🎸"], + "kodi_safe_multi_title_with_attrs": [ + { + "attributes": {"🎸?": "value\nnewlines 🎸"}, + "tag": "the \n tag 1 🎸🎸", + }, + { + "attributes": {"🎸?": "value\nnewlines 🎸"}, + "tag": "the \n tag 2 🎸🎸", + }, + ] }, }, } @@ -60,6 +82,7 @@ class TestNfoTagsPlugins: output_directory=output_directory, transaction_log=transaction_log, transaction_log_summary_file_name=f"plugins/nfo_tags/{transaction_log_file_name}", + regenerate_transaction_log=True ) def test_source_variable_in_output_directory_nfo_tags_errors(