Compare commits
13 Commits
aef8cb5cb5
...
589a664971
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
589a664971 | ||
|
|
67bf926ed8 | ||
|
|
040020e7ed | ||
|
|
911859ef49 | ||
|
|
68bdff53cf | ||
|
|
632937101a | ||
|
|
639f006a10 | ||
|
|
9e27418f80 | ||
|
|
c1448dfdd5 | ||
|
|
5f396a0993 | ||
|
|
e10c2adafe | ||
|
|
b0f6e4e819 | ||
|
|
afd3be608c |
@ -95,6 +95,7 @@ class Config(object):
|
||||
PLAY_SETTLE = 500000
|
||||
PLAYLIST_ICON_CURRENT = ":/icons/green-circle.png"
|
||||
PLAYLIST_ICON_NEXT = ":/icons/yellow-circle.png"
|
||||
PLAYLIST_ICON_TEMPLATE = ":/icons/redstar.png"
|
||||
PREVIEW_ADVANCE_MS = 5000
|
||||
PREVIEW_BACK_MS = 5000
|
||||
PREVIEW_END_BUFFER_MS = 1000
|
||||
|
||||
@ -80,8 +80,8 @@ class PlaylistsTable(Model):
|
||||
cascade="all, delete-orphan",
|
||||
order_by="PlaylistRowsTable.row_number",
|
||||
)
|
||||
query: Mapped["QueriesTable"] = relationship(
|
||||
back_populates="playlist", cascade="all, delete-orphan"
|
||||
favourite: Mapped[bool] = mapped_column(
|
||||
Boolean, nullable=False, index=False, default=False
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
@ -121,20 +121,6 @@ class PlaylistRowsTable(Model):
|
||||
)
|
||||
|
||||
|
||||
class QueriesTable(Model):
|
||||
__tablename__ = "queries"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
query: Mapped[str] = mapped_column(
|
||||
String(2048), index=False, default="", nullable=False
|
||||
)
|
||||
playlist_id: Mapped[int] = mapped_column(ForeignKey("playlists.id"), index=True)
|
||||
playlist: Mapped[PlaylistsTable] = relationship(back_populates="query")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Queries(id={self.id}, playlist={self.playlist}, query={self.query}>"
|
||||
|
||||
|
||||
class SettingsTable(Model):
|
||||
"""Manage settings"""
|
||||
|
||||
|
||||
102
app/menu.yaml
Normal file
102
app/menu.yaml
Normal file
@ -0,0 +1,102 @@
|
||||
menus:
|
||||
- title: "&File"
|
||||
actions:
|
||||
- text: "Save as Template"
|
||||
handler: "save_as_template"
|
||||
- text: "Manage Templates"
|
||||
handler: "manage_templates"
|
||||
- separator: true
|
||||
- separator: true
|
||||
- text: "Exit"
|
||||
handler: "close"
|
||||
|
||||
- title: "&Playlist"
|
||||
actions:
|
||||
- text: "Open Playlist"
|
||||
handler: "open_existing_playlist"
|
||||
shortcut: "Ctrl+O"
|
||||
- text: "New Playlist"
|
||||
handler: "new_playlist_dynamic_submenu"
|
||||
submenu: true
|
||||
- text: "Close Playlist"
|
||||
handler: "close_playlist_tab"
|
||||
- text: "Rename Playlist"
|
||||
handler: "rename_playlist"
|
||||
- text: "Delete Playlist"
|
||||
handler: "delete_playlist"
|
||||
- separator: true
|
||||
- text: "Insert Track"
|
||||
handler: "insert_track"
|
||||
shortcut: "Ctrl+T"
|
||||
- text: "Select Track from Query"
|
||||
handler: "query_dynamic_submenu"
|
||||
submenu: true
|
||||
- text: "Insert Section Header"
|
||||
handler: "insert_header"
|
||||
shortcut: "Ctrl+H"
|
||||
- text: "Import Files"
|
||||
handler: "import_files_wrapper"
|
||||
shortcut: "Ctrl+Shift+I"
|
||||
- separator: true
|
||||
- text: "Mark for Moving"
|
||||
handler: "mark_rows_for_moving"
|
||||
shortcut: "Ctrl+C"
|
||||
- text: "Paste"
|
||||
handler: "paste_rows"
|
||||
shortcut: "Ctrl+V"
|
||||
- separator: true
|
||||
- text: "Export Playlist"
|
||||
handler: "export_playlist_tab"
|
||||
- text: "Download CSV of Played Tracks"
|
||||
handler: "download_played_tracks"
|
||||
- separator: true
|
||||
- text: "Select Duplicate Rows"
|
||||
handler: "select_duplicate_rows"
|
||||
- text: "Move Selected"
|
||||
handler: "move_selected"
|
||||
- text: "Move Unplayed"
|
||||
handler: "move_unplayed"
|
||||
- separator: true
|
||||
- text: "Clear Selection"
|
||||
handler: "clear_selection"
|
||||
shortcut: "Esc"
|
||||
store_reference: true # So we can enable/disable later
|
||||
|
||||
- title: "&Music"
|
||||
actions:
|
||||
- text: "Set Next"
|
||||
handler: "set_selected_track_next"
|
||||
shortcut: "Ctrl+N"
|
||||
- text: "Play Next"
|
||||
handler: "play_next"
|
||||
shortcut: "Return"
|
||||
- text: "Fade"
|
||||
handler: "fade"
|
||||
shortcut: "Ctrl+Z"
|
||||
- text: "Stop"
|
||||
handler: "stop"
|
||||
shortcut: "Ctrl+Alt+S"
|
||||
- text: "Resume"
|
||||
handler: "resume"
|
||||
shortcut: "Ctrl+R"
|
||||
- text: "Skip to Next"
|
||||
handler: "play_next"
|
||||
shortcut: "Ctrl+Alt+Return"
|
||||
- separator: true
|
||||
- text: "Search"
|
||||
handler: "search_playlist"
|
||||
shortcut: "/"
|
||||
- text: "Search Title in Wikipedia"
|
||||
handler: "lookup_row_in_wikipedia"
|
||||
shortcut: "Ctrl+W"
|
||||
- text: "Search Title in Songfacts"
|
||||
handler: "lookup_row_in_songfacts"
|
||||
shortcut: "Ctrl+S"
|
||||
|
||||
- title: "Help"
|
||||
actions:
|
||||
- text: "About"
|
||||
handler: "about"
|
||||
- text: "Debug"
|
||||
handler: "debug"
|
||||
|
||||
@ -179,12 +179,18 @@ class Playdates(dbtables.PlaydatesTable):
|
||||
|
||||
|
||||
class Playlists(dbtables.PlaylistsTable):
|
||||
def __init__(self, session: Session, name: str):
|
||||
def __init__(self, session: Session, name: str, template_id: int) -> None:
|
||||
"""Create playlist with passed name"""
|
||||
|
||||
self.name = name
|
||||
self.last_used = dt.datetime.now()
|
||||
session.add(self)
|
||||
session.commit()
|
||||
|
||||
# If a template is specified, copy from it
|
||||
if template_id:
|
||||
PlaylistRows.copy_playlist(session, template_id, self.id)
|
||||
|
||||
@staticmethod
|
||||
def clear_tabs(session: Session, playlist_ids: list[int]) -> None:
|
||||
"""
|
||||
@ -201,26 +207,6 @@ class Playlists(dbtables.PlaylistsTable):
|
||||
self.open = False
|
||||
session.commit()
|
||||
|
||||
@classmethod
|
||||
def create_playlist_from_template(
|
||||
cls, session: Session, template: "Playlists", playlist_name: str
|
||||
) -> Optional["Playlists"]:
|
||||
"""Create a new playlist from template"""
|
||||
|
||||
# Sanity check
|
||||
if not template.id:
|
||||
return None
|
||||
|
||||
playlist = cls(session, playlist_name)
|
||||
|
||||
# Sanity / mypy checks
|
||||
if not playlist or not playlist.id:
|
||||
return None
|
||||
|
||||
PlaylistRows.copy_playlist(session, template.id, playlist.id)
|
||||
|
||||
return playlist
|
||||
|
||||
def delete(self, session: Session) -> None:
|
||||
"""
|
||||
Delete playlist
|
||||
@ -247,6 +233,19 @@ class Playlists(dbtables.PlaylistsTable):
|
||||
select(cls).where(cls.is_template.is_(True)).order_by(cls.name)
|
||||
).all()
|
||||
|
||||
@classmethod
|
||||
def get_favourite_templates(cls, session: Session) -> Sequence["Playlists"]:
|
||||
"""Returns a list of favourite templates ordered by name"""
|
||||
|
||||
return session.scalars(
|
||||
select(cls)
|
||||
.where(
|
||||
cls.is_template.is_(True),
|
||||
cls.favourite.is_(True)
|
||||
)
|
||||
.order_by(cls.name)
|
||||
).all()
|
||||
|
||||
@classmethod
|
||||
def get_closed(cls, session: Session) -> Sequence["Playlists"]:
|
||||
"""Returns a list of all closed playlists ordered by last use"""
|
||||
@ -301,7 +300,7 @@ class Playlists(dbtables.PlaylistsTable):
|
||||
) -> None:
|
||||
"""Save passed playlist as new template"""
|
||||
|
||||
template = Playlists(session, template_name)
|
||||
template = Playlists(session, template_name, template_id=0)
|
||||
if not template or not template.id:
|
||||
return
|
||||
|
||||
@ -596,7 +595,7 @@ class PlaylistRows(dbtables.PlaylistRowsTable):
|
||||
|
||||
|
||||
class Settings(dbtables.SettingsTable):
|
||||
def __init__(self, session: Session, name: str):
|
||||
def __init__(self, session: Session, name: str) -> None:
|
||||
self.name = name
|
||||
session.add(self)
|
||||
session.commit()
|
||||
@ -624,7 +623,7 @@ class Tracks(dbtables.TracksTable):
|
||||
fade_at: int,
|
||||
silence_at: int,
|
||||
bitrate: int,
|
||||
):
|
||||
) -> None:
|
||||
self.path = path
|
||||
self.title = title
|
||||
self.artist = artist
|
||||
|
||||
1016
app/musicmuster.py
1016
app/musicmuster.py
File diff suppressed because it is too large
Load Diff
@ -26,6 +26,7 @@ from PyQt6.QtGui import (
|
||||
)
|
||||
|
||||
# Third party imports
|
||||
from sqlalchemy.orm.session import Session
|
||||
import obswebsocket # type: ignore
|
||||
|
||||
# import snoop # type: ignore
|
||||
@ -74,12 +75,14 @@ class PlaylistModel(QAbstractTableModel):
|
||||
def __init__(
|
||||
self,
|
||||
playlist_id: int,
|
||||
is_template: bool,
|
||||
*args: Optional[QObject],
|
||||
**kwargs: Optional[QObject],
|
||||
) -> None:
|
||||
log.debug("PlaylistModel.__init__()")
|
||||
|
||||
self.playlist_id = playlist_id
|
||||
self.is_template = is_template
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.playlist_rows: dict[int, RowAndTrack] = {}
|
||||
@ -772,7 +775,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
|
||||
return None
|
||||
|
||||
def load_data(self, session: db.session) -> None:
|
||||
def load_data(self, session: Session) -> None:
|
||||
"""
|
||||
Same as refresh data, but only used when creating playslit.
|
||||
Distinguishes profile time between initial load and other
|
||||
@ -1061,7 +1064,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
# Update display
|
||||
self.invalidate_row(track_sequence.previous.row_number)
|
||||
|
||||
def refresh_data(self, session: db.session) -> None:
|
||||
def refresh_data(self, session: Session) -> None:
|
||||
"""
|
||||
Populate self.playlist_rows with playlist data
|
||||
|
||||
|
||||
@ -364,7 +364,7 @@ class PlaylistTab(QTableView):
|
||||
Override closeEditor to enable play controls and update display.
|
||||
"""
|
||||
|
||||
self.musicmuster.action_Clear_selection.setEnabled(True)
|
||||
self.musicmuster.enable_escape(True)
|
||||
|
||||
super(PlaylistTab, self).closeEditor(editor, hint)
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="icons">
|
||||
<file>yellow-circle.png</file>
|
||||
<file>redstar.png</file>
|
||||
<file>green-circle.png</file>
|
||||
<file>star.png</file>
|
||||
<file>star_empty.png</file>
|
||||
|
||||
1399
app/ui/icons_rc.py
1399
app/ui/icons_rc.py
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1249</width>
|
||||
<height>499</height>
|
||||
<height>538</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
||||
BIN
app/ui/redstar.png
Normal file
BIN
app/ui/redstar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
@ -0,0 +1,58 @@
|
||||
"""add favouirit to playlists
|
||||
|
||||
Revision ID: 04df697e40cd
|
||||
Revises: 33c04e3c12c8
|
||||
Create Date: 2025-02-22 20:20:45.030024
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '04df697e40cd'
|
||||
down_revision = '33c04e3c12c8'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade(engine_name: str) -> None:
|
||||
globals()["upgrade_%s" % engine_name]()
|
||||
|
||||
|
||||
def downgrade(engine_name: str) -> None:
|
||||
globals()["downgrade_%s" % engine_name]()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def upgrade_() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('notecolours', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('strip_substring', sa.Boolean(), nullable=False))
|
||||
batch_op.create_index(batch_op.f('ix_notecolours_substring'), ['substring'], unique=False)
|
||||
|
||||
with op.batch_alter_table('playlist_rows', schema=None) as batch_op:
|
||||
batch_op.drop_constraint('playlist_rows_ibfk_1', type_='foreignkey')
|
||||
|
||||
with op.batch_alter_table('playlists', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('favourite', sa.Boolean(), nullable=False))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade_() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('playlists', schema=None) as batch_op:
|
||||
batch_op.drop_column('favourite')
|
||||
|
||||
with op.batch_alter_table('playlist_rows', schema=None) as batch_op:
|
||||
batch_op.create_foreign_key('playlist_rows_ibfk_1', 'tracks', ['track_id'], ['id'])
|
||||
|
||||
with op.batch_alter_table('notecolours', schema=None) as batch_op:
|
||||
batch_op.drop_index(batch_op.f('ix_notecolours_substring'))
|
||||
batch_op.drop_column('strip_substring')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
Loading…
Reference in New Issue
Block a user