.
This commit is contained in:
parent
1825e48e92
commit
973096ba3f
116
app/classes.py
116
app/classes.py
@ -5,6 +5,7 @@ import ctypes
|
||||
from dataclasses import dataclass, field
|
||||
import datetime as dt
|
||||
from enum import auto, Enum
|
||||
import os
|
||||
import platform
|
||||
import threading
|
||||
from time import sleep
|
||||
@ -12,6 +13,7 @@ from typing import Any, Optional, NamedTuple
|
||||
|
||||
# Third party imports
|
||||
import numpy as np
|
||||
from pprint import pprint as pp
|
||||
import pyqtgraph as pg # type: ignore
|
||||
import vlc # type: ignore
|
||||
|
||||
@ -39,6 +41,7 @@ from helpers import (
|
||||
)
|
||||
|
||||
lock = threading.Lock()
|
||||
players: dict[int, str] = {}
|
||||
|
||||
# Define the VLC callback function type
|
||||
VLC_LOG_CB = ctypes.CFUNCTYPE(
|
||||
@ -199,6 +202,7 @@ class _FadeTrack(QRunnable):
|
||||
if not self.player:
|
||||
return
|
||||
|
||||
log.info("fade starting")
|
||||
# Reduce volume logarithmically
|
||||
total_steps = self.fade_seconds * Config.FADEOUT_STEPS_PER_SECOND
|
||||
db_reduction_per_step = Config.FADEOUT_DB / total_steps
|
||||
@ -212,7 +216,13 @@ class _FadeTrack(QRunnable):
|
||||
)
|
||||
sleep(1 / Config.FADEOUT_STEPS_PER_SECOND)
|
||||
|
||||
self.player.stop()
|
||||
log.info("fade ended")
|
||||
if self.player:
|
||||
log.info(f"Releasing {self.player=}")
|
||||
self.player.release() # Release resources
|
||||
del players[id(self.player)]
|
||||
pp(players)
|
||||
self.player = None # Clear the reference
|
||||
|
||||
|
||||
class _Music:
|
||||
@ -275,27 +285,6 @@ class _Music:
|
||||
else:
|
||||
self.start_dt = dt.datetime.now() - 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()
|
||||
self.player_count -= 1
|
||||
log.debug(f"_Music.stop: Releasing player {p=}, {self.player_count=}")
|
||||
p = None
|
||||
|
||||
def fade(self, fade_seconds: int) -> None:
|
||||
"""
|
||||
Fade the currently playing track.
|
||||
@ -318,6 +307,13 @@ class _Music:
|
||||
# started without interfering here
|
||||
with lock:
|
||||
p = self.player
|
||||
# Connect to the end-of-playback event
|
||||
p.event_manager().event_attach(
|
||||
vlc.EventType.MediaPlayerEndReached, self.on_playback_end
|
||||
)
|
||||
del players[id(self.player)]
|
||||
players[id(p)] = f"From fade {self.player=}"
|
||||
pp(players)
|
||||
self.player = None
|
||||
|
||||
pool = QThreadPool.globalInstance()
|
||||
@ -367,6 +363,18 @@ class _Music:
|
||||
< dt.timedelta(microseconds=Config.PLAY_SETTLE)
|
||||
)
|
||||
|
||||
def on_playback_end(self, *args, **kwargs):
|
||||
"""
|
||||
Release player when playing has ended
|
||||
"""
|
||||
|
||||
if self.player:
|
||||
log.info(f"Releasing {self.player=}")
|
||||
del players[id(self.player)]
|
||||
pp(players)
|
||||
self.player.release() # Release resources
|
||||
self.player = None # Clear the reference
|
||||
|
||||
def play(
|
||||
self,
|
||||
path: str,
|
||||
@ -394,6 +402,15 @@ class _Music:
|
||||
show_warning(None, "Error loading file", f"Cannot play file ({path})")
|
||||
return
|
||||
self.player = media.player_new_from_media()
|
||||
log.info(f"Created {self.player=}")
|
||||
players[id(self.player)] = os.path.basename(path)
|
||||
pp(players)
|
||||
|
||||
# Connect to the end-of-playback event
|
||||
self.player.event_manager().event_attach(
|
||||
vlc.EventType.MediaPlayerEndReached, self.on_playback_end
|
||||
)
|
||||
|
||||
if self.player:
|
||||
_ = self.player.play()
|
||||
self.set_volume(self.max_volume)
|
||||
@ -456,6 +473,31 @@ class _Music:
|
||||
log.debug(f"Reset from {volume=}")
|
||||
sleep(0.1)
|
||||
|
||||
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
|
||||
del players[id(self.player)]
|
||||
players[id(p)] = f"From stop {self.player=}"
|
||||
pp(players)
|
||||
self.player = None
|
||||
self.start_dt = None
|
||||
|
||||
with lock:
|
||||
p.stop()
|
||||
p.release()
|
||||
del players[id(p)]
|
||||
pp(players)
|
||||
log.info(f"_Music.stop: Releasing player {p=}, {self.player_count=}")
|
||||
p = None
|
||||
|
||||
|
||||
@singleton
|
||||
@dataclass
|
||||
@ -514,7 +556,9 @@ class RowAndTrack:
|
||||
self.fade_at = playlist_row.track.fade_at
|
||||
self.intro = playlist_row.track.intro
|
||||
if playlist_row.track.playdates:
|
||||
self.lastplayed = max([a.lastplayed for a in playlist_row.track.playdates])
|
||||
self.lastplayed = max(
|
||||
[a.lastplayed for a in playlist_row.track.playdates]
|
||||
)
|
||||
else:
|
||||
self.lastplayed = Config.EPOCH
|
||||
self.path = playlist_row.track.path
|
||||
@ -544,6 +588,7 @@ class RowAndTrack:
|
||||
self.start_time: Optional[dt.datetime] = None
|
||||
|
||||
# Other object initialisation
|
||||
self.music = _Music(name=Config.VLC_MAIN_PLAYER_NAME)
|
||||
self.signals = MusicMusterSignals()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
@ -564,7 +609,7 @@ class RowAndTrack:
|
||||
if self.end_of_track_signalled:
|
||||
return
|
||||
|
||||
if not self.player.is_playing():
|
||||
if not self.music.is_playing():
|
||||
self.start_time = None
|
||||
if self.fade_graph:
|
||||
self.fade_graph.clear()
|
||||
@ -596,15 +641,15 @@ class RowAndTrack:
|
||||
"""
|
||||
|
||||
if enable:
|
||||
self.player.set_volume(volume=Config.VLC_VOLUME_DROP3db, set_default=False)
|
||||
self.music.set_volume(volume=Config.VLC_VOLUME_DROP3db, set_default=False)
|
||||
else:
|
||||
self.player.set_volume(volume=Config.VLC_VOLUME_DEFAULT, set_default=False)
|
||||
self.music.set_volume(volume=Config.VLC_VOLUME_DEFAULT, set_default=False)
|
||||
|
||||
def fade(self, fade_seconds: int = Config.FADEOUT_SECONDS) -> None:
|
||||
"""Fade music"""
|
||||
|
||||
self.resume_marker = self.player.get_position()
|
||||
self.player.fade(fade_seconds)
|
||||
self.resume_marker = self.music.get_position()
|
||||
self.music.fade(fade_seconds)
|
||||
self.signal_end_of_track()
|
||||
|
||||
def is_playing(self) -> bool:
|
||||
@ -615,21 +660,21 @@ class RowAndTrack:
|
||||
if self.start_time is None:
|
||||
return False
|
||||
|
||||
return self.player.is_playing()
|
||||
return self.music.is_playing()
|
||||
|
||||
def move_back(self, ms: int = Config.PREVIEW_BACK_MS) -> None:
|
||||
"""
|
||||
Rewind player by ms milliseconds
|
||||
"""
|
||||
|
||||
self.player.adjust_by_ms(ms * -1)
|
||||
self.music.adjust_by_ms(ms * -1)
|
||||
|
||||
def move_forward(self, ms: int = Config.PREVIEW_ADVANCE_MS) -> None:
|
||||
"""
|
||||
Rewind player by ms milliseconds
|
||||
"""
|
||||
|
||||
self.player.adjust_by_ms(ms)
|
||||
self.music.adjust_by_ms(ms)
|
||||
|
||||
def play(self, position: Optional[float] = None) -> None:
|
||||
"""Play track"""
|
||||
@ -639,8 +684,7 @@ class RowAndTrack:
|
||||
self.start_time = now
|
||||
|
||||
# Initialise player
|
||||
self.player = _Music(name=Config.VLC_MAIN_PLAYER_NAME)
|
||||
self.player.play(self.path, start_time=now, position=position)
|
||||
self.music.play(self.path, start_time=now, position=position)
|
||||
|
||||
self.end_time = now + dt.timedelta(milliseconds=self.duration)
|
||||
|
||||
@ -658,7 +702,7 @@ class RowAndTrack:
|
||||
Restart player
|
||||
"""
|
||||
|
||||
self.player.adjust_by_ms(self.time_playing() * -1)
|
||||
self.music.adjust_by_ms(self.time_playing() * -1)
|
||||
|
||||
def set_forecast_start_time(
|
||||
self, modified_rows: list[int], start: Optional[dt.datetime]
|
||||
@ -705,7 +749,7 @@ class RowAndTrack:
|
||||
Stop this track playing
|
||||
"""
|
||||
|
||||
self.resume_marker = self.player.get_position()
|
||||
self.resume_marker = self.music.get_position()
|
||||
self.fade(fade_seconds)
|
||||
|
||||
# Reset fade graph
|
||||
@ -720,7 +764,7 @@ class RowAndTrack:
|
||||
if self.start_time is None:
|
||||
return 0
|
||||
|
||||
return self.player.get_playtime()
|
||||
return self.music.get_playtime()
|
||||
|
||||
def time_remaining_intro(self) -> int:
|
||||
"""
|
||||
|
||||
@ -316,6 +316,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.action_quicklog.activated.connect(self.quicklog)
|
||||
|
||||
self.load_last_playlists()
|
||||
self.stop_autoplay = False
|
||||
|
||||
def about(self) -> None:
|
||||
"""Get git tag and database name"""
|
||||
@ -680,6 +681,10 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.catch_return_key = False
|
||||
self.show_status_message("Play controls: Enabled", 0)
|
||||
|
||||
# autoplay
|
||||
if not self.stop_autoplay:
|
||||
self.play_next()
|
||||
|
||||
def export_playlist_tab(self) -> None:
|
||||
"""Export the current playlist to an m3u file"""
|
||||
|
||||
@ -1151,6 +1156,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
self.btnDrop3db.setChecked(False)
|
||||
|
||||
# Play (new) current track
|
||||
log.info(f"Play: {track_sequence.current.title}")
|
||||
track_sequence.current.play(position)
|
||||
|
||||
# Update clocks now, don't wait for next tick
|
||||
@ -1623,6 +1629,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
def stop(self) -> None:
|
||||
"""Stop playing immediately"""
|
||||
|
||||
self.stop_autoplay = True
|
||||
if track_sequence.current:
|
||||
track_sequence.current.stop()
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user