WIP V3: start/end times, moving row bug

Start/end times now stored separately from self.playlist_rows. Moving
next row to above current row now works.
This commit is contained in:
Keith Edmunds 2023-11-15 20:09:00 +00:00
parent 3cbc69b11e
commit de710b1dc7
2 changed files with 61 additions and 40 deletions

View File

@ -1,3 +1,4 @@
from dataclasses import dataclass
from datetime import datetime, timedelta from datetime import datetime, timedelta
from enum import auto, Enum from enum import auto, Enum
from operator import attrgetter from operator import attrgetter
@ -54,12 +55,10 @@ class PlaylistRowData:
self.artist: str = "" self.artist: str = ""
self.bitrate = 0 self.bitrate = 0
self.duration: int = 0 self.duration: int = 0
self.end_time: Optional[datetime] = None
self.lastplayed: datetime = Config.EPOCH self.lastplayed: datetime = Config.EPOCH
self.path = "" self.path = ""
self.played = False self.played = False
self.start_gap: Optional[int] = None self.start_gap: Optional[int] = None
self.start_time: Optional[datetime] = None
self.title: str = "" self.title: str = ""
self.plrid: int = plr.id self.plrid: int = plr.id
@ -86,6 +85,12 @@ class PlaylistRowData:
) )
@dataclass
class StartEndTimes:
start_time: Optional[datetime] = None
end_time: Optional[datetime] = None
class PlaylistModel(QAbstractTableModel): class PlaylistModel(QAbstractTableModel):
""" """
The Playlist Model The Playlist Model
@ -112,6 +117,7 @@ class PlaylistModel(QAbstractTableModel):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.playlist_rows: dict[int, PlaylistRowData] = {} self.playlist_rows: dict[int, PlaylistRowData] = {}
self.start_end_times: dict[int, StartEndTimes] = {}
self.signals = MusicMusterSignals() self.signals = MusicMusterSignals()
self.signals.add_track_to_header_signal.connect(self.add_track_to_header) self.signals.add_track_to_header_signal.connect(self.add_track_to_header)
@ -268,10 +274,9 @@ class PlaylistModel(QAbstractTableModel):
self.refresh_row(session, plr.plr_rownum) self.refresh_row(session, plr.plr_rownum)
# Update track times # Update track times
self.playlist_rows[row_number].start_time = datetime.now() self.start_end_times[row_number].start_time = track_sequence.now.start_time
self.playlist_rows[row_number].end_time = datetime.now() + timedelta( self.start_end_times[row_number].end_time = track_sequence.now.end_time
milliseconds=self.playlist_rows[row_number].duration
)
# Update colour and times for current row # Update colour and times for current row
self.invalidate_row(row_number) self.invalidate_row(row_number)
# Update all other track times # Update all other track times
@ -369,15 +374,17 @@ class PlaylistModel(QAbstractTableModel):
if column == Col.DURATION.value: if column == Col.DURATION.value:
return QVariant(ms_to_mmss(prd.duration)) return QVariant(ms_to_mmss(prd.duration))
if column == Col.START_TIME.value: if column == Col.START_TIME.value:
if prd.start_time: if row in self.start_end_times:
return QVariant(prd.start_time.strftime(Config.TRACK_TIME_FORMAT)) start_time = self.start_end_times[row].start_time
else: if start_time:
return QVariant() return QVariant(start_time.strftime(Config.TRACK_TIME_FORMAT))
return QVariant()
if column == Col.END_TIME.value: if column == Col.END_TIME.value:
if prd.end_time: if row in self.start_end_times:
return QVariant(prd.end_time.strftime(Config.TRACK_TIME_FORMAT)) end_time = self.start_end_times[row].end_time
else: if end_time:
return QVariant() return QVariant(end_time.strftime(Config.TRACK_TIME_FORMAT))
return QVariant()
if column == Col.LAST_PLAYED.value: if column == Col.LAST_PLAYED.value:
return QVariant(get_relative_date(prd.lastplayed)) return QVariant(get_relative_date(prd.lastplayed))
if column == Col.BITRATE.value: if column == Col.BITRATE.value:
@ -649,13 +656,20 @@ class PlaylistModel(QAbstractTableModel):
for modified_row in modified_rows: for modified_row in modified_rows:
self.invalidate_row(modified_row) self.invalidate_row(modified_row)
def mark_unplayed(self, row_number: int) -> None: def mark_unplayed(self, row_numbers: List[int]) -> None:
""" """
Mark row as unplayed Mark row as unplayed
""" """
self.playlist_rows[row_number].played = False with Session() as session:
self.invalidate_row(row_number) for row_number in row_numbers:
plr = session.get(PlaylistRows, self.playlist_rows[row_number].plrid)
if not plr:
return
plr.played = False
self.refresh_row(session, row_number)
self.invalidate_rows(row_numbers)
def move_rows(self, from_rows: List[int], to_row_number: int) -> None: def move_rows(self, from_rows: List[int], to_row_number: int) -> None:
""" """
@ -678,6 +692,7 @@ class PlaylistModel(QAbstractTableModel):
from_rows, range(next_to_row, next_to_row + len(from_rows)) from_rows, range(next_to_row, next_to_row + len(from_rows))
): ):
row_map[from_row] = to_row row_map[from_row] = to_row
# Move the remaining rows to the row_map. We want to fill it # Move the remaining rows to the row_map. We want to fill it
# before (if there are gaps) and after (likewise) the rows that # before (if there are gaps) and after (likewise) the rows that
# are moving. # are moving.
@ -695,6 +710,14 @@ class PlaylistModel(QAbstractTableModel):
old_row, HEADER_NOTES_COLUMN, 1, 1 old_row, HEADER_NOTES_COLUMN, 1, 1
) )
# Check to see whether any rows in track_sequence have moved
if track_sequence.previous.plr_rownum in row_map:
track_sequence.previous.plr_rownum = row_map[track_sequence.previous.plr_rownum]
if track_sequence.now.plr_rownum in row_map:
track_sequence.now.plr_rownum = row_map[track_sequence.now.plr_rownum]
if track_sequence.next.plr_rownum in row_map:
track_sequence.next.plr_rownum = row_map[track_sequence.next.plr_rownum]
# For SQLAlchemy, build a list of dictionaries that map plrid to # For SQLAlchemy, build a list of dictionaries that map plrid to
# new row number: # new row number:
sqla_map: List[dict[str, int]] = [] sqla_map: List[dict[str, int]] = []
@ -756,7 +779,7 @@ class PlaylistModel(QAbstractTableModel):
self.playlist_rows[p.plr_rownum] = PlaylistRowData(p) self.playlist_rows[p.plr_rownum] = PlaylistRowData(p)
def refresh_row(self, session, row_number): def refresh_row(self, session, row_number):
"""Populate dict for one row for data calls""" """Populate dict for one row from database"""
p = PlaylistRows.deep_row(session, self.playlist_id, row_number) p = PlaylistRows.deep_row(session, self.playlist_id, row_number)
self.playlist_rows[row_number] = PlaylistRowData(p) self.playlist_rows[row_number] = PlaylistRowData(p)
@ -950,21 +973,22 @@ class PlaylistModel(QAbstractTableModel):
update_rows: List[int] = [] update_rows: List[int] = []
for row_number in range(len(self.playlist_rows)): for row_number in range(len(self.playlist_rows)):
stend = self.start_end_times[row_number] = StartEndTimes()
prd = self.playlist_rows[row_number] prd = self.playlist_rows[row_number]
# Reset start_time if this is the current row # Reset start_time if this is the current row
if row_number == track_sequence.now.plr_rownum: if row_number == track_sequence.now.plr_rownum:
# Start/end times for current track are set in current_track_started stend.start_time = track_sequence.now.start_time
stend.end_time = track_sequence.now.end_time
if not next_start_time: if not next_start_time:
next_start_time = prd.end_time next_start_time = stend.end_time
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
current_end_time = track_sequence.now.end_time if row_number == track_sequence.next.plr_rownum and track_sequence.now.end_time:
if row_number == track_sequence.next.plr_rownum and current_end_time: stend.start_time = track_sequence.now.end_time
prd.start_time = current_end_time stend.end_time = stend.start_time + timedelta(milliseconds=prd.duration)
prd.end_time = current_end_time + timedelta(milliseconds=prd.duration) next_start_time = stend.end_time
next_start_time = prd.end_time
update_rows.append(row_number) update_rows.append(row_number)
continue continue
@ -985,14 +1009,11 @@ class PlaylistModel(QAbstractTableModel):
< row_number < row_number
< track_sequence.next.plr_rownum < track_sequence.next.plr_rownum
): ):
prd.start_time = None
prd.end_time = None
update_rows.append(row_number) update_rows.append(row_number)
continue continue
# Reset start time if timing in header or at current track # Reset start time if timing in header or at current track
if not prd.path: if self.is_header_row(row_number):
# This is a header row
header_time = get_embedded_time(prd.note) header_time = get_embedded_time(prd.note)
if header_time: if header_time:
next_start_time = header_time next_start_time = header_time
@ -1001,14 +1022,14 @@ class PlaylistModel(QAbstractTableModel):
# start time # start time
if next_start_time is None: if next_start_time is None:
continue continue
if prd.start_time != next_start_time: if stend.start_time != next_start_time:
prd.start_time = next_start_time stend.start_time = next_start_time
update_rows.append(row_number) update_rows.append(row_number)
next_start_time += timedelta( next_start_time += timedelta(
milliseconds=self.playlist_rows[row_number].duration milliseconds=self.playlist_rows[row_number].duration
) )
if prd.end_time != next_start_time: if stend.end_time != next_start_time:
prd.end_time = next_start_time stend.end_time = next_start_time
update_rows.append(row_number) update_rows.append(row_number)
# Update start/stop times of rows that have changed # Update start/stop times of rows that have changed

View File

@ -1103,11 +1103,15 @@ class PlaylistTab(QTableView):
# Mark unplayed # Mark unplayed
if track_row and model.is_unplayed_row(row_number): if track_row and model.is_unplayed_row(row_number):
self._add_context_menu("Mark unplayed", lambda: model.mark_unplayed(row_number)) self._add_context_menu(
"Mark unplayed", lambda: model.mark_unplayed(self._get_selected_rows())
)
# Unmark as next # Unmark as next
if next_row: if next_row:
self._add_context_menu("Unmark as next track", lambda: model.set_next_row(None)) self._add_context_menu(
"Unmark as next track", lambda: model.set_next_row(None)
)
# ---------------------- # ----------------------
self.menu.addSeparator() self.menu.addSeparator()
@ -1134,9 +1138,6 @@ class PlaylistTab(QTableView):
lambda: model.sort_by_lastplayed(self._get_selected_rows()), lambda: model.sort_by_lastplayed(self._get_selected_rows()),
parent_menu=sort_menu, parent_menu=sort_menu,
) )
if sort_menu:
sort_menu.setEnabled(model.selection_is_sortable(self._get_selected_rows()))
self._add_context_menu("Undo sort", self._sort_undo, not bool(self.sort_undo))
# Info TODO # Info TODO
if track_row: if track_row:
@ -1144,8 +1145,7 @@ class PlaylistTab(QTableView):
# Track path TODO # Track path TODO
if track_row: if track_row:
self._add_context_menu( self._add_context_menu("Copy track path", lambda: print("Track path"))
"Copy track path", lambda: print("Track path"))
def _calculate_end_time( def _calculate_end_time(
self, start: Optional[datetime], duration: int self, start: Optional[datetime], duration: int