v0.2 - drop fingerprinting, add LUFS cart normalisation, playlist save/load, editable history with TXT+CSV export

This commit is contained in:
2026-05-08 17:19:11 +10:00
parent 0c8900ac36
commit 95f74d813f
19 changed files with 679 additions and 483 deletions
+102 -3
View File
@@ -8,6 +8,7 @@ from PySide6.QtWidgets import (
)
from PySide6.QtCore import Qt, QTimer
from PySide6.QtGui import QFont, QColor, QAction
import json
import os
@@ -264,6 +265,17 @@ class BasePlaylistPanel(QWidget):
else:
item.setForeground(QColor("#cccccc"))
def get_filepaths(self, lst: QListWidget) -> list:
paths = []
for i in range(lst.count()):
item = lst.item(i)
if isinstance(item, PlaylistItem):
paths.append(item.filepath)
return paths
def clear_list(self, lst: QListWidget):
lst.clear()
# ─────────────────────────────────────────
# TRACK PLAYLIST PANEL
@@ -574,18 +586,58 @@ class CartPlaylistPanel(BasePlaylistPanel):
# ─────────────────────────────────────────
class PlaylistWidget(QWidget):
PLAYLIST_JSON_FILTER = "JSON Files (*.json)"
def __init__(self, engine, deck_widgets: dict,
cart_slots: list, parent=None):
super().__init__(parent)
self.engine = engine
# Expandable - no fixed width
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.setMinimumWidth(200)
root = QVBoxLayout(self)
root.setContentsMargins(0, 0, 0, 0)
root.setSpacing(0)
root.setSpacing(4)
# Toolbar
bar = QHBoxLayout()
bar.setSpacing(4)
self._save_btn = QPushButton("SAVE")
self._save_btn.setFixedHeight(28)
self._save_btn.setFont(QFont("Arial", 9, QFont.Bold))
self._save_btn.setStyleSheet("""
QPushButton {
background-color: #2a4a2a;
color: #aaaaaa;
border: 1px solid #444;
border-radius: 3px;
padding: 0 10px;
}
QPushButton:hover { background-color: #3a5a3a; }
""")
self._save_btn.clicked.connect(self._save_playlist)
self._load_btn = QPushButton("LOAD")
self._load_btn.setFixedHeight(28)
self._load_btn.setFont(QFont("Arial", 9, QFont.Bold))
self._load_btn.setStyleSheet("""
QPushButton {
background-color: #3a2a2a;
color: #aaaaaa;
border: 1px solid #444;
border-radius: 3px;
padding: 0 10px;
}
QPushButton:hover { background-color: #4a3a3a; }
""")
self._load_btn.clicked.connect(self._load_playlist)
bar.addWidget(self._save_btn)
bar.addWidget(self._load_btn)
bar.addStretch()
root.addLayout(bar)
splitter = QSplitter(Qt.Vertical)
splitter.setStyleSheet("""
@@ -602,4 +654,51 @@ class PlaylistWidget(QWidget):
splitter.addWidget(self.cart_panel)
splitter.setSizes([600, 400])
root.addWidget(splitter)
root.addWidget(splitter)
def _save_playlist(self):
path, _ = QFileDialog.getSaveFileName(
self, "Save Playlist",
os.path.expanduser("~/playlist.json"),
self.PLAYLIST_JSON_FILTER
)
if not path:
return
data = {
"version": 1,
"tracks": self.track_panel.get_filepaths(self.track_panel.playlist),
"cart_tracks": self.cart_panel.get_filepaths(self.cart_panel.playlist),
}
with open(path, 'w') as f:
json.dump(data, f, indent=2)
def _load_playlist(self):
path, _ = QFileDialog.getOpenFileName(
self, "Load Playlist",
os.path.expanduser("~"),
self.PLAYLIST_JSON_FILTER
)
if not path:
return
try:
with open(path) as f:
data = json.load(f)
except Exception:
return
tracks = data.get("tracks", [])
cart = data.get("cart_tracks", [])
self.track_panel.playlist.clear()
for fp in tracks:
if os.path.exists(fp):
self.track_panel._add_item_to(self.track_panel.playlist, fp)
self.track_panel._update_count(self.track_panel.playlist,
self.track_panel.count_label)
self.cart_panel.playlist.clear()
for fp in cart:
if os.path.exists(fp):
self.cart_panel._add_item_to(self.cart_panel.playlist, fp)
self.cart_panel._update_count(self.cart_panel.playlist,
self.cart_panel.count_label)