WIP: implemented trackmanager, tracks play, clocks work
This commit is contained in:
parent
fbcedb6c3b
commit
5278b124ca
@ -48,6 +48,7 @@ class MusicMusterSignals(QObject):
|
||||
show_warning_signal = pyqtSignal(str, str)
|
||||
span_cells_signal = pyqtSignal(int, int, int, int, int)
|
||||
status_message_signal = pyqtSignal(str, int)
|
||||
track_ended_signal = pyqtSignal()
|
||||
|
||||
def __post_init__(self):
|
||||
super().__init__()
|
||||
|
||||
@ -47,7 +47,6 @@ from PyQt6.QtWidgets import (
|
||||
)
|
||||
|
||||
# Third party imports
|
||||
# from pygame import mixer
|
||||
import pipeclient
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm.session import Session
|
||||
@ -55,11 +54,7 @@ import stackprinter # type: ignore
|
||||
|
||||
# App imports
|
||||
from classes import (
|
||||
track_sequence,
|
||||
FadeCurve,
|
||||
MusicMusterSignals,
|
||||
PlaylistTrack,
|
||||
PreviewTrackPlayer,
|
||||
TrackFileData,
|
||||
)
|
||||
from config import Config
|
||||
@ -68,6 +63,11 @@ from log import log
|
||||
from models import db, Carts, Playdates, PlaylistRows, Playlists, Settings, Tracks
|
||||
from playlistmodel import PlaylistModel, PlaylistProxyModel
|
||||
from playlists import PlaylistTab
|
||||
from trackmanager import (
|
||||
MainTrackManager,
|
||||
PreviewTrackManager,
|
||||
track_sequence,
|
||||
)
|
||||
from ui import icons_rc # noqa F401
|
||||
from ui.dlg_cart_ui import Ui_DialogCartEdit # 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 utilities import check_db, update_bitrates
|
||||
import helpers
|
||||
import music
|
||||
|
||||
|
||||
class CartButton(QPushButton):
|
||||
@ -231,10 +230,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.timer500: QTimer = QTimer()
|
||||
self.timer1000: QTimer = QTimer()
|
||||
|
||||
self.music: music.Music = music.Music(name=Config.VLC_MAIN_PLAYER_NAME)
|
||||
self.preview_track_player: Optional[PreviewTrackPlayer] = None
|
||||
|
||||
self.playing: bool = False
|
||||
self.preview_track_player: Optional[PreviewTrackManager] = None
|
||||
|
||||
self.set_main_window_size()
|
||||
self.lblSumPlaytime = QLabel("")
|
||||
@ -420,6 +416,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
btn.setEnabled(True)
|
||||
# Setting to position 0 doesn't seem to work
|
||||
btn.player = self.music.VLC.media_player_new(btn.path)
|
||||
MainTrackManager,
|
||||
btn.player.audio_set_volume(Config.VLC_VOLUME_DEFAULT)
|
||||
colour = Config.COLOUR_CART_READY
|
||||
btn.setStyleSheet("background-color: " + colour + ";\n")
|
||||
@ -449,7 +446,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
return
|
||||
|
||||
# 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()
|
||||
helpers.show_warning(
|
||||
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.status_message_signal.connect(self.show_status_message)
|
||||
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.timer500.timeout.connect(self.tick_500ms)
|
||||
@ -724,10 +722,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
def drop3db(self) -> None:
|
||||
"""Drop music level by 3db if button checked"""
|
||||
|
||||
if self.btnDrop3db.isChecked():
|
||||
self.music.set_volume(Config.VLC_VOLUME_DROP3db, set_default=False)
|
||||
else:
|
||||
self.music.set_volume(Config.VLC_VOLUME_DEFAULT, set_default=False)
|
||||
if track_sequence.current:
|
||||
track_sequence.current.drop3db(self.btnDrop3db.isChecked())
|
||||
|
||||
def enable_escape(self, enabled: bool) -> None:
|
||||
"""
|
||||
@ -741,6 +737,38 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
|
||||
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:
|
||||
"""Export the current playlist to an m3u file"""
|
||||
|
||||
@ -788,7 +816,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
def fade(self) -> None:
|
||||
"""Fade currently playing track"""
|
||||
|
||||
self.stop_playing(fade=True)
|
||||
if track_sequence.current:
|
||||
track_sequence.current.fade()
|
||||
|
||||
def hide_played(self):
|
||||
"""Toggle hide played tracks"""
|
||||
@ -1152,8 +1181,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
# Suppress inadvertent double press
|
||||
if (
|
||||
track_sequence.current
|
||||
and track_sequence.current.track_player.start_time
|
||||
and track_sequence.current.track_player.start_time
|
||||
and track_sequence.current.start_time
|
||||
and track_sequence.current.start_time
|
||||
+ dt.timedelta(milliseconds=Config.RETURN_KEY_DEBOUNCE_MS)
|
||||
> dt.datetime.now()
|
||||
):
|
||||
@ -1161,16 +1190,10 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
# If return is pressed during first PLAY_NEXT_GUARD_MS then
|
||||
# default to NOT playing the next track, else default to
|
||||
# playing it.
|
||||
default_yes: bool = (
|
||||
track_sequence.current.track_player.start_time is not None
|
||||
and (
|
||||
(
|
||||
dt.datetime.now()
|
||||
- track_sequence.current.track_player.start_time
|
||||
).total_seconds()
|
||||
* 1000
|
||||
> Config.PLAY_NEXT_GUARD_MS
|
||||
)
|
||||
default_yes: bool = track_sequence.current.start_time is not None and (
|
||||
(dt.datetime.now() - track_sequence.current.start_time).total_seconds()
|
||||
* 1000
|
||||
> Config.PLAY_NEXT_GUARD_MS
|
||||
)
|
||||
if not helpers.ask_yes_no(
|
||||
"Track playing",
|
||||
@ -1194,14 +1217,15 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
# seconds of playback. Re-enabled tick_1000ms
|
||||
|
||||
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.
|
||||
self.stop_playing(fade=True)
|
||||
if track_sequence.current:
|
||||
track_sequence.current.fade()
|
||||
|
||||
# Move next track to current track.
|
||||
# stop_playing() above has called end_of_track_actions()
|
||||
# which will have populated self.previous_track
|
||||
# end_of_track_actions() will have saved current track to
|
||||
# previous_track
|
||||
track_sequence.current = track_sequence.next
|
||||
|
||||
# Clear next track
|
||||
@ -1213,7 +1237,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.btnDrop3db.setChecked(False)
|
||||
|
||||
# Play (new) current track
|
||||
track_sequence.current.track_player.play(position)
|
||||
track_sequence.current.play(position)
|
||||
|
||||
# Disable play next controls
|
||||
self.catch_return_key = True
|
||||
@ -1251,7 +1275,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.btnPreview.setChecked(False)
|
||||
return
|
||||
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()
|
||||
|
||||
else:
|
||||
@ -1586,7 +1610,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
|
||||
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"""
|
||||
|
||||
# Switch to the correct tab
|
||||
@ -1604,7 +1628,9 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
display_row = (
|
||||
self.active_proxy_model()
|
||||
.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()
|
||||
)
|
||||
@ -1640,66 +1666,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
def stop(self) -> None:
|
||||
"""Stop playing immediately"""
|
||||
|
||||
self.stop_playing(fade=False)
|
||||
|
||||
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)
|
||||
if track_sequence.current:
|
||||
track_sequence.current.stop()
|
||||
|
||||
def tab_change(self):
|
||||
"""Called when active tab changed"""
|
||||
@ -1743,11 +1711,15 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
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
|
||||
# because playing an intro takes precedence over timing a
|
||||
# preview.
|
||||
if self.music.is_playing() and track_sequence.now.intro:
|
||||
remaining_ms = track_sequence.now.intro - self.music.get_playtime()
|
||||
if self.music.is_playing() and track_sequence.current.intro:
|
||||
remaining_ms = track_sequence.current.intro - self.music.get_playtime()
|
||||
if remaining_ms > 0:
|
||||
self.label_intro_timer.setText(f"{remaining_ms / 1000:.1f}")
|
||||
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
|
||||
# are synchronised (otherwise it looks odd)
|
||||
|
||||
if not self.playing:
|
||||
return
|
||||
self.update_clocks()
|
||||
|
||||
def update_clocks(self) -> None:
|
||||
"""
|
||||
Update track clocks.
|
||||
"""
|
||||
|
||||
# If track is playing, update track clocks time and colours
|
||||
if self.music.player and self.music.player.is_playing():
|
||||
playtime = self.music.get_playtime()
|
||||
time_to_fade = track_sequence.now.fade_at - playtime
|
||||
time_to_silence = track_sequence.now.silence_at - playtime
|
||||
|
||||
# see play_next() and issue #223
|
||||
if playtime > 10000 and not self.timer10.isActive():
|
||||
if track_sequence.current and track_sequence.current.is_playing():
|
||||
# see play_next() and issue #223.
|
||||
# TODO: find a better way of handling this
|
||||
if (
|
||||
track_sequence.current.time_playing() > 10000
|
||||
and not self.timer10.isActive()
|
||||
):
|
||||
self.timer10.start(10)
|
||||
self.show_status_message("10ms timer enabled", 0)
|
||||
log.debug("10ms timer enabled")
|
||||
|
||||
# Elapsed time
|
||||
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 = 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))
|
||||
|
||||
# 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.catch_return_key = False
|
||||
self.show_status_message("Play controls: Enabled", 0)
|
||||
|
||||
# Set warning colour on time to silence box when fade starts
|
||||
elif time_to_fade <= 500:
|
||||
css_fade = f"background: {Config.COLOUR_WARNING_TIMER}"
|
||||
if self.frame_silent.styleSheet() != css_fade:
|
||||
self.frame_silent.setStyleSheet(css_fade)
|
||||
|
||||
# Five seconds before fade starts, set warning colour on
|
||||
# time to silence box and enable play controls
|
||||
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))
|
||||
|
||||
# Autoplay next track
|
||||
# if time_to_silence <= 1500:
|
||||
# self.play_next()
|
||||
else:
|
||||
if self.playing:
|
||||
self.stop_playing()
|
||||
|
||||
def update_headers(self) -> None:
|
||||
"""
|
||||
Update last / current / next track headers
|
||||
"""
|
||||
|
||||
if track_sequence.previous:
|
||||
player = track_sequence.previous.track_player
|
||||
self.hdrPreviousTrack.setText(f"{player.title} - {player.artist}")
|
||||
self.hdrPreviousTrack.setText(
|
||||
f"{track_sequence.previous.title} - {track_sequence.previous.artist}"
|
||||
)
|
||||
else:
|
||||
self.hdrPreviousTrack.setText("")
|
||||
|
||||
if track_sequence.current:
|
||||
player = track_sequence.current.track_player
|
||||
self.hdrCurrentTrack.setText(
|
||||
f"{player.title.replace('&', '&&')} - "
|
||||
f"{player.artist.replace('&', '&&')}"
|
||||
f"{track_sequence.current.title.replace('&', '&&')} - "
|
||||
f"{track_sequence.current.artist.replace('&', '&&')}"
|
||||
)
|
||||
else:
|
||||
self.hdrCurrentTrack.setText("")
|
||||
|
||||
if track_sequence.next:
|
||||
player = track_sequence.next.track_player
|
||||
self.hdrNextTrack.setText(
|
||||
f"{player.title.replace('&', '&&')} - "
|
||||
f"{player.artist.replace('&', '&&')}"
|
||||
f"{track_sequence.next.title.replace('&', '&&')} - "
|
||||
f"{track_sequence.next.artist.replace('&', '&&')}"
|
||||
)
|
||||
else:
|
||||
self.hdrNextTrack.setText("")
|
||||
|
||||
@ -30,7 +30,7 @@ import obswebsocket # type: ignore
|
||||
# import snoop # type: ignore
|
||||
|
||||
# App imports
|
||||
from classes import Col, track_sequence, MusicMusterSignals, PlaylistTrack
|
||||
from classes import Col, MusicMusterSignals
|
||||
from config import Config
|
||||
from helpers import (
|
||||
file_is_unreadable,
|
||||
@ -41,6 +41,10 @@ from helpers import (
|
||||
)
|
||||
from log import log
|
||||
from models import db, NoteColours, Playdates, PlaylistRows, Tracks
|
||||
from trackmanager import (
|
||||
MainTrackManager,
|
||||
track_sequence,
|
||||
)
|
||||
|
||||
|
||||
HEADER_NOTES_COLUMN = 1
|
||||
@ -684,7 +688,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
end_time_str = ""
|
||||
if (
|
||||
track_sequence.current
|
||||
and track_sequence.current.track_player.end_time
|
||||
and track_sequence.current.end_time
|
||||
and (
|
||||
row_number
|
||||
< track_sequence.current.row_number
|
||||
@ -692,7 +696,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
)
|
||||
):
|
||||
section_end_time = (
|
||||
track_sequence.current.track_player.end_time
|
||||
track_sequence.current.end_time
|
||||
+ dt.timedelta(milliseconds=duration)
|
||||
)
|
||||
end_time_str = (
|
||||
@ -1257,12 +1261,13 @@ class PlaylistModel(QAbstractTableModel):
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
track_sequence.next = PlaylistTrack(prd.plrid)
|
||||
self.invalidate_row(row_number)
|
||||
except ValueError as e:
|
||||
log.error(f"Error creating PlaylistTrack({prd=}): ({str(e)})")
|
||||
return
|
||||
with db.Session() as session:
|
||||
try:
|
||||
track_sequence.next = MainTrackManager(session, prd.plrid)
|
||||
self.invalidate_row(row_number)
|
||||
except ValueError as e:
|
||||
log.error(f"Error creating PlaylistTrack({prd=}): ({str(e)})")
|
||||
return
|
||||
|
||||
self.signals.search_wikipedia_signal.emit(
|
||||
self.playlist_rows[row_number].title
|
||||
@ -1555,9 +1560,9 @@ class PlaylistProxyModel(QSortFilterProxyModel):
|
||||
and previous_plr.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() > (
|
||||
track_sequence.current.track_player.start_time
|
||||
track_sequence.current.start_time
|
||||
+ dt.timedelta(
|
||||
milliseconds=Config.HIDE_AFTER_PLAYING_OFFSET
|
||||
)
|
||||
|
||||
@ -35,7 +35,7 @@ from PyQt6.QtWidgets import (
|
||||
# Third party imports
|
||||
|
||||
# App imports
|
||||
from classes import Col, MusicMusterSignals, track_sequence
|
||||
from classes import Col, MusicMusterSignals
|
||||
from config import Config
|
||||
from dialogs import TrackSelectDialog
|
||||
from helpers import (
|
||||
@ -47,6 +47,7 @@ from helpers import (
|
||||
from log import log
|
||||
from models import db, Settings
|
||||
from playlistmodel import PlaylistModel, PlaylistProxyModel
|
||||
from trackmanager import track_sequence
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from musicmuster import Window
|
||||
|
||||
@ -21,6 +21,7 @@ from PyQt6.QtCore import (
|
||||
)
|
||||
|
||||
# App imports
|
||||
from classes import MusicMusterSignals
|
||||
from config import Config
|
||||
from log import log
|
||||
from models import db, PlaylistRows, Tracks
|
||||
@ -185,6 +186,25 @@ class _Music:
|
||||
else:
|
||||
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:
|
||||
"""
|
||||
Fade the currently playing track.
|
||||
@ -193,8 +213,6 @@ class _Music:
|
||||
to hold up the UI during the fade.
|
||||
"""
|
||||
|
||||
log.info(f"Music[{self.name}].stop()")
|
||||
|
||||
if not self.player:
|
||||
return
|
||||
|
||||
@ -202,7 +220,7 @@ class _Music:
|
||||
return
|
||||
|
||||
if fade_seconds <= 0:
|
||||
self._stop()
|
||||
self.stop()
|
||||
return
|
||||
|
||||
# Take a copy of current player to allow another track to be
|
||||
@ -249,13 +267,10 @@ class _Music:
|
||||
# player.is_playing() returning True, so assume playing if less
|
||||
# than Config.PLAY_SETTLE microseconds have passed since
|
||||
# starting play.
|
||||
return (
|
||||
self.start_dt is not None
|
||||
and (
|
||||
self.player.is_playing()
|
||||
or (dt.datetime.now() - self.start_dt)
|
||||
< dt.timedelta(microseconds=Config.PLAY_SETTLE)
|
||||
)
|
||||
return self.start_dt is not None and (
|
||||
self.player.is_playing()
|
||||
or (dt.datetime.now() - self.start_dt)
|
||||
< dt.timedelta(microseconds=Config.PLAY_SETTLE)
|
||||
)
|
||||
|
||||
def move_back(self, ms: int) -> None:
|
||||
@ -342,25 +357,6 @@ class _Music:
|
||||
log.debug(f"Reset from {volume=}")
|
||||
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:
|
||||
"""
|
||||
@ -397,9 +393,10 @@ class _TrackManager:
|
||||
self.resume_marker: Optional[float]
|
||||
self.start_time: Optional[dt.datetime] = None
|
||||
|
||||
self.player = _Music(name=player_name)
|
||||
self.signals = MusicMusterSignals()
|
||||
|
||||
# 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
|
||||
self.fadecurve_thread = QThread()
|
||||
@ -416,23 +413,50 @@ class _TrackManager:
|
||||
self.fadecurve_thread.finished.connect(self.fadecurve_thread.deleteLater)
|
||||
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:
|
||||
"""Fade music"""
|
||||
|
||||
self.player.fade(fade_seconds)
|
||||
|
||||
@property
|
||||
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:
|
||||
"""Play track"""
|
||||
|
||||
now = dt.datetime.now()
|
||||
self.start_time = now
|
||||
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)
|
||||
|
||||
# Calculate time fade_graph should start updating
|
||||
@ -444,7 +468,7 @@ class _TrackManager:
|
||||
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
|
||||
"""
|
||||
@ -456,41 +480,64 @@ class _TrackManager:
|
||||
if self.fade_graph:
|
||||
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:
|
||||
"""
|
||||
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 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):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
with db.Session() as session:
|
||||
# Ensure we have a track
|
||||
plr = session.get(PlaylistRows, plr_id)
|
||||
if not plr:
|
||||
raise ValueError(f"PlaylistTrack: unable to retreive plr {plr_id=}")
|
||||
# Ensure we have a track
|
||||
plr = session.get(PlaylistRows, plr_id)
|
||||
if not plr:
|
||||
raise ValueError(f"PlaylistTrack: unable to retreive plr {plr_id=}")
|
||||
|
||||
self.track_id: int = plr.track_id
|
||||
self.track_id: int = plr.track_id
|
||||
|
||||
super().__init__(
|
||||
session=session, player_name=Config.VLC_MAIN_PLAYER_NAME, track_id=self.track_id
|
||||
)
|
||||
super().__init__(
|
||||
session=session,
|
||||
player_name=Config.VLC_MAIN_PLAYER_NAME,
|
||||
track_id=self.track_id,
|
||||
)
|
||||
|
||||
# Save non-track plr info
|
||||
self.plr_id: int = plr.id
|
||||
self.playlist_id: int = plr.playlist_id
|
||||
self.row_number: int = plr.plr_rownum
|
||||
# Save non-track plr info
|
||||
self.plr_id: int = plr.id
|
||||
self.playlist_id: int = plr.playlist_id
|
||||
self.row_number: int = plr.plr_rownum
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
@ -504,8 +551,9 @@ class PreviewTrackManager(_TrackManager):
|
||||
Manage previewing tracks
|
||||
"""
|
||||
|
||||
def __init__(self, track_id: int) -> None:
|
||||
def __init__(self, session: db.Session, track_id: int) -> None:
|
||||
super().__init__(
|
||||
session=session,
|
||||
player_name=Config.VLC_PREVIEW_PLAYER_NAME,
|
||||
track_id=track_id,
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user