Rewrite blacklist: paste-friendly text box, toggle, client-side filter

- Replace one-at-a-time tag list with a text box (paste space or
  newline separated tags)
- Add enable/disable checkbox for blacklist
- Switch from server-side -tag appending (broke tag limits) to
  client-side post filtering
- Import merges with existing tags
This commit is contained in:
pax 2026-04-04 21:21:49 -05:00
parent e8f72c6fe6
commit d3f384d5f9
2 changed files with 32 additions and 62 deletions

View File

@ -558,10 +558,6 @@ class BooruApp(QMainWindow):
if self._min_score > 0: if self._min_score > 0:
parts.append(f"score:>={self._min_score}") parts.append(f"score:>={self._min_score}")
# Append blacklisted tags as negatives
for tag in self._db.get_blacklisted_tags():
parts.append(f"-{tag}")
return " ".join(parts) return " ".join(parts)
def _do_search(self) -> None: def _do_search(self) -> None:
@ -590,6 +586,11 @@ class BooruApp(QMainWindow):
self._run_async(_search) self._run_async(_search)
def _on_search_done(self, posts: list) -> None: def _on_search_done(self, posts: list) -> None:
# Client-side blacklist filtering
if self._db.get_setting_bool("blacklist_enabled"):
bl_tags = set(self._db.get_blacklisted_tags())
if bl_tags:
posts = [p for p in posts if not bl_tags.intersection(p.tag_list)]
self._posts = posts self._posts = posts
self._status.showMessage(f"{len(posts)} results") self._status.showMessage(f"{len(posts)} results")
thumbs = self._grid.set_posts(len(posts)) thumbs = self._grid.set_posts(len(posts))

View File

@ -209,30 +209,25 @@ class SettingsDialog(QDialog):
# -- Blacklist tab -- # -- Blacklist tab --
def _build_blacklist_tab(self) -> QWidget: def _build_blacklist_tab(self) -> QWidget:
from PySide6.QtWidgets import QTextEdit
w = QWidget() w = QWidget()
layout = QVBoxLayout(w) layout = QVBoxLayout(w)
layout.addWidget(QLabel("Posts with these tags will be hidden from results:")) self._bl_enabled = QCheckBox("Enable blacklist")
self._bl_enabled.setChecked(self._db.get_setting_bool("blacklist_enabled"))
layout.addWidget(self._bl_enabled)
self._bl_list = QListWidget() layout.addWidget(QLabel(
self._refresh_blacklist() "Posts containing these tags will be hidden from results.\n"
layout.addWidget(self._bl_list) "Paste tags separated by spaces or newlines:"
))
add_row = QHBoxLayout() self._bl_text = QTextEdit()
self._bl_input = QLineEdit() self._bl_text.setPlaceholderText("animated animated_gif hatsune_miku ...")
self._bl_input.setPlaceholderText("Tag to blacklist...") # Load existing tags into the text box
self._bl_input.returnPressed.connect(self._bl_add) tags = self._db.get_blacklisted_tags()
add_row.addWidget(self._bl_input, stretch=1) self._bl_text.setPlainText(" ".join(tags))
layout.addWidget(self._bl_text)
add_btn = QPushButton("Add")
add_btn.clicked.connect(self._bl_add)
add_row.addWidget(add_btn)
remove_btn = QPushButton("Remove")
remove_btn.clicked.connect(self._bl_remove)
add_row.addWidget(remove_btn)
layout.addLayout(add_row)
io_row = QHBoxLayout() io_row = QHBoxLayout()
@ -244,10 +239,6 @@ class SettingsDialog(QDialog):
import_bl_btn.clicked.connect(self._bl_import) import_bl_btn.clicked.connect(self._bl_import)
io_row.addWidget(import_bl_btn) io_row.addWidget(import_bl_btn)
clear_bl_btn = QPushButton("Clear All")
clear_bl_btn.clicked.connect(self._bl_clear)
io_row.addWidget(clear_bl_btn)
layout.addLayout(io_row) layout.addLayout(io_row)
return w return w
@ -522,30 +513,12 @@ class SettingsDialog(QDialog):
QMessageBox.information(self, "Done", f"Evicted {count} files.") QMessageBox.information(self, "Done", f"Evicted {count} files.")
self._refresh_stats() self._refresh_stats()
def _refresh_blacklist(self) -> None:
self._bl_list.clear()
for tag in self._db.get_blacklisted_tags():
self._bl_list.addItem(tag)
def _bl_add(self) -> None:
tag = self._bl_input.text().strip()
if tag:
self._db.add_blacklisted_tag(tag)
self._bl_input.clear()
self._refresh_blacklist()
def _bl_remove(self) -> None:
item = self._bl_list.currentItem()
if item:
self._db.remove_blacklisted_tag(item.text())
self._refresh_blacklist()
def _bl_export(self) -> None: def _bl_export(self) -> None:
from .dialogs import save_file from .dialogs import save_file
path = save_file(self, "Export Blacklist", "blacklist.txt", "Text (*.txt)") path = save_file(self, "Export Blacklist", "blacklist.txt", "Text (*.txt)")
if not path: if not path:
return return
tags = self._db.get_blacklisted_tags() tags = self._bl_text.toPlainText().split()
with open(path, "w") as f: with open(path, "w") as f:
f.write("\n".join(tags)) f.write("\n".join(tags))
QMessageBox.information(self, "Done", f"Exported {len(tags)} tags.") QMessageBox.information(self, "Done", f"Exported {len(tags)} tags.")
@ -558,25 +531,13 @@ class SettingsDialog(QDialog):
try: try:
with open(path) as f: with open(path) as f:
tags = [line.strip() for line in f if line.strip()] tags = [line.strip() for line in f if line.strip()]
count = 0 existing = self._bl_text.toPlainText().split()
for tag in tags: merged = list(dict.fromkeys(existing + tags))
self._db.add_blacklisted_tag(tag) self._bl_text.setPlainText(" ".join(merged))
count += 1 QMessageBox.information(self, "Done", f"Imported {len(tags)} tags.")
self._refresh_blacklist()
QMessageBox.information(self, "Done", f"Imported {count} tags.")
except Exception as e: except Exception as e:
QMessageBox.warning(self, "Error", str(e)) QMessageBox.warning(self, "Error", str(e))
def _bl_clear(self) -> None:
reply = QMessageBox.question(
self, "Confirm", "Remove all blacklisted tags?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
)
if reply == QMessageBox.StandardButton.Yes:
for tag in self._db.get_blacklisted_tags():
self._db.remove_blacklisted_tag(tag)
self._refresh_blacklist()
def _open_data_folder(self) -> None: def _open_data_folder(self) -> None:
from PySide6.QtGui import QDesktopServices from PySide6.QtGui import QDesktopServices
from PySide6.QtCore import QUrl from PySide6.QtCore import QUrl
@ -653,6 +614,14 @@ class SettingsDialog(QDialog):
self._db.set_setting("max_cache_mb", str(self._max_cache.value())) self._db.set_setting("max_cache_mb", str(self._max_cache.value()))
self._db.set_setting("auto_evict", "1" if self._auto_evict.isChecked() else "0") self._db.set_setting("auto_evict", "1" if self._auto_evict.isChecked() else "0")
self._db.set_setting("clear_cache_on_exit", "1" if self._clear_on_exit.isChecked() else "0") self._db.set_setting("clear_cache_on_exit", "1" if self._clear_on_exit.isChecked() else "0")
self._db.set_setting("blacklist_enabled", "1" if self._bl_enabled.isChecked() else "0")
# Sync blacklist from text box
new_tags = set(self._bl_text.toPlainText().split())
old_tags = set(self._db.get_blacklisted_tags())
for tag in old_tags - new_tags:
self._db.remove_blacklisted_tag(tag)
for tag in new_tags - old_tags:
self._db.add_blacklisted_tag(tag)
if self._file_dialog_combo is not None: if self._file_dialog_combo is not None:
self._db.set_setting("file_dialog_platform", self._file_dialog_combo.currentText()) self._db.set_setting("file_dialog_platform", self._file_dialog_combo.currentText())
self.settings_changed.emit() self.settings_changed.emit()