use yaml for print
This commit is contained in:
parent
56bae3f39a
commit
68f2e768e7
13 changed files with 213 additions and 104 deletions
|
|
@ -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:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -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 = {}
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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 🎸
|
||||||
|
|
|
||||||
|
|
@ -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 □□
|
|
||||||
Loading…
Reference in a new issue