Compare commits
No commits in common. "ad391aedc6302f78cb6726c747338daa9c7f860b" and "6d012d7b5a7c61b4bc67e134b4865837e2d3a677" have entirely different histories.
ad391aedc6
...
6d012d7b5a
@ -29,3 +29,5 @@ class DatabaseManager:
|
||||
if DatabaseManager.__instance is None:
|
||||
DatabaseManager(database_url, **kwargs)
|
||||
return DatabaseManager.__instance
|
||||
|
||||
|
||||
|
||||
@ -52,7 +52,7 @@ class JSONEncodedDict(TypeDecorator):
|
||||
class NoteColours(Model):
|
||||
__tablename__ = "notecolours"
|
||||
|
||||
notecolour_id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
substring: Mapped[str] = mapped_column(String(256), index=True, unique=True)
|
||||
colour: Mapped[str] = mapped_column(String(21), index=False)
|
||||
enabled: Mapped[bool] = mapped_column(default=True, index=True)
|
||||
@ -64,7 +64,7 @@ class NoteColours(Model):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<NoteColours(id={self.notecolour_id}, substring={self.substring}, "
|
||||
f"<NoteColours(id={self.id}, substring={self.substring}, "
|
||||
f"colour={self.colour}>"
|
||||
)
|
||||
|
||||
@ -92,11 +92,9 @@ class NoteColours(Model):
|
||||
class Playdates(Model):
|
||||
__tablename__ = "playdates"
|
||||
|
||||
playdate_id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
lastplayed: Mapped[dt.datetime] = mapped_column(index=True)
|
||||
track_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("tracks.track_id", ondelete="CASCADE")
|
||||
)
|
||||
track_id: Mapped[int] = mapped_column(ForeignKey("tracks.id", ondelete="CASCADE"))
|
||||
track: Mapped["Tracks"] = relationship(
|
||||
"Tracks",
|
||||
back_populates="playdates",
|
||||
@ -118,7 +116,7 @@ class Playdates(Model):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<Playdates(id={self.playdate_id}, track_id={self.track_id} "
|
||||
f"<Playdates(id={self.id}, track_id={self.track_id} "
|
||||
f"lastplayed={self.lastplayed}>"
|
||||
)
|
||||
|
||||
@ -130,7 +128,7 @@ class Playlists(Model):
|
||||
|
||||
__tablename__ = "playlists"
|
||||
|
||||
playlist_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)
|
||||
last_used: Mapped[Optional[dt.datetime]] = mapped_column(DateTime, default=None)
|
||||
tab: Mapped[Optional[int]] = mapped_column(default=None)
|
||||
@ -148,7 +146,7 @@ class Playlists(Model):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<Playlists(id={self.playlist_id}, name={self.name}, "
|
||||
f"<Playlists(id={self.id}, name={self.name}, "
|
||||
f"is_templatee={self.is_template}, open={self.open}>"
|
||||
)
|
||||
|
||||
@ -163,24 +161,24 @@ class Playlists(Model):
|
||||
|
||||
# If a template is specified, copy from it
|
||||
if template_id:
|
||||
PlaylistRows.copy_playlist(session, template_id, self.playlist_id)
|
||||
PlaylistRows.copy_playlist(session, template_id, self.id)
|
||||
|
||||
|
||||
class PlaylistRows(Model):
|
||||
__tablename__ = "playlist_rows"
|
||||
|
||||
playlistrow_id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
row_number: Mapped[int] = mapped_column(index=True)
|
||||
note: Mapped[str] = mapped_column(
|
||||
String(2048), index=False, default="", nullable=False
|
||||
)
|
||||
playlist_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("playlists.playlist_id", ondelete="CASCADE"), index=True
|
||||
ForeignKey("playlists.id", ondelete="CASCADE"), index=True
|
||||
)
|
||||
|
||||
playlist: Mapped[Playlists] = relationship(back_populates="rows")
|
||||
track_id: Mapped[Optional[int]] = mapped_column(
|
||||
ForeignKey("tracks.track_id", ondelete="CASCADE")
|
||||
ForeignKey("tracks.id", ondelete="CASCADE")
|
||||
)
|
||||
track: Mapped["Tracks"] = relationship(
|
||||
"Tracks",
|
||||
@ -192,7 +190,7 @@ class PlaylistRows(Model):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<PlaylistRows(id={self.playlistrow_id}, playlist_id={self.playlist_id}, "
|
||||
f"<PlaylistRows(id={self.id}, playlist_id={self.playlist_id}, "
|
||||
f"track_id={self.track_id}, "
|
||||
f"note={self.note}, row_number={self.row_number}>"
|
||||
)
|
||||
@ -219,14 +217,10 @@ class PlaylistRows(Model):
|
||||
class Queries(Model):
|
||||
__tablename__ = "queries"
|
||||
|
||||
query_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(128), nullable=False)
|
||||
_filter_data: Mapped[dict | None] = mapped_column(
|
||||
"filter_data", JSONEncodedDict, nullable=False
|
||||
)
|
||||
favourite: Mapped[bool] = mapped_column(
|
||||
Boolean, nullable=False, index=False, default=False
|
||||
)
|
||||
_filter_data: Mapped[dict | None] = mapped_column("filter_data", JSONEncodedDict, nullable=False)
|
||||
favourite: Mapped[bool] = mapped_column(Boolean, nullable=False, index=False, default=False)
|
||||
|
||||
def _get_filter(self) -> Filter:
|
||||
"""Convert stored JSON dictionary to a Filter object."""
|
||||
@ -242,7 +236,7 @@ class Queries(Model):
|
||||
filter = property(_get_filter, _set_filter)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Queries(id={self.query_id}, name={self.name}, filter={self.filter})>"
|
||||
return f"<Queries(id={self.id}, name={self.name}, filter={self.filter})>"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -266,7 +260,7 @@ class Settings(Model):
|
||||
|
||||
__tablename__ = "settings"
|
||||
|
||||
setting_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(64), unique=True)
|
||||
f_datetime: Mapped[Optional[dt.datetime]] = mapped_column(default=None)
|
||||
f_int: Mapped[Optional[int]] = mapped_column(default=None)
|
||||
@ -274,7 +268,7 @@ class Settings(Model):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<Settings(id={self.setting_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}>"
|
||||
)
|
||||
|
||||
@ -288,7 +282,7 @@ class Settings(Model):
|
||||
class Tracks(Model):
|
||||
__tablename__ = "tracks"
|
||||
|
||||
track_id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
artist: Mapped[str] = mapped_column(String(256), index=True)
|
||||
bitrate: Mapped[int] = mapped_column(default=None)
|
||||
duration: Mapped[int] = mapped_column(index=True)
|
||||
@ -314,7 +308,7 @@ class Tracks(Model):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<Track(id={self.track_id}, title={self.title}, "
|
||||
f"<Track(id={self.id}, title={self.title}, "
|
||||
f"artist={self.artist}, path={self.path}>"
|
||||
)
|
||||
|
||||
|
||||
107
app/ds.py
107
app/ds.py
@ -110,7 +110,7 @@ def _notecolours_all(session: Session) -> list[NoteColoursDTO]:
|
||||
results: list[NoteColoursDTO] = []
|
||||
for record in records:
|
||||
result = NoteColoursDTO(
|
||||
notecolour_id=record.notecolour_id,
|
||||
notecolour_id=record.id,
|
||||
substring=record.substring,
|
||||
colour=record.colour,
|
||||
enabled=record.enabled,
|
||||
@ -211,7 +211,7 @@ def _tracks_where(
|
||||
)
|
||||
stmt = (
|
||||
select(
|
||||
Tracks.track_id,
|
||||
Tracks.id.label("track_id"),
|
||||
Tracks.artist,
|
||||
Tracks.bitrate,
|
||||
Tracks.duration,
|
||||
@ -223,9 +223,7 @@ def _tracks_where(
|
||||
Tracks.title,
|
||||
latest_playdate_subq.c.lastplayed,
|
||||
)
|
||||
.outerjoin(
|
||||
latest_playdate_subq, Tracks.track_id == latest_playdate_subq.c.track_id
|
||||
)
|
||||
.outerjoin(latest_playdate_subq, Tracks.id == latest_playdate_subq.c.track_id)
|
||||
.where(query)
|
||||
)
|
||||
|
||||
@ -261,7 +259,7 @@ def track_add_to_header(playlistrow_id: int, track_id: int) -> None:
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(PlaylistRows)
|
||||
.where(PlaylistRows.playlistrow_id == playlistrow_id)
|
||||
.where(PlaylistRows.id == playlistrow_id)
|
||||
.values(track_id=track_id)
|
||||
)
|
||||
session.commit()
|
||||
@ -270,7 +268,7 @@ def track_add_to_header(playlistrow_id: int, track_id: int) -> None:
|
||||
def tracks_all() -> list[TrackDTO]:
|
||||
"""Return a list of all tracks"""
|
||||
|
||||
return _tracks_where(Tracks.track_id > 0)
|
||||
return _tracks_where(Tracks.id > 0)
|
||||
|
||||
|
||||
def tracks_by_artist(filter_str: str) -> list[TrackDTO]:
|
||||
@ -286,7 +284,7 @@ def track_by_id(track_id: int) -> TrackDTO | None:
|
||||
Return track with specified id
|
||||
"""
|
||||
|
||||
track_list = _tracks_where(Tracks.track_id == track_id)
|
||||
track_list = _tracks_where(Tracks.id == track_id)
|
||||
if not track_list:
|
||||
return None
|
||||
if len(track_list) > 1:
|
||||
@ -335,7 +333,7 @@ def track_create(metadata: dict[str, str | int | float]) -> TrackDTO:
|
||||
bitrate=int(metadata["bitrate"]),
|
||||
)
|
||||
|
||||
track_id = track.track_id
|
||||
track_id = track.id
|
||||
session.commit()
|
||||
except Exception:
|
||||
raise ApplicationError("Can't create Track")
|
||||
@ -389,8 +387,8 @@ def tracks_filtered(filter: Filter) -> list[TrackDTO]:
|
||||
# Process comparator
|
||||
if filter.last_played_comparator == Config.FILTER_PLAYED_COMPARATOR_NEVER:
|
||||
# Select tracks that have never been played
|
||||
query = query.outerjoin(Playdates, Tracks.track_id == Playdates.track_id).where(
|
||||
Playdates.playdate_id.is_(None)
|
||||
query = query.outerjoin(Playdates, Tracks.id == Playdates.track_id).where(
|
||||
Playdates.id.is_(None)
|
||||
)
|
||||
else:
|
||||
# Last played specification
|
||||
@ -416,7 +414,7 @@ def tracks_filtered(filter: Filter) -> list[TrackDTO]:
|
||||
.group_by(Playdates.track_id)
|
||||
.subquery()
|
||||
)
|
||||
query = query.join(subquery, Tracks.track_id == subquery.c.track_id).where(
|
||||
query = query.join(subquery, Tracks.id == subquery.c.track_id).where(
|
||||
subquery.c.max_last_played < before
|
||||
)
|
||||
|
||||
@ -440,7 +438,7 @@ def tracks_filtered(filter: Filter) -> list[TrackDTO]:
|
||||
silence_at=record.silence_at,
|
||||
start_gap=record.start_gap,
|
||||
title=record.title,
|
||||
track_id=record.track_id,
|
||||
track_id=record.id,
|
||||
)
|
||||
results.append(dto)
|
||||
|
||||
@ -500,7 +498,7 @@ def _playlist_check_playlist(
|
||||
|
||||
msg = (
|
||||
"_check_playlist_integrity: incorrect row number "
|
||||
f"({plr.playlistrow_id=}, {plr.row_number=}, {idx=})"
|
||||
f"({plr.id=}, {plr.row_number=}, {idx=})"
|
||||
)
|
||||
if fix:
|
||||
log.debug(msg)
|
||||
@ -544,7 +542,7 @@ def _playlists_where(
|
||||
select(
|
||||
Playlists.favourite,
|
||||
Playlists.is_template,
|
||||
Playlists.playlist_id,
|
||||
Playlists.id.label("playlist_id"),
|
||||
Playlists.name,
|
||||
Playlists.open,
|
||||
)
|
||||
@ -572,7 +570,7 @@ def _playlists_where(
|
||||
def playlists_all():
|
||||
"""Return all playlists"""
|
||||
|
||||
return _playlists_where(Playlists.playlist_id > 0)
|
||||
return _playlists_where(Playlists.id > 0)
|
||||
|
||||
|
||||
# @log_call
|
||||
@ -581,7 +579,7 @@ def playlist_by_id(playlist_id: int) -> PlaylistDTO | None:
|
||||
Return playlist with specified id
|
||||
"""
|
||||
|
||||
playlist_list = _playlists_where(Playlists.playlist_id == playlist_id)
|
||||
playlist_list = _playlists_where(Playlists.id == playlist_id)
|
||||
if not playlist_list:
|
||||
return None
|
||||
if len(playlist_list) > 1:
|
||||
@ -629,7 +627,7 @@ def playlist_create(
|
||||
try:
|
||||
playlist = Playlists(session, name, template_id)
|
||||
playlist.is_template = as_template
|
||||
playlist_id = playlist.playlist_id
|
||||
playlist_id = playlist.id
|
||||
session.commit()
|
||||
except Exception:
|
||||
raise ApplicationError("Can't create Playlist")
|
||||
@ -681,7 +679,7 @@ def playlist_insert_row(
|
||||
track_id=track_id,
|
||||
)
|
||||
session.commit()
|
||||
playlist_row_id = playlist_row.playlistrow_id
|
||||
playlist_row_id = playlist_row.id
|
||||
|
||||
# Sanity check
|
||||
_playlist_check_playlist(session, playlist_id, fix=False)
|
||||
@ -699,9 +697,7 @@ def playlist_mark_status(playlist_id: int, open: bool) -> None:
|
||||
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(Playlists)
|
||||
.where(Playlists.playlist_id == playlist_id)
|
||||
.values(open=open)
|
||||
update(Playlists).where(Playlists.id == playlist_id).values(open=open)
|
||||
)
|
||||
|
||||
session.commit()
|
||||
@ -748,7 +744,9 @@ def _playlist_move_rows_between_playlists(
|
||||
|
||||
# Sanity check destination not being moved
|
||||
if to_row in from_rows:
|
||||
log.error(f"ds._playlist_move_rows_within_playlist: {to_row=} in {from_rows=}")
|
||||
log.error(
|
||||
f"ds._playlist_move_rows_within_playlist: {to_row=} in {from_rows=}"
|
||||
)
|
||||
return
|
||||
|
||||
with db.Session() as session:
|
||||
@ -767,8 +765,12 @@ def _playlist_move_rows_between_playlists(
|
||||
|
||||
for from_row in from_rows:
|
||||
plrid = old_row_to_id[from_row]
|
||||
update_list.append({"playlistrow_id": plrid, "row_number": next_row})
|
||||
update_list.append({"playlistrow_id": plrid, "playlist_id": to_playlist_id})
|
||||
update_list.append(
|
||||
{"id": plrid, "row_number": next_row}
|
||||
)
|
||||
update_list.append(
|
||||
{"id": plrid, "playlist_id": to_playlist_id}
|
||||
)
|
||||
next_row += 1
|
||||
|
||||
session.execute(update(PlaylistRows), update_list)
|
||||
@ -776,7 +778,7 @@ def _playlist_move_rows_between_playlists(
|
||||
|
||||
# Resequence row numbers in source
|
||||
_playlist_check_playlist(session, from_playlist_id, fix=True)
|
||||
# Sanity check destination
|
||||
# Sanity check destionation
|
||||
_playlist_check_playlist(session, from_playlist_id, fix=False)
|
||||
|
||||
|
||||
@ -786,7 +788,8 @@ def _playlist_rows_to_id(playlist_id: int) -> dict[int, int]:
|
||||
"""
|
||||
|
||||
row_to_id = {
|
||||
p.row_number: p.playlistrow_id for p in playlistrows_by_playlist(playlist_id)
|
||||
p.row_number: p.playlistrow_id
|
||||
for p in playlistrows_by_playlist(playlist_id)
|
||||
}
|
||||
|
||||
return row_to_id
|
||||
@ -810,7 +813,9 @@ def _playlist_move_rows_within_playlist(
|
||||
|
||||
# Sanity check destination not being moved
|
||||
if to_row in from_rows:
|
||||
log.error(f"ds._playlist_move_rows_within_playlist: {to_row=} in {from_rows=}")
|
||||
log.error(
|
||||
f"ds._playlist_move_rows_within_playlist: {to_row=} in {from_rows=}"
|
||||
)
|
||||
return
|
||||
|
||||
with db.Session() as session:
|
||||
@ -837,9 +842,9 @@ def _playlist_move_rows_within_playlist(
|
||||
old_row_to_id = _playlist_rows_to_id(from_playlist_id)
|
||||
for old_row, new_row in row_changes.items():
|
||||
plrid = old_row_to_id[old_row]
|
||||
update_list.append({"playlistrow_id": plrid, "row_number": new_row})
|
||||
update_list.append({"id": plrid, "row_number": new_row})
|
||||
|
||||
# Update database
|
||||
# Updte database
|
||||
session.execute(update(PlaylistRows), update_list)
|
||||
session.commit()
|
||||
|
||||
@ -862,9 +867,7 @@ def playlist_rename(playlist_id: int, new_name: str) -> None:
|
||||
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(Playlists)
|
||||
.where(Playlists.playlist_id == playlist_id)
|
||||
.values(name=new_name)
|
||||
update(Playlists).where(Playlists.id == playlist_id).values(name=new_name)
|
||||
)
|
||||
|
||||
session.commit()
|
||||
@ -986,14 +989,12 @@ def playlist_save_tabs(playlist_id_to_tab: dict[int, int]) -> None:
|
||||
# Clear all existing tab numbers
|
||||
session.execute(
|
||||
update(Playlists)
|
||||
.where(Playlists.playlist_id.in_(playlist_id_to_tab.keys()))
|
||||
.where(Playlists.id.in_(playlist_id_to_tab.keys()))
|
||||
.values(tab=None)
|
||||
)
|
||||
for playlist_id, tab in playlist_id_to_tab.items():
|
||||
session.execute(
|
||||
update(Playlists)
|
||||
.where(Playlists.playlist_id == playlist_id)
|
||||
.values(tab=tab)
|
||||
update(Playlists).where(Playlists.id == playlist_id).values(tab=tab)
|
||||
)
|
||||
session.commit()
|
||||
|
||||
@ -1005,7 +1006,7 @@ def playlist_update_template_favourite(template_id: int, favourite: bool) -> Non
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(Playlists)
|
||||
.where(Playlists.playlist_id == template_id)
|
||||
.where(Playlists.id == template_id)
|
||||
.values(favourite=favourite)
|
||||
)
|
||||
session.commit()
|
||||
@ -1023,9 +1024,7 @@ def playlistrow_by_id(playlistrow_id: int) -> PlaylistRowDTO | None:
|
||||
with db.Session() as session:
|
||||
record = (
|
||||
session.execute(
|
||||
select(PlaylistRows).where(
|
||||
PlaylistRows.playlistrow_id == playlistrow_id
|
||||
)
|
||||
select(PlaylistRows).where(PlaylistRows.id == playlistrow_id)
|
||||
)
|
||||
.scalars()
|
||||
.one_or_none()
|
||||
@ -1041,7 +1040,7 @@ def playlistrow_by_id(playlistrow_id: int) -> PlaylistRowDTO | None:
|
||||
note=record.note,
|
||||
played=record.played,
|
||||
playlist_id=record.playlist_id,
|
||||
playlistrow_id=record.playlistrow_id,
|
||||
playlistrow_id=record.id,
|
||||
row_number=record.row_number,
|
||||
track=track,
|
||||
)
|
||||
@ -1075,7 +1074,7 @@ def playlistrows_by_playlist(
|
||||
note=record.note,
|
||||
played=record.played,
|
||||
playlist_id=record.playlist_id,
|
||||
playlistrow_id=record.playlistrow_id,
|
||||
playlistrow_id=record.id,
|
||||
row_number=record.row_number,
|
||||
track=track,
|
||||
)
|
||||
@ -1113,7 +1112,7 @@ def playlistrow_played(playlistrow_id: int, status: bool) -> None:
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(PlaylistRows)
|
||||
.where(PlaylistRows.playlistrow_id == playlistrow_id)
|
||||
.where(PlaylistRows.id == playlistrow_id)
|
||||
.values(played=status)
|
||||
)
|
||||
session.commit()
|
||||
@ -1166,7 +1165,7 @@ def playdates_between_dates(
|
||||
end = dt.datetime.now()
|
||||
|
||||
stmt = select(
|
||||
Playdates.playdate_id,
|
||||
Playdates.id.label("playdate_id"),
|
||||
Playdates.lastplayed,
|
||||
Playdates.track_id,
|
||||
Playdates.track,
|
||||
@ -1214,7 +1213,7 @@ def _queries_where(
|
||||
favourite=record.favourite,
|
||||
filter=record.filter,
|
||||
name=record.name,
|
||||
query_id=record.query_id,
|
||||
query_id=record.id,
|
||||
)
|
||||
results.append(dto)
|
||||
|
||||
@ -1224,14 +1223,14 @@ def _queries_where(
|
||||
def queries_all(favourites_only: bool = False) -> list[QueryDTO]:
|
||||
"""Return a list of all queries"""
|
||||
|
||||
query = Queries.query_id > 0
|
||||
query = Queries.id > 0
|
||||
return _queries_where(query)
|
||||
|
||||
|
||||
def query_by_id(query_id: int) -> QueryDTO | None:
|
||||
"""Return query"""
|
||||
|
||||
query_list = _queries_where(Queries.query_id == query_id)
|
||||
query_list = _queries_where(Queries.id == query_id)
|
||||
if not query_list:
|
||||
return None
|
||||
if len(query_list) > 1:
|
||||
@ -1247,7 +1246,7 @@ def query_create(name: str, filter: Filter) -> QueryDTO:
|
||||
with db.Session() as session:
|
||||
try:
|
||||
query = Queries(session=session, name=name, filter=filter)
|
||||
query_id = query.query_id
|
||||
query_id = query.id
|
||||
session.commit()
|
||||
except Exception:
|
||||
raise ApplicationError("Can't create Query")
|
||||
@ -1273,9 +1272,7 @@ def query_update_favourite(query_id: int, favourite: bool) -> None:
|
||||
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(Queries)
|
||||
.where(Queries.query_id == query_id)
|
||||
.values(favourite=favourite)
|
||||
update(Queries).where(Queries.id == query_id).values(favourite=favourite)
|
||||
)
|
||||
session.commit()
|
||||
|
||||
@ -1285,7 +1282,7 @@ def query_update_filter(query_id: int, filter: Filter) -> None:
|
||||
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(Queries).where(Queries.query_id == query_id).values(filter=filter)
|
||||
update(Queries).where(Queries.id == query_id).values(filter=filter)
|
||||
)
|
||||
session.commit()
|
||||
|
||||
@ -1294,9 +1291,7 @@ def query_update_name(query_id: int, name: str) -> None:
|
||||
"""Update query name"""
|
||||
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(Queries).where(Queries.query_id == query_id).values(name=name)
|
||||
)
|
||||
session.execute(update(Queries).where(Queries.id == query_id).values(name=name))
|
||||
session.commit()
|
||||
|
||||
|
||||
|
||||
@ -101,13 +101,9 @@ def handle_exception(exc_type, exc_value, exc_traceback):
|
||||
log.error(logmsg)
|
||||
else:
|
||||
# Handle unexpected errors (log and display)
|
||||
error_msg = "".join(
|
||||
traceback.format_exception(exc_type, exc_value, exc_traceback)
|
||||
)
|
||||
error_msg = "".join(traceback.format_exception(exc_type, exc_value, exc_traceback))
|
||||
|
||||
print(
|
||||
stackprinter.format(exc_value, suppressed_paths=["/.venv"], style="darkbg")
|
||||
)
|
||||
print(stackprinter.format(exc_value, suppressed_paths=['/.venv'], style='darkbg'))
|
||||
|
||||
stack = stackprinter.format(exc_value)
|
||||
log.error(stack)
|
||||
@ -151,7 +147,6 @@ def log_call(func):
|
||||
except Exception as e:
|
||||
log.debug(f"exception in {func.__name__}: {e}", stacklevel=2)
|
||||
raise
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
|
||||
@ -147,7 +147,7 @@ class Music:
|
||||
< dt.timedelta(microseconds=Config.PLAY_SETTLE)
|
||||
)
|
||||
|
||||
# @log_call
|
||||
# @log_call
|
||||
def play(
|
||||
self,
|
||||
path: str,
|
||||
@ -179,12 +179,8 @@ class Music:
|
||||
return
|
||||
|
||||
self.events = self.player.event_manager()
|
||||
self.events.event_attach(
|
||||
vlc.EventType.MediaPlayerEndReached, self.track_end_event_handler
|
||||
)
|
||||
self.events.event_attach(
|
||||
vlc.EventType.MediaPlayerStopped, self.track_end_event_handler
|
||||
)
|
||||
self.events.event_attach(vlc.EventType.MediaPlayerEndReached, self.track_end_event_handler)
|
||||
self.events.event_attach(vlc.EventType.MediaPlayerStopped, self.track_end_event_handler)
|
||||
|
||||
_ = self.player.play()
|
||||
self.set_volume(self.max_volume)
|
||||
@ -201,7 +197,9 @@ class Music:
|
||||
if self.player:
|
||||
self.player.set_position(position)
|
||||
|
||||
def set_volume(self, volume: int | None = None, set_default: bool = True) -> None:
|
||||
def set_volume(
|
||||
self, volume: int | None = None, set_default: bool = True
|
||||
) -> None:
|
||||
"""Set maximum volume used for player"""
|
||||
|
||||
if not self.player:
|
||||
|
||||
@ -140,7 +140,7 @@ class SignalMonitor:
|
||||
)
|
||||
|
||||
def show_signal(self, name: str, *args: Any) -> None:
|
||||
log.debug(f"SIGNAL: {name=}, args={args}")
|
||||
log.debug(f"{name=}, args={args}")
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -1579,9 +1579,8 @@ class Window(QMainWindow):
|
||||
|
||||
def solicit_template_to_use(self, template_prompt: str | None = None) -> int | None:
|
||||
"""
|
||||
Have user select a template. Return the template.playlist_id, or
|
||||
None if they cancel. template_id of zero means don't use a
|
||||
template.
|
||||
Have user select a template. Return the template.id, or None if they cancel.
|
||||
template_id of zero means don't use a template.
|
||||
"""
|
||||
|
||||
template_name_id_list: list[tuple[str, int]] = []
|
||||
@ -1743,9 +1742,7 @@ class Window(QMainWindow):
|
||||
self.signals.signal_set_next_track.connect(self.set_next_track_handler)
|
||||
self.signals.status_message_signal.connect(self.show_status_message)
|
||||
self.signals.signal_track_ended.connect(self.track_ended_handler)
|
||||
self.signals.signal_playlist_selected_rows.connect(
|
||||
self.playlist_selected_rows_handler
|
||||
)
|
||||
self.signals.signal_playlist_selected_rows.connect(self.playlist_selected_rows_handler)
|
||||
|
||||
self.timer10.timeout.connect(self.tick_10ms)
|
||||
self.timer500.timeout.connect(self.tick_500ms)
|
||||
@ -1768,7 +1765,7 @@ class Window(QMainWindow):
|
||||
if self.current.selected_row_numbers:
|
||||
return self.current.selected_row_numbers[0]
|
||||
if not self.current.base_model:
|
||||
return 0 # hack, but mostly there WILL be a current model
|
||||
return 0 # hack, but mostly there WILL be a current model
|
||||
return self.current.base_model.rowCount()
|
||||
|
||||
def debug(self, checked: bool = False) -> None:
|
||||
@ -2041,7 +2038,7 @@ class Window(QMainWindow):
|
||||
source_playlist_id = self.current.playlist_id
|
||||
|
||||
for playlist in ds.playlists_all():
|
||||
if playlist.playlist_id == source_playlist_id:
|
||||
if playlist.id == source_playlist_id:
|
||||
continue
|
||||
else:
|
||||
playlists.append(playlist)
|
||||
@ -2565,7 +2562,9 @@ class Window(QMainWindow):
|
||||
|
||||
self._active_tab().scroll_to_top(playlist_track.row_number)
|
||||
|
||||
def playlist_selected_rows_handler(self, selected_rows: SelectedRows) -> None:
|
||||
def playlist_selected_rows_handler(
|
||||
self, selected_rows: SelectedRows
|
||||
) -> None:
|
||||
"""
|
||||
Handle signal_playlist_selected_rows to keep track of which rows
|
||||
are selected in the current model
|
||||
@ -2747,7 +2746,7 @@ class Window(QMainWindow):
|
||||
base_model: PlaylistModel,
|
||||
proxy_model: PlaylistProxyModel,
|
||||
playlist_id: int,
|
||||
selected_row_numbers: list[int],
|
||||
selected_row_numbers: list[int]
|
||||
) -> None:
|
||||
"""
|
||||
Update self.current when playlist tab changes. Called by new playlist
|
||||
@ -2757,7 +2756,7 @@ class Window(QMainWindow):
|
||||
base_model=base_model,
|
||||
proxy_model=proxy_model,
|
||||
playlist_id=playlist_id,
|
||||
selected_row_numbers=selected_row_numbers,
|
||||
selected_row_numbers=selected_row_numbers
|
||||
)
|
||||
|
||||
def update_headers(self) -> None:
|
||||
|
||||
@ -93,15 +93,11 @@ class PlaylistModel(QAbstractTableModel):
|
||||
self.played_tracks_hidden = False
|
||||
|
||||
# Connect signals
|
||||
self.signals.signal_add_track_to_header.connect(
|
||||
self.signal_add_track_to_header_handler
|
||||
)
|
||||
self.signals.signal_add_track_to_header.connect(self.signal_add_track_to_header_handler)
|
||||
self.signals.signal_begin_insert_rows.connect(self.begin_insert_rows_handler)
|
||||
self.signals.signal_end_insert_rows.connect(self.end_insert_rows_handler)
|
||||
self.signals.signal_insert_track.connect(self.insert_row_signal_handler)
|
||||
self.signals.signal_playlist_selected_rows.connect(
|
||||
self.playlist_selected_rows_handler
|
||||
)
|
||||
self.signals.signal_playlist_selected_rows.connect(self.playlist_selected_rows_handler)
|
||||
self.signals.signal_set_next_row.connect(self.set_next_row_handler)
|
||||
self.signals.signal_track_started.connect(self.track_started_handler)
|
||||
self.signals.signal_track_ended.connect(self.signal_track_ended_handler)
|
||||
@ -154,9 +150,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
return header_row
|
||||
|
||||
# @log_call
|
||||
def signal_add_track_to_header_handler(
|
||||
self, track_and_playlist: TrackAndPlaylist
|
||||
) -> None:
|
||||
def signal_add_track_to_header_handler(self, track_and_playlist: TrackAndPlaylist) -> None:
|
||||
"""
|
||||
Handle signal_add_track_to_header
|
||||
"""
|
||||
@ -1553,9 +1547,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
current_track_row_number, current_track_start_time
|
||||
)
|
||||
|
||||
if self.update_start_end_times(
|
||||
plr, current_track_start_time, current_track_end_time
|
||||
):
|
||||
if self.update_start_end_times(plr, current_track_start_time, current_track_end_time):
|
||||
update_rows.append(current_track_row_number)
|
||||
|
||||
# If we have a next track, note row number
|
||||
@ -1590,9 +1582,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
# Set start time for next row if we have a current track
|
||||
if current_track_row_number is not None and row_number == next_track_row:
|
||||
next_start_time = self.get_end_time(row_number, current_track_end_time)
|
||||
if self.update_start_end_times(
|
||||
plr, current_track_end_time, next_start_time
|
||||
):
|
||||
if self.update_start_end_times(plr, current_track_end_time, next_start_time):
|
||||
update_rows.append(row_number)
|
||||
|
||||
# If we're between the current and next row, zero out
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Standard library imports
|
||||
from collections import deque
|
||||
import datetime as dt
|
||||
from typing import Any
|
||||
|
||||
# PyQt imports
|
||||
from PyQt6.QtCore import (
|
||||
@ -25,59 +25,6 @@ import ds
|
||||
import helpers
|
||||
|
||||
|
||||
class FadeGraphGenerator(QObject):
|
||||
finished = pyqtSignal(object, object)
|
||||
task_completed = pyqtSignal()
|
||||
|
||||
def generate_graph(self, plr: "PlaylistRow") -> None:
|
||||
fade_graph = FadeCurve(plr.path, plr.fade_at, plr.silence_at)
|
||||
if not fade_graph:
|
||||
log.error(f"Failed to create FadeCurve for {plr=}")
|
||||
return
|
||||
|
||||
self.finished.emit(plr, fade_graph)
|
||||
self.task_completed.emit()
|
||||
|
||||
|
||||
class FadegraphThreadController(QObject):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._thread = None
|
||||
self._generator = None
|
||||
self._request_queue = deque()
|
||||
|
||||
def generate_fade_graph(self, playlist_row):
|
||||
self._request_queue.append(playlist_row) # Use append for enqueue with deque
|
||||
if self._thread is None or not self._thread.isRunning():
|
||||
self._start_next_generation()
|
||||
|
||||
def _start_next_generation(self):
|
||||
if not self._request_queue: # Check if deque is empty
|
||||
return
|
||||
playlist_row = self._request_queue.popleft() # Use popleft for dequeue with deque
|
||||
self._start_thread(playlist_row)
|
||||
|
||||
def _start_thread(self, playlist_row):
|
||||
self._thread = QThread()
|
||||
self._generator = FadeGraphGenerator()
|
||||
self._generator.moveToThread(self._thread)
|
||||
self._generator.finished.connect(lambda row, graph: row.attach_fade_graph(graph))
|
||||
self._generator.task_completed.connect(self._cleanup_thread)
|
||||
self._thread.started.connect(lambda: self._generator.generate_graph(playlist_row))
|
||||
self._thread.start()
|
||||
|
||||
def _cleanup_thread(self):
|
||||
if self._thread:
|
||||
self._thread.quit()
|
||||
self._thread.wait()
|
||||
self._thread.deleteLater()
|
||||
self._thread = None
|
||||
self._generator.deleteLater()
|
||||
self._generator = None
|
||||
# Start the next request if any
|
||||
self._start_next_generation()
|
||||
|
||||
|
||||
class PlaylistRow:
|
||||
"""
|
||||
Object to manage playlist row and track.
|
||||
@ -94,7 +41,7 @@ class PlaylistRow:
|
||||
self.signals = MusicMusterSignals()
|
||||
self.end_of_track_signalled: bool = False
|
||||
self.end_time: dt.datetime | None = None
|
||||
self.fade_graph: FadeCurve | None = None
|
||||
self.fade_graph: Any | None = None
|
||||
self.fade_graph_start_updates: dt.datetime | None = None
|
||||
self.forecast_end_time: dt.datetime | None = None
|
||||
self.forecast_start_time: dt.datetime | None = None
|
||||
@ -104,7 +51,6 @@ class PlaylistRow:
|
||||
self.row_bg: str | None = None
|
||||
self.row_fg: str | None = None
|
||||
self.start_time: dt.datetime | None = None
|
||||
self.fadegraph_thread_controller = FadegraphThreadController()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
track_id = None
|
||||
@ -277,9 +223,6 @@ class PlaylistRow:
|
||||
# the change to the database.
|
||||
self.dto.row_number = value
|
||||
|
||||
def attach_fade_graph(self, fade_graph):
|
||||
self.fade_graph = fade_graph
|
||||
|
||||
def drop3db(self, enable: bool) -> None:
|
||||
"""
|
||||
If enable is true, drop output by 3db else restore to full volume
|
||||
@ -394,6 +337,40 @@ class PlaylistRow:
|
||||
self.fade_graph.tick(self.time_playing())
|
||||
|
||||
|
||||
class _AddFadeCurve(QObject):
|
||||
"""
|
||||
Initialising a fade curve introduces a noticeable delay so carry out in
|
||||
a thread.
|
||||
"""
|
||||
|
||||
finished = pyqtSignal()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
plr: PlaylistRow,
|
||||
track_path: str,
|
||||
track_fade_at: int,
|
||||
track_silence_at: int,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.plr = plr
|
||||
self.track_path = track_path
|
||||
self.track_fade_at = track_fade_at
|
||||
self.track_silence_at = track_silence_at
|
||||
|
||||
def run(self) -> None:
|
||||
"""
|
||||
Create fade curve and add to PlaylistTrack object
|
||||
"""
|
||||
|
||||
fc = FadeCurve(self.track_path, self.track_fade_at, self.track_silence_at)
|
||||
if not fc:
|
||||
log.error(f"Failed to create FadeCurve for {self.track_path=}")
|
||||
else:
|
||||
self.plr.fade_graph = fc
|
||||
self.finished.emit()
|
||||
|
||||
|
||||
class FadeCurve:
|
||||
GraphWidget: PlotWidget | None = None
|
||||
|
||||
@ -411,10 +388,10 @@ class FadeCurve:
|
||||
|
||||
# Start point of curve is Config.FADE_CURVE_MS_BEFORE_FADE
|
||||
# milliseconds before fade starts to silence
|
||||
self.start_ms = max(
|
||||
self.start_ms: int = max(
|
||||
0, track_fade_at - Config.FADE_CURVE_MS_BEFORE_FADE - 1
|
||||
)
|
||||
self.end_ms = track_silence_at
|
||||
self.end_ms: int = track_silence_at
|
||||
audio_segment = audio[self.start_ms : self.end_ms]
|
||||
self.graph_array = np.array(audio_segment.get_array_of_samples())
|
||||
|
||||
@ -493,7 +470,7 @@ class TrackSequence:
|
||||
self.next = None
|
||||
else:
|
||||
self.next = plr
|
||||
plr.fadegraph_thread_controller.generate_fade_graph(plr)
|
||||
self.create_fade_graph()
|
||||
|
||||
def move_next_to_current(self) -> None:
|
||||
"""
|
||||
@ -529,6 +506,27 @@ class TrackSequence:
|
||||
self.next = self.previous
|
||||
self.previous = None
|
||||
|
||||
def create_fade_graph(self) -> None:
|
||||
"""
|
||||
Initialise and add FadeCurve in a thread as it's slow
|
||||
"""
|
||||
|
||||
self.fadecurve_thread = QThread()
|
||||
if self.next is None:
|
||||
raise ApplicationError("hell in a handcart")
|
||||
self.worker = _AddFadeCurve(
|
||||
self.next,
|
||||
track_path=self.next.path,
|
||||
track_fade_at=self.next.fade_at,
|
||||
track_silence_at=self.next.silence_at,
|
||||
)
|
||||
self.worker.moveToThread(self.fadecurve_thread)
|
||||
self.fadecurve_thread.started.connect(self.worker.run)
|
||||
self.worker.finished.connect(self.fadecurve_thread.quit)
|
||||
self.worker.finished.connect(self.worker.deleteLater)
|
||||
self.fadecurve_thread.finished.connect(self.fadecurve_thread.deleteLater)
|
||||
self.fadecurve_thread.start()
|
||||
|
||||
def update(self) -> None:
|
||||
"""
|
||||
If a PlaylistRow is edited (moved, title changed, etc), the
|
||||
|
||||
@ -41,7 +41,7 @@ from classes import (
|
||||
MusicMusterSignals,
|
||||
PlaylistStyle,
|
||||
SelectedRows,
|
||||
TrackInfo,
|
||||
TrackInfo
|
||||
)
|
||||
from config import Config
|
||||
from dialogs import TrackInsertDialog
|
||||
@ -190,7 +190,9 @@ class PlaylistDelegate(QStyledItemDelegate):
|
||||
# Close editor if no changes have been made
|
||||
data_modified = False
|
||||
if isinstance(editor, QTextEdit):
|
||||
data_modified = self.original_model_data != editor.toPlainText()
|
||||
data_modified = (
|
||||
self.original_model_data != editor.toPlainText()
|
||||
)
|
||||
elif isinstance(editor, QDoubleSpinBox):
|
||||
data_modified = (
|
||||
self.original_model_data != int(editor.value()) * 1000
|
||||
@ -542,9 +544,7 @@ class PlaylistTab(QTableView):
|
||||
header_row = self.get_base_model().is_header_row(model_row_number)
|
||||
track_row = not header_row
|
||||
if self.track_sequence.current:
|
||||
this_is_current_row = (
|
||||
model_row_number == self.track_sequence.current.row_number
|
||||
)
|
||||
this_is_current_row = model_row_number == self.track_sequence.current.row_number
|
||||
else:
|
||||
this_is_current_row = False
|
||||
if self.track_sequence.next:
|
||||
@ -844,9 +844,7 @@ class PlaylistTab(QTableView):
|
||||
if not selected_indexes:
|
||||
return []
|
||||
|
||||
return sorted(
|
||||
list(set([self.model().mapToSource(a).row() for a in selected_indexes]))
|
||||
)
|
||||
return sorted(list(set([self.model().mapToSource(a).row() for a in selected_indexes])))
|
||||
|
||||
# @log_call
|
||||
def get_top_visible_row(self) -> int:
|
||||
|
||||
@ -1,68 +0,0 @@
|
||||
"""notes substrings, indexing, playlist faviourites, bitrate not null
|
||||
|
||||
Revision ID: 6d36cde8dea0
|
||||
Revises: 4fc2a9a82ab0
|
||||
Create Date: 2025-04-22 17:03:00.497945
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '6d36cde8dea0'
|
||||
down_revision = '4fc2a9a82ab0'
|
||||
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=True)
|
||||
|
||||
with op.batch_alter_table('playlist_rows', schema=None) as batch_op:
|
||||
batch_op.create_index(batch_op.f('ix_playlist_rows_playlist_id'), ['playlist_id'], unique=False)
|
||||
|
||||
with op.batch_alter_table('playlists', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('favourite', sa.Boolean(), nullable=False))
|
||||
|
||||
with op.batch_alter_table('tracks', schema=None) as batch_op:
|
||||
batch_op.alter_column('bitrate',
|
||||
existing_type=mysql.INTEGER(display_width=11),
|
||||
nullable=False)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade_() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('tracks', schema=None) as batch_op:
|
||||
batch_op.alter_column('bitrate',
|
||||
existing_type=mysql.INTEGER(display_width=11),
|
||||
nullable=True)
|
||||
|
||||
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.drop_index(batch_op.f('ix_playlist_rows_playlist_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 ###
|
||||
|
||||
@ -1,86 +0,0 @@
|
||||
"""Have id field reflect table name
|
||||
|
||||
Revision ID: 8e06d465923a
|
||||
Revises: 6d36cde8dea0
|
||||
Create Date: 2025-04-22 13:23:18.813024
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class TableInfo:
|
||||
table: str
|
||||
old: str
|
||||
new: str
|
||||
|
||||
|
||||
data = [
|
||||
TableInfo("notecolours", "id", "notecolour_id"),
|
||||
TableInfo("playdates", "id", "playdate_id"),
|
||||
TableInfo("playlists", "id", "playlist_id"),
|
||||
TableInfo("playlist_rows", "id", "playlistrow_id"),
|
||||
TableInfo("queries", "id", "query_id"),
|
||||
TableInfo("settings", "id", "setting_id"),
|
||||
TableInfo("tracks", "id", "track_id"),
|
||||
]
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '8e06d465923a'
|
||||
down_revision = '6d36cde8dea0'
|
||||
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:
|
||||
# Drop foreign key constraints
|
||||
op.drop_constraint('fk_playdates_track_id_tracks', 'playdates', type_='foreignkey')
|
||||
op.drop_constraint('fk_playlist_rows_track_id_tracks', 'playlist_rows', type_='foreignkey')
|
||||
|
||||
for record in data:
|
||||
op.alter_column(
|
||||
record.table,
|
||||
record.old,
|
||||
new_column_name=record.new,
|
||||
existing_type=sa.Integer(), # Specify the existing column type
|
||||
existing_nullable=False # If the column is NOT NULL, specify that too
|
||||
)
|
||||
|
||||
|
||||
# Recreate the foreign key constraints
|
||||
op.create_foreign_key('fk_playdates_track_id_tracks', 'playdates', 'tracks', ['track_id'], ['track_id'])
|
||||
op.create_foreign_key('fk_playlist_rows_track_id_tracks', 'playlist_rows', 'tracks', ['track_id'], ['track_id'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade_() -> None:
|
||||
# Drop foreign key constraints
|
||||
op.drop_constraint('fk_playdates_track_id_tracks', 'playdates', type_='foreignkey')
|
||||
op.drop_constraint('fk_playlist_rows_track_id_tracks', 'playlist_rows', type_='foreignkey')
|
||||
|
||||
for record in data:
|
||||
op.alter_column(
|
||||
record.table,
|
||||
record.new,
|
||||
new_column_name=record.old,
|
||||
existing_type=sa.Integer(), # Specify the existing column type
|
||||
existing_nullable=False # If the column is NOT NULL, specify that too
|
||||
)
|
||||
|
||||
# Recreate the foreign key constraints
|
||||
op.create_foreign_key('fk_playdates_track_id_tracks', 'playdates', 'tracks', ['track_id'], ['track_id'])
|
||||
op.create_foreign_key('fk_playlist_rows_track_id_tracks', 'playlist_rows', 'tracks', ['track_id'], ['track_id'])
|
||||
|
||||
# ### end Alembic commands ###
|
||||
@ -177,7 +177,9 @@ class MyTestCase(unittest.TestCase):
|
||||
track = tracks[0]
|
||||
assert track.title == "I'm So Afraid"
|
||||
assert track.artist == "Fleetwood Mac"
|
||||
track_file = os.path.join(self.musicstore, os.path.basename(test_track_path))
|
||||
track_file = os.path.join(
|
||||
self.musicstore, os.path.basename(test_track_path)
|
||||
)
|
||||
assert track.path == track_file
|
||||
assert os.path.exists(track_file)
|
||||
assert os.listdir(self.import_source) == []
|
||||
@ -220,7 +222,9 @@ class MyTestCase(unittest.TestCase):
|
||||
track = tracks[1]
|
||||
assert track.title == "The Lovecats"
|
||||
assert track.artist == "The Cure"
|
||||
track_file = os.path.join(self.musicstore, os.path.basename(test_track_path))
|
||||
track_file = os.path.join(
|
||||
self.musicstore, os.path.basename(test_track_path)
|
||||
)
|
||||
assert track.path == track_file
|
||||
assert os.path.exists(track_file)
|
||||
assert os.listdir(self.import_source) == []
|
||||
@ -271,7 +275,9 @@ class MyTestCase(unittest.TestCase):
|
||||
assert track.title == "The Lovecats"
|
||||
assert track.artist == "The Cure"
|
||||
assert track.track_id == 2
|
||||
track_file = os.path.join(self.musicstore, os.path.basename(test_track_path))
|
||||
track_file = os.path.join(
|
||||
self.musicstore, os.path.basename(test_track_path)
|
||||
)
|
||||
assert track.path == track_file
|
||||
assert os.path.exists(track_file)
|
||||
assert os.listdir(self.import_source) == []
|
||||
@ -464,7 +470,9 @@ class MyTestCase(unittest.TestCase):
|
||||
assert track.title == "The Lovecats xyz"
|
||||
assert track.artist == "The Cure"
|
||||
assert track.track_id == 2
|
||||
track_file = os.path.join(self.musicstore, os.path.basename(test_track_path))
|
||||
track_file = os.path.join(
|
||||
self.musicstore, os.path.basename(test_track_path)
|
||||
)
|
||||
assert track.path == track_file
|
||||
assert os.path.exists(track_file)
|
||||
assert os.listdir(self.import_source) == []
|
||||
|
||||
@ -34,9 +34,7 @@ class TestMMMiscTracks(unittest.TestCase):
|
||||
|
||||
# Create a playlist and model
|
||||
self.playlist = ds.playlist_create(PLAYLIST_NAME, template_id=0)
|
||||
self.model = playlistmodel.PlaylistModel(
|
||||
self.playlist.playlist_id, is_template=False
|
||||
)
|
||||
self.model = playlistmodel.PlaylistModel(self.playlist.playlist_id, is_template=False)
|
||||
|
||||
for row in range(len(self.test_tracks)):
|
||||
track_path = self.test_tracks[row % len(self.test_tracks)]
|
||||
@ -70,12 +68,18 @@ class TestMMMiscTracks(unittest.TestCase):
|
||||
self.model.selected_rows = [self.model.playlist_rows[START_ROW]]
|
||||
self.model.insert_row_signal_handler(
|
||||
InsertTrack(
|
||||
playlist_id=self.playlist.playlist_id, track_id=None, note="start+"
|
||||
playlist_id=self.playlist.playlist_id,
|
||||
track_id=None,
|
||||
note="start+"
|
||||
)
|
||||
)
|
||||
self.model.selected_rows = [self.model.playlist_rows[END_ROW]]
|
||||
self.model.insert_row_signal_handler(
|
||||
InsertTrack(playlist_id=self.playlist.playlist_id, track_id=None, note="-+")
|
||||
InsertTrack(
|
||||
playlist_id=self.playlist.playlist_id,
|
||||
track_id=None,
|
||||
note="-+"
|
||||
)
|
||||
)
|
||||
|
||||
prd = self.model.playlist_rows[START_ROW]
|
||||
@ -140,9 +144,7 @@ class TestMMMiscRowMove(unittest.TestCase):
|
||||
db.create_all()
|
||||
|
||||
self.playlist = ds.playlist_create(self.PLAYLIST_NAME, template_id=0)
|
||||
self.model = playlistmodel.PlaylistModel(
|
||||
self.playlist.playlist_id, is_template=False
|
||||
)
|
||||
self.model = playlistmodel.PlaylistModel(self.playlist.playlist_id, is_template=False)
|
||||
for row in range(self.ROWS_TO_CREATE):
|
||||
self.model.insert_row_signal_handler(
|
||||
InsertTrack(
|
||||
@ -163,7 +165,9 @@ class TestMMMiscRowMove(unittest.TestCase):
|
||||
assert self.model.rowCount() == self.ROWS_TO_CREATE
|
||||
self.model.insert_row_signal_handler(
|
||||
InsertTrack(
|
||||
playlist_id=self.playlist.playlist_id, track_id=None, note=note_text
|
||||
playlist_id=self.playlist.playlist_id,
|
||||
track_id=None,
|
||||
note=note_text
|
||||
)
|
||||
)
|
||||
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
||||
@ -188,7 +192,9 @@ class TestMMMiscRowMove(unittest.TestCase):
|
||||
|
||||
self.model.insert_row_signal_handler(
|
||||
InsertTrack(
|
||||
playlist_id=self.playlist.playlist_id, track_id=None, note=note_text
|
||||
playlist_id=self.playlist.playlist_id,
|
||||
track_id=None,
|
||||
note=note_text
|
||||
)
|
||||
)
|
||||
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
||||
@ -208,7 +214,9 @@ class TestMMMiscRowMove(unittest.TestCase):
|
||||
|
||||
self.model.insert_row_signal_handler(
|
||||
InsertTrack(
|
||||
playlist_id=self.playlist.playlist_id, track_id=None, note=note_text
|
||||
playlist_id=self.playlist.playlist_id,
|
||||
track_id=None,
|
||||
note=note_text
|
||||
)
|
||||
)
|
||||
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
||||
@ -249,13 +257,13 @@ class TestMMMiscRowMove(unittest.TestCase):
|
||||
for row in range(self.ROWS_TO_CREATE):
|
||||
model_dst.insert_row_signal_handler(
|
||||
InsertTrack(
|
||||
playlist_id=playlist_dst.playlist_id, track_id=None, note=str(row)
|
||||
playlist_id=playlist_dst.playlist_id,
|
||||
track_id=None,
|
||||
note=str(row)
|
||||
)
|
||||
)
|
||||
|
||||
model_src.move_rows_between_playlists(
|
||||
from_rows, to_row, playlist_dst.playlist_id
|
||||
)
|
||||
model_src.move_rows_between_playlists(from_rows, to_row, playlist_dst.playlist_id)
|
||||
|
||||
assert model_src.rowCount() == self.ROWS_TO_CREATE - len(from_rows)
|
||||
assert model_dst.rowCount() == self.ROWS_TO_CREATE + len(from_rows)
|
||||
@ -276,13 +284,13 @@ class TestMMMiscRowMove(unittest.TestCase):
|
||||
for row in range(self.ROWS_TO_CREATE):
|
||||
model_dst.insert_row_signal_handler(
|
||||
InsertTrack(
|
||||
playlist_id=playlist_dst.playlist_id, track_id=None, note=str(row)
|
||||
playlist_id=playlist_dst.playlist_id,
|
||||
track_id=None,
|
||||
note=str(row)
|
||||
)
|
||||
)
|
||||
|
||||
model_src.move_rows_between_playlists(
|
||||
from_rows, to_row, playlist_dst.playlist_id
|
||||
)
|
||||
model_src.move_rows_between_playlists(from_rows, to_row, playlist_dst.playlist_id)
|
||||
|
||||
# Check the rows of the destination model
|
||||
row_notes = []
|
||||
@ -310,13 +318,13 @@ class TestMMMiscRowMove(unittest.TestCase):
|
||||
for row in range(self.ROWS_TO_CREATE):
|
||||
model_dst.insert_row_signal_handler(
|
||||
InsertTrack(
|
||||
playlist_id=playlist_dst.playlist_id, track_id=None, note=str(row)
|
||||
playlist_id=playlist_dst.playlist_id,
|
||||
track_id=None,
|
||||
note=str(row)
|
||||
)
|
||||
)
|
||||
|
||||
model_src.move_rows_between_playlists(
|
||||
from_rows, to_row, playlist_dst.playlist_id
|
||||
)
|
||||
model_src.move_rows_between_playlists(from_rows, to_row, playlist_dst.playlist_id)
|
||||
|
||||
# Check the rows of the destination model
|
||||
row_notes = []
|
||||
|
||||
@ -31,7 +31,7 @@ class MyTestCase(unittest.TestCase):
|
||||
path="/alpha/bravo/charlie",
|
||||
silence_at=0,
|
||||
start_gap=0,
|
||||
title="abc",
|
||||
title="abc"
|
||||
)
|
||||
_ = ds.track_create(track1_meta)
|
||||
track2_meta = dict(
|
||||
@ -42,8 +42,8 @@ class MyTestCase(unittest.TestCase):
|
||||
path="/xray/yankee/zulu",
|
||||
silence_at=0,
|
||||
start_gap=0,
|
||||
title="xyz",
|
||||
)
|
||||
title="xyz"
|
||||
)
|
||||
track2 = ds.track_create(track2_meta)
|
||||
|
||||
# Add playdates
|
||||
@ -74,7 +74,7 @@ class MyTestCase(unittest.TestCase):
|
||||
|
||||
results = ds.tracks_filtered(filter)
|
||||
assert len(results) == 1
|
||||
assert "alpha" in results[0].path
|
||||
assert 'alpha' in results[0].path
|
||||
|
||||
def test_search_path_2(self):
|
||||
"""Search for unplayed track that doesn't exist"""
|
||||
@ -91,7 +91,7 @@ class MyTestCase(unittest.TestCase):
|
||||
|
||||
results = ds.tracks_filtered(filter)
|
||||
assert len(results) == 1
|
||||
assert "zulu" in results[0].path
|
||||
assert 'zulu' in results[0].path
|
||||
|
||||
def test_played_over_two_years_ago(self):
|
||||
"""Search for tracks played over 2 years ago"""
|
||||
@ -108,7 +108,7 @@ class MyTestCase(unittest.TestCase):
|
||||
|
||||
results = ds.tracks_filtered(filter)
|
||||
assert len(results) == 1
|
||||
assert "alpha" in results[0].path
|
||||
assert 'alpha' in results[0].path
|
||||
|
||||
def test_played_anytime(self):
|
||||
"""Search for tracks played over a year ago"""
|
||||
@ -117,4 +117,4 @@ class MyTestCase(unittest.TestCase):
|
||||
|
||||
results = ds.tracks_filtered(filter)
|
||||
assert len(results) == 1
|
||||
assert "zulu" in results[0].path
|
||||
assert 'zulu' in results[0].path
|
||||
|
||||
@ -100,7 +100,7 @@ class MyTestCase(unittest.TestCase):
|
||||
InsertTrack(
|
||||
playlist_id=playlist.playlist_id,
|
||||
track_id=self.track1.track_id,
|
||||
note=note_text,
|
||||
note=note_text
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user