From 8f2ab98be0671a74d1753dce02784471dff35bf3 Mon Sep 17 00:00:00 2001 From: Keith Edmunds Date: Sat, 18 Nov 2023 14:29:52 +0000 Subject: [PATCH] Fix create playlist from template and tab handlding Tab restore code rewritten. --- app/models.py | 48 ++++----------- app/musicmuster.py | 51 +++++++--------- app/playlistmodel.py | 3 + ...bb2c572e1e5_add_open_field_to_playlists.py | 60 +++++++++++++++++++ 4 files changed, 98 insertions(+), 64 deletions(-) create mode 100644 migrations/versions/5bb2c572e1e5_add_open_field_to_playlists.py diff --git a/app/models.py b/app/models.py index 918288d..229eeb8 100644 --- a/app/models.py +++ b/app/models.py @@ -25,7 +25,6 @@ from sqlalchemy import ( from sqlalchemy.orm import ( DeclarativeBase, joinedload, - lazyload, Mapped, mapped_column, relationship, @@ -217,7 +216,8 @@ class Playlists(Base): id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) name: Mapped[str] = mapped_column(String(32), unique=True) last_used: Mapped[Optional[datetime]] = mapped_column(DateTime, default=None) - tab: Mapped[Optional[int]] = mapped_column(default=None, unique=True) + tab: Mapped[Optional[int]] = mapped_column(default=None) + open: Mapped[bool] = mapped_column(default=False) is_template: Mapped[bool] = mapped_column(default=False) deleted: Mapped[bool] = mapped_column(default=False) rows: Mapped[List["PlaylistRows"]] = relationship( @@ -230,7 +230,7 @@ class Playlists(Base): def __repr__(self) -> str: return ( f"" + f"is_templatee={self.is_template}, open={self.open}>" ) def __init__(self, session: scoped_session, name: str): @@ -238,19 +238,10 @@ class Playlists(Base): session.add(self) session.flush() - def close(self, session: scoped_session) -> None: + def close(self) -> None: """Mark playlist as unloaded""" - closed_idx = self.tab - self.tab = None - - # Closing this tab will mean all higher-number tabs have moved - # down by one - session.execute( - update(Playlists) - .where(Playlists.tab > closed_idx) - .values(tab=Playlists.tab - 1) - ) + self.open = False @classmethod def create_playlist_from_template( @@ -283,7 +274,7 @@ class Playlists(Base): return session.scalars( select(cls) .filter(cls.is_template.is_(False)) - .order_by(cls.tab.desc(), cls.last_used.desc()) + .order_by(cls.last_used.desc()) ).all() @classmethod @@ -301,7 +292,7 @@ class Playlists(Base): return session.scalars( select(cls) .filter( - cls.tab.is_(None), + cls.open.is_(False), cls.is_template.is_(False), cls.deleted.is_(False), ) @@ -311,32 +302,18 @@ class Playlists(Base): @classmethod def get_open(cls, session: scoped_session) -> Sequence[Optional["Playlists"]]: """ - Return a list of loaded playlists ordered by tab order. + Return a list of loaded playlists ordered by tab. """ return session.scalars( - select(cls).where(cls.tab.is_not(None)).order_by(cls.tab) + select(cls).where(cls.open.is_(True)) + .order_by(cls.tab) ).all() - def mark_open(self, session: scoped_session, tab_index: int) -> None: + def mark_open(self) -> None: """Mark playlist as loaded and used now""" - self.tab = tab_index - self.last_used = datetime.now() - - @staticmethod - def move_tab(session: scoped_session, frm: int, to: int) -> None: - """Move tabs""" - - row_frm = session.execute(select(Playlists).filter_by(tab=frm)).scalar_one() - - row_to = session.execute(select(Playlists).filter_by(tab=to)).scalar_one() - - row_frm.tab = None - row_to.tab = None - session.commit() - row_to.tab = frm - row_frm.tab = to + self.open = True def rename(self, session: scoped_session, new_name: str) -> None: """ @@ -684,7 +661,6 @@ class Settings(Base): f_string: Mapped[Optional[str]] = mapped_column(String(128), default=None) def __repr__(self) -> str: - value = self.f_datetime or self.f_int or self.f_string return ( f"" diff --git a/app/musicmuster.py b/app/musicmuster.py index f1eb13b..5ef3f83 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -442,6 +442,14 @@ class Window(QMainWindow, Ui_MainWindow): if record.f_int != splitter_bottom: record.update(session, {"f_int": splitter_bottom}) + # Save tab number of open playlists + for idx in range(self.tabPlaylist.count()): + playlist_id = self.tabPlaylist.widget(idx).playlist_id + playlist = session.get(Playlists, playlist_id) + if playlist: + playlist.tab = idx + session.flush() + # Save current tab record = settings["active_tab"] record.update(session, {"f_int": self.tabPlaylist.currentIndex()}) @@ -475,7 +483,7 @@ class Window(QMainWindow, Ui_MainWindow): with Session() as session: playlist = session.get(Playlists, closing_tab_playlist_id) if playlist: - playlist.close(session) + playlist.close() # Close playlist and remove tab self.tabPlaylist.widget(tab_index).close() @@ -535,10 +543,8 @@ class Window(QMainWindow, Ui_MainWindow): self.btnStop.clicked.connect(self.stop) self.hdrCurrentTrack.clicked.connect(self.show_current) self.hdrNextTrack.clicked.connect(self.show_next) - self.tabPlaylist.currentChanged.connect(self.tab_change) self.tabPlaylist.tabCloseRequested.connect(self.close_tab) self.tabBar = self.tabPlaylist.tabBar() - self.tabBar.tabMoved.connect(self.move_tab) self.txtSearch.returnPressed.connect(self.search_playlist_return) self.signals.enable_escape_signal.connect(self.enable_escape) @@ -566,9 +572,9 @@ class Window(QMainWindow, Ui_MainWindow): with Session() as session: playlist = self.create_playlist(session) if playlist: - self.create_playlist_tab(session, playlist) + self.create_playlist_tab(playlist) - def create_playlist_tab(self, session: scoped_session, playlist: Playlists) -> int: + def create_playlist_tab(self, playlist: Playlists) -> int: """ Take the passed playlist database object, create a playlist tab and add tab to display. Return index number of tab. @@ -869,7 +875,7 @@ class Window(QMainWindow, Ui_MainWindow): with Session() as session: for playlist in Playlists.get_open(session): if playlist: - _ = self.create_playlist_tab(session, playlist) + _ = self.create_playlist_tab(playlist) # Set active tab record = Settings.get_int_settings(session, "active_tab") if record.f_int and record.f_int >= 0: @@ -963,12 +969,6 @@ class Window(QMainWindow, Ui_MainWindow): self.move_playlist_rows(session, selected_plrs) - def move_tab(self, frm: int, to: int) -> None: - """Handle tabs being moved""" - - with Session() as session: - Playlists.move_tab(session, frm, to) - def move_unplayed(self) -> None: """ Move unplayed rows to another playlist @@ -997,12 +997,16 @@ class Window(QMainWindow, Ui_MainWindow): playlist = Playlists.create_playlist_from_template( session, template, playlist_name ) - if not playlist: - return - tab_index = self.create_playlist_tab(session, playlist) - playlist.mark_open(session, tab_index) - def open_playlist(self): + # Need to ensure that the new playlist is committed to + # the database before it is opened by the model. + + session.commit() + if playlist: + playlist.mark_open() + self.create_playlist_tab(playlist) + + def open_playlist(self) -> None: """Open existing playlist""" with Session() as session: @@ -1011,8 +1015,8 @@ class Window(QMainWindow, Ui_MainWindow): dlg.exec() playlist = dlg.playlist if playlist: - tab_index = self.create_playlist_tab(session, playlist) - playlist.mark_open(session, tab_index) + self.create_playlist_tab(playlist) + playlist.mark_open() def paste_rows(self) -> None: """ @@ -1460,15 +1464,6 @@ class Window(QMainWindow, Ui_MainWindow): # Enable controls self.enable_play_next_controls() - def tab_change(self): - """Called when active tab changed""" - - try: - self.tabPlaylist.currentWidget().tab_visible() - except AttributeError: - # May also be called when last tab is closed - pass - def set_next_plr_id( self, next_plr_id: Optional[int], playlist_tab: PlaylistTab ) -> None: diff --git a/app/playlistmodel.py b/app/playlistmodel.py index 0fd7edc..8ef1867 100644 --- a/app/playlistmodel.py +++ b/app/playlistmodel.py @@ -124,6 +124,9 @@ class PlaylistModel(QAbstractTableModel): self.signals.add_track_to_playlist_signal.connect(self.add_track) with Session() as session: + # Ensure row numbers in playlist are contiguous + PlaylistRows.fixup_rownumbers(session, playlist_id) + # Populate self.playlist_rows self.refresh_data(session) self.update_track_times() diff --git a/migrations/versions/5bb2c572e1e5_add_open_field_to_playlists.py b/migrations/versions/5bb2c572e1e5_add_open_field_to_playlists.py new file mode 100644 index 0000000..f532b8e --- /dev/null +++ b/migrations/versions/5bb2c572e1e5_add_open_field_to_playlists.py @@ -0,0 +1,60 @@ +"""Add 'open' field to Playlists + +Revision ID: 5bb2c572e1e5 +Revises: 3a53a9fb26ab +Create Date: 2023-11-18 14:19:02.643914 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = '5bb2c572e1e5' +down_revision = '3a53a9fb26ab' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('carts', 'duration', + existing_type=mysql.INTEGER(display_width=11), + nullable=True) + op.alter_column('carts', 'path', + existing_type=mysql.VARCHAR(length=2048), + nullable=True) + op.alter_column('carts', 'enabled', + existing_type=mysql.TINYINT(display_width=1), + nullable=True) + op.alter_column('playlist_rows', 'note', + existing_type=mysql.VARCHAR(length=2048), + nullable=False) + op.add_column('playlists', sa.Column('open', sa.Boolean(), nullable=False)) + op.alter_column('settings', 'name', + existing_type=mysql.VARCHAR(length=32), + type_=sa.String(length=64), + existing_nullable=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('settings', 'name', + existing_type=sa.String(length=64), + type_=mysql.VARCHAR(length=32), + existing_nullable=False) + op.drop_column('playlists', 'open') + op.alter_column('playlist_rows', 'note', + existing_type=mysql.VARCHAR(length=2048), + nullable=True) + op.alter_column('carts', 'enabled', + existing_type=mysql.TINYINT(display_width=1), + nullable=False) + op.alter_column('carts', 'path', + existing_type=mysql.VARCHAR(length=2048), + nullable=False) + op.alter_column('carts', 'duration', + existing_type=mysql.INTEGER(display_width=11), + nullable=False) + # ### end Alembic commands ###