soulsync/ui/pages/artists.py
Broque Thomas 36c7e5922e fix
2025-07-09 13:01:11 -07:00

426 lines
No EOL
14 KiB
Python

from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
QFrame, QPushButton, QLineEdit, QScrollArea,
QGridLayout, QComboBox, QProgressBar)
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QFont, QPixmap
class ArtistCard(QFrame):
def __init__(self, name: str, album_count: int, track_count: int, last_updated: str, parent=None):
super().__init__(parent)
self.name = name
self.album_count = album_count
self.track_count = track_count
self.last_updated = last_updated
self.setup_ui()
def setup_ui(self):
self.setFixedSize(280, 200)
self.setStyleSheet("""
ArtistCard {
background: #282828;
border-radius: 8px;
border: 1px solid #404040;
}
ArtistCard:hover {
background: #333333;
border: 1px solid #1db954;
}
""")
layout = QVBoxLayout(self)
layout.setContentsMargins(20, 20, 20, 20)
layout.setSpacing(10)
# Artist image placeholder
image_placeholder = QLabel()
image_placeholder.setFixedSize(80, 80)
image_placeholder.setAlignment(Qt.AlignmentFlag.AlignCenter)
image_placeholder.setStyleSheet("""
QLabel {
background: #404040;
border-radius: 40px;
color: #b3b3b3;
font-size: 32px;
}
""")
image_placeholder.setText("🎵")
# Artist name
name_label = QLabel(self.name)
name_label.setFont(QFont("Arial", 14, QFont.Weight.Bold))
name_label.setStyleSheet("color: #ffffff;")
name_label.setWordWrap(True)
name_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
# Stats
stats_layout = QVBoxLayout()
stats_layout.setSpacing(5)
albums_label = QLabel(f"{self.album_count} albums")
albums_label.setFont(QFont("Arial", 10))
albums_label.setStyleSheet("color: #b3b3b3;")
albums_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
tracks_label = QLabel(f"{self.track_count} tracks")
tracks_label.setFont(QFont("Arial", 10))
tracks_label.setStyleSheet("color: #b3b3b3;")
tracks_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
updated_label = QLabel(f"Updated: {self.last_updated}")
updated_label.setFont(QFont("Arial", 9))
updated_label.setStyleSheet("color: #666666;")
updated_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
stats_layout.addWidget(albums_label)
stats_layout.addWidget(tracks_label)
stats_layout.addWidget(updated_label)
# Action buttons
actions_layout = QHBoxLayout()
actions_layout.setSpacing(10)
update_btn = QPushButton("Update")
update_btn.setFixedSize(60, 25)
update_btn.setToolTip("Update metadata for this artist only")
update_btn.setStyleSheet("""
QPushButton {
background: #1db954;
border: none;
border-radius: 12px;
color: #000000;
font-size: 9px;
font-weight: bold;
}
QPushButton:hover {
background: #1ed760;
}
""")
download_btn = QPushButton("Download")
download_btn.setFixedSize(60, 25)
download_btn.setToolTip("Download complete discography for this artist")
download_btn.setStyleSheet("""
QPushButton {
background: transparent;
border: 1px solid #1db954;
border-radius: 12px;
color: #1db954;
font-size: 9px;
font-weight: bold;
}
QPushButton:hover {
background: rgba(29, 185, 84, 0.1);
}
""")
actions_layout.addWidget(update_btn)
actions_layout.addWidget(download_btn)
# Center the image
image_container = QHBoxLayout()
image_container.addStretch()
image_container.addWidget(image_placeholder)
image_container.addStretch()
layout.addLayout(image_container)
layout.addWidget(name_label)
layout.addLayout(stats_layout)
layout.addStretch()
layout.addLayout(actions_layout)
class ArtistsPage(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setup_ui()
def setup_ui(self):
self.setStyleSheet("""
ArtistsPage {
background: #191414;
}
""")
main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(30, 30, 30, 30)
main_layout.setSpacing(25)
# Header
header = self.create_header()
main_layout.addWidget(header)
# Controls
controls = self.create_controls()
main_layout.addWidget(controls)
# Artists grid
artists_section = self.create_artists_section()
main_layout.addWidget(artists_section)
def create_header(self):
header = QWidget()
layout = QVBoxLayout(header)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(5)
# Title
title_label = QLabel("Artists")
title_label.setFont(QFont("Arial", 28, QFont.Weight.Bold))
title_label.setStyleSheet("color: #ffffff;")
# Subtitle
subtitle_label = QLabel("Manage artist metadata and download complete discographies")
subtitle_label.setFont(QFont("Arial", 14))
subtitle_label.setStyleSheet("color: #b3b3b3;")
layout.addWidget(title_label)
layout.addWidget(subtitle_label)
return header
def create_controls(self):
controls = QWidget()
layout = QVBoxLayout(controls)
layout.setSpacing(15)
# First row - search and filters
first_row = QWidget()
first_row_layout = QHBoxLayout(first_row)
first_row_layout.setSpacing(15)
# Search bar
search_bar = QLineEdit()
search_bar.setPlaceholderText("Search artists...")
search_bar.setFixedHeight(40)
search_bar.setStyleSheet("""
QLineEdit {
background: #282828;
border: 1px solid #404040;
border-radius: 20px;
padding: 0 15px;
color: #ffffff;
font-size: 12px;
}
QLineEdit:focus {
border: 1px solid #1db954;
}
""")
# Filter dropdown
filter_combo = QComboBox()
filter_combo.addItems(["All Artists", "Recently Updated", "Need Update", "Missing Albums"])
filter_combo.setFixedHeight(40)
filter_combo.setStyleSheet("""
QComboBox {
background: #282828;
border: 1px solid #404040;
border-radius: 20px;
padding: 0 15px;
color: #ffffff;
font-size: 12px;
min-width: 120px;
}
QComboBox::drop-down {
border: none;
}
QComboBox::down-arrow {
image: none;
}
""")
# Scan all button
scan_btn = QPushButton("🔍 Scan All Artists")
scan_btn.setFixedHeight(40)
scan_btn.setStyleSheet("""
QPushButton {
background: #1db954;
border: none;
border-radius: 20px;
color: #000000;
font-size: 12px;
font-weight: bold;
padding: 0 20px;
}
QPushButton:hover {
background: #1ed760;
}
""")
# Update all button
update_btn = QPushButton("📝 Update All Metadata")
update_btn.setFixedHeight(40)
update_btn.setToolTip("Batch update metadata for all artists (skips already completed)")
update_btn.setStyleSheet("""
QPushButton {
background: transparent;
border: 1px solid #1db954;
border-radius: 20px;
color: #1db954;
font-size: 12px;
font-weight: bold;
padding: 0 20px;
}
QPushButton:hover {
background: rgba(29, 185, 84, 0.1);
}
""")
first_row_layout.addWidget(search_bar)
first_row_layout.addWidget(filter_combo)
first_row_layout.addStretch()
first_row_layout.addWidget(scan_btn)
first_row_layout.addWidget(update_btn)
# Second row - alphabet navigation
alphabet_row = self.create_alphabet_navigation()
layout.addWidget(first_row)
layout.addWidget(alphabet_row)
return controls
def create_alphabet_navigation(self):
alphabet_widget = QWidget()
alphabet_layout = QHBoxLayout(alphabet_widget)
alphabet_layout.setContentsMargins(0, 0, 0, 0)
alphabet_layout.setSpacing(3)
# Create alphabet buttons
letters = ['All', '#'] + [chr(i) for i in range(ord('A'), ord('Z')+1)]
for letter in letters:
btn = QPushButton(letter)
btn.setFixedSize(30, 30)
btn.setStyleSheet("""
QPushButton {
background: #282828;
border: 1px solid #404040;
border-radius: 15px;
color: #b3b3b3;
font-size: 10px;
font-weight: bold;
}
QPushButton:hover {
background: #1db954;
border: 1px solid #1db954;
color: #000000;
}
QPushButton:pressed {
background: #1aa34a;
}
""")
# Set "All" as active by default
if letter == 'All':
btn.setStyleSheet("""
QPushButton {
background: #1db954;
border: 1px solid #1db954;
border-radius: 15px;
color: #000000;
font-size: 10px;
font-weight: bold;
}
QPushButton:hover {
background: #1ed760;
}
""")
alphabet_layout.addWidget(btn)
alphabet_layout.addStretch()
return alphabet_widget
def create_artists_section(self):
section = QWidget()
layout = QVBoxLayout(section)
layout.setSpacing(20)
# Progress bar for batch operations
progress_frame = QFrame()
progress_frame.setFixedHeight(60)
progress_frame.setStyleSheet("""
QFrame {
background: #282828;
border-radius: 8px;
border: 1px solid #404040;
}
""")
progress_layout = QVBoxLayout(progress_frame)
progress_layout.setContentsMargins(20, 15, 20, 15)
progress_layout.setSpacing(8)
progress_label = QLabel("Scanning artists...")
progress_label.setFont(QFont("Arial", 11))
progress_label.setStyleSheet("color: #ffffff;")
progress_bar = QProgressBar()
progress_bar.setFixedHeight(6)
progress_bar.setValue(0)
progress_bar.setStyleSheet("""
QProgressBar {
border: none;
border-radius: 3px;
background: #404040;
}
QProgressBar::chunk {
background: #1db954;
border-radius: 3px;
}
""")
progress_layout.addWidget(progress_label)
progress_layout.addWidget(progress_bar)
# Artists grid
artists_scroll = QScrollArea()
artists_scroll.setWidgetResizable(True)
artists_scroll.setStyleSheet("""
QScrollArea {
border: none;
background: transparent;
}
QScrollBar:vertical {
background: #282828;
width: 8px;
border-radius: 4px;
}
QScrollBar::handle:vertical {
background: #1db954;
border-radius: 4px;
}
""")
artists_widget = QWidget()
artists_grid = QGridLayout(artists_widget)
artists_grid.setSpacing(20)
# Sample artists
artists = [
("The Beatles", 13, 213, "2 days ago"),
("Pink Floyd", 15, 147, "1 week ago"),
("Led Zeppelin", 8, 108, "3 days ago"),
("Queen", 16, 186, "5 days ago"),
("The Rolling Stones", 22, 289, "1 week ago"),
("Bob Dylan", 38, 456, "2 weeks ago"),
("David Bowie", 27, 342, "4 days ago"),
("Radiohead", 9, 127, "6 days ago"),
("The Who", 12, 156, "1 week ago"),
("Nirvana", 5, 67, "3 weeks ago"),
("AC/DC", 17, 198, "2 days ago"),
("Fleetwood Mac", 18, 234, "1 week ago")
]
for i, (name, albums, tracks, updated) in enumerate(artists):
card = ArtistCard(name, albums, tracks, updated)
artists_grid.addWidget(card, i // 3, i % 3)
# Add stretch to fill remaining space
artists_grid.setRowStretch(artists_grid.rowCount(), 1)
artists_scroll.setWidget(artists_widget)
layout.addWidget(progress_frame)
layout.addWidget(artists_scroll)
return section