Fix create playlist from template and tab handlding

Tab restore code rewritten.
This commit is contained in:
Keith Edmunds 2023-11-18 14:29:52 +00:00
parent 199f0e27fa
commit 8f2ab98be0
4 changed files with 98 additions and 64 deletions

View File

@ -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"<Playlists(id={self.id}, name={self.name}, "
f"is_templatee={self.is_template}>"
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"<Settings(id={self.id}, name={self.name}, "
f"f_datetime={self.f_datetime}, f_int={self.f_int}, f_string={self.f_string}>"

View File

@ -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:

View File

@ -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()

View File

@ -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 ###