Næster version
This commit is contained in:
@@ -5,6 +5,7 @@ library_panel.py — Musikbibliotek med søgning og drag-and-drop til danseliste
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QListWidget, QListWidgetItem,
|
||||
QLineEdit, QLabel, QHBoxLayout, QPushButton,
|
||||
QFrame, QSlider, QCheckBox,
|
||||
QAbstractItemView, QStyledItemDelegate,
|
||||
)
|
||||
from PyQt6.QtCore import Qt, pyqtSignal, QTimer, QMimeData, QByteArray, QRect
|
||||
@@ -175,6 +176,73 @@ class LibraryPanel(QWidget):
|
||||
self._list.setItemDelegate(DanseButtonDelegate(self._list))
|
||||
layout.addWidget(self._list)
|
||||
|
||||
# Preview-afspiller bar
|
||||
self._preview_bar = self._build_preview_bar()
|
||||
self._preview_bar.setVisible(False)
|
||||
layout.addWidget(self._preview_bar)
|
||||
|
||||
# Timer til preview progress opdatering
|
||||
self._preview_timer = QTimer(self)
|
||||
self._preview_timer.setInterval(200)
|
||||
self._preview_timer.timeout.connect(self._update_preview_progress)
|
||||
|
||||
def _build_preview_bar(self) -> QWidget:
|
||||
bar = QFrame()
|
||||
bar.setObjectName("track_display")
|
||||
bar.setFixedHeight(62)
|
||||
row = QHBoxLayout(bar)
|
||||
row.setContentsMargins(10, 8, 10, 8)
|
||||
row.setSpacing(10)
|
||||
|
||||
# Play/pause knap — orange som hoved-afspiller
|
||||
self._btn_preview_play = QPushButton("▶")
|
||||
self._btn_preview_play.setFixedSize(36, 36)
|
||||
self._btn_preview_play.setObjectName("btn_play_small")
|
||||
self._btn_preview_play.setToolTip("Afspil / Pause")
|
||||
self._btn_preview_play.clicked.connect(self._toggle_preview_playback)
|
||||
row.addWidget(self._btn_preview_play)
|
||||
|
||||
# Stop knap
|
||||
self._btn_preview_stop = QPushButton("■")
|
||||
self._btn_preview_stop.setFixedSize(30, 30)
|
||||
self._btn_preview_stop.setObjectName("btn_stop_small")
|
||||
self._btn_preview_stop.setToolTip("Stop preview")
|
||||
self._btn_preview_stop.clicked.connect(self._stop_preview)
|
||||
row.addWidget(self._btn_preview_stop)
|
||||
|
||||
# Titel + progress i midten
|
||||
info = QVBoxLayout()
|
||||
info.setSpacing(4)
|
||||
info.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self._lbl_preview_title = QLabel("—")
|
||||
self._lbl_preview_title.setObjectName("track_meta")
|
||||
info.addWidget(self._lbl_preview_title)
|
||||
|
||||
self._preview_progress = QSlider(Qt.Orientation.Horizontal)
|
||||
self._preview_progress.setRange(0, 1000)
|
||||
self._preview_progress.sliderMoved.connect(self._seek_preview)
|
||||
info.addWidget(self._preview_progress)
|
||||
row.addLayout(info, stretch=1)
|
||||
|
||||
# Tid
|
||||
self._lbl_preview_time = QLabel("0:00")
|
||||
self._lbl_preview_time.setObjectName("track_meta")
|
||||
self._lbl_preview_time.setFixedWidth(70)
|
||||
self._lbl_preview_time.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
|
||||
row.addWidget(self._lbl_preview_time)
|
||||
|
||||
# Volumen
|
||||
self._preview_vol = QSlider(Qt.Orientation.Horizontal)
|
||||
self._preview_vol.setRange(0, 100)
|
||||
self._preview_vol.setValue(78)
|
||||
self._preview_vol.setFixedWidth(70)
|
||||
self._preview_vol.setToolTip("Volumen preview")
|
||||
self._preview_vol.valueChanged.connect(self._on_preview_volume)
|
||||
row.addWidget(self._preview_vol)
|
||||
|
||||
return bar
|
||||
|
||||
# ── Scanning ──────────────────────────────────────────────────────────────
|
||||
|
||||
def _on_scan_clicked(self):
|
||||
@@ -312,11 +380,6 @@ class LibraryPanel(QWidget):
|
||||
|
||||
# ── Handlinger ────────────────────────────────────────────────────────────
|
||||
|
||||
def _on_double_click(self, item: QListWidgetItem):
|
||||
song = item.data(Qt.ItemDataRole.UserRole)
|
||||
if song:
|
||||
self.song_selected.emit(song)
|
||||
|
||||
def _show_context_menu(self, pos):
|
||||
from PyQt6.QtWidgets import QMenu
|
||||
item = self._list.itemAt(pos)
|
||||
@@ -326,8 +389,17 @@ class LibraryPanel(QWidget):
|
||||
if not song:
|
||||
return
|
||||
menu = QMenu(self)
|
||||
act_add = menu.addAction("Tilføj til danseliste")
|
||||
act_play = menu.addAction("Afspil")
|
||||
act_add = menu.addAction("Tilføj til danseliste")
|
||||
act_play = menu.addAction("Afspil")
|
||||
# Preview kun hvis preview player er sat op
|
||||
is_previewing = (
|
||||
hasattr(self, "_preview_player") and
|
||||
self._preview_player and
|
||||
self._preview_player.is_playing()
|
||||
)
|
||||
act_preview = menu.addAction(
|
||||
"⏹ Stop preview" if is_previewing else "▶ Preview (høretelefoner)"
|
||||
)
|
||||
menu.addSeparator()
|
||||
act_tags = menu.addAction("✎ Rediger dans-tags...")
|
||||
act_info = menu.addAction("ℹ Dans-info...")
|
||||
@@ -340,6 +412,8 @@ class LibraryPanel(QWidget):
|
||||
self.add_to_playlist.emit(song)
|
||||
elif action == act_play:
|
||||
self.song_selected.emit(song)
|
||||
elif action == act_preview:
|
||||
self._toggle_preview(song)
|
||||
elif action == act_tags:
|
||||
self.edit_tags_requested.emit(song)
|
||||
elif action == act_info:
|
||||
@@ -351,6 +425,78 @@ class LibraryPanel(QWidget):
|
||||
elif action == act_mail:
|
||||
self.send_mail_requested.emit(song)
|
||||
|
||||
def set_preview_player(self, preview_player):
|
||||
"""Sæt preview-afspilleren fra main_window."""
|
||||
self._preview_player = preview_player
|
||||
|
||||
def _on_double_click(self, item: QListWidgetItem):
|
||||
song = item.data(Qt.ItemDataRole.UserRole)
|
||||
if song:
|
||||
self._start_preview(song)
|
||||
|
||||
def _start_preview(self, song: dict):
|
||||
if not hasattr(self, "_preview_player") or not self._preview_player:
|
||||
self.song_selected.emit(song)
|
||||
return
|
||||
path = song.get("local_path", "")
|
||||
if not path:
|
||||
return
|
||||
self._preview_song = song
|
||||
self._preview_player.play(path)
|
||||
title = song.get("title", "—")
|
||||
artist = song.get("artist", "")
|
||||
self._lbl_preview_title.setText(f"{title} · {artist}")
|
||||
self._btn_preview_play.setText("⏸")
|
||||
self._preview_bar.setVisible(True)
|
||||
self._preview_timer.start()
|
||||
|
||||
def _toggle_preview_playback(self):
|
||||
if not hasattr(self, "_preview_player") or not self._preview_player:
|
||||
return
|
||||
if self._preview_player.is_playing():
|
||||
self._preview_player.pause()
|
||||
self._btn_preview_play.setText("▶")
|
||||
else:
|
||||
self._preview_player.resume()
|
||||
self._btn_preview_play.setText("⏸")
|
||||
|
||||
def _stop_preview(self):
|
||||
if hasattr(self, "_preview_player") and self._preview_player:
|
||||
self._preview_player.stop()
|
||||
self._preview_timer.stop()
|
||||
self._preview_bar.setVisible(False)
|
||||
self._btn_preview_play.setText("▶")
|
||||
|
||||
def _seek_preview(self, value: int):
|
||||
if hasattr(self, "_preview_player") and self._preview_player:
|
||||
self._preview_player.seek(value / 1000.0)
|
||||
|
||||
def _on_preview_volume(self, value: int):
|
||||
if hasattr(self, "_preview_player") and self._preview_player:
|
||||
self._preview_player.set_volume(value)
|
||||
|
||||
def _update_preview_progress(self):
|
||||
if not hasattr(self, "_preview_player") or not self._preview_player:
|
||||
return
|
||||
if not self._preview_player.is_playing():
|
||||
self._btn_preview_play.setText("▶")
|
||||
return
|
||||
pos = self._preview_player.get_position()
|
||||
cur = self._preview_player.get_time()
|
||||
dur = self._preview_player.get_duration()
|
||||
self._preview_progress.setValue(int(pos * 1000))
|
||||
def fmt(s): return f"{s//60}:{s%60:02d}"
|
||||
self._lbl_preview_time.setText(f"{fmt(cur)} / {fmt(dur)}")
|
||||
|
||||
def _toggle_preview(self, song: dict):
|
||||
"""Start/stop preview af en sang."""
|
||||
if not hasattr(self, "_preview_player") or not self._preview_player:
|
||||
return
|
||||
if self._preview_player.is_playing():
|
||||
self._stop_preview()
|
||||
else:
|
||||
self._start_preview(song)
|
||||
|
||||
def _analyze_bpm(self, song: dict):
|
||||
"""Analysér BPM i baggrundstråd og opdater biblioteket."""
|
||||
path = song.get("local_path", "")
|
||||
|
||||
Reference in New Issue
Block a user