Fix create playlist from template and tab handlding
Tab restore code rewritten.
This commit is contained in:
parent
199f0e27fa
commit
8f2ab98be0
@ -25,7 +25,6 @@ from sqlalchemy import (
|
|||||||
from sqlalchemy.orm import (
|
from sqlalchemy.orm import (
|
||||||
DeclarativeBase,
|
DeclarativeBase,
|
||||||
joinedload,
|
joinedload,
|
||||||
lazyload,
|
|
||||||
Mapped,
|
Mapped,
|
||||||
mapped_column,
|
mapped_column,
|
||||||
relationship,
|
relationship,
|
||||||
@ -217,7 +216,8 @@ class Playlists(Base):
|
|||||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||||
name: Mapped[str] = mapped_column(String(32), unique=True)
|
name: Mapped[str] = mapped_column(String(32), unique=True)
|
||||||
last_used: Mapped[Optional[datetime]] = mapped_column(DateTime, default=None)
|
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)
|
is_template: Mapped[bool] = mapped_column(default=False)
|
||||||
deleted: Mapped[bool] = mapped_column(default=False)
|
deleted: Mapped[bool] = mapped_column(default=False)
|
||||||
rows: Mapped[List["PlaylistRows"]] = relationship(
|
rows: Mapped[List["PlaylistRows"]] = relationship(
|
||||||
@ -230,7 +230,7 @@ class Playlists(Base):
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f"<Playlists(id={self.id}, name={self.name}, "
|
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):
|
def __init__(self, session: scoped_session, name: str):
|
||||||
@ -238,19 +238,10 @@ class Playlists(Base):
|
|||||||
session.add(self)
|
session.add(self)
|
||||||
session.flush()
|
session.flush()
|
||||||
|
|
||||||
def close(self, session: scoped_session) -> None:
|
def close(self) -> None:
|
||||||
"""Mark playlist as unloaded"""
|
"""Mark playlist as unloaded"""
|
||||||
|
|
||||||
closed_idx = self.tab
|
self.open = False
|
||||||
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)
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_playlist_from_template(
|
def create_playlist_from_template(
|
||||||
@ -283,7 +274,7 @@ class Playlists(Base):
|
|||||||
return session.scalars(
|
return session.scalars(
|
||||||
select(cls)
|
select(cls)
|
||||||
.filter(cls.is_template.is_(False))
|
.filter(cls.is_template.is_(False))
|
||||||
.order_by(cls.tab.desc(), cls.last_used.desc())
|
.order_by(cls.last_used.desc())
|
||||||
).all()
|
).all()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -301,7 +292,7 @@ class Playlists(Base):
|
|||||||
return session.scalars(
|
return session.scalars(
|
||||||
select(cls)
|
select(cls)
|
||||||
.filter(
|
.filter(
|
||||||
cls.tab.is_(None),
|
cls.open.is_(False),
|
||||||
cls.is_template.is_(False),
|
cls.is_template.is_(False),
|
||||||
cls.deleted.is_(False),
|
cls.deleted.is_(False),
|
||||||
)
|
)
|
||||||
@ -311,32 +302,18 @@ class Playlists(Base):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def get_open(cls, session: scoped_session) -> Sequence[Optional["Playlists"]]:
|
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(
|
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()
|
).all()
|
||||||
|
|
||||||
def mark_open(self, session: scoped_session, tab_index: int) -> None:
|
def mark_open(self) -> None:
|
||||||
"""Mark playlist as loaded and used now"""
|
"""Mark playlist as loaded and used now"""
|
||||||
|
|
||||||
self.tab = tab_index
|
self.open = True
|
||||||
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
|
|
||||||
|
|
||||||
def rename(self, session: scoped_session, new_name: str) -> None:
|
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)
|
f_string: Mapped[Optional[str]] = mapped_column(String(128), default=None)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
value = self.f_datetime or self.f_int or self.f_string
|
|
||||||
return (
|
return (
|
||||||
f"<Settings(id={self.id}, name={self.name}, "
|
f"<Settings(id={self.id}, name={self.name}, "
|
||||||
f"f_datetime={self.f_datetime}, f_int={self.f_int}, f_string={self.f_string}>"
|
f"f_datetime={self.f_datetime}, f_int={self.f_int}, f_string={self.f_string}>"
|
||||||
|
|||||||
@ -442,6 +442,14 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
if record.f_int != splitter_bottom:
|
if record.f_int != splitter_bottom:
|
||||||
record.update(session, {"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
|
# Save current tab
|
||||||
record = settings["active_tab"]
|
record = settings["active_tab"]
|
||||||
record.update(session, {"f_int": self.tabPlaylist.currentIndex()})
|
record.update(session, {"f_int": self.tabPlaylist.currentIndex()})
|
||||||
@ -475,7 +483,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
with Session() as session:
|
with Session() as session:
|
||||||
playlist = session.get(Playlists, closing_tab_playlist_id)
|
playlist = session.get(Playlists, closing_tab_playlist_id)
|
||||||
if playlist:
|
if playlist:
|
||||||
playlist.close(session)
|
playlist.close()
|
||||||
|
|
||||||
# Close playlist and remove tab
|
# Close playlist and remove tab
|
||||||
self.tabPlaylist.widget(tab_index).close()
|
self.tabPlaylist.widget(tab_index).close()
|
||||||
@ -535,10 +543,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.btnStop.clicked.connect(self.stop)
|
self.btnStop.clicked.connect(self.stop)
|
||||||
self.hdrCurrentTrack.clicked.connect(self.show_current)
|
self.hdrCurrentTrack.clicked.connect(self.show_current)
|
||||||
self.hdrNextTrack.clicked.connect(self.show_next)
|
self.hdrNextTrack.clicked.connect(self.show_next)
|
||||||
self.tabPlaylist.currentChanged.connect(self.tab_change)
|
|
||||||
self.tabPlaylist.tabCloseRequested.connect(self.close_tab)
|
self.tabPlaylist.tabCloseRequested.connect(self.close_tab)
|
||||||
self.tabBar = self.tabPlaylist.tabBar()
|
self.tabBar = self.tabPlaylist.tabBar()
|
||||||
self.tabBar.tabMoved.connect(self.move_tab)
|
|
||||||
self.txtSearch.returnPressed.connect(self.search_playlist_return)
|
self.txtSearch.returnPressed.connect(self.search_playlist_return)
|
||||||
|
|
||||||
self.signals.enable_escape_signal.connect(self.enable_escape)
|
self.signals.enable_escape_signal.connect(self.enable_escape)
|
||||||
@ -566,9 +572,9 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
with Session() as session:
|
with Session() as session:
|
||||||
playlist = self.create_playlist(session)
|
playlist = self.create_playlist(session)
|
||||||
if playlist:
|
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
|
Take the passed playlist database object, create a playlist tab and
|
||||||
add tab to display. Return index number of tab.
|
add tab to display. Return index number of tab.
|
||||||
@ -869,7 +875,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
with Session() as session:
|
with Session() as session:
|
||||||
for playlist in Playlists.get_open(session):
|
for playlist in Playlists.get_open(session):
|
||||||
if playlist:
|
if playlist:
|
||||||
_ = self.create_playlist_tab(session, playlist)
|
_ = self.create_playlist_tab(playlist)
|
||||||
# Set active tab
|
# Set active tab
|
||||||
record = Settings.get_int_settings(session, "active_tab")
|
record = Settings.get_int_settings(session, "active_tab")
|
||||||
if record.f_int and record.f_int >= 0:
|
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)
|
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:
|
def move_unplayed(self) -> None:
|
||||||
"""
|
"""
|
||||||
Move unplayed rows to another playlist
|
Move unplayed rows to another playlist
|
||||||
@ -997,12 +997,16 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
playlist = Playlists.create_playlist_from_template(
|
playlist = Playlists.create_playlist_from_template(
|
||||||
session, template, playlist_name
|
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"""
|
"""Open existing playlist"""
|
||||||
|
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
@ -1011,8 +1015,8 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
dlg.exec()
|
dlg.exec()
|
||||||
playlist = dlg.playlist
|
playlist = dlg.playlist
|
||||||
if playlist:
|
if playlist:
|
||||||
tab_index = self.create_playlist_tab(session, playlist)
|
self.create_playlist_tab(playlist)
|
||||||
playlist.mark_open(session, tab_index)
|
playlist.mark_open()
|
||||||
|
|
||||||
def paste_rows(self) -> None:
|
def paste_rows(self) -> None:
|
||||||
"""
|
"""
|
||||||
@ -1460,15 +1464,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
# Enable controls
|
# Enable controls
|
||||||
self.enable_play_next_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(
|
def set_next_plr_id(
|
||||||
self, next_plr_id: Optional[int], playlist_tab: PlaylistTab
|
self, next_plr_id: Optional[int], playlist_tab: PlaylistTab
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
@ -124,6 +124,9 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
self.signals.add_track_to_playlist_signal.connect(self.add_track)
|
self.signals.add_track_to_playlist_signal.connect(self.add_track)
|
||||||
|
|
||||||
with Session() as session:
|
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.refresh_data(session)
|
||||||
self.update_track_times()
|
self.update_track_times()
|
||||||
|
|
||||||
|
|||||||
@ -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 ###
|
||||||
Loading…
Reference in New Issue
Block a user