[BACKEND] Use native xml library, remove dicttoxml (#196)

This commit is contained in:
Jesse Bannon 2022-08-27 15:01:14 -07:00 committed by GitHub
parent 0642ddf3b7
commit d79e94e7e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 122 additions and 139 deletions

View file

@ -195,7 +195,7 @@ music_tags
nfo_tags nfo_tags
'''''''' ''''''''
.. autoclass:: ytdl_sub.plugins.nfo_tags.NfoTagsOptions() .. autoclass:: ytdl_sub.plugins.nfo_tags.NfoTagsOptions()
:members: :members: nfo_name, nfo_root, tags, kodi_safe
:member-order: bysource :member-order: bysource
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -203,7 +203,7 @@ nfo_tags
output_directory_nfo_tags output_directory_nfo_tags
''''''''''''''''''''''''' '''''''''''''''''''''''''
.. autoclass:: ytdl_sub.plugins.output_directory_nfo_tags.OutputDirectoryNfoTagsOptions() .. autoclass:: ytdl_sub.plugins.output_directory_nfo_tags.OutputDirectoryNfoTagsOptions()
:members: :members: nfo_name, nfo_root, tags, kodi_safe
:member-order: bysource :member-order: bysource
------------------------------------------------------------------------------- -------------------------------------------------------------------------------

View file

@ -26,7 +26,6 @@ packages=find:
install_requires = install_requires =
yt-dlp yt-dlp
argparse==1.4.0 argparse==1.4.0
dicttoxml==1.7.4
mergedeep==1.3.4 mergedeep==1.3.4
mediafile==0.9.0 mediafile==0.9.0
PyYAML==6.0 PyYAML==6.0

View file

@ -1,6 +1,10 @@
import os import os
from abc import ABC
from pathlib import Path from pathlib import Path
from typing import Generic
from typing import Optional from typing import Optional
from typing import Type
from typing import TypeVar
from ytdl_sub.entries.entry import Entry from ytdl_sub.entries.entry import Entry
from ytdl_sub.plugins.plugin import Plugin from ytdl_sub.plugins.plugin import Plugin
@ -14,7 +18,69 @@ from ytdl_sub.validators.string_formatter_validators import StringFormatterValid
from ytdl_sub.validators.validators import BoolValidator from ytdl_sub.validators.validators import BoolValidator
class NfoTagsOptions(PluginOptions): class SharedNfoTagsOptions(PluginOptions, ABC):
"""
Shared code between NFO tags and Ouptut Directory NFO Tags
"""
_formatter_validator: Type[StringFormatterValidator]
_dict_formatter_validator: Type[DictFormatterValidator]
_required_keys = {"nfo_name", "nfo_root", "tags"}
_optional_keys = {"kodi_safe"}
def __init__(self, name, value):
super().__init__(name, value)
self._nfo_name = self._validate_key(key="nfo_name", validator=self._formatter_validator)
self._nfo_root = self._validate_key(key="nfo_root", validator=self._formatter_validator)
self._tags = self._validate_key(key="tags", validator=self._dict_formatter_validator)
self._kodi_safe = self._validate_key_if_present(
key="kodi_safe", validator=BoolValidator, default=False
).value
TSharedNfoTagsOptions = TypeVar("TSharedNfoTagsOptions", bound=SharedNfoTagsOptions)
class SharedNfoTagsPlugin(Plugin[TSharedNfoTagsOptions], Generic[TSharedNfoTagsOptions], ABC):
"""
Shared code between NFO tags and Ouptut Directory NFO Tags
"""
def _create_nfo(self, entry: Optional[Entry] = None) -> None:
nfo = {}
for tag, tag_formatter in sorted(self.plugin_options.tags.dict.items()):
nfo[tag] = self.overrides.apply_formatter(formatter=tag_formatter, entry=entry)
# Write the nfo tags to XML with the nfo_root
nfo_root = self.overrides.apply_formatter(
formatter=self.plugin_options.nfo_root, entry=entry
)
if self.plugin_options.kodi_safe:
nfo = to_max_3_byte_utf8_dict(nfo)
nfo_root = to_max_3_byte_utf8_string(nfo_root)
xml = to_xml(nfo_dict=nfo, nfo_root=nfo_root)
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
os.makedirs(os.path.dirname(nfo_file_path), exist_ok=True)
with open(nfo_file_path, "wb") as nfo_file:
nfo_file.write(xml)
# Save the nfo file and log its metadata
nfo_metadata = FileMetadata.from_dict(value_dict={nfo_root: nfo}, title="NFO tags:")
self.save_file(file_name=nfo_file_name, file_metadata=nfo_metadata, entry=entry)
class NfoTagsOptions(SharedNfoTagsOptions):
""" """
Adds an NFO file for every download file. An NFO file is simply an XML file Adds an NFO file for every download file. An NFO file is simply an XML file
with a ``.nfo`` extension. You can add any values into the NFO. with a ``.nfo`` extension. You can add any values into the NFO.
@ -37,18 +103,8 @@ class NfoTagsOptions(PluginOptions):
kodi_safe: False kodi_safe: False
""" """
_required_keys = {"nfo_name", "nfo_root", "tags"} _formatter_validator = StringFormatterValidator
_optional_keys = {"kodi_safe"} _dict_formatter_validator = DictFormatterValidator
def __init__(self, name, value):
super().__init__(name, value)
self._nfo_name = self._validate_key(key="nfo_name", validator=StringFormatterValidator)
self._nfo_root = self._validate_key(key="nfo_root", validator=StringFormatterValidator)
self._tags = self._validate_key(key="tags", validator=DictFormatterValidator)
self._kodi_safe = self._validate_key_if_present(
key="kodi_safe", validator=BoolValidator, default=False
).value
@property @property
def nfo_name(self) -> StringFormatterValidator: def nfo_name(self) -> StringFormatterValidator:
@ -96,7 +152,7 @@ class NfoTagsOptions(PluginOptions):
return self._kodi_safe return self._kodi_safe
class NfoTagsPlugin(Plugin[NfoTagsOptions]): class NfoTagsPlugin(SharedNfoTagsPlugin[NfoTagsOptions]):
plugin_options_type = NfoTagsOptions plugin_options_type = NfoTagsOptions
def post_process_entry(self, entry: Entry) -> None: def post_process_entry(self, entry: Entry) -> None:
@ -108,32 +164,4 @@ class NfoTagsPlugin(Plugin[NfoTagsOptions]):
entry: entry:
Entry to create an NFO file for Entry to create an NFO file for
""" """
nfo = {} self._create_nfo(entry=entry)
for tag, tag_formatter in sorted(self.plugin_options.tags.dict.items()):
nfo[tag] = self.overrides.apply_formatter(formatter=tag_formatter, entry=entry)
# Write the nfo tags to XML with the nfo_root
nfo_root = self.overrides.apply_formatter(
formatter=self.plugin_options.nfo_root, entry=entry
)
if self.plugin_options.kodi_safe:
nfo = to_max_3_byte_utf8_dict(nfo)
nfo_root = to_max_3_byte_utf8_string(nfo_root)
xml = to_xml(nfo_dict=nfo, nfo_root=nfo_root)
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
os.makedirs(os.path.dirname(nfo_file_path), exist_ok=True)
with open(nfo_file_path, "wb") as nfo_file:
nfo_file.write(xml)
# Save the nfo file and log its metadata
nfo_metadata = FileMetadata.from_dict(value_dict={nfo_root: nfo}, title="NFO tags:")
self.save_file(file_name=nfo_file_name, file_metadata=nfo_metadata, entry=entry)

View file

@ -1,19 +1,12 @@
import os
from pathlib import Path
from typing import Optional from typing import Optional
from ytdl_sub.plugins.plugin import Plugin from ytdl_sub.plugins.nfo_tags import SharedNfoTagsOptions
from ytdl_sub.plugins.plugin import PluginOptions from ytdl_sub.plugins.nfo_tags import SharedNfoTagsPlugin
from ytdl_sub.utils.file_handler import FileMetadata
from ytdl_sub.utils.xml import to_max_3_byte_utf8_dict
from ytdl_sub.utils.xml import to_max_3_byte_utf8_string
from ytdl_sub.utils.xml import to_xml
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.validators import BoolValidator
class OutputDirectoryNfoTagsOptions(PluginOptions): class OutputDirectoryNfoTagsOptions(SharedNfoTagsOptions):
""" """
Adds a single NFO file in the output directory. An NFO file is simply an XML file with a Adds a single NFO file in the output directory. An NFO file is simply an XML file with a
``.nfo`` extension. You can add any values into the NFO. ``.nfo`` extension. You can add any values into the NFO.
@ -34,23 +27,8 @@ class OutputDirectoryNfoTagsOptions(PluginOptions):
kodi_safe: False kodi_safe: False
""" """
_required_keys = {"nfo_name", "nfo_root", "tags"} _formatter_validator = OverridesStringFormatterValidator
_optional_keys = {"kodi_safe"} _dict_formatter_validator = OverridesDictFormatterValidator
def __init__(self, name, value):
super().__init__(name, value)
# Since this does not create NFOs for entries, we must use the overrides formatter classes
# to ensure we are only using values defined as string literals or in overrides
self._nfo_name = self._validate_key(
key="nfo_name", validator=OverridesStringFormatterValidator
)
self._nfo_root = self._validate_key(
key="nfo_root", validator=OverridesStringFormatterValidator
)
self._tags = self._validate_key(key="tags", validator=OverridesDictFormatterValidator)
self._kodi_safe = self._validate_key_if_present(
key="kodi_safe", validator=BoolValidator, default=False
).value
@property @property
def nfo_name(self) -> OverridesStringFormatterValidator: def nfo_name(self) -> OverridesStringFormatterValidator:
@ -96,33 +74,11 @@ class OutputDirectoryNfoTagsOptions(PluginOptions):
return self._kodi_safe return self._kodi_safe
class OutputDirectoryNfoTagsPlugin(Plugin[OutputDirectoryNfoTagsOptions]): class OutputDirectoryNfoTagsPlugin(SharedNfoTagsPlugin[OutputDirectoryNfoTagsOptions]):
plugin_options_type = OutputDirectoryNfoTagsOptions plugin_options_type = OutputDirectoryNfoTagsOptions
def post_process_subscription(self): def post_process_subscription(self):
""" """
Creates an NFO file in the root of the output directory Creates an NFO file in the root of the output directory
""" """
nfo = {} self._create_nfo()
for tag, tag_formatter in sorted(self.plugin_options.tags.dict.items()):
nfo[tag] = self.overrides.apply_formatter(formatter=tag_formatter)
# Write the nfo tags to XML with the nfo_root
nfo_root = self.overrides.apply_formatter(formatter=self.plugin_options.nfo_root)
if self.plugin_options.kodi_safe:
nfo = to_max_3_byte_utf8_dict(nfo)
nfo_root = to_max_3_byte_utf8_string(nfo_root)
xml = to_xml(nfo_dict=nfo, nfo_root=nfo_root)
nfo_file_name = self.overrides.apply_formatter(formatter=self.plugin_options.nfo_name)
# Save the nfo's XML to file
nfo_file_path = Path(self.working_directory) / nfo_file_name
os.makedirs(os.path.dirname(nfo_file_path), exist_ok=True)
with open(nfo_file_path, "wb") as nfo_file:
nfo_file.write(xml)
nfo_metadata = FileMetadata.from_dict(value_dict={nfo_root: nfo}, title="NFO tags:")
self.save_file(file_name=nfo_file_name, file_metadata=nfo_metadata)

View file

@ -1,7 +1,6 @@
import xml.etree.ElementTree as et
from typing import Dict from typing import Dict
import dicttoxml
def _to_max_3_byte_utf8_char(char: str) -> str: def _to_max_3_byte_utf8_char(char: str) -> str:
return "" if len(char.encode("utf-8")) > 3 else char return "" if len(char.encode("utf-8")) > 3 else char
@ -53,9 +52,10 @@ def to_xml(nfo_dict: Dict[str, str], nfo_root: str) -> bytes:
------- -------
XML bytes XML bytes
""" """
return dicttoxml.dicttoxml( xml_root = et.Element(nfo_root)
obj=nfo_dict, for key, value in nfo_dict.items():
root=True, sub_element = et.SubElement(xml_root, key)
custom_root=nfo_root, sub_element.text = value
attr_type=False,
) et.indent(tree=xml_root, space=" ", level=0)
return et.tostring(element=xml_root, encoding="utf-8", xml_declaration=True)

View file

@ -1,5 +1,5 @@
{ {
"JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case-thumb.jpg": "b5353a824a4800cc26f884e3025ed969", "JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case-thumb.jpg": "b5353a824a4800cc26f884e3025ed969",
"JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case.mp4": "2d40822bf4c0527f9080f00357b26ce0", "JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case.mp4": "2d40822bf4c0527f9080f00357b26ce0",
"JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case.nfo": "0c06fe6874588209fccbd9276a446750" "JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case.nfo": "b9bd35e4f260c728774d8dd31f83637c"
} }

View file

@ -1,5 +1,5 @@
{ {
"JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case-thumb.jpg": "b5353a824a4800cc26f884e3025ed969", "JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case-thumb.jpg": "b5353a824a4800cc26f884e3025ed969",
"JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case.mp4": "6f0bac1c364ff3bb13d3e8a955aaa002", "JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case.mp4": "6f0bac1c364ff3bb13d3e8a955aaa002",
"JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case.nfo": "0c06fe6874588209fccbd9276a446750" "JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case.nfo": "b9bd35e4f260c728774d8dd31f83637c"
} }

View file

@ -1,5 +1,5 @@
{ {
"JMC - YouTube Rewind 2019 For the Record #YouTubeRewind-thumb.jpg": "50ee47c80f679029f5d3503bb91b045a", "JMC - YouTube Rewind 2019 For the Record #YouTubeRewind-thumb.jpg": "50ee47c80f679029f5d3503bb91b045a",
"JMC - YouTube Rewind 2019 For the Record #YouTubeRewind.mp4": "8562853314b75c1e47abd4f5ba97315c", "JMC - YouTube Rewind 2019 For the Record #YouTubeRewind.mp4": "8562853314b75c1e47abd4f5ba97315c",
"JMC - YouTube Rewind 2019 For the Record #YouTubeRewind.nfo": "ffa10f1cbc098ace7b1c7a8fbe3097a8" "JMC - YouTube Rewind 2019 For the Record #YouTubeRewind.nfo": "c64964fab07574080e5da3242e3bfd48"
} }

View file

@ -3,5 +3,5 @@
"JMC - YouTube Rewind 2019 For the Record #YouTubeRewind.de.srt": "b343c3bb9257b7ee7ba38f570a115b37", "JMC - YouTube Rewind 2019 For the Record #YouTubeRewind.de.srt": "b343c3bb9257b7ee7ba38f570a115b37",
"JMC - YouTube Rewind 2019 For the Record #YouTubeRewind.en.srt": "fe8c6ee92cae6e059fd80fd61691adbe", "JMC - YouTube Rewind 2019 For the Record #YouTubeRewind.en.srt": "fe8c6ee92cae6e059fd80fd61691adbe",
"JMC - YouTube Rewind 2019 For the Record #YouTubeRewind.mp4": "8562853314b75c1e47abd4f5ba97315c", "JMC - YouTube Rewind 2019 For the Record #YouTubeRewind.mp4": "8562853314b75c1e47abd4f5ba97315c",
"JMC - YouTube Rewind 2019 For the Record #YouTubeRewind.nfo": "ffa10f1cbc098ace7b1c7a8fbe3097a8" "JMC - YouTube Rewind 2019 For the Record #YouTubeRewind.nfo": "c64964fab07574080e5da3242e3bfd48"
} }

View file

@ -2,42 +2,42 @@
".ytdl-sub-pz-download-archive.json": "f0f692ef29653ca8d0af556415bfb065", ".ytdl-sub-pz-download-archive.json": "f0f692ef29653ca8d0af556415bfb065",
"Season 2010/s2010.e0813 - Oblivion Mod Falcor p.1-thumb.jpg": "fb95b510681676e81c321171fc23143e", "Season 2010/s2010.e0813 - Oblivion Mod Falcor p.1-thumb.jpg": "fb95b510681676e81c321171fc23143e",
"Season 2010/s2010.e0813 - Oblivion Mod Falcor p.1.mp4": "931a705864c57d21d6fedebed4af6bbc", "Season 2010/s2010.e0813 - Oblivion Mod Falcor p.1.mp4": "931a705864c57d21d6fedebed4af6bbc",
"Season 2010/s2010.e0813 - Oblivion Mod Falcor p.1.nfo": "67d8d71d048039080acbba3bce4febaa", "Season 2010/s2010.e0813 - Oblivion Mod Falcor p.1.nfo": "2d0738094d8e649eaebbab16fd647da1",
"Season 2010/s2010.e1202 - Oblivion Mod Falcor p.2-thumb.jpg": "8b32ee9c037fa669e444a0ac181525a1", "Season 2010/s2010.e1202 - Oblivion Mod Falcor p.2-thumb.jpg": "8b32ee9c037fa669e444a0ac181525a1",
"Season 2010/s2010.e1202 - Oblivion Mod Falcor p.2.mp4": "d3469b4dca7139cb3dbc38712b6796bf", "Season 2010/s2010.e1202 - Oblivion Mod Falcor p.2.mp4": "d3469b4dca7139cb3dbc38712b6796bf",
"Season 2010/s2010.e1202 - Oblivion Mod Falcor p.2.nfo": "d81f49cedbd7edaee987521e89b37904", "Season 2010/s2010.e1202 - Oblivion Mod Falcor p.2.nfo": "5c258f9e54854ef292ce3c58331da110",
"Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg": "b232d253df621aa770b780c1301d364d", "Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg": "b232d253df621aa770b780c1301d364d",
"Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1].mp4": "e66287b9832277b6a4d1554e29d9fdcc", "Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1].mp4": "e66287b9832277b6a4d1554e29d9fdcc",
"Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1].nfo": "f7c0de89038f8c491bded8a3968720a2", "Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1].nfo": "abb3ac33366cc3b86d0467c8fb80a323",
"Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27]-thumb.jpg": "d17c379ea8b362f5b97c6b213b0342cb", "Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27]-thumb.jpg": "d17c379ea8b362f5b97c6b213b0342cb",
"Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27].mp4": "04ab5cb3cc12325d0c96a7cd04a8b91d", "Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27].mp4": "04ab5cb3cc12325d0c96a7cd04a8b91d",
"Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27].nfo": "ee1eda78fa0980bc703e602b5012dd1f", "Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27].nfo": "46190954652c9d9812e061fc0c9e1d92",
"Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21]-thumb.jpg": "e7830aa8a64b0cde65ba3f7e5fc56530", "Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21]-thumb.jpg": "e7830aa8a64b0cde65ba3f7e5fc56530",
"Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21].mp4": "025de6099a5c98e6397153c7a62d517d", "Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21].mp4": "025de6099a5c98e6397153c7a62d517d",
"Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21].nfo": "61eb6369430da0ab6134d78829a7621b", "Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21].nfo": "30993fa8e00a0e370b4db244f3da8f7d",
"Season 2011/s2011.e0529 - Project Zombie Official Trailer (IP mc.projectzombie.beastnode.net)-thumb.jpg": "c956192a379b3661595c9920972d4819", "Season 2011/s2011.e0529 - Project Zombie Official Trailer (IP mc.projectzombie.beastnode.net)-thumb.jpg": "c956192a379b3661595c9920972d4819",
"Season 2011/s2011.e0529 - Project Zombie Official Trailer (IP mc.projectzombie.beastnode.net).mp4": "3d9c19835b03355d6fd5d00cd59dbe5b", "Season 2011/s2011.e0529 - Project Zombie Official Trailer (IP mc.projectzombie.beastnode.net).mp4": "3d9c19835b03355d6fd5d00cd59dbe5b",
"Season 2011/s2011.e0529 - Project Zombie Official Trailer (IP mc.projectzombie.beastnode.net).nfo": "60f72b99f5c69f9e03a071a12160928f", "Season 2011/s2011.e0529 - Project Zombie Official Trailer (IP mc.projectzombie.beastnode.net).nfo": "11a0e8754c414875bcd454358683da5f",
"Season 2011/s2011.e0630 - Project Zombie Fin-thumb.jpg": "00ed383591779ffe98291de60f198fe9", "Season 2011/s2011.e0630 - Project Zombie Fin-thumb.jpg": "00ed383591779ffe98291de60f198fe9",
"Season 2011/s2011.e0630 - Project Zombie Fin.mp4": "4971cb2d4fa29460361031f3fa8e1ea9", "Season 2011/s2011.e0630 - Project Zombie Fin.mp4": "4971cb2d4fa29460361031f3fa8e1ea9",
"Season 2011/s2011.e0630 - Project Zombie Fin.nfo": "a7b5d9e57d20852f5daf360a1373bb7a", "Season 2011/s2011.e0630 - Project Zombie Fin.nfo": "a464b9c8c48a9a5d4776436d8108f8f5",
"Season 2011/s2011.e1121 - Skyrim 'Ultra HD wMods' [PC]-thumb.jpg": "1718599d5189c65f7d8cf6acfa5ea851", "Season 2011/s2011.e1121 - Skyrim 'Ultra HD wMods' [PC]-thumb.jpg": "1718599d5189c65f7d8cf6acfa5ea851",
"Season 2011/s2011.e1121 - Skyrim 'Ultra HD wMods' [PC].mp4": "55e9b0add08c48c9c66105da0def2426", "Season 2011/s2011.e1121 - Skyrim 'Ultra HD wMods' [PC].mp4": "55e9b0add08c48c9c66105da0def2426",
"Season 2011/s2011.e1121 - Skyrim 'Ultra HD wMods' [PC].nfo": "fe60e2b6b564f9316b6c7c183e1cf300", "Season 2011/s2011.e1121 - Skyrim 'Ultra HD wMods' [PC].nfo": "3562934ab9a5e802d955eda24ad355de",
"Season 2012/s2012.e0123 - Project Zombie Map Trailer-thumb.jpg": "54ebe9df801b278fdd17b21afa8373a6", "Season 2012/s2012.e0123 - Project Zombie Map Trailer-thumb.jpg": "54ebe9df801b278fdd17b21afa8373a6",
"Season 2012/s2012.e0123 - Project Zombie Map Trailer.mp4": "65e4ce53ed5ec4139995469f99477a50", "Season 2012/s2012.e0123 - Project Zombie Map Trailer.mp4": "65e4ce53ed5ec4139995469f99477a50",
"Season 2012/s2012.e0123 - Project Zombie Map Trailer.nfo": "c8900adcca83c473c79a4afbc7ad2de1", "Season 2012/s2012.e0123 - Project Zombie Map Trailer.nfo": "5e810d839be90dab579400a6177f90b3",
"Season 2013/s2013.e0719 - Project Zombie Rewind Trailer-thumb.jpg": "e29d49433175de8a761af35c5307791f", "Season 2013/s2013.e0719 - Project Zombie Rewind Trailer-thumb.jpg": "e29d49433175de8a761af35c5307791f",
"Season 2013/s2013.e0719 - Project Zombie Rewind Trailer.mp4": "18620a8257a686beda65e54add4d4cd1", "Season 2013/s2013.e0719 - Project Zombie Rewind Trailer.mp4": "18620a8257a686beda65e54add4d4cd1",
"Season 2013/s2013.e0719 - Project Zombie Rewind Trailer.nfo": "1c993c41d4308a6049333154d0adee16", "Season 2013/s2013.e0719 - Project Zombie Rewind Trailer.nfo": "83772bec917bb5d71e1ca0c061c1ec78",
"Season 2018/s2018.e1029 - Jesse's Minecraft Server Teaser Trailer-thumb.jpg": "705ca4e0d99b37e9ecdf6bfe4b90c59b", "Season 2018/s2018.e1029 - Jesse's Minecraft Server Teaser Trailer-thumb.jpg": "705ca4e0d99b37e9ecdf6bfe4b90c59b",
"Season 2018/s2018.e1029 - Jesse's Minecraft Server Teaser Trailer.mp4": "82f6ee7253e1dbb83ae7215af08ffacc", "Season 2018/s2018.e1029 - Jesse's Minecraft Server Teaser Trailer.mp4": "82f6ee7253e1dbb83ae7215af08ffacc",
"Season 2018/s2018.e1029 - Jesse's Minecraft Server Teaser Trailer.nfo": "cc7886aae3af6b7b0facd82f95390242", "Season 2018/s2018.e1029 - Jesse's Minecraft Server Teaser Trailer.nfo": "368d68db0cbe9eb4f43ece0517445e82",
"Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id-thumb.jpg": "28d852ede73b879b9ebf9a061cfc7d46", "Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id-thumb.jpg": "28d852ede73b879b9ebf9a061cfc7d46",
"Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.en.srt": "3d2c4e7f65d2ca5e96da38ce7eecfc4e", "Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.en.srt": "3d2c4e7f65d2ca5e96da38ce7eecfc4e",
"Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.mp4": "e733b4cc385b953b08c8eb0f47e03c1e", "Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.mp4": "e733b4cc385b953b08c8eb0f47e03c1e",
"Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.nfo": "2b3ccb3f1ef81ee49fe1afb88f275a09", "Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.nfo": "d9114d43d87907b2afc06eb089a8ac0a",
"fanart.jpg": "129c6639b47299bc48062f0365e670ee", "fanart.jpg": "129c6639b47299bc48062f0365e670ee",
"poster.jpg": "5de28eea5a921a041452ab3ce1041f73", "poster.jpg": "5de28eea5a921a041452ab3ce1041f73",
"tvshow.nfo": "83c7db96081ac5bdf289fcf396bec157" "tvshow.nfo": "85cef9db54e9afb7af8e4912b5262d3f"
} }

View file

@ -2,5 +2,5 @@
".ytdl-sub-pz-download-archive.json": "99914b932bd37a50b983c5e7c90ae93b", ".ytdl-sub-pz-download-archive.json": "99914b932bd37a50b983c5e7c90ae93b",
"fanart.jpg": "129c6639b47299bc48062f0365e670ee", "fanart.jpg": "129c6639b47299bc48062f0365e670ee",
"poster.jpg": "5de28eea5a921a041452ab3ce1041f73", "poster.jpg": "5de28eea5a921a041452ab3ce1041f73",
"tvshow.nfo": "83c7db96081ac5bdf289fcf396bec157" "tvshow.nfo": "85cef9db54e9afb7af8e4912b5262d3f"
} }

View file

@ -2,12 +2,12 @@
".ytdl-sub-pz-download-archive.json": "756b60d7a6c47d8163e3283404493a8d", ".ytdl-sub-pz-download-archive.json": "756b60d7a6c47d8163e3283404493a8d",
"Season 2018/s2018.e1029 - Jesse's Minecraft Server Teaser Trailer-thumb.jpg": "705ca4e0d99b37e9ecdf6bfe4b90c59b", "Season 2018/s2018.e1029 - Jesse's Minecraft Server Teaser Trailer-thumb.jpg": "705ca4e0d99b37e9ecdf6bfe4b90c59b",
"Season 2018/s2018.e1029 - Jesse's Minecraft Server Teaser Trailer.mp4": "82f6ee7253e1dbb83ae7215af08ffacc", "Season 2018/s2018.e1029 - Jesse's Minecraft Server Teaser Trailer.mp4": "82f6ee7253e1dbb83ae7215af08ffacc",
"Season 2018/s2018.e1029 - Jesse's Minecraft Server Teaser Trailer.nfo": "cc7886aae3af6b7b0facd82f95390242", "Season 2018/s2018.e1029 - Jesse's Minecraft Server Teaser Trailer.nfo": "368d68db0cbe9eb4f43ece0517445e82",
"Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id-thumb.jpg": "28d852ede73b879b9ebf9a061cfc7d46", "Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id-thumb.jpg": "28d852ede73b879b9ebf9a061cfc7d46",
"Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.en.srt": "3d2c4e7f65d2ca5e96da38ce7eecfc4e", "Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.en.srt": "3d2c4e7f65d2ca5e96da38ce7eecfc4e",
"Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.mp4": "e733b4cc385b953b08c8eb0f47e03c1e", "Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.mp4": "e733b4cc385b953b08c8eb0f47e03c1e",
"Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.nfo": "2b3ccb3f1ef81ee49fe1afb88f275a09", "Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.nfo": "d9114d43d87907b2afc06eb089a8ac0a",
"fanart.jpg": "129c6639b47299bc48062f0365e670ee", "fanart.jpg": "129c6639b47299bc48062f0365e670ee",
"poster.jpg": "5de28eea5a921a041452ab3ce1041f73", "poster.jpg": "5de28eea5a921a041452ab3ce1041f73",
"tvshow.nfo": "83c7db96081ac5bdf289fcf396bec157" "tvshow.nfo": "85cef9db54e9afb7af8e4912b5262d3f"
} }

View file

@ -3,8 +3,8 @@
"Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id-thumb.jpg": "28d852ede73b879b9ebf9a061cfc7d46", "Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id-thumb.jpg": "28d852ede73b879b9ebf9a061cfc7d46",
"Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.en.srt": "3d2c4e7f65d2ca5e96da38ce7eecfc4e", "Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.en.srt": "3d2c4e7f65d2ca5e96da38ce7eecfc4e",
"Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.mp4": "e733b4cc385b953b08c8eb0f47e03c1e", "Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.mp4": "e733b4cc385b953b08c8eb0f47e03c1e",
"Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.nfo": "2b3ccb3f1ef81ee49fe1afb88f275a09", "Season 2018/s2018.e1102 - Jesse's Minecraft Server IP mc.jesse.id.nfo": "d9114d43d87907b2afc06eb089a8ac0a",
"fanart.jpg": "129c6639b47299bc48062f0365e670ee", "fanart.jpg": "129c6639b47299bc48062f0365e670ee",
"poster.jpg": "5de28eea5a921a041452ab3ce1041f73", "poster.jpg": "5de28eea5a921a041452ab3ce1041f73",
"tvshow.nfo": "83c7db96081ac5bdf289fcf396bec157" "tvshow.nfo": "85cef9db54e9afb7af8e4912b5262d3f"
} }

View file

@ -1,5 +1,5 @@
{ {
"JMC - Jesse's Minecraft Server-thumb.jpg": "a3f1910f9c51f6442f845a528e190829", "JMC - Jesse's Minecraft Server-thumb.jpg": "a3f1910f9c51f6442f845a528e190829",
"JMC - Jesse's Minecraft Server.mkv": "21f246b1c922e11add509ea26c43c53d", "JMC - Jesse's Minecraft Server.mkv": "21f246b1c922e11add509ea26c43c53d",
"JMC - Jesse's Minecraft Server.nfo": "10df5dcdb65ab18ecf21b3503c77e48b" "JMC - Jesse's Minecraft Server.nfo": "d16396c60b63c06a4f2c9239553bdf61"
} }

View file

@ -2,11 +2,11 @@
".ytdl-sub-music_video_playlist_test-download-archive.json": "9f785c29194a6ecfba6a6b4018763ddc", ".ytdl-sub-music_video_playlist_test-download-archive.json": "9f785c29194a6ecfba6a6b4018763ddc",
"JMC - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg": "b232d253df621aa770b780c1301d364d", "JMC - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg": "b232d253df621aa770b780c1301d364d",
"JMC - Jesse's Minecraft Server [Trailer - Feb.1].mp4": "e66287b9832277b6a4d1554e29d9fdcc", "JMC - Jesse's Minecraft Server [Trailer - Feb.1].mp4": "e66287b9832277b6a4d1554e29d9fdcc",
"JMC - Jesse's Minecraft Server [Trailer - Feb.1].nfo": "3d272fe58487b6011ad049b6000b046f", "JMC - Jesse's Minecraft Server [Trailer - Feb.1].nfo": "f8fd72bb97ed03938487494ad9094ca0",
"JMC - Jesse's Minecraft Server [Trailer - Feb.27]-thumb.jpg": "d17c379ea8b362f5b97c6b213b0342cb", "JMC - Jesse's Minecraft Server [Trailer - Feb.27]-thumb.jpg": "d17c379ea8b362f5b97c6b213b0342cb",
"JMC - Jesse's Minecraft Server [Trailer - Feb.27].mp4": "04ab5cb3cc12325d0c96a7cd04a8b91d", "JMC - Jesse's Minecraft Server [Trailer - Feb.27].mp4": "04ab5cb3cc12325d0c96a7cd04a8b91d",
"JMC - Jesse's Minecraft Server [Trailer - Feb.27].nfo": "6f99af10bef67276a507d1d9770c5e92", "JMC - Jesse's Minecraft Server [Trailer - Feb.27].nfo": "6de4d997cfb300356072b4ebb09cbe38",
"JMC - Jesse's Minecraft Server [Trailer - Mar.21]-thumb.jpg": "e7830aa8a64b0cde65ba3f7e5fc56530", "JMC - Jesse's Minecraft Server [Trailer - Mar.21]-thumb.jpg": "e7830aa8a64b0cde65ba3f7e5fc56530",
"JMC - Jesse's Minecraft Server [Trailer - Mar.21].mp4": "025de6099a5c98e6397153c7a62d517d", "JMC - Jesse's Minecraft Server [Trailer - Mar.21].mp4": "025de6099a5c98e6397153c7a62d517d",
"JMC - Jesse's Minecraft Server [Trailer - Mar.21].nfo": "beec3c1326654bd8c858cecf4e40977a" "JMC - Jesse's Minecraft Server [Trailer - Mar.21].nfo": "f000a6ed8caacb62a134a6ca81e3f308"
} }

View file

@ -6,15 +6,15 @@
"Project Zombie - 5-6.Part 4.mp4": "de9afd0b6fa5f845b086e2d4915f2f14", "Project Zombie - 5-6.Part 4.mp4": "de9afd0b6fa5f845b086e2d4915f2f14",
"Project Zombie - 6-6.Part 5.mp4": "d485c2a5df9a2d5bf9037a7a96a04d10", "Project Zombie - 6-6.Part 5.mp4": "d485c2a5df9a2d5bf9037a7a96a04d10",
"Project Zombie - Intro-thumb.jpg": "fb95b510681676e81c321171fc23143e", "Project Zombie - Intro-thumb.jpg": "fb95b510681676e81c321171fc23143e",
"Project Zombie - Intro.nfo": "ded59ac906f579312cc3cf98a57e7ea3", "Project Zombie - Intro.nfo": "3a89bd4707e64b66a22d13bb7f81bf16",
"Project Zombie - Part 1-thumb.jpg": "fb95b510681676e81c321171fc23143e", "Project Zombie - Part 1-thumb.jpg": "fb95b510681676e81c321171fc23143e",
"Project Zombie - Part 1.nfo": "70ff5cd0092b8bc22dc4db93a824789b", "Project Zombie - Part 1.nfo": "a57a3a98d0b9db655bc4a792953ab96e",
"Project Zombie - Part 2-thumb.jpg": "fb95b510681676e81c321171fc23143e", "Project Zombie - Part 2-thumb.jpg": "fb95b510681676e81c321171fc23143e",
"Project Zombie - Part 2.nfo": "54450c18a2cbb9d6d2ee5d0a1fb3f279", "Project Zombie - Part 2.nfo": "68921dd5386fd90e2c0640127dda5dc2",
"Project Zombie - Part 3-thumb.jpg": "fb95b510681676e81c321171fc23143e", "Project Zombie - Part 3-thumb.jpg": "fb95b510681676e81c321171fc23143e",
"Project Zombie - Part 3.nfo": "0effb13fc4039363a95969d1048dde57", "Project Zombie - Part 3.nfo": "4198aec2d4cd70e4cb3946f10be205a7",
"Project Zombie - Part 4-thumb.jpg": "fb95b510681676e81c321171fc23143e", "Project Zombie - Part 4-thumb.jpg": "fb95b510681676e81c321171fc23143e",
"Project Zombie - Part 4.nfo": "74bd0d7c12105469838768a0cc323a8c", "Project Zombie - Part 4.nfo": "8a87fa99c5ba5b11cd14c592a0b97dbb",
"Project Zombie - Part 5-thumb.jpg": "fb95b510681676e81c321171fc23143e", "Project Zombie - Part 5-thumb.jpg": "fb95b510681676e81c321171fc23143e",
"Project Zombie - Part 5.nfo": "a8cf2e77721335ea7c18e22734e7996c" "Project Zombie - Part 5.nfo": "73091166bd5bd3d65d7e481e317ed128"
} }

View file

@ -1,5 +1,5 @@
{ {
"JMC - Oblivion Mod Falcor p.1-thumb.jpg": "fb95b510681676e81c321171fc23143e", "JMC - Oblivion Mod Falcor p.1-thumb.jpg": "fb95b510681676e81c321171fc23143e",
"JMC - Oblivion Mod Falcor p.1.mp4": "28c14cdac05c803efe71abb9454ab306", "JMC - Oblivion Mod Falcor p.1.mp4": "28c14cdac05c803efe71abb9454ab306",
"JMC - Oblivion Mod Falcor p.1.nfo": "89f509a8a3d9003e22a9091abeeae5dc" "JMC - Oblivion Mod Falcor p.1.nfo": "24cc4e17d2bebc89b2759ce5471d403e"
} }