musicmuster/app/model.py
2021-04-03 22:45:30 +01:00

234 lines
7.1 KiB
Python

#!/usr/bin/python3
import sqlalchemy
from datetime import datetime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import (
Column,
DateTime,
Float,
ForeignKey,
Integer,
String
)
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.orm import relationship, sessionmaker
from config import Config
from log import DEBUG, ERROR, INFO
# Create session at the global level as per
# https://docs.sqlalchemy.org/en/13/orm/session_basics.html
# Set up database connection
INFO("Connect to database")
engine = sqlalchemy.create_engine(f"{Config.MYSQL_CONNECT}?charset=utf8",
encoding='utf-8',
echo=Config.DISPLAY_SQL)
Base = declarative_base()
Base.metadata.create_all(engine)
# Create a Session
Session = sessionmaker(bind=engine)
session = Session()
# Database classes
class Settings(Base):
__tablename__ = 'settings'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(32), nullable=False, unique=True)
f_datetime = Column(DateTime, default=None, nullable=True)
f_int = Column(Integer, default=None, nullable=True)
f_string = Column(String(128), default=None, nullable=True)
@classmethod
def get_int(cls, name):
try:
int_setting = session.query(cls).filter(
cls.name == name).one()
except NoResultFound:
int_setting = Settings()
int_setting.name = name
int_setting.f_int = None
session.add(int_setting)
session.commit()
return int_setting
def update(self, data):
for key, value in data.items():
assert hasattr(self, key)
setattr(self, key, value)
session.commit()
class PlaylistTracks(Base):
__tablename__ = 'playlisttracks'
Base.metadata,
id = Column(Integer, primary_key=True, autoincrement=True)
playlist_id = Column(Integer, ForeignKey('playlists.id'), primary_key=True)
track_id = Column(Integer, ForeignKey('tracks.id'), primary_key=True)
sort = Column(Integer, nullable=False)
tracks = relationship("Tracks", back_populates="playlists")
playlists = relationship("Playlists", back_populates="tracks")
class Playlists(Base):
"""
Usage:
pl = session.query(Playlists).filter(Playlists.id == 1).one()
pl
<Playlist(id=1, name=Default>
pl.tracks
[<__main__.PlaylistTracks at 0x7fcd20181c18>,
<__main__.PlaylistTracks at 0x7fcd20181c88>,
<__main__.PlaylistTracks at 0x7fcd20181be0>,
<__main__.PlaylistTracks at 0x7fcd20181c50>]
[a.tracks for a in pl.tracks]
[<Track(id=3992, title=Yesterday Man, artist=Various, path=/h[...]
<Track(id=2238, title=These Boots Are Made For Walkin', arti[...]
<Track(id=3837, title=Babe, artist=Various, path=/home/kae/m[...]
<Track(id=2332, title=Such Great Heights - Remastered, artis[...]]
glue = PlaylistTracks(sort=5)
tr = session.query(Tracks).filter(Tracks.id == 676).one()
tr
<Track(id=676, title=Seven Nation Army, artist=White Stripes,
path=/home/kae/music/White Stripes/Elephant/01. Seven Nation Army.flac>
glue.track_id = tr.id
pl.tracks.append(glue)
session.commit()
[a.tracks for a in pl.tracks]
[<Track(id=3992, title=Yesterday Man, artist=Various, path=/h[...]
<Track(id=2238, title=These Boots Are Made For Walkin', arti[...]
<Track(id=3837, title=Babe, artist=Various, path=/home/kae/m[...]
<Track(id=2332, title=Such Great Heights - Remastered, artis[...]
<Track(id=676, title=Seven Nation Army, artist=White Stripes[...]]
"""
__tablename__ = "playlists"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(32), nullable=False, unique=True)
tracks = relationship("PlaylistTracks",
order_by="PlaylistTracks.sort",
back_populates="playlists")
def __repr__(self):
return (f"<Playlist(id={self.id}, name={self.name}>")
# Currently we only support one playlist, so make that obvious from
# function name
@classmethod
def get_playlist_by_name(cls, name):
"Returns a playlist object for named playlist"
return session.query(Playlists).filter(Playlists.name == name).one()
def add_track(self, track, position):
glue = PlaylistTracks(sort=position)
glue.track_id = track.id
self.tracks.append(glue)
def get_tracks(self):
return [a.tracks for a in self.tracks]
class Tracks(Base):
__tablename__ = 'tracks'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(256), index=True)
artist = Column(String(256), index=True)
duration = Column(Integer, index=True)
start_gap = Column(Integer, index=False)
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("PlaylistTracks", back_populates="tracks")
playdates = relationship("Playdates", back_populates="tracks")
def __repr__(self):
return (
f"<Track(id={self.id}, title={self.title}, "
f"artist={self.artist}, path={self.path}>"
)
@classmethod
def get_or_create(cls, path):
DEBUG(f"get_or_create(cls, {path})")
try:
track = session.query(cls).filter(cls.path == path).one()
except NoResultFound:
track = Tracks()
track.path = path
session.add(track)
return track
@staticmethod
def get_path(id):
try:
return session.query(Tracks.path).filter(Tracks.id == id).one()[0]
except NoResultFound:
ERROR(f"Can't find track id {id}")
return None
@staticmethod
def get_track(id):
try:
DEBUG(f"get_track({id})")
track = session.query(Tracks).filter(Tracks.id == id).one()
return track
except NoResultFound:
ERROR(f"get_track({id}): not found")
return None
@staticmethod
def search_titles(text):
return (
session.query(Tracks)
.filter(Tracks.title.ilike(f"%{text}%"))
.order_by(Tracks.title)
).all()
@staticmethod
def track_from_id(id):
return session.query(Tracks).filter(
Tracks.id == id).one()
def update_lastplayed(self):
self.lastplayed = datetime.now()
class Playdates(Base):
__tablename__ = 'playdates'
id = Column(Integer, primary_key=True, autoincrement=True)
lastplayed = Column(DateTime, index=True, default=None)
track_id = Column(Integer, ForeignKey('tracks.id'))
tracks = relationship("Tracks", back_populates="playdates")
@staticmethod
def add_playdate(track):
DEBUG(f"add_playdate({track})")
pd = Playdates()
pd.lastplayed = datetime.now()
pd.track_id = track.id
session.add(pd)
track.update_lastplayed()
session.commit()