1
0
mirror of https://github.com/spl0k/supysonic.git synced 2024-11-10 04:02:17 +00:00
supysonic/db.py

175 lines
4.7 KiB
Python
Raw Normal View History

2012-10-13 09:29:48 +00:00
# coding: utf-8
import config
2012-10-13 12:53:09 +00:00
from sqlalchemy import create_engine, Column, ForeignKey
from sqlalchemy import Integer, String, Boolean, DateTime
2012-10-13 12:53:09 +00:00
from sqlalchemy.orm import scoped_session, sessionmaker, relationship, backref
2012-10-13 09:29:48 +00:00
from sqlalchemy.ext.declarative import declarative_base
2012-10-13 12:53:09 +00:00
from sqlalchemy.types import TypeDecorator
2012-10-13 09:29:48 +00:00
from sqlalchemy import BINARY
2012-10-13 12:53:09 +00:00
import uuid, datetime
import os.path
2012-10-13 09:29:48 +00:00
2012-10-13 12:53:09 +00:00
class UUID(TypeDecorator):
2012-10-13 09:29:48 +00:00
impl = BINARY
2012-10-13 12:53:09 +00:00
2012-10-13 09:29:48 +00:00
def __init__(self):
self.impl.length = 16
2012-10-13 12:53:09 +00:00
TypeDecorator.__init__(self, length = self.impl.length)
2012-10-13 09:29:48 +00:00
def process_bind_param(self, value, dialect = None):
if value and isinstance(value, uuid.UUID):
return value.bytes
if value and not isinstance(value, uuid.UUID):
raise ValueError, 'value %s is not a valid uuid.UUID' % value
return None
def process_result_value(self, value, dialect = None):
if value:
return uuid.UUID(bytes = value)
return None
def is_mutable(self):
return False
@staticmethod
def gen_id_column():
return Column(UUID, primary_key = True, default = uuid.uuid4)
engine = create_engine(config.get('DATABASE_URI'), convert_unicode = True)
session = scoped_session(sessionmaker(autocommit = False, autoflush = False, bind = engine))
2012-10-13 09:29:48 +00:00
Base = declarative_base()
Base.query = session.query_property()
2012-10-13 09:29:48 +00:00
class User(Base):
2012-10-13 12:53:09 +00:00
__tablename__ = 'user'
2012-10-13 09:29:48 +00:00
id = UUID.gen_id_column()
name = Column(String, unique = True)
mail = Column(String)
password = Column(String(40))
salt = Column(String(6))
admin = Column(Boolean, default = False)
lastfm_session = Column(String(32), nullable = True)
lastfm_status = Column(Boolean, default = True) # True: ok/unlinked, False: invalid session
2012-10-13 09:29:48 +00:00
2012-10-21 14:18:35 +00:00
class Folder(Base):
2012-10-13 12:53:09 +00:00
__tablename__ = 'folder'
2012-10-13 09:29:48 +00:00
id = UUID.gen_id_column()
2012-10-21 14:18:35 +00:00
root = Column(Boolean, default = False)
name = Column(String)
path = Column(String, unique = True)
2012-11-11 20:39:26 +00:00
has_cover_art = Column(Boolean, default = False)
last_scan = Column(DateTime, default = datetime.datetime.min)
2012-10-13 12:53:09 +00:00
2012-10-21 14:18:35 +00:00
parent_id = Column(UUID, ForeignKey('folder.id'), nullable = True)
children = relationship('Folder', backref = backref('parent', remote_side = [ id ]))
2012-11-10 23:01:52 +00:00
def as_subsonic_child(self):
info = {
'id': str(self.id),
'isDir': True,
'title': self.name,
}
if not self.root:
info['parent'] = str(self.parent_id)
info['artist'] = self.parent.name
2012-11-11 20:39:26 +00:00
if self.has_cover_art:
info['coverArt'] = str(self.id)
2012-11-10 23:01:52 +00:00
return info
2012-10-13 12:53:09 +00:00
class Artist(Base):
__tablename__ = 'artist'
id = UUID.gen_id_column()
name = Column(String, unique = True)
albums = relationship('Album', backref = 'artist')
2012-10-13 12:53:09 +00:00
class Album(Base):
__tablename__ = 'album'
id = UUID.gen_id_column()
name = Column(String)
artist_id = Column(UUID, ForeignKey('artist.id'))
tracks = relationship('Track', backref = 'album')
2012-10-13 12:53:09 +00:00
class Track(Base):
__tablename__ = 'track'
id = UUID.gen_id_column()
disc = Column(Integer)
number = Column(Integer)
title = Column(String)
year = Column(Integer, nullable = True)
genre = Column(String, nullable = True)
duration = Column(Integer)
2012-10-13 12:53:09 +00:00
album_id = Column(UUID, ForeignKey('album.id'))
path = Column(String, unique = True)
bitrate = Column(Integer)
2012-10-13 09:29:48 +00:00
2012-10-21 14:18:35 +00:00
root_folder_id = Column(UUID, ForeignKey('folder.id'))
root_folder = relationship('Folder', primaryjoin = Folder.id == root_folder_id)
2012-10-14 11:07:02 +00:00
folder_id = Column(UUID, ForeignKey('folder.id'))
2012-10-21 14:18:35 +00:00
folder = relationship('Folder', primaryjoin = Folder.id == folder_id, backref = 'tracks')
2012-10-14 11:07:02 +00:00
def as_subsonic_child(self):
info = {
'id': str(self.id),
2012-10-21 14:18:35 +00:00
'parent': str(self.folder.id),
'isDir': False,
'title': self.title,
'album': self.album.name,
'artist': self.album.artist.name,
'track': self.number,
'size': os.path.getsize(self.path),
'contentType': 'audio/mpeg', # we only know how to read mp3s
'suffix': 'mp3', # same as above
'duration': self.duration,
'bitRate': self.bitrate,
2012-10-21 14:18:35 +00:00
'path': self.path[len(self.root_folder.path) + 1:],
'isVideo': False,
'discNumber': self.disc,
'albumId': str(self.album.id),
'artistId': str(self.album.artist.id),
'type': 'music'
}
if self.year:
info['year'] = self.year
if self.genre:
info['genre'] = self.genre
2012-11-11 20:39:26 +00:00
if self.folder.has_cover_art:
info['coverArt'] = str(self.folder_id)
# transcodedContentType
# transcodedSuffix
# userRating
# averageRating
# created
# starred
return info
def duration_str(self):
ret = '%02i:%02i' % ((self.duration % 3600) / 60, self.duration % 60)
if self.duration >= 3600:
ret = '%02i:%s' % (self.duration / 3600, ret)
return ret
2012-11-10 23:01:52 +00:00
def sort_key(self):
return self.album.artist.name + self.album.name + ("%02i" % self.disc) + ("%02i" % self.number) + self.title
2012-10-13 09:29:48 +00:00
def init_db():
Base.metadata.create_all(bind = engine)
def recreate_db():
2012-10-13 09:29:48 +00:00
Base.metadata.drop_all(bind = engine)
Base.metadata.create_all(bind = engine)