# playlist_widget.py import json import os from datetime import datetime, timedelta from PySide6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFileDialog, QFrame, QSizePolicy, QListWidget, QListWidgetItem, QAbstractItemView, QMenu, QSplitter, QTextEdit, QLineEdit ) from PySide6.QtCore import Qt, QTimer, QSize, Signal from PySide6.QtGui import QFont, QColor, QAction from icons import icon_save, icon_load class PlaylistItem(QListWidgetItem): def __init__(self, filepath: str): self.filepath = filepath self.filename = os.path.basename(filepath) display = os.path.splitext(self.filename)[0] super().__init__(display) self.setFont(QFont("Courier New", 10, QFont.Bold)) self.setForeground(QColor("#cccccc")) self.setToolTip(filepath) class HourDisplay(QFrame): def __init__(self, parent=None): super().__init__(parent) self.setFrameStyle(QFrame.Box | QFrame.Sunken) self.setFixedHeight(44) self.setStyleSheet(""" QFrame { background-color: #0a0a1a; border: 1px solid #333; border-radius: 3px; } """) layout = QHBoxLayout(self) layout.setContentsMargins(8, 2, 8, 2) self.hour_label = QLabel("HOUR --:--") self.hour_label.setFont(QFont("Courier New", 12, QFont.Bold)) self.hour_label.setStyleSheet( "color: #4488ff; background: transparent; border: none;" ) self.hour_label.setAlignment(Qt.AlignCenter) layout.addWidget(self.hour_label) self.clock_label = QLabel("00:00:00") self.clock_label.setFont(QFont("Courier New", 12, QFont.Bold)) self.clock_label.setStyleSheet( "color: #aaaaaa; background: transparent; border: none;" ) self.clock_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) layout.addWidget(self.clock_label) self.clock_timer = QTimer(self) self.clock_timer.setInterval(1000) self.clock_timer.timeout.connect(self._update_clock) self.clock_timer.start() self._update_clock() def _update_clock(self): from datetime import datetime now = datetime.now() self.clock_label.setText(now.strftime("%H:%M:%S")) self.hour_label.setText(f"HOUR {now.strftime('%H:00')}") class DeckSelector(QFrame): def __init__(self, parent=None): super().__init__(parent) self.setFixedHeight(36) self.setStyleSheet(""" QFrame { background-color: #1a1a1a; border: 1px solid #333; border-radius: 3px; } """) layout = QHBoxLayout(self) layout.setContentsMargins(6, 2, 6, 2) layout.setSpacing(6) label = QLabel("SEND TO:") label.setFont(QFont("Arial", 9, QFont.Bold)) label.setStyleSheet("color: #888; background: transparent; border: none;") layout.addWidget(label) self.deck1_btn = QPushButton("DECK 1") self.deck1_btn.setCheckable(True) self.deck1_btn.setChecked(True) self.deck1_btn.setFixedHeight(26) self.deck1_btn.setFont(QFont("Arial", 9, QFont.Bold)) self._style_btn(self.deck1_btn, "#00897b") self.deck2_btn = QPushButton("DECK 2") self.deck2_btn.setCheckable(True) self.deck2_btn.setFixedHeight(26) self.deck2_btn.setFont(QFont("Arial", 9, QFont.Bold)) self._style_btn(self.deck2_btn, "#f9ca24") self.deck1_btn.clicked.connect(lambda: self._select("deck1")) self.deck2_btn.clicked.connect(lambda: self._select("deck2")) layout.addWidget(self.deck1_btn) layout.addWidget(self.deck2_btn) layout.addStretch() self._selected = "deck1" def _style_btn(self, btn: QPushButton, accent: str): btn.setStyleSheet(f""" QPushButton {{ background-color: #2a2a2a; color: #888; border: 1px solid #444; border-radius: 3px; padding: 0 8px; }} QPushButton:checked {{ background-color: #1a1a1a; color: {accent}; border: 1px solid {accent}; }} QPushButton:hover {{ background-color: #333; }} """) def _select(self, deck: str): self._selected = deck self.deck1_btn.setChecked(deck == "deck1") self.deck2_btn.setChecked(deck == "deck2") @property def selected_deck(self) -> str: return self._selected # ───────────────────────────────────────── # SHOW LOG PANEL # ───────────────────────────────────────── # ───────────────────────────────────────── # BASE PLAYLIST PANEL # ───────────────────────────────────────── class BasePlaylistPanel(QWidget): AUDIO_FILTER = ( "Audio Files " "(*.wav *.flac *.mp3 *.m4a *.mp4 *.ogg *.opus *.aac *.aiff)" ) AUDIO_EXTENSIONS = { '.wav', '.flac', '.mp3', '.m4a', '.mp4', '.ogg', '.opus', '.aac', '.aiff' } def __init__(self, parent=None): super().__init__(parent) def _make_list(self) -> QListWidget: lst = QListWidget() lst.setStyleSheet(""" QListWidget { background-color: #0d0d0d; color: #cccccc; border: 1px solid #333; border-radius: 3px; outline: none; } QListWidget::item { padding: 5px 6px; border-bottom: 1px solid #1a1a1a; } QListWidget::item:selected { background-color: #1a3a5a; color: #ffffff; } QListWidget::item:hover { background-color: #1e1e1e; } QScrollBar:vertical { background: #1a1a1a; width: 8px; border-radius: 4px; } QScrollBar::handle:vertical { background: #444; border-radius: 4px; } """) lst.setSelectionMode(QAbstractItemView.SingleSelection) lst.setDragDropMode(QAbstractItemView.InternalMove) lst.setContextMenuPolicy(Qt.CustomContextMenu) return lst def _make_btn(self, text: str, color: str, height: int = 28) -> QPushButton: btn = QPushButton(text) btn.setFixedHeight(height) btn.setFont(QFont("Arial", 9, QFont.Bold)) btn.setStyleSheet(f""" QPushButton {{ background-color: {color}; color: #cccccc; border: 1px solid #444; border-radius: 3px; }} QPushButton:hover {{ background-color: #555; }} QPushButton:pressed {{ background-color: #222; }} """) return btn def _add_files_to(self, lst: QListWidget): paths, _ = QFileDialog.getOpenFileNames( self, "Add Files", os.path.expanduser("~"), self.AUDIO_FILTER ) for path in paths: self._add_item_to(lst, path) self._update_count(lst) def _add_folder_to(self, lst: QListWidget): folder = QFileDialog.getExistingDirectory( self, "Add Folder", os.path.expanduser("~") ) if not folder: return for root_dir, dirs, files in os.walk(folder): dirs.sort() for filename in sorted(files): ext = os.path.splitext(filename)[1].lower() if ext in self.AUDIO_EXTENSIONS: self._add_item_to(lst, os.path.join(root_dir, filename)) self._update_count(lst) def _add_item_to(self, lst: QListWidget, filepath: str): for i in range(lst.count()): item = lst.item(i) if isinstance(item, PlaylistItem) and item.filepath == filepath: return lst.addItem(PlaylistItem(filepath)) def _update_count(self, lst: QListWidget, label: QLabel = None): n = lst.count() if label: label.setText(f"{n} track{'s' if n != 1 else ''}") def _highlight(self, item: PlaylistItem, active: bool = False, queued: bool = False): if active: item.setForeground(QColor("#00ff41")) elif queued: item.setForeground(QColor("#f9ca24")) 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 # ───────────────────────────────────────── # PREVIEW BAR # ───────────────────────────────────────── class PreviewBar(QFrame): def __init__(self, engine, parent=None): super().__init__(parent) self.engine = engine self.setFixedHeight(40) self.setStyleSheet(""" QFrame { background-color: #0a0a12; border: 1px solid #222; border-radius: 3px; } """) layout = QHBoxLayout(self) layout.setContentsMargins(6, 2, 6, 2) layout.setSpacing(6) self.track_label = QLabel("NO TRACK") self.track_label.setFont(QFont("Courier New", 9, QFont.Bold)) self.track_label.setStyleSheet("color: #4488ff; background: transparent; border: none;") self.track_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) layout.addWidget(self.track_label, stretch=1) self.time_label = QLabel("--:--") self.time_label.setFont(QFont("Courier New", 12, QFont.Bold)) self.time_label.setStyleSheet("color: #4488ff; background: transparent; border: none;") layout.addWidget(self.time_label) self.btn_play = QPushButton("\u25B6") self.btn_play.setFixedSize(30, 26) self.btn_play.setFont(QFont("Arial", 10, QFont.Bold)) self.btn_play.setStyleSheet(""" QPushButton { background-color: #1a3a5a; color: #4488ff; border: 1px solid #4488ff; border-radius: 3px; } QPushButton:hover { background-color: #2a4a6a; } QPushButton:checked { background-color: #1a5a3a; color: #00ff41; border-color: #00ff41; } """) self.btn_play.setCheckable(True) layout.addWidget(self.btn_play) self.btn_forward = QPushButton("+10s") self.btn_forward.setFixedSize(40, 26) self.btn_forward.setFont(QFont("Arial", 8, QFont.Bold)) self.btn_forward.setStyleSheet(""" QPushButton { background-color: #2a1a0a; color: #aa6622; border: 1px solid #553311; border-radius: 3px; } QPushButton:hover { background-color: #3a2a1a; } """) layout.addWidget(self.btn_forward) self.btn_play.clicked.connect(self._toggle_preview) self.btn_forward.clicked.connect(self._skip_forward) self.timer = QTimer(self) self.timer.setInterval(100) self.timer.timeout.connect(self._refresh) self._playing = False self._pending_play = False def set_playlist(self, playlist): self._playlist = playlist def _selected_item(self): item = self._playlist.currentItem() if isinstance(item, PlaylistItem): return item return None def _toggle_preview(self): item = self._selected_item() if item is None: self.btn_play.setChecked(False) return deck = self.engine.decks['preview'] if self._playing: deck.stop() self._playing = False self._pending_play = False self.btn_play.setChecked(False) self.track_label.setText("NO TRACK") self.time_label.setText("--:--") else: self.engine.load_track('preview', item.filepath) self.track_label.setText(item.text()[:30]) self.time_label.setText("00:00") self._pending_play = True def _skip_forward(self): if not self._playing: return self.engine.decks['preview'].skip_seconds(10) def _refresh(self): deck = self.engine.decks['preview'] with deck.lock: playing = deck.playing track = deck.track pos = deck.position if self._pending_play and track is not None: self._pending_play = False deck.play() self._playing = True if playing and track: s = pos / track.sr self.time_label.setText(f"{int(s//60):02d}:{int(s%60):02d}") if pos >= track.num_samples - 1: self._playing = False self.btn_play.setChecked(False) self.track_label.setText("NO TRACK") self.time_label.setText("--:--") elif not playing and self._playing: self._playing = False self.btn_play.setChecked(False) # ───────────────────────────────────────── # TRACK PLAYLIST PANEL # ───────────────────────────────────────── class TrackPlaylistPanel(BasePlaylistPanel): def __init__(self, engine, deck_widgets: dict, parent=None): super().__init__(parent) self.engine = engine self.deck_widgets = deck_widgets self._build_ui() def _build_ui(self): self.setStyleSheet(""" QWidget { background-color: #111111; border: 2px solid #333333; border-radius: 6px; } """) root = QVBoxLayout(self) root.setContentsMargins(8, 8, 8, 8) root.setSpacing(6) header = QLabel("PLAYLIST") header.setFont(QFont("Arial", 12, QFont.Bold)) header.setStyleSheet( "color: #aaaaaa; background: transparent; border: none; padding: 2px;" ) header.setAlignment(Qt.AlignCenter) root.addWidget(header) self.preview_bar = PreviewBar(self.engine) root.addWidget(self.preview_bar) self.hour_display = HourDisplay() root.addWidget(self.hour_display) self.deck_selector = DeckSelector() root.addWidget(self.deck_selector) self.search_box = QLineEdit() self.search_box.setPlaceholderText("search...") self.search_box.setFont(QFont("Courier New", 9)) self.search_box.setStyleSheet(""" QLineEdit { background-color: #0d0d0d; color: #00ff41; border: 1px solid #333; border-radius: 3px; padding: 3px 6px; } QLineEdit:focus { border-color: #00ff41; } """) self.search_box.textChanged.connect(self._filter_list) root.addWidget(self.search_box) self.playlist = self._make_list() self.playlist.itemDoubleClicked.connect(self._send_to_deck) self.playlist.customContextMenuRequested.connect(self._context_menu) self.preview_bar.set_playlist(self.playlist) root.addWidget(self.playlist) row1 = QHBoxLayout() row1.setSpacing(4) self.btn_add_files = self._make_btn("ADD FILES", "#2a5a8a") self.btn_add_folder = self._make_btn("ADD FOLDER", "#2a4a6a") row1.addWidget(self.btn_add_files) row1.addWidget(self.btn_add_folder) root.addLayout(row1) row2 = QHBoxLayout() row2.setSpacing(4) self.btn_send_deck = self._make_btn("→ LOAD DECK", "#1a5a3a") self.btn_send_queue = self._make_btn("→ QUEUE", "#1a3a2a") row2.addWidget(self.btn_send_deck) row2.addWidget(self.btn_send_queue) root.addLayout(row2) row3 = QHBoxLayout() row3.setSpacing(4) self.btn_remove = self._make_btn("REMOVE", "#5a1a1a") self.btn_clear = self._make_btn("CLEAR ALL", "#3a1a1a") row3.addWidget(self.btn_remove) row3.addWidget(self.btn_clear) root.addLayout(row3) self.count_label = QLabel("0 tracks") self.count_label.setFont(QFont("Courier New", 9)) self.count_label.setStyleSheet( "color: #555; background: transparent; border: none;" ) self.count_label.setAlignment(Qt.AlignCenter) root.addWidget(self.count_label) self.btn_add_files.clicked.connect( lambda: self._add_files_to(self.playlist) ) self.btn_add_folder.clicked.connect( lambda: self._add_folder_to(self.playlist) ) self.btn_send_deck.clicked.connect(self._send_to_deck) self.btn_send_queue.clicked.connect(self._send_to_queue) self.btn_remove.clicked.connect(self._remove_selected) self.btn_clear.clicked.connect(self._clear_all) def _filter_list(self): query = self.search_box.text().lower() for i in range(self.playlist.count()): item = self.playlist.item(i) if isinstance(item, PlaylistItem): item.setHidden(bool(query and query not in item.filename.lower())) def _send_to_deck(self, item=None): if item is None: item = self.playlist.currentItem() if not isinstance(item, PlaylistItem): return deck_key = self.deck_selector.selected_deck self.engine.load_track(deck_key, item.filepath) name = os.path.splitext(item.filename)[0] if deck_key in self.deck_widgets: self.deck_widgets[deck_key].set_track_name(name) self._highlight(item, active=True) def _send_to_queue(self): item = self.playlist.currentItem() if not isinstance(item, PlaylistItem): return deck_key = self.deck_selector.selected_deck self.engine.load_queue(deck_key, item.filepath) name = os.path.splitext(item.filename)[0] if deck_key in self.deck_widgets: self.deck_widgets[deck_key].set_queue_name(name) self._highlight(item, queued=True) def _remove_selected(self): row = self.playlist.currentRow() if row >= 0: self.playlist.takeItem(row) self._update_count(self.playlist, self.count_label) def _clear_all(self): self.playlist.clear() self._update_count(self.playlist, self.count_label) def _context_menu(self, position): item = self.playlist.itemAt(position) if not isinstance(item, PlaylistItem): return menu = QMenu(self) menu.setStyleSheet(""" QMenu { background-color: #1e1e1e; color: #cccccc; border: 1px solid #444; } QMenu::item:selected { background-color: #1a3a5a; } """) def load(deck_key): self.engine.load_track(deck_key, item.filepath) name = os.path.splitext(item.filename)[0] if deck_key in self.deck_widgets: self.deck_widgets[deck_key].set_track_name(name) self._highlight(item, active=True) def queue(deck_key): self.engine.load_queue(deck_key, item.filepath) name = os.path.splitext(item.filename)[0] if deck_key in self.deck_widgets: self.deck_widgets[deck_key].set_queue_name(name) self._highlight(item, queued=True) a1 = QAction("Load → Deck 1", self) a2 = QAction("Load → Deck 2", self) a3 = QAction("Queue → Deck 1", self) a4 = QAction("Queue → Deck 2", self) ar = QAction("Remove", self) a1.triggered.connect(lambda: load("deck1")) a2.triggered.connect(lambda: load("deck2")) a3.triggered.connect(lambda: queue("deck1")) a4.triggered.connect(lambda: queue("deck2")) ar.triggered.connect( lambda: self.playlist.takeItem(self.playlist.row(item)) ) menu.addAction(a1) menu.addAction(a2) menu.addSeparator() menu.addAction(a3) menu.addAction(a4) menu.addSeparator() menu.addAction(ar) menu.exec(self.playlist.viewport().mapToGlobal(position)) self._update_count(self.playlist, self.count_label) # ───────────────────────────────────────── # CART PLAYLIST PANEL # ───────────────────────────────────────── class CartPlaylistPanel(BasePlaylistPanel): def __init__(self, engine, cart_player, parent=None): super().__init__(parent) self.engine = engine self.cart_player = cart_player self._build_ui() def _build_ui(self): self.setStyleSheet(""" QWidget { background-color: #111111; border: 2px solid #cc3300; border-radius: 6px; } """) root = QVBoxLayout(self) root.setContentsMargins(8, 8, 8, 8) root.setSpacing(6) header = QLabel("CART PLAYLIST") header.setFont(QFont("Arial", 12, QFont.Bold)) header.setStyleSheet( "color: #cc3300; background: transparent; border: none; padding: 2px;" ) header.setAlignment(Qt.AlignCenter) root.addWidget(header) self.search_box = QLineEdit() self.search_box.setPlaceholderText("search...") self.search_box.setFont(QFont("Courier New", 9)) self.search_box.setStyleSheet(""" QLineEdit { background-color: #0d0d0d; color: #ffaa00; border: 1px solid #333; border-radius: 3px; padding: 3px 6px; } QLineEdit:focus { border-color: #ffaa00; } """) self.search_box.textChanged.connect(self._filter_list) root.addWidget(self.search_box) self.playlist = self._make_list() self.playlist.itemDoubleClicked.connect(self._send_to_cart) self.playlist.customContextMenuRequested.connect(self._context_menu) root.addWidget(self.playlist) row1 = QHBoxLayout() row1.setSpacing(4) self.btn_add_files = self._make_btn("ADD FILES", "#5a2a00") self.btn_add_folder = self._make_btn("ADD FOLDER", "#4a2000") row1.addWidget(self.btn_add_files) row1.addWidget(self.btn_add_folder) root.addLayout(row1) row2 = QHBoxLayout() row2.setSpacing(4) self.btn_send_cart = self._make_btn("→ LOAD CART", "#3a5a1a") self.btn_send_queue = self._make_btn("→ QUEUE", "#4a3a00") row2.addWidget(self.btn_send_cart) row2.addWidget(self.btn_send_queue) root.addLayout(row2) row3 = QHBoxLayout() row3.setSpacing(4) self.btn_remove = self._make_btn("REMOVE", "#5a1a1a") self.btn_clear = self._make_btn("CLEAR ALL", "#3a1a1a") row3.addWidget(self.btn_remove) row3.addWidget(self.btn_clear) root.addLayout(row3) self.count_label = QLabel("0 tracks") self.count_label.setFont(QFont("Courier New", 9)) self.count_label.setStyleSheet( "color: #555; background: transparent; border: none;" ) self.count_label.setAlignment(Qt.AlignCenter) root.addWidget(self.count_label) self.btn_add_files.clicked.connect( lambda: self._add_files_to(self.playlist) ) self.btn_add_folder.clicked.connect( lambda: self._add_folder_to(self.playlist) ) self.btn_send_cart.clicked.connect(self._send_to_cart) self.btn_send_queue.clicked.connect(self._send_to_cart_queue) self.btn_remove.clicked.connect(self._remove_selected) self.btn_clear.clicked.connect(self._clear_all) def _filter_list(self): query = self.search_box.text().lower() for i in range(self.playlist.count()): item = self.playlist.item(i) if isinstance(item, PlaylistItem): item.setHidden(bool(query and query not in item.filename.lower())) def _send_to_cart(self, item=None): if item is None: item = self.playlist.currentItem() if not isinstance(item, PlaylistItem): return self.cart_player.load_track(item.filepath) self._highlight(item, active=True) def _send_to_cart_queue(self, item=None): if item is None: item = self.playlist.currentItem() if not isinstance(item, PlaylistItem): return self.engine.add_to_cart_queue(item.filepath) self._highlight(item, queued=True) def _remove_selected(self): row = self.playlist.currentRow() if row >= 0: self.playlist.takeItem(row) self._update_count(self.playlist, self.count_label) def _clear_all(self): self.playlist.clear() self._update_count(self.playlist, self.count_label) def _context_menu(self, position): item = self.playlist.itemAt(position) if not isinstance(item, PlaylistItem): return menu = QMenu(self) menu.setStyleSheet(""" QMenu { background-color: #1e1e1e; color: #cccccc; border: 1px solid #444; } QMenu::item:selected { background-color: #1a3a5a; } """) a1 = QAction("Load → Cart", self) a1.triggered.connect(lambda: self._send_to_cart(item)) menu.addAction(a1) a2 = QAction("Queue → Cart", self) a2.triggered.connect(lambda: self._send_to_cart_queue(item)) menu.addAction(a2) menu.addSeparator() ar = QAction("Remove", self) ar.triggered.connect( lambda: self.playlist.takeItem(self.playlist.row(item)) ) menu.addAction(ar) menu.exec(self.playlist.viewport().mapToGlobal(position)) self._update_count(self.playlist, self.count_label) # ───────────────────────────────────────── # COMBINED PLAYLIST WIDGET # ───────────────────────────────────────── class PlaylistWidget(QWidget): PLAYLIST_JSON_FILTER = "JSON Files (*.json)" def __init__(self, engine, deck_widgets: dict, cart_player, parent=None): super().__init__(parent) self.engine = engine self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.setMinimumWidth(200) root = QVBoxLayout(self) root.setContentsMargins(0, 0, 0, 0) root.setSpacing(4) bar = QHBoxLayout() bar.setSpacing(4) self._save_btn = QPushButton("SAVE") self._save_btn.setFixedHeight(28) self._save_btn.setIconSize(QSize(16, 16)) self._save_btn.setIcon(icon_save("#aaaaaa")) 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.setIconSize(QSize(16, 16)) self._load_btn.setIcon(icon_load("#aaaaaa")) 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(""" QSplitter::handle { background-color: #333; height: 4px; } """) self.track_panel = TrackPlaylistPanel(engine, deck_widgets) self.cart_panel = CartPlaylistPanel(engine, cart_player) splitter.addWidget(self.track_panel) splitter.addWidget(self.cart_panel) splitter.setSizes([400, 400]) 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)