Compare commits
4 Commits
5d7b55a0ab
...
25771f5235
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25771f5235 | ||
|
|
c93d24aef5 | ||
|
|
a2a9afb04f | ||
|
|
db6fb7b367 |
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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())
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user