Compare commits

..

4 Commits

Author SHA1 Message Date
Keith Edmunds
25771f5235 Slightly simplify track times; appears to be working 2025-04-20 22:08:25 +01:00
Keith Edmunds
c93d24aef5 Row timings improved but next row not always correct 2025-04-20 21:37:18 +01:00
Keith Edmunds
a2a9afb04f Rewrite track timings; looks to be working 2025-04-19 21:45:53 +01:00
Keith Edmunds
db6fb7b367 Cleanup signals 2025-04-19 21:45:26 +01:00
6 changed files with 113 additions and 104 deletions

View File

@ -282,7 +282,7 @@ class MusicMusterSignals(QObject):
signal_next_track_changed = pyqtSignal() signal_next_track_changed = pyqtSignal()
# Emited when a track starts playing # Emited when a track starts playing
signal_track_started = pyqtSignal(TrackAndPlaylist) signal_track_started = pyqtSignal()
# Used by model to signal spanning of cells to playlist for headers # Used by model to signal spanning of cells to playlist for headers
span_cells_signal = pyqtSignal(int, int, int, int, int) span_cells_signal = pyqtSignal(int, int, int, int, int)

View File

@ -86,6 +86,9 @@ def handle_exception(exc_type, exc_value, exc_traceback):
# Navigate to the inner stack frame # Navigate to the inner stack frame
tb = exc_traceback tb = exc_traceback
if not tb:
log.error(f"handle_excption({exc_type=}, {exc_value=}, {exc_traceback=}")
return
while tb.tb_next: while tb.tb_next:
tb = tb.tb_next tb = tb.tb_next

View File

@ -1255,7 +1255,7 @@ class Window(QMainWindow):
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.track_sequence.current and self.track_sequence.current.is_playing(): if self.track_sequence.current and self.track_sequence.current.music.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"
@ -2195,12 +2195,7 @@ class Window(QMainWindow):
ds.playdates_update(self.track_sequence.current.track_id) ds.playdates_update(self.track_sequence.current.track_id)
# Notify others # Notify others
self.signals.signal_track_started.emit( self.signals.signal_track_started.emit()
TrackAndPlaylist(
self.track_sequence.current.playlist_id,
self.track_sequence.current.track_id,
)
)
# Update headers # Update headers
self.update_headers() self.update_headers()
@ -2634,7 +2629,7 @@ class Window(QMainWindow):
""" """
# If track is playing, update track clocks time and colours # If track is playing, update track clocks time and colours
if self.track_sequence.current and self.track_sequence.current.is_playing(): if self.track_sequence.current and self.track_sequence.current.music.is_playing():
# Elapsed time # Elapsed time
self.header_section.label_elapsed_timer.setText( self.header_section.label_elapsed_timer.setText(
helpers.ms_to_mmss(self.track_sequence.current.time_playing()) helpers.ms_to_mmss(self.track_sequence.current.time_playing())

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from operator import attrgetter from operator import attrgetter
from random import shuffle from random import shuffle
from typing import cast, Optional from typing import cast
import datetime as dt import datetime as dt
import re import re
@ -101,7 +101,9 @@ class PlaylistModel(QAbstractTableModel):
self.signals.signal_set_next_row.connect(self.set_next_row) self.signals.signal_set_next_row.connect(self.set_next_row)
self.signals.signal_track_started.connect(self.track_started) self.signals.signal_track_started.connect(self.track_started)
self.signals.signal_track_ended.connect(self.previous_track_ended) self.signals.signal_track_ended.connect(self.previous_track_ended)
self.signals.signal_next_track_changed.connect(self.signal_next_track_changed_handler) self.signals.signal_next_track_changed.connect(
self.signal_next_track_changed_handler
)
# Populate self.playlist_rows # Populate self.playlist_rows
for dto in ds.playlistrows_by_playlist(self.playlist_id): for dto in ds.playlistrows_by_playlist(self.playlist_id):
@ -159,7 +161,9 @@ class PlaylistModel(QAbstractTableModel):
return return
if not self.selected_rows: if not self.selected_rows:
raise ApplicationError("Add track to header but no row selected") self.signals.show_warning_signal.emit(
"Add track to header", "Add track to header but no row selected"
)
if len(self.selected_rows) > 1: if len(self.selected_rows) > 1:
self.signals.show_warning_signal.emit( self.signals.show_warning_signal.emit(
@ -170,7 +174,7 @@ class PlaylistModel(QAbstractTableModel):
selected_row = self.selected_rows[0] selected_row = self.selected_rows[0]
if selected_row.path: if selected_row.path:
self.signals.show_warning_signal.emit( self.signals.show_warning_signal.emit(
"Add track to header", "Select header to add track to" "Add track to header", "Select a header to add track to"
) )
return return
@ -247,10 +251,9 @@ class PlaylistModel(QAbstractTableModel):
return len(Col) return len(Col)
# @log_call # @log_call
def track_started(self, play_track: TrackAndPlaylist) -> None: def track_started(self) -> None:
""" """
Notification from musicmuster that the current track has just Handle signal_track_started signal.
started playing
Actions required: Actions required:
- sanity check - sanity check
@ -262,25 +265,17 @@ class PlaylistModel(QAbstractTableModel):
- update track times - update track times
""" """
if play_track.playlist_id != self.playlist_id: if self.track_sequence.current is None:
raise ApplicationError("track_started called with no current track")
if self.track_sequence.current.playlist_id != self.playlist_id:
# Not for us # Not for us
return return
track_id = play_track.track_id track_id = self.track_sequence.current.track_id
# Sanity check - 1
if not track_id: if not track_id:
raise ApplicationError("track_started() called with no track_id") raise ApplicationError("track_started() called with no track_id")
# Sanity check - 2
if self.track_sequence.current is None:
raise ApplicationError("track_started callced with no current track")
row_number = self.track_sequence.current.row_number row_number = self.track_sequence.current.row_number
playlist_dto = self.playlist_rows[row_number]
# Sanity check - 3
if playlist_dto.track_id != track_id:
raise ApplicationError("track_id mismatch between playlist_rows and signal")
# Check for OBS scene change # Check for OBS scene change
self.obs_scene_change(row_number) self.obs_scene_change(row_number)
@ -289,18 +284,16 @@ class PlaylistModel(QAbstractTableModel):
ds.playdates_update(track_id) ds.playdates_update(track_id)
# Mark track as played in playlist # Mark track as played in playlist
playlist_dto.played = True self.playlist_rows[row_number].played = True
# Update colour and times for current row # Update colour and times for current row
roles_to_invalidate = [Qt.ItemDataRole.DisplayRole] roles = [Qt.ItemDataRole.DisplayRole]
self.invalidate_row(row_number, roles_to_invalidate) self.invalidate_row(row_number, roles)
# Update previous row in case we're hiding played rows # Update previous row in case we're hiding played rows
if self.track_sequence.previous and self.track_sequence.previous.row_number: if self.track_sequence.previous and self.track_sequence.previous.row_number:
# only invalidate required roles # only invalidate required roles
self.invalidate_row( self.invalidate_row(self.track_sequence.previous.row_number, roles)
self.track_sequence.previous.row_number, roles_to_invalidate
)
# Update all other track times # Update all other track times
self.update_track_times() self.update_track_times()
@ -566,7 +559,7 @@ class PlaylistModel(QAbstractTableModel):
return self.playlist_rows[row_number] return self.playlist_rows[row_number]
# @log_call # @log_call
def get_row_track_id(self, row_number: int) -> Optional[int]: def get_row_track_id(self, row_number: int) -> int | None:
""" """
Return id of track associated with row or None if no track associated Return id of track associated with row or None if no track associated
""" """
@ -706,9 +699,7 @@ class PlaylistModel(QAbstractTableModel):
move_existing = True move_existing = True
if move_existing and existing_plr: if move_existing and existing_plr:
self.move_track_add_note( self.move_track_add_note(new_row_number, existing_plr, note="")
new_row_number, existing_plr, note=""
)
else: else:
super().beginInsertRows(QModelIndex(), new_row_number, new_row_number) super().beginInsertRows(QModelIndex(), new_row_number, new_row_number)
@ -776,7 +767,7 @@ class PlaylistModel(QAbstractTableModel):
return self.playlist_rows[row_number].played return self.playlist_rows[row_number].played
def is_track_in_playlist(self, track_id: int) -> Optional[PlaylistRow]: def is_track_in_playlist(self, track_id: int) -> PlaylistRow | None:
""" """
If this track_id is in the playlist, return the RowAndTrack object If this track_id is in the playlist, return the RowAndTrack object
else return None else return None
@ -1300,7 +1291,7 @@ class PlaylistModel(QAbstractTableModel):
if plr.track_id is None: if plr.track_id is None:
raise ApplicationError(f"set_next_row: no track_id ({plr=})") raise ApplicationError(f"set_next_row: no track_id ({plr=})")
old_next_row: Optional[int] = None old_next_row: int | None = None
if self.track_sequence.next: if self.track_sequence.next:
old_next_row = self.track_sequence.next.row_number old_next_row = self.track_sequence.next.row_number
@ -1498,49 +1489,92 @@ class PlaylistModel(QAbstractTableModel):
InsertTrack(playlist_id=self.playlist_id, track_id=track_id, note="") InsertTrack(playlist_id=self.playlist_id, track_id=track_id, note="")
) )
def get_end_time(self, row_number: int, start_time: dt.datetime) -> dt.datetime:
"""
Return the end time for row_number from the passed start_time and
the row duration.
"""
plr = self.playlist_rows[row_number]
end_time = start_time + dt.timedelta(milliseconds=plr.duration)
return end_time
def update_start_end_times(
self, plr: PlaylistRow, start_time: dt.datetime, end_time: dt.datetime
) -> bool:
"""
Set the the start time of the passed PlaylistRow. If we changed
it, return True else return False.
"""
changed = False
if start_time != plr.forecast_start_time:
plr.forecast_start_time = start_time
changed = True
if end_time != plr.forecast_end_time:
plr.forecast_end_time = end_time
changed = True
return changed
# @log_call # @log_call
def update_track_times(self) -> None: def update_track_times(self) -> None:
""" """
Update track start/end times in self.playlist_rows Update track start/end times in self.playlist_rows
""" """
next_start_time: Optional[dt.datetime] = None next_start_time: dt.datetime | None = None
update_rows: list[int] = [] update_rows: list[int] = []
current_track_row_number: int | None = None
next_track_row: int | None = None
row_count = len(self.playlist_rows) row_count = len(self.playlist_rows)
current_track_row = None # If we have a current track, get its end time
next_track_row = None
if ( if (
self.track_sequence.current self.track_sequence.current
and self.track_sequence.current.playlist_id == self.playlist_id and self.track_sequence.current.playlist_id == self.playlist_id
): ):
current_track_row = self.track_sequence.current.row_number plr = self.track_sequence.current
# Update current track details now so that they are available current_track_row_number = plr.row_number
# when we deal with next track row which may be above current current_track_start_time = self.track_sequence.current.start_time
# track row. if current_track_start_time is None:
self.playlist_rows[current_track_row].set_forecast_start_time( raise ApplicationError(
update_rows, self.track_sequence.current.start_time f"Can't get start time for current track ({self.track_sequence.current=})"
)
current_track_end_time = self.get_end_time(
current_track_row_number, current_track_start_time
) )
if self.update_start_end_times(plr, current_track_start_time, current_track_end_time):
update_rows.append(current_track_row_number)
# If we have a next track, note row number
if ( if (
self.track_sequence.next self.track_sequence.next
and self.track_sequence.next.playlist_id == self.playlist_id and self.track_sequence.next.playlist_id == self.playlist_id
): ):
next_track_row = self.track_sequence.next.row_number next_track_row = self.track_sequence.next.row_number
# Step through rows and update start/end times
for row_number in range(row_count): for row_number in range(row_count):
plr = self.playlist_rows[row_number] plr = self.playlist_rows[row_number]
# Don't update times for tracks that have been played, for # Don't update times for tracks that have been played unless
# unreadable tracks or for the current track, handled above. # this is the next track, for unreadable tracks or for the
# current track, handled above.
if ( if (
plr.played (plr.played and row_number != next_track_row)
or row_number == current_track_row or row_number == current_track_row_number
or (plr.path and file_is_unreadable(plr.path)) or (plr.path and file_is_unreadable(plr.path))
): ):
continue continue
# Reset start time if timing in header # Reset start time if timing in header; otherwise skip header
if self.is_header_row(row_number): if self.is_header_row(row_number):
header_time = get_embedded_time(plr.note) header_time = get_embedded_time(plr.note)
if header_time: if header_time:
@ -1548,24 +1582,33 @@ class PlaylistModel(QAbstractTableModel):
continue continue
# Set start time for next row if we have a current track # Set start time for next row if we have a current track
if ( if current_track_row_number is not None and row_number == next_track_row:
row_number == next_track_row next_start_time = self.get_end_time(row_number, current_track_end_time)
and self.track_sequence.current if self.update_start_end_times(plr, current_track_end_time, next_start_time):
and self.track_sequence.current.end_time update_rows.append(row_number)
):
next_start_time = plr.set_forecast_start_time(
update_rows, self.track_sequence.current.end_time
)
continue
# If we're between the current and next row, zero out # If we're between the current and next row, zero out
# times # times
if (current_track_row or row_count) < row_number < (next_track_row or 0): if (
plr.set_forecast_start_time(update_rows, None) (current_track_row_number or row_count)
< row_number
< (next_track_row or 0)
):
plr.forecast_start_time = plr.forecast_end_time = None
update_rows.append(row_number)
continue
# If we don't have a start time, keep looking
if next_start_time is None:
continue continue
# Set start/end # Set start/end
plr.forecast_start_time = next_start_time plr = self.playlist_rows[row_number]
start_time = next_start_time
next_start_time = self.get_end_time(row_number, next_start_time)
if self.update_start_end_times(plr, start_time, next_start_time):
update_rows.append(row_number)
continue
# Update start/stop times of rows that have changed # Update start/stop times of rows that have changed
for updated_row in update_rows: for updated_row in update_rows:

View File

@ -264,39 +264,6 @@ class PlaylistRow:
milliseconds=update_graph_at_ms milliseconds=update_graph_at_ms
) )
def set_forecast_start_time(
self, modified_rows: list[int], start: dt.datetime | None
) -> dt.datetime | None:
"""
Set forecast start time for this row
Update passed modified rows list if we changed the row.
Return new start time
"""
changed = False
if self.forecast_start_time != start:
self.forecast_start_time = start
changed = True
if start is None:
if self.forecast_end_time is not None:
self.forecast_end_time = None
changed = True
new_start_time = None
else:
end_time = start + dt.timedelta(milliseconds=self.duration)
new_start_time = end_time
if self.forecast_end_time != end_time:
self.forecast_end_time = end_time
changed = True
if changed and self.row_number not in modified_rows:
modified_rows.append(self.row_number)
return new_start_time
def stop(self, fade_seconds: int = 0) -> None: def stop(self, fade_seconds: int = 0) -> None:
""" """
Stop this track playing Stop this track playing

View File

@ -729,17 +729,18 @@ class PlaylistTab(QTableView):
cb.setText(track_path, mode=cb.Mode.Clipboard) cb.setText(track_path, mode=cb.Mode.Clipboard)
# @log_call # @log_call
def track_started(self, play_track: TrackAndPlaylist) -> None: def track_started(self) -> None:
""" """
Called when track starts playing Called when track starts playing
""" """
if play_track.playlist_id != self.playlist_id: if self.track_sequence.current is None:
return
if self.track_sequence.current.playlist_id != self.playlist_id:
# Not for us # Not for us
return return
# TODO - via signal
# self.get_base_model().current_track_started()
# Scroll to current section if hide mode is by section # Scroll to current section if hide mode is by section
if ( if (
self.musicmuster.hide_played_tracks self.musicmuster.hide_played_tracks