import re import shutil from collections import Counter from collections.abc import Callable, Iterable, Mapping from datetime import datetime from pathlib import Path from src.core.config import DEFAULT_TAG_SCHEMA, load_global_config, save_global_config from . import subtitles from .file import File from .integrity import IntegrityResult, check_video_integrity, ffmpeg_available from .naming import ( canonical_pool_stem, is_canonical_pool_stem, is_valid_year, parse_3d_format, parse_pool_stem, parse_tmdb_id, ) from .pool_index import PoolIndex from .tag_manager import TagManager from .utils import list_files # Top-level folders inside the managed pool POOL_MOVIES = "Filmy" POOL_SERIES = "Seriály" # A pool library (top-level folder) has one of these kinds: # movie — single-file movies, TMDb naming (Title (rok) [tmdbid].ext) # series — series/season/episode tree, mirrored as-is (Jellyfin Shows) # mixed — both movies and series in one folder (Jellyfin "Mixed"; wiring TBD) # homevideo — files without TMDb (home videos; Jellyfin "Home videos"; wiring TBD) LIBRARY_KINDS = ("movie", "series", "mixed", "homevideo") _DEFAULT_LIBRARIES = [ {"name": POOL_MOVIES, "kind": "movie"}, {"name": POOL_SERIES, "kind": "series"}, ] # Library-level settings that live in the pool index (not the global config). # ``libraries`` is the source of truth; the legacy ``movie_libraries`` / # ``copyasis_folders`` are kept only to synthesise ``libraries`` for old pools. _LIBRARY_SETTINGS_DEFAULTS = { "tag_schema": DEFAULT_TAG_SCHEMA, "copyasis_folders": [POOL_SERIES], "movie_libraries": [POOL_MOVIES], } # Curator metadata files that must never be treated as content. The legacy # per-file (.!tag) and per-folder (.!ftag) sidecars are no longer written, but # stray ones are still skipped so they never leak into the Filmotéka. METADATA_SUFFIXES = (".!tag", ".!ftag", ".!gtag", ".!index") # Sidecars Jellyfin writes next to a movie (NFO + artwork/subtitles). These are # not movies and must be skipped when scanning a library. SIDECAR_SUFFIXES = ( ".nfo", ".jpg", ".jpeg", ".png", ".webp", ".bmp", ".gif", ".tbn", ".srt", ".ass", ".ssa", ".sub", ".idx", ".vtt", ) def _version_filename(stem: str, label: str, suffix: str) -> str: """Filename of one version of a movie folder. The default version ("Výchozí", or no label at all) carries no label — it's just ````, the same name the movie had flat. A part label (``part2``, ``CD2``, … see :func:`movie.normalize_part_label`) is ``-part``. Any other version is `` -