Relayout files
Created classes.py and moved common classes to classes.py. Ordered imports.
This commit is contained in:
parent
15ecae54cf
commit
d9ad001c75
176
app/classes.py
Normal file
176
app/classes.py
Normal 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
|
||||
@ -1,14 +1,14 @@
|
||||
from PyQt6.QtCore import QEvent, Qt
|
||||
from PyQt6.QtWidgets import QDialog, QListWidgetItem
|
||||
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 (
|
||||
get_relative_date,
|
||||
ms_to_mmss,
|
||||
MusicMusterSignals,
|
||||
)
|
||||
|
||||
from dbconfig import scoped_session
|
||||
from models import Settings, Tracks
|
||||
from ui.dlg_TrackSelect_ui import Ui_Dialog # type: ignore
|
||||
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
from datetime import datetime
|
||||
from email.message import EmailMessage
|
||||
from typing import Any, Dict, Optional
|
||||
import functools
|
||||
import os
|
||||
import psutil
|
||||
@ -6,52 +9,19 @@ import smtplib
|
||||
import ssl
|
||||
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.mp3 import MP3 # type: ignore
|
||||
from pydub import AudioSegment, effects
|
||||
from pydub.utils import mediainfo
|
||||
from PyQt6.QtCore import pyqtSignal, QObject
|
||||
from PyQt6.QtWidgets import QMainWindow, QMessageBox # 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):
|
||||
"""
|
||||
Make a class a Singleton class (see
|
||||
https://realpython.com/primer-on-python-decorators/#creating-singletons)
|
||||
"""
|
||||
# Classes are defined after global functions so that classes can use
|
||||
# those functions.
|
||||
|
||||
@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:
|
||||
"""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)
|
||||
|
||||
|
||||
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(
|
||||
audio_segment: AudioSegment,
|
||||
silence_threshold: int = -50,
|
||||
|
||||
@ -1,18 +1,7 @@
|
||||
#!/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 pygame import mixer
|
||||
from os.path import basename
|
||||
from time import sleep
|
||||
from typing import (
|
||||
cast,
|
||||
@ -20,18 +9,20 @@ from typing import (
|
||||
Optional,
|
||||
Sequence,
|
||||
)
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
|
||||
from playlistmodel import PlaylistModel
|
||||
|
||||
from sqlalchemy import text
|
||||
|
||||
from pygame import mixer
|
||||
from PyQt6.QtCore import (
|
||||
pyqtSignal,
|
||||
QDate,
|
||||
QEvent,
|
||||
QObject,
|
||||
Qt,
|
||||
QSize,
|
||||
Qt,
|
||||
QThread,
|
||||
QTime,
|
||||
QTimer,
|
||||
@ -54,27 +45,39 @@ from PyQt6.QtWidgets import (
|
||||
QListWidgetItem,
|
||||
QMainWindow,
|
||||
QMessageBox,
|
||||
QPushButton,
|
||||
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 (
|
||||
engine,
|
||||
Session,
|
||||
scoped_session,
|
||||
Session,
|
||||
)
|
||||
import helpers
|
||||
import icons_rc # noqa F401
|
||||
import music
|
||||
from dialogs import TrackSelectDialog
|
||||
from log import log
|
||||
from models import Base, Carts, Playdates, PlaylistRows, Playlists, Settings, Tracks
|
||||
from config import Config
|
||||
from playlistmodel import PlaylistModel
|
||||
from playlists import PlaylistTab
|
||||
from ui.dlg_cart_ui import Ui_DialogCartEdit # type: ignore
|
||||
from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist # type: ignore
|
||||
from ui.downloadcsv_ui import Ui_DateSelect # type: ignore
|
||||
from ui.main_window_ui import Ui_MainWindow # type: ignore
|
||||
from utilities import check_db, update_bitrates
|
||||
import helpers
|
||||
import icons_rc # noqa F401
|
||||
import music
|
||||
|
||||
|
||||
class CartButton(QPushButton):
|
||||
@ -144,59 +147,6 @@ class CartButton(QPushButton):
|
||||
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):
|
||||
import_error = pyqtSignal(str)
|
||||
importing = pyqtSignal(str)
|
||||
@ -237,84 +187,6 @@ class ImportTrack(QObject):
|
||||
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):
|
||||
def __init__(self, parent=None, *args, **kwargs) -> None:
|
||||
super().__init__(parent)
|
||||
@ -327,9 +199,9 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.music: music.Music = music.Music()
|
||||
self.playing: bool = False
|
||||
|
||||
self.current_track = PlaylistTrack()
|
||||
self.next_track = PlaylistTrack()
|
||||
self.previous_track = PlaylistTrack()
|
||||
self.current_track = CurrentTrack()
|
||||
self.next_track = NextTrack()
|
||||
self.previous_track = PreviousTrack()
|
||||
|
||||
self.previous_track_position: Optional[float] = None
|
||||
self.selected_plrs: Optional[List[PlaylistRows]] = None
|
||||
@ -362,7 +234,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.timer10.start(10)
|
||||
self.timer500.start(500)
|
||||
self.timer1000.start(1000)
|
||||
self.signals = helpers.MusicMusterSignals()
|
||||
self.signals = MusicMusterSignals()
|
||||
self.connect_signals_slots()
|
||||
|
||||
def about(self) -> None:
|
||||
@ -521,7 +393,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
Clear next track
|
||||
"""
|
||||
|
||||
self.next_track = PlaylistTrack()
|
||||
self.next_track = NextTrack()
|
||||
self.update_headers()
|
||||
|
||||
def clear_selection(self) -> None:
|
||||
@ -598,27 +470,29 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
Return True if tab closed else False.
|
||||
"""
|
||||
|
||||
# Don't close current track playlist
|
||||
if self.tabPlaylist.widget(tab_index) == (self.current_track.playlist_tab):
|
||||
self.statusbar.showMessage("Can't close current track playlist", 5000)
|
||||
return False
|
||||
return False
|
||||
# TODO Reimplement without ussing self.current_track.playlist_tab
|
||||
# # Don't close current track playlist
|
||||
# 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
|
||||
if self.tabPlaylist.widget(tab_index) == self.next_track.playlist_tab:
|
||||
self.next_track.playlist_tab.clear_next()
|
||||
# # Attempt to close next track playlist
|
||||
# if self.tabPlaylist.widget(tab_index) == self.next_track.playlist_tab:
|
||||
# self.next_track.playlist_tab.clear_next()
|
||||
|
||||
# Record playlist as closed and update remaining playlist tabs
|
||||
with Session() as session:
|
||||
playlist_id = self.tabPlaylist.widget(tab_index).playlist_id
|
||||
playlist = session.get(Playlists, playlist_id)
|
||||
if playlist:
|
||||
playlist.close(session)
|
||||
# # Record playlist as closed and update remaining playlist tabs
|
||||
# with Session() as session:
|
||||
# playlist_id = self.tabPlaylist.widget(tab_index).playlist_id
|
||||
# playlist = session.get(Playlists, playlist_id)
|
||||
# if playlist:
|
||||
# playlist.close(session)
|
||||
|
||||
# Close playlist and remove tab
|
||||
self.tabPlaylist.widget(tab_index).close()
|
||||
self.tabPlaylist.removeTab(tab_index)
|
||||
# # Close playlist and remove tab
|
||||
# self.tabPlaylist.widget(tab_index).close()
|
||||
# self.tabPlaylist.removeTab(tab_index)
|
||||
|
||||
return True
|
||||
# return True
|
||||
|
||||
def connect_signals_slots(self) -> None:
|
||||
self.action_About.triggered.connect(self.about)
|
||||
@ -834,16 +708,17 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.playing = False
|
||||
|
||||
# Tell playlist_tab track has finished
|
||||
if self.current_track.playlist_tab:
|
||||
self.current_track.playlist_tab.play_ended()
|
||||
# TODO Reimplement as a signal
|
||||
# if self.current_track.playlist_tab:
|
||||
# self.current_track.playlist_tab.play_ended()
|
||||
|
||||
# Reset fade graph
|
||||
self.current_track.fade_graph.clear()
|
||||
|
||||
# Reset PlaylistTrack objects
|
||||
if self.current_track.track_id:
|
||||
self.previous_track = self.current_track
|
||||
self.current_track = PlaylistTrack()
|
||||
self.previous_track = cast(PreviousTrack, self.current_track)
|
||||
self.current_track = CurrentTrack()
|
||||
|
||||
# Reset clocks
|
||||
self.frame_fade.setStyleSheet("")
|
||||
@ -907,18 +782,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
|
||||
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:
|
||||
"""
|
||||
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.import_thread.start()
|
||||
|
||||
def import_complete(self, playlist_tab: PlaylistTab):
|
||||
def import_complete(self):
|
||||
"""
|
||||
Called by thread when track import complete
|
||||
"""
|
||||
@ -1302,7 +1165,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
# Move next track to current track.
|
||||
# stop_playing() above has called end_of_track_actions()
|
||||
# which will have populated self.previous_track
|
||||
self.current_track = self.next_track
|
||||
self.current_track = cast(CurrentTrack, self.next_track)
|
||||
self.clear_next()
|
||||
|
||||
if not self.current_track.track_id:
|
||||
@ -1313,9 +1176,10 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
return
|
||||
|
||||
# Set current track playlist_tab colour
|
||||
current_tab = self.current_track.playlist_tab
|
||||
if current_tab:
|
||||
self.set_tab_colour(current_tab, QColor(Config.COLOUR_CURRENT_TAB))
|
||||
# TODO Reimplement without reference to self.current_track.playlist_tab
|
||||
# current_tab = self.current_track.playlist_tab
|
||||
# if current_tab:
|
||||
# self.set_tab_colour(current_tab, QColor(Config.COLOUR_CURRENT_TAB))
|
||||
|
||||
# Restore volume if -3dB active
|
||||
if self.btnDrop3db.isChecked():
|
||||
@ -1344,8 +1208,9 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
Playdates(session, self.current_track.track_id)
|
||||
|
||||
# Tell playlist track is now playing
|
||||
if self.current_track.playlist_tab:
|
||||
self.current_track.playlist_tab.play_started(session)
|
||||
# TODO Reimplement without reference to self.current_track.playlist_tab:
|
||||
# if self.current_track.playlist_tab:
|
||||
# self.current_track.playlist_tab.play_started(session)
|
||||
|
||||
# Note that track is now playing
|
||||
self.playing = True
|
||||
@ -1404,41 +1269,45 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
- If a track is playing, make that the next track
|
||||
"""
|
||||
|
||||
# Return if no saved position
|
||||
if not self.previous_track_position:
|
||||
return
|
||||
return
|
||||
# TODO Reimplement without reference to playlist_tab
|
||||
# # Return if no saved position
|
||||
# if not self.previous_track_position:
|
||||
# return
|
||||
|
||||
# Note any playing track as this will become the next track
|
||||
playing_track = None
|
||||
if self.current_track.track_id:
|
||||
playing_track = self.current_track
|
||||
# # Note any playing track as this will become the next track
|
||||
# playing_track = None
|
||||
# if self.current_track.track_id:
|
||||
# playing_track = self.current_track
|
||||
|
||||
# Set next plr to be track to resume
|
||||
if not self.previous_track.plr_id:
|
||||
return
|
||||
if not self.previous_track.playlist_tab:
|
||||
return
|
||||
# # Set next plr to be track to resume
|
||||
# if not self.previous_track.plr_id:
|
||||
# return
|
||||
# # TODO Reimplement following two lines
|
||||
# # if not self.previous_track.playlist_tab:
|
||||
# # return
|
||||
|
||||
# Resume last track
|
||||
self.set_next_plr_id(
|
||||
self.previous_track.plr_id, self.previous_track.playlist_tab
|
||||
)
|
||||
self.play_next(self.previous_track_position)
|
||||
# # Resume last track
|
||||
# # TODO Reimplement next four lines
|
||||
# # self.set_next_plr_id(
|
||||
# # self.previous_track.plr_id, self.previous_track.playlist_tab
|
||||
# # )
|
||||
# # self.play_next(self.previous_track_position)
|
||||
|
||||
# Adjust track info so that clocks and graph are correct.
|
||||
# Easiest way is to fake the start time.
|
||||
if self.current_track.start_time and self.current_track.duration:
|
||||
elapsed_ms = self.current_track.duration * self.previous_track_position
|
||||
self.current_track.start_time -= timedelta(milliseconds=elapsed_ms)
|
||||
# # Adjust track info so that clocks and graph are correct.
|
||||
# # Easiest way is to fake the start time.
|
||||
# if self.current_track.start_time and self.current_track.duration:
|
||||
# elapsed_ms = self.current_track.duration * self.previous_track_position
|
||||
# self.current_track.start_time -= timedelta(milliseconds=elapsed_ms)
|
||||
|
||||
# If a track was playing when we were called, get details to
|
||||
# set it as the next track
|
||||
if playing_track:
|
||||
if not playing_track.plr_id:
|
||||
return
|
||||
if not playing_track.playlist_tab:
|
||||
return
|
||||
self.set_next_plr_id(playing_track.plr_id, playing_track.playlist_tab)
|
||||
# # If a track was playing when we were called, get details to
|
||||
# # set it as the next track
|
||||
# if playing_track:
|
||||
# if not playing_track.plr_id:
|
||||
# return
|
||||
# if not playing_track.playlist_tab:
|
||||
# return
|
||||
# self.set_next_plr_id(playing_track.plr_id, playing_track.playlist_tab)
|
||||
|
||||
def save_as_template(self) -> None:
|
||||
"""Save current playlist as template"""
|
||||
@ -1564,16 +1433,20 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
def show_current(self) -> None:
|
||||
"""Scroll to show current track"""
|
||||
|
||||
if self.current_track.playlist_tab != self.active_tab():
|
||||
self.tabPlaylist.setCurrentWidget(self.current_track.playlist_tab)
|
||||
self.tabPlaylist.currentWidget().scroll_current_to_top()
|
||||
return
|
||||
# TODO Reimplement
|
||||
# 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:
|
||||
"""Scroll to show next track"""
|
||||
|
||||
if self.next_track.playlist_tab != self.active_tab():
|
||||
self.tabPlaylist.setCurrentWidget(self.next_track.playlist_tab)
|
||||
self.tabPlaylist.currentWidget().scroll_next_to_top()
|
||||
return
|
||||
# TODO Reimplement
|
||||
# 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]:
|
||||
"""Get name of playlist from user"""
|
||||
@ -1618,15 +1491,16 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.music.stop()
|
||||
|
||||
# Reset playlist_tab colour
|
||||
if self.current_track.playlist_tab:
|
||||
if self.current_track.playlist_tab == self.next_track.playlist_tab:
|
||||
self.set_tab_colour(
|
||||
self.current_track.playlist_tab, QColor(Config.COLOUR_NEXT_TAB)
|
||||
)
|
||||
else:
|
||||
self.set_tab_colour(
|
||||
self.current_track.playlist_tab, QColor(Config.COLOUR_NORMAL_TAB)
|
||||
)
|
||||
# TODO Reimplement
|
||||
# if self.current_track.playlist_tab:
|
||||
# if self.current_track.playlist_tab == self.next_track.playlist_tab:
|
||||
# self.set_tab_colour(
|
||||
# self.current_track.playlist_tab, QColor(Config.COLOUR_NEXT_TAB)
|
||||
# )
|
||||
# else:
|
||||
# self.set_tab_colour(
|
||||
# self.current_track.playlist_tab, QColor(Config.COLOUR_NORMAL_TAB)
|
||||
# )
|
||||
|
||||
# Run end-of-track actions
|
||||
self.end_of_track_actions()
|
||||
@ -1670,11 +1544,11 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
with Session() as session:
|
||||
# Update self.next_track PlaylistTrack structure
|
||||
old_next_track = self.next_track
|
||||
self.next_track = PlaylistTrack()
|
||||
self.next_track = NextTrack()
|
||||
if next_plr_id:
|
||||
next_plr = session.get(PlaylistRows, next_plr_id)
|
||||
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
|
||||
# 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
|
||||
# new one or the current track, it needs its colour reset.
|
||||
if (
|
||||
old_next_track
|
||||
and old_next_track.playlist_tab
|
||||
and old_next_track.playlist_tab
|
||||
not in [self.next_track.playlist_tab, self.current_track.playlist_tab]
|
||||
):
|
||||
self.set_tab_colour(
|
||||
old_next_track.playlist_tab, QColor(Config.COLOUR_NORMAL_TAB)
|
||||
)
|
||||
# If the new next playlist tab isn't the same as the
|
||||
# old one or the current track, it needs its colour set.
|
||||
if old_next_track:
|
||||
old_tab = old_next_track.playlist_tab
|
||||
else:
|
||||
old_tab = None
|
||||
if (
|
||||
self.next_track
|
||||
and self.next_track.playlist_tab
|
||||
and self.next_track.playlist_tab
|
||||
not in [old_tab, self.current_track.playlist_tab]
|
||||
):
|
||||
self.set_tab_colour(
|
||||
self.next_track.playlist_tab, QColor(Config.COLOUR_NEXT_TAB)
|
||||
)
|
||||
return
|
||||
# TODO Reimplement
|
||||
# if (
|
||||
# old_next_track
|
||||
# and old_next_track.playlist_tab
|
||||
# and old_next_track.playlist_tab
|
||||
# not in [self.next_track.playlist_tab, self.current_track.playlist_tab]
|
||||
# ):
|
||||
# self.set_tab_colour(
|
||||
# old_next_track.playlist_tab, QColor(Config.COLOUR_NORMAL_TAB)
|
||||
# )
|
||||
# # If the new next playlist tab isn't the same as the
|
||||
# # old one or the current track, it needs its colour set.
|
||||
# if old_next_track:
|
||||
# old_tab = old_next_track.playlist_tab
|
||||
# else:
|
||||
# old_tab = None
|
||||
# if (
|
||||
# self.next_track
|
||||
# and self.next_track.playlist_tab
|
||||
# and self.next_track.playlist_tab
|
||||
# not in [old_tab, self.current_track.playlist_tab]
|
||||
# ):
|
||||
# self.set_tab_colour(
|
||||
# self.next_track.playlist_tab, QColor(Config.COLOUR_NEXT_TAB)
|
||||
# )
|
||||
|
||||
def tick_10ms(self) -> None:
|
||||
"""
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
from datetime import datetime
|
||||
from enum import auto, Enum
|
||||
from sqlalchemy import bindparam, update
|
||||
from typing import List, Optional, TYPE_CHECKING
|
||||
from typing import List, Optional
|
||||
|
||||
from PyQt6.QtCore import (
|
||||
QAbstractTableModel,
|
||||
@ -15,12 +14,10 @@ from PyQt6.QtGui import (
|
||||
QFont,
|
||||
)
|
||||
|
||||
from classes import CurrentTrack, MusicMusterSignals, NextTrack
|
||||
from config import Config
|
||||
from dbconfig import scoped_session, Session
|
||||
from helpers import (
|
||||
file_is_unreadable,
|
||||
MusicMusterSignals,
|
||||
)
|
||||
from helpers import file_is_unreadable
|
||||
from log import log
|
||||
from models import PlaylistRows, Tracks
|
||||
|
||||
|
||||
@ -37,13 +37,13 @@ from PyQt6.QtWidgets import (
|
||||
|
||||
from dbconfig import Session, scoped_session
|
||||
from dialogs import TrackSelectDialog
|
||||
from classes import MusicMusterSignals
|
||||
from config import Config
|
||||
from helpers import (
|
||||
ask_yes_no,
|
||||
file_is_unreadable,
|
||||
get_relative_date,
|
||||
ms_to_mmss,
|
||||
MusicMusterSignals,
|
||||
open_in_audacity,
|
||||
send_mail,
|
||||
set_track_metadata,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user