Compare commits
5 Commits
6d012d7b5a
...
ad391aedc6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad391aedc6 | ||
|
|
735c927864 | ||
|
|
e2aac65dac | ||
|
|
86c3c3fd80 | ||
|
|
a4ba013306 |
@ -29,5 +29,3 @@ 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"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
notecolour_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.id}, substring={self.substring}, "
|
||||
f"<NoteColours(id={self.notecolour_id}, substring={self.substring}, "
|
||||
f"colour={self.colour}>"
|
||||
)
|
||||
|
||||
@ -92,9 +92,11 @@ class NoteColours(Model):
|
||||
class Playdates(Model):
|
||||
__tablename__ = "playdates"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
playdate_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.id", ondelete="CASCADE"))
|
||||
track_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("tracks.track_id", ondelete="CASCADE")
|
||||
)
|
||||
track: Mapped["Tracks"] = relationship(
|
||||
"Tracks",
|
||||
back_populates="playdates",
|
||||
@ -116,7 +118,7 @@ class Playdates(Model):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<Playdates(id={self.id}, track_id={self.track_id} "
|
||||
f"<Playdates(id={self.playdate_id}, track_id={self.track_id} "
|
||||
f"lastplayed={self.lastplayed}>"
|
||||
)
|
||||
|
||||
@ -128,7 +130,7 @@ class Playlists(Model):
|
||||
|
||||
__tablename__ = "playlists"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
playlist_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)
|
||||
@ -146,7 +148,7 @@ class Playlists(Model):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<Playlists(id={self.id}, name={self.name}, "
|
||||
f"<Playlists(id={self.playlist_id}, name={self.name}, "
|
||||
f"is_templatee={self.is_template}, open={self.open}>"
|
||||
)
|
||||
|
||||
@ -161,24 +163,24 @@ class Playlists(Model):
|
||||
|
||||
# If a template is specified, copy from it
|
||||
if template_id:
|
||||
PlaylistRows.copy_playlist(session, template_id, self.id)
|
||||
PlaylistRows.copy_playlist(session, template_id, self.playlist_id)
|
||||
|
||||
|
||||
class PlaylistRows(Model):
|
||||
__tablename__ = "playlist_rows"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
playlistrow_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.id", ondelete="CASCADE"), index=True
|
||||
ForeignKey("playlists.playlist_id", ondelete="CASCADE"), index=True
|
||||
)
|
||||
|
||||
playlist: Mapped[Playlists] = relationship(back_populates="rows")
|
||||
track_id: Mapped[Optional[int]] = mapped_column(
|
||||
ForeignKey("tracks.id", ondelete="CASCADE")
|
||||
ForeignKey("tracks.track_id", ondelete="CASCADE")
|
||||
)
|
||||
track: Mapped["Tracks"] = relationship(
|
||||
"Tracks",
|
||||
@ -190,7 +192,7 @@ class PlaylistRows(Model):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<PlaylistRows(id={self.id}, playlist_id={self.playlist_id}, "
|
||||
f"<PlaylistRows(id={self.playlistrow_id}, playlist_id={self.playlist_id}, "
|
||||
f"track_id={self.track_id}, "
|
||||
f"note={self.note}, row_number={self.row_number}>"
|
||||
)
|
||||
@ -217,10 +219,14 @@ class PlaylistRows(Model):
|
||||
class Queries(Model):
|
||||
__tablename__ = "queries"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
query_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."""
|
||||
@ -236,7 +242,7 @@ class Queries(Model):
|
||||
filter = property(_get_filter, _set_filter)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Queries(id={self.id}, name={self.name}, filter={self.filter})>"
|
||||
return f"<Queries(id={self.query_id}, name={self.name}, filter={self.filter})>"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -260,7 +266,7 @@ class Settings(Model):
|
||||
|
||||
__tablename__ = "settings"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
setting_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)
|
||||
@ -268,7 +274,7 @@ class Settings(Model):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<Settings(id={self.id}, name={self.name}, "
|
||||
f"<Settings(id={self.setting_id}, name={self.name}, "
|
||||
f"f_datetime={self.f_datetime}, f_int={self.f_int}, f_string={self.f_string}>"
|
||||
)
|
||||
|
||||
@ -282,7 +288,7 @@ class Settings(Model):
|
||||
class Tracks(Model):
|
||||
__tablename__ = "tracks"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
track_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)
|
||||
@ -308,7 +314,7 @@ class Tracks(Model):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<Track(id={self.id}, title={self.title}, "
|
||||
f"<Track(id={self.track_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.id,
|
||||
notecolour_id=record.notecolour_id,
|
||||
substring=record.substring,
|
||||
colour=record.colour,
|
||||
enabled=record.enabled,
|
||||
@ -211,7 +211,7 @@ def _tracks_where(
|
||||
)
|
||||
stmt = (
|
||||
select(
|
||||
Tracks.id.label("track_id"),
|
||||
Tracks.track_id,
|
||||
Tracks.artist,
|
||||
Tracks.bitrate,
|
||||
Tracks.duration,
|
||||
@ -223,7 +223,9 @@ def _tracks_where(
|
||||
Tracks.title,
|
||||
latest_playdate_subq.c.lastplayed,
|
||||
)
|
||||
.outerjoin(latest_playdate_subq, Tracks.id == latest_playdate_subq.c.track_id)
|
||||
.outerjoin(
|
||||
latest_playdate_subq, Tracks.track_id == latest_playdate_subq.c.track_id
|
||||
)
|
||||
.where(query)
|
||||
)
|
||||
|
||||
@ -259,7 +261,7 @@ def track_add_to_header(playlistrow_id: int, track_id: int) -> None:
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(PlaylistRows)
|
||||
.where(PlaylistRows.id == playlistrow_id)
|
||||
.where(PlaylistRows.playlistrow_id == playlistrow_id)
|
||||
.values(track_id=track_id)
|
||||
)
|
||||
session.commit()
|
||||
@ -268,7 +270,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.id > 0)
|
||||
return _tracks_where(Tracks.track_id > 0)
|
||||
|
||||
|
||||
def tracks_by_artist(filter_str: str) -> list[TrackDTO]:
|
||||
@ -284,7 +286,7 @@ def track_by_id(track_id: int) -> TrackDTO | None:
|
||||
Return track with specified id
|
||||
"""
|
||||
|
||||
track_list = _tracks_where(Tracks.id == track_id)
|
||||
track_list = _tracks_where(Tracks.track_id == track_id)
|
||||
if not track_list:
|
||||
return None
|
||||
if len(track_list) > 1:
|
||||
@ -333,7 +335,7 @@ def track_create(metadata: dict[str, str | int | float]) -> TrackDTO:
|
||||
bitrate=int(metadata["bitrate"]),
|
||||
)
|
||||
|
||||
track_id = track.id
|
||||
track_id = track.track_id
|
||||
session.commit()
|
||||
except Exception:
|
||||
raise ApplicationError("Can't create Track")
|
||||
@ -387,8 +389,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.id == Playdates.track_id).where(
|
||||
Playdates.id.is_(None)
|
||||
query = query.outerjoin(Playdates, Tracks.track_id == Playdates.track_id).where(
|
||||
Playdates.playdate_id.is_(None)
|
||||
)
|
||||
else:
|
||||
# Last played specification
|
||||
@ -414,7 +416,7 @@ def tracks_filtered(filter: Filter) -> list[TrackDTO]:
|
||||
.group_by(Playdates.track_id)
|
||||
.subquery()
|
||||
)
|
||||
query = query.join(subquery, Tracks.id == subquery.c.track_id).where(
|
||||
query = query.join(subquery, Tracks.track_id == subquery.c.track_id).where(
|
||||
subquery.c.max_last_played < before
|
||||
)
|
||||
|
||||
@ -438,7 +440,7 @@ def tracks_filtered(filter: Filter) -> list[TrackDTO]:
|
||||
silence_at=record.silence_at,
|
||||
start_gap=record.start_gap,
|
||||
title=record.title,
|
||||
track_id=record.id,
|
||||
track_id=record.track_id,
|
||||
)
|
||||
results.append(dto)
|
||||
|
||||
@ -498,7 +500,7 @@ def _playlist_check_playlist(
|
||||
|
||||
msg = (
|
||||
"_check_playlist_integrity: incorrect row number "
|
||||
f"({plr.id=}, {plr.row_number=}, {idx=})"
|
||||
f"({plr.playlistrow_id=}, {plr.row_number=}, {idx=})"
|
||||
)
|
||||
if fix:
|
||||
log.debug(msg)
|
||||
@ -542,7 +544,7 @@ def _playlists_where(
|
||||
select(
|
||||
Playlists.favourite,
|
||||
Playlists.is_template,
|
||||
Playlists.id.label("playlist_id"),
|
||||
Playlists.playlist_id,
|
||||
Playlists.name,
|
||||
Playlists.open,
|
||||
)
|
||||
@ -570,7 +572,7 @@ def _playlists_where(
|
||||
def playlists_all():
|
||||
"""Return all playlists"""
|
||||
|
||||
return _playlists_where(Playlists.id > 0)
|
||||
return _playlists_where(Playlists.playlist_id > 0)
|
||||
|
||||
|
||||
# @log_call
|
||||
@ -579,7 +581,7 @@ def playlist_by_id(playlist_id: int) -> PlaylistDTO | None:
|
||||
Return playlist with specified id
|
||||
"""
|
||||
|
||||
playlist_list = _playlists_where(Playlists.id == playlist_id)
|
||||
playlist_list = _playlists_where(Playlists.playlist_id == playlist_id)
|
||||
if not playlist_list:
|
||||
return None
|
||||
if len(playlist_list) > 1:
|
||||
@ -627,7 +629,7 @@ def playlist_create(
|
||||
try:
|
||||
playlist = Playlists(session, name, template_id)
|
||||
playlist.is_template = as_template
|
||||
playlist_id = playlist.id
|
||||
playlist_id = playlist.playlist_id
|
||||
session.commit()
|
||||
except Exception:
|
||||
raise ApplicationError("Can't create Playlist")
|
||||
@ -679,7 +681,7 @@ def playlist_insert_row(
|
||||
track_id=track_id,
|
||||
)
|
||||
session.commit()
|
||||
playlist_row_id = playlist_row.id
|
||||
playlist_row_id = playlist_row.playlistrow_id
|
||||
|
||||
# Sanity check
|
||||
_playlist_check_playlist(session, playlist_id, fix=False)
|
||||
@ -697,7 +699,9 @@ def playlist_mark_status(playlist_id: int, open: bool) -> None:
|
||||
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(Playlists).where(Playlists.id == playlist_id).values(open=open)
|
||||
update(Playlists)
|
||||
.where(Playlists.playlist_id == playlist_id)
|
||||
.values(open=open)
|
||||
)
|
||||
|
||||
session.commit()
|
||||
@ -744,9 +748,7 @@ 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:
|
||||
@ -765,12 +767,8 @@ def _playlist_move_rows_between_playlists(
|
||||
|
||||
for from_row in from_rows:
|
||||
plrid = old_row_to_id[from_row]
|
||||
update_list.append(
|
||||
{"id": plrid, "row_number": next_row}
|
||||
)
|
||||
update_list.append(
|
||||
{"id": plrid, "playlist_id": to_playlist_id}
|
||||
)
|
||||
update_list.append({"playlistrow_id": plrid, "row_number": next_row})
|
||||
update_list.append({"playlistrow_id": plrid, "playlist_id": to_playlist_id})
|
||||
next_row += 1
|
||||
|
||||
session.execute(update(PlaylistRows), update_list)
|
||||
@ -778,7 +776,7 @@ def _playlist_move_rows_between_playlists(
|
||||
|
||||
# Resequence row numbers in source
|
||||
_playlist_check_playlist(session, from_playlist_id, fix=True)
|
||||
# Sanity check destionation
|
||||
# Sanity check destination
|
||||
_playlist_check_playlist(session, from_playlist_id, fix=False)
|
||||
|
||||
|
||||
@ -788,8 +786,7 @@ 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
|
||||
@ -813,9 +810,7 @@ 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:
|
||||
@ -842,9 +837,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({"id": plrid, "row_number": new_row})
|
||||
update_list.append({"playlistrow_id": plrid, "row_number": new_row})
|
||||
|
||||
# Updte database
|
||||
# Update database
|
||||
session.execute(update(PlaylistRows), update_list)
|
||||
session.commit()
|
||||
|
||||
@ -867,7 +862,9 @@ def playlist_rename(playlist_id: int, new_name: str) -> None:
|
||||
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(Playlists).where(Playlists.id == playlist_id).values(name=new_name)
|
||||
update(Playlists)
|
||||
.where(Playlists.playlist_id == playlist_id)
|
||||
.values(name=new_name)
|
||||
)
|
||||
|
||||
session.commit()
|
||||
@ -989,12 +986,14 @@ def playlist_save_tabs(playlist_id_to_tab: dict[int, int]) -> None:
|
||||
# Clear all existing tab numbers
|
||||
session.execute(
|
||||
update(Playlists)
|
||||
.where(Playlists.id.in_(playlist_id_to_tab.keys()))
|
||||
.where(Playlists.playlist_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.id == playlist_id).values(tab=tab)
|
||||
update(Playlists)
|
||||
.where(Playlists.playlist_id == playlist_id)
|
||||
.values(tab=tab)
|
||||
)
|
||||
session.commit()
|
||||
|
||||
@ -1006,7 +1005,7 @@ def playlist_update_template_favourite(template_id: int, favourite: bool) -> Non
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(Playlists)
|
||||
.where(Playlists.id == template_id)
|
||||
.where(Playlists.playlist_id == template_id)
|
||||
.values(favourite=favourite)
|
||||
)
|
||||
session.commit()
|
||||
@ -1024,7 +1023,9 @@ def playlistrow_by_id(playlistrow_id: int) -> PlaylistRowDTO | None:
|
||||
with db.Session() as session:
|
||||
record = (
|
||||
session.execute(
|
||||
select(PlaylistRows).where(PlaylistRows.id == playlistrow_id)
|
||||
select(PlaylistRows).where(
|
||||
PlaylistRows.playlistrow_id == playlistrow_id
|
||||
)
|
||||
)
|
||||
.scalars()
|
||||
.one_or_none()
|
||||
@ -1040,7 +1041,7 @@ def playlistrow_by_id(playlistrow_id: int) -> PlaylistRowDTO | None:
|
||||
note=record.note,
|
||||
played=record.played,
|
||||
playlist_id=record.playlist_id,
|
||||
playlistrow_id=record.id,
|
||||
playlistrow_id=record.playlistrow_id,
|
||||
row_number=record.row_number,
|
||||
track=track,
|
||||
)
|
||||
@ -1074,7 +1075,7 @@ def playlistrows_by_playlist(
|
||||
note=record.note,
|
||||
played=record.played,
|
||||
playlist_id=record.playlist_id,
|
||||
playlistrow_id=record.id,
|
||||
playlistrow_id=record.playlistrow_id,
|
||||
row_number=record.row_number,
|
||||
track=track,
|
||||
)
|
||||
@ -1112,7 +1113,7 @@ def playlistrow_played(playlistrow_id: int, status: bool) -> None:
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(PlaylistRows)
|
||||
.where(PlaylistRows.id == playlistrow_id)
|
||||
.where(PlaylistRows.playlistrow_id == playlistrow_id)
|
||||
.values(played=status)
|
||||
)
|
||||
session.commit()
|
||||
@ -1165,7 +1166,7 @@ def playdates_between_dates(
|
||||
end = dt.datetime.now()
|
||||
|
||||
stmt = select(
|
||||
Playdates.id.label("playdate_id"),
|
||||
Playdates.playdate_id,
|
||||
Playdates.lastplayed,
|
||||
Playdates.track_id,
|
||||
Playdates.track,
|
||||
@ -1213,7 +1214,7 @@ def _queries_where(
|
||||
favourite=record.favourite,
|
||||
filter=record.filter,
|
||||
name=record.name,
|
||||
query_id=record.id,
|
||||
query_id=record.query_id,
|
||||
)
|
||||
results.append(dto)
|
||||
|
||||
@ -1223,14 +1224,14 @@ def _queries_where(
|
||||
def queries_all(favourites_only: bool = False) -> list[QueryDTO]:
|
||||
"""Return a list of all queries"""
|
||||
|
||||
query = Queries.id > 0
|
||||
query = Queries.query_id > 0
|
||||
return _queries_where(query)
|
||||
|
||||
|
||||
def query_by_id(query_id: int) -> QueryDTO | None:
|
||||
"""Return query"""
|
||||
|
||||
query_list = _queries_where(Queries.id == query_id)
|
||||
query_list = _queries_where(Queries.query_id == query_id)
|
||||
if not query_list:
|
||||
return None
|
||||
if len(query_list) > 1:
|
||||
@ -1246,7 +1247,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.id
|
||||
query_id = query.query_id
|
||||
session.commit()
|
||||
except Exception:
|
||||
raise ApplicationError("Can't create Query")
|
||||
@ -1272,7 +1273,9 @@ def query_update_favourite(query_id: int, favourite: bool) -> None:
|
||||
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(Queries).where(Queries.id == query_id).values(favourite=favourite)
|
||||
update(Queries)
|
||||
.where(Queries.query_id == query_id)
|
||||
.values(favourite=favourite)
|
||||
)
|
||||
session.commit()
|
||||
|
||||
@ -1282,7 +1285,7 @@ def query_update_filter(query_id: int, filter: Filter) -> None:
|
||||
|
||||
with db.Session() as session:
|
||||
session.execute(
|
||||
update(Queries).where(Queries.id == query_id).values(filter=filter)
|
||||
update(Queries).where(Queries.query_id == query_id).values(filter=filter)
|
||||
)
|
||||
session.commit()
|
||||
|
||||
@ -1291,7 +1294,9 @@ def query_update_name(query_id: int, name: str) -> None:
|
||||
"""Update query name"""
|
||||
|
||||
with db.Session() as session:
|
||||
session.execute(update(Queries).where(Queries.id == query_id).values(name=name))
|
||||
session.execute(
|
||||
update(Queries).where(Queries.query_id == query_id).values(name=name)
|
||||
)
|
||||
session.commit()
|
||||
|
||||
|
||||
|
||||
@ -101,9 +101,13 @@ 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)
|
||||
@ -147,6 +151,7 @@ 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,8 +179,12 @@ 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)
|
||||
@ -197,9 +201,7 @@ 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"{name=}, args={args}")
|
||||
log.debug(f"SIGNAL: {name=}, args={args}")
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -1579,8 +1579,9 @@ class Window(QMainWindow):
|
||||
|
||||
def solicit_template_to_use(self, template_prompt: str | None = None) -> int | None:
|
||||
"""
|
||||
Have user select a template. Return the template.id, or None if they cancel.
|
||||
template_id of zero means don't use a template.
|
||||
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.
|
||||
"""
|
||||
|
||||
template_name_id_list: list[tuple[str, int]] = []
|
||||
@ -1742,7 +1743,9 @@ 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)
|
||||
@ -1765,7 +1768,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:
|
||||
@ -2038,7 +2041,7 @@ class Window(QMainWindow):
|
||||
source_playlist_id = self.current.playlist_id
|
||||
|
||||
for playlist in ds.playlists_all():
|
||||
if playlist.id == source_playlist_id:
|
||||
if playlist.playlist_id == source_playlist_id:
|
||||
continue
|
||||
else:
|
||||
playlists.append(playlist)
|
||||
@ -2562,9 +2565,7 @@ 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
|
||||
@ -2746,7 +2747,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
|
||||
@ -2756,7 +2757,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,11 +93,15 @@ 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)
|
||||
@ -150,7 +154,9 @@ 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
|
||||
"""
|
||||
@ -1547,7 +1553,9 @@ 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
|
||||
@ -1582,7 +1590,9 @@ 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,6 +25,59 @@ 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.
|
||||
@ -41,7 +94,7 @@ class PlaylistRow:
|
||||
self.signals = MusicMusterSignals()
|
||||
self.end_of_track_signalled: bool = False
|
||||
self.end_time: dt.datetime | None = None
|
||||
self.fade_graph: Any | None = None
|
||||
self.fade_graph: FadeCurve | 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
|
||||
@ -51,6 +104,7 @@ 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
|
||||
@ -223,6 +277,9 @@ 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
|
||||
@ -337,40 +394,6 @@ 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
|
||||
|
||||
@ -388,10 +411,10 @@ class FadeCurve:
|
||||
|
||||
# Start point of curve is Config.FADE_CURVE_MS_BEFORE_FADE
|
||||
# milliseconds before fade starts to silence
|
||||
self.start_ms: int = max(
|
||||
self.start_ms = max(
|
||||
0, track_fade_at - Config.FADE_CURVE_MS_BEFORE_FADE - 1
|
||||
)
|
||||
self.end_ms: int = track_silence_at
|
||||
self.end_ms = track_silence_at
|
||||
audio_segment = audio[self.start_ms : self.end_ms]
|
||||
self.graph_array = np.array(audio_segment.get_array_of_samples())
|
||||
|
||||
@ -470,7 +493,7 @@ class TrackSequence:
|
||||
self.next = None
|
||||
else:
|
||||
self.next = plr
|
||||
self.create_fade_graph()
|
||||
plr.fadegraph_thread_controller.generate_fade_graph(plr)
|
||||
|
||||
def move_next_to_current(self) -> None:
|
||||
"""
|
||||
@ -506,27 +529,6 @@ 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,9 +190,7 @@ 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
|
||||
@ -544,7 +542,9 @@ 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,7 +844,9 @@ 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:
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
"""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 ###
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
"""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,9 +177,7 @@ 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) == []
|
||||
@ -222,9 +220,7 @@ 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) == []
|
||||
@ -275,9 +271,7 @@ 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) == []
|
||||
@ -470,9 +464,7 @@ 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,7 +34,9 @@ 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)]
|
||||
@ -68,18 +70,12 @@ 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]
|
||||
@ -144,7 +140,9 @@ 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(
|
||||
@ -165,9 +163,7 @@ 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
|
||||
@ -192,9 +188,7 @@ 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
|
||||
@ -214,9 +208,7 @@ 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
|
||||
@ -257,13 +249,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)
|
||||
@ -284,13 +276,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 = []
|
||||
@ -318,13 +310,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