[BACKEND] Use title for TV show preset file names

This commit is contained in:
Jesse Bannon 2023-03-10 09:50:12 -08:00
parent f9318b9d07
commit cec1328684
6 changed files with 67 additions and 23 deletions

View file

@ -328,6 +328,8 @@ The `config.yaml`_ uses various types for its configurable fields. Below is a de
.. autoclass:: ytdl_sub.validators.string_formatter_validators.OverridesStringFormatterValidator() .. autoclass:: ytdl_sub.validators.string_formatter_validators.OverridesStringFormatterValidator()
.. autoclass:: ytdl_sub.validators.file_path_validators.StringFormatterFileNameValidator()
.. autoclass:: ytdl_sub.validators.string_datetime.StringDatetimeValidator() .. autoclass:: ytdl_sub.validators.string_datetime.StringDatetimeValidator()
.. autoclass:: ytdl_sub.validators.string_formatter_validators.DictFormatterValidator() .. autoclass:: ytdl_sub.validators.string_formatter_validators.DictFormatterValidator()

View file

@ -7,10 +7,8 @@ from typing import Optional
from yt_dlp.utils import sanitize_filename from yt_dlp.utils import sanitize_filename
from ytdl_sub.entries.entry import Entry from ytdl_sub.entries.entry import Entry
from ytdl_sub.validators.file_path_validators import ( from ytdl_sub.validators.file_path_validators import OverridesStringFormatterFilePathValidator
OverridesStringFormatterValidatorFilePathValidator, from ytdl_sub.validators.file_path_validators import StringFormatterFileNameValidator
)
from ytdl_sub.validators.file_path_validators import StringFormatterFilePathValidator
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
from ytdl_sub.validators.string_datetime import StringDatetimeValidator from ytdl_sub.validators.string_datetime import StringDatetimeValidator
from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator
@ -226,18 +224,18 @@ class OutputOptions(StrictDictValidator):
# Output directory should resolve without any entry variables. # Output directory should resolve without any entry variables.
# This is to check the directory for any download-archives before any downloads begin # This is to check the directory for any download-archives before any downloads begin
self._output_directory = self._validate_key( self._output_directory = self._validate_key(
key="output_directory", validator=OverridesStringFormatterValidatorFilePathValidator key="output_directory", validator=OverridesStringFormatterFilePathValidator
) )
# file name and thumbnails however can use entry variables # file name and thumbnails however can use entry variables
self._file_name = self._validate_key( self._file_name = self._validate_key(
key="file_name", validator=StringFormatterFilePathValidator key="file_name", validator=StringFormatterFileNameValidator
) )
self._thumbnail_name = self._validate_key_if_present( self._thumbnail_name = self._validate_key_if_present(
key="thumbnail_name", validator=StringFormatterFilePathValidator key="thumbnail_name", validator=StringFormatterFileNameValidator
) )
self._info_json_name = self._validate_key_if_present( self._info_json_name = self._validate_key_if_present(
key="info_json_name", validator=StringFormatterFilePathValidator key="info_json_name", validator=StringFormatterFileNameValidator
) )
self._maintain_download_archive = self._validate_key_if_present( self._maintain_download_archive = self._validate_key_if_present(

View file

@ -16,7 +16,7 @@ from ytdl_sub.utils.xml import XmlElement
from ytdl_sub.utils.xml import to_max_3_byte_utf8_dict 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_max_3_byte_utf8_string
from ytdl_sub.utils.xml import to_xml from ytdl_sub.utils.xml import to_xml
from ytdl_sub.validators.file_path_validators import StringFormatterFilePathValidator from ytdl_sub.validators.file_path_validators import StringFormatterFileNameValidator
from ytdl_sub.validators.nfo_validators import NfoTagsValidator from ytdl_sub.validators.nfo_validators import NfoTagsValidator
from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
@ -47,7 +47,7 @@ class SharedNfoTagsOptions(PluginOptions):
super().__init__(name, value) super().__init__(name, value)
self._nfo_name = self._validate_key_if_present( self._nfo_name = self._validate_key_if_present(
key="nfo_name", validator=StringFormatterFilePathValidator key="nfo_name", validator=StringFormatterFileNameValidator
) )
self._nfo_root = self._validate_key_if_present( self._nfo_root = self._validate_key_if_present(
key="nfo_root", validator=StringFormatterValidator key="nfo_root", validator=StringFormatterValidator
@ -58,7 +58,7 @@ class SharedNfoTagsOptions(PluginOptions):
).value ).value
@property @property
def nfo_name(self) -> StringFormatterFilePathValidator: def nfo_name(self) -> StringFormatterFileNameValidator:
""" """
The NFO file name. The NFO file name.
""" """

View file

@ -11,7 +11,7 @@ from ytdl_sub.utils.file_handler import FileHandler
from ytdl_sub.utils.file_handler import FileMetadata from ytdl_sub.utils.file_handler import FileMetadata
from ytdl_sub.utils.logger import Logger from ytdl_sub.utils.logger import Logger
from ytdl_sub.utils.subtitles import SUBTITLE_EXTENSIONS from ytdl_sub.utils.subtitles import SUBTITLE_EXTENSIONS
from ytdl_sub.validators.file_path_validators import StringFormatterFilePathValidator from ytdl_sub.validators.file_path_validators import StringFormatterFileNameValidator
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
from ytdl_sub.validators.string_select_validator import StringSelectValidator from ytdl_sub.validators.string_select_validator import StringSelectValidator
from ytdl_sub.validators.validators import BoolValidator from ytdl_sub.validators.validators import BoolValidator
@ -56,7 +56,7 @@ class SubtitleOptions(PluginOptions):
def __init__(self, name, value): def __init__(self, name, value):
super().__init__(name, value) super().__init__(name, value)
self._subtitles_name = self._validate_key_if_present( self._subtitles_name = self._validate_key_if_present(
key="subtitles_name", validator=StringFormatterFilePathValidator key="subtitles_name", validator=StringFormatterFileNameValidator
) )
self._subtitles_type = self._validate_key_if_present( self._subtitles_type = self._validate_key_if_present(
key="subtitles_type", validator=SubtitlesTypeValidator, default="srt" key="subtitles_type", validator=SubtitlesTypeValidator, default="srt"

View file

@ -39,7 +39,56 @@ class FFprobeFileValidator(FFmpegFileValidator):
_ffmpeg_dependency = "ffprobe" _ffmpeg_dependency = "ffprobe"
class StringFormatterFilePathValidator(StringFormatterValidator): class FilePathValidatorMixin:
@classmethod
def _is_file_name_too_long(cls, file_name: str) -> bool:
return len(file_name.encode("utf-8")) > _MAX_FILE_NAME_BYTES
@classmethod
def _get_extension_split(cls, file_name: str) -> Tuple[str, str]:
if file_name.endswith(".info.json"):
ext = "info.json"
elif any(file_name.endswith(f".{subtitle_ext}") for subtitle_ext in SUBTITLE_EXTENSIONS):
file_name_split = file_name.split(".")
ext = file_name_split[-1]
# Try to capture .lang.ext
if len(file_name_split) > 2 and len(file_name_split[-2]) < 6:
ext = f"{file_name_split[-2]}.{file_name_split[-1]}"
else:
ext = file_name.rsplit(".", maxsplit=1)[-1]
return file_name[: -len(ext)], ext
@classmethod
def _truncate_file_name(cls, file_name: str) -> str:
file_sub_name, file_ext = cls._get_extension_split(file_name)
desired_size = _MAX_FILE_NAME_BYTES - len(file_ext.encode("utf-8")) - 1
while len(file_sub_name.encode("utf-8")) > desired_size:
file_sub_name = file_sub_name[:-1]
return f"{file_sub_name}.{file_ext}"
@classmethod
def _maybe_truncate_file_path(cls, file_path: Path) -> str:
"""Turn into a Path, then a string, to get correct directory separators"""
file_directory, file_name = os.path.split(Path(file_path))
if cls._is_file_name_too_long(file_name):
return str(Path(file_directory) / cls._truncate_file_name(file_name))
return str(file_path)
class StringFormatterFileNameValidator(StringFormatterValidator, FilePathValidatorMixin):
"""
Same as a
:class:`StringFormatterValidator <ytdl_sub.validators.string_formatter_validators.StringFormatterValidator>`
but ensures the file name does not exceed the OS limit (typically 255 bytes). If it does exceed,
it will preserve the extension and truncate the end of the file name.
"""
_expected_value_type_name = "filepath" _expected_value_type_name = "filepath"
@classmethod @classmethod
@ -75,15 +124,10 @@ class StringFormatterFilePathValidator(StringFormatterValidator):
def apply_formatter(self, variable_dict: Dict[str, str]) -> str: def apply_formatter(self, variable_dict: Dict[str, str]) -> str:
"""Turn into a Path, then a string, to get correct directory separators""" """Turn into a Path, then a string, to get correct directory separators"""
file_path = Path(super().apply_formatter(variable_dict)) file_path = Path(super().apply_formatter(variable_dict))
file_directory, file_name = os.path.split(Path(file_path)) return self._maybe_truncate_file_path(file_path)
if self._is_file_name_too_long(file_name):
return str(Path(file_directory) / self._truncate_file_name(file_name))
return str(file_path)
class OverridesStringFormatterValidatorFilePathValidator(OverridesStringFormatterValidator): class OverridesStringFormatterFilePathValidator(OverridesStringFormatterValidator):
_expected_value_type_name = "static filepath" _expected_value_type_name = "static filepath"
def apply_formatter(self, variable_dict: Dict[str, str]) -> str: def apply_formatter(self, variable_dict: Dict[str, str]) -> str:

View file

@ -4,7 +4,7 @@ from pathlib import Path
import pytest import pytest
from ytdl_sub.utils.subtitles import SUBTITLE_EXTENSIONS from ytdl_sub.utils.subtitles import SUBTITLE_EXTENSIONS
from ytdl_sub.validators.file_path_validators import StringFormatterFilePathValidator from ytdl_sub.validators.file_path_validators import StringFormatterFileNameValidator
class TestStringFormatterFilePathValidator: class TestStringFormatterFilePathValidator:
@ -26,7 +26,7 @@ class TestStringFormatterFilePathValidator:
file_name = (file_name_char * file_name_len) + ext file_name = (file_name_char * file_name_len) + ext
file_path = str(Path(temp_dir) / file_name) file_path = str(Path(temp_dir) / file_name)
formatter = StringFormatterFilePathValidator(name="test", value=str(file_path)) formatter = StringFormatterFileNameValidator(name="test", value=str(file_path))
truncated_file_path = formatter.apply_formatter({}) truncated_file_path = formatter.apply_formatter({})
assert truncated_file_path.count(".") == ext.count(".") assert truncated_file_path.count(".") == ext.count(".")