38 KiB
src/ui/qt_app.py
PySide6 GUI for Curator — reframed around the Filmotéka (movie-library) workflow.
Layout:
- Toolbar / menu: configure the Pool and the Filmotéka output, import a movie, generate the Filmotéka hardlink tree.
- Left sidebar: tag tree used as an AND-filter over the movie list.
- Center: the movie table (the contents of pool/Filmy).
- Status bar: counts and the current pool/output paths.
Source: src/ui/qt_app.py
Classes
ImportMoviesDialog
class · line 52
class ImportMoviesDialog(QDialog):
Collect a Title + Year + TMDb id per file for a batch import into the pool.
One row per source file (filename shown; Title, Year and TMDb id editable). "Dohledat z TMDb" fills them from TMDb — by the given id, or by searching the filename. A single toggle decides whether the files are copied (default) or moved into the pool.
ImportMoviesDialog.__init__
method · line 61
__init__(parent: QWidget, sources: List[Path], existing_names: set[str] | None=None, libraries: list[str] | None=None) -> None
Build the batch import dialog for the given source files. existing_names marks names already in the pool, libraries fills the library selector.
ImportMoviesDialog._append_row
method · line 162
_append_row(source: Path) -> None
Add one source file as a row: title/year/TMDb/ČSFD fields pre-filled from its name and its sidecar metadata, a 3D format combo, the version button and a remove button.
ImportMoviesDialog._prefill_row
method · line 235
_prefill_row(source: Path, title_edit: QLineEdit, year_edit: QLineEdit, tmdb_edit: QLineEdit, csfd_edit: QLineEdit) -> None
Seed a new import row from metadata already next to the source file.
Curator's pre-index layouts win for the ČSFD link (a per-file
.{name}.!tag sidecar, or an old .Curator.!index up the tree); a
Kodi/Jellyfin .nfo fills what they don't carry (notably the TMDb id).
ImportMoviesDialog._row_of
method · line 267
_row_of(title_edit: QLineEdit)
The row entry whose title field is title_edit, or None.
ImportMoviesDialog._row_by_source
method · line 273
_row_by_source(source: Path)
The row entry for the source file source, or None.
ImportMoviesDialog._refresh_skip_marks
method · line 279
_refresh_skip_marks() -> None
Strike through the rows that "Vynechat řádky bez TMDb ID" would drop. A version row is struck only when its target would be dropped.
ImportMoviesDialog._candidate_stem
method · line 301
_candidate_stem(title: str, year: str, tmdb_id: str) -> str | None
The canonical 'Title (year) [tmdbid]' stem, or None if incomplete.
ImportMoviesDialog._mark_collision
method · line 310
_mark_collision(title_edit: QLineEdit) -> None
Red title when the resulting pooled name already exists.
ImportMoviesDialog._set_suggested
method · line 325
_set_suggested(edit: QLineEdit, suggested: bool) -> None
Blue tint marks an auto-filled value (TMDb / ČSFD), not yet confirmed.
ImportMoviesDialog._remove_row
method · line 330
_remove_row(title_edit: QLineEdit) -> None
Drop a single file from the import list without restarting the dialog.
ImportMoviesDialog._has_followers
method · line 354
_has_followers(source: Path) -> bool
True when another row is linked as a version of source.
ImportMoviesDialog._sync_version_row_fields
method · line 358
_sync_version_row_fields(source: Path) -> None
Grey out a row's own identity fields while it's linked as a version — its title/year/tmdbid/ČSFD come from the film it's a version of.
The 3D formát combo is separate: it's for a row that stays a flat,
single-file movie, so it's disabled both when the row is a version
(its 3D-ness belongs on its own popisek instead — see
_link_as_version) and when it has one (once grouped, 3D
lives on whichever version is actually 3D, not on the group itself).
ImportMoviesDialog._refresh_version_button
method · line 390
_refresh_version_button(source: Path) -> None
Update the caption and tooltip of a row's version button: "Přiřadit jako verzi…" when unlinked, the target's name when linked.
ImportMoviesDialog._link_as_version
method · line 409
_link_as_version(source: Path) -> None
Click handler of the version button. Unlinks a linked row (after confirmation), or asks for a target row and a popisek and links the row as a version of it.
ImportMoviesDialog.version_links
method · line 466
version_links() -> dict[Path, tuple[Path, str]]
{version source: (target source, popisek)} set via "Přiřadit
jako verzi…" — read by the caller to import these as versions
(FileManager.add_movie_version) instead of standalone films.
ImportMoviesDialog._add_files
method · line 472
_add_files() -> None
Add more source files chosen in a file dialog as new rows.
ImportMoviesDialog._autofill_tmdb
method · line 477
_autofill_tmdb() -> None
Fill Title/Year/TMDb-id from TMDb per row (blue = suggested, verify).
A row that already has a TMDb id is resolved by id; otherwise the filename is searched (narrowed by the year if present) and the top hit's id/title/year are filled in.
ImportMoviesDialog._autofill_via_csfd
method · line 523
_autofill_via_csfd() -> None
Fill ČSFD-odkaz / TMDb-id / Title / Year per row via the ČSFD bridge.
Per row: use the ČSFD link if given, otherwise search ČSFD by the cleaned filename. From the ČSFD film take the original title + year and search TMDb by those (Czech title as a fallback). Filled values are blue (suggested — verify). Rows that already have a value keep it.
ImportMoviesDialog.accept
method · line 594
accept() -> None
Validate the rows before the dialog closes: standalone rows need a four-digit year, version rows don't. Applies the "skip rows without TMDb ID" option first.
ImportMoviesDialog.move_files
property · line 644
move_files -> bool
Whether the files should be moved into the pool instead of copied.
ImportMoviesDialog.skipped_no_tmdb
property · line 648
skipped_no_tmdb -> list[Path]
The source files dropped by "Vynechat řádky bez TMDb ID" (valid after accept).
ImportMoviesDialog.selected_library
property · line 653
selected_library -> str
The library chosen in the library combo box.
ImportMoviesDialog.entries
method · line 656
entries() -> list[tuple[Path, str, str, str, str, str | None]]
Return (source, title, year, tmdb_id, csfd_url, format_3d) per
standalone row — a row linked as a version (see
version_links) is excluded, it isn't imported as a film of
its own. format_3d is always None for a row that has a
follower (see _has_followers) even if its combo still shows a
value — once grouped, 3D belongs to a specific version's popisek, not
the flat identity this row would otherwise import as.
ImportEpisodesDialog
class · line 679
class ImportEpisodesDialog(QDialog):
Collect a series identity + episode files, name them Series SxxEyy.ext.
ImportEpisodesDialog.__init__
method · line 682
__init__(parent: QWidget, sources: List[Path], libraries: list[str] | None=None) -> None
Build the episode import dialog for a series: series identity fields (title, year, TMDb id, library) and one row per episode file.
ImportEpisodesDialog._append_row
method · line 767
_append_row(source: Path) -> None
Add one episode file as a row. The SxxEyy field is pre-filled from the file name (including a part number) and colour-coded by validity.
ImportEpisodesDialog._mark_code
method · line 790
_mark_code(edit: QLineEdit) -> None
Colour the SxxEyy field: green = valid, red = unparsable, plain = empty.
ImportEpisodesDialog._remove_row
method · line 805
_remove_row(source: Path) -> None
Remove the row for the episode file source from the table and the row list.
ImportEpisodesDialog._add_files
method · line 812
_add_files() -> None
Add more episode files chosen in a file dialog.
ImportEpisodesDialog._add_folder
method · line 817
_add_folder() -> None
Add every video file under a picked folder (recursively, subfolders too).
ImportEpisodesDialog._autofill_tmdb
method · line 844
_autofill_tmdb() -> None
Fill title, year and TMDb id from TMDb's TV search (or the given TMDb id).
ImportEpisodesDialog.accept
method · line 870
accept() -> None
Validate the series identity and the episode list; warns when some episodes have no valid SxxEyy code (they will be skipped) and asks whether to continue.
ImportEpisodesDialog.title
property · line 892
title -> str
The series title as typed.
ImportEpisodesDialog.year
property · line 896
year -> str
The series year as typed (may be empty).
ImportEpisodesDialog.tmdb_id
property · line 900
tmdb_id -> str
The TMDb TV id as typed (may be empty).
ImportEpisodesDialog.library
property · line 904
library -> str
The series library chosen in the combo box.
ImportEpisodesDialog.move_files
property · line 908
move_files -> bool
Whether the episode files should be moved into the pool instead of copied.
ImportEpisodesDialog.sources
method · line 911
sources() -> list[Path]
The episode source files, in table order.
ImportEpisodesDialog.episode_codes
method · line 914
episode_codes() -> dict[Path, tuple[int, int, Optional[int]]]
Per-file (season, episode, part) taken from the SxxEyy column.
Covers what the user typed / corrected in the dialog; rows whose field
is empty or unparsable are omitted (they fall back to filename parsing
in import_episodes and are skipped if that fails too).
SeriesOverviewDialog
class · line 929
class SeriesOverviewDialog(QDialog):
Read-only Series → Season → Episode tree with a per-series rename action.
SeriesOverviewDialog.__init__
method · line 932
__init__(parent: QWidget, filehandler: FileManager) -> None
Build the series overview: a tree of series → seasons → episodes with buttons to edit, rename or remove the selected node.
SeriesOverviewDialog._reload
method · line 967
_reload() -> None
Rescan the series libraries and rebuild the tree. Each episode shows whether its file name is already canonical ("OK") or needs renaming.
SeriesOverviewDialog._selected_series_index
method · line 994
_selected_series_index()
Index of the series that contains the current tree selection, or None.
SeriesOverviewDialog._selected_node
method · line 1000
_selected_node()
("series"|"season"|"episode", Series, extra) for the current
selection, by tree depth — extra is None / (season, eps) /
the Episode. None when nothing is selected.
SeriesOverviewDialog._rename_selected
method · line 1015
_rename_selected() -> None
Rename the episodes of the selected series to the canonical SxxEyy - Title form and report how many were renamed, skipped or conflicting.
SeriesOverviewDialog._remove_selected
method · line 1027
_remove_selected() -> None
Delete what is selected in the tree — a whole series, one season, or one episode — after confirmation.
SeriesOverviewDialog._edit_selected
method · line 1056
_edit_selected() -> None
Open the series editor for the selected series and apply the changes: episode numbers, folder rename/move and retitled episodes.
VersionLabelDialog
class · line 1079
class VersionLabelDialog(QDialog):
Pick a label for a new movie version — a 3D formát preset or free text.
Jellyfin tells versions apart purely by this label text, so a 3D preset
just is the label (3D HSBS) — same tokens it already scans for in a
flat .3D.HSBS filename, just placed after " - " instead.
VersionLabelDialog.__init__
method · line 1087
__init__(parent: QWidget, initial: str='', intro: str='') -> None
Build the popisek dialog: a 3D format preset or free text. An empty text is allowed and means the default (plain) version.
VersionLabelDialog._sync_label
method · line 1126
_sync_label() -> None
Keep the text field in step with the 3D preset: a chosen format fills the field with 3D <FORMAT> and disables it; "vlastní popisek" re-enables it.
VersionLabelDialog.label
method · line 1131
label() -> str
The popisek as entered (stripped). Empty means the default version.
DuplicatesDialog
class · line 1135
class DuplicatesDialog(QDialog):
The Tools → Duplicate check dialog: lists groups of movies that look like the same film (same TMDb id, same title and year, or identical file size), with the reasons, and offers to merge a movie into another as a version or to delete it.
DuplicatesDialog.__init__
method · line 1143
__init__(parent: QWidget, filehandler: FileManager) -> None
Build the duplicate checker: a tree of duplicate groups with the reasons they were flagged, and merge / delete actions for the selected movie.
DuplicatesDialog._reload
method · line 1179
_reload() -> None
Recompute the duplicate groups from the file manager and rebuild the tree.
DuplicatesDialog._selected
method · line 1215
_selected() -> tuple[dict, File] | None
The selected (group, movie) pair, or None when a group header or nothing is selected.
DuplicatesDialog._sync_buttons
method · line 1225
_sync_buttons() -> None
Enable the merge and delete buttons only when a movie (not a group header) is selected.
DuplicatesDialog._merge_selected
method · line 1230
_merge_selected() -> None
Fold the selected movie into another movie of its group as a version. Asks which movie to keep (when there are several) and for the popisek of the moved file.
DuplicatesDialog._remove_selected
method · line 1260
_remove_selected() -> None
Delete the selected movie from the pool (all its versions), after confirmation.
MovieEditDialog
class · line 1282
class MovieEditDialog(QDialog):
One dialog for every editable parameter of a movie.
MovieEditDialog.__init__
method · line 1285
__init__(parent: QWidget, file_obj: File, filehandler: FileManager) -> None
Build the "Upravit film" dialog for one movie: identity fields, 3D format, attributes, subtitles and the version list with add / rename / remove buttons.
MovieEditDialog._reload_subtitles
method · line 1407
_reload_subtitles() -> None
Refill the subtitle table from the subtitle files attached to the movie.
MovieEditDialog._add_subtitle
method · line 1416
_add_subtitle() -> None
Attach a subtitle file chosen in a dialog, asking for its language and whether it is forced.
MovieEditDialog._remove_subtitle
method · line 1445
_remove_subtitle() -> None
Delete the selected subtitle file after confirmation.
MovieEditDialog._refresh_identity_fields
method · line 1462
_refresh_identity_fields() -> None
(Re)fill TMDb ID and enable/disable the identity fields for the
current grouping state of self.file_obj — called at open and again
after adding/removing a version, since that can promote or demote it.
MovieEditDialog._reload_versions
method · line 1494
_reload_versions() -> None
Refill the version table. A grouped movie lists its files with their popisek; a flat movie shows one row labelled "Výchozí".
MovieEditDialog._add_version
method · line 1518
_add_version() -> None
Add another version: pick a file and a popisek, then call add_movie_version (which promotes a flat movie to a folder).
MovieEditDialog._rename_version
method · line 1535
_rename_version() -> None
Change the popisek of the selected version (only once the movie has two or more versions).
MovieEditDialog._remove_version
method · line 1559
_remove_version() -> None
Delete the selected version after confirmation. The dialog's own file can't be removed here. Down to one version, the movie is demoted back to a flat file.
MovieEditDialog._add_attr
method · line 1587
_add_attr(key: str, value: str) -> None
Append a key/value row to the attribute table.
MovieEditDialog._remove_attr
method · line 1593
_remove_attr() -> None
Remove the selected attribute row.
MovieEditDialog._lookup
method · line 1598
_lookup() -> None
Look up the title and year from TMDb by the typed TMDb id, or by the title when no id is given.
MovieEditDialog.title
property · line 1624
title -> str
The title as typed.
MovieEditDialog.year
property · line 1628
year -> str
The year as typed (may be empty).
MovieEditDialog.tmdb_id
property · line 1632
tmdb_id -> str
The TMDb id as typed (may be empty).
MovieEditDialog.library
property · line 1636
library -> str
The library chosen in the combo box.
MovieEditDialog.date
property · line 1640
date -> str
The date-added field as typed (YYYY-MM-DD or empty).
MovieEditDialog.attributes
property · line 1644
attributes -> dict
The attribute table as a dictionary; rows without a key are ignored.
MovieEditDialog.csfd_url
property · line 1655
csfd_url -> str
The ČSFD link as typed (may be empty).
MovieEditDialog.format_3d
property · line 1659
format_3d -> str
Selected 3D tag, or "" for "— žádný —" (clears any existing tag).
SeriesEditDialog
class · line 1664
class SeriesEditDialog(QDialog):
Edit a series (title/year/id/library) and each episode's SxxEyy.
SeriesEditDialog.__init__
method · line 1667
__init__(parent: QWidget, ser, filehandler: FileManager) -> None
Build the series editor: series identity fields plus a season/episode number spin box pair for every episode.
SeriesEditDialog.accept
method · line 1721
accept() -> None
Refuse to close without a series title; otherwise accept the dialog.
SeriesEditDialog.episode_numbers
method · line 1727
episode_numbers() -> list[tuple]
The edited numbers as (episode, season, episode number) triples.
SeriesEditDialog.title
property · line 1731
title -> str
The series title as typed.
SeriesEditDialog.year
property · line 1735
year -> str
The series year as typed (may be empty).
SeriesEditDialog.tmdb_id
property · line 1739
tmdb_id -> str
The TMDb TV id as typed (may be empty).
SeriesEditDialog.library
property · line 1743
library -> str
The library chosen in the combo box.
LibraryOrganizerDialog
class · line 1747
class LibraryOrganizerDialog(QDialog):
Manage the pool's libraries (the top-level folders): name + kind.
LibraryOrganizerDialog.__init__
method · line 1757
__init__(parent: QWidget, libraries: list[dict]) -> None
Build the library list editor: one row per library folder with a kind selector.
LibraryOrganizerDialog._add_row
method · line 1798
_add_row(name: str, kind: str) -> None
Add a library row with the given name and kind.
LibraryOrganizerDialog._remove_row
method · line 1809
_remove_row() -> None
Remove the selected library row (the folder on disk is not touched).
LibraryOrganizerDialog.libraries
method · line 1814
libraries() -> list[dict]
The edited libraries as {name, kind} dictionaries; rows without a name are skipped.
MinidlnaLibrariesDialog
class · line 1826
class MinidlnaLibrariesDialog(QDialog):
Choose which pool libraries feed miniDLNA generation.
MinidlnaLibrariesDialog.__init__
method · line 1829
__init__(parent: QWidget, libraries: list[dict], selected: list[str] | None) -> None
Build the checklist of libraries that miniDLNA generation should include.
MinidlnaLibrariesDialog._set_all
method · line 1872
_set_all(state) -> None
Check or uncheck every library in the list.
MinidlnaLibrariesDialog.selected_libraries
method · line 1876
selected_libraries() -> list[str]
The names of the checked libraries.
MigrateDialog
class · line 1881
class MigrateDialog(QDialog):
Review TMDb match proposals for an existing pool and apply the ids.
MigrateDialog.__init__
method · line 1887
__init__(parent: QWidget, filehandler: FileManager, proposals: list) -> None
Build the review table of TMDb matching proposals for existing movies, with the candidate list per row.
MigrateDialog._apply
method · line 1959
_apply(only_auto: bool) -> None
Assign the chosen TMDb id to each proposal (FileManager.assign_tmdb_id, which renames the file to the canonical Title (rok) [tmdbid] form). only_auto limits it to proposals classified as confident. Failures are collected and reported; the dialog then closes.
AssignTagsDialog
class · line 1983
class AssignTagsDialog(QDialog):
Tri-state bulk tag assignment over the selected movies.
Result maps full_path -> 1 (assign), 0 (remove), 2 (leave mixed/unchanged).
AssignTagsDialog.__init__
method · line 1989
__init__(parent: QWidget, tagmanager: TagManager, files: List[File], categories: List[str] | None=None) -> None
Build the tag checklist for the selected movies. A tag shown as partially checked is present on only some of them.
AssignTagsDialog.tagmanager_categories
static method · line 2028
tagmanager_categories(tagmanager: TagManager) -> List[str]
The category names of the tag manager, sorted.
AssignTagsDialog._category_item
method · line 2031
_category_item(category: str) -> QTreeWidgetItem
Return (creating if needed) the header item for a category.
AssignTagsDialog._add_tag_item
method · line 2041
_add_tag_item(category: str, name: str, force_checked: bool=False)
Add a checkable tag row under its category (deduplicated).
AssignTagsDialog._add_new_tag
method · line 2063
_add_new_tag() -> None
Prompt for a category + value and add it as a checked tag.
AssignTagsDialog.accept
method · line 2079
accept() -> None
Store the checkbox state of every tag in result_map and accept the dialog.
TagSchemaDialog
class · line 2085
class TagSchemaDialog(QDialog):
Editor for the tag schema: categories, their NFO source and folder rules.
Each row defines a tag category, optionally which NFO field feeds it (with a value transform), and how it maps into the miniDLNA tree (a grouping folder, the output root, or no folders at all).
TagSchemaDialog.__init__
method · line 2106
__init__(parent: QWidget, schema: list[dict]) -> None
Build the editor of the tag schema: one row per tag category with its NFO source, whether it creates folders, and the folder name rules.
TagSchemaDialog._append_row
method · line 2151
_append_row(entry: dict) -> None
Add a category row filled from a schema entry.
TagSchemaDialog._combo_index
static method · line 2185
_combo_index(choices: list, value) -> int
Index of the entry whose value is value in a (label, value) choice list, or 0.
TagSchemaDialog._remove_selected
method · line 2191
_remove_selected() -> None
Remove the selected category row.
TagSchemaDialog._save
method · line 2196
_save() -> None
Read the table back into the schema list result_schema: one entry per row with a category, holding its CSFD field, value transform, miniDLNA folder root (only when "make folders" is ticked) and filename template. Then accept the dialog.
StatsDialog
class · line 2216
class StatsDialog(QDialog):
Read-only overview of the pool: totals + per-category tag breakdown.
StatsDialog.__init__
method · line 2219
__init__(parent: QWidget, stats: dict, size_text: str) -> None
Show the library statistics (movie count, total size, average rating and the other figures from FileManager.statistics).
QtApp
class · line 2254
class QtApp(QMainWindow):
The main window: menus, the tag sidebar, the movie and series table, the detail panel and the status bar. It delegates all work to FileManager.
QtApp.__init__
method · line 2255
__init__(filehandler: FileManager, tagmanager: TagManager) -> None
Build the main window around the file manager and the tag manager, then load the pool and restore the window geometry.
QtApp.reload_series
method · line 2285
reload_series() -> None
Refresh the cached series list (scans the series/mixed libraries).
QtApp._build_menu
method · line 2293
_build_menu() -> None
Create the menu bar: Pool, Filmy, Seriály, miniDLNA, Jellyfin, Nástroje, Nastavení and Testy.
QtApp._add_action
method · line 2355
_add_action(menu: QMenu, text: str, slot, shortcut: str | None=None) -> QAction
Create a menu action with an optional keyboard shortcut and connect it to slot.
QtApp._build_central
method · line 2363
_build_central() -> None
Build the central widget: the tag sidebar, the movie/series table and the detail panel, with a splitter between them.
QtApp._update_details
method · line 2443
_update_details() -> None
Fill the right panel from the single selected item (movie or series).
QtApp._build_statusbar
method · line 2469
_build_statusbar() -> None
Create the status bar with the pool, output and Jellyfin state labels.
QtApp._update_jellyfin_status
method · line 2482
_update_jellyfin_status(version: str | None) -> None
Show Jellyfin connection state; None = not checked / not configured.
QtApp.refresh_sidebar
method · line 2496
refresh_sidebar(filtered: Optional[List[File]]=None) -> None
Rebuild the filter tree, preserving the active filter and updating counts.
The count after each tag is how many of filtered (the movies matching
the current filter; all movies when nothing is checked) also carry that
tag — i.e. how many would remain if that tag were checked. Check state is
restored from self._active_filter so it survives the rebuild.
QtApp._on_tag_filter_changed
method · line 2536
_on_tag_filter_changed(item, _col) -> None
Update the active AND filter when a tag checkbox in the sidebar is toggled. Category header rows are ignored.
QtApp._active_filter_tags
method · line 2549
_active_filter_tags() -> List[Tag]
The active filter as Tag objects.
QtApp._refresh_library_filter
method · line 2560
_refresh_library_filter() -> None
Repopulate the library filter combo, keeping the current choice.
QtApp._selected_library_filter
method · line 2574
_selected_library_filter() -> str | None
The library the table is filtered to, or None for 'Vše'.
QtApp.refresh_table
method · line 2581
refresh_table(*_args) -> None
Rebuild the table from the current state: the tag filter, the library filter and the search text are applied to movies; series are filtered by library and search only (they carry no tags). Each movie group appears once, via its primary version.
QtApp._versions_word
static method · line 2620
_versions_word(count: int) -> str
The Czech plural form of "version" for a count (verze for 2–4, verzí otherwise).
QtApp._fill_row
method · line 2623
_fill_row(row: int, item) -> None
Fill one table row from a movie (File) or a series.Series.
A movie made of several media files (a version folder) is marked so it
doesn't pass for a single file: ▣ before the name in bold, the
version count in the Typ column, the summed size and a tooltip listing
every file.
QtApp._item_size
method · line 2655
_item_size(item) -> str
Formatted size of a movie (all its versions), or a series' episodes.
QtApp._format_size
static method · line 2668
_format_size(size_bytes: float) -> str
Format a byte count with a binary unit (B, KB, MB, GB, TB), one decimal place.
QtApp._selected_items
method · line 2675
_selected_items() -> list
The row objects (File or series) for the selected table rows, in row order.
QtApp._selected_movies
method · line 2679
_selected_movies() -> List[File]
The selected rows that are movies.
QtApp._selected_series
method · line 2682
_selected_series() -> list
The selected rows that are series.
QtApp.edit_item
method · line 2685
edit_item() -> None
Open the right editor for the single selected row (movie or series).
QtApp._show_table_menu
method · line 2696
_show_table_menu(pos) -> None
Show the context menu of the table for the current selection: series actions for a series-only selection, movie actions otherwise.
QtApp._update_selection_status
method · line 2721
_update_selection_status() -> None
Refresh the detail panel and the status bar for the current selection.
QtApp._update_path_status
method · line 2735
_update_path_status() -> None
Show the pool and output paths and the number of movies in the status bar.
QtApp.set_pool
method · line 2746
set_pool() -> None
Choose the pool folder, then reload the movies, series and library filter.
QtApp.set_filmoteka
method · line 2757
set_filmoteka() -> None
Choose the Filmotéka output folder.
QtApp.reload_pool
method · line 2764
reload_pool() -> None
Reload the movies and series from the pool and refresh the table.
QtApp.import_movie
method · line 2772
import_movie() -> None
Batch-import movie files: pick files, review them in ImportMoviesDialog, resolve collisions with films already in the pool (add as version, replace, or skip), then import the rows and attach version-linked rows to their targets.
QtApp._resolve_import_conflicts
method · line 2888
_resolve_import_conflicts(names: list[str]) -> str | None
Ask what to do with films already in the pool. Returns the policy
for FileManager.import_movie — "version" (add the new file
as another version of that film), "replace" (the whole old film
goes, then the new one is imported) or "skip" — or None when
the user cancels the import.
There's deliberately no "keep both as separate films": these share a TMDb id, so they would share one movie record anyway — the same film twice is a duplicate, and a duplicate is what versions are for.
QtApp.open_movies
method · line 2923
open_movies() -> None
Open the selected movie files with the system viewer.
QtApp._open_path
method · line 2927
_open_path(path: Path) -> None
Open a file with the platform's default application (startfile, open or xdg-open).
QtApp.assign_tags
method · line 2938
assign_tags() -> None
Assign or remove tags on the selected movies through AssignTagsDialog.
QtApp.set_date
method · line 2955
set_date() -> None
Set or clear the date-added field on the selected movies.
QtApp.set_attribute
method · line 2967
set_attribute() -> None
Set or remove one free-form attribute on the selected movies.
QtApp.rename_movie
method · line 2991
rename_movie() -> None
Rename the single selected movie's file (keeping its extension).
QtApp.edit_movie
method · line 3012
edit_movie() -> None
Open the consolidated edit dialog for the selected movie.
QtApp.edit_series
method · line 3035
edit_series(ser) -> None
Open the series editor (params + per-episode SxxEyy tree) for ser.
QtApp.rename_to_canonical_naming
method · line 3054
rename_to_canonical_naming() -> None
Rename movies to the 'Title (rok) [tmdbid].ext' convention from metadata.
QtApp.import_episodes
method · line 3116
import_episodes() -> None
Import episode files into a series (Series/Season NN/Series SxxEyy).
The dialog opens empty — add episodes file-by-file or pick a whole series folder (its subfolders are scanned too).
QtApp.show_series
method · line 3144
show_series() -> None
Open the series overview (SeriesOverviewDialog) and refresh the table afterwards.
QtApp.rename_all_series
method · line 3152
rename_all_series() -> None
Bulk-fix every series' episode filenames to SxxEyy - {série}.ext.
QtApp.normalize_default_names
method · line 3217
normalize_default_names() -> None
Tools menu action: rename <folder> - Výchozí.ext files of grouped movies to the bare <folder>.ext name after confirmation.
QtApp.check_duplicates
method · line 3235
check_duplicates() -> None
Tools menu action: open DuplicatesDialog and refresh the table if it changed anything.
QtApp.migrate_pool
method · line 3245
migrate_pool() -> None
Match the existing pool against TMDb and assign ids (one-off migration).
QtApp.refresh_from_tmdb
method · line 3279
refresh_from_tmdb() -> None
(Re)load genre/year/rating tags from TMDb (by the tmdbid in the name).
QtApp.remove_movies
method · line 3309
remove_movies() -> None
Delete the selected movies from the pool (every version of a grouped movie is removed too), after confirmation.
QtApp.remove_series
method · line 3330
remove_series(series_list: list | None=None) -> None
Delete whole series folders from the pool (all seasons/episodes).
QtApp.select_minidlna_libraries
method · line 3352
select_minidlna_libraries() -> None
Choose which pool libraries feed miniDLNA generation (persisted).
QtApp.generate_filmoteka
method · line 3370
generate_filmoteka() -> None
Generate the miniDLNA / Filmotéka output for the libraries in scope. Refreshes genres from TMDb, syncs the genre hardlink tree (HardlinkManager.sync_structure, which also removes obsolete links), hardlinks mirrored libraries 1:1 and rebuilds the mixed-library series bucket. Empty scope shows an information message instead.
QtApp.edit_libraries
method · line 3464
edit_libraries() -> None
Open the library organizer (manage the pool's top-level libraries).
QtApp._cleanup_orphan_folders
method · line 3483
_cleanup_orphan_folders() -> None
Offer to delete empty pool folders that are no longer libraries.
QtApp.move_to_library
method · line 3509
move_to_library() -> None
Move the selected movies into another library.
QtApp._jellyfin_ready
method · line 3539
_jellyfin_ready() -> bool
True when the Jellyfin URL and API key are configured; otherwise shows a warning and returns False.
QtApp._jellyfin_call
method · line 3549
_jellyfin_call(fn)
Run a Jellyfin call under a wait cursor; return (result, error_str).
QtApp.jellyfin_check_connection
method · line 3560
jellyfin_check_connection() -> None
Check that the configured Jellyfin server responds and show its version.
QtApp.jellyfin_scan
method · line 3572
jellyfin_scan() -> None
Ask Jellyfin to scan its libraries.
QtApp.jellyfin_verify
method · line 3586
jellyfin_verify() -> None
Fetch the movie list from Jellyfin and compare it with the pool (FileManager.verify_against_jellyfin). Reports the matched count and lists movies present only in the pool or only in Jellyfin.
QtApp.edit_tag_schema
method · line 3627
edit_tag_schema() -> None
Edit the tag schema through TagSchemaDialog and store the result in the pool index.
QtApp.show_statistics
method · line 3639
show_statistics() -> None
Show the library statistics in StatsDialog; the pool must not be empty.
QtApp.check_consistency
method · line 3646
check_consistency() -> None
Test: does the pool index match the actual files in pool/Filmy?
QtApp.check_video_integrity
method · line 3689
check_video_integrity() -> None
Test: scan movies for corrupted / unreadable video data (FFmpeg).
QtApp.closeEvent
method · line 3772
closeEvent(event) -> None
Save the window size to the global config before the window closes.
Functions
run
function · line 3779
run(filehandler: FileManager, tagmanager: TagManager) -> None
Create the Qt application (if none exists yet), show the main window and run the event loop until it closes.