Files
Tagger/PROJECT.md

3.9 KiB

Tagger - Project Documentation

Version: 1.2.0 | Status: Stable | GUI: PySide6/Qt6


About

Desktop app for organizing files using hierarchical tags (category/name).

Features: Folder scanning, tag filtering (AND/OR/NOT), rename/merge tags, CSFD.cz integration (with local cache), hardlink structure, 3-level config (global/folder/file), orphaned sidecar detection.


Structure

Tagger/
├── Tagger.py              # Entry point
├── src/core/              # Business logic (NO UI imports!)
│   ├── tag.py             # Tag value object (immutable)
│   ├── tag_manager.py     # Tag/category management
│   ├── file.py            # File with metadata + CSFD cache
│   ├── file_manager.py    # File management, filtering, orphan detection
│   ├── config.py          # 3-level config system
│   ├── hardlink_manager.py
│   ├── csfd.py            # CSFD scraper + CSFDMovie serialization
│   ├── media_utils.py     # ffprobe integration
│   ├── constants.py       # APP_NAME, VERSION
│   └── _version.py        # Version fallback for PyInstaller
├── src/ui/
│   ├── gui.py             # Qt6 GUI (MainWindow)
│   └── utils.py           # load_icon()
└── tests/                 # 274 tests

Architecture Rules

  1. UI must not contain business logic — call FileManager/TagManager
  2. Core must not import UI — no PySide6 in src/core/
  3. Dependency injection — pass via constructor
  4. UTF-8 everywhereencoding='utf-8', ensure_ascii=False

Config Files

Level File Location Contents
Global .Tagger.!gtag ~/.config/Tagger/ window geometry, last folder, recent folders
Folder .Tagger.!ftag project folder ignore patterns, hardlink settings
File .filename.!tag same dir as file tags, date, csfd_url, csfd_cache

Key Components

Tag — immutable, Tag(category, name), Tag.from_string("cat/name")

Filefile_path, tags[], date, csfd_url, csfd_cache, metadata in .filename.!tag

  • apply_csfd_tags() — fetch + cache CSFD data
  • get_cached_movie() — return CSFDMovie from cache (no network)
  • set_csfd_url() — invalidates cache on URL change

TagManageradd_tag(), get_categories(), rename_tag(), merge_tag()

FileManagerappend(folder), filter_files_by_tags(), close_folder()

  • on_orphaned_tags callback — fires when orphaned .!tag sidecars are found
  • find_orphaned_tags() — manual scan for orphaned sidecars

HardlinkManagercreate_structure_for_files(), sync_structure()

CSFDMovieto_dict() / from_dict() for cache serialization


Filtering

# AND (default — all must match)
fm.filter_files_by_tags(["Žánr/Drama", "Rok/1990"])

# OR — at least one must match
fm.filter_files_by_tags(any_of=["Žánr/Drama", "Žánr/Thriller"])

# NOT — none of these
fm.filter_files_by_tags(must_not=["Stav/Nové"])

# Combined
fm.filter_files_by_tags(
    must_have=["Žánr/Drama"],
    any_of=["Rok/1990", "Rok/1991"],
    must_not=["Stav/Nové"],
)

Running

poetry run python Tagger.py      # GUI
poetry run pytest -q             # Tests
poetry run pyinstaller --onefile Tagger.py  # Build

Shortcuts

Ctrl+O Open | Ctrl+T Tags | Ctrl+D Date | Ctrl+W Close | F5 Refresh | Del Remove


Debugging

Version 0.0.0 in build: Run app once from source to update _version.py, then rebuild.

Cannot import: Use poetry run python Tagger.py

Metadata corrupted: Auto-recovers with defaults.

Config not saved: Check ~/.config/Tagger/ exists and is writable.



Metrics

  • Tests: 274
  • Python: 3.14+
  • Dependencies: PySide6, requests, beautifulsoup4, loguru, python-dotenv