Rebase dev onto v2_id
This commit is contained in:
parent
a91309477b
commit
bc6a4c11cf
@ -51,8 +51,10 @@ engine = sqlalchemy.create_engine(
|
|||||||
echo=Config.DISPLAY_SQL,
|
echo=Config.DISPLAY_SQL,
|
||||||
pool_pre_ping=True)
|
pool_pre_ping=True)
|
||||||
|
|
||||||
sm: sessionmaker = sessionmaker(bind=engine)
|
# Create a Session factory
|
||||||
Session = scoped_session(sm)
|
Session = scoped_session(sessionmaker(bind=engine))
|
||||||
|
# sm: sessionmaker = sessionmaker(bind=engine) # , expire_on_commit=False)
|
||||||
|
# Session = scoped_session(sm)
|
||||||
|
|
||||||
Base: DeclarativeMeta = declarative_base()
|
Base: DeclarativeMeta = declarative_base()
|
||||||
Base.metadata.create_all(engine)
|
Base.metadata.create_all(engine)
|
||||||
@ -105,7 +107,7 @@ class NoteColours(Base):
|
|||||||
Optional["NoteColours"]:
|
Optional["NoteColours"]:
|
||||||
"""Return record identified by id, or None if not found"""
|
"""Return record identified by id, or None if not found"""
|
||||||
|
|
||||||
return session.query(NoteColours).local_filter(
|
return session.query(NoteColours).filter(
|
||||||
NoteColours.id == note_id).first()
|
NoteColours.id == note_id).first()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -116,7 +118,7 @@ class NoteColours(Base):
|
|||||||
|
|
||||||
for rec in (
|
for rec in (
|
||||||
session.query(NoteColours)
|
session.query(NoteColours)
|
||||||
.local_filter(NoteColours.enabled.is_(True))
|
.filter(NoteColours.enabled.is_(True))
|
||||||
.order_by(NoteColours.order)
|
.order_by(NoteColours.order)
|
||||||
.all()
|
.all()
|
||||||
):
|
):
|
||||||
@ -213,7 +215,7 @@ class Playdates(Base):
|
|||||||
"""Return datetime track last played or None"""
|
"""Return datetime track last played or None"""
|
||||||
|
|
||||||
last_played: Optional[Playdates] = session.query(
|
last_played: Optional[Playdates] = session.query(
|
||||||
Playdates.lastplayed).local_filter((Playdates.track_id == track_id)
|
Playdates.lastplayed).filter((Playdates.track_id == track_id)
|
||||||
).order_by(Playdates.lastplayed.desc()).first()
|
).order_by(Playdates.lastplayed.desc()).first()
|
||||||
if last_played:
|
if last_played:
|
||||||
return last_played[0]
|
return last_played[0]
|
||||||
@ -226,7 +228,7 @@ class Playdates(Base):
|
|||||||
Remove all records of track_id
|
Remove all records of track_id
|
||||||
"""
|
"""
|
||||||
|
|
||||||
session.query(Playdates).local_filter(
|
session.query(Playdates).filter(
|
||||||
Playdates.track_id == track_id,
|
Playdates.track_id == track_id,
|
||||||
).delete()
|
).delete()
|
||||||
session.commit()
|
session.commit()
|
||||||
@ -293,7 +295,7 @@ class Playlists(Base):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_by_id(cls, session: Session, playlist_id: int) -> "Playlists":
|
def get_by_id(cls, session: Session, playlist_id: int) -> "Playlists":
|
||||||
return (session.query(cls).local_filter(cls.id == playlist_id)).one()
|
return (session.query(cls).filter(cls.id == playlist_id)).one()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_closed(cls, session: Session) -> List["Playlists"]:
|
def get_closed(cls, session: Session) -> List["Playlists"]:
|
||||||
@ -301,7 +303,7 @@ class Playlists(Base):
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
session.query(cls)
|
session.query(cls)
|
||||||
.local_filter(cls.loaded.is_(False))
|
.filter(cls.loaded.is_(False))
|
||||||
.order_by(cls.last_used.desc())
|
.order_by(cls.last_used.desc())
|
||||||
).all()
|
).all()
|
||||||
|
|
||||||
@ -313,7 +315,7 @@ class Playlists(Base):
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
session.query(cls)
|
session.query(cls)
|
||||||
.local_filter(cls.loaded.is_(True))
|
.filter(cls.loaded.is_(True))
|
||||||
.order_by(cls.last_used.desc())
|
.order_by(cls.last_used.desc())
|
||||||
).all()
|
).all()
|
||||||
|
|
||||||
@ -330,7 +332,7 @@ class Playlists(Base):
|
|||||||
Remove all tracks from this playlist
|
Remove all tracks from this playlist
|
||||||
"""
|
"""
|
||||||
|
|
||||||
session.query(PlaylistTracks).local_filter(
|
session.query(PlaylistTracks).filter(
|
||||||
PlaylistTracks.playlist_id == self.id,
|
PlaylistTracks.playlist_id == self.id,
|
||||||
).delete()
|
).delete()
|
||||||
session.commit()
|
session.commit()
|
||||||
@ -338,7 +340,7 @@ class Playlists(Base):
|
|||||||
def remove_track(self, session: Session, row: int) -> None:
|
def remove_track(self, session: Session, row: int) -> None:
|
||||||
DEBUG(f"Playlist.remove_track({self.id=}, {row=})")
|
DEBUG(f"Playlist.remove_track({self.id=}, {row=})")
|
||||||
|
|
||||||
session.query(PlaylistTracks).local_filter(
|
session.query(PlaylistTracks).filter(
|
||||||
PlaylistTracks.playlist_id == self.id,
|
PlaylistTracks.playlist_id == self.id,
|
||||||
PlaylistTracks.row == row
|
PlaylistTracks.row == row
|
||||||
).delete()
|
).delete()
|
||||||
@ -389,7 +391,7 @@ class PlaylistTracks(Base):
|
|||||||
|
|
||||||
new_row: int
|
new_row: int
|
||||||
max_row: Optional[int] = session.query(
|
max_row: Optional[int] = session.query(
|
||||||
func.max(PlaylistTracks.row)).local_filter(
|
func.max(PlaylistTracks.row)).filter(
|
||||||
PlaylistTracks.playlist_id == to_playlist_id).scalar()
|
PlaylistTracks.playlist_id == to_playlist_id).scalar()
|
||||||
if max_row is None:
|
if max_row is None:
|
||||||
# Destination playlist is empty; use row 0
|
# Destination playlist is empty; use row 0
|
||||||
@ -398,7 +400,7 @@ class PlaylistTracks(Base):
|
|||||||
# Destination playlist has tracks; add to end
|
# Destination playlist has tracks; add to end
|
||||||
new_row = max_row + 1
|
new_row = max_row + 1
|
||||||
try:
|
try:
|
||||||
record: PlaylistTracks = session.query(PlaylistTracks).local_filter(
|
record: PlaylistTracks = session.query(PlaylistTracks).filter(
|
||||||
PlaylistTracks.playlist_id == from_playlist_id,
|
PlaylistTracks.playlist_id == from_playlist_id,
|
||||||
PlaylistTracks.row == row).one()
|
PlaylistTracks.row == row).one()
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
@ -446,7 +448,7 @@ class Settings(Base):
|
|||||||
int_setting: Settings
|
int_setting: Settings
|
||||||
|
|
||||||
try:
|
try:
|
||||||
int_setting = session.query(cls).local_filter(
|
int_setting = session.query(cls).filter(
|
||||||
cls.name == name).one()
|
cls.name == name).one()
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
int_setting = Settings()
|
int_setting = Settings()
|
||||||
@ -516,7 +518,7 @@ class Tracks(Base):
|
|||||||
DEBUG(f"Tracks.get_or_create({path=})")
|
DEBUG(f"Tracks.get_or_create({path=})")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
track = session.query(cls).local_filter(cls.path == path).one()
|
track = session.query(cls).filter(cls.path == path).one()
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
track = Tracks(session, path)
|
track = Tracks(session, path)
|
||||||
|
|
||||||
@ -533,7 +535,7 @@ class Tracks(Base):
|
|||||||
|
|
||||||
DEBUG(f"Tracks.get_track_from_filename({filename=})")
|
DEBUG(f"Tracks.get_track_from_filename({filename=})")
|
||||||
try:
|
try:
|
||||||
track = session.query(Tracks).local_filter(Tracks.path.ilike(
|
track = session.query(Tracks).filter(Tracks.path.ilike(
|
||||||
f'%{os.path.sep}{filename}')).one()
|
f'%{os.path.sep}{filename}')).one()
|
||||||
return track
|
return track
|
||||||
except (NoResultFound, MultipleResultsFound):
|
except (NoResultFound, MultipleResultsFound):
|
||||||
@ -547,7 +549,7 @@ class Tracks(Base):
|
|||||||
|
|
||||||
DEBUG(f"Tracks.get_track_from_path({path=})")
|
DEBUG(f"Tracks.get_track_from_path({path=})")
|
||||||
|
|
||||||
return session.query(Tracks).local_filter(Tracks.path == path).first()
|
return session.query(Tracks).filter(Tracks.path == path).first()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_by_id(cls, session: Session, track_id: int) -> Optional["Tracks"]:
|
def get_by_id(cls, session: Session, track_id: int) -> Optional["Tracks"]:
|
||||||
@ -555,7 +557,7 @@ class Tracks(Base):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
DEBUG(f"Tracks.get_track(track_id={track_id})")
|
DEBUG(f"Tracks.get_track(track_id={track_id})")
|
||||||
track = session.query(Tracks).local_filter(Tracks.id == track_id).one()
|
track = session.query(Tracks).filter(Tracks.id == track_id).one()
|
||||||
return track
|
return track
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
ERROR(f"get_track({track_id}): not found")
|
ERROR(f"get_track({track_id}): not found")
|
||||||
@ -584,7 +586,7 @@ class Tracks(Base):
|
|||||||
DEBUG(f"Tracks.remove_path({path=})")
|
DEBUG(f"Tracks.remove_path({path=})")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
session.query(Tracks).local_filter(Tracks.path == path).delete()
|
session.query(Tracks).filter(Tracks.path == path).delete()
|
||||||
session.commit()
|
session.commit()
|
||||||
except IntegrityError as exception:
|
except IntegrityError as exception:
|
||||||
ERROR(f"Can't remove track with {path=} ({exception=})")
|
ERROR(f"Can't remove track with {path=} ({exception=})")
|
||||||
@ -594,7 +596,7 @@ class Tracks(Base):
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
session.query(cls)
|
session.query(cls)
|
||||||
.local_filter(cls.artist.ilike(f"%{text}%"))
|
.filter(cls.artist.ilike(f"%{text}%"))
|
||||||
.order_by(cls.title)
|
.order_by(cls.title)
|
||||||
).all()
|
).all()
|
||||||
|
|
||||||
@ -602,7 +604,7 @@ class Tracks(Base):
|
|||||||
def search_titles(cls, session: Session, text: str) -> List["Tracks"]:
|
def search_titles(cls, session: Session, text: str) -> List["Tracks"]:
|
||||||
return (
|
return (
|
||||||
session.query(cls)
|
session.query(cls)
|
||||||
.local_filter(cls.title.ilike(f"%{text}%"))
|
.filter(cls.title.ilike(f"%{text}%"))
|
||||||
.order_by(cls.title)
|
.order_by(cls.title)
|
||||||
).all()
|
).all()
|
||||||
|
|
||||||
|
|||||||
@ -86,6 +86,8 @@ class Music:
|
|||||||
p.stop()
|
p.stop()
|
||||||
DEBUG(f"Releasing player {p=}", True)
|
DEBUG(f"Releasing player {p=}", True)
|
||||||
p.release()
|
p.release()
|
||||||
|
# Ensure we don't reference player after release
|
||||||
|
p = None
|
||||||
|
|
||||||
self.fading -= 1
|
self.fading -= 1
|
||||||
|
|
||||||
|
|||||||
@ -412,7 +412,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
rows = []
|
rows = []
|
||||||
for (row, track) in (
|
for (row, track) in (
|
||||||
self.visible_playlist_tab().get_selected_rows_and_tracks()
|
self.visible_playlist_tab().get_selected_rows_and_tracks(
|
||||||
|
session)
|
||||||
):
|
):
|
||||||
rows.append(row)
|
rows.append(row)
|
||||||
if destination_visible_playlist_tab:
|
if destination_visible_playlist_tab:
|
||||||
@ -436,8 +437,14 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
Ensure we have info tabs for next and current track titles
|
Ensure we have info tabs for next and current track titles
|
||||||
"""
|
"""
|
||||||
|
|
||||||
title_list: List[str, str] = [self.previous_track.title,
|
title_list: List[str] = []
|
||||||
self.current_track.title]
|
|
||||||
|
if self.previous_track:
|
||||||
|
title_list.append(self.previous_track.title)
|
||||||
|
if self.current_track:
|
||||||
|
title_list.append(self.current_track.title)
|
||||||
|
if self.next_track:
|
||||||
|
title_list.append(self.next_track.title)
|
||||||
|
|
||||||
for title in title_list:
|
for title in title_list:
|
||||||
if title in self.info_tabs.keys():
|
if title in self.info_tabs.keys():
|
||||||
@ -533,7 +540,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
Playdates(session, self.current_track)
|
Playdates(session, self.current_track)
|
||||||
|
|
||||||
# Tell playlist track is now playing
|
# Tell playlist track is now playing
|
||||||
self.current_track_playlist_tab.play_started()
|
self.current_track_playlist_tab.play_started(session)
|
||||||
|
|
||||||
# Disable play next controls
|
# Disable play next controls
|
||||||
self.disable_play_next_controls()
|
self.disable_play_next_controls()
|
||||||
@ -680,11 +687,12 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
with Session() as session:
|
||||||
# Clear next track if on another tab
|
# Clear next track if on another tab
|
||||||
if self.next_track_playlist_tab != playlist_tab:
|
if self.next_track_playlist_tab != playlist_tab:
|
||||||
# We need to reset the ex-next-track playlist
|
# We need to reset the ex-next-track playlist
|
||||||
if self.next_track_playlist_tab:
|
if self.next_track_playlist_tab:
|
||||||
self.next_track_playlist_tab.clear_next()
|
self.next_track_playlist_tab.clear_next(session)
|
||||||
|
|
||||||
# Reset tab colour if on other tab
|
# Reset tab colour if on other tab
|
||||||
if (self.next_track_playlist_tab !=
|
if (self.next_track_playlist_tab !=
|
||||||
@ -739,7 +747,10 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
# If track is playing, update track clocks time and colours
|
# If track is playing, update track clocks time and colours
|
||||||
if self.music.player and self.music.playing():
|
if self.music.player and self.music.playing():
|
||||||
|
with Session() as session:
|
||||||
self.playing = True
|
self.playing = True
|
||||||
|
if self.current_track not in session:
|
||||||
|
session.add(self.current_track)
|
||||||
playtime: int = self.music.get_playtime()
|
playtime: int = self.music.get_playtime()
|
||||||
time_to_fade: int = (self.current_track.fade_at - playtime)
|
time_to_fade: int = (self.current_track.fade_at - playtime)
|
||||||
time_to_silence: int = (self.current_track.silence_at - playtime)
|
time_to_silence: int = (self.current_track.silence_at - playtime)
|
||||||
|
|||||||
136
app/playlists.py
136
app/playlists.py
@ -16,6 +16,7 @@ from PyQt5.QtWidgets import (
|
|||||||
QTableWidgetItem,
|
QTableWidgetItem,
|
||||||
)
|
)
|
||||||
from sqlalchemy import inspect
|
from sqlalchemy import inspect
|
||||||
|
from sqlalchemy.orm.exc import DetachedInstanceError
|
||||||
|
|
||||||
import helpers
|
import helpers
|
||||||
import os
|
import os
|
||||||
@ -192,9 +193,9 @@ class PlaylistTab(QTableWidget):
|
|||||||
f"Moved row(s) {rows} to become row {drop_row}"
|
f"Moved row(s) {rows} to become row {drop_row}"
|
||||||
)
|
)
|
||||||
|
|
||||||
with Session() as session:
|
with Session() as session: # checked
|
||||||
self.save_playlist(session)
|
self.save_playlist(session)
|
||||||
self.update_display()
|
self.update_display(session)
|
||||||
|
|
||||||
def edit(self, index, trigger, event): # review
|
def edit(self, index, trigger, event): # review
|
||||||
result = super(PlaylistTab, self).edit(index, trigger, event)
|
result = super(PlaylistTab, self).edit(index, trigger, event)
|
||||||
@ -249,7 +250,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
def closeEvent(self, event) -> None:
|
def closeEvent(self, event) -> None:
|
||||||
"""Save column widths"""
|
"""Save column widths"""
|
||||||
|
|
||||||
with Session() as session:
|
with Session() as session: # checked
|
||||||
for column in range(self.columnCount()):
|
for column in range(self.columnCount()):
|
||||||
width = self.columnWidth(column)
|
width = self.columnWidth(column)
|
||||||
name = f"playlist_col_{str(column)}_width"
|
name = f"playlist_col_{str(column)}_width"
|
||||||
@ -259,11 +260,11 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
event.accept()
|
event.accept()
|
||||||
|
|
||||||
def clear_next(self) -> None:
|
def clear_next(self, session) -> None:
|
||||||
"""Clear next track"""
|
"""Clear next track"""
|
||||||
|
|
||||||
self._meta_clear_next()
|
self._meta_clear_next()
|
||||||
self.update_display()
|
self.update_display(session)
|
||||||
|
|
||||||
def create_note(self) -> None:
|
def create_note(self) -> None:
|
||||||
"""
|
"""
|
||||||
@ -287,7 +288,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
with Session() as session:
|
with Session() as session:
|
||||||
note: Notes = Notes(
|
note: Notes = Notes(
|
||||||
session, self.playlist.id, row, dlg.textValue())
|
session, self.playlist.id, row, dlg.textValue())
|
||||||
self._insert_note(session, note, row, True)
|
self._insert_note(session, note, row, True) # checked
|
||||||
|
|
||||||
def get_selected_row(self) -> Optional[int]:
|
def get_selected_row(self) -> Optional[int]:
|
||||||
"""Return row number of first selected row, or None if none selected"""
|
"""Return row number of first selected row, or None if none selected"""
|
||||||
@ -297,7 +298,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
else:
|
else:
|
||||||
return self.selectionModel().selectedRows()[0].row()
|
return self.selectionModel().selectedRows()[0].row()
|
||||||
|
|
||||||
def get_selected_rows_and_tracks(self) \
|
def get_selected_rows_and_tracks(self, session: Session) \
|
||||||
-> Optional[List[Tuple[int, Tracks]]]:
|
-> Optional[List[Tuple[int, Tracks]]]:
|
||||||
"""Return a list of selected (row-number, track) tuples"""
|
"""Return a list of selected (row-number, track) tuples"""
|
||||||
|
|
||||||
@ -307,7 +308,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
result = []
|
result = []
|
||||||
|
|
||||||
for row in [r.row() for r in self.selectionModel().selectedRows()]:
|
for row in [r.row() for r in self.selectionModel().selectedRows()]:
|
||||||
result.append((row, self._get_row_object(row)))
|
result.append((row, self._get_row_object(row, session)))
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -378,14 +379,14 @@ class PlaylistTab(QTableWidget):
|
|||||||
self._set_row_content(row, track)
|
self._set_row_content(row, track)
|
||||||
|
|
||||||
# Mark track if file is unreadable
|
# Mark track if file is unreadable
|
||||||
if not self._track_is_readable(track):
|
if not self._file_is_readable(track.path):
|
||||||
self._meta_set_unreadable(row)
|
self._meta_set_unreadable(row)
|
||||||
# Scroll to new row
|
# Scroll to new row
|
||||||
self.scrollToItem(title_item, QAbstractItemView.PositionAtCenter)
|
self.scrollToItem(title_item, QAbstractItemView.PositionAtCenter)
|
||||||
|
|
||||||
if repaint:
|
if repaint:
|
||||||
self.save_playlist(session)
|
self.save_playlist(session)
|
||||||
self.update_display(clear_selection=False)
|
self.update_display(session, clear_selection=False)
|
||||||
|
|
||||||
def remove_rows(self, rows) -> None:
|
def remove_rows(self, rows) -> None:
|
||||||
"""Remove rows passed in rows list"""
|
"""Remove rows passed in rows list"""
|
||||||
@ -398,10 +399,9 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
self.save_playlist(session)
|
self.save_playlist(session)
|
||||||
|
self.update_display(session)
|
||||||
|
|
||||||
self.update_display()
|
def play_started(self, session: Session) -> None:
|
||||||
|
|
||||||
def play_started(self) -> None:
|
|
||||||
"""
|
"""
|
||||||
Notification from musicmuster that track has started playing.
|
Notification from musicmuster that track has started playing.
|
||||||
|
|
||||||
@ -425,16 +425,17 @@ class PlaylistTab(QTableWidget):
|
|||||||
self._meta_set_played(current_row)
|
self._meta_set_played(current_row)
|
||||||
|
|
||||||
# Scroll to put current track in middle
|
# Scroll to put current track in middle
|
||||||
scroll_to = self.item(current_row, self.COL_INDEX)
|
scroll_to = self.item(current_row, self.COL_MSS)
|
||||||
self.scrollToItem(scroll_to, QAbstractItemView.PositionAtCenter)
|
self.scrollToItem(scroll_to, QAbstractItemView.PositionAtCenter)
|
||||||
|
|
||||||
# Set next track
|
# Set next track
|
||||||
search_from = current_row + 1
|
search_from = current_row + 1
|
||||||
next_row = self._find_next_track_row(search_from)
|
next_row = self._find_next_track_row(search_from)
|
||||||
|
if next_row:
|
||||||
self._set_next(next_row)
|
self._set_next(next_row)
|
||||||
|
|
||||||
# Update display
|
# Update display
|
||||||
self.update_display()
|
self.update_display(session)
|
||||||
|
|
||||||
def play_stopped(self) -> None:
|
def play_stopped(self) -> None:
|
||||||
"""
|
"""
|
||||||
@ -448,7 +449,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
self._meta_clear_current()
|
self._meta_clear_current()
|
||||||
self.current_track_start_time = None
|
self.current_track_start_time = None
|
||||||
self.update_display()
|
|
||||||
|
|
||||||
def save_playlist(self, session) -> None:
|
def save_playlist(self, session) -> None:
|
||||||
"""
|
"""
|
||||||
@ -474,7 +474,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
# PlaylistTab
|
# PlaylistTab
|
||||||
for row in notes_rows:
|
for row in notes_rows:
|
||||||
note: Notes = self._get_row_object(row)
|
note: Notes = self._get_row_object(row, session)
|
||||||
session.add(note)
|
session.add(note)
|
||||||
playlist_notes[note.id] = note
|
playlist_notes[note.id] = note
|
||||||
|
|
||||||
@ -604,9 +604,9 @@ class PlaylistTab(QTableWidget):
|
|||||||
if row is None:
|
if row is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
self.set_next_track(row)
|
self._set_next(row)
|
||||||
|
|
||||||
def update_display(self, clear_selection: bool = True) -> None:
|
def update_display(self, session, clear_selection: bool = True) -> None:
|
||||||
"""
|
"""
|
||||||
Set row colours, fonts, etc
|
Set row colours, fonts, etc
|
||||||
|
|
||||||
@ -618,6 +618,8 @@ class PlaylistTab(QTableWidget):
|
|||||||
- Show unplayed tracks in bold
|
- Show unplayed tracks in bold
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if self.playlist not in session:
|
||||||
|
session.add(self.playlist)
|
||||||
DEBUG(f"playlist. update_display [{self.playlist=}]")
|
DEBUG(f"playlist. update_display [{self.playlist=}]")
|
||||||
|
|
||||||
# Clear selection if required
|
# Clear selection if required
|
||||||
@ -654,7 +656,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
if not start_times_row:
|
if not start_times_row:
|
||||||
start_times_row = 0
|
start_times_row = 0
|
||||||
|
|
||||||
with Session() as session:
|
|
||||||
# Cycle through all rows
|
# Cycle through all rows
|
||||||
for row in range(self.rowCount()):
|
for row in range(self.rowCount()):
|
||||||
|
|
||||||
@ -696,7 +697,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
last_played_str)
|
last_played_str)
|
||||||
|
|
||||||
# Calculate next_start_time
|
# Calculate next_start_time
|
||||||
track = self._get_row_object(row)
|
track = self._get_row_object(row, session)
|
||||||
next_start_time = self._calculate_track_end_time(
|
next_start_time = self._calculate_track_end_time(
|
||||||
track, self.current_track_start_time)
|
track, self.current_track_start_time)
|
||||||
|
|
||||||
@ -724,7 +725,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
self._set_row_start_time(row, start_time)
|
self._set_row_start_time(row, start_time)
|
||||||
|
|
||||||
# Set end time
|
# Set end time
|
||||||
track = self._get_row_object(row)
|
track = self._get_row_object(row, session)
|
||||||
next_start_time = self._calculate_track_end_time(
|
next_start_time = self._calculate_track_end_time(
|
||||||
track, start_time)
|
track, start_time)
|
||||||
self._set_row_end_time(row, next_start_time)
|
self._set_row_end_time(row, next_start_time)
|
||||||
@ -738,7 +739,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
# This is a track row other than next or current
|
# This is a track row other than next or current
|
||||||
track = self._get_row_object(row)
|
track = self._get_row_object(row, session)
|
||||||
if row in played:
|
if row in played:
|
||||||
# Played today, so update last played column
|
# Played today, so update last played column
|
||||||
last_playedtime = Playdates.last_played(
|
last_playedtime = Playdates.last_played(
|
||||||
@ -779,7 +780,8 @@ class PlaylistTab(QTableWidget):
|
|||||||
if row in self._meta_get_notes():
|
if row in self._meta_get_notes():
|
||||||
return None
|
return None
|
||||||
|
|
||||||
track: Tracks = self._get_row_object(row)
|
with Session() as session:
|
||||||
|
track: Tracks = self._get_row_object(row, session)
|
||||||
open_in_audacity(track.path)
|
open_in_audacity(track.path)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -811,7 +813,8 @@ class PlaylistTab(QTableWidget):
|
|||||||
if row in self._meta_get_notes():
|
if row in self._meta_get_notes():
|
||||||
return None
|
return None
|
||||||
|
|
||||||
track: Optional[Tracks] = self._get_row_object(row)
|
with Session() as session:
|
||||||
|
track: Optional[Tracks] = self._get_row_object(row, session)
|
||||||
if track:
|
if track:
|
||||||
cb: QApplication.clipboard = QApplication.clipboard()
|
cb: QApplication.clipboard = QApplication.clipboard()
|
||||||
cb.clear(mode=cb.Clipboard)
|
cb.clear(mode=cb.Clipboard)
|
||||||
@ -828,8 +831,9 @@ class PlaylistTab(QTableWidget):
|
|||||||
new_text: str = self.item(row, column).text()
|
new_text: str = self.item(row, column).text()
|
||||||
DEBUG(f"_cell_changed({row=}, {column=}, {new_text=}")
|
DEBUG(f"_cell_changed({row=}, {column=}, {new_text=}")
|
||||||
|
|
||||||
row_object: Union[Tracks, Notes] = self._get_row_object(row)
|
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
|
row_object: Union[Tracks, Notes] = self._get_row_object(
|
||||||
|
row, session)
|
||||||
if row in self._meta_get_notes():
|
if row in self._meta_get_notes():
|
||||||
# Save change to database
|
# Save change to database
|
||||||
DEBUG(f"Notes.update_note: saving new note text '{new_text=}'")
|
DEBUG(f"Notes.update_note: saving new note text '{new_text=}'")
|
||||||
@ -866,7 +870,8 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
# update_display to update start times, such as when a note has
|
# update_display to update start times, such as when a note has
|
||||||
# been edited
|
# been edited
|
||||||
self.update_display()
|
with Session() as session:
|
||||||
|
self.update_display(session)
|
||||||
|
|
||||||
self.parent.enable_play_next_controls()
|
self.parent.enable_play_next_controls()
|
||||||
|
|
||||||
@ -911,7 +916,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
# delete in reverse row order so row numbers don't
|
# delete in reverse row order so row numbers don't
|
||||||
# change
|
# change
|
||||||
for row in sorted(rows_to_delete, reverse=True):
|
for row in sorted(rows_to_delete, reverse=True):
|
||||||
row_object = self._get_row_object(row)
|
row_object = self._get_row_object(row, session)
|
||||||
if row in note_rows:
|
if row in note_rows:
|
||||||
row_object.delete_note(session)
|
row_object.delete_note(session)
|
||||||
else:
|
else:
|
||||||
@ -919,7 +924,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
self.removeRow(row)
|
self.removeRow(row)
|
||||||
|
|
||||||
self.save_playlist(session)
|
self.save_playlist(session)
|
||||||
self.update_display()
|
self.update_display(session)
|
||||||
|
|
||||||
def _drop_on(self, event): # review
|
def _drop_on(self, event): # review
|
||||||
index = self.indexAt(event.pos())
|
index = self.indexAt(event.pos())
|
||||||
@ -958,10 +963,14 @@ class PlaylistTab(QTableWidget):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_row_object(self, row: int) -> Union[Tracks, Notes]:
|
def _get_row_object(self, row: int, session: Session) \
|
||||||
|
-> Union[Tracks, Notes]:
|
||||||
"""Return content associated with this row"""
|
"""Return content associated with this row"""
|
||||||
|
|
||||||
return self.item(row, self.COL_USERDATA).data(self.CONTENT_OBJECT)
|
obj = self.item(row, self.COL_USERDATA).data(self.CONTENT_OBJECT)
|
||||||
|
if obj not in session:
|
||||||
|
session.add(obj)
|
||||||
|
return obj
|
||||||
|
|
||||||
def _get_row_start_time(self, row: int) -> Optional[datetime]:
|
def _get_row_start_time(self, row: int) -> Optional[datetime]:
|
||||||
try:
|
try:
|
||||||
@ -980,7 +989,9 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
txt: str
|
txt: str
|
||||||
|
|
||||||
row_object: Union[Tracks, Notes] = self._get_row_object(row)
|
with Session() as session:
|
||||||
|
row_object: Union[Tracks, Notes] = self._get_row_object(
|
||||||
|
row, session)
|
||||||
if row in self._meta_get_notes():
|
if row in self._meta_get_notes():
|
||||||
txt = row_object.note
|
txt = row_object.note
|
||||||
else:
|
else:
|
||||||
@ -1040,7 +1051,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
if repaint:
|
if repaint:
|
||||||
self.save_playlist(session)
|
self.save_playlist(session)
|
||||||
self.update_display(clear_selection=False)
|
self.update_display(session, clear_selection=False)
|
||||||
|
|
||||||
def _is_below(self, pos, index): # review
|
def _is_below(self, pos, index): # review
|
||||||
rect = self.visualRect(index)
|
rect = self.visualRect(index)
|
||||||
@ -1110,6 +1121,13 @@ class PlaylistTab(QTableWidget):
|
|||||||
current_row: Optional[int] = self._meta_get_current()
|
current_row: Optional[int] = self._meta_get_current()
|
||||||
if current_row is not None:
|
if current_row is not None:
|
||||||
self._meta_clear_attribute(current_row, RowMeta.CURRENT)
|
self._meta_clear_attribute(current_row, RowMeta.CURRENT)
|
||||||
|
# Reset row colour
|
||||||
|
if current_row % 2:
|
||||||
|
self._set_row_colour(
|
||||||
|
current_row, QColor(Config.COLOUR_ODD_PLAYLIST))
|
||||||
|
else:
|
||||||
|
self._set_row_colour(
|
||||||
|
current_row, QColor(Config.COLOUR_EVEN_PLAYLIST))
|
||||||
|
|
||||||
def _meta_clear_next(self) -> None:
|
def _meta_clear_next(self) -> None:
|
||||||
"""
|
"""
|
||||||
@ -1274,7 +1292,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
# We possibly don't need to save the playlist here, but row
|
# We possibly don't need to save the playlist here, but row
|
||||||
# numbers may have changed during population, and it's cheap to do
|
# numbers may have changed during population, and it's cheap to do
|
||||||
self.save_playlist(session)
|
self.save_playlist(session)
|
||||||
self.update_display()
|
self.update_display(session)
|
||||||
|
|
||||||
def _rescan(self, row: int) -> None:
|
def _rescan(self, row: int) -> None:
|
||||||
"""
|
"""
|
||||||
@ -1287,11 +1305,11 @@ class PlaylistTab(QTableWidget):
|
|||||||
if row in self._meta_get_notes():
|
if row in self._meta_get_notes():
|
||||||
return None
|
return None
|
||||||
|
|
||||||
track: Tracks = self._get_row_object(row)
|
|
||||||
if track:
|
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
|
track: Tracks = self._get_row_object(row, session)
|
||||||
|
if track:
|
||||||
track.rescan(session)
|
track.rescan(session)
|
||||||
self._update_row(row, track)
|
self._update_row(session, row, track)
|
||||||
|
|
||||||
def _select_event(self) -> None:
|
def _select_event(self) -> None:
|
||||||
"""
|
"""
|
||||||
@ -1304,7 +1322,11 @@ class PlaylistTab(QTableWidget):
|
|||||||
track_rows = list(row_set - note_row_set)
|
track_rows = list(row_set - note_row_set)
|
||||||
tracks: List[Tracks]
|
tracks: List[Tracks]
|
||||||
|
|
||||||
tracks = [self._get_row_object(row) for row in track_rows]
|
with Session() as session: # checked
|
||||||
|
tracks = [self._get_row_object(row, session) for row in track_rows]
|
||||||
|
for track in tracks:
|
||||||
|
if track not in session:
|
||||||
|
session.add(track)
|
||||||
ms: int = sum([track.duration for track in tracks])
|
ms: int = sum([track.duration for track in tracks])
|
||||||
|
|
||||||
# Only paint message if there are selected track rows
|
# Only paint message if there are selected track rows
|
||||||
@ -1340,15 +1362,16 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
DEBUG(f"_set_next({row=})")
|
DEBUG(f"_set_next({row=})")
|
||||||
|
|
||||||
|
with Session() as session:
|
||||||
# Check row is a track row
|
# Check row is a track row
|
||||||
if row in self._meta_get_notes():
|
if row in self._meta_get_notes():
|
||||||
return None
|
return None
|
||||||
track: Tracks = self._get_row_object(row)
|
track: Tracks = self._get_row_object(row, session)
|
||||||
if not track:
|
if not track:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Check track is readable
|
# Check track is readable
|
||||||
if not self._track_is_readable(track):
|
if not self._file_is_readable(track.path):
|
||||||
self._meta_set_unreadable(row)
|
self._meta_set_unreadable(row)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -1359,7 +1382,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
self.parent.this_is_the_next_track(self, track)
|
self.parent.this_is_the_next_track(self, track)
|
||||||
|
|
||||||
# Update display
|
# Update display
|
||||||
self.update_display()
|
self.update_display(session)
|
||||||
|
|
||||||
def _set_row_bold(self, row: int, bold: bool = True) -> None:
|
def _set_row_bold(self, row: int, bold: bool = True) -> None:
|
||||||
"""Make row bold (bold=True) or not bold"""
|
"""Make row bold (bold=True) or not bold"""
|
||||||
@ -1415,23 +1438,42 @@ class PlaylistTab(QTableWidget):
|
|||||||
item: QTableWidgetItem = QTableWidgetItem(time_str)
|
item: QTableWidgetItem = QTableWidgetItem(time_str)
|
||||||
self.setItem(row, self.COL_START_TIME, item)
|
self.setItem(row, self.COL_START_TIME, item)
|
||||||
|
|
||||||
def _track_path_is_readable(self, track_id):
|
def _select_tracks(self, played: bool) -> None:
|
||||||
|
"""
|
||||||
|
Select all played (played=True) or unplayed (played=False)
|
||||||
|
tracks in playlist
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Need to allow multiple rows to be selected
|
||||||
|
self.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
|
||||||
|
notes_rows: List[int] = self._meta_get_notes()
|
||||||
|
self.clearSelection()
|
||||||
|
|
||||||
|
played_rows: List[int] = self._meta_get_played()
|
||||||
|
for row in range(self.rowCount()):
|
||||||
|
if row in notes_rows:
|
||||||
|
continue
|
||||||
|
if row in played_rows == played:
|
||||||
|
self.selectRow(row)
|
||||||
|
|
||||||
|
# Reset extended selection
|
||||||
|
self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _file_is_readable(path: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Returns True if track path is readable, else False
|
Returns True if track path is readable, else False
|
||||||
|
|
||||||
vlc cannot read files with a colon in the path
|
vlc cannot read files with a colon in the path
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with Session() as session:
|
|
||||||
path = Tracks.get_path(session, track_id)
|
|
||||||
|
|
||||||
if os.access(path, os.R_OK):
|
if os.access(path, os.R_OK):
|
||||||
if ':' not in path:
|
if ':' not in path:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _update_row(self, row: int, track: Tracks) -> None:
|
def _update_row(self, session, row: int, track: Tracks) -> None:
|
||||||
"""
|
"""
|
||||||
Update the passed row with info from the passed track.
|
Update the passed row with info from the passed track.
|
||||||
"""
|
"""
|
||||||
@ -1454,4 +1496,4 @@ class PlaylistTab(QTableWidget):
|
|||||||
item_duration: QTableWidgetItem = self.item(row, self.COL_DURATION)
|
item_duration: QTableWidgetItem = self.item(row, self.COL_DURATION)
|
||||||
item_duration.setText(helpers.ms_to_mmss(track.duration))
|
item_duration.setText(helpers.ms_to_mmss(track.duration))
|
||||||
|
|
||||||
self.update_display()
|
self.update_display(session)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user