Compare commits
3 Commits
a8c5a56c1a
...
9ac2911a55
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ac2911a55 | ||
|
|
3513c32a62 | ||
|
|
ae87ac82ba |
@ -30,10 +30,10 @@ else:
|
|||||||
|
|
||||||
engine = create_engine(
|
engine = create_engine(
|
||||||
MYSQL_CONNECT,
|
MYSQL_CONNECT,
|
||||||
encoding="utf-8",
|
|
||||||
echo=Config.DISPLAY_SQL,
|
echo=Config.DISPLAY_SQL,
|
||||||
pool_pre_ping=True,
|
pool_pre_ping=True,
|
||||||
future=True,
|
future=True,
|
||||||
|
connect_args={"charset": "utf8mb4"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -75,8 +75,8 @@ def log_uncaught_exceptions(_ex_cls, ex, tb):
|
|||||||
print("\033[1;31;47m")
|
print("\033[1;31;47m")
|
||||||
logging.critical(''.join(traceback.format_tb(tb)))
|
logging.critical(''.join(traceback.format_tb(tb)))
|
||||||
print("\033[1;37;40m")
|
print("\033[1;37;40m")
|
||||||
print(stackprinter.format(ex, show_vals="all", add_summary=True,
|
# print(stackprinter.format(ex, show_vals="all", add_summary=True,
|
||||||
style="darkbg"))
|
# style="darkbg"))
|
||||||
if os.environ["MM_ENV"] == "PRODUCTION":
|
if os.environ["MM_ENV"] == "PRODUCTION":
|
||||||
msg = stackprinter.format(ex)
|
msg = stackprinter.format(ex)
|
||||||
send_mail(Config.ERRORS_TO, Config.ERRORS_FROM,
|
send_mail(Config.ERRORS_TO, Config.ERRORS_FROM,
|
||||||
|
|||||||
175
app/models.py
175
app/models.py
@ -6,27 +6,27 @@ from config import Config
|
|||||||
from dbconfig import scoped_session
|
from dbconfig import scoped_session
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import List, Optional
|
from typing import List, Optional, Sequence
|
||||||
|
|
||||||
from sqlalchemy.ext.associationproxy import association_proxy
|
from sqlalchemy.ext.associationproxy import association_proxy
|
||||||
|
|
||||||
from sqlalchemy import (
|
from sqlalchemy import (
|
||||||
Boolean,
|
Boolean,
|
||||||
Column,
|
|
||||||
DateTime,
|
DateTime,
|
||||||
delete,
|
delete,
|
||||||
Float,
|
|
||||||
ForeignKey,
|
ForeignKey,
|
||||||
func,
|
func,
|
||||||
Integer,
|
|
||||||
select,
|
select,
|
||||||
String,
|
String,
|
||||||
update,
|
update,
|
||||||
)
|
)
|
||||||
|
|
||||||
from sqlalchemy.orm import (
|
from sqlalchemy.orm import (
|
||||||
declarative_base,
|
DeclarativeBase,
|
||||||
joinedload,
|
joinedload,
|
||||||
|
lazyload,
|
||||||
|
Mapped,
|
||||||
|
mapped_column,
|
||||||
relationship,
|
relationship,
|
||||||
)
|
)
|
||||||
from sqlalchemy.orm.exc import (
|
from sqlalchemy.orm.exc import (
|
||||||
@ -37,19 +37,21 @@ from sqlalchemy.exc import (
|
|||||||
)
|
)
|
||||||
from log import log
|
from log import log
|
||||||
|
|
||||||
Base = declarative_base()
|
|
||||||
|
class Base(DeclarativeBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Database classes
|
# Database classes
|
||||||
class Carts(Base):
|
class Carts(Base):
|
||||||
__tablename__ = "carts"
|
__tablename__ = "carts"
|
||||||
|
|
||||||
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||||
cart_number: int = Column(Integer, nullable=False, unique=True)
|
cart_number: Mapped[int] = mapped_column(unique=True)
|
||||||
name = Column(String(256), index=True)
|
name: Mapped[str] = mapped_column(String(256), index=True)
|
||||||
duration = Column(Integer, index=True)
|
duration: Mapped[int] = mapped_column(index=True)
|
||||||
path = Column(String(2048), index=False)
|
path: Mapped[str] = mapped_column(String(2048), index=False)
|
||||||
enabled: bool = Column(Boolean, default=False, nullable=False)
|
enabled: Mapped[bool] = mapped_column(default=False)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
@ -81,13 +83,13 @@ class Carts(Base):
|
|||||||
class NoteColours(Base):
|
class NoteColours(Base):
|
||||||
__tablename__ = "notecolours"
|
__tablename__ = "notecolours"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||||
substring = Column(String(256), index=False)
|
substring: Mapped[str] = mapped_column(String(256), index=False)
|
||||||
colour = Column(String(21), index=False)
|
colour: Mapped[str] = mapped_column(String(21), index=False)
|
||||||
enabled = Column(Boolean, default=True, index=True)
|
enabled: Mapped[bool] = mapped_column(default=True, index=True)
|
||||||
is_regex = Column(Boolean, default=False, index=False)
|
is_regex: Mapped[bool] = mapped_column(default=False, index=False)
|
||||||
is_casesensitive = Column(Boolean, default=False, index=False)
|
is_casesensitive: Mapped[bool] = mapped_column(default=False, index=False)
|
||||||
order = Column(Integer, index=True)
|
order: Mapped[Optional[int]] = mapped_column(index=True)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
@ -134,10 +136,10 @@ class NoteColours(Base):
|
|||||||
class Playdates(Base):
|
class Playdates(Base):
|
||||||
__tablename__ = "playdates"
|
__tablename__ = "playdates"
|
||||||
|
|
||||||
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||||
lastplayed: datetime = Column(DateTime, index=True)
|
lastplayed: Mapped[datetime] = mapped_column(index=True)
|
||||||
track_id = Column(Integer, ForeignKey("tracks.id"))
|
track_id: Mapped[int] = mapped_column(ForeignKey("tracks.id"))
|
||||||
track: "Tracks" = relationship("Tracks", back_populates="playdates")
|
track: Mapped["Tracks"] = relationship("Tracks", back_populates="playdates")
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
@ -170,7 +172,7 @@ class Playdates(Base):
|
|||||||
return Config.EPOCH
|
return Config.EPOCH
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def played_after(session: scoped_session, since: datetime) -> List["Playdates"]:
|
def played_after(session: scoped_session, since: datetime) -> Sequence["Playdates"]:
|
||||||
"""Return a list of Playdates objects since passed time"""
|
"""Return a list of Playdates objects since passed time"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -191,16 +193,13 @@ class Playlists(Base):
|
|||||||
|
|
||||||
__tablename__ = "playlists"
|
__tablename__ = "playlists"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||||
name: str = Column(String(32), nullable=False, unique=True)
|
name: Mapped[str] = mapped_column(String(32), unique=True)
|
||||||
last_used = Column(DateTime, default=None, nullable=True)
|
last_used: Mapped[Optional[datetime]] = mapped_column(DateTime, default=None)
|
||||||
tab = Column(Integer, default=None, nullable=True, unique=True)
|
tab: Mapped[Optional[int]] = mapped_column(default=None, unique=True)
|
||||||
# TODO sort_column is unused
|
is_template: Mapped[bool] = mapped_column(default=False)
|
||||||
sort_column = Column(Integer, default=None, nullable=True, unique=False)
|
deleted: Mapped[bool] = mapped_column(default=False)
|
||||||
is_template: bool = Column(Boolean, default=False, nullable=False)
|
rows: Mapped[List["PlaylistRows"]] = relationship(
|
||||||
query = Column(String(256), default=None, nullable=True, unique=False)
|
|
||||||
deleted: bool = Column(Boolean, default=False, nullable=False)
|
|
||||||
rows: List["PlaylistRows"] = relationship(
|
|
||||||
"PlaylistRows",
|
"PlaylistRows",
|
||||||
back_populates="playlist",
|
back_populates="playlist",
|
||||||
cascade="all, delete-orphan",
|
cascade="all, delete-orphan",
|
||||||
@ -257,7 +256,7 @@ class Playlists(Base):
|
|||||||
session.flush()
|
session.flush()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_all(cls, session: scoped_session) -> List["Playlists"]:
|
def get_all(cls, session: scoped_session) -> Sequence["Playlists"]:
|
||||||
"""Returns a list of all playlists ordered by last use"""
|
"""Returns a list of all playlists ordered by last use"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -271,7 +270,7 @@ class Playlists(Base):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_all_templates(cls, session: scoped_session) -> List["Playlists"]:
|
def get_all_templates(cls, session: scoped_session) -> Sequence["Playlists"]:
|
||||||
"""Returns a list of all templates ordered by name"""
|
"""Returns a list of all templates ordered by name"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -283,7 +282,7 @@ class Playlists(Base):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_closed(cls, session: scoped_session) -> List["Playlists"]:
|
def get_closed(cls, session: scoped_session) -> Sequence["Playlists"]:
|
||||||
"""Returns a list of all closed playlists ordered by last use"""
|
"""Returns a list of all closed playlists ordered by last use"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -301,7 +300,7 @@ class Playlists(Base):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_open(cls, session: scoped_session) -> List[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 order.
|
||||||
"""
|
"""
|
||||||
@ -359,14 +358,17 @@ class Playlists(Base):
|
|||||||
class PlaylistRows(Base):
|
class PlaylistRows(Base):
|
||||||
__tablename__ = "playlist_rows"
|
__tablename__ = "playlist_rows"
|
||||||
|
|
||||||
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||||
plr_rownum: int = Column(Integer, nullable=False)
|
plr_rownum: Mapped[int]
|
||||||
note: str = Column(String(2048), index=False, default="", nullable=False)
|
note: Mapped[str] = mapped_column(String(2048), index=False, default="", nullable=False)
|
||||||
playlist_id: int = Column(Integer, ForeignKey("playlists.id"), nullable=False)
|
playlist_id: Mapped[int] = mapped_column(ForeignKey("playlists.id"))
|
||||||
playlist: Playlists = relationship(Playlists, back_populates="rows")
|
playlist: Mapped[Playlists] = relationship(back_populates="rows")
|
||||||
track_id = Column(Integer, ForeignKey("tracks.id"), nullable=True)
|
track_id: Mapped[Optional[int]] = mapped_column(ForeignKey("tracks.id"))
|
||||||
track: "Tracks" = relationship("Tracks", back_populates="playlistrows")
|
track: Mapped["Tracks"] = relationship(
|
||||||
played: bool = Column(Boolean, nullable=False, index=False, default=False)
|
"Tracks",
|
||||||
|
back_populates="playlistrows",
|
||||||
|
)
|
||||||
|
played: Mapped[bool] = mapped_column(Boolean, nullable=False, index=False, default=False)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
@ -433,6 +435,23 @@ class PlaylistRows(Base):
|
|||||||
)
|
)
|
||||||
session.flush()
|
session.flush()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def deep_rows(cls, session: scoped_session, playlist_id: int) -> Sequence["PlaylistRows"]:
|
||||||
|
"""
|
||||||
|
Return a list of playlist rows that include full track and lastplayed data for
|
||||||
|
given playlist_id., Sequence
|
||||||
|
"""
|
||||||
|
|
||||||
|
stmt = (
|
||||||
|
select(PlaylistRows)
|
||||||
|
.options(joinedload(cls.track))
|
||||||
|
.where(PlaylistRows.playlist_id == playlist_id)
|
||||||
|
.order_by(PlaylistRows.plr_rownum)
|
||||||
|
# .options(joinedload(Tracks.playdates))
|
||||||
|
)
|
||||||
|
|
||||||
|
return session.scalars(stmt).unique().all()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def fixup_rownumbers(session: scoped_session, playlist_id: int) -> None:
|
def fixup_rownumbers(session: scoped_session, playlist_id: int) -> None:
|
||||||
"""
|
"""
|
||||||
@ -458,7 +477,7 @@ class PlaylistRows(Base):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def plrids_to_plrs(
|
def plrids_to_plrs(
|
||||||
cls, session: scoped_session, playlist_id: int, plr_ids: List[int]
|
cls, session: scoped_session, playlist_id: int, plr_ids: List[int]
|
||||||
) -> List["PlaylistRows"]:
|
) -> Sequence["PlaylistRows"]:
|
||||||
"""
|
"""
|
||||||
Take a list of PlaylistRows ids and return a list of corresponding
|
Take a list of PlaylistRows ids and return a list of corresponding
|
||||||
PlaylistRows objects
|
PlaylistRows objects
|
||||||
@ -504,7 +523,7 @@ class PlaylistRows(Base):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def get_played_rows(
|
def get_played_rows(
|
||||||
cls, session: scoped_session, playlist_id: int
|
cls, session: scoped_session, playlist_id: int
|
||||||
) -> List["PlaylistRows"]:
|
) -> Sequence["PlaylistRows"]:
|
||||||
"""
|
"""
|
||||||
For passed playlist, return a list of rows that
|
For passed playlist, return a list of rows that
|
||||||
have been played.
|
have been played.
|
||||||
@ -529,7 +548,7 @@ class PlaylistRows(Base):
|
|||||||
playlist_id: int,
|
playlist_id: int,
|
||||||
from_row: Optional[int] = None,
|
from_row: Optional[int] = None,
|
||||||
to_row: Optional[int] = None,
|
to_row: Optional[int] = None,
|
||||||
) -> List["PlaylistRows"]:
|
) -> Sequence["PlaylistRows"]:
|
||||||
"""
|
"""
|
||||||
For passed playlist, return a list of rows that
|
For passed playlist, return a list of rows that
|
||||||
contain tracks
|
contain tracks
|
||||||
@ -550,7 +569,7 @@ class PlaylistRows(Base):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def get_unplayed_rows(
|
def get_unplayed_rows(
|
||||||
cls, session: scoped_session, playlist_id: int
|
cls, session: scoped_session, playlist_id: int
|
||||||
) -> List["PlaylistRows"]:
|
) -> Sequence["PlaylistRows"]:
|
||||||
"""
|
"""
|
||||||
For passed playlist, return a list of playlist rows that
|
For passed playlist, return a list of playlist rows that
|
||||||
have not been played.
|
have not been played.
|
||||||
@ -596,11 +615,11 @@ class Settings(Base):
|
|||||||
|
|
||||||
__tablename__ = "settings"
|
__tablename__ = "settings"
|
||||||
|
|
||||||
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||||
name: str = Column(String(64), nullable=False, unique=True)
|
name: Mapped[str] = mapped_column(String(64), unique=True)
|
||||||
f_datetime = Column(DateTime, default=None, nullable=True)
|
f_datetime: Mapped[Optional[datetime]] = mapped_column(default=None)
|
||||||
f_int: int = Column(Integer, default=None, nullable=True)
|
f_int: Mapped[Optional[int]] = mapped_column(default=None)
|
||||||
f_string = Column(String(128), default=None, nullable=True)
|
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
|
value = self.f_datetime or self.f_int or self.f_string
|
||||||
@ -611,6 +630,20 @@ class Settings(Base):
|
|||||||
session.add(self)
|
session.add(self)
|
||||||
session.flush()
|
session.flush()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def all_as_dict(cls, session):
|
||||||
|
"""
|
||||||
|
Return all setting in a dictionary keyed by name
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
settings = session.execute(select(cls)).scalars().all()
|
||||||
|
for setting in settings:
|
||||||
|
result[setting.name] = setting
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_int_settings(cls, session: scoped_session, name: str) -> "Settings":
|
def get_int_settings(cls, session: scoped_session, name: str) -> "Settings":
|
||||||
"""Get setting for an integer or return new setting record"""
|
"""Get setting for an integer or return new setting record"""
|
||||||
@ -631,21 +664,25 @@ class Settings(Base):
|
|||||||
class Tracks(Base):
|
class Tracks(Base):
|
||||||
__tablename__ = "tracks"
|
__tablename__ = "tracks"
|
||||||
|
|
||||||
id: int = Column(Integer, primary_key=True, autoincrement=True)
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||||
title = Column(String(256), index=True)
|
title: Mapped[str] = mapped_column(String(256), index=True)
|
||||||
artist = Column(String(256), index=True)
|
artist: Mapped[str] = mapped_column(String(256), index=True)
|
||||||
duration = Column(Integer, index=True)
|
duration: Mapped[int] = mapped_column(index=True)
|
||||||
start_gap = Column(Integer, index=False)
|
start_gap: Mapped[int] = mapped_column(index=False)
|
||||||
fade_at = Column(Integer, index=False)
|
fade_at: Mapped[int] = mapped_column(index=False)
|
||||||
silence_at = Column(Integer, index=False)
|
silence_at: Mapped[int] = mapped_column(index=False)
|
||||||
path: str = Column(String(2048), index=False, nullable=False, unique=True)
|
path: Mapped[str] = mapped_column(String(2048), index=False, unique=True)
|
||||||
mtime = Column(Float, index=True)
|
mtime: Mapped[float] = mapped_column(index=True)
|
||||||
bitrate = Column(Integer, nullable=True, default=None)
|
bitrate: Mapped[Optional[int]] = mapped_column(default=None)
|
||||||
playlistrows: List[PlaylistRows] = relationship(
|
playlistrows: Mapped[List[PlaylistRows]] = relationship(
|
||||||
"PlaylistRows", back_populates="track"
|
"PlaylistRows", back_populates="track"
|
||||||
)
|
)
|
||||||
playlists = association_proxy("playlistrows", "playlist")
|
playlists = association_proxy("playlistrows", "playlist")
|
||||||
playdates: List[Playdates] = relationship("Playdates", back_populates="track")
|
playdates: Mapped[List[Playdates]] = relationship(
|
||||||
|
"Playdates",
|
||||||
|
back_populates="track",
|
||||||
|
lazy="joined",
|
||||||
|
)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
@ -707,7 +744,7 @@ class Tracks(Base):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def search_artists(cls, session: scoped_session, text: str) -> List["Tracks"]:
|
def search_artists(cls, session: scoped_session, text: str) -> Sequence["Tracks"]:
|
||||||
"""
|
"""
|
||||||
Search case-insenstively for artists containing str
|
Search case-insenstively for artists containing str
|
||||||
|
|
||||||
@ -729,7 +766,7 @@ class Tracks(Base):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def search_titles(cls, session: scoped_session, text: str) -> List["Tracks"]:
|
def search_titles(cls, session: scoped_session, text: str) -> Sequence["Tracks"]:
|
||||||
"""
|
"""
|
||||||
Search case-insenstively for titles containing str
|
Search case-insenstively for titles containing str
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ from typing import (
|
|||||||
cast,
|
cast,
|
||||||
List,
|
List,
|
||||||
Optional,
|
Optional,
|
||||||
|
Sequence,
|
||||||
)
|
)
|
||||||
|
|
||||||
from PyQt6.QtCore import (
|
from PyQt6.QtCore import (
|
||||||
@ -33,6 +34,7 @@ from PyQt6.QtCore import (
|
|||||||
QTimer,
|
QTimer,
|
||||||
)
|
)
|
||||||
from PyQt6.QtGui import (
|
from PyQt6.QtGui import (
|
||||||
|
QCloseEvent,
|
||||||
QColor,
|
QColor,
|
||||||
QFont,
|
QFont,
|
||||||
QMouseEvent,
|
QMouseEvent,
|
||||||
@ -119,9 +121,12 @@ class CartButton(QPushButton):
|
|||||||
f"path={self.path}, is_playing={self.is_playing}>"
|
f"path={self.path}, is_playing={self.is_playing}>"
|
||||||
)
|
)
|
||||||
|
|
||||||
def event(self, event: QEvent) -> bool:
|
def event(self, event: Optional[QEvent]) -> bool:
|
||||||
"""Allow right click even when button is disabled"""
|
"""Allow right click even when button is disabled"""
|
||||||
|
|
||||||
|
if not event:
|
||||||
|
return False
|
||||||
|
|
||||||
if event.type() == QEvent.Type.MouseButtonRelease:
|
if event.type() == QEvent.Type.MouseButtonRelease:
|
||||||
mouse_event = cast(QMouseEvent, event)
|
mouse_event = cast(QMouseEvent, event)
|
||||||
if mouse_event.button() == Qt.MouseButton.RightButton:
|
if mouse_event.button() == Qt.MouseButton.RightButton:
|
||||||
@ -130,7 +135,7 @@ class CartButton(QPushButton):
|
|||||||
|
|
||||||
return super().event(event)
|
return super().event(event)
|
||||||
|
|
||||||
def resizeEvent(self, event: QResizeEvent) -> None:
|
def resizeEvent(self, event: Optional[QResizeEvent]) -> None:
|
||||||
"""Resize progess bar when button size changes"""
|
"""Resize progess bar when button size changes"""
|
||||||
|
|
||||||
self.pgb.setGeometry(0, 0, self.width(), 10)
|
self.pgb.setGeometry(0, 0, self.width(), 10)
|
||||||
@ -534,9 +539,12 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
# Clear the search bar
|
# Clear the search bar
|
||||||
self.search_playlist_clear()
|
self.search_playlist_clear()
|
||||||
|
|
||||||
def closeEvent(self, event: QEvent) -> None:
|
def closeEvent(self, event: Optional[QCloseEvent]) -> None:
|
||||||
"""Handle attempt to close main window"""
|
"""Handle attempt to close main window"""
|
||||||
|
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
|
||||||
# Don't allow window to close when a track is playing
|
# Don't allow window to close when a track is playing
|
||||||
if self.playing:
|
if self.playing:
|
||||||
event.ignore()
|
event.ignore()
|
||||||
@ -545,19 +553,20 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
record = Settings.get_int_settings(session, "mainwindow_height")
|
settings = Settings.all_as_dict(session)
|
||||||
|
record = settings["mainwindow_height"]
|
||||||
if record.f_int != self.height():
|
if record.f_int != self.height():
|
||||||
record.update(session, {"f_int": self.height()})
|
record.update(session, {"f_int": self.height()})
|
||||||
|
|
||||||
record = Settings.get_int_settings(session, "mainwindow_width")
|
record = settings["mainwindow_width"]
|
||||||
if record.f_int != self.width():
|
if record.f_int != self.width():
|
||||||
record.update(session, {"f_int": self.width()})
|
record.update(session, {"f_int": self.width()})
|
||||||
|
|
||||||
record = Settings.get_int_settings(session, "mainwindow_x")
|
record = settings["mainwindow_x"]
|
||||||
if record.f_int != self.x():
|
if record.f_int != self.x():
|
||||||
record.update(session, {"f_int": self.x()})
|
record.update(session, {"f_int": self.x()})
|
||||||
|
|
||||||
record = Settings.get_int_settings(session, "mainwindow_y")
|
record = settings["mainwindow_y"]
|
||||||
if record.f_int != self.y():
|
if record.f_int != self.y():
|
||||||
record.update(session, {"f_int": self.y()})
|
record.update(session, {"f_int": self.y()})
|
||||||
|
|
||||||
@ -566,16 +575,16 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
assert len(splitter_sizes) == 2
|
assert len(splitter_sizes) == 2
|
||||||
splitter_top, splitter_bottom = splitter_sizes
|
splitter_top, splitter_bottom = splitter_sizes
|
||||||
|
|
||||||
record = Settings.get_int_settings(session, "splitter_top")
|
record = settings["splitter_top"]
|
||||||
if record.f_int != splitter_top:
|
if record.f_int != splitter_top:
|
||||||
record.update(session, {"f_int": splitter_top})
|
record.update(session, {"f_int": splitter_top})
|
||||||
|
|
||||||
record = Settings.get_int_settings(session, "splitter_bottom")
|
record = settings["splitter_bottom"]
|
||||||
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 current tab
|
# Save current tab
|
||||||
record = Settings.get_int_settings(session, "active_tab")
|
record = settings["active_tab"]
|
||||||
record.update(session, {"f_int": self.tabPlaylist.currentIndex()})
|
record.update(session, {"f_int": self.tabPlaylist.currentIndex()})
|
||||||
|
|
||||||
event.accept()
|
event.accept()
|
||||||
@ -1037,11 +1046,11 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
_ = self.create_playlist_tab(session, playlist)
|
_ = self.create_playlist_tab(session, 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 and record.f_int >= 0:
|
if record.f_int and record.f_int >= 0:
|
||||||
self.tabPlaylist.setCurrentIndex(record.f_int)
|
self.tabPlaylist.setCurrentIndex(record.f_int)
|
||||||
|
|
||||||
def move_playlist_rows(
|
def move_playlist_rows(
|
||||||
self, session: scoped_session, playlistrows: List[PlaylistRows]
|
self, session: scoped_session, playlistrows: Sequence[PlaylistRows]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Move passed playlist rows to another playlist
|
Move passed playlist rows to another playlist
|
||||||
@ -1104,7 +1113,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
visible_tab.save_playlist(session)
|
visible_tab.save_playlist(session)
|
||||||
|
|
||||||
# Disable sort undo
|
# Disable sort undo
|
||||||
self.sort_undo = None
|
self.sort_undo = []
|
||||||
|
|
||||||
# Update destination playlist_tab if visible (if not visible, it
|
# Update destination playlist_tab if visible (if not visible, it
|
||||||
# will be re-populated when it is opened)
|
# will be re-populated when it is opened)
|
||||||
@ -1484,18 +1493,19 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
"""Set size of window from database"""
|
"""Set size of window from database"""
|
||||||
|
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
record = Settings.get_int_settings(session, "mainwindow_x")
|
settings = Settings.all_as_dict(session)
|
||||||
|
record = settings["mainwindow_x"]
|
||||||
x = record.f_int or 1
|
x = record.f_int or 1
|
||||||
record = Settings.get_int_settings(session, "mainwindow_y")
|
record = settings["mainwindow_y"]
|
||||||
y = record.f_int or 1
|
y = record.f_int or 1
|
||||||
record = Settings.get_int_settings(session, "mainwindow_width")
|
record = settings["mainwindow_width"]
|
||||||
width = record.f_int or 1599
|
width = record.f_int or 1599
|
||||||
record = Settings.get_int_settings(session, "mainwindow_height")
|
record = settings["mainwindow_height"]
|
||||||
height = record.f_int or 981
|
height = record.f_int or 981
|
||||||
self.setGeometry(x, y, width, height)
|
self.setGeometry(x, y, width, height)
|
||||||
record = Settings.get_int_settings(session, "splitter_top")
|
record = settings["splitter_top"]
|
||||||
splitter_top = record.f_int or 256
|
splitter_top = record.f_int or 256
|
||||||
record = Settings.get_int_settings(session, "splitter_bottom")
|
record = settings["splitter_bottom"]
|
||||||
splitter_bottom = record.f_int or 256
|
splitter_bottom = record.f_int or 256
|
||||||
self.splitter.setSizes([splitter_top, splitter_bottom])
|
self.splitter.setSizes([splitter_top, splitter_bottom])
|
||||||
return
|
return
|
||||||
@ -2161,6 +2171,6 @@ if __name__ == "__main__":
|
|||||||
msg,
|
msg,
|
||||||
)
|
)
|
||||||
|
|
||||||
print("\033[1;31;47mUnhandled exception starts")
|
# print("\033[1;31;47mUnhandled exception starts")
|
||||||
stackprinter.show(style="darkbg")
|
# stackprinter.show(style="darkbg")
|
||||||
print("Unhandled exception ends\033[1;37;40m")
|
# print("Unhandled exception ends\033[1;37;40m")
|
||||||
|
|||||||
@ -164,16 +164,19 @@ class PlaylistTab(QTableWidget):
|
|||||||
self.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
self.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
||||||
self.setRowCount(0)
|
self.setRowCount(0)
|
||||||
self.setColumnCount(len(columns))
|
self.setColumnCount(len(columns))
|
||||||
self.v_header = self.verticalHeader()
|
|
||||||
self.v_header.setMinimumSectionSize(Config.MINIMUM_ROW_HEIGHT)
|
|
||||||
self.horizontalHeader().setStretchLastSection(True)
|
|
||||||
|
|
||||||
# Header row
|
# Header row
|
||||||
|
self.h_header = self.horizontalHeader()
|
||||||
for idx in [a for a in range(len(columns))]:
|
for idx in [a for a in range(len(columns))]:
|
||||||
item = QTableWidgetItem()
|
item = QTableWidgetItem()
|
||||||
self.setHorizontalHeaderItem(idx, item)
|
self.setHorizontalHeaderItem(idx, item)
|
||||||
self.horizontalHeader().setMinimumSectionSize(0)
|
if self.h_header:
|
||||||
|
self.h_header.setStretchLastSection(True)
|
||||||
|
self.h_header.setMinimumSectionSize(0)
|
||||||
# Set column headings sorted by idx
|
# Set column headings sorted by idx
|
||||||
|
self.v_header = self.verticalHeader()
|
||||||
|
if self.v_header:
|
||||||
|
self.v_header.setMinimumSectionSize(Config.MINIMUM_ROW_HEIGHT)
|
||||||
self.setHorizontalHeaderLabels(
|
self.setHorizontalHeaderLabels(
|
||||||
[
|
[
|
||||||
a.heading
|
a.heading
|
||||||
@ -215,13 +218,16 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
# ########## Events other than cell editing ##########
|
# ########## Events other than cell editing ##########
|
||||||
|
|
||||||
def dropEvent(self, event: QDropEvent) -> None:
|
def dropEvent(self, event: Optional[QDropEvent]) -> None:
|
||||||
"""
|
"""
|
||||||
Handle drag/drop of rows
|
Handle drag/drop of rows
|
||||||
|
|
||||||
https://stackoverflow.com/questions/26227885/drag-and-drop-rows-within-qtablewidget
|
https://stackoverflow.com/questions/26227885/drag-and-drop-rows-within-qtablewidget
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if not event:
|
||||||
|
return
|
||||||
|
|
||||||
if not event.source() == self:
|
if not event.source() == self:
|
||||||
return # We don't accept external drops
|
return # We don't accept external drops
|
||||||
|
|
||||||
@ -267,7 +273,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
# Reset drag mode to allow row selection by dragging
|
# Reset drag mode to allow row selection by dragging
|
||||||
self.setDragEnabled(False)
|
self.setDragEnabled(False)
|
||||||
# Disable sort undo
|
# Disable sort undo
|
||||||
self.sort_undo = None
|
self.sort_undo = []
|
||||||
|
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
self.save_playlist(session)
|
self.save_playlist(session)
|
||||||
@ -613,8 +619,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
if played:
|
if played:
|
||||||
bold = False
|
bold = False
|
||||||
_ = self._set_row_userdata(row_number, self.PLAYED, True)
|
_ = self._set_row_userdata(row_number, self.PLAYED, True)
|
||||||
if plr.note is None:
|
|
||||||
plr.note = ""
|
|
||||||
self._set_row_note_text(session, row_number, plr.note)
|
self._set_row_note_text(session, row_number, plr.note)
|
||||||
else:
|
else:
|
||||||
# This is a section header so it must have note text
|
# This is a section header so it must have note text
|
||||||
@ -745,7 +749,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
stackprinter.format(),
|
stackprinter.format(),
|
||||||
)
|
)
|
||||||
print("playlists:play_started:current_row is None")
|
print("playlists:play_started:current_row is None")
|
||||||
stackprinter.show(add_summary=True, style="darkbg")
|
# stackprinter.show(add_summary=True, style="darkbg")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Mark current row as played
|
# Mark current row as played
|
||||||
@ -797,10 +801,10 @@ class PlaylistTab(QTableWidget):
|
|||||||
stackprinter.format(),
|
stackprinter.format(),
|
||||||
)
|
)
|
||||||
print("playlists:populate_display:no playlist")
|
print("playlists:populate_display:no playlist")
|
||||||
stackprinter.show(add_summary=True, style="darkbg")
|
# stackprinter.show(add_summary=True, style="darkbg")
|
||||||
return
|
return
|
||||||
|
|
||||||
for plr in playlist.rows:
|
for plr in PlaylistRows.deep_rows(session, playlist_id):
|
||||||
self.insert_row(
|
self.insert_row(
|
||||||
session,
|
session,
|
||||||
plr,
|
plr,
|
||||||
@ -817,7 +821,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
# Set widths
|
# Set widths
|
||||||
self._set_column_widths(session)
|
self._set_column_widths(session)
|
||||||
|
|
||||||
self.save_playlist(session)
|
|
||||||
# Queue up time calculations to take place after UI has
|
# Queue up time calculations to take place after UI has
|
||||||
# updated
|
# updated
|
||||||
self._update_start_end_times(session)
|
self._update_start_end_times(session)
|
||||||
@ -1106,7 +1109,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
)
|
)
|
||||||
if sort_menu:
|
if sort_menu:
|
||||||
sort_menu.setEnabled(self._sortable())
|
sort_menu.setEnabled(self._sortable())
|
||||||
self._add_context_menu("Undo sort", self._sort_undo, self.sort_undo is None)
|
self._add_context_menu("Undo sort", self._sort_undo, not bool(self.sort_undo))
|
||||||
|
|
||||||
# Build submenu
|
# Build submenu
|
||||||
|
|
||||||
@ -1143,6 +1146,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
|
settings = Settings.all_as_dict(session)
|
||||||
for column_name, data in columns.items():
|
for column_name, data in columns.items():
|
||||||
idx = data.idx
|
idx = data.idx
|
||||||
if idx == len(columns) - 1:
|
if idx == len(columns) - 1:
|
||||||
@ -1151,7 +1155,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
continue
|
continue
|
||||||
width = self.columnWidth(idx)
|
width = self.columnWidth(idx)
|
||||||
attribute_name = f"playlist_{column_name}_col_width"
|
attribute_name = f"playlist_{column_name}_col_width"
|
||||||
record = Settings.get_int_settings(session, attribute_name)
|
record = settings[attribute_name]
|
||||||
if record.f_int != self.columnWidth(idx):
|
if record.f_int != self.columnWidth(idx):
|
||||||
record.update(session, {"f_int": width})
|
record.update(session, {"f_int": width})
|
||||||
|
|
||||||
@ -1896,6 +1900,8 @@ class PlaylistTab(QTableWidget):
|
|||||||
def _set_column_widths(self, session: scoped_session) -> None:
|
def _set_column_widths(self, session: scoped_session) -> None:
|
||||||
"""Column widths from settings"""
|
"""Column widths from settings"""
|
||||||
|
|
||||||
|
settings = Settings.all_as_dict(session)
|
||||||
|
|
||||||
for column_name, data in columns.items():
|
for column_name, data in columns.items():
|
||||||
idx = data.idx
|
idx = data.idx
|
||||||
if idx == len(columns) - 1:
|
if idx == len(columns) - 1:
|
||||||
@ -1903,7 +1909,7 @@ class PlaylistTab(QTableWidget):
|
|||||||
self.setColumnWidth(idx, 0)
|
self.setColumnWidth(idx, 0)
|
||||||
continue
|
continue
|
||||||
attr_name = f"playlist_{column_name}_col_width"
|
attr_name = f"playlist_{column_name}_col_width"
|
||||||
record: Settings = Settings.get_int_settings(session, attr_name)
|
record = settings[attr_name]
|
||||||
if record and record.f_int >= 0:
|
if record and record.f_int >= 0:
|
||||||
self.setColumnWidth(idx, record.f_int)
|
self.setColumnWidth(idx, record.f_int)
|
||||||
else:
|
else:
|
||||||
@ -2080,8 +2086,10 @@ class PlaylistTab(QTableWidget):
|
|||||||
"playlists:_set_row_header_text() called on track row",
|
"playlists:_set_row_header_text() called on track row",
|
||||||
stackprinter.format(),
|
stackprinter.format(),
|
||||||
)
|
)
|
||||||
print("playists:_set_row_header_text() called on track row")
|
print(
|
||||||
stackprinter.show(add_summary=True, style="darkbg")
|
f"playists:_set_row_header_text() called on track row ({row_number=}, {text=}"
|
||||||
|
)
|
||||||
|
# stackprinter.show(add_summary=True, style="darkbg")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set text
|
# Set text
|
||||||
@ -2120,8 +2128,8 @@ class PlaylistTab(QTableWidget):
|
|||||||
"playlists:_set_row_note_colour() on header row",
|
"playlists:_set_row_note_colour() on header row",
|
||||||
stackprinter.format(),
|
stackprinter.format(),
|
||||||
)
|
)
|
||||||
print("playists:_set_row_note_colour() called on header row")
|
# stackprinter.show(add_summary=True, style="darkbg")
|
||||||
stackprinter.show(add_summary=True, style="darkbg")
|
print(f"playists:_set_row_note_colour() called on track row ({row_number=}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set colour
|
# Set colour
|
||||||
@ -2146,8 +2154,10 @@ class PlaylistTab(QTableWidget):
|
|||||||
"playlists:_set_row_note_text() called on header row",
|
"playlists:_set_row_note_text() called on header row",
|
||||||
stackprinter.format(),
|
stackprinter.format(),
|
||||||
)
|
)
|
||||||
print("playists:_set_row_note_text() called on header row")
|
print(
|
||||||
stackprinter.show(add_summary=True, style="darkbg")
|
f"playists:_set_row_note_text() called on header row ({row_number=}, {text=}"
|
||||||
|
)
|
||||||
|
# stackprinter.show(add_summary=True, style="darkbg")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set text
|
# Set text
|
||||||
@ -2388,9 +2398,11 @@ class PlaylistTab(QTableWidget):
|
|||||||
_ = self._set_row_bitrate(row, track.bitrate)
|
_ = self._set_row_bitrate(row, track.bitrate)
|
||||||
_ = self._set_row_duration(row, track.duration)
|
_ = self._set_row_duration(row, track.duration)
|
||||||
_ = self._set_row_end_time(row, None)
|
_ = self._set_row_end_time(row, None)
|
||||||
_ = self._set_row_last_played_time(
|
if track.playdates:
|
||||||
row, Playdates.last_played(session, track.id)
|
last_play = max([a.lastplayed for a in track.playdates])
|
||||||
)
|
else:
|
||||||
|
last_play = Config.EPOCH
|
||||||
|
_ = self._set_row_last_played_time(row, last_play)
|
||||||
_ = self._set_row_start_gap(row, track.start_gap)
|
_ = self._set_row_start_gap(row, track.start_gap)
|
||||||
_ = self._set_row_start_time(row, None)
|
_ = self._set_row_start_time(row, None)
|
||||||
_ = self._set_row_title(row, track.title)
|
_ = self._set_row_title(row, track.title)
|
||||||
|
|||||||
@ -1,14 +1,12 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# Form implementation generated from reading ui file 'dlg_Cart.ui'
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'app/ui/dlg_Cart.ui'
|
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.15.6
|
# Created by: PyQt6 UI code generator 6.5.3
|
||||||
#
|
#
|
||||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||||
# run again. Do not edit this file unless you know what you are doing.
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
class Ui_DialogCartEdit(object):
|
class Ui_DialogCartEdit(object):
|
||||||
@ -17,43 +15,43 @@ class Ui_DialogCartEdit(object):
|
|||||||
DialogCartEdit.resize(564, 148)
|
DialogCartEdit.resize(564, 148)
|
||||||
self.gridLayout = QtWidgets.QGridLayout(DialogCartEdit)
|
self.gridLayout = QtWidgets.QGridLayout(DialogCartEdit)
|
||||||
self.gridLayout.setObjectName("gridLayout")
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
self.label = QtWidgets.QLabel(DialogCartEdit)
|
self.label = QtWidgets.QLabel(parent=DialogCartEdit)
|
||||||
self.label.setMaximumSize(QtCore.QSize(56, 16777215))
|
self.label.setMaximumSize(QtCore.QSize(56, 16777215))
|
||||||
self.label.setObjectName("label")
|
self.label.setObjectName("label")
|
||||||
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
|
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
|
||||||
self.lineEditName = QtWidgets.QLineEdit(DialogCartEdit)
|
self.lineEditName = QtWidgets.QLineEdit(parent=DialogCartEdit)
|
||||||
self.lineEditName.setInputMask("")
|
self.lineEditName.setInputMask("")
|
||||||
self.lineEditName.setObjectName("lineEditName")
|
self.lineEditName.setObjectName("lineEditName")
|
||||||
self.gridLayout.addWidget(self.lineEditName, 0, 1, 1, 2)
|
self.gridLayout.addWidget(self.lineEditName, 0, 1, 1, 2)
|
||||||
self.chkEnabled = QtWidgets.QCheckBox(DialogCartEdit)
|
self.chkEnabled = QtWidgets.QCheckBox(parent=DialogCartEdit)
|
||||||
self.chkEnabled.setObjectName("chkEnabled")
|
self.chkEnabled.setObjectName("chkEnabled")
|
||||||
self.gridLayout.addWidget(self.chkEnabled, 0, 3, 1, 1)
|
self.gridLayout.addWidget(self.chkEnabled, 0, 3, 1, 1)
|
||||||
self.label_2 = QtWidgets.QLabel(DialogCartEdit)
|
self.label_2 = QtWidgets.QLabel(parent=DialogCartEdit)
|
||||||
self.label_2.setMaximumSize(QtCore.QSize(56, 16777215))
|
self.label_2.setMaximumSize(QtCore.QSize(56, 16777215))
|
||||||
self.label_2.setObjectName("label_2")
|
self.label_2.setObjectName("label_2")
|
||||||
self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
|
self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
|
||||||
self.lblPath = QtWidgets.QLabel(DialogCartEdit)
|
self.lblPath = QtWidgets.QLabel(parent=DialogCartEdit)
|
||||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.lblPath.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.lblPath.sizePolicy().hasHeightForWidth())
|
||||||
self.lblPath.setSizePolicy(sizePolicy)
|
self.lblPath.setSizePolicy(sizePolicy)
|
||||||
self.lblPath.setMinimumSize(QtCore.QSize(301, 41))
|
self.lblPath.setMinimumSize(QtCore.QSize(301, 41))
|
||||||
self.lblPath.setText("")
|
self.lblPath.setText("")
|
||||||
self.lblPath.setTextFormat(QtCore.Qt.PlainText)
|
self.lblPath.setTextFormat(QtCore.Qt.TextFormat.PlainText)
|
||||||
self.lblPath.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
|
self.lblPath.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
|
||||||
self.lblPath.setWordWrap(True)
|
self.lblPath.setWordWrap(True)
|
||||||
self.lblPath.setObjectName("lblPath")
|
self.lblPath.setObjectName("lblPath")
|
||||||
self.gridLayout.addWidget(self.lblPath, 1, 1, 1, 1)
|
self.gridLayout.addWidget(self.lblPath, 1, 1, 1, 1)
|
||||||
self.btnFile = QtWidgets.QPushButton(DialogCartEdit)
|
self.btnFile = QtWidgets.QPushButton(parent=DialogCartEdit)
|
||||||
self.btnFile.setMaximumSize(QtCore.QSize(31, 16777215))
|
self.btnFile.setMaximumSize(QtCore.QSize(31, 16777215))
|
||||||
self.btnFile.setObjectName("btnFile")
|
self.btnFile.setObjectName("btnFile")
|
||||||
self.gridLayout.addWidget(self.btnFile, 1, 3, 1, 1)
|
self.gridLayout.addWidget(self.btnFile, 1, 3, 1, 1)
|
||||||
spacerItem = QtWidgets.QSpacerItem(116, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
spacerItem = QtWidgets.QSpacerItem(116, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
|
||||||
self.gridLayout.addItem(spacerItem, 2, 1, 1, 1)
|
self.gridLayout.addItem(spacerItem, 2, 1, 1, 1)
|
||||||
self.buttonBox = QtWidgets.QDialogButtonBox(DialogCartEdit)
|
self.buttonBox = QtWidgets.QDialogButtonBox(parent=DialogCartEdit)
|
||||||
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
|
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
|
||||||
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok)
|
||||||
self.buttonBox.setObjectName("buttonBox")
|
self.buttonBox.setObjectName("buttonBox")
|
||||||
self.gridLayout.addWidget(self.buttonBox, 2, 2, 1, 2)
|
self.gridLayout.addWidget(self.buttonBox, 2, 2, 1, 2)
|
||||||
self.label.setBuddy(self.lineEditName)
|
self.label.setBuddy(self.lineEditName)
|
||||||
|
|||||||
@ -1,10 +1,34 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# Form implementation generated from reading ui file 'dlg_SelectPlaylist.ui'
|
||||||
|
|
||||||
# Form implementation generated from reading ui file 'ui/playlist.ui'
|
|
||||||
#
|
#
|
||||||
# Created by: PyQt5 UI code generator 5.15.4
|
# Created by: PyQt6 UI code generator 6.5.3
|
||||||
#
|
#
|
||||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
|
||||||
# run again. Do not edit this file unless you know what you are doing.
|
# run again. Do not edit this file unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
|
from PyQt6 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
|
class Ui_dlgSelectPlaylist(object):
|
||||||
|
def setupUi(self, dlgSelectPlaylist):
|
||||||
|
dlgSelectPlaylist.setObjectName("dlgSelectPlaylist")
|
||||||
|
dlgSelectPlaylist.resize(276, 150)
|
||||||
|
self.verticalLayout = QtWidgets.QVBoxLayout(dlgSelectPlaylist)
|
||||||
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
|
self.lstPlaylists = QtWidgets.QListWidget(parent=dlgSelectPlaylist)
|
||||||
|
self.lstPlaylists.setObjectName("lstPlaylists")
|
||||||
|
self.verticalLayout.addWidget(self.lstPlaylists)
|
||||||
|
self.buttonBox = QtWidgets.QDialogButtonBox(parent=dlgSelectPlaylist)
|
||||||
|
self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal)
|
||||||
|
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok)
|
||||||
|
self.buttonBox.setObjectName("buttonBox")
|
||||||
|
self.verticalLayout.addWidget(self.buttonBox)
|
||||||
|
|
||||||
|
self.retranslateUi(dlgSelectPlaylist)
|
||||||
|
self.buttonBox.accepted.connect(dlgSelectPlaylist.accept) # type: ignore
|
||||||
|
self.buttonBox.rejected.connect(dlgSelectPlaylist.reject) # type: ignore
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(dlgSelectPlaylist)
|
||||||
|
|
||||||
|
def retranslateUi(self, dlgSelectPlaylist):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
dlgSelectPlaylist.setWindowTitle(_translate("dlgSelectPlaylist", "Dialog"))
|
||||||
|
|||||||
@ -0,0 +1,72 @@
|
|||||||
|
"""Migrate SQLA 2 and remove redundant columns
|
||||||
|
|
||||||
|
Revision ID: 3a53a9fb26ab
|
||||||
|
Revises: 07dcbe6c4f0e
|
||||||
|
Create Date: 2023-10-15 09:39:16.449419
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '3a53a9fb26ab'
|
||||||
|
down_revision = '07dcbe6c4f0e'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column('playlists', 'query')
|
||||||
|
op.drop_column('playlists', 'sort_column')
|
||||||
|
op.alter_column('tracks', 'title',
|
||||||
|
existing_type=mysql.VARCHAR(length=256),
|
||||||
|
nullable=False)
|
||||||
|
op.alter_column('tracks', 'artist',
|
||||||
|
existing_type=mysql.VARCHAR(length=256),
|
||||||
|
nullable=False)
|
||||||
|
op.alter_column('tracks', 'duration',
|
||||||
|
existing_type=mysql.INTEGER(display_width=11),
|
||||||
|
nullable=False)
|
||||||
|
op.alter_column('tracks', 'start_gap',
|
||||||
|
existing_type=mysql.INTEGER(display_width=11),
|
||||||
|
nullable=False)
|
||||||
|
op.alter_column('tracks', 'fade_at',
|
||||||
|
existing_type=mysql.INTEGER(display_width=11),
|
||||||
|
nullable=False)
|
||||||
|
op.alter_column('tracks', 'silence_at',
|
||||||
|
existing_type=mysql.INTEGER(display_width=11),
|
||||||
|
nullable=False)
|
||||||
|
op.alter_column('tracks', 'mtime',
|
||||||
|
existing_type=mysql.FLOAT(),
|
||||||
|
nullable=False)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.alter_column('tracks', 'mtime',
|
||||||
|
existing_type=mysql.FLOAT(),
|
||||||
|
nullable=True)
|
||||||
|
op.alter_column('tracks', 'silence_at',
|
||||||
|
existing_type=mysql.INTEGER(display_width=11),
|
||||||
|
nullable=True)
|
||||||
|
op.alter_column('tracks', 'fade_at',
|
||||||
|
existing_type=mysql.INTEGER(display_width=11),
|
||||||
|
nullable=True)
|
||||||
|
op.alter_column('tracks', 'start_gap',
|
||||||
|
existing_type=mysql.INTEGER(display_width=11),
|
||||||
|
nullable=True)
|
||||||
|
op.alter_column('tracks', 'duration',
|
||||||
|
existing_type=mysql.INTEGER(display_width=11),
|
||||||
|
nullable=True)
|
||||||
|
op.alter_column('tracks', 'artist',
|
||||||
|
existing_type=mysql.VARCHAR(length=256),
|
||||||
|
nullable=True)
|
||||||
|
op.alter_column('tracks', 'title',
|
||||||
|
existing_type=mysql.VARCHAR(length=256),
|
||||||
|
nullable=True)
|
||||||
|
op.add_column('playlists', sa.Column('sort_column', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True))
|
||||||
|
op.add_column('playlists', sa.Column('query', mysql.VARCHAR(length=256), nullable=True))
|
||||||
|
# ### end Alembic commands ###
|
||||||
1293
poetry.lock
generated
1293
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -7,13 +7,12 @@ authors = ["Keith Edmunds <kae@midnighthax.com>"]
|
|||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.9"
|
python = "^3.9"
|
||||||
tinytag = "^1.7.0"
|
tinytag = "^1.7.0"
|
||||||
SQLAlchemy = "^1.4.31"
|
SQLAlchemy = "^2.0.22"
|
||||||
python-vlc = "^3.0.12118"
|
python-vlc = "^3.0.12118"
|
||||||
mysqlclient = "^2.1.0"
|
mysqlclient = "^2.1.0"
|
||||||
mutagen = "^1.45.1"
|
mutagen = "^1.45.1"
|
||||||
alembic = "^1.7.5"
|
alembic = "^1.7.5"
|
||||||
psutil = "^5.9.0"
|
psutil = "^5.9.0"
|
||||||
PyQtWebEngine = "^5.15.5"
|
|
||||||
pydub = "^0.25.1"
|
pydub = "^0.25.1"
|
||||||
types-psutil = "^5.8.22"
|
types-psutil = "^5.8.22"
|
||||||
python-slugify = "^6.1.2"
|
python-slugify = "^6.1.2"
|
||||||
@ -35,8 +34,6 @@ pytest-qt = "^4.0.2"
|
|||||||
pydub-stubs = "^0.25.1"
|
pydub-stubs = "^0.25.1"
|
||||||
line-profiler = "^4.0.2"
|
line-profiler = "^4.0.2"
|
||||||
flakehell = "^0.9.0"
|
flakehell = "^0.9.0"
|
||||||
sqlalchemy2-stubs = "^0.0.2-alpha.32"
|
|
||||||
mypy = "^0.991"
|
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
pudb = "^2022.1.3"
|
pudb = "^2022.1.3"
|
||||||
@ -44,6 +41,7 @@ sphinx = "^7.0.1"
|
|||||||
furo = "^2023.5.20"
|
furo = "^2023.5.20"
|
||||||
black = "^23.3.0"
|
black = "^23.3.0"
|
||||||
flakehell = "^0.9.0"
|
flakehell = "^0.9.0"
|
||||||
|
mypy = "^1.6.0"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core>=1.0.0"]
|
requires = ["poetry-core>=1.0.0"]
|
||||||
@ -52,7 +50,6 @@ build-backend = "poetry.core.masonry.api"
|
|||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
# mypy_path = "/home/kae/.cache/pypoetry/virtualenvs/musicmuster-oWgGw1IG-py3.9:/home/kae/git/musicmuster/app"
|
# mypy_path = "/home/kae/.cache/pypoetry/virtualenvs/musicmuster-oWgGw1IG-py3.9:/home/kae/git/musicmuster/app"
|
||||||
mypy_path = "/home/kae/git/musicmuster/app"
|
mypy_path = "/home/kae/git/musicmuster/app"
|
||||||
plugins = "sqlalchemy.ext.mypy.plugin"
|
|
||||||
|
|
||||||
[tool.vulture]
|
[tool.vulture]
|
||||||
exclude = ["migrations", "app/ui", "archive"]
|
exclude = ["migrations", "app/ui", "archive"]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user