use yaml for print

This commit is contained in:
Jesse Bannon 2022-09-07 10:51:25 -07:00
parent 56bae3f39a
commit 68f2e768e7
13 changed files with 213 additions and 104 deletions

View file

@ -122,7 +122,7 @@ class YoutubeMergePlaylistDownloader(
file_duration_sec=merged_video.kwargs("duration"), file_duration_sec=merged_video.kwargs("duration"),
) )
return chapters.to_file_metadata(title="Timestamps of playlist videos in the merged file:") return chapters.to_file_metadata(title="Timestamps of playlist videos in the merged file")
def _to_merged_video(self, entry_dict: Dict) -> YoutubeVideo: def _to_merged_video(self, entry_dict: Dict) -> YoutubeVideo:
""" """

View file

@ -334,7 +334,7 @@ class ChaptersPlugin(Plugin[ChaptersOptions]):
chapters = Chapters.from_timestamps_file( chapters = Chapters.from_timestamps_file(
chapters_file_path=self.plugin_options.embed_chapter_timestamps chapters_file_path=self.plugin_options.embed_chapter_timestamps
) )
return chapters.to_file_metadata(title="Chapters embedded from timestamp file:") return chapters.to_file_metadata(title="Chapters embedded from timestamp file")
if self.plugin_options.embed_chapters: if self.plugin_options.embed_chapters:
metadata_dict = {} metadata_dict = {}

View file

@ -84,4 +84,4 @@ class MusicTagsPlugin(Plugin[MusicTagsOptions]):
audio_file.save() audio_file.save()
# report the tags written # report the tags written
return FileMetadata.from_dict(value_dict=tags_to_write, title="Music Tags:") return FileMetadata.from_dict(value_dict=tags_to_write, title="Music Tags")

View file

@ -1,5 +1,6 @@
import os import os
from abc import ABC from abc import ABC
from collections import defaultdict
from pathlib import Path from pathlib import Path
from typing import Dict from typing import Dict
from typing import Generic from typing import Generic
@ -97,19 +98,19 @@ class SharedNfoTagsPlugin(
""" """
def _get_xml_element_dict(self, entry: Optional[Entry]) -> Dict[str, List[XmlElement]]: def _get_xml_element_dict(self, entry: Optional[Entry]) -> Dict[str, List[XmlElement]]:
nfo_tags: Dict[str, List[XmlElement]] = {} nfo_tags: Dict[str, List[XmlElement]] = defaultdict(list)
for key, string_tags in self.plugin_options.tags.string_tags.items(): for key, string_tags in self.plugin_options.tags.string_tags.items():
nfo_tags[key] = [ nfo_tags[key].extend(
XmlElement( XmlElement(
text=self.overrides.apply_formatter(formatter=string_tag, entry=entry), text=self.overrides.apply_formatter(formatter=string_tag, entry=entry),
attributes={}, attributes={},
) )
for string_tag in string_tags for string_tag in string_tags
] )
for key, attribute_tags in self.plugin_options.tags.attribute_tags.items(): for key, attribute_tags in self.plugin_options.tags.attribute_tags.items():
nfo_tags[key] = [ nfo_tags[key].extend(
XmlElement( XmlElement(
text=self.overrides.apply_formatter(formatter=attribute_tag.tag, entry=entry), text=self.overrides.apply_formatter(formatter=attribute_tag.tag, entry=entry),
attributes={ attributes={
@ -120,7 +121,7 @@ class SharedNfoTagsPlugin(
}, },
) )
for attribute_tag in attribute_tags for attribute_tag in attribute_tags
] )
return nfo_tags return nfo_tags
@ -168,7 +169,7 @@ class SharedNfoTagsPlugin(
for key, xml_elems in nfo_tags.items() for key, xml_elems in nfo_tags.items()
} }
}, },
title="NFO tags:", title="NFO tags",
) )
self.save_file(file_name=nfo_file_name, file_metadata=nfo_metadata, entry=entry) self.save_file(file_name=nfo_file_name, file_metadata=nfo_metadata, entry=entry)

View file

@ -136,7 +136,7 @@ class SplitByChaptersPlugin(Plugin[SplitByChaptersOptions]):
metadata = FileMetadata.from_dict( metadata = FileMetadata.from_dict(
value_dict=metadata_value_dict, value_dict=metadata_value_dict,
title="From Chapter Split:", title="From Chapter Split",
sort_dict=False, sort_dict=False,
) )

View file

@ -59,4 +59,4 @@ class VideoTagsPlugin(Plugin[VideoTagsOptions]):
) )
# report the tags written # report the tags written
return FileMetadata.from_dict(value_dict=tags_to_write, title="Video Tags:") return FileMetadata.from_dict(value_dict=tags_to_write, title="Video Tags")

View file

@ -1,3 +1,4 @@
import json
import os import os
import shutil import shutil
from pathlib import Path from pathlib import Path
@ -7,6 +8,7 @@ from typing import List
from typing import Optional from typing import Optional
from typing import Set from typing import Set
from typing import Union from typing import Union
import yaml
class FileMetadata: class FileMetadata:
@ -56,32 +58,13 @@ class FileMetadata:
sort_dict sort_dict
Whether to sort dicts in the value_dict. Defaults to true. Whether to sort dicts in the value_dict. Defaults to true.
""" """
lines: List[str] = [] if title:
if title is not None: value_dict = {title: value_dict}
lines.append(title)
def _recursive_add_dict_lines(rdict: Dict, indent: int):
rdict_items = rdict.items()
if sort_dict: if sort_dict:
rdict_items = sorted(rdict_items) value_dict = json.loads(json.dumps(value_dict, sort_keys=True))
for key, value in rdict_items: out = yaml.safe_dump(value_dict, allow_unicode=True, indent=2, default_style='', width=100)
_indent = " " * indent return cls(metadata=out.rstrip().split('\n'))
if isinstance(value, Dict):
lines.append(f"{_indent}{key}:")
_recursive_add_dict_lines(rdict=value, indent=indent + 2)
else:
value = str(value)
# If there are newlines in the value, print them indented
if "\n" in value:
lines.append(f"{_indent}{key}:")
for value_line in value.split("\n"):
lines.append(f" {_indent}{value_line.strip()}")
else:
lines.append(f"{_indent}{key}: {value}")
_recursive_add_dict_lines(rdict=value_dict, indent=2)
return cls(metadata=lines)
class FileHandlerTransactionLog: class FileHandlerTransactionLog:

View file

@ -1,6 +1,6 @@
import xml.etree.ElementTree as et import xml.etree.ElementTree as et
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any from typing import Any, List
from typing import Dict from typing import Dict
from typing import Union from typing import Union
@ -59,7 +59,7 @@ def to_max_3_byte_utf8_dict(string_dict: Dict[str, str]) -> Dict[str, str]:
} }
def to_xml(nfo_dict: Dict[str, XmlElement], nfo_root: str) -> bytes: def to_xml(nfo_dict: Dict[str, List[XmlElement]], nfo_root: str) -> bytes:
""" """
Transforms a dict to XML Transforms a dict to XML
@ -75,7 +75,8 @@ def to_xml(nfo_dict: Dict[str, XmlElement], nfo_root: str) -> bytes:
XML bytes XML bytes
""" """
xml_root = et.Element(nfo_root) xml_root = et.Element(nfo_root)
for key, xml_elem in sorted(nfo_dict.items()): for key, xml_elems in sorted(nfo_dict.items()):
for xml_elem in xml_elems:
sorted_attr = dict(sorted(xml_elem.attributes.items())) sorted_attr = dict(sorted(xml_elem.attributes.items()))
sub_element = et.SubElement(xml_root, key, sorted_attr) sub_element = et.SubElement(xml_root, key, sorted_attr)
sub_element.text = xml_elem.text sub_element.text = xml_elem.text

View file

@ -7,11 +7,13 @@ from typing import Type
from typing import TypeVar from typing import TypeVar
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator, \
ListFormatterValidator, ListOverridesFormatterValidator
from ytdl_sub.validators.string_formatter_validators import OverridesDictFormatterValidator from ytdl_sub.validators.string_formatter_validators import OverridesDictFormatterValidator
from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator 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
from ytdl_sub.validators.validators import DictValidator from ytdl_sub.validators.validators import DictValidator
from ytdl_sub.validators.validators import ListValidator
TStringFormatterValidator = TypeVar("TStringFormatterValidator", bound=StringFormatterValidator) TStringFormatterValidator = TypeVar("TStringFormatterValidator", bound=StringFormatterValidator)
TDictFormatterValidator = TypeVar("TDictFormatterValidator", bound=DictFormatterValidator) TDictFormatterValidator = TypeVar("TDictFormatterValidator", bound=DictFormatterValidator)
@ -55,34 +57,54 @@ class _NfoTagsWithAttributesValidator(
class NfoTagsWithAttributesValidator( class NfoTagsWithAttributesValidator(
_NfoTagsWithAttributesValidator[StringFormatterValidator, DictFormatterValidator] _NfoTagsWithAttributesValidator[StringFormatterValidator, DictFormatterValidator]
): ):
"""TagsWithAttributes for the entry NFO validator"""
formatter_validator = StringFormatterValidator formatter_validator = StringFormatterValidator
dict_formatter_validator = DictFormatterValidator dict_formatter_validator = DictFormatterValidator
class NfoTagsWithAttributesListValidator(ListValidator[NfoTagsWithAttributesValidator]):
"""TagsWithAttributes list for the entry NFO validator"""
_inner_list_type = NfoTagsWithAttributesValidator
class NfoOverrideTagsWithAttributesValidator( class NfoOverrideTagsWithAttributesValidator(
_NfoTagsWithAttributesValidator[ _NfoTagsWithAttributesValidator[
OverridesStringFormatterValidator, OverridesDictFormatterValidator OverridesStringFormatterValidator, OverridesDictFormatterValidator
] ]
): ):
"""TagsWithAttributes for the output directory NFO validator"""
formatter_validator = OverridesStringFormatterValidator formatter_validator = OverridesStringFormatterValidator
dict_formatter_validator = OverridesDictFormatterValidator dict_formatter_validator = OverridesDictFormatterValidator
TNfoTagsWithAttributesValidator = _NfoTagsWithAttributesValidator[ class NfoOverrideTagsWithAttributesListValidator(
TStringFormatterValidator, TDictFormatterValidator ListValidator[NfoOverrideTagsWithAttributesValidator]
] ):
"""TagsWithAttributes list for the output directory NFO validator"""
_inner_list_type = NfoOverrideTagsWithAttributesValidator
# Generic TagsWithAttribute to use for SharedNfoTagsValidator
TNfoTagsWithAttributesValidator = _NfoTagsWithAttributesValidator[TStringFormatterValidator, TDictFormatterValidator]
# List validators
TNfoTagsWithAttributesListValidator = ListValidator[TNfoTagsWithAttributesValidator]
TNfoTagsListValidator = ListValidator[TStringFormatterValidator]
class SharedNfoTagsValidator( class SharedNfoTagsValidator(
DictValidator, Generic[TStringFormatterValidator, TDictFormatterValidator], ABC DictValidator, ABC
): ):
_tags_validator: Type[TNfoTagsListValidator]
_tags_with_attributes_validator: Type[TNfoTagsWithAttributesValidator] _tags_with_attributes_validator: Type[TNfoTagsWithAttributesListValidator]
def __init__(self, name, value): def __init__(self, name, value):
super().__init__(name, value) super().__init__(name, value)
self._string_tags: Dict[str, List[StringFormatterValidator]] = defaultdict(list) self._string_tags: Dict[str, List[TStringFormatterValidator]] = defaultdict(list)
self._attribute_tags: Dict[str, List[TNfoTagsWithAttributesValidator]] = defaultdict(list) self._attribute_tags: Dict[str, List[TNfoTagsWithAttributesValidator]] = defaultdict(list)
for key, tag_value in self._dict.items(): for key, tag_value in self._dict.items():
@ -93,15 +115,17 @@ class SharedNfoTagsValidator(
# iterate each list, validate accordingly if it is a string tag or attribute tag # iterate each list, validate accordingly if it is a string tag or attribute tag
for tag_value_i in tag_value: for tag_value_i in tag_value:
if isinstance(tag_value_i, str): if isinstance(tag_value_i, str):
self._string_tags[key].append( self._string_tags[key].extend(
self._validate_key( self._validate_key(
key=key, key=key,
validator=self._tags_with_attributes_validator.formatter_validator, validator=self._tags_validator,
) ).list
) )
elif isinstance(tag_value_i, dict): elif isinstance(tag_value_i, dict):
self._attribute_tags[key].append( self._attribute_tags[key].extend(
self._validate_key(key=key, validator=self._tags_with_attributes_validator) self._validate_key(
key=key, validator=self._tags_with_attributes_validator
).list
) )
else: else:
raise self._validation_exception( raise self._validation_exception(
@ -109,7 +133,7 @@ class SharedNfoTagsValidator(
) )
@property @property
def string_tags(self) -> Dict[str, List[StringFormatterValidator]]: def string_tags(self) -> Dict[str, List[TStringFormatterValidator]]:
""" """
Returns Returns
------- -------
@ -127,11 +151,11 @@ class SharedNfoTagsValidator(
return self._attribute_tags return self._attribute_tags
class NfoTagsValidator(SharedNfoTagsValidator[StringFormatterValidator, DictFormatterValidator]): class NfoTagsValidator(SharedNfoTagsValidator):
_tags_with_attributes_validator = NfoTagsWithAttributesValidator _tags_validator = ListFormatterValidator
_tags_with_attributes_validator = NfoTagsWithAttributesListValidator
class NfoOverrideTagsValidator( class NfoOverrideTagsValidator(SharedNfoTagsValidator):
SharedNfoTagsValidator[OverridesStringFormatterValidator, OverridesDictFormatterValidator] _tags_validator = ListOverridesFormatterValidator
): _tags_with_attributes_validator = NfoOverrideTagsWithAttributesListValidator
_tags_with_attributes_validator = NfoOverrideTagsWithAttributesValidator

View file

@ -209,6 +209,9 @@ class ListFormatterValidator(ListValidator[StringFormatterValidator]):
_inner_list_type = StringFormatterValidator _inner_list_type = StringFormatterValidator
class ListOverridesFormatterValidator(ListValidator[StringFormatterValidator]):
_inner_list_type = OverridesStringFormatterValidator
class DictFormatterValidator(LiteralDictValidator): class DictFormatterValidator(LiteralDictValidator):
""" """
A dict made up of A dict made up of
@ -241,4 +244,3 @@ class OverridesDictFormatterValidator(DictFormatterValidator):
""" """
_key_validator = OverridesStringFormatterValidator _key_validator = OverridesStringFormatterValidator

View file

@ -33,7 +33,7 @@ def subscription_dict(output_directory):
"attributes": {"🎸?": "value\nnewlines 🎸"}, "attributes": {"🎸?": "value\nnewlines 🎸"},
"tag": "the \n tag 2 🎸🎸", "tag": "the \n tag 2 🎸🎸",
}, },
] ],
}, },
}, },
"output_directory_nfo_tags": { "output_directory_nfo_tags": {
@ -55,7 +55,7 @@ def subscription_dict(output_directory):
"attributes": {"🎸?": "value\nnewlines 🎸"}, "attributes": {"🎸?": "value\nnewlines 🎸"},
"tag": "the \n tag 2 🎸🎸", "tag": "the \n tag 2 🎸🎸",
}, },
] ],
}, },
}, },
} }
@ -82,7 +82,7 @@ class TestNfoTagsPlugins:
output_directory=output_directory, output_directory=output_directory,
transaction_log=transaction_log, transaction_log=transaction_log,
transaction_log_summary_file_name=f"plugins/nfo_tags/{transaction_log_file_name}", transaction_log_summary_file_name=f"plugins/nfo_tags/{transaction_log_file_name}",
regenerate_transaction_log=True regenerate_transaction_log=True,
) )
def test_source_variable_in_output_directory_nfo_tags_errors( def test_source_variable_in_output_directory_nfo_tags_errors(

View file

@ -5,12 +5,37 @@ Rick Beato - Can you hear the difference 🎸🔥 #shorts.3gp
Rick Beato - Can you hear the difference 🎸🔥 #shorts.info.json Rick Beato - Can you hear the difference 🎸🔥 #shorts.info.json
Rick Beato - Can you hear the difference 🎸🔥 #shorts.nfo Rick Beato - Can you hear the difference 🎸🔥 #shorts.nfo
NFO tags: NFO tags:
musicvideo:
album: Music Videos album: Music Videos
artist: Rick Beato artist: Rick Beato
- value 1 🎸
- value 2 🎸
- value 1 🎸
- value 2 🎸
🎸?:
value
newlines 🎸
tag:
the
tag 1 🎸🎸
🎸?:
value
newlines 🎸
tag:
the
tag 2 🎸🎸
🎸?:
value
newlines 🎸
tag:
the
tag 1 🎸🎸
🎸?:
value
newlines 🎸
tag:
the
tag 2 🎸🎸
kodi_safe_title 🎸: kodi_safe_value 🎸 kodi_safe_title 🎸: kodi_safe_value 🎸
kodi_safe_title_with_attrs:
attributes:
🎸?: 🎸?:
value value
newlines 🎸 newlines 🎸
@ -21,10 +46,35 @@ Rick Beato - Can you hear the difference 🎸🔥 #shorts.nfo
year: 2022 year: 2022
test.nfo test.nfo
NFO tags: NFO tags:
kodi_safe_root 🎸: - value 1 🎸
- value 2 🎸
- value 1 🎸
- value 2 🎸
🎸?:
value
newlines 🎸
tag:
the
tag 1 🎸🎸
🎸?:
value
newlines 🎸
tag:
the
tag 2 🎸🎸
🎸?:
value
newlines 🎸
tag:
the
tag 1 🎸🎸
🎸?:
value
newlines 🎸
tag:
the
tag 2 🎸🎸
kodi_safe_title 🎸: kodi_safe_value 🎸 kodi_safe_title 🎸: kodi_safe_value 🎸
kodi_safe_title_with_attrs:
attributes:
🎸?: 🎸?:
value value
newlines 🎸 newlines 🎸

View file

@ -8,26 +8,74 @@ Rick Beato - Can you hear the difference 🎸🔥 #shorts.nfo
musicvideo: musicvideo:
album: Music Videos album: Music Videos
artist: Rick Beato artist: Rick Beato
kodi_safe_multi_title □:
- value 1 □
- value 2 □
- value 1 □
- value 2 □
kodi_safe_multi_title_with_attrs:
- attributes:
□?: 'value
newlines □'
tag: "the \n tag 1 □□"
- attributes:
□?: 'value
newlines □'
tag: "the \n tag 2 □□"
- attributes:
□?: 'value
newlines □'
tag: "the \n tag 1 □□"
- attributes:
□?: 'value
newlines □'
tag: "the \n tag 2 □□"
kodi_safe_title □: kodi_safe_value □ kodi_safe_title □: kodi_safe_value □
kodi_safe_title_with_attrs: kodi_safe_title_with_attrs:
attributes: attributes:
□?: □?: 'value
value
newlines □ newlines □'
tag: tag: "the \n tag □□"
the title: 'Can you hear the difference? □□ #shorts'
tag □□ year: '2022'
title: Can you hear the difference? □□ #shorts
year: 2022
test.nfo test.nfo
NFO tags: NFO tags:
kodi_safe_root □: kodi_safe_root □:
kodi_safe_multi_title □:
- value 1 □
- value 2 □
- value 1 □
- value 2 □
kodi_safe_multi_title_with_attrs:
- attributes:
□?: 'value
newlines □'
tag: "the \n tag 1 □□"
- attributes:
□?: 'value
newlines □'
tag: "the \n tag 2 □□"
- attributes:
□?: 'value
newlines □'
tag: "the \n tag 1 □□"
- attributes:
□?: 'value
newlines □'
tag: "the \n tag 2 □□"
kodi_safe_title □: kodi_safe_value □ kodi_safe_title □: kodi_safe_value □
kodi_safe_title_with_attrs: kodi_safe_title_with_attrs:
attributes: attributes:
□?: □?: 'value
value
newlines □ newlines □'
tag: tag: "the \n tag □□"
the
tag □□