[BACKEND] Use title for TV show preset file names
This commit is contained in:
parent
f9318b9d07
commit
cec1328684
6 changed files with 67 additions and 23 deletions
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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(".")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue