Skip to content

Commit 66ec091

Browse files
committed
Revert "Add duplicate entry handling (Fix #179)"
This reverts commit 491ebb6.
1 parent 6357fea commit 66ec091

File tree

7 files changed

+326
-280
lines changed

7 files changed

+326
-280
lines changed

tagstudio/src/core/library.py

Lines changed: 155 additions & 95 deletions
Large diffs are not rendered by default.

tagstudio/src/qt/modals/delete_unlinked.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def __init__(self, library: "Library", driver: "QtDriver"):
3232
super().__init__()
3333
self.lib = library
3434
self.driver = driver
35-
self.setWindowTitle("Delete Unlinked Entries")
35+
self.setWindowTitle(f"Delete Unlinked Entries")
3636
self.setWindowModality(Qt.WindowModality.ApplicationModal)
3737
self.setMinimumSize(500, 400)
3838
self.root_layout = QVBoxLayout(self)
@@ -81,6 +81,20 @@ def refresh_list(self):
8181
self.model.appendRow(QStandardItem(i))
8282

8383
def delete_entries(self):
84+
# pb = QProgressDialog('', None, 0, len(self.lib.missing_files))
85+
# # pb.setMaximum(len(self.lib.missing_files))
86+
# pb.setFixedSize(432, 112)
87+
# pb.setWindowFlags(pb.windowFlags() & ~Qt.WindowType.WindowCloseButtonHint)
88+
# pb.setWindowTitle('Deleting Entries')
89+
# pb.setWindowModality(Qt.WindowModality.ApplicationModal)
90+
# pb.show()
91+
92+
# r = CustomRunnable(lambda: self.lib.ref(pb))
93+
# r.done.connect(lambda: self.done.emit())
94+
# # r.done.connect(lambda: self.model.clear())
95+
# QThreadPool.globalInstance().start(r)
96+
# # r.run()
97+
8498
iterator = FunctionIterator(self.lib.remove_missing_files)
8599

86100
pw = ProgressWidget(
@@ -105,3 +119,23 @@ def delete_entries(self):
105119
r = CustomRunnable(lambda: iterator.run())
106120
QThreadPool.globalInstance().start(r)
107121
r.done.connect(lambda: (pw.hide(), pw.deleteLater(), self.done.emit()))
122+
123+
# def delete_entries_runnable(self):
124+
# deleted = []
125+
# for i, missing in enumerate(self.lib.missing_files):
126+
# # pb.setValue(i)
127+
# # pb.setLabelText(f'Deleting {i}/{len(self.lib.missing_files)} Unlinked Entries')
128+
# try:
129+
# id = self.lib.get_entry_id_from_filepath(missing)
130+
# logging.info(f'Removing Entry ID {id}:\n\t{missing}')
131+
# self.lib.remove_entry(id)
132+
# self.driver.purge_item_from_navigation(ItemType.ENTRY, id)
133+
# deleted.append(missing)
134+
# except KeyError:
135+
# logging.info(
136+
# f'{ERROR} \"{id}\" was reported as missing, but is not in the file_to_entry_id map.')
137+
# yield i
138+
# for d in deleted:
139+
# self.lib.missing_files.remove(d)
140+
# # self.driver.filter_items('')
141+
# # self.done.emit()

tagstudio/src/qt/modals/fix_unlinked.py

Lines changed: 82 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -6,95 +6,80 @@
66
import logging
77
import typing
88

9-
from PySide6.QtCore import Qt, QThreadPool
9+
from PySide6.QtCore import QThread, Qt, QThreadPool
1010
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton
1111

1212
from src.core.library import Library
1313
from src.qt.helpers.function_iterator import FunctionIterator
1414
from src.qt.helpers.custom_runnable import CustomRunnable
1515
from src.qt.modals.delete_unlinked import DeleteUnlinkedEntriesModal
1616
from src.qt.modals.relink_unlinked import RelinkUnlinkedEntries
17-
from src.qt.modals.merge_dupe_entries import MergeDuplicateEntries
1817
from src.qt.widgets.progress import ProgressWidget
1918

2019
# Only import for type checking/autocompletion, will not be imported at runtime.
2120
if typing.TYPE_CHECKING:
2221
from src.qt.ts_qt import QtDriver
2322

2423

25-
ERROR = "[ERROR]"
26-
WARNING = "[WARNING]"
27-
INFO = "[INFO]"
24+
ERROR = f"[ERROR]"
25+
WARNING = f"[WARNING]"
26+
INFO = f"[INFO]"
2827

2928
logging.basicConfig(format="%(message)s", level=logging.INFO)
3029

3130

3231
class FixUnlinkedEntriesModal(QWidget):
32+
# done = Signal(int)
3333
def __init__(self, library: "Library", driver: "QtDriver"):
3434
super().__init__()
3535
self.lib = library
3636
self.driver = driver
37-
self.missing_count = -1
38-
self.dupe_count = -1
39-
self.setWindowTitle("Fix Unlinked Entries")
37+
self.count = -1
38+
self.setWindowTitle(f"Fix Unlinked Entries")
4039
self.setWindowModality(Qt.WindowModality.ApplicationModal)
4140
self.setMinimumSize(400, 300)
4241
self.root_layout = QVBoxLayout(self)
4342
self.root_layout.setContentsMargins(6, 6, 6, 6)
4443

45-
self.unlinked_desc_widget = QLabel()
46-
self.unlinked_desc_widget.setObjectName("unlinkedDescriptionLabel")
47-
self.unlinked_desc_widget.setWordWrap(True)
48-
self.unlinked_desc_widget.setStyleSheet("text-align:left;")
49-
self.unlinked_desc_widget.setText(
50-
"""Each library entry is linked to a file in one of your directories. If a file linked to an entry is moved or deleted outside of TagStudio, it is then considered unlinked. Unlinked entries may be automatically relinked via searching your directories, manually relinked by the user, or deleted if desired."""
44+
self.desc_widget = QLabel()
45+
self.desc_widget.setObjectName("descriptionLabel")
46+
self.desc_widget.setWordWrap(True)
47+
self.desc_widget.setStyleSheet(
48+
# 'background:blue;'
49+
"text-align:left;"
50+
# 'font-weight:bold;'
51+
# 'font-size:14px;'
52+
# 'padding-top: 6px'
53+
""
5154
)
52-
53-
self.dupe_desc_widget = QLabel()
54-
self.dupe_desc_widget.setObjectName("dupeDescriptionLabel")
55-
self.dupe_desc_widget.setWordWrap(True)
56-
self.dupe_desc_widget.setStyleSheet("text-align:left;")
57-
self.dupe_desc_widget.setText(
58-
"""Duplicate entries are defined as multiple entries which point to the same file on disk. Merging these will combine the tags and metadata from all duplicates into a single consolidated entry. These are not to be confused with "duplicate files", which are duplicates of your files themselves outside of TagStudio."""
59-
)
60-
61-
self.missing_count_label = QLabel()
62-
self.missing_count_label.setObjectName("missingCountLabel")
63-
self.missing_count_label.setStyleSheet("font-weight:bold;" "font-size:14px;")
64-
self.missing_count_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
65-
66-
self.dupe_count_label = QLabel()
67-
self.dupe_count_label.setObjectName("dupeCountLabel")
68-
self.dupe_count_label.setStyleSheet("font-weight:bold;" "font-size:14px;")
69-
self.dupe_count_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
70-
71-
self.refresh_unlinked_button = QPushButton()
72-
self.refresh_unlinked_button.setText("&Refresh All")
73-
self.refresh_unlinked_button.clicked.connect(
74-
lambda: self.refresh_missing_files()
55+
self.desc_widget.setText("""Each library entry is linked to a file in one of your directories. If a file linked to an entry is moved or deleted outside of TagStudio, it is then considered unlinked.
56+
Unlinked entries may be automatically relinked via searching your directories, manually relinked by the user, or deleted if desired.""")
57+
self.desc_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
58+
59+
self.missing_count = QLabel()
60+
self.missing_count.setObjectName("missingCountLabel")
61+
self.missing_count.setStyleSheet(
62+
# 'background:blue;'
63+
# 'text-align:center;'
64+
"font-weight:bold;"
65+
"font-size:14px;"
66+
# 'padding-top: 6px'
67+
""
7568
)
69+
self.missing_count.setAlignment(Qt.AlignmentFlag.AlignCenter)
70+
# self.missing_count.setText('Missing Files: N/A')
7671

77-
self.merge_class = MergeDuplicateEntries(self.lib, self.driver)
78-
self.relink_class = RelinkUnlinkedEntries(self.lib, self.driver)
72+
self.refresh_button = QPushButton()
73+
self.refresh_button.setText("&Refresh")
74+
self.refresh_button.clicked.connect(lambda: self.refresh_missing_files())
7975

8076
self.search_button = QPushButton()
8177
self.search_button.setText("&Search && Relink")
82-
self.relink_class.done.connect(
83-
lambda: self.refresh_and_repair_dupe_entries(self.merge_class)
84-
)
78+
self.relink_class = RelinkUnlinkedEntries(self.lib, self.driver)
79+
self.relink_class.done.connect(lambda: self.refresh_missing_files())
80+
self.relink_class.done.connect(lambda: self.driver.update_thumbs())
8581
self.search_button.clicked.connect(lambda: self.relink_class.repair_entries())
8682

87-
self.refresh_dupe_button = QPushButton()
88-
self.refresh_dupe_button.setText("Refresh Duplicate Entries")
89-
self.refresh_dupe_button.clicked.connect(lambda: self.refresh_dupe_entries())
90-
91-
self.merge_dupe_button = QPushButton()
92-
self.merge_dupe_button.setText("&Merge Duplicate Entries")
93-
self.merge_class.done.connect(lambda: self.set_dupe_count(-1))
94-
self.merge_class.done.connect(lambda: self.set_missing_count(-1))
95-
self.merge_class.done.connect(lambda: self.driver.filter_items())
96-
self.merge_dupe_button.clicked.connect(lambda: self.merge_class.merge_entries())
97-
9883
self.manual_button = QPushButton()
9984
self.manual_button.setText("&Manual Relink")
10085

@@ -107,46 +92,65 @@ def __init__(self, library: "Library", driver: "QtDriver"):
10792
self.delete_button.setText("De&lete Unlinked Entries")
10893
self.delete_button.clicked.connect(lambda: self.delete_modal.show())
10994

95+
# self.combo_box = QComboBox()
96+
# self.combo_box.setEditable(False)
97+
# # self.combo_box.setMaxVisibleItems(5)
98+
# self.combo_box.setStyleSheet('combobox-popup:0;')
99+
# self.combo_box.view().setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
100+
# for df in self.lib.default_fields:
101+
# self.combo_box.addItem(f'{df["name"]} ({df["type"].replace("_", " ").title()})')
102+
110103
self.button_container = QWidget()
111104
self.button_layout = QHBoxLayout(self.button_container)
112105
self.button_layout.setContentsMargins(6, 6, 6, 6)
113106
self.button_layout.addStretch(1)
114107

115108
self.done_button = QPushButton()
116109
self.done_button.setText("&Done")
110+
# self.save_button.setAutoDefault(True)
117111
self.done_button.setDefault(True)
118112
self.done_button.clicked.connect(self.hide)
113+
# self.done_button.clicked.connect(lambda: self.done.emit(self.combo_box.currentIndex()))
114+
# self.save_button.clicked.connect(lambda: save_callback(widget.get_content()))
119115
self.button_layout.addWidget(self.done_button)
120116

121-
self.root_layout.addWidget(self.missing_count_label)
122-
self.root_layout.addWidget(self.unlinked_desc_widget)
123-
self.root_layout.addWidget(self.refresh_unlinked_button)
117+
# self.returnPressed.connect(lambda: self.done.emit(self.combo_box.currentIndex()))
118+
119+
# self.done.connect(lambda x: callback(x))
120+
121+
self.root_layout.addWidget(self.desc_widget)
122+
self.root_layout.addWidget(self.missing_count)
123+
self.root_layout.addWidget(self.refresh_button)
124124
self.root_layout.addWidget(self.search_button)
125125
self.manual_button.setHidden(True)
126126
self.root_layout.addWidget(self.manual_button)
127127
self.root_layout.addWidget(self.delete_button)
128+
# self.root_layout.setStretch(1,2)
128129
self.root_layout.addStretch(1)
129-
self.root_layout.addWidget(self.dupe_count_label)
130-
self.root_layout.addWidget(self.dupe_desc_widget)
131-
self.root_layout.addWidget(self.refresh_dupe_button)
132-
self.root_layout.addWidget(self.merge_dupe_button)
133-
self.root_layout.addStretch(2)
134130
self.root_layout.addWidget(self.button_container)
135131

136-
self.set_missing_count(self.missing_count)
137-
self.set_dupe_count(self.dupe_count)
132+
self.set_missing_count(self.count)
138133

139134
def refresh_missing_files(self):
135+
logging.info(f"Start RMF: {QThread.currentThread()}")
136+
# pb = QProgressDialog(f'Scanning Library for Unlinked Entries...', None, 0,len(self.lib.entries))
137+
# pb.setFixedSize(432, 112)
138+
# pb.setWindowFlags(pb.windowFlags() & ~Qt.WindowType.WindowCloseButtonHint)
139+
# pb.setWindowTitle('Scanning Library')
140+
# pb.setWindowModality(Qt.WindowModality.ApplicationModal)
141+
# pb.show()
142+
140143
iterator = FunctionIterator(self.lib.refresh_missing_files)
141144
pw = ProgressWidget(
142145
window_title="Scanning Library",
143-
label_text="Scanning Library for Unlinked Entries...",
146+
label_text=f"Scanning Library for Unlinked Entries...",
144147
cancel_button_text=None,
145148
minimum=0,
146149
maximum=len(self.lib.entries),
147150
)
148151
pw.show()
149152
iterator.value.connect(lambda v: pw.update_progress(v + 1))
153+
# rmf.value.connect(lambda v: pw.update_label(f'Progress: {v}'))
150154
r = CustomRunnable(lambda: iterator.run())
151155
QThreadPool.globalInstance().start(r)
152156
r.done.connect(
@@ -155,76 +159,30 @@ def refresh_missing_files(self):
155159
pw.deleteLater(),
156160
self.set_missing_count(len(self.lib.missing_files)),
157161
self.delete_modal.refresh_list(),
158-
self.refresh_dupe_entries(),
159162
)
160163
)
161164

162-
def refresh_dupe_entries(self):
163-
iterator = FunctionIterator(self.lib.refresh_dupe_entries)
164-
pw = ProgressWidget(
165-
window_title="Scanning Library",
166-
label_text="Scanning Library for Duplicate Entries...",
167-
cancel_button_text=None,
168-
minimum=0,
169-
maximum=len(self.lib.entries),
170-
)
171-
pw.show()
172-
iterator.value.connect(lambda v: pw.update_progress(v + 1))
173-
r = CustomRunnable(lambda: iterator.run())
174-
QThreadPool.globalInstance().start(r)
175-
r.done.connect(
176-
lambda: (
177-
pw.hide(),
178-
pw.deleteLater(),
179-
self.set_dupe_count(len(self.lib.dupe_entries)),
180-
)
181-
)
165+
# r = CustomRunnable(lambda: self.lib.refresh_missing_files(lambda v: self.update_scan_value(pb, v)))
166+
# r.done.connect(lambda: (pb.hide(), pb.deleteLater(), self.set_missing_count(len(self.lib.missing_files)), self.delete_modal.refresh_list()))
167+
# QThreadPool.globalInstance().start(r)
168+
# # r.run()
169+
# pass
182170

183-
def refresh_and_repair_dupe_entries(self, merge_class: MergeDuplicateEntries):
184-
iterator = FunctionIterator(self.lib.refresh_dupe_entries)
185-
pw = ProgressWidget(
186-
window_title="Scanning Library",
187-
label_text="Scanning Library for Duplicate Entries...",
188-
cancel_button_text=None,
189-
minimum=0,
190-
maximum=len(self.lib.entries),
191-
)
192-
pw.show()
193-
iterator.value.connect(lambda v: pw.update_progress(v + 1))
194-
r = CustomRunnable(lambda: iterator.run())
195-
QThreadPool.globalInstance().start(r)
196-
r.done.connect(
197-
lambda: (
198-
pw.hide(),
199-
pw.deleteLater(),
200-
self.set_dupe_count(len(self.lib.dupe_entries)),
201-
merge_class.merge_entries(),
202-
)
203-
)
171+
# def update_scan_value(self, pb:QProgressDialog, value=int):
172+
# # pb.setLabelText(f'Scanning Library for Unlinked Entries ({value}/{len(self.lib.entries)})...')
173+
# pb.setValue(value)
204174

205175
def set_missing_count(self, count: int):
206-
self.missing_count = count
207-
if self.missing_count < 0:
176+
self.count = count
177+
if self.count < 0:
208178
self.search_button.setDisabled(True)
209179
self.delete_button.setDisabled(True)
210-
self.missing_count_label.setText("Unlinked Entries: N/A")
211-
elif self.missing_count == 0:
180+
self.missing_count.setText(f"Unlinked Entries: N/A")
181+
elif self.count == 0:
212182
self.search_button.setDisabled(True)
213183
self.delete_button.setDisabled(True)
214-
self.missing_count_label.setText(f"Unlinked Entries: {count}")
184+
self.missing_count.setText(f"Unlinked Entries: {count}")
215185
else:
216186
self.search_button.setDisabled(False)
217187
self.delete_button.setDisabled(False)
218-
self.missing_count_label.setText(f"Unlinked Entries: {count}")
219-
220-
def set_dupe_count(self, count: int):
221-
self.dupe_count = count
222-
if self.dupe_count < 0:
223-
self.dupe_count_label.setText("Duplicate Entries: N/A")
224-
self.merge_dupe_button.setDisabled(True)
225-
elif self.dupe_count == 0:
226-
self.dupe_count_label.setText(f"Duplicate Entries: {count}")
227-
self.merge_dupe_button.setDisabled(True)
228-
else:
229-
self.dupe_count_label.setText(f"Duplicate Entries: {count}")
230-
self.merge_dupe_button.setDisabled(False)
188+
self.missing_count.setText(f"Unlinked Entries: {count}")

0 commit comments

Comments
 (0)