Relayout files

Created classes.py and moved common classes to classes.py. Ordered
imports.
This commit is contained in:
Keith Edmunds 2023-11-01 19:08:22 +00:00
parent 15ecae54cf
commit d9ad001c75
6 changed files with 354 additions and 319 deletions

176
app/classes.py Normal file
View File

@ -0,0 +1,176 @@
from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import Optional
from PyQt6.QtCore import pyqtSignal, QObject
import numpy as np
import pyqtgraph as pg # type: ignore
from config import Config
from dbconfig import scoped_session
from models import PlaylistRows
import helpers
class FadeCurve:
GraphWidget = None
def __init__(self, track):
"""
Set up fade graph array
"""
audio = helpers.get_audio_segment(track.path)
if not audio:
return None
# Start point of curve is Config.FADE_CURVE_MS_BEFORE_FADE
# milliseconds before fade starts to silence
self.start_ms = max(0, track.fade_at - Config.FADE_CURVE_MS_BEFORE_FADE - 1)
self.end_ms = track.silence_at
self.audio_segment = audio[self.start_ms : self.end_ms]
self.graph_array = np.array(self.audio_segment.get_array_of_samples())
# Calculate the factor to map milliseconds of track to array
self.ms_to_array_factor = len(self.graph_array) / (self.end_ms - self.start_ms)
self.region = None
def clear(self) -> None:
"""Clear the current graph"""
if self.GraphWidget:
self.GraphWidget.clear()
def plot(self):
self.curve = self.GraphWidget.plot(self.graph_array)
self.curve.setPen(Config.FADE_CURVE_FOREGROUND)
def tick(self, play_time) -> None:
"""Update volume fade curve"""
if not self.GraphWidget:
return
ms_of_graph = play_time - self.start_ms
if ms_of_graph < 0:
return
if self.region is None:
# Create the region now that we're into fade
self.region = pg.LinearRegionItem([0, 0], bounds=[0, len(self.graph_array)])
self.GraphWidget.addItem(self.region)
# Update region position
self.region.setRegion([0, ms_of_graph * self.ms_to_array_factor])
@helpers.singleton
@dataclass
class MusicMusterSignals(QObject):
"""
Class for all MusicMuster signals. See:
- https://zetcode.com/gui/pyqt5/eventssignals/
- https://stackoverflow.com/questions/62654525/
emit-a-signal-from-another-class-to-main-class
and Singleton class at
https://refactoring.guru/design-patterns/singleton/python/example#example-0
"""
add_track_to_header_signal = pyqtSignal(int, int, int)
add_track_to_playlist_signal = pyqtSignal(int, int, int, str)
enable_escape_signal = pyqtSignal(bool)
set_next_track_signal = pyqtSignal(int, int)
span_cells_signal = pyqtSignal(int, int, int, int)
def __post_init__(self):
super().__init__()
class PlaylistTrack:
"""
Used to provide a single reference point for specific playlist tracks,
typically the previous, current and next track.
"""
def __init__(self) -> None:
"""
Only initialises data structure. Call set_plr to populate.
Do NOT store row_number here - that changes if tracks are reordered
in playlist (add, remove, drag/drop) and we shouldn't care about row
number: that's the playlist's problem.
"""
self.artist: Optional[str] = None
self.duration: Optional[int] = None
self.end_time: Optional[datetime] = None
self.fade_at: Optional[int] = None
self.fade_curve: Optional[FadeCurve] = None
self.fade_length: Optional[int] = None
self.path: Optional[str] = None
self.playlist_id: Optional[int] = None
self.plr_id: Optional[int] = None
self.silence_at: Optional[int] = None
self.start_gap: Optional[int] = None
self.start_time: Optional[datetime] = None
self.title: Optional[str] = None
self.track_id: Optional[int] = None
def __repr__(self) -> str:
return (
f"<PlaylistTrack(title={self.title}, artist={self.artist}, "
f"playlist_id={self.playlist_id}>"
)
def set_plr(self, session: scoped_session, plr: PlaylistRows) -> None:
"""
Update with new plr information
"""
if not plr.track:
return
session.add(plr)
track = plr.track
self.artist = track.artist
self.duration = track.duration
self.end_time = None
self.fade_at = track.fade_at
self.fade_graph = FadeCurve(track)
self.path = track.path
self.playlist_id = plr.playlist_id
self.plr_id = plr.id
self.silence_at = track.silence_at
self.start_gap = track.start_gap
self.start_time = None
self.title = track.title
self.track_id = track.id
if track.silence_at and track.fade_at:
self.fade_length = track.silence_at - track.fade_at
def start(self) -> None:
"""
Called when track starts playing
"""
self.start_time = datetime.now()
if self.duration:
self.end_time = self.start_time + timedelta(milliseconds=self.duration)
@helpers.singleton
class CurrentTrack(PlaylistTrack):
pass
@helpers.singleton
class NextTrack(PlaylistTrack):
pass
@helpers.singleton
class PreviousTrack(PlaylistTrack):
pass

View File

@ -1,14 +1,14 @@
from PyQt6.QtCore import QEvent, Qt
from PyQt6.QtWidgets import QDialog, QListWidgetItem
from typing import Optional from typing import Optional
from PyQt6.QtCore import QEvent, Qt
from PyQt6.QtWidgets import QDialog, QListWidgetItem
from classes import MusicMusterSignals
from dbconfig import scoped_session
from helpers import ( from helpers import (
get_relative_date, get_relative_date,
ms_to_mmss, ms_to_mmss,
MusicMusterSignals,
) )
from dbconfig import scoped_session
from models import Settings, Tracks from models import Settings, Tracks
from ui.dlg_TrackSelect_ui import Ui_Dialog # type: ignore from ui.dlg_TrackSelect_ui import Ui_Dialog # type: ignore

View File

@ -1,3 +1,6 @@
from datetime import datetime
from email.message import EmailMessage
from typing import Any, Dict, Optional
import functools import functools
import os import os
import psutil import psutil
@ -6,52 +9,19 @@ import smtplib
import ssl import ssl
import tempfile import tempfile
from config import Config
from datetime import datetime
from email.message import EmailMessage
from log import log
from mutagen.flac import FLAC # type: ignore from mutagen.flac import FLAC # type: ignore
from mutagen.mp3 import MP3 # type: ignore from mutagen.mp3 import MP3 # type: ignore
from pydub import AudioSegment, effects from pydub import AudioSegment, effects
from pydub.utils import mediainfo from pydub.utils import mediainfo
from PyQt6.QtCore import pyqtSignal, QObject
from PyQt6.QtWidgets import QMainWindow, QMessageBox # type: ignore from PyQt6.QtWidgets import QMainWindow, QMessageBox # type: ignore
from tinytag import TinyTag # type: ignore from tinytag import TinyTag # type: ignore
from typing import Any, Dict, Optional
from config import Config
from log import log
def singleton(cls): # Classes are defined after global functions so that classes can use
""" # those functions.
Make a class a Singleton class (see
https://realpython.com/primer-on-python-decorators/#creating-singletons)
"""
@functools.wraps(cls)
def wrapper_singleton(*args, **kwargs):
if not wrapper_singleton.instance:
wrapper_singleton.instance = cls(*args, **kwargs)
return wrapper_singleton.instance
wrapper_singleton.instance = None
return wrapper_singleton
@singleton
class MusicMusterSignals(QObject):
"""
Class for all MusicMuster signals. See:
- https://zetcode.com/gui/pyqt5/eventssignals/
- https://stackoverflow.com/questions/62654525/
emit-a-signal-from-another-class-to-main-class
and Singleton class at
https://refactoring.guru/design-patterns/singleton/python/example#example-0
"""
add_track_to_header_signal = pyqtSignal(int, int, int)
add_track_to_playlist_signal = pyqtSignal(int, int, int, str)
enable_escape_signal = pyqtSignal(bool)
set_next_track_signal = pyqtSignal(int, int)
span_cells_signal = pyqtSignal(int, int, int, int)
def ask_yes_no(title: str, question: str, default_yes: bool = False) -> bool: def ask_yes_no(title: str, question: str, default_yes: bool = False) -> bool:
"""Ask question; return True for yes, False for no""" """Ask question; return True for yes, False for no"""
@ -414,6 +384,22 @@ def show_warning(parent: QMainWindow, title: str, msg: str) -> None:
QMessageBox.warning(parent, title, msg, buttons=QMessageBox.StandardButton.Cancel) QMessageBox.warning(parent, title, msg, buttons=QMessageBox.StandardButton.Cancel)
def singleton(cls):
"""
Make a class a Singleton class (see
https://realpython.com/primer-on-python-decorators/#creating-singletons)
"""
@functools.wraps(cls)
def wrapper_singleton(*args, **kwargs):
if not wrapper_singleton.instance:
wrapper_singleton.instance = cls(*args, **kwargs)
return wrapper_singleton.instance
wrapper_singleton.instance = None
return wrapper_singleton
def trailing_silence( def trailing_silence(
audio_segment: AudioSegment, audio_segment: AudioSegment,
silence_threshold: int = -50, silence_threshold: int = -50,

View File

@ -1,18 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from log import log
from os.path import basename
import argparse
import os
import numpy as np
import pyqtgraph as pg # type: ignore
import stackprinter # type: ignore
import subprocess
import sys
import threading
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pygame import mixer from os.path import basename
from time import sleep from time import sleep
from typing import ( from typing import (
cast, cast,
@ -20,18 +9,20 @@ from typing import (
Optional, Optional,
Sequence, Sequence,
) )
import argparse
import os
import subprocess
import sys
import threading
from playlistmodel import PlaylistModel from pygame import mixer
from sqlalchemy import text
from PyQt6.QtCore import ( from PyQt6.QtCore import (
pyqtSignal, pyqtSignal,
QDate, QDate,
QEvent, QEvent,
QObject, QObject,
Qt,
QSize, QSize,
Qt,
QThread, QThread,
QTime, QTime,
QTimer, QTimer,
@ -54,27 +45,39 @@ from PyQt6.QtWidgets import (
QListWidgetItem, QListWidgetItem,
QMainWindow, QMainWindow,
QMessageBox, QMessageBox,
QPushButton,
QProgressBar, QProgressBar,
QPushButton,
) )
from sqlalchemy import text
import stackprinter # type: ignore
from classes import (
CurrentTrack,
FadeCurve,
MusicMusterSignals,
NextTrack,
PlaylistTrack,
PreviousTrack,
)
from config import Config
from dbconfig import ( from dbconfig import (
engine, engine,
Session,
scoped_session, scoped_session,
Session,
) )
import helpers
import icons_rc # noqa F401
import music
from dialogs import TrackSelectDialog from dialogs import TrackSelectDialog
from log import log
from models import Base, Carts, Playdates, PlaylistRows, Playlists, Settings, Tracks from models import Base, Carts, Playdates, PlaylistRows, Playlists, Settings, Tracks
from config import Config from playlistmodel import PlaylistModel
from playlists import PlaylistTab from playlists import PlaylistTab
from ui.dlg_cart_ui import Ui_DialogCartEdit # type: ignore from ui.dlg_cart_ui import Ui_DialogCartEdit # type: ignore
from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist # type: ignore from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist # type: ignore
from ui.downloadcsv_ui import Ui_DateSelect # type: ignore from ui.downloadcsv_ui import Ui_DateSelect # type: ignore
from ui.main_window_ui import Ui_MainWindow # type: ignore from ui.main_window_ui import Ui_MainWindow # type: ignore
from utilities import check_db, update_bitrates from utilities import check_db, update_bitrates
import helpers
import icons_rc # noqa F401
import music
class CartButton(QPushButton): class CartButton(QPushButton):
@ -144,59 +147,6 @@ class CartButton(QPushButton):
self.pgb.setGeometry(0, 0, self.width(), 10) self.pgb.setGeometry(0, 0, self.width(), 10)
class FadeCurve:
GraphWidget = None
def __init__(self, track):
"""
Set up fade graph array
"""
audio = helpers.get_audio_segment(track.path)
if not audio:
return None
# Start point of curve is Config.FADE_CURVE_MS_BEFORE_FADE
# milliseconds before fade starts to silence
self.start_ms = max(0, track.fade_at - Config.FADE_CURVE_MS_BEFORE_FADE - 1)
self.end_ms = track.silence_at
self.audio_segment = audio[self.start_ms : self.end_ms]
self.graph_array = np.array(self.audio_segment.get_array_of_samples())
# Calculate the factor to map milliseconds of track to array
self.ms_to_array_factor = len(self.graph_array) / (self.end_ms - self.start_ms)
self.region = None
def clear(self) -> None:
"""Clear the current graph"""
if self.GraphWidget:
self.GraphWidget.clear()
def plot(self):
self.curve = self.GraphWidget.plot(self.graph_array)
self.curve.setPen(Config.FADE_CURVE_FOREGROUND)
def tick(self, play_time) -> None:
"""Update volume fade curve"""
if not self.GraphWidget:
return
ms_of_graph = play_time - self.start_ms
if ms_of_graph < 0:
return
if self.region is None:
# Create the region now that we're into fade
self.region = pg.LinearRegionItem([0, 0], bounds=[0, len(self.graph_array)])
self.GraphWidget.addItem(self.region)
# Update region position
self.region.setRegion([0, ms_of_graph * self.ms_to_array_factor])
class ImportTrack(QObject): class ImportTrack(QObject):
import_error = pyqtSignal(str) import_error = pyqtSignal(str)
importing = pyqtSignal(str) importing = pyqtSignal(str)
@ -237,84 +187,6 @@ class ImportTrack(QObject):
self.finished.emit(self.playlist) self.finished.emit(self.playlist)
class PlaylistTrack:
"""
Used to provide a single reference point for specific playlist tracks,
typically the previous, current and next track.
"""
def __init__(self) -> None:
"""
Only initialises data structure. Call set_plr to populate.
Do NOT store row_number here - that changes if tracks are reordered
in playlist (add, remove, drag/drop) and we shouldn't care about row
number: that's the playlist's problem.
"""
self.artist: Optional[str] = None
self.duration: Optional[int] = None
self.end_time: Optional[datetime] = None
self.fade_at: Optional[int] = None
self.fade_curve: Optional[FadeCurve] = None
self.fade_length: Optional[int] = None
self.path: Optional[str] = None
self.playlist_id: Optional[int] = None
self.playlist_tab: Optional[PlaylistTab] = None
self.plr_id: Optional[int] = None
self.silence_at: Optional[int] = None
self.start_gap: Optional[int] = None
self.start_time: Optional[datetime] = None
self.title: Optional[str] = None
self.track_id: Optional[int] = None
def __repr__(self) -> str:
return (
f"<PlaylistTrack(title={self.title}, artist={self.artist}, "
f"playlist_id={self.playlist_id}>"
)
def set_plr(
self, session: scoped_session, plr: PlaylistRows, tab: PlaylistTab
) -> None:
"""
Update with new plr information
"""
if not plr.track:
return
self.playlist_tab = tab
session.add(plr)
track = plr.track
self.artist = track.artist
self.duration = track.duration
self.end_time = None
self.fade_at = track.fade_at
self.fade_graph = FadeCurve(track)
self.path = track.path
self.playlist_id = plr.playlist_id
self.plr_id = plr.id
self.silence_at = track.silence_at
self.start_gap = track.start_gap
self.start_time = None
self.title = track.title
self.track_id = track.id
if track.silence_at and track.fade_at:
self.fade_length = track.silence_at - track.fade_at
def start(self) -> None:
"""
Called when track starts playing
"""
self.start_time = datetime.now()
if self.duration:
self.end_time = self.start_time + timedelta(milliseconds=self.duration)
class Window(QMainWindow, Ui_MainWindow): class Window(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None, *args, **kwargs) -> None: def __init__(self, parent=None, *args, **kwargs) -> None:
super().__init__(parent) super().__init__(parent)
@ -327,9 +199,9 @@ class Window(QMainWindow, Ui_MainWindow):
self.music: music.Music = music.Music() self.music: music.Music = music.Music()
self.playing: bool = False self.playing: bool = False
self.current_track = PlaylistTrack() self.current_track = CurrentTrack()
self.next_track = PlaylistTrack() self.next_track = NextTrack()
self.previous_track = PlaylistTrack() self.previous_track = PreviousTrack()
self.previous_track_position: Optional[float] = None self.previous_track_position: Optional[float] = None
self.selected_plrs: Optional[List[PlaylistRows]] = None self.selected_plrs: Optional[List[PlaylistRows]] = None
@ -362,7 +234,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.timer10.start(10) self.timer10.start(10)
self.timer500.start(500) self.timer500.start(500)
self.timer1000.start(1000) self.timer1000.start(1000)
self.signals = helpers.MusicMusterSignals() self.signals = MusicMusterSignals()
self.connect_signals_slots() self.connect_signals_slots()
def about(self) -> None: def about(self) -> None:
@ -521,7 +393,7 @@ class Window(QMainWindow, Ui_MainWindow):
Clear next track Clear next track
""" """
self.next_track = PlaylistTrack() self.next_track = NextTrack()
self.update_headers() self.update_headers()
def clear_selection(self) -> None: def clear_selection(self) -> None:
@ -598,27 +470,29 @@ class Window(QMainWindow, Ui_MainWindow):
Return True if tab closed else False. Return True if tab closed else False.
""" """
# Don't close current track playlist return False
if self.tabPlaylist.widget(tab_index) == (self.current_track.playlist_tab): # TODO Reimplement without ussing self.current_track.playlist_tab
self.statusbar.showMessage("Can't close current track playlist", 5000) # # Don't close current track playlist
return False # if self.tabPlaylist.widget(tab_index) == (self.current_track.playlist_tab):
# self.statusbar.showMessage("Can't close current track playlist", 5000)
# return False
# Attempt to close next track playlist # # Attempt to close next track playlist
if self.tabPlaylist.widget(tab_index) == self.next_track.playlist_tab: # if self.tabPlaylist.widget(tab_index) == self.next_track.playlist_tab:
self.next_track.playlist_tab.clear_next() # self.next_track.playlist_tab.clear_next()
# Record playlist as closed and update remaining playlist tabs # # Record playlist as closed and update remaining playlist tabs
with Session() as session: # with Session() as session:
playlist_id = self.tabPlaylist.widget(tab_index).playlist_id # playlist_id = self.tabPlaylist.widget(tab_index).playlist_id
playlist = session.get(Playlists, playlist_id) # playlist = session.get(Playlists, playlist_id)
if playlist: # if playlist:
playlist.close(session) # playlist.close(session)
# Close playlist and remove tab # # Close playlist and remove tab
self.tabPlaylist.widget(tab_index).close() # self.tabPlaylist.widget(tab_index).close()
self.tabPlaylist.removeTab(tab_index) # self.tabPlaylist.removeTab(tab_index)
return True # return True
def connect_signals_slots(self) -> None: def connect_signals_slots(self) -> None:
self.action_About.triggered.connect(self.about) self.action_About.triggered.connect(self.about)
@ -834,16 +708,17 @@ class Window(QMainWindow, Ui_MainWindow):
self.playing = False self.playing = False
# Tell playlist_tab track has finished # Tell playlist_tab track has finished
if self.current_track.playlist_tab: # TODO Reimplement as a signal
self.current_track.playlist_tab.play_ended() # if self.current_track.playlist_tab:
# self.current_track.playlist_tab.play_ended()
# Reset fade graph # Reset fade graph
self.current_track.fade_graph.clear() self.current_track.fade_graph.clear()
# Reset PlaylistTrack objects # Reset PlaylistTrack objects
if self.current_track.track_id: if self.current_track.track_id:
self.previous_track = self.current_track self.previous_track = cast(PreviousTrack, self.current_track)
self.current_track = PlaylistTrack() self.current_track = CurrentTrack()
# Reset clocks # Reset clocks
self.frame_fade.setStyleSheet("") self.frame_fade.setStyleSheet("")
@ -907,18 +782,6 @@ class Window(QMainWindow, Ui_MainWindow):
self.stop_playing(fade=True) self.stop_playing(fade=True)
def get_one_track(self, session: scoped_session) -> Optional[Tracks]:
"""Show dialog box to select one track and return it to caller"""
dlg = TrackSelectDialog(self, session)
dlg.ui.txtNote.hide()
dlg.ui.lblNote.hide()
if dlg.exec():
return dlg.track
else:
return None
def get_playtime(self) -> int: def get_playtime(self) -> int:
""" """
Return number of milliseconds current track has been playing or Return number of milliseconds current track has been playing or
@ -1013,7 +876,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.worker.finished.connect(self.import_complete) self.worker.finished.connect(self.import_complete)
self.import_thread.start() self.import_thread.start()
def import_complete(self, playlist_tab: PlaylistTab): def import_complete(self):
""" """
Called by thread when track import complete Called by thread when track import complete
""" """
@ -1302,7 +1165,7 @@ class Window(QMainWindow, Ui_MainWindow):
# Move next track to current track. # Move next track to current track.
# stop_playing() above has called end_of_track_actions() # stop_playing() above has called end_of_track_actions()
# which will have populated self.previous_track # which will have populated self.previous_track
self.current_track = self.next_track self.current_track = cast(CurrentTrack, self.next_track)
self.clear_next() self.clear_next()
if not self.current_track.track_id: if not self.current_track.track_id:
@ -1313,9 +1176,10 @@ class Window(QMainWindow, Ui_MainWindow):
return return
# Set current track playlist_tab colour # Set current track playlist_tab colour
current_tab = self.current_track.playlist_tab # TODO Reimplement without reference to self.current_track.playlist_tab
if current_tab: # current_tab = self.current_track.playlist_tab
self.set_tab_colour(current_tab, QColor(Config.COLOUR_CURRENT_TAB)) # if current_tab:
# self.set_tab_colour(current_tab, QColor(Config.COLOUR_CURRENT_TAB))
# Restore volume if -3dB active # Restore volume if -3dB active
if self.btnDrop3db.isChecked(): if self.btnDrop3db.isChecked():
@ -1344,8 +1208,9 @@ class Window(QMainWindow, Ui_MainWindow):
Playdates(session, self.current_track.track_id) Playdates(session, self.current_track.track_id)
# Tell playlist track is now playing # Tell playlist track is now playing
if self.current_track.playlist_tab: # TODO Reimplement without reference to self.current_track.playlist_tab:
self.current_track.playlist_tab.play_started(session) # if self.current_track.playlist_tab:
# self.current_track.playlist_tab.play_started(session)
# Note that track is now playing # Note that track is now playing
self.playing = True self.playing = True
@ -1404,41 +1269,45 @@ class Window(QMainWindow, Ui_MainWindow):
- If a track is playing, make that the next track - If a track is playing, make that the next track
""" """
# Return if no saved position return
if not self.previous_track_position: # TODO Reimplement without reference to playlist_tab
return # # Return if no saved position
# if not self.previous_track_position:
# return
# Note any playing track as this will become the next track # # Note any playing track as this will become the next track
playing_track = None # playing_track = None
if self.current_track.track_id: # if self.current_track.track_id:
playing_track = self.current_track # playing_track = self.current_track
# Set next plr to be track to resume # # Set next plr to be track to resume
if not self.previous_track.plr_id: # if not self.previous_track.plr_id:
return # return
if not self.previous_track.playlist_tab: # # TODO Reimplement following two lines
return # # if not self.previous_track.playlist_tab:
# # return
# Resume last track # # Resume last track
self.set_next_plr_id( # # TODO Reimplement next four lines
self.previous_track.plr_id, self.previous_track.playlist_tab # # self.set_next_plr_id(
) # # self.previous_track.plr_id, self.previous_track.playlist_tab
self.play_next(self.previous_track_position) # # )
# # self.play_next(self.previous_track_position)
# Adjust track info so that clocks and graph are correct. # # Adjust track info so that clocks and graph are correct.
# Easiest way is to fake the start time. # # Easiest way is to fake the start time.
if self.current_track.start_time and self.current_track.duration: # if self.current_track.start_time and self.current_track.duration:
elapsed_ms = self.current_track.duration * self.previous_track_position # elapsed_ms = self.current_track.duration * self.previous_track_position
self.current_track.start_time -= timedelta(milliseconds=elapsed_ms) # self.current_track.start_time -= timedelta(milliseconds=elapsed_ms)
# If a track was playing when we were called, get details to # # If a track was playing when we were called, get details to
# set it as the next track # # set it as the next track
if playing_track: # if playing_track:
if not playing_track.plr_id: # if not playing_track.plr_id:
return # return
if not playing_track.playlist_tab: # if not playing_track.playlist_tab:
return # return
self.set_next_plr_id(playing_track.plr_id, playing_track.playlist_tab) # self.set_next_plr_id(playing_track.plr_id, playing_track.playlist_tab)
def save_as_template(self) -> None: def save_as_template(self) -> None:
"""Save current playlist as template""" """Save current playlist as template"""
@ -1564,16 +1433,20 @@ class Window(QMainWindow, Ui_MainWindow):
def show_current(self) -> None: def show_current(self) -> None:
"""Scroll to show current track""" """Scroll to show current track"""
if self.current_track.playlist_tab != self.active_tab(): return
self.tabPlaylist.setCurrentWidget(self.current_track.playlist_tab) # TODO Reimplement
self.tabPlaylist.currentWidget().scroll_current_to_top() # if self.current_track.playlist_tab != self.active_tab():
# self.tabPlaylist.setCurrentWidget(self.current_track.playlist_tab)
# self.tabPlaylist.currentWidget().scroll_current_to_top()
def show_next(self) -> None: def show_next(self) -> None:
"""Scroll to show next track""" """Scroll to show next track"""
if self.next_track.playlist_tab != self.active_tab(): return
self.tabPlaylist.setCurrentWidget(self.next_track.playlist_tab) # TODO Reimplement
self.tabPlaylist.currentWidget().scroll_next_to_top() # if self.next_track.playlist_tab != self.active_tab():
# self.tabPlaylist.setCurrentWidget(self.next_track.playlist_tab)
# self.tabPlaylist.currentWidget().scroll_next_to_top()
def solicit_playlist_name(self, default: Optional[str] = "") -> Optional[str]: def solicit_playlist_name(self, default: Optional[str] = "") -> Optional[str]:
"""Get name of playlist from user""" """Get name of playlist from user"""
@ -1618,15 +1491,16 @@ class Window(QMainWindow, Ui_MainWindow):
self.music.stop() self.music.stop()
# Reset playlist_tab colour # Reset playlist_tab colour
if self.current_track.playlist_tab: # TODO Reimplement
if self.current_track.playlist_tab == self.next_track.playlist_tab: # if self.current_track.playlist_tab:
self.set_tab_colour( # if self.current_track.playlist_tab == self.next_track.playlist_tab:
self.current_track.playlist_tab, QColor(Config.COLOUR_NEXT_TAB) # self.set_tab_colour(
) # self.current_track.playlist_tab, QColor(Config.COLOUR_NEXT_TAB)
else: # )
self.set_tab_colour( # else:
self.current_track.playlist_tab, QColor(Config.COLOUR_NORMAL_TAB) # self.set_tab_colour(
) # self.current_track.playlist_tab, QColor(Config.COLOUR_NORMAL_TAB)
# )
# Run end-of-track actions # Run end-of-track actions
self.end_of_track_actions() self.end_of_track_actions()
@ -1670,11 +1544,11 @@ class Window(QMainWindow, Ui_MainWindow):
with Session() as session: with Session() as session:
# Update self.next_track PlaylistTrack structure # Update self.next_track PlaylistTrack structure
old_next_track = self.next_track old_next_track = self.next_track
self.next_track = PlaylistTrack() self.next_track = NextTrack()
if next_plr_id: if next_plr_id:
next_plr = session.get(PlaylistRows, next_plr_id) next_plr = session.get(PlaylistRows, next_plr_id)
if next_plr: if next_plr:
self.next_track.set_plr(session, next_plr, playlist_tab) self.next_track.set_plr(session, next_plr)
# Tell playlist tabs to update their 'next track' highlighting # Tell playlist tabs to update their 'next track' highlighting
# Args must both be ints, so use zero for no next track # Args must both be ints, so use zero for no next track
@ -1709,30 +1583,32 @@ class Window(QMainWindow, Ui_MainWindow):
# If the original next playlist tab isn't the same as the # If the original next playlist tab isn't the same as the
# new one or the current track, it needs its colour reset. # new one or the current track, it needs its colour reset.
if ( return
old_next_track # TODO Reimplement
and old_next_track.playlist_tab # if (
and old_next_track.playlist_tab # old_next_track
not in [self.next_track.playlist_tab, self.current_track.playlist_tab] # and old_next_track.playlist_tab
): # and old_next_track.playlist_tab
self.set_tab_colour( # not in [self.next_track.playlist_tab, self.current_track.playlist_tab]
old_next_track.playlist_tab, QColor(Config.COLOUR_NORMAL_TAB) # ):
) # self.set_tab_colour(
# If the new next playlist tab isn't the same as the # old_next_track.playlist_tab, QColor(Config.COLOUR_NORMAL_TAB)
# old one or the current track, it needs its colour set. # )
if old_next_track: # # If the new next playlist tab isn't the same as the
old_tab = old_next_track.playlist_tab # # old one or the current track, it needs its colour set.
else: # if old_next_track:
old_tab = None # old_tab = old_next_track.playlist_tab
if ( # else:
self.next_track # old_tab = None
and self.next_track.playlist_tab # if (
and self.next_track.playlist_tab # self.next_track
not in [old_tab, self.current_track.playlist_tab] # and self.next_track.playlist_tab
): # and self.next_track.playlist_tab
self.set_tab_colour( # not in [old_tab, self.current_track.playlist_tab]
self.next_track.playlist_tab, QColor(Config.COLOUR_NEXT_TAB) # ):
) # self.set_tab_colour(
# self.next_track.playlist_tab, QColor(Config.COLOUR_NEXT_TAB)
# )
def tick_10ms(self) -> None: def tick_10ms(self) -> None:
""" """

View File

@ -1,7 +1,6 @@
from datetime import datetime from datetime import datetime
from enum import auto, Enum from enum import auto, Enum
from sqlalchemy import bindparam, update from typing import List, Optional
from typing import List, Optional, TYPE_CHECKING
from PyQt6.QtCore import ( from PyQt6.QtCore import (
QAbstractTableModel, QAbstractTableModel,
@ -15,12 +14,10 @@ from PyQt6.QtGui import (
QFont, QFont,
) )
from classes import CurrentTrack, MusicMusterSignals, NextTrack
from config import Config from config import Config
from dbconfig import scoped_session, Session from dbconfig import scoped_session, Session
from helpers import ( from helpers import file_is_unreadable
file_is_unreadable,
MusicMusterSignals,
)
from log import log from log import log
from models import PlaylistRows, Tracks from models import PlaylistRows, Tracks

View File

@ -37,13 +37,13 @@ from PyQt6.QtWidgets import (
from dbconfig import Session, scoped_session from dbconfig import Session, scoped_session
from dialogs import TrackSelectDialog from dialogs import TrackSelectDialog
from classes import MusicMusterSignals
from config import Config from config import Config
from helpers import ( from helpers import (
ask_yes_no, ask_yes_no,
file_is_unreadable, file_is_unreadable,
get_relative_date, get_relative_date,
ms_to_mmss, ms_to_mmss,
MusicMusterSignals,
open_in_audacity, open_in_audacity,
send_mail, send_mail,
set_track_metadata, set_track_metadata,