|
|
|
|
@ -4,7 +4,7 @@ import os.path
|
|
|
|
|
import re
|
|
|
|
|
import stackprinter # type: ignore
|
|
|
|
|
|
|
|
|
|
from dbconfig import Session
|
|
|
|
|
from dbconfig import Session, scoped_session
|
|
|
|
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from typing import List, Optional
|
|
|
|
|
@ -61,7 +61,8 @@ class Carts(Base):
|
|
|
|
|
f"name={self.name}, path={self.path}>"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def __init__(self, session: Session, cart_number: int, name: str = None,
|
|
|
|
|
def __init__(self, session: scoped_session, cart_number: int,
|
|
|
|
|
name: Optional[str] = None,
|
|
|
|
|
duration: int = None, path: str = None,
|
|
|
|
|
enabled: bool = True) -> None:
|
|
|
|
|
"""Create new cart"""
|
|
|
|
|
@ -94,7 +95,7 @@ class NoteColours(Base):
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def get_colour(session: Session, text: str) -> Optional[str]:
|
|
|
|
|
def get_colour(session: scoped_session, text: str) -> Optional[str]:
|
|
|
|
|
"""
|
|
|
|
|
Parse text and return colour string if matched, else None
|
|
|
|
|
"""
|
|
|
|
|
@ -139,7 +140,7 @@ class Playdates(Base):
|
|
|
|
|
f"lastplayed={self.lastplayed}>"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def __init__(self, session: Session, track_id: int) -> None:
|
|
|
|
|
def __init__(self, session: scoped_session, track_id: int) -> None:
|
|
|
|
|
"""Record that track was played"""
|
|
|
|
|
|
|
|
|
|
self.lastplayed = datetime.now()
|
|
|
|
|
@ -148,7 +149,7 @@ class Playdates(Base):
|
|
|
|
|
session.commit()
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def last_played(session: Session, track_id: int) -> Optional[datetime]:
|
|
|
|
|
def last_played(session: scoped_session, track_id: int) -> Optional[datetime]:
|
|
|
|
|
"""Return datetime track last played or None"""
|
|
|
|
|
|
|
|
|
|
last_played = session.execute(
|
|
|
|
|
@ -164,7 +165,7 @@ class Playdates(Base):
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def played_after(session: Session, since: datetime) -> List["Playdates"]:
|
|
|
|
|
def played_after(session: scoped_session, since: datetime) -> List["Playdates"]:
|
|
|
|
|
"""Return a list of Playdates objects since passed time"""
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
@ -206,12 +207,12 @@ class Playlists(Base):
|
|
|
|
|
f"is_templatee={self.is_template}>"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def __init__(self, session: Session, name: str) -> None:
|
|
|
|
|
def __init__(self, session: scoped_session, name: str) -> None:
|
|
|
|
|
self.name = name
|
|
|
|
|
session.add(self)
|
|
|
|
|
session.commit()
|
|
|
|
|
|
|
|
|
|
def close(self, session: Session) -> None:
|
|
|
|
|
def close(self, session: scoped_session) -> None:
|
|
|
|
|
"""Mark playlist as unloaded"""
|
|
|
|
|
|
|
|
|
|
closed_idx = self.tab
|
|
|
|
|
@ -227,7 +228,7 @@ class Playlists(Base):
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def create_playlist_from_template(cls,
|
|
|
|
|
session: Session,
|
|
|
|
|
session: scoped_session,
|
|
|
|
|
template: "Playlists",
|
|
|
|
|
playlist_name: str) \
|
|
|
|
|
-> "Playlists":
|
|
|
|
|
@ -239,7 +240,7 @@ class Playlists(Base):
|
|
|
|
|
return playlist
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def get_all(cls, session: Session) -> List["Playlists"]:
|
|
|
|
|
def get_all(cls, session: scoped_session) -> List["Playlists"]:
|
|
|
|
|
"""Returns a list of all playlists ordered by last use"""
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
@ -253,7 +254,7 @@ class Playlists(Base):
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def get_all_templates(cls, session: Session) -> List["Playlists"]:
|
|
|
|
|
def get_all_templates(cls, session: scoped_session) -> List["Playlists"]:
|
|
|
|
|
"""Returns a list of all templates ordered by name"""
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
@ -267,7 +268,7 @@ class Playlists(Base):
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def get_closed(cls, session: Session) -> List["Playlists"]:
|
|
|
|
|
def get_closed(cls, session: scoped_session) -> List["Playlists"]:
|
|
|
|
|
"""Returns a list of all closed playlists ordered by last use"""
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
@ -284,7 +285,7 @@ class Playlists(Base):
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def get_open(cls, session: Session) -> List[Optional["Playlists"]]:
|
|
|
|
|
def get_open(cls, session: scoped_session) -> List[Optional["Playlists"]]:
|
|
|
|
|
"""
|
|
|
|
|
Return a list of loaded playlists ordered by tab order.
|
|
|
|
|
"""
|
|
|
|
|
@ -299,14 +300,14 @@ class Playlists(Base):
|
|
|
|
|
.all()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def mark_open(self, session: Session, tab_index: int) -> None:
|
|
|
|
|
def mark_open(self, session: scoped_session, tab_index: int) -> None:
|
|
|
|
|
"""Mark playlist as loaded and used now"""
|
|
|
|
|
|
|
|
|
|
self.tab = tab_index
|
|
|
|
|
self.last_used = datetime.now()
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def move_tab(session: Session, frm: int, to: int) -> None:
|
|
|
|
|
def move_tab(session: scoped_session, frm: int, to: int) -> None:
|
|
|
|
|
"""Move tabs"""
|
|
|
|
|
|
|
|
|
|
row_frm = session.execute(
|
|
|
|
|
@ -326,7 +327,7 @@ class Playlists(Base):
|
|
|
|
|
row_frm.tab = to
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def save_as_template(session: Session,
|
|
|
|
|
def save_as_template(session: scoped_session,
|
|
|
|
|
playlist_id: int, template_name: str) -> None:
|
|
|
|
|
"""Save passed playlist as new template"""
|
|
|
|
|
|
|
|
|
|
@ -357,7 +358,7 @@ class PlaylistRows(Base):
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def __init__(self,
|
|
|
|
|
session: Session,
|
|
|
|
|
session: scoped_session,
|
|
|
|
|
playlist_id: int,
|
|
|
|
|
track_id: int,
|
|
|
|
|
row_number: int,
|
|
|
|
|
@ -372,8 +373,17 @@ class PlaylistRows(Base):
|
|
|
|
|
session.add(self)
|
|
|
|
|
session.flush()
|
|
|
|
|
|
|
|
|
|
def append_note(self, extra_note: str) -> None:
|
|
|
|
|
"""Append passed note to any existing note"""
|
|
|
|
|
|
|
|
|
|
current_note = self.note
|
|
|
|
|
if current_note:
|
|
|
|
|
self.note = current_note + '\n' + extra_note
|
|
|
|
|
else:
|
|
|
|
|
self.note = extra_note
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def copy_playlist(session: Session,
|
|
|
|
|
def copy_playlist(session: scoped_session,
|
|
|
|
|
src_id: int,
|
|
|
|
|
dst_id: int) -> None:
|
|
|
|
|
"""Copy playlist entries"""
|
|
|
|
|
@ -388,7 +398,7 @@ class PlaylistRows(Base):
|
|
|
|
|
plr.note)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def delete_plrids_not_in_list(session: Session, playlist_id: int,
|
|
|
|
|
def delete_plrids_not_in_list(session: scoped_session, playlist_id: int,
|
|
|
|
|
plrids: List["PlaylistRows"]) -> None:
|
|
|
|
|
"""
|
|
|
|
|
Delete rows in given playlist that have a higher row number
|
|
|
|
|
@ -406,7 +416,7 @@ class PlaylistRows(Base):
|
|
|
|
|
session.commit()
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def fixup_rownumbers(session: Session, playlist_id: int) -> None:
|
|
|
|
|
def fixup_rownumbers(session: scoped_session, playlist_id: int) -> None:
|
|
|
|
|
"""
|
|
|
|
|
Ensure the row numbers for passed playlist have no gaps
|
|
|
|
|
"""
|
|
|
|
|
@ -424,7 +434,7 @@ class PlaylistRows(Base):
|
|
|
|
|
session.commit()
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def get_track_plr(session: Session, track_id: int,
|
|
|
|
|
def get_track_plr(session: scoped_session, track_id: int,
|
|
|
|
|
playlist_id: int) -> Optional["PlaylistRows"]:
|
|
|
|
|
"""Return first matching PlaylistRows object or None"""
|
|
|
|
|
|
|
|
|
|
@ -438,7 +448,7 @@ class PlaylistRows(Base):
|
|
|
|
|
).first()
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def get_last_used_row(session: Session, playlist_id: int) -> Optional[int]:
|
|
|
|
|
def get_last_used_row(session: scoped_session, playlist_id: int) -> Optional[int]:
|
|
|
|
|
"""Return the last used row for playlist, or None if no rows"""
|
|
|
|
|
|
|
|
|
|
return session.execute(
|
|
|
|
|
@ -447,7 +457,7 @@ class PlaylistRows(Base):
|
|
|
|
|
).scalar_one()
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def get_played_rows(cls, session: Session,
|
|
|
|
|
def get_played_rows(cls, session: scoped_session,
|
|
|
|
|
playlist_id: int) -> List[int]:
|
|
|
|
|
"""
|
|
|
|
|
For passed playlist, return a list of rows that
|
|
|
|
|
@ -466,7 +476,7 @@ class PlaylistRows(Base):
|
|
|
|
|
return plrs
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def get_rows_with_tracks(cls, session: Session,
|
|
|
|
|
def get_rows_with_tracks(cls, session: scoped_session,
|
|
|
|
|
playlist_id: int) -> List[int]:
|
|
|
|
|
"""
|
|
|
|
|
For passed playlist, return a list of rows that
|
|
|
|
|
@ -485,10 +495,10 @@ class PlaylistRows(Base):
|
|
|
|
|
return plrs
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def get_unplayed_rows(cls, session: Session,
|
|
|
|
|
playlist_id: int) -> List[int]:
|
|
|
|
|
def get_unplayed_rows(cls, session: scoped_session,
|
|
|
|
|
playlist_id: int) -> List["PlaylistRows"]:
|
|
|
|
|
"""
|
|
|
|
|
For passed playlist, return a list of track rows that
|
|
|
|
|
For passed playlist, return a list of playlist rows that
|
|
|
|
|
have not been played.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
@ -505,7 +515,7 @@ class PlaylistRows(Base):
|
|
|
|
|
return plrs
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def move_rows_down(session: Session, playlist_id: int, starting_row: int,
|
|
|
|
|
def move_rows_down(session: scoped_session, playlist_id: int, starting_row: int,
|
|
|
|
|
move_by: int) -> None:
|
|
|
|
|
"""
|
|
|
|
|
Create space to insert move_by additional rows by incremented row
|
|
|
|
|
@ -522,7 +532,7 @@ class PlaylistRows(Base):
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def indexed_by_id(session: Session, plr_ids: List[int]) -> dict:
|
|
|
|
|
def indexed_by_id(session: scoped_session, plr_ids: List[int]) -> dict:
|
|
|
|
|
"""
|
|
|
|
|
Return a dictionary of playlist_rows indexed by their plr id from
|
|
|
|
|
the passed plr_id list.
|
|
|
|
|
@ -558,7 +568,7 @@ class Settings(Base):
|
|
|
|
|
return f"<Settings(id={self.id}, name={self.name}, {value=}>"
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def get_int_settings(cls, session: Session, name: str) -> "Settings":
|
|
|
|
|
def get_int_settings(cls, session: scoped_session, name: str) -> "Settings":
|
|
|
|
|
"""Get setting for an integer or return new setting record"""
|
|
|
|
|
|
|
|
|
|
int_setting: Settings
|
|
|
|
|
@ -577,7 +587,7 @@ class Settings(Base):
|
|
|
|
|
|
|
|
|
|
return int_setting
|
|
|
|
|
|
|
|
|
|
def update(self, session: Session, data: "Settings"):
|
|
|
|
|
def update(self, session: scoped_session, data: "Settings"):
|
|
|
|
|
for key, value in data.items():
|
|
|
|
|
assert hasattr(self, key)
|
|
|
|
|
setattr(self, key, value)
|
|
|
|
|
@ -609,7 +619,7 @@ class Tracks(Base):
|
|
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
|
self,
|
|
|
|
|
session: Session,
|
|
|
|
|
session: scoped_session,
|
|
|
|
|
path: str,
|
|
|
|
|
title: Optional[str] = None,
|
|
|
|
|
artist: Optional[str] = None,
|
|
|
|
|
@ -640,7 +650,7 @@ class Tracks(Base):
|
|
|
|
|
return session.execute(select(cls)).scalars().all()
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def get_by_path(cls, session: Session, path: str) -> "Tracks":
|
|
|
|
|
def get_by_path(cls, session: scoped_session, path: str) -> "Tracks":
|
|
|
|
|
"""
|
|
|
|
|
Return track with passed path, or None.
|
|
|
|
|
"""
|
|
|
|
|
@ -656,7 +666,7 @@ class Tracks(Base):
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def search_artists(cls, session: Session, text: str) -> List["Tracks"]:
|
|
|
|
|
def search_artists(cls, session: scoped_session, text: str) -> List["Tracks"]:
|
|
|
|
|
"""Search case-insenstively for artists containing str"""
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
@ -670,7 +680,7 @@ class Tracks(Base):
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def search_titles(cls, session: Session, text: str) -> List["Tracks"]:
|
|
|
|
|
def search_titles(cls, session: scoped_session, text: str) -> List["Tracks"]:
|
|
|
|
|
"""Search case-insenstively for titles containing str"""
|
|
|
|
|
return (
|
|
|
|
|
session.execute(
|
|
|
|
|
|