Initial commit

This commit is contained in:
Storm Dragon
2025-12-15 04:09:55 -05:00
commit 555ca0bba9
26 changed files with 4532 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
"""
Accessible search dialog for finding tracks quickly.
"""
from __future__ import annotations
from typing import List
from PySide6.QtCore import Signal, Qt
from PySide6.QtWidgets import (
QDialog,
QDialogButtonBox,
QHBoxLayout,
QLabel,
QLineEdit,
QListWidget,
QListWidgetItem,
QPushButton,
QVBoxLayout,
)
from src.api.client import SubsonicClient
from src.api.models import Song
from src.widgets.accessible_text_dialog import AccessibleTextDialog
class SearchDialog(QDialog):
"""Simple dialog for searching the Navidrome library."""
songActivated = Signal(Song)
def __init__(self, client: SubsonicClient, parent=None):
super().__init__(parent)
self.client = client
self.setWindowTitle("Search Library")
self.setMinimumSize(500, 420)
self.setupUi()
def setupUi(self):
layout = QVBoxLayout(self)
self.instructions = QLabel("Enter a song, artist, or album title. Press Enter to play results.")
self.instructions.setAccessibleName("Search Instructions")
self.instructions.setWordWrap(True)
layout.addWidget(self.instructions)
inputLayout = QHBoxLayout()
self.queryEdit = QLineEdit()
self.queryEdit.setAccessibleName("Search Query")
self.queryEdit.returnPressed.connect(self.runSearch)
inputLayout.addWidget(self.queryEdit)
self.searchButton = QPushButton("&Search")
self.searchButton.setAccessibleName("Start Search")
self.searchButton.clicked.connect(self.runSearch)
inputLayout.addWidget(self.searchButton)
layout.addLayout(inputLayout)
self.resultsList = QListWidget()
self.resultsList.setAccessibleName("Search Results")
self.resultsList.itemActivated.connect(self.activateSelection)
layout.addWidget(self.resultsList)
self.statusLabel = QLabel("")
self.statusLabel.setAccessibleName("Search Status")
layout.addWidget(self.statusLabel)
buttonBox = QDialogButtonBox(QDialogButtonBox.Close)
buttonBox.rejected.connect(self.reject)
layout.addWidget(buttonBox)
def runSearch(self):
"""Run the Subsonic search and populate results."""
query = self.queryEdit.text().strip()
if not query:
self.statusLabel.setText("Enter a search term to begin.")
return
self.statusLabel.setText("Searching...")
self.resultsList.clear()
try:
results = self.client.search(query, artistCount=0, albumCount=0, songCount=50)
songs: List[Song] = results.get("songs", [])
if not songs:
self.statusLabel.setText("No songs found.")
return
for song in songs:
text = f"{song.title}{song.artist} ({song.album}, {song.durationFormatted})"
item = QListWidgetItem(text)
item.setData(Qt.UserRole, song)
item.setData(Qt.AccessibleTextRole, text)
self.resultsList.addItem(item)
self.statusLabel.setText(f"Found {len(songs)} songs. Activate a result to play.")
self.resultsList.setCurrentRow(0)
self.resultsList.setFocus()
except Exception as e:
AccessibleTextDialog.showError("Search Error", str(e), parent=self)
self.statusLabel.setText("Search failed.")
def activateSelection(self, item: QListWidgetItem):
"""Emit the chosen song and close the dialog."""
song = item.data(Qt.UserRole)
if isinstance(song, Song):
self.songActivated.emit(song)
self.accept()