From af895ef577d821afed29f45c323a15509bf86ebe Mon Sep 17 00:00:00 2001 From: Keith Edmunds Date: Sat, 27 Mar 2021 22:41:29 +0000 Subject: [PATCH] Store playlist in db --- app/model.py | 94 ++++++++++++++++++- app/musicmuster.py | 18 +++- ...f07b96a5e60f_add_playlist_and_playtimes.py | 52 ++++++++++ notes.otl | 1 + 4 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 migrations/versions/f07b96a5e60f_add_playlist_and_playtimes.py diff --git a/app/model.py b/app/model.py index 8f30dd2..c5e544a 100644 --- a/app/model.py +++ b/app/model.py @@ -3,10 +3,19 @@ import os import sqlalchemy +from datetime import datetime from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy import Column, Float, DateTime, Integer, String +from sqlalchemy import ( + Column, + DateTime, + Float, + ForeignKey, + Integer, + String, + Table +) from sqlalchemy.orm.exc import NoResultFound -from sqlalchemy.orm import sessionmaker +from sqlalchemy.orm import relationship, sessionmaker from config import Config @@ -60,6 +69,63 @@ class Settings(Base): session.commit() +playlist_tracks = Table( + 'playlistracks', + Base.metadata, + Column('playlist_id', Integer, ForeignKey('playlists.id')), + Column('track_id', Integer, ForeignKey('tracks.id')) +) + + +class Playlists(Base): + """ + Usage: + + In [3]: pl = session.query(Playlists).filter(Playlists.id == 1).one() + + In [4]: pl + Out[4]: + + In [5]: tr = session.query(Tracks).filter(Tracks.id == 3837).one() + + In [6]: tr + Out[6]: ] + + In [9]: pl.tracks + Out[9]: [") + + # Currently we only support one playlist, so make that obvious from + # function name + @classmethod + def get_only_playlist(cls): + return session.query(Playlists).filter(Playlists.id == 1).one() + + def add_track(self, track): + self.tracks.append(track) + + def get_tracks(self): + return self.tracks + + class Tracks(Base): __tablename__ = 'tracks' @@ -71,9 +137,14 @@ class Tracks(Base): fade_at = Column(Integer, index=False) silence_at = Column(Integer, index=False) path = Column(String(2048), index=False, nullable=False) - mtime = Column(Float, index=True) lastplayed = Column(DateTime, index=True, default=None) + playlists = relationship( + "Playlists", + secondary=playlist_tracks, + back_populates="tracks") + playdates_id = Column(Integer, ForeignKey('playdates.id')) + playdates = relationship("Playdates", back_populates="tracks") def __repr__(self): return ( @@ -123,3 +194,20 @@ class Tracks(Base): def track_from_id(id): return session.query(Tracks).filter( Tracks.id == id).one() + + +class Playdates(Base): + __tablename__ = 'playdates' + + id = Column(Integer, primary_key=True, autoincrement=True) + lastplayed = Column(DateTime, index=True, default=None) + tracks = relationship("Tracks", back_populates="playdates") + + @staticmethod + def add_playdate(track): + DEBUG(f"add_playdate({track})") + pd = Playdates() + pd.lastplayed = datetime.now() + pd.tracks.append(track) + session.add(pd) + session.commit() diff --git a/app/musicmuster.py b/app/musicmuster.py index 93759da..5ae8258 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -14,7 +14,7 @@ from ui.main_window_ui import Ui_MainWindow from ui.dlg_search_database_ui import Ui_Dialog from config import Config -from model import Settings, Tracks +from model import Playdates, Playlists, Settings, Tracks class Music: @@ -106,6 +106,7 @@ class Music: self.previous_track = self.current_track self.current_track = self.next_track self.current_track['player'].play() + Playdates.add_playdate(self.current_track['meta']) # Tidy up self.next_track = { @@ -158,6 +159,11 @@ class Window(QMainWindow, Ui_MainWindow): if record.f_int is not None: self.playlist.setColumnWidth(column, record.f_int) + # Load playlist + db_playlist = Playlists.get_only_playlist() + for track in db_playlist.get_tracks(): + self.add_to_playlist(track) + self.timer.start(Config.TIMER_MS) def __del__(self): @@ -369,6 +375,12 @@ class DbDialog(QDialog): def listdclick(self, entry): track_id = entry.data(Qt.UserRole) track = Tracks.track_from_id(track_id) + + # Store in current playlist in database + db_playlist = Playlists.get_only_playlist() + db_playlist.add_track(track) + + # Add to on-screen playlist self.parent().add_to_playlist(track) @@ -384,8 +396,8 @@ def ms_to_mmss(ms, decimals=0, negative=False): minutes, remainder = divmod(ms, 60 * 1000) seconds = remainder / 1000 - if seconds == 60: - print(f"ms_to_mmss({ms}) gave 60 seconds") + if int(seconds) == 60: + DEBUG(f"ms_to_mmss({ms}) gave 60 seconds") return f"{sign}{minutes:.0f}:{seconds:02.{decimals}f}" diff --git a/migrations/versions/f07b96a5e60f_add_playlist_and_playtimes.py b/migrations/versions/f07b96a5e60f_add_playlist_and_playtimes.py new file mode 100644 index 0000000..5649a14 --- /dev/null +++ b/migrations/versions/f07b96a5e60f_add_playlist_and_playtimes.py @@ -0,0 +1,52 @@ +"""Add playlist and playtimes + +Revision ID: f07b96a5e60f +Revises: b0983648595e +Create Date: 2021-03-27 19:53:09.524989 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'f07b96a5e60f' +down_revision = 'b0983648595e' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('playdates', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('lastplayed', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_playdates_lastplayed'), 'playdates', ['lastplayed'], unique=False) + op.create_table('playlists', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(length=32), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('name') + ) + op.create_table('playlistracks', + sa.Column('playlist_id', sa.Integer(), nullable=True), + sa.Column('track_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['playlist_id'], ['playlists.id'], ), + sa.ForeignKeyConstraint(['track_id'], ['tracks.id'], ) + ) + op.add_column('tracks', sa.Column('playdates_id', sa.Integer(), nullable=True)) + op.create_foreign_key(None, 'tracks', 'playdates', ['playdates_id'], ['id']) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'tracks', type_='foreignkey') + op.drop_column('tracks', 'playdates_id') + op.drop_table('playlistracks') + op.drop_table('playlists') + op.drop_index(op.f('ix_playdates_lastplayed'), table_name='playdates') + op.drop_table('playdates') + # ### end Alembic commands ### diff --git a/notes.otl b/notes.otl index 45e6d4e..3a18aa6 100644 --- a/notes.otl +++ b/notes.otl @@ -54,6 +54,7 @@ First release Status bar Timer colour warnings Non-track playlist entries + Fade entry for some tracks is zero Later releases Autoplay next track Track properties