mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-09-04 10:09:04 +02:00
chore: cleanup after rebase
This commit is contained in:
+8
-5
@@ -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))
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user