[FEATURE] Ability to write .info.json files (#201)
This commit is contained in:
parent
99925836f3
commit
ca0ac787b0
51 changed files with 650 additions and 394 deletions
|
|
@ -38,6 +38,7 @@ presets:
|
|||
output_directory: "{music_video_directory}"
|
||||
file_name: "{music_video_name}.{ext}"
|
||||
thumbnail_name: "{music_video_name}-thumb.jpg"
|
||||
info_json_name: "{music_video_name}.{info_json_ext}"
|
||||
|
||||
# For each video downloaded, add a music video NFO file for it. Populate it
|
||||
# with tags that Kodi will read and use to display it in the music or music
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ presets:
|
|||
output_directory: "{youtube_tv_shows_directory}/{tv_show_name_sanitized}"
|
||||
file_name: "{episode_name}.{ext}"
|
||||
thumbnail_name: "{episode_name}-thumb.jpg"
|
||||
info_json_name: "{episode_name}.{info_json_ext}"
|
||||
maintain_download_archive: True
|
||||
|
||||
# For each video downloaded, add an episode NFO file for it. We give it
|
||||
|
|
|
|||
|
|
@ -44,8 +44,9 @@ presets:
|
|||
# them on a successive invocation.
|
||||
output_options:
|
||||
output_directory: "{music_directory}"
|
||||
file_name: "{artist_sanitized}/{album_directory_name}/{track_number_padded} - {title_sanitized}.{ext}"
|
||||
thumbnail_name: "{artist_sanitized}/{album_directory_name}/folder.jpg"
|
||||
file_name: "{album_path}/{track_file_name}.{ext}"
|
||||
thumbnail_name: "{album_path}/folder.{thumbnail_ext}"
|
||||
info_json_name: "{album_path}/{track_file_name}.{info_json_ext}"
|
||||
maintain_download_archive: True
|
||||
|
||||
# For each song downloaded, populate the audio file with music tags.
|
||||
|
|
@ -67,4 +68,6 @@ presets:
|
|||
# which gets reused above for the audio file name and album art path.
|
||||
overrides:
|
||||
album_directory_name: "[{album_year}] {album_sanitized}"
|
||||
track_file_name: "{track_number_padded} - {title_sanitized}"
|
||||
album_path: "{artist_sanitized}/{album_directory_name}"
|
||||
music_directory: "/path/to/music"
|
||||
|
|
@ -10,6 +10,7 @@ presets:
|
|||
output_options:
|
||||
output_directory: "{music_directory}"
|
||||
file_name: "{custom_track_name_sanitized}.{ext}"
|
||||
info_json_name: "{custom_track_name_sanitized}.{info_json_ext}"
|
||||
|
||||
audio_extract:
|
||||
codec: "mp3"
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ class OutputOptions(StrictDictValidator):
|
|||
file_name: "{title_sanitized}.{ext}"
|
||||
# optional
|
||||
thumbnail_name: "{title_sanitized}.{thumbnail_ext}"
|
||||
info_json_name: "{title_sanitized}.{info_json_ext}"
|
||||
maintain_download_archive: True
|
||||
keep_files_before: now
|
||||
keep_files_after: 19000101
|
||||
|
|
@ -126,6 +127,7 @@ class OutputOptions(StrictDictValidator):
|
|||
_required_keys = {"output_directory", "file_name"}
|
||||
_optional_keys = {
|
||||
"thumbnail_name",
|
||||
"info_json_name",
|
||||
"subtitles_name",
|
||||
"maintain_download_archive",
|
||||
"keep_files_before",
|
||||
|
|
@ -148,6 +150,9 @@ class OutputOptions(StrictDictValidator):
|
|||
self._thumbnail_name = self._validate_key_if_present(
|
||||
key="thumbnail_name", validator=StringFormatterValidator
|
||||
)
|
||||
self._info_json_name = self._validate_key_if_present(
|
||||
key="info_json_name", validator=StringFormatterValidator
|
||||
)
|
||||
|
||||
self._maintain_download_archive = self._validate_key_if_present(
|
||||
key="maintain_download_archive", validator=BoolValidator, default=False
|
||||
|
|
@ -191,6 +196,15 @@ class OutputOptions(StrictDictValidator):
|
|||
"""
|
||||
return self._thumbnail_name
|
||||
|
||||
@property
|
||||
def info_json_name(self) -> Optional[StringFormatterValidator]:
|
||||
"""
|
||||
Optional. The file name for the media's info json file. This can include directories such
|
||||
as ``"Season {upload_year}/{title}.{info_json_ext}"``, and will be placed in the output
|
||||
directory.
|
||||
"""
|
||||
return self._info_json_name
|
||||
|
||||
@property
|
||||
def maintain_download_archive(self) -> bool:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import copy
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
|
@ -59,6 +61,33 @@ class Entry(EntryVariables, BaseEntry):
|
|||
|
||||
return None
|
||||
|
||||
def get_download_info_json_name(self) -> str:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The download info json's file name
|
||||
"""
|
||||
return f"{self.uid}.{self.info_json_ext}"
|
||||
|
||||
def get_download_info_json_path(self) -> str:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
Entry's downloaded info json file path
|
||||
"""
|
||||
return str(Path(self.working_directory()) / self.get_download_info_json_name())
|
||||
|
||||
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"] = 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:
|
||||
file.write(kwargs_json)
|
||||
|
||||
@final
|
||||
def is_downloaded(self) -> bool:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -304,3 +304,13 @@ class EntryVariables(SourceVariables):
|
|||
The uploaded date formatted as YYYY-MM-DD
|
||||
"""
|
||||
return f"{self.upload_year}-{self.upload_month_padded}-{self.upload_day_padded}"
|
||||
|
||||
@property
|
||||
def info_json_ext(self) -> str:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
The "info.json" extension
|
||||
"""
|
||||
return "info.json"
|
||||
|
|
|
|||
136
src/ytdl_sub/subscriptions/base_subscription.py
Normal file
136
src/ytdl_sub/subscriptions/base_subscription.py
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
from abc import ABC
|
||||
from pathlib import Path
|
||||
from typing import Type
|
||||
|
||||
from ytdl_sub.config.config_file import ConfigOptions
|
||||
from ytdl_sub.config.preset import Preset
|
||||
from ytdl_sub.config.preset import PresetPlugins
|
||||
from ytdl_sub.config.preset_options import OutputOptions
|
||||
from ytdl_sub.config.preset_options import Overrides
|
||||
from ytdl_sub.config.preset_options import YTDLOptions
|
||||
from ytdl_sub.downloaders.downloader import Downloader
|
||||
from ytdl_sub.downloaders.downloader import DownloaderValidator
|
||||
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
|
||||
|
||||
|
||||
class BaseSubscription(ABC):
|
||||
"""
|
||||
Subscription classes are the 'controllers' that perform...
|
||||
|
||||
- Downloading via ytdlp
|
||||
- Adding metadata
|
||||
- Placing files in the output directory
|
||||
|
||||
while configuring each step with provided configs. Child classes are expected to
|
||||
provide SourceValidator (SourceT), which defines the source and its configurable options.
|
||||
In addition, they should provide in the init an Entry type (EntryT), which is the entry that
|
||||
will be returned after downloading.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
config_options: ConfigOptions,
|
||||
preset_options: Preset,
|
||||
):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
name: str
|
||||
Name of the subscription
|
||||
config_options: ConfigOptions
|
||||
preset_options: Preset
|
||||
"""
|
||||
self.name = name
|
||||
self._config_options = config_options
|
||||
self._preset_options = preset_options
|
||||
|
||||
self._enhanced_download_archive = EnhancedDownloadArchive(
|
||||
subscription_name=name,
|
||||
working_directory=self.working_directory,
|
||||
output_directory=self.output_directory,
|
||||
)
|
||||
|
||||
@property
|
||||
def downloader_class(self) -> Type[Downloader]:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
This subscription's downloader class
|
||||
"""
|
||||
return self._preset_options.downloader
|
||||
|
||||
@property
|
||||
def downloader_options(self) -> DownloaderValidator:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The download options for this subscription's downloader
|
||||
"""
|
||||
return self._preset_options.downloader_options
|
||||
|
||||
@property
|
||||
def plugins(self) -> PresetPlugins:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
List of tuples containing (plugin class, plugin options)
|
||||
"""
|
||||
return self._preset_options.plugins
|
||||
|
||||
@property
|
||||
def ytdl_options(self) -> YTDLOptions:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
YTDL options for this subscription
|
||||
"""
|
||||
return self._preset_options.ytdl_options
|
||||
|
||||
@property
|
||||
def output_options(self) -> OutputOptions:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The output options defined for this subscription
|
||||
"""
|
||||
return self._preset_options.output_options
|
||||
|
||||
@property
|
||||
def overrides(self) -> Overrides:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The overrides defined for this subscription
|
||||
"""
|
||||
return self._preset_options.overrides
|
||||
|
||||
@property
|
||||
def working_directory(self) -> str:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The directory that the downloader saves files to
|
||||
"""
|
||||
return str(Path(self._config_options.working_directory) / Path(self.name))
|
||||
|
||||
@property
|
||||
def output_directory(self) -> str:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The formatted output directory
|
||||
"""
|
||||
return self.overrides.apply_formatter(formatter=self.output_options.output_directory)
|
||||
|
||||
@property
|
||||
def maintain_download_archive(self) -> bool:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
Whether to maintain a download archive
|
||||
"""
|
||||
return (
|
||||
self.output_options.maintain_download_archive
|
||||
and self.downloader_class.supports_download_archive
|
||||
)
|
||||
|
|
@ -1,388 +1,11 @@
|
|||
import contextlib
|
||||
import os
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Type
|
||||
|
||||
from ytdl_sub.config.config_file import ConfigFile
|
||||
from ytdl_sub.config.config_file import ConfigOptions
|
||||
from ytdl_sub.config.preset import Preset
|
||||
from ytdl_sub.config.preset import PresetPlugins
|
||||
from ytdl_sub.config.preset_options import OutputOptions
|
||||
from ytdl_sub.config.preset_options import Overrides
|
||||
from ytdl_sub.config.preset_options import YTDLOptions
|
||||
from ytdl_sub.downloaders.downloader import Downloader
|
||||
from ytdl_sub.downloaders.downloader import DownloaderValidator
|
||||
from ytdl_sub.entries.entry import Entry
|
||||
from ytdl_sub.plugins.plugin import Plugin
|
||||
from ytdl_sub.subscriptions.subscription_ytdl_options import SubscriptionYTDLOptions
|
||||
from ytdl_sub.utils.datetime import to_date_range
|
||||
from ytdl_sub.utils.exceptions import ValidationException
|
||||
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
||||
from ytdl_sub.utils.file_handler import FileMetadata
|
||||
from ytdl_sub.utils.thumbnail import convert_download_thumbnail
|
||||
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
|
||||
from ytdl_sub.subscriptions.subscription_download import SubscriptionDownload
|
||||
|
||||
|
||||
def _get_split_plugin(plugins: List[Plugin]) -> Optional[Plugin]:
|
||||
split_plugins = [plugin for plugin in plugins if plugin.is_split_plugin]
|
||||
|
||||
if len(split_plugins) == 1:
|
||||
return split_plugins[0]
|
||||
if len(split_plugins) > 1:
|
||||
raise ValidationException("Can not use more than one split plugins at a time")
|
||||
return None
|
||||
|
||||
|
||||
class Subscription:
|
||||
"""
|
||||
Subscription classes are the 'controllers' that perform...
|
||||
|
||||
- Downloading via ytdlp
|
||||
- Adding metadata
|
||||
- Placing files in the output directory
|
||||
|
||||
while configuring each step with provided configs. Child classes are expected to
|
||||
provide SourceValidator (SourceT), which defines the source and its configurable options.
|
||||
In addition, they should provide in the init an Entry type (EntryT), which is the entry that
|
||||
will be returned after downloading.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
config_options: ConfigOptions,
|
||||
preset_options: Preset,
|
||||
):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
name: str
|
||||
Name of the subscription
|
||||
config_options: ConfigOptions
|
||||
preset_options: Preset
|
||||
"""
|
||||
self.name = name
|
||||
self.__config_options = config_options
|
||||
self.__preset_options = preset_options
|
||||
|
||||
self._enhanced_download_archive = EnhancedDownloadArchive(
|
||||
subscription_name=name,
|
||||
working_directory=self.working_directory,
|
||||
output_directory=self.output_directory,
|
||||
)
|
||||
|
||||
@property
|
||||
def downloader_class(self) -> Type[Downloader]:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
This subscription's downloader class
|
||||
"""
|
||||
return self.__preset_options.downloader
|
||||
|
||||
@property
|
||||
def downloader_options(self) -> DownloaderValidator:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The download options for this subscription's downloader
|
||||
"""
|
||||
return self.__preset_options.downloader_options
|
||||
|
||||
@property
|
||||
def plugins(self) -> PresetPlugins:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
List of tuples containing (plugin class, plugin options)
|
||||
"""
|
||||
return self.__preset_options.plugins
|
||||
|
||||
@property
|
||||
def ytdl_options(self) -> YTDLOptions:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
YTDL options for this subscription
|
||||
"""
|
||||
return self.__preset_options.ytdl_options
|
||||
|
||||
@property
|
||||
def output_options(self) -> OutputOptions:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The output options defined for this subscription
|
||||
"""
|
||||
return self.__preset_options.output_options
|
||||
|
||||
@property
|
||||
def overrides(self) -> Overrides:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The overrides defined for this subscription
|
||||
"""
|
||||
return self.__preset_options.overrides
|
||||
|
||||
@property
|
||||
def working_directory(self) -> str:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The directory that the downloader saves files to
|
||||
"""
|
||||
return str(Path(self.__config_options.working_directory) / Path(self.name))
|
||||
|
||||
@property
|
||||
def output_directory(self) -> str:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
The formatted output directory
|
||||
"""
|
||||
return self.overrides.apply_formatter(formatter=self.output_options.output_directory)
|
||||
|
||||
@property
|
||||
def maintain_download_archive(self) -> bool:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
Whether to maintain a download archive
|
||||
"""
|
||||
return (
|
||||
self.output_options.maintain_download_archive
|
||||
and self.downloader_class.supports_download_archive
|
||||
)
|
||||
|
||||
def _move_entry_files_to_output_directory(
|
||||
self,
|
||||
dry_run: bool,
|
||||
entry: Entry,
|
||||
entry_metadata: Optional[FileMetadata] = None,
|
||||
):
|
||||
"""
|
||||
Helper function to move the media file and optionally thumbnail file to the output directory
|
||||
for a single entry.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
dry_run
|
||||
Whether this session is a dry-run or not
|
||||
entry:
|
||||
The entry with files to move
|
||||
entry_metadata
|
||||
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, entry=entry
|
||||
)
|
||||
self._enhanced_download_archive.save_file_to_output_directory(
|
||||
file_name=entry.get_download_file_name(),
|
||||
file_metadata=entry_metadata,
|
||||
output_file_name=output_file_name,
|
||||
entry=entry,
|
||||
)
|
||||
|
||||
# TODO: see if entry even has a thumbnail
|
||||
if self.output_options.thumbnail_name:
|
||||
output_thumbnail_name = self.overrides.apply_formatter(
|
||||
formatter=self.output_options.thumbnail_name, entry=entry
|
||||
)
|
||||
|
||||
# We always convert entry thumbnails to jpgs, and is performed here
|
||||
if not dry_run:
|
||||
convert_download_thumbnail(entry=entry)
|
||||
|
||||
self._enhanced_download_archive.save_file_to_output_directory(
|
||||
file_name=entry.get_download_thumbnail_name(),
|
||||
output_file_name=output_thumbnail_name,
|
||||
entry=entry,
|
||||
)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _prepare_working_directory(self):
|
||||
"""
|
||||
Context manager to create all directories to the working directory. Deletes the entire
|
||||
working directory when cleaning up.
|
||||
"""
|
||||
os.makedirs(self.working_directory, exist_ok=True)
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
shutil.rmtree(self.working_directory)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _maintain_archive_file(self):
|
||||
"""
|
||||
Context manager to initialize the enhanced download archive
|
||||
"""
|
||||
if self.maintain_download_archive:
|
||||
self._enhanced_download_archive.prepare_download_archive()
|
||||
|
||||
yield
|
||||
|
||||
# If output options maintains stale file deletion, perform the delete here prior to saving
|
||||
# the download archive
|
||||
if self.maintain_download_archive:
|
||||
date_range_to_keep = to_date_range(
|
||||
before=self.output_options.keep_files_before,
|
||||
after=self.output_options.keep_files_after,
|
||||
overrides=self.overrides,
|
||||
)
|
||||
if date_range_to_keep:
|
||||
self._enhanced_download_archive.remove_stale_files(date_range=date_range_to_keep)
|
||||
|
||||
self._enhanced_download_archive.save_download_mappings()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _subscription_download_context_managers(self) -> None:
|
||||
with (
|
||||
self._prepare_working_directory(),
|
||||
self._maintain_archive_file(),
|
||||
):
|
||||
yield
|
||||
|
||||
def _initialize_plugins(self) -> List[Plugin]:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
List of plugins defined in the subscription, initialized and ready to use.
|
||||
"""
|
||||
plugins: List[Plugin] = []
|
||||
for plugin_type, plugin_options in self.plugins.zipped():
|
||||
plugin = plugin_type(
|
||||
plugin_options=plugin_options,
|
||||
overrides=self.overrides,
|
||||
enhanced_download_archive=self._enhanced_download_archive,
|
||||
)
|
||||
|
||||
plugins.append(plugin)
|
||||
|
||||
return plugins
|
||||
|
||||
def _post_process_entry(
|
||||
self, plugins: List[Plugin], dry_run: bool, entry: Entry, entry_metadata: FileMetadata
|
||||
):
|
||||
# Post-process the entry with all plugins
|
||||
for plugin in sorted(plugins, key=lambda _plugin: _plugin.priority.post_process):
|
||||
optional_plugin_entry_metadata = plugin.post_process_entry(entry)
|
||||
if optional_plugin_entry_metadata:
|
||||
entry_metadata.extend(optional_plugin_entry_metadata)
|
||||
|
||||
# Then, move it to the output directory
|
||||
self._move_entry_files_to_output_directory(
|
||||
dry_run=dry_run, entry=entry, entry_metadata=entry_metadata
|
||||
)
|
||||
|
||||
# Re-save the download archive after each entry is moved to the output directory
|
||||
if self.maintain_download_archive:
|
||||
self._enhanced_download_archive.save_download_mappings()
|
||||
|
||||
def _process_entry(
|
||||
self, plugins: List[Plugin], dry_run: bool, entry: Entry, entry_metadata: FileMetadata
|
||||
) -> None:
|
||||
# First, modify the entry with all plugins
|
||||
for plugin in sorted(plugins, key=lambda _plugin: _plugin.priority.modify_entry):
|
||||
# Return if it is None, it is indicated to not process any further
|
||||
if (entry := plugin.modify_entry(entry)) is None:
|
||||
return
|
||||
|
||||
self._post_process_entry(
|
||||
plugins=plugins, dry_run=dry_run, entry=entry, entry_metadata=entry_metadata
|
||||
)
|
||||
|
||||
def _process_split_entry(
|
||||
self, split_plugin: Plugin, plugins: List[Plugin], dry_run: bool, entry: Entry
|
||||
) -> None:
|
||||
plugins_pre_split = sorted(
|
||||
[plugin for plugin in plugins if not plugin.priority.modify_entry_after_split],
|
||||
key=lambda _plugin: _plugin.priority.modify_entry,
|
||||
)
|
||||
|
||||
plugins_post_split = sorted(
|
||||
[plugin for plugin in plugins if plugin.priority.modify_entry_after_split],
|
||||
key=lambda _plugin: _plugin.priority.modify_entry,
|
||||
)
|
||||
|
||||
# First, modify the entry with pre_split plugins
|
||||
for plugin in plugins_pre_split:
|
||||
# Return if it is None, it is indicated to not process any further
|
||||
if (entry := plugin.modify_entry(entry)) is None:
|
||||
return
|
||||
|
||||
# Then, perform the split
|
||||
for split_entry, split_entry_metadata in split_plugin.split(entry=entry):
|
||||
|
||||
for plugin in plugins_post_split:
|
||||
# Return if it is None, it is indicated to not process any further.
|
||||
# Break out of the plugin loop
|
||||
if (split_entry := plugin.modify_entry(split_entry)) is None:
|
||||
break
|
||||
|
||||
# If split_entry is None from modify_entry, do not post process
|
||||
if split_entry:
|
||||
self._process_entry(
|
||||
plugins=plugins,
|
||||
dry_run=dry_run,
|
||||
entry=split_entry,
|
||||
entry_metadata=split_entry_metadata,
|
||||
)
|
||||
|
||||
def download(self, dry_run: bool = False) -> FileHandlerTransactionLog:
|
||||
"""
|
||||
Performs the subscription download
|
||||
|
||||
Parameters
|
||||
----------
|
||||
dry_run
|
||||
If true, do not download any video/audio files or move anything to the output
|
||||
directory.
|
||||
"""
|
||||
self._enhanced_download_archive.reinitialize(dry_run=dry_run)
|
||||
plugins = self._initialize_plugins()
|
||||
|
||||
ytdl_options_builder = SubscriptionYTDLOptions(
|
||||
preset=self.__preset_options,
|
||||
plugins=plugins,
|
||||
enhanced_download_archive=self._enhanced_download_archive,
|
||||
working_directory=self.working_directory,
|
||||
dry_run=dry_run,
|
||||
).builder()
|
||||
|
||||
with self._subscription_download_context_managers():
|
||||
downloader = self.downloader_class(
|
||||
download_options=self.downloader_options,
|
||||
enhanced_download_archive=self._enhanced_download_archive,
|
||||
ytdl_options_builder=ytdl_options_builder,
|
||||
)
|
||||
|
||||
for entry in downloader.download():
|
||||
entry_metadata = FileMetadata()
|
||||
if isinstance(entry, tuple):
|
||||
entry, entry_metadata = entry
|
||||
|
||||
if split_plugin := _get_split_plugin(plugins):
|
||||
self._process_split_entry(
|
||||
split_plugin=split_plugin, plugins=plugins, dry_run=dry_run, entry=entry
|
||||
)
|
||||
else:
|
||||
self._process_entry(
|
||||
plugins=plugins, dry_run=dry_run, entry=entry, entry_metadata=entry_metadata
|
||||
)
|
||||
|
||||
downloader.post_download(overrides=self.overrides)
|
||||
for plugin in plugins:
|
||||
plugin.post_process_subscription()
|
||||
|
||||
return self._enhanced_download_archive.get_file_handler_transaction_log()
|
||||
|
||||
class Subscription(SubscriptionDownload):
|
||||
@classmethod
|
||||
def from_preset(cls, preset: Preset, config: ConfigFile) -> "Subscription":
|
||||
"""
|
||||
|
|
|
|||
271
src/ytdl_sub/subscriptions/subscription_download.py
Normal file
271
src/ytdl_sub/subscriptions/subscription_download.py
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
import contextlib
|
||||
import os
|
||||
import shutil
|
||||
from abc import ABC
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
from ytdl_sub.entries.entry import Entry
|
||||
from ytdl_sub.plugins.plugin import Plugin
|
||||
from ytdl_sub.subscriptions.base_subscription import BaseSubscription
|
||||
from ytdl_sub.subscriptions.subscription_ytdl_options import SubscriptionYTDLOptions
|
||||
from ytdl_sub.utils.datetime import to_date_range
|
||||
from ytdl_sub.utils.exceptions import ValidationException
|
||||
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
||||
from ytdl_sub.utils.file_handler import FileMetadata
|
||||
from ytdl_sub.utils.thumbnail import convert_download_thumbnail
|
||||
|
||||
|
||||
def _get_split_plugin(plugins: List[Plugin]) -> Optional[Plugin]:
|
||||
split_plugins = [plugin for plugin in plugins if plugin.is_split_plugin]
|
||||
|
||||
if len(split_plugins) == 1:
|
||||
return split_plugins[0]
|
||||
if len(split_plugins) > 1:
|
||||
raise ValidationException("Can not use more than one split plugins at a time")
|
||||
return None
|
||||
|
||||
|
||||
class SubscriptionDownload(BaseSubscription, ABC):
|
||||
"""
|
||||
Handles the subscription download logic
|
||||
"""
|
||||
|
||||
def _move_entry_files_to_output_directory(
|
||||
self,
|
||||
dry_run: bool,
|
||||
entry: Entry,
|
||||
entry_metadata: Optional[FileMetadata] = None,
|
||||
):
|
||||
"""
|
||||
Helper function to move the media file and optionally thumbnail file to the output directory
|
||||
for a single entry.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
dry_run
|
||||
Whether this session is a dry-run or not
|
||||
entry:
|
||||
The entry with files to move
|
||||
entry_metadata
|
||||
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, entry=entry
|
||||
)
|
||||
self._enhanced_download_archive.save_file_to_output_directory(
|
||||
file_name=entry.get_download_file_name(),
|
||||
file_metadata=entry_metadata,
|
||||
output_file_name=output_file_name,
|
||||
entry=entry,
|
||||
)
|
||||
|
||||
# TODO: see if entry even has a thumbnail
|
||||
if self.output_options.thumbnail_name:
|
||||
output_thumbnail_name = self.overrides.apply_formatter(
|
||||
formatter=self.output_options.thumbnail_name, entry=entry
|
||||
)
|
||||
|
||||
# We always convert entry thumbnails to jpgs, and is performed here
|
||||
if not dry_run:
|
||||
convert_download_thumbnail(entry=entry)
|
||||
|
||||
self._enhanced_download_archive.save_file_to_output_directory(
|
||||
file_name=entry.get_download_thumbnail_name(),
|
||||
output_file_name=output_thumbnail_name,
|
||||
entry=entry,
|
||||
)
|
||||
|
||||
if self.output_options.info_json_name:
|
||||
output_info_json_name = self.overrides.apply_formatter(
|
||||
formatter=self.output_options.info_json_name, entry=entry
|
||||
)
|
||||
|
||||
# if not dry-run, write the info json
|
||||
if not dry_run:
|
||||
entry.write_info_json()
|
||||
|
||||
self._enhanced_download_archive.save_file_to_output_directory(
|
||||
file_name=entry.get_download_info_json_name(),
|
||||
output_file_name=output_info_json_name,
|
||||
entry=entry,
|
||||
)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _prepare_working_directory(self):
|
||||
"""
|
||||
Context manager to create all directories to the working directory. Deletes the entire
|
||||
working directory when cleaning up.
|
||||
"""
|
||||
os.makedirs(self.working_directory, exist_ok=True)
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
shutil.rmtree(self.working_directory)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _maintain_archive_file(self):
|
||||
"""
|
||||
Context manager to initialize the enhanced download archive
|
||||
"""
|
||||
if self.maintain_download_archive:
|
||||
self._enhanced_download_archive.prepare_download_archive()
|
||||
|
||||
yield
|
||||
|
||||
# If output options maintains stale file deletion, perform the delete here prior to saving
|
||||
# the download archive
|
||||
if self.maintain_download_archive:
|
||||
date_range_to_keep = to_date_range(
|
||||
before=self.output_options.keep_files_before,
|
||||
after=self.output_options.keep_files_after,
|
||||
overrides=self.overrides,
|
||||
)
|
||||
if date_range_to_keep:
|
||||
self._enhanced_download_archive.remove_stale_files(date_range=date_range_to_keep)
|
||||
|
||||
self._enhanced_download_archive.save_download_mappings()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _subscription_download_context_managers(self) -> None:
|
||||
with (
|
||||
self._prepare_working_directory(),
|
||||
self._maintain_archive_file(),
|
||||
):
|
||||
yield
|
||||
|
||||
def _initialize_plugins(self) -> List[Plugin]:
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
List of plugins defined in the subscription, initialized and ready to use.
|
||||
"""
|
||||
plugins: List[Plugin] = []
|
||||
for plugin_type, plugin_options in self.plugins.zipped():
|
||||
plugin = plugin_type(
|
||||
plugin_options=plugin_options,
|
||||
overrides=self.overrides,
|
||||
enhanced_download_archive=self._enhanced_download_archive,
|
||||
)
|
||||
|
||||
plugins.append(plugin)
|
||||
|
||||
return plugins
|
||||
|
||||
def _post_process_entry(
|
||||
self, plugins: List[Plugin], dry_run: bool, entry: Entry, entry_metadata: FileMetadata
|
||||
):
|
||||
# Post-process the entry with all plugins
|
||||
for plugin in sorted(plugins, key=lambda _plugin: _plugin.priority.post_process):
|
||||
optional_plugin_entry_metadata = plugin.post_process_entry(entry)
|
||||
if optional_plugin_entry_metadata:
|
||||
entry_metadata.extend(optional_plugin_entry_metadata)
|
||||
|
||||
# Then, move it to the output directory
|
||||
self._move_entry_files_to_output_directory(
|
||||
dry_run=dry_run, entry=entry, entry_metadata=entry_metadata
|
||||
)
|
||||
|
||||
# Re-save the download archive after each entry is moved to the output directory
|
||||
if self.maintain_download_archive:
|
||||
self._enhanced_download_archive.save_download_mappings()
|
||||
|
||||
def _process_entry(
|
||||
self, plugins: List[Plugin], dry_run: bool, entry: Entry, entry_metadata: FileMetadata
|
||||
) -> None:
|
||||
# First, modify the entry with all plugins
|
||||
for plugin in sorted(plugins, key=lambda _plugin: _plugin.priority.modify_entry):
|
||||
# Return if it is None, it is indicated to not process any further
|
||||
if (entry := plugin.modify_entry(entry)) is None:
|
||||
return
|
||||
|
||||
self._post_process_entry(
|
||||
plugins=plugins, dry_run=dry_run, entry=entry, entry_metadata=entry_metadata
|
||||
)
|
||||
|
||||
def _process_split_entry(
|
||||
self, split_plugin: Plugin, plugins: List[Plugin], dry_run: bool, entry: Entry
|
||||
) -> None:
|
||||
plugins_pre_split = sorted(
|
||||
[plugin for plugin in plugins if not plugin.priority.modify_entry_after_split],
|
||||
key=lambda _plugin: _plugin.priority.modify_entry,
|
||||
)
|
||||
|
||||
plugins_post_split = sorted(
|
||||
[plugin for plugin in plugins if plugin.priority.modify_entry_after_split],
|
||||
key=lambda _plugin: _plugin.priority.modify_entry,
|
||||
)
|
||||
|
||||
# First, modify the entry with pre_split plugins
|
||||
for plugin in plugins_pre_split:
|
||||
# Return if it is None, it is indicated to not process any further
|
||||
if (entry := plugin.modify_entry(entry)) is None:
|
||||
return
|
||||
|
||||
# Then, perform the split
|
||||
for split_entry, split_entry_metadata in split_plugin.split(entry=entry):
|
||||
|
||||
for plugin in plugins_post_split:
|
||||
# Return if it is None, it is indicated to not process any further.
|
||||
# Break out of the plugin loop
|
||||
if (split_entry := plugin.modify_entry(split_entry)) is None:
|
||||
break
|
||||
|
||||
# If split_entry is None from modify_entry, do not post process
|
||||
if split_entry:
|
||||
self._process_entry(
|
||||
plugins=plugins,
|
||||
dry_run=dry_run,
|
||||
entry=split_entry,
|
||||
entry_metadata=split_entry_metadata,
|
||||
)
|
||||
|
||||
def download(self, dry_run: bool = False) -> FileHandlerTransactionLog:
|
||||
"""
|
||||
Performs the subscription download
|
||||
|
||||
Parameters
|
||||
----------
|
||||
dry_run
|
||||
If true, do not download any video/audio files or move anything to the output
|
||||
directory.
|
||||
"""
|
||||
self._enhanced_download_archive.reinitialize(dry_run=dry_run)
|
||||
plugins = self._initialize_plugins()
|
||||
|
||||
ytdl_options_builder = SubscriptionYTDLOptions(
|
||||
preset=self._preset_options,
|
||||
plugins=plugins,
|
||||
enhanced_download_archive=self._enhanced_download_archive,
|
||||
working_directory=self.working_directory,
|
||||
dry_run=dry_run,
|
||||
).builder()
|
||||
|
||||
with self._subscription_download_context_managers():
|
||||
downloader = self.downloader_class(
|
||||
download_options=self.downloader_options,
|
||||
enhanced_download_archive=self._enhanced_download_archive,
|
||||
ytdl_options_builder=ytdl_options_builder,
|
||||
)
|
||||
|
||||
for entry in downloader.download():
|
||||
entry_metadata = FileMetadata()
|
||||
if isinstance(entry, tuple):
|
||||
entry, entry_metadata = entry
|
||||
|
||||
if split_plugin := _get_split_plugin(plugins):
|
||||
self._process_split_entry(
|
||||
split_plugin=split_plugin, plugins=plugins, dry_run=dry_run, entry=entry
|
||||
)
|
||||
else:
|
||||
self._process_entry(
|
||||
plugins=plugins, dry_run=dry_run, entry=entry, entry_metadata=entry_metadata
|
||||
)
|
||||
|
||||
downloader.post_download(overrides=self.overrides)
|
||||
for plugin in plugins:
|
||||
plugin.post_process_subscription()
|
||||
|
||||
return self._enhanced_download_archive.get_file_handler_transaction_log()
|
||||
|
|
@ -54,6 +54,7 @@ class ExpectedDownloads:
|
|||
):
|
||||
"""
|
||||
Assert each expected file exists and that its respective md5 hash matches.
|
||||
Ignores .info.json files by default since metadata can easily change
|
||||
"""
|
||||
if ignore_md5_hashes_for is None:
|
||||
ignore_md5_hashes_for = []
|
||||
|
|
@ -66,12 +67,11 @@ class ExpectedDownloads:
|
|||
assert len(relative_file_paths) == self.file_count, "Mismatch in number of created files"
|
||||
|
||||
for expected_download in self.expected_downloads:
|
||||
full_path = Path(relative_directory) / expected_download.path
|
||||
assert os.path.isfile(
|
||||
full_path
|
||||
), f"Expected {str(expected_download.path)} to be a file but it is not"
|
||||
path = str(expected_download.path)
|
||||
full_path = Path(relative_directory) / path
|
||||
assert os.path.isfile(full_path), f"Expected {path} to be a file but it is not"
|
||||
|
||||
if str(expected_download.path) in ignore_md5_hashes_for:
|
||||
if path in ignore_md5_hashes_for or path.endswith(".info.json"):
|
||||
continue
|
||||
|
||||
md5_hash = _get_file_md5_hash(full_file_path=full_path)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
{
|
||||
".ytdl-sub-recent-download-archive.json": "756b60d7a6c47d8163e3283404493a8d",
|
||||
".ytdl-sub-recent-download-archive.json": "23520634f7908081f3d1333a1441a578",
|
||||
"Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer-thumb.jpg": "705ca4e0d99b37e9ecdf6bfe4b90c59b",
|
||||
"Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer.info.json": "ad68ce82c78f1a0a757fc1fbd4d6b531",
|
||||
"Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer.mp4": "82f6ee7253e1dbb83ae7215af08ffacc",
|
||||
"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.en.srt": "3d2c4e7f65d2ca5e96da38ce7eecfc4e",
|
||||
"Season 2018/s2018.e1102 - Jesse's Minecraft Server | IP mc.jesse.id.info.json": "b085dea1c06975464acc5bdc1b950e4c",
|
||||
"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": "d9114d43d87907b2afc06eb089a8ac0a",
|
||||
"fanart.jpg": "129c6639b47299bc48062f0365e670ee",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
".ytdl-sub-recent-download-archive.json": "68e164a35d1541ce761a6d7fef19ee08",
|
||||
".ytdl-sub-recent-download-archive.json": "3bbf72c014d055ecf672c8ea603140f7",
|
||||
"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.info.json": "b085dea1c06975464acc5bdc1b950e4c",
|
||||
"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": "d9114d43d87907b2afc06eb089a8ac0a",
|
||||
"fanart.jpg": "129c6639b47299bc48062f0365e670ee",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,15 @@
|
|||
{
|
||||
"01. Intro (Feat. Racheal Ofori & Barney Artist).info.json": "4ec926da9b74eee3f9bf9e1895d3af30",
|
||||
"02. Answers (Feat. Rick David & Kaya Thomas - Dyke).info.json": "ae183114899d0b7bac8bfa01a09f6b36",
|
||||
"03. Blaze (Feat. Kaya Thomas - Dyke).info.json": "86bff845b087dfc240d63a65afed9117",
|
||||
"04. What If (Interlude).info.json": "d2e92735fcb8eaccaf2e28c8f2a0ab1f",
|
||||
"05. No Peace (Feat. Tom Misch).info.json": "ed2a9ec8a833dd90bb6681d62c68ff3a",
|
||||
"06. Closer (Feat. Lester Duval).info.json": "aa29efd172bb6afd27e948761087d01e",
|
||||
"07. Delusions: Rumination (Interlude) (Feat. Racheal Ofori).info.json": "79c0626f28d0a02074f14eac90e0670e",
|
||||
"08. Dreams (Feat. Carmody).info.json": "c7bb7c910c0d74492ef62e4a32922f17",
|
||||
"09. Dreaming (Interlude) (Feat. Racheal Ofori).info.json": "077efa0aa1d25afb05fb3285f139b77b",
|
||||
"10. Hopeful (Feat. Jordan Rakei).info.json": "4541ba99f9b683f34652a60bc3c34ff5",
|
||||
"11. Sunrise (Pillows) (Feat. Emmavie).info.json": "41a7ceb520a7d9e6736cba3f4be6a9c4",
|
||||
"Alfa Mist - Nocturne [Full Album]/01 - 01. Intro (Feat. Racheal Ofori & Barney Artist).mp3": "ddc24257729f24055bf1b8dc06f8224c",
|
||||
"Alfa Mist - Nocturne [Full Album]/02 - 02. Answers (Feat. Rick David & Kaya Thomas - Dyke).mp3": "10dd7f13c469bd51ffcf3f8ff3ed3d69",
|
||||
"Alfa Mist - Nocturne [Full Album]/03 - 03. Blaze (Feat. Kaya Thomas - Dyke).mp3": "376722aaf08d1ef6dcba0aaf7a3b7a79",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"Oblivion Mod \"Falcor\" p.1.info.json": "358e43cb1c00e7f4c960690994092e9e",
|
||||
"Oblivion Mod \"Falcor\" p.1/01 - Oblivion Mod \"Falcor\" p.1.mp3": "703ffb93964ac025ee66221b98ee4d49",
|
||||
"Oblivion Mod \"Falcor\" p.1/folder.jpg": "fb95b510681676e81c321171fc23143e"
|
||||
}
|
||||
|
|
@ -1,4 +1,13 @@
|
|||
{
|
||||
"Answers (Feat. Rick David & Kaya Thomas - Dyke).info.json": "b4d6ae81fd1313c5ce962e88ab027782",
|
||||
"Blaze (Feat. Kaya Thomas - Dyke).info.json": "05857a128e229ac9b364e8f5f09ff4b4",
|
||||
"Closer (Feat. Lester Duval).info.json": "00c3e84f0b5506051ed31071208ff33d",
|
||||
"Delusions: Rumination (Interlude) (Feat. Racheal Ofori).info.json": "b360033980fda9bcc3056aed2c3265b1",
|
||||
"Dreaming (Interlude) (Feat. Racheal Ofori).info.json": "fd3df9675d2b01ad88a6d12d75ab526e",
|
||||
"Dreams (Feat. Carmody).info.json": "402fb9066d25db35f801a6b94524bb38",
|
||||
"Hopeful (Feat. Jordan Rakei).info.json": "ab750e7c550aac184f3ef82c3cf64212",
|
||||
"Intro (Feat. Racheal Ofori & Barney Artist).info.json": "0777ecaaac9ffb1c4df3989e618dedf7",
|
||||
"No Peace (Feat. Tom Misch).info.json": "0de13987489e4a8f9a94dd91dcbc4cf5",
|
||||
"Nocturne/01 - Intro (Feat. Racheal Ofori & Barney Artist).mp3": "15157be58e0f72485de1e7e10961321f",
|
||||
"Nocturne/02 - Answers (Feat. Rick David & Kaya Thomas - Dyke).mp3": "28ff5e2dda45771f1c6d5265dd03095e",
|
||||
"Nocturne/03 - Blaze (Feat. Kaya Thomas - Dyke).mp3": "705d7fc1799c1a14aa0656bd6e2bf260",
|
||||
|
|
@ -10,5 +19,7 @@
|
|||
"Nocturne/09 - Dreaming (Interlude) (Feat. Racheal Ofori).mp3": "cd213379cf53a8c1e4eb9134da257cd8",
|
||||
"Nocturne/10 - Hopeful (Feat. Jordan Rakei).mp3": "ef7e06690ac849e1dd35771936d6ddb6",
|
||||
"Nocturne/11 - Sunrise (Pillows) (Feat. Emmavie).mp3": "56293fb70edc1ef7decc2d417d87ff1d",
|
||||
"Nocturne/folder.jpg": "bd3685acc53072e591bae2505ecb0648"
|
||||
"Nocturne/folder.jpg": "bd3685acc53072e591bae2505ecb0648",
|
||||
"Sunrise (Pillows) (Feat. Emmavie).info.json": "09f9975e3355e2d5888c07900f54f895",
|
||||
"What If (Interlude).info.json": "328c05aeb45892d7ad6368e81d0947a5"
|
||||
}
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
{
|
||||
"Jesse's Minecraft Server [Trailer - Feb.1].info.json": "0a484e88b6d37b20017615aa6df3b5d9",
|
||||
"Jesse's Minecraft Server [Trailer - Feb.1].ogg": "16f66f1d81541a1f18b62bc08ad01d16",
|
||||
"Jesse's Minecraft Server [Trailer - Feb.27].info.json": "807c9f35c97beac21d8b3ca64df50860",
|
||||
"Jesse's Minecraft Server [Trailer - Feb.27].ogg": "6f322d842a1e43b4c9981b42423d9b4c",
|
||||
"Jesse's Minecraft Server [Trailer - Mar.21].info.json": "b87e89258f04fb3acb669e5b1e0fa468",
|
||||
"Jesse's Minecraft Server [Trailer - Mar.21].ogg": "fe1dc3361a102661c27020d5b95a2a81"
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
"YouTube Rewind 2019: For the Record | #YouTubeRewind.info.json": "ebc30f897906fa0f04738089c7e7715d",
|
||||
"YouTube Rewind 2019: For the Record | #YouTubeRewind.mp3": "3a156b122bd79c956cce5079d3530cc3"
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"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.info.json": "89d611e4f61a72204b9658acd492468d",
|
||||
"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": "b9bd35e4f260c728774d8dd31f83637c"
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"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.info.json": "c0ad8fd87d9a70bd66ce069ae0acd490",
|
||||
"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": "b9bd35e4f260c728774d8dd31f83637c"
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind-thumb.jpg": "50ee47c80f679029f5d3503bb91b045a",
|
||||
"JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind.info.json": "eedd73b78d8a9d3b129049e87eb17b6c",
|
||||
"JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind.mp4": "8562853314b75c1e47abd4f5ba97315c",
|
||||
"JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind.nfo": "c64964fab07574080e5da3242e3bfd48"
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
"JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind-thumb.jpg": "50ee47c80f679029f5d3503bb91b045a",
|
||||
"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.info.json": "577038a7e5a9833ebbd33562f5d6b376",
|
||||
"JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind.mp4": "8562853314b75c1e47abd4f5ba97315c",
|
||||
"JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind.nfo": "c64964fab07574080e5da3242e3bfd48"
|
||||
}
|
||||
|
|
@ -1,19 +1,32 @@
|
|||
{
|
||||
".ytdl-sub-jb-download-archive.json": "1a99156e9ece62539fb2608416a07200",
|
||||
".ytdl-sub-jb-download-archive.json": "1114f07090dfb35eea6848efeaf9755a",
|
||||
"j_b/[2021] Baby Santana's Dorian Groove/01 - Baby Santana's Dorian Groove.info.json": "8f736aac8bbaa732e89efdcb61c3bc3d",
|
||||
"j_b/[2021] Baby Santana's Dorian Groove/01 - Baby Santana's Dorian Groove.mp3": "bffbd558e12c6a9e029dc136a88342c4",
|
||||
"j_b/[2021] Baby Santana's Dorian Groove/folder.jpg": "967892be44b8c47e1be73f055a7c6f08",
|
||||
"j_b/[2021] Purple Clouds/01 - Purple Clouds.info.json": "da0a923a6d0272e4dc726ac997360b28",
|
||||
"j_b/[2021] Purple Clouds/01 - Purple Clouds.mp3": "038db58aebe2ba875b733932b42a94d6",
|
||||
"j_b/[2021] Purple Clouds/folder.jpg": "967892be44b8c47e1be73f055a7c6f08",
|
||||
"j_b/[2022] Acoustic Treats/01 - 20160426 184214.info.json": "f952fe0830f106fac031d18022213892",
|
||||
"j_b/[2022] Acoustic Treats/01 - 20160426 184214.mp3": "e145f0a2f6012768280c38655ca58065",
|
||||
"j_b/[2022] Acoustic Treats/02 - 20160502 123150.info.json": "63f5a211448f95c78b866f048e4f2fc9",
|
||||
"j_b/[2022] Acoustic Treats/02 - 20160502 123150.mp3": "60c8b8817a197a13e4bb90903af612c5",
|
||||
"j_b/[2022] Acoustic Treats/03 - 20160504 143832.info.json": "a363365339ad3ab5139a51a6a6ed256d",
|
||||
"j_b/[2022] Acoustic Treats/03 - 20160504 143832.mp3": "8265b7e4f79878af877bc6ecd9757efe",
|
||||
"j_b/[2022] Acoustic Treats/04 - 20160601 221234.info.json": "9a1901fbd9761e29173b0fda5654e908",
|
||||
"j_b/[2022] Acoustic Treats/04 - 20160601 221234.mp3": "accf46b76891d2954b893d0f91d82816",
|
||||
"j_b/[2022] Acoustic Treats/05 - 20160601 222440.info.json": "886716e85228d46ce981aa0ba7bc2947",
|
||||
"j_b/[2022] Acoustic Treats/05 - 20160601 222440.mp3": "e1f584f523336160d5c1104a61de77f3",
|
||||
"j_b/[2022] Acoustic Treats/06 - 20170604 190236.info.json": "d23e769c83bc3303709b64432400a60d",
|
||||
"j_b/[2022] Acoustic Treats/06 - 20170604 190236.mp3": "f6885b25901177f0357649afe97328cc",
|
||||
"j_b/[2022] Acoustic Treats/07 - 20170612 193646.info.json": "04f467b4903aa9f090dc705db432edf2",
|
||||
"j_b/[2022] Acoustic Treats/07 - 20170612 193646.mp3": "fa057f221cbe4cf2442cd2fdb960743e",
|
||||
"j_b/[2022] Acoustic Treats/08 - 20170628 215206.info.json": "92f027ec29f445566242a82c27ed76ad",
|
||||
"j_b/[2022] Acoustic Treats/08 - 20170628 215206.mp3": "7794ae812c64580e2ac8fc457d5cc85f",
|
||||
"j_b/[2022] Acoustic Treats/09 - Finding Home.info.json": "f7458eaab93903e1568734ea1b33ee21",
|
||||
"j_b/[2022] Acoustic Treats/09 - Finding Home.mp3": "adbf02eddb2090c008eb497d13ff84b9",
|
||||
"j_b/[2022] Acoustic Treats/10 - Shallow Water WIP.info.json": "553e676f982b364493fd323e6d886e05",
|
||||
"j_b/[2022] Acoustic Treats/10 - Shallow Water WIP.mp3": "65bb10c84366c71498161734f953e93d",
|
||||
"j_b/[2022] Acoustic Treats/11 - Untold History.info.json": "8abbad5d0821ed6c64dc632b49a133ec",
|
||||
"j_b/[2022] Acoustic Treats/11 - Untold History.mp3": "6904b2918e5dc38d9a9f72d967eb74bf",
|
||||
"j_b/[2022] Acoustic Treats/folder.jpg": "967892be44b8c47e1be73f055a7c6f08"
|
||||
}
|
||||
|
|
@ -1,40 +1,52 @@
|
|||
{
|
||||
".ytdl-sub-pz-download-archive.json": "f0f692ef29653ca8d0af556415bfb065",
|
||||
".ytdl-sub-pz-download-archive.json": "70615451318cdb5e018e007c77893a39",
|
||||
"Season 2010/s2010.e0813 - Oblivion Mod "Falcor" p.1-thumb.jpg": "fb95b510681676e81c321171fc23143e",
|
||||
"Season 2010/s2010.e0813 - Oblivion Mod "Falcor" p.1.info.json": "5d16096bc4256239932db2c2d98161a3",
|
||||
"Season 2010/s2010.e0813 - Oblivion Mod "Falcor" p.1.mp4": "931a705864c57d21d6fedebed4af6bbc",
|
||||
"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.info.json": "514d2c3eab500f9541910d7573cb495e",
|
||||
"Season 2010/s2010.e1202 - Oblivion Mod "Falcor" p.2.mp4": "d3469b4dca7139cb3dbc38712b6796bf",
|
||||
"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].info.json": "f9ccc0b104551ecd7b737451051118aa",
|
||||
"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": "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].info.json": "6ad149bea780198512d73f075722ef82",
|
||||
"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": "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].info.json": "d040165f8ebc346c202605b19ed8e46b",
|
||||
"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": "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).info.json": "331af1c81b1bfa1df62179067a5f06b0",
|
||||
"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": "11a0e8754c414875bcd454358683da5f",
|
||||
"Season 2011/s2011.e0630 - Project Zombie |Fin|-thumb.jpg": "00ed383591779ffe98291de60f198fe9",
|
||||
"Season 2011/s2011.e0630 - Project Zombie |Fin|.info.json": "07d6b5e38b8e03cbe0f1ca795f266a43",
|
||||
"Season 2011/s2011.e0630 - Project Zombie |Fin|.mp4": "4971cb2d4fa29460361031f3fa8e1ea9",
|
||||
"Season 2011/s2011.e0630 - Project Zombie |Fin|.nfo": "a464b9c8c48a9a5d4776436d8108f8f5",
|
||||
"Season 2011/s2011.e1121 - Skyrim 'Ultra HD w⧸Mods' [PC]-thumb.jpg": "1718599d5189c65f7d8cf6acfa5ea851",
|
||||
"Season 2011/s2011.e1121 - Skyrim 'Ultra HD w⧸Mods' [PC].info.json": "e804c82258849767d5f006627e66ac8f",
|
||||
"Season 2011/s2011.e1121 - Skyrim 'Ultra HD w⧸Mods' [PC].mp4": "55e9b0add08c48c9c66105da0def2426",
|
||||
"Season 2011/s2011.e1121 - Skyrim 'Ultra HD w⧸Mods' [PC].nfo": "3562934ab9a5e802d955eda24ad355de",
|
||||
"Season 2012/s2012.e0123 - Project Zombie |Map Trailer|-thumb.jpg": "54ebe9df801b278fdd17b21afa8373a6",
|
||||
"Season 2012/s2012.e0123 - Project Zombie |Map Trailer|.info.json": "d81b260f37fbf8499d6ba716f5d1c42a",
|
||||
"Season 2012/s2012.e0123 - Project Zombie |Map Trailer|.mp4": "65e4ce53ed5ec4139995469f99477a50",
|
||||
"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|.info.json": "4e450b92e57f8c99033b49e3326cdf72",
|
||||
"Season 2013/s2013.e0719 - Project Zombie Rewind |Trailer|.mp4": "18620a8257a686beda65e54add4d4cd1",
|
||||
"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.info.json": "01a181a123d1b92d016dced5615ca67a",
|
||||
"Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer.mp4": "82f6ee7253e1dbb83ae7215af08ffacc",
|
||||
"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.en.srt": "3d2c4e7f65d2ca5e96da38ce7eecfc4e",
|
||||
"Season 2018/s2018.e1102 - Jesse's Minecraft Server | IP mc.jesse.id.info.json": "2ca573a5eb58e92df951be0a05e8e49c",
|
||||
"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": "d9114d43d87907b2afc06eb089a8ac0a",
|
||||
"fanart.jpg": "129c6639b47299bc48062f0365e670ee",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"JMC - Jesse's Minecraft Server-thumb.jpg": "a3f1910f9c51f6442f845a528e190829",
|
||||
"JMC - Jesse's Minecraft Server.info.json": "604a3e1f5b0f27524772a90869fbbe06",
|
||||
"JMC - Jesse's Minecraft Server.mkv": "21f246b1c922e11add509ea26c43c53d",
|
||||
"JMC - Jesse's Minecraft Server.nfo": "d16396c60b63c06a4f2c9239553bdf61"
|
||||
}
|
||||
|
|
@ -1,12 +1,15 @@
|
|||
{
|
||||
".ytdl-sub-music_video_playlist_test-download-archive.json": "9f785c29194a6ecfba6a6b4018763ddc",
|
||||
".ytdl-sub-music_video_playlist_test-download-archive.json": "25b8e44961343116436584e341c7fe9b",
|
||||
"JMC - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg": "b232d253df621aa770b780c1301d364d",
|
||||
"JMC - Jesse's Minecraft Server [Trailer - Feb.1].info.json": "7c870aea7df6733ddac1adc190797d6a",
|
||||
"JMC - Jesse's Minecraft Server [Trailer - Feb.1].mp4": "e66287b9832277b6a4d1554e29d9fdcc",
|
||||
"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].info.json": "e1d9ce4f91d4657468bb10de829a76e8",
|
||||
"JMC - Jesse's Minecraft Server [Trailer - Feb.27].mp4": "04ab5cb3cc12325d0c96a7cd04a8b91d",
|
||||
"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].info.json": "c601d4f904e45c8cc73e78c5873eba08",
|
||||
"JMC - Jesse's Minecraft Server [Trailer - Mar.21].mp4": "025de6099a5c98e6397153c7a62d517d",
|
||||
"JMC - Jesse's Minecraft Server [Trailer - Mar.21].nfo": "f000a6ed8caacb62a134a6ca81e3f308"
|
||||
}
|
||||
|
|
@ -6,15 +6,21 @@
|
|||
"Project Zombie - 5-6.Part 4.mp4": "de9afd0b6fa5f845b086e2d4915f2f14",
|
||||
"Project Zombie - 6-6.Part 5.mp4": "d485c2a5df9a2d5bf9037a7a96a04d10",
|
||||
"Project Zombie - Intro-thumb.jpg": "fb95b510681676e81c321171fc23143e",
|
||||
"Project Zombie - Intro.info.json": "0aa234ae04a218f18c9aa2eb605ff078",
|
||||
"Project Zombie - Intro.nfo": "3a89bd4707e64b66a22d13bb7f81bf16",
|
||||
"Project Zombie - Part 1-thumb.jpg": "fb95b510681676e81c321171fc23143e",
|
||||
"Project Zombie - Part 1.info.json": "1d1a70bf1bd9753c7035c9069fb99859",
|
||||
"Project Zombie - Part 1.nfo": "a57a3a98d0b9db655bc4a792953ab96e",
|
||||
"Project Zombie - Part 2-thumb.jpg": "fb95b510681676e81c321171fc23143e",
|
||||
"Project Zombie - Part 2.info.json": "2781ca7fae971ca7a2825a6f16f432aa",
|
||||
"Project Zombie - Part 2.nfo": "68921dd5386fd90e2c0640127dda5dc2",
|
||||
"Project Zombie - Part 3-thumb.jpg": "fb95b510681676e81c321171fc23143e",
|
||||
"Project Zombie - Part 3.info.json": "2d79048f59070044e519ab68ef469d62",
|
||||
"Project Zombie - Part 3.nfo": "4198aec2d4cd70e4cb3946f10be205a7",
|
||||
"Project Zombie - Part 4-thumb.jpg": "fb95b510681676e81c321171fc23143e",
|
||||
"Project Zombie - Part 4.info.json": "4074e8d1fa1c0fccf1fe55ba52cc46a1",
|
||||
"Project Zombie - Part 4.nfo": "8a87fa99c5ba5b11cd14c592a0b97dbb",
|
||||
"Project Zombie - Part 5-thumb.jpg": "fb95b510681676e81c321171fc23143e",
|
||||
"Project Zombie - Part 5.info.json": "48b519b3867bafb51c8f463293f357c3",
|
||||
"Project Zombie - Part 5.nfo": "73091166bd5bd3d65d7e481e317ed128"
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"JMC - Oblivion Mod "Falcor" p.1-thumb.jpg": "fb95b510681676e81c321171fc23143e",
|
||||
"JMC - Oblivion Mod "Falcor" p.1.info.json": "b7e3751fe749817392d2d69be71a391c",
|
||||
"JMC - Oblivion Mod "Falcor" p.1.mp4": "28c14cdac05c803efe71abb9454ab306",
|
||||
"JMC - Oblivion Mod "Falcor" p.1.nfo": "24cc4e17d2bebc89b2759ce5471d403e"
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ Files created in '{output_directory}'
|
|||
----------------------------------------
|
||||
.ytdl-sub-recent-download-archive.json
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer-thumb.jpg
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer.info.json
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer.mp4
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer.nfo
|
||||
NFO tags:
|
||||
|
|
@ -23,6 +24,7 @@ Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer.nfo
|
|||
year: 2018
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server | IP mc.jesse.id-thumb.jpg
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server | IP mc.jesse.id.en.srt
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server | IP mc.jesse.id.info.json
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server | IP mc.jesse.id.mp4
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server | IP mc.jesse.id.nfo
|
||||
NFO tags:
|
||||
|
|
|
|||
|
|
@ -11,5 +11,6 @@ tvshow.nfo
|
|||
Files removed from '{output_directory}'
|
||||
----------------------------------------
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer-thumb.jpg
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer.info.json
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer.mp4
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer.nfo
|
||||
|
|
@ -2,6 +2,7 @@ Files created in '{output_directory}'
|
|||
----------------------------------------
|
||||
Rick Beato - Can you hear the difference? 🎸🔥 #shorts-thumb.jpg
|
||||
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.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ Files created in '{output_directory}'
|
|||
----------------------------------------
|
||||
Rick Beato - Can you hear the difference? 🎸🔥 #shorts-thumb.jpg
|
||||
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.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
01. Intro (Feat. Racheal Ofori & Barney Artist).info.json
|
||||
02. Answers (Feat. Rick David & Kaya Thomas - Dyke).info.json
|
||||
03. Blaze (Feat. Kaya Thomas - Dyke).info.json
|
||||
04. What If (Interlude).info.json
|
||||
05. No Peace (Feat. Tom Misch).info.json
|
||||
06. Closer (Feat. Lester Duval).info.json
|
||||
07. Delusions: Rumination (Interlude) (Feat. Racheal Ofori).info.json
|
||||
08. Dreams (Feat. Carmody).info.json
|
||||
09. Dreaming (Interlude) (Feat. Racheal Ofori).info.json
|
||||
10. Hopeful (Feat. Jordan Rakei).info.json
|
||||
11. Sunrise (Pillows) (Feat. Emmavie).info.json
|
||||
Alfa Mist - Nocturne [Full Album]/01 - 01. Intro (Feat. Racheal Ofori & Barney Artist).mp3
|
||||
From Chapter Split:
|
||||
Warning: Dry-run assumes embedded chapters with no modifications
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
01. Intro (Feat. Racheal Ofori & Barney Artist).info.json
|
||||
02. Answers (Feat. Rick David & Kaya Thomas - Dyke).info.json
|
||||
03. Blaze (Feat. Kaya Thomas - Dyke).info.json
|
||||
04. What If (Interlude).info.json
|
||||
05. No Peace (Feat. Tom Misch).info.json
|
||||
06. Closer (Feat. Lester Duval).info.json
|
||||
07. Delusions: Rumination (Interlude) (Feat. Racheal Ofori).info.json
|
||||
08. Dreams (Feat. Carmody).info.json
|
||||
09. Dreaming (Interlude) (Feat. Racheal Ofori).info.json
|
||||
10. Hopeful (Feat. Jordan Rakei).info.json
|
||||
11. Sunrise (Pillows) (Feat. Emmavie).info.json
|
||||
Alfa Mist - Nocturne [Full Album]/01 - 01. Intro (Feat. Racheal Ofori & Barney Artist).mp3
|
||||
From Chapter Split:
|
||||
Source Title: Alfa Mist - Nocturne [Full Album]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
Oblivion Mod "Falcor" p.1.info.json
|
||||
Oblivion Mod "Falcor" p.1/01 - Oblivion Mod "Falcor" p.1.mp3
|
||||
Music Tags:
|
||||
album: Oblivion Mod "Falcor" p.1
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
Answers (Feat. Rick David & Kaya Thomas - Dyke).info.json
|
||||
Blaze (Feat. Kaya Thomas - Dyke).info.json
|
||||
Closer (Feat. Lester Duval).info.json
|
||||
Delusions: Rumination (Interlude) (Feat. Racheal Ofori).info.json
|
||||
Dreaming (Interlude) (Feat. Racheal Ofori).info.json
|
||||
Dreams (Feat. Carmody).info.json
|
||||
Hopeful (Feat. Jordan Rakei).info.json
|
||||
Intro (Feat. Racheal Ofori & Barney Artist).info.json
|
||||
No Peace (Feat. Tom Misch).info.json
|
||||
Nocturne/01 - Intro (Feat. Racheal Ofori & Barney Artist).mp3
|
||||
From Chapter Split:
|
||||
Warning: Dry-run assumes embedded chapters with no modifications
|
||||
|
|
@ -143,4 +152,6 @@ Nocturne/11 - Sunrise (Pillows) (Feat. Emmavie).mp3
|
|||
title: Sunrise (Pillows) (Feat. Emmavie)
|
||||
track: 11
|
||||
year: 2017
|
||||
Nocturne/folder.jpg
|
||||
Nocturne/folder.jpg
|
||||
Sunrise (Pillows) (Feat. Emmavie).info.json
|
||||
What If (Interlude).info.json
|
||||
|
|
@ -1,5 +1,14 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
Answers (Feat. Rick David & Kaya Thomas - Dyke).info.json
|
||||
Blaze (Feat. Kaya Thomas - Dyke).info.json
|
||||
Closer (Feat. Lester Duval).info.json
|
||||
Delusions: Rumination (Interlude) (Feat. Racheal Ofori).info.json
|
||||
Dreaming (Interlude) (Feat. Racheal Ofori).info.json
|
||||
Dreams (Feat. Carmody).info.json
|
||||
Hopeful (Feat. Jordan Rakei).info.json
|
||||
Intro (Feat. Racheal Ofori & Barney Artist).info.json
|
||||
No Peace (Feat. Tom Misch).info.json
|
||||
Nocturne/01 - Intro (Feat. Racheal Ofori & Barney Artist).mp3
|
||||
From Chapter Split:
|
||||
Source Title: Alfa Mist - Nocturne [Full Album]
|
||||
|
|
@ -132,4 +141,6 @@ Nocturne/11 - Sunrise (Pillows) (Feat. Emmavie).mp3
|
|||
title: Sunrise (Pillows) (Feat. Emmavie)
|
||||
track: 11
|
||||
year: 2017
|
||||
Nocturne/folder.jpg
|
||||
Nocturne/folder.jpg
|
||||
Sunrise (Pillows) (Feat. Emmavie).info.json
|
||||
What If (Interlude).info.json
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
Jesse's Minecraft Server [Trailer - Feb.1].info.json
|
||||
Jesse's Minecraft Server [Trailer - Feb.1].ogg
|
||||
Music Tags:
|
||||
album: Singles
|
||||
|
|
@ -9,6 +10,7 @@ Jesse's Minecraft Server [Trailer - Feb.1].ogg
|
|||
title: Jesse's Minecraft Server [Trailer - Feb.1]
|
||||
track: 3
|
||||
year: 2011
|
||||
Jesse's Minecraft Server [Trailer - Feb.27].info.json
|
||||
Jesse's Minecraft Server [Trailer - Feb.27].ogg
|
||||
Music Tags:
|
||||
album: Singles
|
||||
|
|
@ -18,6 +20,7 @@ Jesse's Minecraft Server [Trailer - Feb.27].ogg
|
|||
title: Jesse's Minecraft Server [Trailer - Feb.27]
|
||||
track: 2
|
||||
year: 2011
|
||||
Jesse's Minecraft Server [Trailer - Mar.21].info.json
|
||||
Jesse's Minecraft Server [Trailer - Mar.21].ogg
|
||||
Music Tags:
|
||||
album: Singles
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
YouTube Rewind 2019: For the Record | #YouTubeRewind.info.json
|
||||
YouTube Rewind 2019: For the Record | #YouTubeRewind.mp3
|
||||
Music Tags:
|
||||
album: Singles
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case-thumb.jpg
|
||||
JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case.info.json
|
||||
JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case.mp4
|
||||
Chapters embedded from timestamp file:
|
||||
0:00: Intro
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case-thumb.jpg
|
||||
JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case.info.json
|
||||
JMC - This GPU SLIDES into this Case! - Silverstone SUGO 16 ITX Case.mp4
|
||||
Embedded Chapters
|
||||
Removed Chapter(s): Intro, Outro
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ Files created in '{output_directory}'
|
|||
----------------------------------------
|
||||
.ytdl-sub-regex_capture_playlist_test-download-archive.json
|
||||
Project Zombie - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg
|
||||
Project Zombie - Jesse's Minecraft Server [Trailer - Feb.1].info.json
|
||||
Project Zombie - Jesse's Minecraft Server [Trailer - Feb.1].mp4
|
||||
Project Zombie - Jesse's Minecraft Server [Trailer - Feb.1].nfo
|
||||
NFO tags:
|
||||
|
|
@ -20,6 +21,7 @@ Project Zombie - Jesse's Minecraft Server [Trailer - Feb.1].nfo
|
|||
upload_date_both_caps: First and Second containing in regex default
|
||||
year: 2011
|
||||
Project Zombie - Jesse's Minecraft Server [Trailer - Feb.27]-thumb.jpg
|
||||
Project Zombie - Jesse's Minecraft Server [Trailer - Feb.27].info.json
|
||||
Project Zombie - Jesse's Minecraft Server [Trailer - Feb.27].mp4
|
||||
Project Zombie - Jesse's Minecraft Server [Trailer - Feb.27].nfo
|
||||
NFO tags:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind-thumb.jpg
|
||||
JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind.info.json
|
||||
JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind.mp4
|
||||
Embedded subtitles with lang(s) en, de
|
||||
JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind.nfo
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ Files created in '{output_directory}'
|
|||
JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind-thumb.jpg
|
||||
JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind.de.srt
|
||||
JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind.en.srt
|
||||
JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind.info.json
|
||||
JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind.mp4
|
||||
Embedded subtitles with lang(s) en, de
|
||||
JMC - YouTube Rewind 2019: For the Record | #YouTubeRewind.nfo
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
.ytdl-sub-jb-download-archive.json
|
||||
j_b/[2021] Baby Santana's Dorian Groove/01 - Baby Santana's Dorian Groove.info.json
|
||||
j_b/[2021] Baby Santana's Dorian Groove/01 - Baby Santana's Dorian Groove.mp3
|
||||
Music Tags:
|
||||
album: Baby Santana's Dorian Groove
|
||||
|
|
@ -11,6 +12,7 @@ j_b/[2021] Baby Santana's Dorian Groove/01 - Baby Santana's Dorian Groove.mp3
|
|||
track: 1
|
||||
year: 2021
|
||||
j_b/[2021] Baby Santana's Dorian Groove/folder.jpg
|
||||
j_b/[2021] Purple Clouds/01 - Purple Clouds.info.json
|
||||
j_b/[2021] Purple Clouds/01 - Purple Clouds.mp3
|
||||
Music Tags:
|
||||
album: Purple Clouds
|
||||
|
|
@ -21,6 +23,7 @@ j_b/[2021] Purple Clouds/01 - Purple Clouds.mp3
|
|||
track: 1
|
||||
year: 2021
|
||||
j_b/[2021] Purple Clouds/folder.jpg
|
||||
j_b/[2022] Acoustic Treats/01 - 20160426 184214.info.json
|
||||
j_b/[2022] Acoustic Treats/01 - 20160426 184214.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
|
|
@ -30,6 +33,7 @@ j_b/[2022] Acoustic Treats/01 - 20160426 184214.mp3
|
|||
title: 20160426 184214
|
||||
track: 1
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/02 - 20160502 123150.info.json
|
||||
j_b/[2022] Acoustic Treats/02 - 20160502 123150.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
|
|
@ -39,6 +43,7 @@ j_b/[2022] Acoustic Treats/02 - 20160502 123150.mp3
|
|||
title: 20160502 123150
|
||||
track: 2
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/03 - 20160504 143832.info.json
|
||||
j_b/[2022] Acoustic Treats/03 - 20160504 143832.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
|
|
@ -48,6 +53,7 @@ j_b/[2022] Acoustic Treats/03 - 20160504 143832.mp3
|
|||
title: 20160504 143832
|
||||
track: 3
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/04 - 20160601 221234.info.json
|
||||
j_b/[2022] Acoustic Treats/04 - 20160601 221234.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
|
|
@ -57,6 +63,7 @@ j_b/[2022] Acoustic Treats/04 - 20160601 221234.mp3
|
|||
title: 20160601 221234
|
||||
track: 4
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/05 - 20160601 222440.info.json
|
||||
j_b/[2022] Acoustic Treats/05 - 20160601 222440.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
|
|
@ -66,6 +73,7 @@ j_b/[2022] Acoustic Treats/05 - 20160601 222440.mp3
|
|||
title: 20160601 222440
|
||||
track: 5
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/06 - 20170604 190236.info.json
|
||||
j_b/[2022] Acoustic Treats/06 - 20170604 190236.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
|
|
@ -75,6 +83,7 @@ j_b/[2022] Acoustic Treats/06 - 20170604 190236.mp3
|
|||
title: 20170604 190236
|
||||
track: 6
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/07 - 20170612 193646.info.json
|
||||
j_b/[2022] Acoustic Treats/07 - 20170612 193646.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
|
|
@ -84,6 +93,7 @@ j_b/[2022] Acoustic Treats/07 - 20170612 193646.mp3
|
|||
title: 20170612 193646
|
||||
track: 7
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/08 - 20170628 215206.info.json
|
||||
j_b/[2022] Acoustic Treats/08 - 20170628 215206.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
|
|
@ -93,6 +103,7 @@ j_b/[2022] Acoustic Treats/08 - 20170628 215206.mp3
|
|||
title: 20170628 215206
|
||||
track: 8
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/09 - Finding Home.info.json
|
||||
j_b/[2022] Acoustic Treats/09 - Finding Home.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
|
|
@ -102,6 +113,7 @@ j_b/[2022] Acoustic Treats/09 - Finding Home.mp3
|
|||
title: Finding Home
|
||||
track: 9
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/10 - Shallow Water WIP.info.json
|
||||
j_b/[2022] Acoustic Treats/10 - Shallow Water WIP.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
|
|
@ -111,6 +123,7 @@ j_b/[2022] Acoustic Treats/10 - Shallow Water WIP.mp3
|
|||
title: Shallow Water WIP
|
||||
track: 10
|
||||
year: 2022
|
||||
j_b/[2022] Acoustic Treats/11 - Untold History.info.json
|
||||
j_b/[2022] Acoustic Treats/11 - Untold History.mp3
|
||||
Music Tags:
|
||||
album: Acoustic Treats
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ Files created in '{output_directory}'
|
|||
----------------------------------------
|
||||
.ytdl-sub-pz-download-archive.json
|
||||
Season 2010/s2010.e0813 - Oblivion Mod "Falcor" p.1-thumb.jpg
|
||||
Season 2010/s2010.e0813 - Oblivion Mod "Falcor" p.1.info.json
|
||||
Season 2010/s2010.e0813 - Oblivion Mod "Falcor" p.1.mp4
|
||||
Season 2010/s2010.e0813 - Oblivion Mod "Falcor" p.1.nfo
|
||||
NFO tags:
|
||||
|
|
@ -19,6 +20,7 @@ Season 2010/s2010.e0813 - Oblivion Mod "Falcor" p.1.nfo
|
|||
title: Oblivion Mod "Falcor" p.1
|
||||
year: 2010
|
||||
Season 2010/s2010.e1202 - Oblivion Mod "Falcor" p.2-thumb.jpg
|
||||
Season 2010/s2010.e1202 - Oblivion Mod "Falcor" p.2.info.json
|
||||
Season 2010/s2010.e1202 - Oblivion Mod "Falcor" p.2.mp4
|
||||
Season 2010/s2010.e1202 - Oblivion Mod "Falcor" p.2.nfo
|
||||
NFO tags:
|
||||
|
|
@ -36,6 +38,7 @@ Season 2010/s2010.e1202 - Oblivion Mod "Falcor" p.2.nfo
|
|||
title: Oblivion Mod "Falcor" p.2
|
||||
year: 2010
|
||||
Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg
|
||||
Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1].info.json
|
||||
Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1].mp4
|
||||
Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1].nfo
|
||||
NFO tags:
|
||||
|
|
@ -54,6 +57,7 @@ Season 2011/s2011.e0201 - Jesse's Minecraft Server [Trailer - Feb.1].nfo
|
|||
title: Jesse's Minecraft Server [Trailer - Feb.1]
|
||||
year: 2011
|
||||
Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27]-thumb.jpg
|
||||
Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27].info.json
|
||||
Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27].mp4
|
||||
Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27].nfo
|
||||
NFO tags:
|
||||
|
|
@ -91,6 +95,7 @@ Season 2011/s2011.e0227 - Jesse's Minecraft Server [Trailer - Feb.27].nfo
|
|||
title: Jesse's Minecraft Server [Trailer - Feb.27]
|
||||
year: 2011
|
||||
Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21]-thumb.jpg
|
||||
Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21].info.json
|
||||
Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21].mp4
|
||||
Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21].nfo
|
||||
NFO tags:
|
||||
|
|
@ -128,6 +133,7 @@ Season 2011/s2011.e0321 - Jesse's Minecraft Server [Trailer - Mar.21].nfo
|
|||
title: Jesse's Minecraft Server [Trailer - Mar.21]
|
||||
year: 2011
|
||||
Season 2011/s2011.e0529 - Project Zombie |Official Trailer| (IP: mc.projectzombie.beastnode.net)-thumb.jpg
|
||||
Season 2011/s2011.e0529 - Project Zombie |Official Trailer| (IP: mc.projectzombie.beastnode.net).info.json
|
||||
Season 2011/s2011.e0529 - Project Zombie |Official Trailer| (IP: mc.projectzombie.beastnode.net).mp4
|
||||
Season 2011/s2011.e0529 - Project Zombie |Official Trailer| (IP: mc.projectzombie.beastnode.net).nfo
|
||||
NFO tags:
|
||||
|
|
@ -155,6 +161,7 @@ Season 2011/s2011.e0529 - Project Zombie |Official Trailer| (IP: mc.projec
|
|||
title: Project Zombie |Official Trailer| (IP: mc.projectzombie.beastnode.net)
|
||||
year: 2011
|
||||
Season 2011/s2011.e0630 - Project Zombie |Fin|-thumb.jpg
|
||||
Season 2011/s2011.e0630 - Project Zombie |Fin|.info.json
|
||||
Season 2011/s2011.e0630 - Project Zombie |Fin|.mp4
|
||||
Season 2011/s2011.e0630 - Project Zombie |Fin|.nfo
|
||||
NFO tags:
|
||||
|
|
@ -168,6 +175,7 @@ Season 2011/s2011.e0630 - Project Zombie |Fin|.nfo
|
|||
title: Project Zombie |Fin|
|
||||
year: 2011
|
||||
Season 2011/s2011.e1121 - Skyrim 'Ultra HD w⧸Mods' [PC]-thumb.jpg
|
||||
Season 2011/s2011.e1121 - Skyrim 'Ultra HD w⧸Mods' [PC].info.json
|
||||
Season 2011/s2011.e1121 - Skyrim 'Ultra HD w⧸Mods' [PC].mp4
|
||||
Season 2011/s2011.e1121 - Skyrim 'Ultra HD w⧸Mods' [PC].nfo
|
||||
NFO tags:
|
||||
|
|
@ -188,6 +196,7 @@ Season 2011/s2011.e1121 - Skyrim 'Ultra HD w⧸Mods' [PC].nfo
|
|||
title: Skyrim 'Ultra HD w/Mods' [PC]
|
||||
year: 2011
|
||||
Season 2012/s2012.e0123 - Project Zombie |Map Trailer|-thumb.jpg
|
||||
Season 2012/s2012.e0123 - Project Zombie |Map Trailer|.info.json
|
||||
Season 2012/s2012.e0123 - Project Zombie |Map Trailer|.mp4
|
||||
Season 2012/s2012.e0123 - Project Zombie |Map Trailer|.nfo
|
||||
NFO tags:
|
||||
|
|
@ -208,6 +217,7 @@ Season 2012/s2012.e0123 - Project Zombie |Map Trailer|.nfo
|
|||
title: Project Zombie |Map Trailer|
|
||||
year: 2012
|
||||
Season 2013/s2013.e0719 - Project Zombie Rewind |Trailer|-thumb.jpg
|
||||
Season 2013/s2013.e0719 - Project Zombie Rewind |Trailer|.info.json
|
||||
Season 2013/s2013.e0719 - Project Zombie Rewind |Trailer|.mp4
|
||||
Season 2013/s2013.e0719 - Project Zombie Rewind |Trailer|.nfo
|
||||
NFO tags:
|
||||
|
|
@ -223,6 +233,7 @@ Season 2013/s2013.e0719 - Project Zombie Rewind |Trailer|.nfo
|
|||
title: Project Zombie Rewind |Trailer|
|
||||
year: 2013
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer-thumb.jpg
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer.info.json
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer.mp4
|
||||
Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer.nfo
|
||||
NFO tags:
|
||||
|
|
@ -244,6 +255,7 @@ Season 2018/s2018.e1029 - Jesse's Minecraft Server | Teaser Trailer.nfo
|
|||
year: 2018
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server | IP mc.jesse.id-thumb.jpg
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server | IP mc.jesse.id.en.srt
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server | IP mc.jesse.id.info.json
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server | IP mc.jesse.id.mp4
|
||||
Season 2018/s2018.e1102 - Jesse's Minecraft Server | IP mc.jesse.id.nfo
|
||||
NFO tags:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
JMC - Jesse's Minecraft Server-thumb.jpg
|
||||
JMC - Jesse's Minecraft Server.info.json
|
||||
JMC - Jesse's Minecraft Server.mkv
|
||||
Timestamps of playlist videos in the merged file:
|
||||
0:00: Jesse's Minecraft Server [Trailer - Mar.21]
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ Files created in '{output_directory}'
|
|||
----------------------------------------
|
||||
.ytdl-sub-music_video_playlist_test-download-archive.json
|
||||
JMC - Jesse's Minecraft Server [Trailer - Feb.1]-thumb.jpg
|
||||
JMC - Jesse's Minecraft Server [Trailer - Feb.1].info.json
|
||||
JMC - Jesse's Minecraft Server [Trailer - Feb.1].mp4
|
||||
JMC - Jesse's Minecraft Server [Trailer - Feb.1].nfo
|
||||
NFO tags:
|
||||
|
|
@ -11,6 +12,7 @@ JMC - Jesse's Minecraft Server [Trailer - Feb.1].nfo
|
|||
title: Jesse's Minecraft Server [Trailer - Feb.1]
|
||||
year: 2011
|
||||
JMC - Jesse's Minecraft Server [Trailer - Feb.27]-thumb.jpg
|
||||
JMC - Jesse's Minecraft Server [Trailer - Feb.27].info.json
|
||||
JMC - Jesse's Minecraft Server [Trailer - Feb.27].mp4
|
||||
JMC - Jesse's Minecraft Server [Trailer - Feb.27].nfo
|
||||
NFO tags:
|
||||
|
|
@ -20,6 +22,7 @@ JMC - Jesse's Minecraft Server [Trailer - Feb.27].nfo
|
|||
title: Jesse's Minecraft Server [Trailer - Feb.27]
|
||||
year: 2011
|
||||
JMC - Jesse's Minecraft Server [Trailer - Mar.21]-thumb.jpg
|
||||
JMC - Jesse's Minecraft Server [Trailer - Mar.21].info.json
|
||||
JMC - Jesse's Minecraft Server [Trailer - Mar.21].mp4
|
||||
JMC - Jesse's Minecraft Server [Trailer - Mar.21].nfo
|
||||
NFO tags:
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ Project Zombie - 5-6.Part 4.mp4
|
|||
Project Zombie - 6-6.Part 5.mp4
|
||||
1:01 - 1:28
|
||||
Project Zombie - Intro-thumb.jpg
|
||||
Project Zombie - Intro.info.json
|
||||
Project Zombie - Intro.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
|
|
@ -21,6 +22,7 @@ Project Zombie - Intro.nfo
|
|||
title: Intro
|
||||
year: 2010
|
||||
Project Zombie - Part 1-thumb.jpg
|
||||
Project Zombie - Part 1.info.json
|
||||
Project Zombie - Part 1.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
|
|
@ -29,6 +31,7 @@ Project Zombie - Part 1.nfo
|
|||
title: Part 1
|
||||
year: 2010
|
||||
Project Zombie - Part 2-thumb.jpg
|
||||
Project Zombie - Part 2.info.json
|
||||
Project Zombie - Part 2.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
|
|
@ -37,6 +40,7 @@ Project Zombie - Part 2.nfo
|
|||
title: Part 2
|
||||
year: 2010
|
||||
Project Zombie - Part 3-thumb.jpg
|
||||
Project Zombie - Part 3.info.json
|
||||
Project Zombie - Part 3.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
|
|
@ -45,6 +49,7 @@ Project Zombie - Part 3.nfo
|
|||
title: Part 3
|
||||
year: 2010
|
||||
Project Zombie - Part 4-thumb.jpg
|
||||
Project Zombie - Part 4.info.json
|
||||
Project Zombie - Part 4.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
|
|
@ -53,6 +58,7 @@ Project Zombie - Part 4.nfo
|
|||
title: Part 4
|
||||
year: 2010
|
||||
Project Zombie - Part 5-thumb.jpg
|
||||
Project Zombie - Part 5.info.json
|
||||
Project Zombie - Part 5.nfo
|
||||
NFO tags:
|
||||
musicvideo:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
Files created in '{output_directory}'
|
||||
----------------------------------------
|
||||
JMC - Oblivion Mod "Falcor" p.1-thumb.jpg
|
||||
JMC - Oblivion Mod "Falcor" p.1.info.json
|
||||
JMC - Oblivion Mod "Falcor" p.1.mp4
|
||||
Video Tags:
|
||||
title: Oblivion Mod "Falcor" p.1
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ def mock_entry_to_dict(
|
|||
"upload_day_of_year_reversed": 354,
|
||||
"upload_day_of_year_reversed_padded": "354",
|
||||
"thumbnail_ext": thumbnail_ext,
|
||||
"info_json_ext": "info.json",
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue