WIP: implemented trackmanager, tracks play, clocks work

This commit is contained in:
Keith Edmunds 2024-06-02 11:57:45 +01:00
parent fbcedb6c3b
commit 5278b124ca
5 changed files with 216 additions and 189 deletions

View File

@ -48,6 +48,7 @@ class MusicMusterSignals(QObject):
show_warning_signal = pyqtSignal(str, str) show_warning_signal = pyqtSignal(str, str)
span_cells_signal = pyqtSignal(int, int, int, int, int) span_cells_signal = pyqtSignal(int, int, int, int, int)
status_message_signal = pyqtSignal(str, int) status_message_signal = pyqtSignal(str, int)
track_ended_signal = pyqtSignal()
def __post_init__(self): def __post_init__(self):
super().__init__() super().__init__()

View File

@ -47,7 +47,6 @@ from PyQt6.QtWidgets import (
) )
# Third party imports # Third party imports
# from pygame import mixer
import pipeclient import pipeclient
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
@ -55,11 +54,7 @@ import stackprinter # type: ignore
# App imports # App imports
from classes import ( from classes import (
track_sequence,
FadeCurve,
MusicMusterSignals, MusicMusterSignals,
PlaylistTrack,
PreviewTrackPlayer,
TrackFileData, TrackFileData,
) )
from config import Config from config import Config
@ -68,6 +63,11 @@ from log import log
from models import db, Carts, Playdates, PlaylistRows, Playlists, Settings, Tracks from models import db, Carts, Playdates, PlaylistRows, Playlists, Settings, Tracks
from playlistmodel import PlaylistModel, PlaylistProxyModel from playlistmodel import PlaylistModel, PlaylistProxyModel
from playlists import PlaylistTab from playlists import PlaylistTab
from trackmanager import (
MainTrackManager,
PreviewTrackManager,
track_sequence,
)
from ui import icons_rc # noqa F401 from ui import icons_rc # noqa F401
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
@ -75,7 +75,6 @@ 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 helpers
import music
class CartButton(QPushButton): class CartButton(QPushButton):
@ -231,10 +230,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.timer500: QTimer = QTimer() self.timer500: QTimer = QTimer()
self.timer1000: QTimer = QTimer() self.timer1000: QTimer = QTimer()
self.music: music.Music = music.Music(name=Config.VLC_MAIN_PLAYER_NAME) self.preview_track_player: Optional[PreviewTrackManager] = None
self.preview_track_player: Optional[PreviewTrackPlayer] = None
self.playing: bool = False
self.set_main_window_size() self.set_main_window_size()
self.lblSumPlaytime = QLabel("") self.lblSumPlaytime = QLabel("")
@ -420,6 +416,7 @@ class Window(QMainWindow, Ui_MainWindow):
btn.setEnabled(True) btn.setEnabled(True)
# Setting to position 0 doesn't seem to work # Setting to position 0 doesn't seem to work
btn.player = self.music.VLC.media_player_new(btn.path) btn.player = self.music.VLC.media_player_new(btn.path)
MainTrackManager,
btn.player.audio_set_volume(Config.VLC_VOLUME_DEFAULT) btn.player.audio_set_volume(Config.VLC_VOLUME_DEFAULT)
colour = Config.COLOUR_CART_READY colour = Config.COLOUR_CART_READY
btn.setStyleSheet("background-color: " + colour + ";\n") btn.setStyleSheet("background-color: " + colour + ";\n")
@ -449,7 +446,7 @@ class Window(QMainWindow, Ui_MainWindow):
return return
# Don't allow window to close when a track is playing # Don't allow window to close when a track is playing
if self.playing: if track_sequence.current and track_sequence.current.is_playing():
event.ignore() event.ignore()
helpers.show_warning( helpers.show_warning(
self, "Track playing", "Can't close application while track is playing" self, "Track playing", "Can't close application while track is playing"
@ -604,6 +601,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.signals.next_track_changed_signal.connect(self.update_headers) self.signals.next_track_changed_signal.connect(self.update_headers)
self.signals.status_message_signal.connect(self.show_status_message) self.signals.status_message_signal.connect(self.show_status_message)
self.signals.show_warning_signal.connect(self.show_warning) self.signals.show_warning_signal.connect(self.show_warning)
self.signals.track_ended_signal.connect(self.end_of_track_actions)
self.timer10.timeout.connect(self.tick_10ms) self.timer10.timeout.connect(self.tick_10ms)
self.timer500.timeout.connect(self.tick_500ms) self.timer500.timeout.connect(self.tick_500ms)
@ -724,10 +722,8 @@ class Window(QMainWindow, Ui_MainWindow):
def drop3db(self) -> None: def drop3db(self) -> None:
"""Drop music level by 3db if button checked""" """Drop music level by 3db if button checked"""
if self.btnDrop3db.isChecked(): if track_sequence.current:
self.music.set_volume(Config.VLC_VOLUME_DROP3db, set_default=False) track_sequence.current.drop3db(self.btnDrop3db.isChecked())
else:
self.music.set_volume(Config.VLC_VOLUME_DEFAULT, set_default=False)
def enable_escape(self, enabled: bool) -> None: def enable_escape(self, enabled: bool) -> None:
""" """
@ -741,6 +737,38 @@ class Window(QMainWindow, Ui_MainWindow):
self.action_Clear_selection.setEnabled(enabled) self.action_Clear_selection.setEnabled(enabled)
def end_of_track_actions(self) -> None:
"""
Actions required:
- Reset track_sequence objects
- Tell model track has finished
- Reset clocks
- Update headers
- Enable controls
"""
# Reset track_sequence objects
track_sequence.previous = track_sequence.current
track_sequence.current = None
# Tell model previous track has finished
self.active_proxy_model().previous_track_ended()
# Reset clocks
self.frame_fade.setStyleSheet("")
self.frame_silent.setStyleSheet("")
self.label_elapsed_timer.setText("00:00 / 00:00")
self.label_fade_timer.setText("00:00")
self.label_silent_timer.setText("00:00")
# Update headers
self.update_headers()
# Enable controls
self.catch_return_key = False
self.show_status_message("Play controls: Enabled", 0)
def export_playlist_tab(self) -> None: def export_playlist_tab(self) -> None:
"""Export the current playlist to an m3u file""" """Export the current playlist to an m3u file"""
@ -788,7 +816,8 @@ class Window(QMainWindow, Ui_MainWindow):
def fade(self) -> None: def fade(self) -> None:
"""Fade currently playing track""" """Fade currently playing track"""
self.stop_playing(fade=True) if track_sequence.current:
track_sequence.current.fade()
def hide_played(self): def hide_played(self):
"""Toggle hide played tracks""" """Toggle hide played tracks"""
@ -1152,8 +1181,8 @@ class Window(QMainWindow, Ui_MainWindow):
# Suppress inadvertent double press # Suppress inadvertent double press
if ( if (
track_sequence.current track_sequence.current
and track_sequence.current.track_player.start_time and track_sequence.current.start_time
and track_sequence.current.track_player.start_time and track_sequence.current.start_time
+ dt.timedelta(milliseconds=Config.RETURN_KEY_DEBOUNCE_MS) + dt.timedelta(milliseconds=Config.RETURN_KEY_DEBOUNCE_MS)
> dt.datetime.now() > dt.datetime.now()
): ):
@ -1161,17 +1190,11 @@ class Window(QMainWindow, Ui_MainWindow):
# If return is pressed during first PLAY_NEXT_GUARD_MS then # If return is pressed during first PLAY_NEXT_GUARD_MS then
# default to NOT playing the next track, else default to # default to NOT playing the next track, else default to
# playing it. # playing it.
default_yes: bool = ( default_yes: bool = track_sequence.current.start_time is not None and (
track_sequence.current.track_player.start_time is not None (dt.datetime.now() - track_sequence.current.start_time).total_seconds()
and (
(
dt.datetime.now()
- track_sequence.current.track_player.start_time
).total_seconds()
* 1000 * 1000
> Config.PLAY_NEXT_GUARD_MS > Config.PLAY_NEXT_GUARD_MS
) )
)
if not helpers.ask_yes_no( if not helpers.ask_yes_no(
"Track playing", "Track playing",
"Really play next track now?", "Really play next track now?",
@ -1194,14 +1217,15 @@ class Window(QMainWindow, Ui_MainWindow):
# seconds of playback. Re-enabled tick_1000ms # seconds of playback. Re-enabled tick_1000ms
self.timer10.stop() self.timer10.stop()
self.show_status_message("10ms timer disabled", 0) log.debug("10ms timer disabled", 0)
# If there's currently a track playing, fade it. # If there's currently a track playing, fade it.
self.stop_playing(fade=True) if track_sequence.current:
track_sequence.current.fade()
# Move next track to current track. # Move next track to current track.
# stop_playing() above has called end_of_track_actions() # end_of_track_actions() will have saved current track to
# which will have populated self.previous_track # previous_track
track_sequence.current = track_sequence.next track_sequence.current = track_sequence.next
# Clear next track # Clear next track
@ -1213,7 +1237,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.btnDrop3db.setChecked(False) self.btnDrop3db.setChecked(False)
# Play (new) current track # Play (new) current track
track_sequence.current.track_player.play(position) track_sequence.current.play(position)
# Disable play next controls # Disable play next controls
self.catch_return_key = True self.catch_return_key = True
@ -1251,7 +1275,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.btnPreview.setChecked(False) self.btnPreview.setChecked(False)
return return
with db.Session() as session: with db.Session() as session:
self.preview_track_player = PreviewTrackPlayer(session, track_id) self.preview_track_player = PreviewTrackManager(session, track_id)
self.preview_track_player.play() self.preview_track_player.play()
else: else:
@ -1586,7 +1610,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.statusbar.showMessage(message, timing) self.statusbar.showMessage(message, timing)
def show_track(self, playlist_track: PlaylistTrack) -> None: def show_track(self, playlist_track: MainTrackManager) -> None:
"""Scroll to show track in plt""" """Scroll to show track in plt"""
# Switch to the correct tab # Switch to the correct tab
@ -1604,7 +1628,9 @@ class Window(QMainWindow, Ui_MainWindow):
display_row = ( display_row = (
self.active_proxy_model() self.active_proxy_model()
.mapFromSource( .mapFromSource(
self.active_proxy_model().source_model.index(playlist_track.row_number, 0) self.active_proxy_model().source_model.index(
playlist_track.row_number, 0
)
) )
.row() .row()
) )
@ -1640,66 +1666,8 @@ class Window(QMainWindow, Ui_MainWindow):
def stop(self) -> None: def stop(self) -> None:
"""Stop playing immediately""" """Stop playing immediately"""
self.stop_playing(fade=False) if track_sequence.current:
track_sequence.current.stop()
def stop_playing(self, fade: bool = True) -> None:
"""
Stop playing current track
Actions required:
- Set flag to say we're not playing a track
- Return if not playing
- Stop/fade track
- Reset playlist_tab colour
- Tell playlist_tab track has finished
- Reset PlaylistTrack objects
- Reset clocks
- Reset fade graph
- Update headers
- Enable controls
"""
# Set flag to say we're not playing a track so that timer ticks
# don't see player=None and kick off end-of-track actions
if self.playing:
self.playing = False
else:
# Return if not playing
log.info("stop_playing() called but not playing")
return
# Stop/fade track
track_sequence.now.resume_marker = self.music.get_position()
if fade:
self.music.fade()
else:
self.music.stop()
# Reset fade graph
if track_sequence.now.fade_graph:
track_sequence.now.fade_graph.clear()
# Reset track_sequence objects
if track_sequence.now.track_id:
track_sequence.previous = track_sequence.now
track_sequence.now = PlaylistTrack()
# Tell model previous track has finished
self.active_proxy_model().previous_track_ended()
# Reset clocks
self.frame_fade.setStyleSheet("")
self.frame_silent.setStyleSheet("")
self.label_elapsed_timer.setText("00:00 / 00:00")
self.label_fade_timer.setText("00:00")
self.label_silent_timer.setText("00:00")
# Update headers
self.update_headers()
# Enable controls
self.catch_return_key = False
self.show_status_message("Play controls: Enabled", 0)
def tab_change(self): def tab_change(self):
"""Called when active tab changed""" """Called when active tab changed"""
@ -1743,11 +1711,15 @@ class Window(QMainWindow, Ui_MainWindow):
Called every 100ms Called every 100ms
""" """
if track_sequence.current:
track_sequence.current.check_for_end_of_track()
return
# Update intro counter if applicable and, if updated, return # Update intro counter if applicable and, if updated, return
# because playing an intro takes precedence over timing a # because playing an intro takes precedence over timing a
# preview. # preview.
if self.music.is_playing() and track_sequence.now.intro: if self.music.is_playing() and track_sequence.current.intro:
remaining_ms = track_sequence.now.intro - self.music.get_playtime() remaining_ms = track_sequence.current.intro - self.music.get_playtime()
if remaining_ms > 0: if remaining_ms > 0:
self.label_intro_timer.setText(f"{remaining_ms / 1000:.1f}") self.label_intro_timer.setText(f"{remaining_ms / 1000:.1f}")
if remaining_ms <= Config.INTRO_SECONDS_WARNING_MS: if remaining_ms <= Config.INTRO_SECONDS_WARNING_MS:
@ -1784,28 +1756,34 @@ class Window(QMainWindow, Ui_MainWindow):
# Only update play clocks once a second so that their updates # Only update play clocks once a second so that their updates
# are synchronised (otherwise it looks odd) # are synchronised (otherwise it looks odd)
if not self.playing: self.update_clocks()
return
def update_clocks(self) -> None:
"""
Update track clocks.
"""
# If track is playing, update track clocks time and colours # If track is playing, update track clocks time and colours
if self.music.player and self.music.player.is_playing(): if track_sequence.current and track_sequence.current.is_playing():
playtime = self.music.get_playtime() # see play_next() and issue #223.
time_to_fade = track_sequence.now.fade_at - playtime # TODO: find a better way of handling this
time_to_silence = track_sequence.now.silence_at - playtime if (
track_sequence.current.time_playing() > 10000
# see play_next() and issue #223 and not self.timer10.isActive()
if playtime > 10000 and not self.timer10.isActive(): ):
self.timer10.start(10) self.timer10.start(10)
self.show_status_message("10ms timer enabled", 0) log.debug("10ms timer enabled")
# Elapsed time # Elapsed time
self.label_elapsed_timer.setText( self.label_elapsed_timer.setText(
helpers.ms_to_mmss(playtime) helpers.ms_to_mmss(track_sequence.current.time_playing())
+ " / " + " / "
+ helpers.ms_to_mmss(track_sequence.now.duration) + helpers.ms_to_mmss(track_sequence.current.duration)
) )
# Time to fade # Time to fade
time_to_fade = track_sequence.current.time_to_fade()
time_to_silence = track_sequence.current.time_to_silence()
self.label_fade_timer.setText(helpers.ms_to_mmss(time_to_fade)) self.label_fade_timer.setText(helpers.ms_to_mmss(time_to_fade))
# If silent in the next 5 seconds, put warning colour on # If silent in the next 5 seconds, put warning colour on
@ -1816,11 +1794,13 @@ class Window(QMainWindow, Ui_MainWindow):
self.frame_silent.setStyleSheet(css_silence) self.frame_silent.setStyleSheet(css_silence)
self.catch_return_key = False self.catch_return_key = False
self.show_status_message("Play controls: Enabled", 0) self.show_status_message("Play controls: Enabled", 0)
# Set warning colour on time to silence box when fade starts # Set warning colour on time to silence box when fade starts
elif time_to_fade <= 500: elif time_to_fade <= 500:
css_fade = f"background: {Config.COLOUR_WARNING_TIMER}" css_fade = f"background: {Config.COLOUR_WARNING_TIMER}"
if self.frame_silent.styleSheet() != css_fade: if self.frame_silent.styleSheet() != css_fade:
self.frame_silent.setStyleSheet(css_fade) self.frame_silent.setStyleSheet(css_fade)
# Five seconds before fade starts, set warning colour on # Five seconds before fade starts, set warning colour on
# time to silence box and enable play controls # time to silence box and enable play controls
elif time_to_fade <= Config.WARNING_MS_BEFORE_FADE: elif time_to_fade <= Config.WARNING_MS_BEFORE_FADE:
@ -1835,38 +1815,30 @@ class Window(QMainWindow, Ui_MainWindow):
self.label_silent_timer.setText(helpers.ms_to_mmss(time_to_silence)) self.label_silent_timer.setText(helpers.ms_to_mmss(time_to_silence))
# Autoplay next track
# if time_to_silence <= 1500:
# self.play_next()
else:
if self.playing:
self.stop_playing()
def update_headers(self) -> None: def update_headers(self) -> None:
""" """
Update last / current / next track headers Update last / current / next track headers
""" """
if track_sequence.previous: if track_sequence.previous:
player = track_sequence.previous.track_player self.hdrPreviousTrack.setText(
self.hdrPreviousTrack.setText(f"{player.title} - {player.artist}") f"{track_sequence.previous.title} - {track_sequence.previous.artist}"
)
else: else:
self.hdrPreviousTrack.setText("") self.hdrPreviousTrack.setText("")
if track_sequence.current: if track_sequence.current:
player = track_sequence.current.track_player
self.hdrCurrentTrack.setText( self.hdrCurrentTrack.setText(
f"{player.title.replace('&', '&&')} - " f"{track_sequence.current.title.replace('&', '&&')} - "
f"{player.artist.replace('&', '&&')}" f"{track_sequence.current.artist.replace('&', '&&')}"
) )
else: else:
self.hdrCurrentTrack.setText("") self.hdrCurrentTrack.setText("")
if track_sequence.next: if track_sequence.next:
player = track_sequence.next.track_player
self.hdrNextTrack.setText( self.hdrNextTrack.setText(
f"{player.title.replace('&', '&&')} - " f"{track_sequence.next.title.replace('&', '&&')} - "
f"{player.artist.replace('&', '&&')}" f"{track_sequence.next.artist.replace('&', '&&')}"
) )
else: else:
self.hdrNextTrack.setText("") self.hdrNextTrack.setText("")

View File

@ -30,7 +30,7 @@ import obswebsocket # type: ignore
# import snoop # type: ignore # import snoop # type: ignore
# App imports # App imports
from classes import Col, track_sequence, MusicMusterSignals, PlaylistTrack from classes import Col, MusicMusterSignals
from config import Config from config import Config
from helpers import ( from helpers import (
file_is_unreadable, file_is_unreadable,
@ -41,6 +41,10 @@ from helpers import (
) )
from log import log from log import log
from models import db, NoteColours, Playdates, PlaylistRows, Tracks from models import db, NoteColours, Playdates, PlaylistRows, Tracks
from trackmanager import (
MainTrackManager,
track_sequence,
)
HEADER_NOTES_COLUMN = 1 HEADER_NOTES_COLUMN = 1
@ -684,7 +688,7 @@ class PlaylistModel(QAbstractTableModel):
end_time_str = "" end_time_str = ""
if ( if (
track_sequence.current track_sequence.current
and track_sequence.current.track_player.end_time and track_sequence.current.end_time
and ( and (
row_number row_number
< track_sequence.current.row_number < track_sequence.current.row_number
@ -692,7 +696,7 @@ class PlaylistModel(QAbstractTableModel):
) )
): ):
section_end_time = ( section_end_time = (
track_sequence.current.track_player.end_time track_sequence.current.end_time
+ dt.timedelta(milliseconds=duration) + dt.timedelta(milliseconds=duration)
) )
end_time_str = ( end_time_str = (
@ -1257,8 +1261,9 @@ class PlaylistModel(QAbstractTableModel):
) )
return return
with db.Session() as session:
try: try:
track_sequence.next = PlaylistTrack(prd.plrid) track_sequence.next = MainTrackManager(session, prd.plrid)
self.invalidate_row(row_number) self.invalidate_row(row_number)
except ValueError as e: except ValueError as e:
log.error(f"Error creating PlaylistTrack({prd=}): ({str(e)})") log.error(f"Error creating PlaylistTrack({prd=}): ({str(e)})")
@ -1555,9 +1560,9 @@ class PlaylistProxyModel(QSortFilterProxyModel):
and previous_plr.playlist_id and previous_plr.playlist_id
== self.source_model.playlist_id == self.source_model.playlist_id
): ):
if track_sequence.current.track_player.start_time: if track_sequence.current.start_time:
if dt.datetime.now() > ( if dt.datetime.now() > (
track_sequence.current.track_player.start_time track_sequence.current.start_time
+ dt.timedelta( + dt.timedelta(
milliseconds=Config.HIDE_AFTER_PLAYING_OFFSET milliseconds=Config.HIDE_AFTER_PLAYING_OFFSET
) )

View File

@ -35,7 +35,7 @@ from PyQt6.QtWidgets import (
# Third party imports # Third party imports
# App imports # App imports
from classes import Col, MusicMusterSignals, track_sequence from classes import Col, MusicMusterSignals
from config import Config from config import Config
from dialogs import TrackSelectDialog from dialogs import TrackSelectDialog
from helpers import ( from helpers import (
@ -47,6 +47,7 @@ from helpers import (
from log import log from log import log
from models import db, Settings from models import db, Settings
from playlistmodel import PlaylistModel, PlaylistProxyModel from playlistmodel import PlaylistModel, PlaylistProxyModel
from trackmanager import track_sequence
if TYPE_CHECKING: if TYPE_CHECKING:
from musicmuster import Window from musicmuster import Window

View File

@ -21,6 +21,7 @@ from PyQt6.QtCore import (
) )
# App imports # App imports
from classes import MusicMusterSignals
from config import Config from config import Config
from log import log from log import log
from models import db, PlaylistRows, Tracks from models import db, PlaylistRows, Tracks
@ -185,6 +186,25 @@ class _Music:
else: else:
self.start_dt -= dt.timedelta(milliseconds=ms) self.start_dt -= dt.timedelta(milliseconds=ms)
def stop(self) -> None:
"""Immediately stop playing"""
log.debug(f"Music[{self.name}].stop()")
self.start_dt = None
if not self.player:
return
p = self.player
self.player = None
self.start_dt = None
with lock:
p.stop()
p.release()
p = None
def fade(self, fade_seconds: int) -> None: def fade(self, fade_seconds: int) -> None:
""" """
Fade the currently playing track. Fade the currently playing track.
@ -193,8 +213,6 @@ class _Music:
to hold up the UI during the fade. to hold up the UI during the fade.
""" """
log.info(f"Music[{self.name}].stop()")
if not self.player: if not self.player:
return return
@ -202,7 +220,7 @@ class _Music:
return return
if fade_seconds <= 0: if fade_seconds <= 0:
self._stop() self.stop()
return return
# Take a copy of current player to allow another track to be # Take a copy of current player to allow another track to be
@ -249,14 +267,11 @@ class _Music:
# player.is_playing() returning True, so assume playing if less # player.is_playing() returning True, so assume playing if less
# than Config.PLAY_SETTLE microseconds have passed since # than Config.PLAY_SETTLE microseconds have passed since
# starting play. # starting play.
return ( return self.start_dt is not None and (
self.start_dt is not None
and (
self.player.is_playing() self.player.is_playing()
or (dt.datetime.now() - self.start_dt) or (dt.datetime.now() - self.start_dt)
< dt.timedelta(microseconds=Config.PLAY_SETTLE) < dt.timedelta(microseconds=Config.PLAY_SETTLE)
) )
)
def move_back(self, ms: int) -> None: def move_back(self, ms: int) -> None:
""" """
@ -342,25 +357,6 @@ class _Music:
log.debug(f"Reset from {volume=}") log.debug(f"Reset from {volume=}")
sleep(0.1) sleep(0.1)
def _stop(self) -> None:
"""Immediately stop playing"""
log.info(f"Music[{self.name}].stop()")
self.start_dt = None
if not self.player:
return
p = self.player
self.player = None
self.start_dt = None
with lock:
p.stop()
p.release()
p = None
class _TrackManager: class _TrackManager:
""" """
@ -397,9 +393,10 @@ class _TrackManager:
self.resume_marker: Optional[float] self.resume_marker: Optional[float]
self.start_time: Optional[dt.datetime] = None self.start_time: Optional[dt.datetime] = None
self.player = _Music(name=player_name) self.signals = MusicMusterSignals()
# Initialise player # Initialise player
self.track_player = MainTrackManager(session=session, track_id=self.track_id) self.player = _Music(name=player_name)
# Initialise and add FadeCurve in a thread as it's slow # Initialise and add FadeCurve in a thread as it's slow
self.fadecurve_thread = QThread() self.fadecurve_thread = QThread()
@ -416,23 +413,50 @@ class _TrackManager:
self.fadecurve_thread.finished.connect(self.fadecurve_thread.deleteLater) self.fadecurve_thread.finished.connect(self.fadecurve_thread.deleteLater)
self.fadecurve_thread.start() self.fadecurve_thread.start()
def check_for_end_of_track(self) -> None:
"""
Check whether track has ended. If so, emit track_ended_signal
"""
if self.start_time is None:
return
if not self.player.is_playing():
self.start_time = None
self.signals.track_ended_signal.emit()
def drop3db(self, enable: bool) -> None:
"""
If enable is true, drop output by 3db else restore to full volume
"""
if enable:
self.player.set_volume(volume=Config.VLC_VOLUME_DROP3db, set_default=False)
else:
self.player.set_volume(volume=Config.VLC_VOLUME_DEFAULT, set_default=False)
def fade(self, fade_seconds: int = Config.FADEOUT_SECONDS) -> None: def fade(self, fade_seconds: int = Config.FADEOUT_SECONDS) -> None:
"""Fade music""" """Fade music"""
self.player.fade(fade_seconds) self.player.fade(fade_seconds)
@property
def is_playing(self) -> bool: def is_playing(self) -> bool:
return self.track_player.is_playing() """
Return True if we're currently playing else False
"""
if self.start_time is None:
return False
return self.player.is_playing()
def play(self, position: Optional[float] = None) -> None: def play(self, position: Optional[float] = None) -> None:
"""Play track""" """Play track"""
now = dt.datetime.now()
self.start_time = now
self.player.play(self.path, position) self.player.play(self.path, position)
now = dt.datetime.now()
self.start_time = now
self.end_time = self.start_time + dt.timedelta(milliseconds=self.duration) self.end_time = self.start_time + dt.timedelta(milliseconds=self.duration)
# Calculate time fade_graph should start updating # Calculate time fade_graph should start updating
@ -444,7 +468,7 @@ class _TrackManager:
milliseconds=update_graph_at_ms milliseconds=update_graph_at_ms
) )
def stop_playing(self, fade_seconds: int = 0) -> None: def stop(self, fade_seconds: int = 0) -> None:
""" """
Stop this track playing Stop this track playing
""" """
@ -456,26 +480,47 @@ class _TrackManager:
if self.fade_graph: if self.fade_graph:
self.fade_graph.clear() self.fade_graph.clear()
def time_playing(self) -> int:
"""
Return time track has been playing in milliseconds, zero if not playing
"""
if self.start_time is None:
return 0
return self.player.get_playtime()
def time_to_fade(self) -> int: def time_to_fade(self) -> int:
""" """
Return milliseconds until fade time. Return zero if we're not playing. Return milliseconds until fade time. Return zero if we're not playing.
""" """
if not self.player.is_playing: if self.start_time is None:
return 0 return 0
return self.fade_at - self.time_playing()
def time_to_silence(self) -> int:
"""
Return milliseconds until silent. Return zero if we're not playing.
"""
if self.start_time is None:
return 0
return self.silence_at - self.time_playing()
class MainTrackManager(_TrackManager): class MainTrackManager(_TrackManager):
""" """
Manage playing tracks from the playlist with associated data Manage playing tracks from the playlist with associated data
""" """
def __init__(self, plr_id: int) -> None: def __init__(self, session: db.Session, plr_id: int) -> None:
""" """
Set up manager for playlist tracks Set up manager for playlist tracks
""" """
with db.Session() as session:
# Ensure we have a track # Ensure we have a track
plr = session.get(PlaylistRows, plr_id) plr = session.get(PlaylistRows, plr_id)
if not plr: if not plr:
@ -484,7 +529,9 @@ class MainTrackManager(_TrackManager):
self.track_id: int = plr.track_id self.track_id: int = plr.track_id
super().__init__( super().__init__(
session=session, player_name=Config.VLC_MAIN_PLAYER_NAME, track_id=self.track_id session=session,
player_name=Config.VLC_MAIN_PLAYER_NAME,
track_id=self.track_id,
) )
# Save non-track plr info # Save non-track plr info
@ -504,8 +551,9 @@ class PreviewTrackManager(_TrackManager):
Manage previewing tracks Manage previewing tracks
""" """
def __init__(self, track_id: int) -> None: def __init__(self, session: db.Session, track_id: int) -> None:
super().__init__( super().__init__(
session=session,
player_name=Config.VLC_PREVIEW_PLAYER_NAME, player_name=Config.VLC_PREVIEW_PLAYER_NAME,
track_id=track_id, track_id=track_id,
) )