Migrate from Poetry and pyenv to uv

This commit is contained in:
2026-10-12 15:48:26 +02:00
parent c399a6aed3
commit c69f5f9166
93 changed files with 13068 additions and 2401 deletions
+88 -10
View File
@@ -202,19 +202,94 @@ library manager**. Agreed shape:
prefixed with the exact folder name — `Title (rok) [tmdbid]/Title (rok)
[tmdbid] - {popisek}.ext`; a 3D version's popisek doubles as its 3D tag
(`3D HSBS`, scanned by Jellyfin the same as the flat `.3D.HSBS` form).
Curator does the promotion/demotion itself (*Upravit film → Verze*); the
table always shows one row per movie (`visible_movies`). Shared metadata
lives on one **primary** version (`File.is_primary_version`), migrated if
that one is ever removed. **Not yet supported:** renaming/moving/retitling
a grouped movie (remove the version or edit files directly), and grouped
movies inside `mixed` libraries (scoped to `movie`-kind for now — a mixed
library's subfolders are still read as series only).
Curator does the promotion/demotion itself (*Upravit film → Verze*), and a
version's popisek is renamable too (`rename_movie_version`) — a flat movie
always shows one placeholder row ("Výchozí") in the list even though it
has no on-disk label yet, but renaming only applies once it's grouped. A
popisek left empty (a plain "2D" file with nothing else to call it) is
`DEFAULT_VERSION_LABEL` ("Výchozí"), not an error — everywhere a popisek is
entered (`add_movie_version`, `rename_movie_version`, `merge_as_version`,
the import dialog's version link). Promoting/demoting always carries the
file's *own* current 3D formát over automatically when no popisek is
given for it (`existing_label`/`keep_label` default to `None` → derived
via `naming.parse_3d_format`) — a movie can freely gain and lose versions
in any order (3D-only → +2D → -3D → 3D-only again, …) without a formát
ever getting silently dropped along the way.
The default (plain) version of a grouped movie carries **no label**:
`Folder/Folder.ext` (`file_manager._version_filename`); "Výchozí" is the
UI's name for it. Identity — what decides "is this film already in the
pool" — is the folder name for a grouped movie and the stem minus any
`.3D.<FORMAT>` for a flat one (`FileManager._identity_stem`,
`pooled_with_stem`). Importing a film that's already there defaults to
adding a version, never a root-level duplicate, and "keep both as separate
films" is not offered for same-TMDb-id films (they'd share one index record).
**Parts** (one film split across files, Jellyfin "stacking") are versions
with a `partN` popisek, written as `Folder-partN.ext` instead of
`Folder - popisek.ext` (`file_manager._version_filename`,
`movie.normalize_part_label`); a film with parts always lives in its own
folder. Parts and versions in one folder are not combined yet.
*Upravit film* presents this as one "Média filmu" section: flat
("Samostatný film") until a second medium arrives, then a folder whose
mode is switched between versions and parts (`movie_group_mode`,
`set_movie_group_mode`); parts are reordered with ↑/↓ (`move_movie_part`).
Renames after a relabel go through `File.save_location`, never a plain
`save_metadata`, so a stale sibling can't overwrite shared metadata.
Custom per-movie attributes are no longer editable in the GUI; stored ones
still feed the miniDLNA filename templates.
The table always shows one row per movie (`visible_movies`), marked `▣` /
`Film · N verze` when it has several; shared metadata lives on the movie
record (index v2), so every version reads the same. Accidental duplicates
(same tmdbid / title+year / file size as separate movies) are found by
*Nástroje → Kontrola duplicit* and folded in via `merge_as_version`.
**Not yet supported:** renaming/moving/retitling a grouped movie (remove
the version or edit files directly).
Works in `mixed` libraries too, disambiguated from a series by **shape**:
a movie's version folder and a series folder are both
`Title (rok) [tmdbid]/`, but a series always nests episodes in a
`Season NN`/`Specials` folder and a movie's version folder never does
(`series.looks_like_series_folder`) — `load_pool_movies`/
`is_movie_group_member` read the shape and `scan_series`
(`skip_movie_folders=True` for `mixed`) skips what it recognises as a
movie, so both kinds can share one identity-named folder pair without
either leaking into the other's listing. A `series`-kind library (which
never holds movies) keeps reading every subfolder as a series regardless
of shape, so a raw not-yet-renamed import can still have loose episode
files at the series root before `Season NN` exists.
Assignable at **import** time too, not just after the fact: picking
several files for the same film in one batch (*Importovat filmy…*), a
row's "🔗 Přiřadit jako verzi…" marks it a version of another row instead
of importing it as its own movie — the earlier attempt at this (a 3D
formát combo per row) was rejected because a lone row has nothing to
attach the version *to* and just created a duplicate; picking the target
row explicitly is what that was missing. The popisek is pre-filled from
the filename (`naming.three_d_format_from_filename`) the same way
`year_from_filename` pre-fills the year. Import runs the target rows
first, then folds each linked row onto its target via
`add_movie_version`. The reverse case — a film whose only file *is* 3D,
with nothing to link it to — gets its own **3D formát combo per row**
instead (`import_movie(..., format_3d=…)`, same as *Upravit film*);
disabled the moment the row is linked as a version or gains one as a
follower, since a grouped movie's 3D-ness lives on the version's popisek,
not on the row that started the group.
- Series: `{název série} (rok) [tmdbid]/Season NN/S{ss}E{ee} - {série}.ext`
(season 0 → `Specials`). The episode filename is the code + the series title
only — the release string is dropped (identity is the code; Jellyfin fetches
episode metadata from TMDb via the code + `[tmdbid]`). Episode numbers are
recognised from the filename and editable at import / in the series GUI;
*Seriály → "Napravit názvy souborů…"* bulk-fixes the whole pool.
- **Episodes split into physical parts** (an old recording cut into
several files) get Jellyfin's own `.ptN` stacking suffix —
`SxxEyy - {série}.ptN.ext` — recognised from *cd/dvd/part/pt/disc/disk* +
a number in the source filename (`Show.S01E02.pt2.mkv`,
`Show S01E01-part-1.mkv`, …) or typed by hand at import as `S01E02P3`
(`src.core.series.parse_episode_code`/`parse_episode_part`). Distinct from
a multi-episode file (`E01-E02`, one file = several episodes) — a part is
the opposite: several files = one episode.
- **Removing a series is scoped to the tree selection**: *Přehled seriálů →
"Odebrat z poolu…"* deletes the series, a single season, or a single
episode, whichever is selected (`FileManager.remove_series`/
`remove_season`/`remove_episodes`) — series still carry no index metadata,
so this is always just deleting files/folders from disk.
- **CZ title source:** TMDb (cs-CZ) **primarily** — reliable when matched by
`tmdbid` (yearless title search picks the wrong film, hence the earlier
"weird names"); **ČSFD as fallback/override** (a ČSFD link at import, or when
@@ -316,9 +391,12 @@ Note: libraries hold **flat** files for now (`pool/<Library>/Title (rok)
- Scanning **[done]**: a `mixed` library's top-level files load as movies
(`load_pool_movies`), its subfolders as series (`load_series` /
`series_libraries` = kind `series` **or** `mixed`, reusing `scan_series`).
Both are kind-aware over the unified `libraries` list. The library filter /
table already surfaces mixed libraries.
`series_libraries` = kind `series` **or** `mixed`, reusing `scan_series`) —
*except* a canonical-shaped subfolder with no `Season NN`/`Specials` inside
it, which is a movie's version folder instead and loads as a movie too (see
"Multiple movie versions" above). Both are kind-aware over the unified
`libraries` list. The library filter / table already surfaces mixed
libraries.
- miniDLNA generation **[done]**: a mixed library's movies are genre-tagged as
usual; series in its subfolders have no genre tags, so they're hardcopied
straight into `output/Seriály/<název>/…` instead