Files
Curator/CHANGELOG.md

191 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
Document all notable changes to the project, grouped by version and release date.
## Format
Each version entry uses these sections (include only those that apply):
- **Added** — new features
- **Changed** — changes to existing functionality
- **Fixed** — bug fixes
- **Removed** — removed features
- **Dependencies** — added, updated, or removed dependencies
## Rules
- Follow semantic versioning: `MAJOR.MINOR.PATCH`
- Newest version goes at the top
- Always update this file before bumping the version in `pyproject.toml`
- Document changes as they are made, not all at once at release time
## Unreleased
## 1.5.0 — 2026-06-16
### Added
- **Free-form per-movie attributes** (`attributes` in the index, set via *Filmy →
Nastavit atribut…* / context menu): arbitrary `key → value` pairs (e.g.
`collection_sort`) that are exposed to Filmotéka filename templates through
`File.name_context`. So a Kolekce template `"{collection_sort} - {title}{ext}"`
orders the folder as `01 - Dr. No.mkv`, `02 - …`. Empty value removes the attr.
- **Per-category filename template** in the tag schema (`filename_template`,
editable in *Nastavení → Tag schéma…*): the hardlink inside that category's
folders is named from the template (fields `title`/`year`/`rating`/`ext`/
`stem`/`filename`), e.g. a *Kolekce* with `"{year} - {title}{ext}"` yields
`Dle kolekce/James Bond/1962 - Dr. No.mkv` while every other folder (and the
pool) keeps the plain filename. `HardlinkManager` takes
`category_filename_templates`; `File.name_context()` supplies the fields.
- The "Přiřadit štítky" dialog now lists **all tag-schema categories** (including
empty user ones like *Kolekce*) and has a **" Nový štítek…"** button to create
a tag value (e.g. *Kolekce/James Bond*) on the spot and assign it. Tag values
are created on first assignment — the schema editor defines categories + rules,
this dialog defines/assigns the individual values.
### Changed
- Tag schema `transform` now shapes only the **Filmotéka folder name**, not the
tag value. ČSFD tags keep the **exact value** (rating → `Hodnocení/90`) while
the grouping transform is applied when building folders (→ `Dle hodnocení/
90100 %`). `csfd_field_values` no longer transforms; `HardlinkManager` takes a
`category_transforms` map (`FileManager.filmoteka_category_transforms`) and
`apply_transform`/`decade_band` run at folder-generation time.
### Fixed
- Sidebar filter tree no longer re-expands every category on each check/uncheck:
the expanded/collapsed state is preserved across the rebuild (new categories
still default to expanded).
## 1.3.0 — 2026-06-16
### Added
- **Configurable tag schema** (`tag_schema` in the global config, edited via
*Nastavení → Tag schéma…*): a single source of truth for which tag categories
exist, which ČSFD field feeds each (with an optional value transform, e.g.
`decade_band` for the rating), and how each maps into the Filmotéka tree
(`""` = output root, `"Dle …"` = grouping folder, none = filter-only, no
folders). Replaces the hard-coded `FILMOTEKA_CATEGORY_ROOTS` and the fixed
category list in `apply_csfd_tags`; `HardlinkManager` roots are now derived
from the schema (`FileManager.filmoteka_category_roots`).
- **Tag provenance (ČSFD vs user)**: each file tracks which tags came from ČSFD
(`csfd_tags` in the index). `apply_csfd_tags` now **regenerates only the ČSFD
tags** from the schema and leaves user-added tags untouched — so changing a
movie's ČSFD link refreshes its ČSFD tags without wiping your own.
## 1.2.0 — 2026-06-16
### Added
- Fork of the former **Tagger** project as **Curator**, a movie-library manager.
- **Pool** concept (single source of truth) with `Filmy` / `Seriály` folders and
a configurable **Filmotéka** output folder, stored in the global config.
- **Multi-file "Import movies" flow**: pick several videos at once and give each
its own Title + ČSFD link (one row per file, more addable in the dialog); a
copy/move toggle chooses whether sources are copied (default, non-destructive)
or moved into `pool/Filmy` as `Title.ext`. Imported movies are indexed and, if
a ČSFD link is set, enriched with tags right away.
- **Auto-find ČSFD links** in the import dialog ("🔎 Najít ČSFD odkazy"): for
every row without a link it cleans the filename into a query
(`clean_filename_to_query` strips resolution/codec/source/group, keeps the
year) and fills in the first ČSFD search hit (`find_csfd_url`
`search_movies`, reusing one Anubis session). Existing links are never
overwritten; results are a suggestion the user can review before importing.
- Import dialog now **highlights in red any title that already exists in the
pool** (live as you type) and lets you **remove a single row** (✕ per row), so
you can drop one file without discarding the whole batch and re-picking.
- **Conflict resolution on import**: when a row is left red (name already in the
pool), import asks whether to **replace** the existing movie(s) with the new
file, **keep both** (numeric suffix), or **cancel**. `FileManager.import_movie`
gained an `on_conflict` policy (`suffix` / `replace` / `skip`); `replace`
evicts the existing same-named movie (file + index metadata) first.
- `File` now stores `title` and `csfd_link`.
- New **PySide6** GUI reframed around the Filmotéka workflow (pool setup, import,
tag filter sidebar, movie table, one-click Filmotéka generation), replacing the
tkinter GUI as the entry point.
- Seriály **copy-as-is mirror**: `pool/Seriály` is cloned 1:1 into the Filmotéka
output as hardlinks (`HardlinkManager.mirror_as_is`), generated alongside the
movie tree.
- **Unified pool metadata index** (`pool_index.py`): the whole pool's metadata
lives in a single `<pool>/.Curator.!index` JSON keyed by pool-relative path.
`File` reads/writes the index when one is injected and otherwise keeps using
per-file `.!tag` sidecars; `FileManager` uses the index for the pool.
- **Configurable copy-as-is folders**: `copyasis_folders` in the global config
(editable from the GUI) lists pool subfolders mirrored 1:1 during generation;
`Seriály` is the default. Generalizes the previously hardcoded Seriály mirror.
- Project `README.md` (overview, concepts, workflow, run/build instructions).
- **ČSFD scraping** (`csfd.py`, ported from the Tagger devel branch): fetches
movie data from a ČSFD link (JSON-LD + HTML parsing). `File.apply_csfd_tags`
assigns **Žánr / Rok / Země původu / Hodnocení** tags and caches the fetched
data (incl. directors and the first 10 actors) in the metadata. The rating is
bucketed into ten-point bands (`rating_band`, e.g. `8089 %`, `90100 %`).
**Directors and actors are collected but intentionally not turned into tags or
Filmotéka folders** — there would be far too many. The GUI auto-fetches on
import when a link is given and offers "Načíst tagy z ČSFD" for selected movies.
- **Rename a pooled movie** from the app ("Přejmenovat…" in the Movie menu /
context menu, F2): `FileManager.rename_movie` renames the physical file in
pool/Filmy to `<new name>.<ext>` (extension preserved), moves its metadata to
the new index key, and syncs `title`/`filename`. Refuses empty names, names
with path separators, and collisions with an existing pooled file.
- App startup injects `truststore` so HTTPS uses the OS certificate store —
ČSFD fetching works behind corporate SSL inspection (where certifi's bundle
lacks the proxy root CA).
### Fixed
- `media_utils.add_video_resolution_tag` referenced `subprocess` without importing it.
- ČSFD parsing updated for the current site HTML: year is read from JSON-LD
`dateCreated`, and the origin line (now bullet-separated, no commas) is
tokenized so country / year / duration are extracted correctly.
- **ČSFD anti-bot wall (Anubis):** ČSFD now serves a proof-of-work challenge
page instead of the movie, so fetches returned a film with no genres/year
("načteno 0 tagů"). `csfd.py` now detects the Anubis challenge, solves the
SHA-256 proof-of-work the way the bundled worker JS does, and replays the
request through a `requests.Session` (reused across a batch so only the first
fetch pays the PoW cost). Žánr / Rok / Země původu tags load again.
- "Assign tags" dialog crashed on PySide6/Qt6 — `Qt.ItemIsTristate` was renamed
to `Qt.ItemIsAutoTristate`.
- Sidebar tag-filter checkboxes never appeared checked: every toggle triggered a
table refresh that rebuilt the tree from scratch (all unchecked), wiping the
click. The active filter is now kept in a separate model (`_active_filter`) and
restored on rebuild. The count after each tag is also now filter-aware — it
shows how many of the currently filtered movies carry that tag (i.e. how many
would remain if it were checked), instead of always the pool-wide total. The
refresh is deferred via `QTimer.singleShot` so the tree is not rebuilt inside
its own `itemChanged` signal (which deleted the item Qt was still processing
and crashed the app with SIGSEGV on a real click).
### Changed
- ČSFD country tag category renamed **Země → Země původu**. Added
`scripts/migrate_tag_category.py` to rewrite the category in an existing pool
index (backs up `.Curator.!index` first); run against the live pool.
- Filmotéka tree **relaid out**: genre folders now sit **directly at the output
root** (next to the copy-as-is Seriály mirror), with year tags grouped under a
**`Dle roku`** folder and country tags under **`Dle země původu`**.
`HardlinkManager` gained a category → root-folder map (`category_roots`,
empty root = tag folders at the output root) and now restricts obsolete-link
cleanup to the tag-tree's own top-level folders, so copy-as-is mirrors are
never touched. The tree also groups the ČSFD rating under `Dle hodnocení`.
- ČSFD origin is now parsed as **multiple countries**: a co-production like
"USA / Velká Británie" becomes a separate **Země původu** tag per country
(so the film is filed under each), instead of one combined tag. `CSFDMovie`
gained `countries: list[str]` (replacing the single `country`); the csfd cache
schema bumped to v2 (legacy single-country caches are split on read).
- Movie table trimmed to **Název / Štítky / Velikost** — the Datum and ČSFD
columns were dropped (a ČSFD link is a prerequisite, so its indicator was
always the same).
- All references to "Tagger" renamed to "Curator" (code, spec, config filenames
`.Curator.!gtag` / `.Curator.!ftag`, tests).
- `requires-python` narrowed to `>=3.14,<3.15` (PySide6 compatibility).
### Removed
- Duplicate `src/core/constants.py` (hard-coded stale `VERSION = "v1.0.3"`). The
GUI window title imported from it, so it showed the wrong version. All imports
now use `src/constants.py` (version derived from `pyproject.toml`), which also
absorbed `APP_VIEWPORT`; the window title uses `APP_TITLE` → "Curator vX.Y.Z".
- Legacy Tagger predefined tags: the always-available **Hodnocení** (⭐ rating)
and **Barva** (color) categories in `TagManager`, and the automatic
**Stav/Nové** tag assigned to every newly imported file. `DEFAULT_TAGS` is now
empty; the pool is driven by ČSFD-derived tags (Žánr / Rok / Země původu).
### Dependencies
- Added `pyside6` (GUI), `requests` + `beautifulsoup4` (ČSFD scraping),
`truststore` (OS cert store for HTTPS). Declared `python-dotenv`, `pillow`,
`loguru` (already imported by the inherited code).