426 lines
No EOL
14 KiB
Python
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 |