WIP: progressing no sessions in app files

This commit is contained in:
Keith Edmunds 2025-04-04 16:19:17 +01:00
parent 2fce0b34be
commit 4eaab98745
5 changed files with 133 additions and 190 deletions

View File

@ -68,7 +68,7 @@ class TrackFileData:
destination_path: str = "" destination_path: str = ""
import_this_file: bool = False import_this_file: bool = False
error: str = "" error: str = ""
file_path_to_remove: Optional[str] = None file_path_to_remove: str | None = None
track_id: int = 0 track_id: int = 0
track_match_data: list[TrackMatchData] = field(default_factory=list) track_match_data: list[TrackMatchData] = field(default_factory=list)
@ -251,7 +251,7 @@ class FileImporter:
artist_match=artist_score, artist_match=artist_score,
title=existing_track.title, title=existing_track.title,
title_match=title_score, title_match=title_score,
track_id=existing_track.id, track_id=existing_track.track_id,
) )
) )
@ -384,12 +384,12 @@ class FileImporter:
else: else:
tfd.destination_path = existing_track_path tfd.destination_path = existing_track_path
def _get_existing_track(self, track_id: int) -> Tracks: def _get_existing_track(self, track_id: int) -> TrackDTO:
""" """
Lookup in existing track in the local cache and return it Lookup in existing track in the local cache and return it
""" """
existing_track_records = [a for a in self.existing_tracks if a.id == track_id] existing_track_records = [a for a in self.existing_tracks if a.track_id == track_id]
if len(existing_track_records) != 1: if len(existing_track_records) != 1:
raise ApplicationError( raise ApplicationError(
f"Internal error in _get_existing_track: {existing_track_records=}" f"Internal error in _get_existing_track: {existing_track_records=}"
@ -462,8 +462,7 @@ class FileImporter:
# file). Check that because the path field in the database is # file). Check that because the path field in the database is
# unique and so adding a duplicate will give a db integrity # unique and so adding a duplicate will give a db integrity
# error. # error.
with db.Session() as session: if repository.track_with_path(tfd.destination_path):
if Tracks.get_by_path(session, tfd.destination_path):
tfd.error = ( tfd.error = (
"Importing a new track but destination path already exists " "Importing a new track but destination path already exists "
f"in database ({tfd.destination_path})" f"in database ({tfd.destination_path})"
@ -590,7 +589,7 @@ class DoTrackImport(QThread):
tags: Tags, tags: Tags,
destination_path: str, destination_path: str,
track_id: int, track_id: int,
file_path_to_remove: Optional[str] = None, file_path_to_remove: str | None = None,
) -> None: ) -> None:
""" """
Save parameters Save parameters
@ -621,7 +620,7 @@ class DoTrackImport(QThread):
) )
# Get audio metadata in this thread rather than calling function to save interactive time # Get audio metadata in this thread rather than calling function to save interactive time
self.audio_metadata = helpers.get_audio_metadata(self.import_file_path) self.audio_metadata = get_audio_metadata(self.import_file_path)
# Remove old file if so requested # Remove old file if so requested
if self.file_path_to_remove and os.path.exists(self.file_path_to_remove): if self.file_path_to_remove and os.path.exists(self.file_path_to_remove):
@ -660,7 +659,7 @@ class DoTrackImport(QThread):
return return
session.commit() session.commit()
helpers.normalise_track(self.destination_track_path) normalise_track(self.destination_track_path)
self.signals.status_message_signal.emit( self.signals.status_message_signal.emit(
f"{os.path.basename(self.import_file_path)} imported", 10000 f"{os.path.basename(self.import_file_path)} imported", 10000

View File

@ -2321,7 +2321,7 @@ class Window(QMainWindow):
track.intro = intro track.intro = intro
session.commit() session.commit()
self.preview_manager.set_intro(intro) self.preview_manager.set_intro(intro)
self.current.base_model.refresh_row(session, row_number) self.current.base_model.refresh_row(row_number)
roles = [ roles = [
Qt.ItemDataRole.DisplayRole, Qt.ItemDataRole.DisplayRole,
] ]

View File

@ -285,12 +285,7 @@ class PlaylistModel(QAbstractTableModel):
f"current_track_started() called with no track_id ({playlist_dto=})" f"current_track_started() called with no track_id ({playlist_dto=})"
) )
# TODO: ensure Playdates is updated repository.update_playdates(track_id)
# with db.Session() as session:
# # Update Playdates in database
# log.debug(f"{self}: update playdates {track_id=}")
# Playdates(session, track_id)
# session.commit()
# Mark track as played in playlist # Mark track as played in playlist
playlist_dto.played = True playlist_dto.played = True
@ -356,23 +351,23 @@ class PlaylistModel(QAbstractTableModel):
row = index.row() row = index.row()
column = index.column() column = index.column()
# rat for playlist row data as it's used a lot # plr for playlist row data as it's used a lot
rat = self.playlist_rows[row] plr = self.playlist_rows[row]
# These are ordered in approximately the frequency with which # These are ordered in approximately the frequency with which
# they are called # they are called
if role == Qt.ItemDataRole.BackgroundRole: if role == Qt.ItemDataRole.BackgroundRole:
return self._background_role(row, column, rat) return self._background_role(row, column, plr)
elif role == Qt.ItemDataRole.DisplayRole: elif role == Qt.ItemDataRole.DisplayRole:
return self._display_role(row, column, rat) return self._display_role(row, column, plr)
elif role == Qt.ItemDataRole.EditRole: elif role == Qt.ItemDataRole.EditRole:
return self._edit_role(row, column, rat) return self._edit_role(row, column, plr)
elif role == Qt.ItemDataRole.FontRole: elif role == Qt.ItemDataRole.FontRole:
return self._font_role(row, column, rat) return self._font_role(row, column, plr)
elif role == Qt.ItemDataRole.ForegroundRole: elif role == Qt.ItemDataRole.ForegroundRole:
return self._foreground_role(row, column, rat) return self._foreground_role(row, column, plr)
elif role == Qt.ItemDataRole.ToolTipRole: elif role == Qt.ItemDataRole.ToolTipRole:
return self._tooltip_role(row, column, rat) return self._tooltip_role(row, column, plr)
return QVariant() return QVariant()
@ -399,7 +394,7 @@ class PlaylistModel(QAbstractTableModel):
self.track_sequence.update() self.track_sequence.update()
self.update_track_times() self.update_track_times()
def _display_role(self, row: int, column: int, rat: PlaylistRow) -> str: def _display_role(self, row: int, column: int, plr: PlaylistRow) -> str:
""" """
Return text for display Return text for display
""" """
@ -417,42 +412,42 @@ class PlaylistModel(QAbstractTableModel):
if header_row: if header_row:
if column == HEADER_NOTES_COLUMN: if column == HEADER_NOTES_COLUMN:
header_text = self.header_text(rat) header_text = self.header_text(plr)
if not header_text: if not header_text:
return Config.SECTION_HEADER return Config.SECTION_HEADER
else: else:
formatted_header = self.header_text(rat) formatted_header = self.header_text(plr)
trimmed_header = self.remove_section_timer_markers(formatted_header) trimmed_header = self.remove_section_timer_markers(formatted_header)
return trimmed_header return trimmed_header
else: else:
return "" return ""
if column == Col.START_TIME.value: if column == Col.START_TIME.value:
start_time = rat.forecast_start_time start_time = plr.forecast_start_time
if start_time: if start_time:
return start_time.strftime(Config.TRACK_TIME_FORMAT) return start_time.strftime(Config.TRACK_TIME_FORMAT)
return "" return ""
if column == Col.END_TIME.value: if column == Col.END_TIME.value:
end_time = rat.forecast_end_time end_time = plr.forecast_end_time
if end_time: if end_time:
return end_time.strftime(Config.TRACK_TIME_FORMAT) return end_time.strftime(Config.TRACK_TIME_FORMAT)
return "" return ""
if column == Col.INTRO.value: if column == Col.INTRO.value:
if rat.intro: if plr.intro:
return f"{rat.intro / 1000:{Config.INTRO_SECONDS_FORMAT}}" return f"{plr.intro / 1000:{Config.INTRO_SECONDS_FORMAT}}"
else: else:
return "" return ""
dispatch_table: dict[int, str] = { dispatch_table: dict[int, str] = {
Col.ARTIST.value: rat.artist, Col.ARTIST.value: plr.artist,
Col.BITRATE.value: str(rat.bitrate), Col.BITRATE.value: str(plr.bitrate),
Col.DURATION.value: ms_to_mmss(rat.duration), Col.DURATION.value: ms_to_mmss(plr.duration),
Col.LAST_PLAYED.value: get_relative_date(rat.lastplayed), Col.LAST_PLAYED.value: get_relative_date(plr.lastplayed),
Col.NOTE.value: rat.note, Col.NOTE.value: plr.note,
Col.START_GAP.value: str(rat.start_gap), Col.START_GAP.value: str(plr.start_gap),
Col.TITLE.value: rat.title, Col.TITLE.value: plr.title,
} }
if column in dispatch_table: if column in dispatch_table:
return dispatch_table[column] return dispatch_table[column]
@ -467,13 +462,12 @@ class PlaylistModel(QAbstractTableModel):
if playlist_id != self.playlist_id: if playlist_id != self.playlist_id:
return return
with db.Session() as session: self.refresh_data()
self.refresh_data(session)
super().endResetModel() super().endResetModel()
self.track_sequence.update() self.track_sequence.update()
self.update_track_times() self.update_track_times()
def _edit_role(self, row: int, column: int, rat: PlaylistRow) -> str | int: def _edit_role(self, row: int, column: int, plr: PlaylistRow) -> str | int:
""" """
Return value for editing Return value for editing
""" """
@ -481,31 +475,25 @@ class PlaylistModel(QAbstractTableModel):
# If this is a header row and we're being asked for the # If this is a header row and we're being asked for the
# HEADER_NOTES_COLUMN, return the note value # HEADER_NOTES_COLUMN, return the note value
if self.is_header_row(row) and column == HEADER_NOTES_COLUMN: if self.is_header_row(row) and column == HEADER_NOTES_COLUMN:
return rat.note return plr.note
if column == Col.INTRO.value: if column == Col.INTRO.value:
return rat.intro or 0 return plr.intro or 0
if column == Col.TITLE.value: if column == Col.TITLE.value:
return rat.title return plr.title
if column == Col.ARTIST.value: if column == Col.ARTIST.value:
return rat.artist return plr.artist
if column == Col.NOTE.value: if column == Col.NOTE.value:
return rat.note return plr.note
return "" return ""
def _foreground_role(self, row: int, column: int, rat: PlaylistRow) -> QBrush: def _foreground_role(self, row: int, column: int, plr: PlaylistRow) -> QBrush:
"""Return header foreground colour or QBrush() if none""" """Return header foreground colour or QBrush() if none"""
if self.is_header_row(row): plr.row_fg = repository.get_colour(plr.note, foreground=True)
if rat.row_fg is None: if plr.row_fg:
with db.Session() as session: return QBrush(QColor(plr.row_fg))
rat.row_fg = NoteColours.get_colour(
session, rat.note, foreground=True
)
if rat.row_fg:
return QBrush(QColor(rat.row_fg))
return QBrush() return QBrush()
def flags(self, index: QModelIndex) -> Qt.ItemFlag: def flags(self, index: QModelIndex) -> Qt.ItemFlag:
@ -531,7 +519,7 @@ class PlaylistModel(QAbstractTableModel):
return default return default
def _font_role(self, row: int, column: int, rat: PlaylistRow) -> QFont: def _font_role(self, row: int, column: int, plr: PlaylistRow) -> QFont:
""" """
Return font Return font
""" """
@ -670,21 +658,21 @@ class PlaylistModel(QAbstractTableModel):
return QVariant() return QVariant()
def header_text(self, rat: PlaylistRow) -> str: def header_text(self, plr: PlaylistRow) -> str:
""" """
Process possible section timing directives embeded in header Process possible section timing directives embeded in header
""" """
if rat.note.endswith(Config.SECTION_STARTS): if plr.note.endswith(Config.SECTION_STARTS):
return self.start_of_timed_section_header(rat) return self.start_of_timed_section_header(plr)
elif rat.note.endswith("="): elif plr.note.endswith("="):
return self.section_subtotal_header(rat) return self.section_subtotal_header(plr)
elif rat.note == "-": elif plr.note == "-":
# If the hyphen is the only thing on the line, echo the note # If the hyphen is the only thing on the line, echo the note
# that started the section without the trailing "+". # that started the section without the trailing "+".
for row_number in range(rat.row_number - 1, -1, -1): for row_number in range(plr.row_number - 1, -1, -1):
row_rat = self.playlist_rows[row_number] row_rat = self.playlist_rows[row_number]
if self.is_header_row(row_number): if self.is_header_row(row_number):
if row_rat.note.endswith("-"): if row_rat.note.endswith("-"):
@ -694,7 +682,7 @@ class PlaylistModel(QAbstractTableModel):
return f"[End: {row_rat.note[:-1]}]" return f"[End: {row_rat.note[:-1]}]"
return "-" return "-"
return rat.note return plr.note
def hide_played_tracks(self, hide: bool) -> None: def hide_played_tracks(self, hide: bool) -> None:
""" """
@ -852,31 +840,6 @@ class PlaylistModel(QAbstractTableModel):
if to_row_number in from_rows: if to_row_number in from_rows:
return False # Destination within rows to be moved return False # Destination within rows to be moved
# # Remove rows from bottom to top to avoid index shifting
# for row in sorted(from_rows, reverse=True):
# self.beginRemoveRows(QModelIndex(), row, row)
# del self.playlist_rows[row]
# # At this point self.playlist_rows has been updated but the
# # underlying database has not (that's done below after
# # inserting the rows)
# self.endRemoveRows()
# # Adjust insertion point after removal
# if to_row_number > max(from_rows):
# rows_below_dest = len([r for r in from_rows if r < to_row_number])
# insertion_point = to_row_number - rows_below_dest
# else:
# insertion_point = to_row_number
# # Insert rows at the destination
# plrid_to_new_row_number: list[dict[int, int]] = []
# for offset, row_data in enumerate(rows_to_move):
# row_number = insertion_point + offset
# self.beginInsertRows(QModelIndex(), row_number, row_number)
# self.playlist_rows[row_number] = row_data
# plrid_to_new_row_number.append({row_data.playlistrow_id: row_number})
# self.endInsertRows()
# Notify model going to change # Notify model going to change
self.beginResetModel() self.beginResetModel()
# Update database # Update database
@ -884,25 +847,6 @@ class PlaylistModel(QAbstractTableModel):
# Notify model changed # Notify model changed
self.endResetModel() self.endResetModel()
# Handle the moves in row_group chunks
for row_group in row_groups:
# Tell model we will be moving rows
# See https://doc.qt.io/qt-6/qabstractitemmodel.html#beginMoveRows
# for how destination is calculated
destination = to_row_number
if to_row_number > max(row_group):
destination = to_row_number - max(row_group) + 1
super().beginMoveRows(QModelIndex(),
min(row_group),
max(row_group),
QModelIndex(),
destination
)
# Update database
repository.move_rows_within_playlist(self.playlist_id, row_group, to_row_number)
# Tell model we have finished moving rows
super().endMoveRows()
# Update display # Update display
self.refresh_data() self.refresh_data()
self.track_sequence.update() self.track_sequence.update()
@ -913,26 +857,7 @@ class PlaylistModel(QAbstractTableModel):
# Qt.ItemDataRole.DisplayRole, # Qt.ItemDataRole.DisplayRole,
# ] # ]
# self.invalidate_rows(list(row_map.keys()), roles) # self.invalidate_rows(list(row_map.keys()), roles)
return True
def begin_insert_rows(self, insert_rows: InsertRows) -> None:
"""
Prepare model to insert rows
"""
if insert_rows.playlist_id != self.playlist_id:
return
super().beginInsertRows(QModelIndex(), insert_rows.from_row, insert_rows.to_row)
def end_insert_rows(self, playlist_id: int) -> None:
"""
End insert rows
"""
if playlist_id != self.playlist_id:
return
super().endInsertRows()
@log_call @log_call
def move_rows_between_playlists( def move_rows_between_playlists(
@ -971,11 +896,10 @@ class PlaylistModel(QAbstractTableModel):
to_row_number + len(row_group) to_row_number + len(row_group)
) )
self.signals.signal_begin_insert_rows.emit(insert_rows) self.signals.signal_begin_insert_rows.emit(insert_rows)
repository.move_rows_to_playlist(from_rows=row_group, repository.move_rows(from_rows=row_group,
from_playlist_id=self.playlist_id, from_playlist_id=self.playlist_id,
to_row=to_row_number, to_row=to_row_number,
to_playlist_id=to_playlist_id to_playlist_id=to_playlist_id)
)
self.signals.signal_end_insert_rows.emit(to_playlist_id) self.signals.signal_end_insert_rows.emit(to_playlist_id)
super().endRemoveRows() super().endRemoveRows()
@ -983,6 +907,26 @@ class PlaylistModel(QAbstractTableModel):
self.track_sequence.update() self.track_sequence.update()
self.update_track_times() self.update_track_times()
def begin_insert_rows(self, insert_rows: InsertRows) -> None:
"""
Prepare model to insert rows
"""
if insert_rows.playlist_id != self.playlist_id:
return
super().beginInsertRows(QModelIndex(), insert_rows.from_row, insert_rows.to_row)
def end_insert_rows(self, playlist_id: int) -> None:
"""
End insert rows
"""
if playlist_id != self.playlist_id:
return
super().endInsertRows()
@log_call @log_call
def move_track_add_note( def move_track_add_note(
self, new_row_number: int, existing_plr: PlaylistRow, note: str self, new_row_number: int, existing_plr: PlaylistRow, note: str
@ -1004,23 +948,6 @@ class PlaylistModel(QAbstractTableModel):
self.move_rows([existing_plr.row_number], new_row_number) self.move_rows([existing_plr.row_number], new_row_number)
self.signals.resize_rows_signal.emit(self.playlist_id) self.signals.resize_rows_signal.emit(self.playlist_id)
@log_call
def move_track_to_header(
self,
header_row_number: int,
existing_rat: RowAndTrack,
note: Optional[str],
) -> None:
"""
Add the existing_rat track details to the existing header at header_row_number
"""
if existing_rat.track_id:
if note and existing_rat.note:
note += "\n" + existing_rat.note
self.add_track_to_header(header_row_number, existing_rat.track_id, note)
self.delete_rows([existing_rat.row_number])
@log_call @log_call
def obs_scene_change(self, row_number: int) -> None: def obs_scene_change(self, row_number: int) -> None:
""" """
@ -1167,17 +1094,7 @@ class PlaylistModel(QAbstractTableModel):
looking up the playlistrow_id and retrieving the row number from the database. looking up the playlistrow_id and retrieving the row number from the database.
""" """
# Check the track_sequence.next, current and previous plrs and self.track_sequence.update()
# update the row number
with db.Session() as session:
for ts in [
track_sequence.next,
track_sequence.current,
track_sequence.previous,
]:
if ts:
ts.update_playlist_and_row(session)
session.commit()
self.update_track_times() self.update_track_times()
@ -1299,7 +1216,7 @@ class PlaylistModel(QAbstractTableModel):
return len(self.playlist_rows) return len(self.playlist_rows)
def section_subtotal_header(self, rat: PlaylistRow) -> str: def section_subtotal_header(self, plr: PlaylistRow) -> str:
""" """
Process this row as subtotal within a timed section and Process this row as subtotal within a timed section and
return display text for this row return display text for this row
@ -1309,12 +1226,12 @@ class PlaylistModel(QAbstractTableModel):
unplayed_count: int = 0 unplayed_count: int = 0
duration: int = 0 duration: int = 0
if rat.row_number == 0: if plr.row_number == 0:
# Meaningless to have a subtotal on row 0 # Meaningless to have a subtotal on row 0
return Config.SUBTOTAL_ON_ROW_ZERO return Config.SUBTOTAL_ON_ROW_ZERO
# Show subtotal # Show subtotal
for row_number in range(rat.row_number - 1, -1, -1): for row_number in range(plr.row_number - 1, -1, -1):
row_rat = self.playlist_rows[row_number] row_rat = self.playlist_rows[row_number]
if self.is_header_row(row_number) or row_number == 0: if self.is_header_row(row_number) or row_number == 0:
if row_rat.note.endswith(Config.SECTION_STARTS) or row_number == 0: if row_rat.note.endswith(Config.SECTION_STARTS) or row_number == 0:
@ -1327,7 +1244,7 @@ class PlaylistModel(QAbstractTableModel):
and ( and (
row_number row_number
< self.track_sequence.current.row_number < self.track_sequence.current.row_number
< rat.row_number < plr.row_number
) )
): ):
section_end_time = ( section_end_time = (
@ -1338,7 +1255,7 @@ class PlaylistModel(QAbstractTableModel):
", section end time " ", section end time "
+ section_end_time.strftime(Config.TRACK_TIME_FORMAT) + section_end_time.strftime(Config.TRACK_TIME_FORMAT)
) )
clean_header = self.remove_section_timer_markers(rat.note) clean_header = self.remove_section_timer_markers(plr.note)
if clean_header: if clean_header:
return ( return (
f"{clean_header} [" f"{clean_header} ["
@ -1418,15 +1335,15 @@ class PlaylistModel(QAbstractTableModel):
) )
return return
rat = self.selected_rows[0] plr = self.selected_rows[0]
if rat.track_id is None: if plr.track_id is None:
raise ApplicationError(f"set_next_row: no track_id ({rat=})") raise ApplicationError(f"set_next_row: no track_id ({plr=})")
old_next_row: Optional[int] = None old_next_row: Optional[int] = 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
self.track_sequence.set_next(rat) self.track_sequence.set_next(plr)
roles = [ roles = [
Qt.ItemDataRole.BackgroundRole, Qt.ItemDataRole.BackgroundRole,
@ -1435,7 +1352,7 @@ class PlaylistModel(QAbstractTableModel):
# only invalidate required roles # only invalidate required roles
self.invalidate_row(old_next_row, roles) self.invalidate_row(old_next_row, roles)
# only invalidate required roles # only invalidate required roles
self.invalidate_row(rat.row_number, roles) self.invalidate_row(plr.row_number, roles)
self.signals.next_track_changed_signal.emit() self.signals.next_track_changed_signal.emit()
self.update_track_times() self.update_track_times()
@ -1549,7 +1466,7 @@ class PlaylistModel(QAbstractTableModel):
self.sort_by_attribute(row_numbers, "title") self.sort_by_attribute(row_numbers, "title")
def start_of_timed_section_header(self, rat: PlaylistRow) -> str: def start_of_timed_section_header(self, plr: PlaylistRow) -> str:
""" """
Process this row as the start of a timed section and Process this row as the start of a timed section and
return display text for this row return display text for this row
@ -1559,9 +1476,9 @@ class PlaylistModel(QAbstractTableModel):
unplayed_count: int = 0 unplayed_count: int = 0
duration: int = 0 duration: int = 0
clean_header = self.remove_section_timer_markers(rat.note) clean_header = self.remove_section_timer_markers(plr.note)
for row_number in range(rat.row_number + 1, len(self.playlist_rows)): for row_number in range(plr.row_number + 1, len(self.playlist_rows)):
row_rat = self.playlist_rows[row_number] row_rat = self.playlist_rows[row_number]
if self.is_header_row(row_number): if self.is_header_row(row_number):
if row_rat.note.endswith(Config.SECTION_ENDINGS): if row_rat.note.endswith(Config.SECTION_ENDINGS):
@ -1585,7 +1502,7 @@ class PlaylistModel(QAbstractTableModel):
def supportedDropActions(self) -> Qt.DropAction: def supportedDropActions(self) -> Qt.DropAction:
return Qt.DropAction.MoveAction | Qt.DropAction.CopyAction return Qt.DropAction.MoveAction | Qt.DropAction.CopyAction
def _tooltip_role(self, row: int, column: int, rat: PlaylistRow) -> str: def _tooltip_role(self, row: int, column: int, plr: PlaylistRow) -> str:
""" """
Return tooltip. Currently only used for last_played column. Return tooltip. Currently only used for last_played column.
""" """
@ -1656,20 +1573,20 @@ class PlaylistModel(QAbstractTableModel):
next_track_row = self.track_sequence.next.row_number next_track_row = self.track_sequence.next.row_number
for row_number in range(row_count): for row_number in range(row_count):
rat = 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, for
# unreadable tracks or for the current track, handled above. # unreadable tracks or for the current track, handled above.
if ( if (
rat.played plr.played
or row_number == current_track_row or row_number == current_track_row
or (rat.path and file_is_unreadable(rat.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
if self.is_header_row(row_number): if self.is_header_row(row_number):
header_time = get_embedded_time(rat.note) header_time = get_embedded_time(plr.note)
if header_time: if header_time:
next_start_time = header_time next_start_time = header_time
continue continue
@ -1680,7 +1597,7 @@ class PlaylistModel(QAbstractTableModel):
and self.track_sequence.current and self.track_sequence.current
and self.track_sequence.current.end_time and self.track_sequence.current.end_time
): ):
next_start_time = rat.set_forecast_start_time( next_start_time = plr.set_forecast_start_time(
update_rows, self.track_sequence.current.end_time update_rows, self.track_sequence.current.end_time
) )
continue continue
@ -1688,11 +1605,11 @@ class PlaylistModel(QAbstractTableModel):
# 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 (current_track_row or row_count) < row_number < (next_track_row or 0):
rat.set_forecast_start_time(update_rows, None) plr.set_forecast_start_time(update_rows, None)
continue continue
# Set start/end # Set start/end
rat.forecast_start_time = next_start_time plr.forecast_start_time = next_start_time
# 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

@ -136,7 +136,7 @@ class QuerylistModel(QAbstractTableModel):
row = index.row() row = index.row()
column = index.column() column = index.column()
# rat for playlist row data as it's used a lot # plr for playlist row data as it's used a lot
qrow = self.querylist_rows[row] qrow = self.querylist_rows[row]
# Dispatch to role-specific functions # Dispatch to role-specific functions
@ -268,7 +268,7 @@ class QuerylistModel(QAbstractTableModel):
bottom_right = self.index(row, self.columnCount() - 1) bottom_right = self.index(row, self.columnCount() - 1)
self.dataChanged.emit(top_left, bottom_right, [Qt.ItemDataRole.BackgroundRole]) self.dataChanged.emit(top_left, bottom_right, [Qt.ItemDataRole.BackgroundRole])
def _tooltip_role(self, row: int, column: int, rat: PlaylistRow) -> str | QVariant: def _tooltip_role(self, row: int, column: int, plr: PlaylistRow) -> str | QVariant:
""" """
Return tooltip. Currently only used for last_played column. Return tooltip. Currently only used for last_played column.
""" """

View File

@ -238,6 +238,24 @@ def _tracks_where(where: BinaryExpression | ColumnElement[bool]) -> list[TrackDT
return results return results
def track_with_path(path: str) -> bool:
"""
Return True if a track with passed path exists, else False
"""
with db.Session() as session:
track = (
session.execute(
select(Tracks)
.where(Tracks.path == path)
)
.scalars()
.one_or_none()
)
return track is not None
def tracks_like_artist(filter_str: str) -> list[TrackDTO]: def tracks_like_artist(filter_str: str) -> list[TrackDTO]:
""" """
Return tracks where artist is like filter Return tracks where artist is like filter
@ -399,6 +417,15 @@ def move_rows(
_check_playlist_integrity(session, to_playlist_id, fix=False) _check_playlist_integrity(session, to_playlist_id, fix=False)
def update_playdates(track_id: int) -> None:
"""
Update playdates for passed track
"""
with db.Session() as session:
_ = Playdates(session, track_id)
def update_row_numbers( def update_row_numbers(
playlist_id: int, id_to_row_number: list[dict[int, int]] playlist_id: int, id_to_row_number: list[dict[int, int]]
) -> None: ) -> None: