Files
Curator/PROJECT.md
T

6.4 KiB

PROJECT.md

This file is project-specific. Only include information directly related to the concrete project — goals, current state, architecture decisions, known issues, and tasks.

Origin

Curator is a fork of the former Tagger project. The tagging, filtering and hardlink-tree parts are inherited and keep working as before. On top of that, Curator becomes a full movie library manager (Filmotéka).

Core idea

Curator manages a personal movie library based on two folders:

  • Pool — the managed repository of video files. This is the single source of truth. Curator manages the pool itself (insert/remove file), so files are never moved by hand. The pool has exactly two top-level folders: Filmy (movies — tag-based tree) and Seriály (series — a "copy-as-is" folder mirrored 1:1 into the output; see Design decisions). Every file lives here exactly once.
  • Filmotéka (output) — a generated, browsable directory tree made only of hardlinks into the pool (the same mechanism as today's hardlink manager). It is fully disposable: deleting the Filmotéka folder loses nothing, because it can always be regenerated from the pool.

Workflow

  1. The user configures two folders: the pool and the Filmotéka output.
  2. The user picks a video file via "Open file".
  3. Curator opens a dialog to fill in basic info — at minimum the title/name and a ČSFD link.
  4. Curator renames the file and moves it into the managed pool, and writes a metadata file describing it.
  5. From the pool, Curator generates the Filmotéka — a complex tree of hardlinks built from each file's tags/metadata (like the current hardlink manager, but driven by the pool).
  6. Deleting the Filmotéka has no effect on the pool; the tree is regenerated on demand.

Current state

  • Inherited from Tagger: Tag, TagManager, File (sidecar metadata), FileManager (folder scan, filtering, ignore patterns), 3-level config, HardlinkManager (create/sync/cleanup), pytest suite.
  • Rename Tagger → Curator done across code, spec, config filenames (.Curator.!gtag / .Curator.!ftag) and tests.
  • PySide6 GUI (src/ui/qt_app.py) reframed around the Filmotéka workflow is the entry point; the old tkinter src/ui/gui.py is retained for reference.
  • Pool + Filmotéka wired up: global config holds pool_dir / filmoteka_dir; FileManager creates Filmy/Seriály, imports movies (copy → Title.ext), loads the pool, and the GUI generates the Filmotéka tree via HardlinkManager.
  • File carries title + csfd_link. Pool metadata lives in a unified index (<pool>/.Curator.!index, see pool_index.py); File writes there when an index is injected, and still falls back to per-file .!tag sidecars for arbitrary (non-pool) folders.

GUI decision

The GUI was reframed around the Filmotéka (not kept as a generic tagger) and rewritten in PySide6: Pool/Filmotéka setup, Import movie, tag-filter sidebar, movie table, and one-click Filmotéka generation.

Design decisions

  • Metadata storage: one unified metadata file for the whole pool (a central index), not per-file sidecars. Justified because Curator owns the pool and files are never moved manually, so it is not exposed to path drift.
  • Import dialog: collects only Title + ČSFD link. The file is renamed to Title.ext. When a ČSFD link is given, Curator fetches the movie and assigns Žánr / Rok / Země tags automatically; further tags can be added via the UI.
  • Genres: a movie can have multiple genres, so it appears under each of its genre branches in the Filmotéka (multiple hardlinks).
  • Pool layout: two top-level folders — Filmy and Seriály. Movies are the first target; the Seriály branch follows the "copy-as-is" rule below.
  • Copy-as-is folders (Seriály): a subfolder inside the pool can be marked as copy / as-is. For such a folder Curator does not build a tag-based tree; instead it mirrors the exact directory hierarchy from the pool into the Filmotéka output, with the files materialized as hardlinks into the pool. So pool/Seriály/... is cloned 1:1 into output/Seriály/... (same structure, hardlinked files). This is how Seriály work.
  • File naming: imported movies are renamed to Title.ext (no year in the filename; year lives in metadata/tags).
  • Import is non-destructive: the original file is copied into the pool, the source is left in place.
  • Filmotéka tree: one level per categoryoutput/Category/Tag/film (hardlink), same shape as the current hardlink manager. For now the tree is built from these categories: Rok, Žánr, Hodnocení.

Tasks

(no open tasks — see Done)

Done

  • Pool-root and Filmotéka-output folder settings in the global config
  • Filmy / Seriály top-level folder handling in the pool
  • "Import movie" dialog (Title + ČSFD link), copy into pool/Filmy as Title.ext
  • Remove-from-pool (delete file + its metadata)
  • Generate the Filmotéka hardlink tree from the pool (Rok / Žánr / Hodnocení)
  • Filmotéka fully regenerable from the pool alone (delete output = no loss)
  • GUI reframed around the Filmotéka and rewritten in PySide6
  • Seriály "copy-as-is" mirror: pool/Seriály cloned 1:1 into the output as hardlinks (HardlinkManager.mirror_as_is), wired into Filmotéka generation
  • Fixed media_utils missing subprocess import
  • Unified pool metadata index (pool_index.py): one .Curator.!index per pool; File reads/writes it when injected, FileManager uses it for the pool
  • Configurable copy-as-is folders (copyasis_folders in global config, editable from the GUI); each is mirrored 1:1 during Filmotéka generation (Seriály default)
  • README.md written (overview, concepts, workflow, run/build instructions)
  • ČSFD scraping (csfd.py, ported from Tagger devel): File.apply_csfd_tags fetches a movie and assigns Žánr / Rok / Země tags (cached in metadata); wired into the GUI (auto-fetch on import with a ČSFD link, plus "Načíst tagy z ČSFD"). Parsing updated for current ČSFD HTML and verified live against Matrix (film/9499); HTTPS uses the OS cert store via truststore (corporate SSL)
  • Fixed template cruft: src/constants.py made consistent (Curator values, get_version/get_debug_mode API) and test_constants.py aligned; removed the imported tagger/ devel dump