A bit of a refactor, attempt to be nicer to servers and still load quickly.

This commit is contained in:
Storm Dragon
2025-12-16 02:38:05 -05:00
parent 5507aa8faa
commit 1658321a4b
3 changed files with 609 additions and 232 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ def setupLogging():
format='%(message)s [%(asctime)s]', format='%(message)s [%(asctime)s]',
datefmt='%a %b %d %I:%M:%S %p %Z %Y', datefmt='%a %b %d %I:%M:%S %p %Z %Y',
handlers=[ handlers=[
logging.FileHandler(logFile, encoding='utf-8'), logging.FileHandler(logFile, mode='w', encoding='utf-8'),
logging.StreamHandler(sys.stdout) logging.StreamHandler(sys.stdout)
] ]
) )
+553 -191
View File
File diff suppressed because it is too large Load Diff
+25 -10
View File
@@ -18,6 +18,7 @@ from PySide6.QtWidgets import (
QPushButton, QPushButton,
QTreeWidgetItem, QTreeWidgetItem,
QVBoxLayout, QVBoxLayout,
QAbstractItemView,
) )
from src.api.client import SubsonicClient from src.api.client import SubsonicClient
@@ -75,6 +76,12 @@ class SearchDialog(QDialog):
self.resultsTree.setHeaderHidden(True) self.resultsTree.setHeaderHidden(True)
self.resultsTree.setRootIsDecorated(True) self.resultsTree.setRootIsDecorated(True)
self.resultsTree.setContextMenuPolicy(Qt.CustomContextMenu) self.resultsTree.setContextMenuPolicy(Qt.CustomContextMenu)
self.resultsTree.setUniformRowHeights(True)
self.resultsTree.setItemsExpandable(True)
self.resultsTree.setExpandsOnDoubleClick(True)
self.resultsTree.setSelectionMode(QAbstractItemView.SingleSelection)
self.resultsTree.setAllColumnsShowFocus(True)
self.resultsTree.setFocusPolicy(Qt.StrongFocus)
self.resultsTree.itemActivated.connect(self.handleActivate) self.resultsTree.itemActivated.connect(self.handleActivate)
self.resultsTree.itemExpanded.connect(self.onItemExpanded) self.resultsTree.itemExpanded.connect(self.onItemExpanded)
self.resultsTree.customContextMenuRequested.connect(self.showContextMenu) self.resultsTree.customContextMenuRequested.connect(self.showContextMenu)
@@ -112,10 +119,7 @@ class SearchDialog(QDialog):
self.populateResults(results) self.populateResults(results)
if self._resultsLoaded: if self._resultsLoaded:
self.resultsTree.setFocus() self._focusFirstResult()
first = self.resultsTree.topLevelItem(0)
if first:
self.resultsTree.setCurrentItem(first)
self.statusLabel.setText("Search complete. Use the tree to play or queue items.") self.statusLabel.setText("Search complete. Use the tree to play or queue items.")
else: else:
self.statusLabel.setText("No results found.") self.statusLabel.setText("No results found.")
@@ -123,6 +127,15 @@ class SearchDialog(QDialog):
AccessibleTextDialog.showError("Search Error", str(e), parent=self) AccessibleTextDialog.showError("Search Error", str(e), parent=self)
self.statusLabel.setText("Search failed.") self.statusLabel.setText("Search failed.")
def _focusFirstResult(self):
"""Move focus to the first result item for immediate keyboard navigation."""
self.resultsTree.setFocus(Qt.OtherFocusReason)
first = self.resultsTree.topLevelItem(0)
if not first:
return
target = first.child(0) if first.childCount() else first
self.resultsTree.setCurrentItem(target)
def populateResults(self, results: Dict[str, List]): def populateResults(self, results: Dict[str, List]):
"""Populate the results tree from search3 data.""" """Populate the results tree from search3 data."""
artists: List[Artist] = results.get("artists", []) artists: List[Artist] = results.get("artists", [])
@@ -131,8 +144,7 @@ class SearchDialog(QDialog):
if artists: if artists:
root = QTreeWidgetItem([f"Artists ({len(artists)})"]) root = QTreeWidgetItem([f"Artists ({len(artists)})"])
root.setData(0, Qt.UserRole, {"type": "artists_root"}) root.setData(0, Qt.UserRole, {"type": "artists_root", "loaded": True})
root.setExpanded(True)
for artist in artists: for artist in artists:
item = QTreeWidgetItem([artist.name]) item = QTreeWidgetItem([artist.name])
item.setData(0, Qt.UserRole, {"type": "artist", "id": artist.id, "artist": artist, "loaded": False}) item.setData(0, Qt.UserRole, {"type": "artist", "id": artist.id, "artist": artist, "loaded": False})
@@ -140,12 +152,12 @@ class SearchDialog(QDialog):
item.addChild(QTreeWidgetItem(["Loading..."])) item.addChild(QTreeWidgetItem(["Loading..."]))
root.addChild(item) root.addChild(item)
self.resultsTree.addTopLevelItem(root) self.resultsTree.addTopLevelItem(root)
self.resultsTree.expandItem(root)
self._resultsLoaded = True self._resultsLoaded = True
if albums: if albums:
root = QTreeWidgetItem([f"Albums ({len(albums)})"]) root = QTreeWidgetItem([f"Albums ({len(albums)})"])
root.setData(0, Qt.UserRole, {"type": "albums_root"}) root.setData(0, Qt.UserRole, {"type": "albums_root", "loaded": True})
root.setExpanded(True)
for album in albums: for album in albums:
label = f"{album.name}{album.artist} ({album.songCount} tracks)" label = f"{album.name}{album.artist} ({album.songCount} tracks)"
item = QTreeWidgetItem([label]) item = QTreeWidgetItem([label])
@@ -154,6 +166,7 @@ class SearchDialog(QDialog):
item.addChild(QTreeWidgetItem(["Loading..."])) item.addChild(QTreeWidgetItem(["Loading..."]))
root.addChild(item) root.addChild(item)
self.resultsTree.addTopLevelItem(root) self.resultsTree.addTopLevelItem(root)
self.resultsTree.expandItem(root)
self._resultsLoaded = True self._resultsLoaded = True
if songs: if songs:
@@ -164,8 +177,7 @@ class SearchDialog(QDialog):
if not songs: if not songs:
return return
root = QTreeWidgetItem([rootLabel]) root = QTreeWidgetItem([rootLabel])
root.setData(0, Qt.UserRole, {"type": "songs_root"}) root.setData(0, Qt.UserRole, {"type": "songs_root", "loaded": True})
root.setExpanded(True)
for song in songs: for song in songs:
text = f"{song.title}{song.artist} ({song.album}, {song.durationFormatted})" text = f"{song.title}{song.artist} ({song.album}, {song.durationFormatted})"
item = QTreeWidgetItem([text]) item = QTreeWidgetItem([text])
@@ -174,6 +186,7 @@ class SearchDialog(QDialog):
root.addChild(item) root.addChild(item)
self.resultsTree.addTopLevelItem(root) self.resultsTree.addTopLevelItem(root)
self._resultsLoaded = True self._resultsLoaded = True
self.resultsTree.expandItem(root)
def handleActivate(self, item: QTreeWidgetItem): def handleActivate(self, item: QTreeWidgetItem):
"""Activate the selected item similar to the main tree.""" """Activate the selected item similar to the main tree."""
@@ -230,6 +243,8 @@ class SearchDialog(QDialog):
item.addChild(child) item.addChild(child)
data["loaded"] = True data["loaded"] = True
item.setData(0, Qt.UserRole, data) item.setData(0, Qt.UserRole, data)
# Update accessibility for newly added children
self.resultsTree.updateChildAccessibility(item, True)
except Exception as e: except Exception as e:
AccessibleTextDialog.showError("Search Error", str(e), parent=self) AccessibleTextDialog.showError("Search Error", str(e), parent=self)