chore: cleanup after rebase

This commit is contained in:
Travis Abendshien
2026-09-03 16:46:23 -07:00
parent 45d761f6b4
commit 5f9a243fc6
5 changed files with 72 additions and 74 deletions
+8 -5
View File
@@ -3,6 +3,9 @@ title: Entry Relinking
icon: material/link-variant
---
<!-- SPDX-FileCopyrightText: (c) TagStudio Contributors -->
<!-- SPDX-License-Identifier: GPL-3.0-only -->
# :material-link-variant: Entry Relinking
### Fix Unlinked Entries
@@ -11,15 +14,15 @@ This tool displays the number of unlinked [entries](entries.md), and some option
Refresh
- Scans through the library and updates the unlinked entry count.
- Scans through the library and updates the unlinked entry count.
Search & Relink
- Attempts to automatically find and reassign missing files.
- Attempts to automatically find and reassign missing files.
Delete Unlinked Entries
- Displays a confirmation prompt containing the list of all missing files to be deleted before committing to or cancelling the operation.
- Displays a confirmation prompt containing the list of all missing files to be deleted before committing to or cancelling the operation.
### Fix Duplicate Files
@@ -27,8 +30,8 @@ This tool allows for management of duplicate files in the library using a [DupeG
Load DupeGuru File
- load the "results" file created from a DupeGuru scan
- load the "results" file created from a DupeGuru scan
Mirror Entries
- Duplicate entries will have their contents mirrored across all instances. This allows for duplicate files to then be deleted with DupeGuru as desired, without losing the [field](fields.md) data that has been assigned to either. (Once deleted, the "Fix Unlinked Entries" tool can be used to clean up the duplicates)
- Duplicate entries will have their contents mirrored across all instances. This allows for duplicate files to then be deleted with DupeGuru as desired, without losing the [field](fields.md) data that has been assigned to either. (Once deleted, the "Fix Unlinked Entries" tool can be used to clean up the duplicates)
@@ -877,8 +877,8 @@ class Library:
if search_query:
query = query.where(
or_(
Tag.name.istartswith(search_query),
Tag.shorthand.istartswith(search_query),
Tag.name.contains(search_query),
Tag.shorthand.contains(search_query),
)
)
@@ -886,7 +886,7 @@ class Library:
if search_query:
query = select(TagAlias.tag_id, TagAlias.name).where(
TagAlias.name.istartswith(search_query)
TagAlias.name.contains(search_query)
)
tags.extend(session.execute(query))
+55 -55
View File
@@ -1,6 +1,5 @@
# Copyright (C) 2025 Travis Abendshien (CyanVoxel).
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio
# SPDX-FileCopyrightText: (c) TagStudio Contributors
# SPDX-License-Identifier: MIT
import json
from copy import deepcopy
@@ -12,8 +11,6 @@ import structlog
import toml
from wcmatch import glob
from tagstudio.core.library.alchemy.fields import FieldID
if TYPE_CHECKING:
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Tag
@@ -74,16 +71,17 @@ class Instruction:
pass
class AddFieldInstruction(Instruction):
def __init__(self, content, name: FieldID, field_type: str) -> None:
super().__init__()
self.content = content
self.name = name
self.type = field_type
# TODO: Reimplement adding fields form macros
# class AddFieldInstruction(Instruction):
# def __init__(self, content, name: FieldID, field_type: str) -> None:
# super().__init__()
# self.content = content
# self.name = name
# self.type = field_type
@override
def __str__(self) -> str:
return str(self.content)
# @override
# def __str__(self) -> str:
# return str(self.content)
class AddTagInstruction(Instruction):
@@ -329,16 +327,17 @@ def _import_data(table: dict[str, Any], table_key: str, filepath: Path) -> list[
else:
continue
# TODO: Determine if the source_type is even really ever needed
# source_type: str = str(tab_value.get(SOURCE_TYPE, ""))
# TODO: Reimplement adding fields form macros
# # TODO: Determine if the source_type is even really ever needed
# # source_type: str = str(tab_value.get(SOURCE_TYPE, ""))
str_name: str = str(obj.get(NAME, FieldID.NOTES.name))
name: FieldID = FieldID.NOTES
for fid in FieldID:
field_id = str_name.upper().replace(" ", "_")
if field_id == fid.name:
name = fid
continue
# str_name: str = str(obj.get(NAME, FieldID.NOTES.name))
# name: FieldID = FieldID.NOTES
# for fid in FieldID:
# field_id = str_name.upper().replace(" ", "_")
# if field_id == fid.name:
# name = fid
# continue
if ts_type == TAGS:
use_context: bool = bool(obj.get(USE_CONTEXT, False))
@@ -395,10 +394,11 @@ def _import_data(table: dict[str, Any], table_key: str, filepath: Path) -> list[
)
)
elif ts_type in (TEXT_LINE, TEXT_BOX, DATETIME):
results.append(
AddFieldInstruction(content=content_value, name=name, field_type=ts_type)
)
# TODO: Reimplement adding fields form macros
# elif ts_type in (TEXT_LINE, TEXT_BOX, DATETIME):
# results.append(
# AddFieldInstruction(content=content_value, name=name, field_type=ts_type)
# )
else:
logger.error('[MacroParser] [{table_key}] Unknown "{TS_TYPE}"', type=ts_type)
@@ -425,25 +425,22 @@ def _add_data(table: dict[str, Any]) -> list[Instruction]:
if ts_type == TAGS:
tag_strings: list[str] = obj.get(VALUE, [])
logger.error(tag_strings)
results.append(
AddTagInstruction(
tag_strings=tag_strings,
use_context=False,
)
)
elif ts_type in (TEXT_LINE, TEXT_BOX, DATETIME):
str_name: str = str(obj.get(NAME, FieldID.NOTES.name))
name: FieldID = FieldID.NOTES
for fid in FieldID:
field_id = str_name.upper().replace(" ", "_")
if field_id == fid.name:
name = fid
continue
results.append(AddTagInstruction(tag_strings=tag_strings, use_context=False))
content_value: str = str(obj.get(VALUE, ""))
results.append(
AddFieldInstruction(content=content_value, name=name, field_type=ts_type)
)
# TODO: Reimplement adding fields form macros
# elif ts_type in (TEXT_LINE, TEXT_BOX, DATETIME):
# str_name: str = str(obj.get(NAME, FieldID.NOTES.name))
# name: FieldID = FieldID.NOTES
# for fid in FieldID:
# field_id = str_name.upper().replace(" ", "_")
# if field_id == fid.name:
# name = fid
# continue
# content_value: str = str(obj.get(VALUE, ""))
# results.append(
# AddFieldInstruction(content=content_value, name=name, field_type=ts_type)
# )
return results
@@ -477,15 +474,16 @@ def _fill_template(
return template.replace(f"{{{key}}}", f"{value}")
def exec_instructions(library: "Library", entry_id: int, results: list[Instruction]) -> None:
def exec_instructions(library: Library, entry_id: int, results: list[Instruction]) -> None:
for result in results:
if isinstance(result, AddTagInstruction):
_exec_add_tag(library, entry_id, result)
elif isinstance(result, AddFieldInstruction):
_exec_add_field(library, entry_id, result)
# TODO: Reimplement adding fields form macros
# elif isinstance(result, AddFieldInstruction):
# _exec_add_field(library, entry_id, result)
def _exec_add_tag(library: "Library", entry_id: int, result: AddTagInstruction):
def _exec_add_tag(library: Library, entry_id: int, result: AddTagInstruction):
tag_ids: set[int] = set()
for string in result.tag_strings:
if not string.strip():
@@ -497,12 +495,12 @@ def _exec_add_tag(library: "Library", entry_id: int, result: AddTagInstruction):
parent_results: list[int] = []
if len(base_and_parent) > 1:
parent = base_and_parent[1].split(")")[0]
r: list[set[Tag]] = library.search_tags(name=parent, limit=-1)
r: tuple[list[Tag], list[Tag]] = library.search_tags(name=parent, limit=-1)
if len(r) > 0:
parent_results = [t.id for t in r[0]]
# NOTE: The following code overlaps with update_tags() in tag_search.py
# Sort and prioritize the results
tag_results: list[set[Tag]] = library.search_tags(name=base, limit=-1)
tag_results: tuple[list[Tag], list[Tag]] = library.search_tags(name=base, limit=-1)
results_0 = list(tag_results[0])
results_0.sort(key=lambda tag: tag.name.lower())
results_1 = list(tag_results[1])
@@ -538,7 +536,9 @@ def _exec_add_tag(library: "Library", entry_id: int, result: AddTagInstruction):
library.add_tags_to_entries(entry_id, tag_ids)
def _exec_add_field(library: "Library", entry_id: int, result: AddFieldInstruction):
library.add_field_to_entry(
entry_id, field_id=result.name, value=result.content, skip_on_exists=True
)
# TODO: Reimplement adding fields form macros
# def _exec_add_field(library: Library, entry_id: int, result: AddFieldInstruction):
# pass
# library.add_field_to_entry(
# entry_id, field_id=result.name, value=result.content, skip_on_exists=True
# )
@@ -275,6 +275,10 @@ class Inspector(QWidget):
except Exception as e:
logger.error("[Preview Panel] Error updating selection", error=e)
def refresh_selection(self, update_preview: bool = True):
"""Refresh the panel's widgets to use current library data."""
self.set_selection(self._selected, update_preview)
def stop_media_playback(self) -> None:
"""Stop any media playback in the inspector."""
self.layout().preview_thumb.media_player.stop()
+2 -11
View File
@@ -20,8 +20,7 @@ from collections import OrderedDict
from functools import partial
from pathlib import Path
from queue import Queue
from shutil import which
from typing import Generic, TypeVar
from typing import TypeVar
from unittest.mock import Mock
from warnings import catch_warnings
@@ -41,11 +40,6 @@ from PySide6.QtGui import (
QPalette,
)
from PySide6.QtWidgets import QApplication, QFileDialog, QMessageBox, QPushButton, QScrollArea
from tagstudio.qt.controllers.ffmpeg_missing_message_box import FfmpegMissingMessageBox
from tagstudio.qt.controllers.fix_ignored_modal_controller import FixIgnoredEntriesModal
from tagstudio.qt.controllers.ignore_modal_controller import IgnoreModal
from tagstudio.qt.controllers.library_info_window_controller import LibraryInfoWindow
from tagstudio.qt.global_settings import DEFAULT_GLOBAL_SETTINGS_PATH, GlobalSettings, Theme
import tagstudio.qt.resources_rc # noqa: F401 # pyright: ignore[reportUnusedImport]
from tagstudio.core.constants import (
@@ -64,10 +58,7 @@ from tagstudio.core.library.alchemy.models import Entry
from tagstudio.core.library.ignore import Ignore
from tagstudio.core.library.refresh import RefreshTracker
from tagstudio.core.macro_parser import (
DataResult,
FieldResult,
Instruction,
TagResult,
exec_instructions,
get_macro_name,
parse_macro_file,
@@ -1821,7 +1812,7 @@ class QtDriver(DriverMixin, QObject):
self.main_window.menu_bar.folders_to_tags_action.setEnabled(True)
self.main_window.menu_bar.library_info_action.setEnabled(True)
self.main_window.preview_panel.set_selection()
self.main_window.preview_panel.set_selection(self.selected)
# page (re)rendering, extract eventually
initial_state = BrowsingState(