diff --git a/api/__init__.py b/api/__init__.py index d4bdc90..61e769f 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -23,6 +23,7 @@ from xml.etree import ElementTree import simplejson import uuid +import config from web import app from managers.user import UserManager @@ -190,12 +191,27 @@ def get_entity(req, ent, param = 'id'): return True, entity +def disabled_api_feature(): + return request.error_formatter(0, 'Feature disabled') + from .system import * from .browse import * from .user import * from .albums_songs import * from .media import * -from .annotation import * +from .scrobble import * + +if config.getbool('features', 'star', True): + from .star import * +else: + app.add_url_rule('/rest/star.view', view_func = disabled_api_feature, methods = [ 'GET', 'POST' ]) + app.add_url_rule('/rest/unstar.view', view_func = disabled_api_feature, methods = [ 'GET', 'POST' ]) + +if config.getbool('features', 'rating', True): + from .rating import * +else: + app.add_url_rule('/rest/setRating.view', view_func = disabled_api_feature, methods = [ 'GET', 'POST' ]) + from .chat import * from .search import * from .playlists import * diff --git a/api/rating.py b/api/rating.py new file mode 100644 index 0000000..6ceea36 --- /dev/null +++ b/api/rating.py @@ -0,0 +1,61 @@ +# coding: utf-8 + +# This file is part of Supysonic. +# +# Supysonic is a Python implementation of the Subsonic server API. +# Copyright (C) 2013 Alban 'spl0k' Féron +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import uuid +from flask import request +from web import app +from db import RatingTrack, RatingFolder, Track, Folder, session + +@app.route('/rest/setRating.view', methods = [ 'GET', 'POST' ]) +def rate(): + id, rating = map(request.args.get, [ 'id', 'rating' ]) + if not id or not rating: + return request.error_formatter(10, 'Missing parameter') + + try: + uid = uuid.UUID(id) + rating = int(rating) + except: + return request.error_formatter(0, 'Invalid parameter') + + if not rating in xrange(6): + return request.error_formatter(0, 'rating must be between 0 and 5 (inclusive)') + + if rating == 0: + RatingTrack.query.filter(RatingTrack.user_id == request.user.id).filter(RatingTrack.rated_id == uid).delete() + RatingFolder.query.filter(RatingFolder.user_id == request.user.id).filter(RatingFolder.rated_id == uid).delete() + else: + rated = Track.query.get(uid) + rating_ent = RatingTrack + if not rated: + rated = Folder.query.get(uid) + rating_ent = RatingFolder + if not rated: + return request.error_formatter(70, 'Unknown id') + + rating_info = rating_ent.query.get((request.user.id, uid)) + if rating_info: + rating_info.rating = rating + else: + session.add(rating_ent(user = request.user, rated = rated, rating = rating)) + + session.commit() + return request.formatter({}) + diff --git a/api/scrobble.py b/api/scrobble.py new file mode 100644 index 0000000..e271903 --- /dev/null +++ b/api/scrobble.py @@ -0,0 +1,52 @@ +# coding: utf-8 + +# This file is part of Supysonic. +# +# Supysonic is a Python implementation of the Subsonic server API. +# Copyright (C) 2013 Alban 'spl0k' Féron +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import time +from flask import request +from web import app +from . import get_entity +from lastfm import LastFm +from db import Track + +@app.route('/rest/scrobble.view', methods = [ 'GET', 'POST' ]) +def scrobble(): + status, res = get_entity(request, Track) + if not status: + return res + + t, submission = map(request.args.get, [ 'time', 'submission' ]) + + if t: + try: + t = int(t) / 1000 + except: + return request.error_formatter(0, 'Invalid time value') + else: + t = int(time.time()) + + lfm = LastFm(request.user, app.logger) + + if submission in (None, '', True, 'true', 'True', 1, '1'): + lfm.scrobble(res, t) + else: + lfm.now_playing(res) + + return request.formatter({}) + diff --git a/api/annotation.py b/api/star.py similarity index 60% rename from api/annotation.py rename to api/star.py index 39943cb..d83f092 100644 --- a/api/annotation.py +++ b/api/star.py @@ -18,13 +18,9 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -import time -import uuid from flask import request from web import app -from . import get_entity -from lastfm import LastFm -from db import * +from db import Track, StarredTrack, Album, StarredAlbum, Artist, StarredArtist, Folder, StarredFolder, session @app.route('/rest/star.view', methods = [ 'GET', 'POST' ]) def star(): @@ -102,64 +98,3 @@ def unstar(): session.commit() return request.formatter({}) -@app.route('/rest/setRating.view', methods = [ 'GET', 'POST' ]) -def rate(): - id, rating = map(request.args.get, [ 'id', 'rating' ]) - if not id or not rating: - return request.error_formatter(10, 'Missing parameter') - - try: - uid = uuid.UUID(id) - rating = int(rating) - except: - return request.error_formatter(0, 'Invalid parameter') - - if not rating in xrange(6): - return request.error_formatter(0, 'rating must be between 0 and 5 (inclusive)') - - if rating == 0: - RatingTrack.query.filter(RatingTrack.user_id == request.user.id).filter(RatingTrack.rated_id == uid).delete() - RatingFolder.query.filter(RatingFolder.user_id == request.user.id).filter(RatingFolder.rated_id == uid).delete() - else: - rated = Track.query.get(uid) - rating_ent = RatingTrack - if not rated: - rated = Folder.query.get(uid) - rating_ent = RatingFolder - if not rated: - return request.error_formatter(70, 'Unknown id') - - rating_info = rating_ent.query.get((request.user.id, uid)) - if rating_info: - rating_info.rating = rating - else: - session.add(rating_ent(user = request.user, rated = rated, rating = rating)) - - session.commit() - return request.formatter({}) - -@app.route('/rest/scrobble.view', methods = [ 'GET', 'POST' ]) -def scrobble(): - status, res = get_entity(request, Track) - if not status: - return res - - t, submission = map(request.args.get, [ 'time', 'submission' ]) - - if t: - try: - t = int(t) / 1000 - except: - return request.error_formatter(0, 'Invalid time value') - else: - t = int(time.time()) - - lfm = LastFm(request.user, app.logger) - - if submission in (None, '', True, 'true', 'True', 1, '1'): - lfm.scrobble(res, t) - else: - lfm.now_playing(res) - - return request.formatter({}) - diff --git a/db.py b/db.py index c7897d3..5d68d85 100644 --- a/db.py +++ b/db.py @@ -151,16 +151,18 @@ class Folder(Base): if self.has_cover_art: info['coverArt'] = str(self.id) - starred = StarredFolder.query.get((user.id, self.id)) - if starred: - info['starred'] = starred.date.isoformat() + if config.getbool('features', 'star', True): + starred = StarredFolder.query.get((user.id, self.id)) + if starred: + info['starred'] = starred.date.isoformat() - rating = RatingFolder.query.get((user.id, self.id)) - if rating: - info['userRating'] = rating.rating - avgRating = RatingFolder.query.filter(RatingFolder.rated_id == self.id).value(func.avg(RatingFolder.rating)) - if avgRating: - info['averageRating'] = avgRating + if config.getbool('features', 'rating', True): + rating = RatingFolder.query.get((user.id, self.id)) + if rating: + info['userRating'] = rating.rating + avgRating = RatingFolder.query.filter(RatingFolder.rated_id == self.id).value(func.avg(RatingFolder.rating)) + if avgRating: + info['averageRating'] = avgRating return info @@ -179,9 +181,10 @@ class Artist(Base): 'albumCount': len(self.albums) } - starred = StarredArtist.query.get((user.id, self.id)) - if starred: - info['starred'] = starred.date.isoformat() + if config.getbool('features', 'star', True): + starred = StarredArtist.query.get((user.id, self.id)) + if starred: + info['starred'] = starred.date.isoformat() return info @@ -206,9 +209,10 @@ class Album(Base): if self.tracks[0].folder.has_cover_art: info['coverArt'] = str(self.tracks[0].folder_id) - starred = StarredAlbum.query.get((user.id, self.id)) - if starred: - info['starred'] = starred.date.isoformat() + if config.getbool('features', 'star', True): + starred = StarredAlbum.query.get((user.id, self.id)) + if starred: + info['starred'] = starred.date.isoformat() return info @@ -272,16 +276,18 @@ class Track(Base): if self.folder.has_cover_art: info['coverArt'] = str(self.folder_id) - starred = StarredTrack.query.get((user.id, self.id)) - if starred: - info['starred'] = starred.date.isoformat() + if config.getbool('features', 'star', True): + starred = StarredTrack.query.get((user.id, self.id)) + if starred: + info['starred'] = starred.date.isoformat() - rating = RatingTrack.query.get((user.id, self.id)) - if rating: - info['userRating'] = rating.rating - avgRating = RatingTrack.query.filter(RatingTrack.rated_id == self.id).value(func.avg(RatingTrack.rating)) - if avgRating: - info['averageRating'] = avgRating + if config.getbool('features', 'rating', True): + rating = RatingTrack.query.get((user.id, self.id)) + if rating: + info['userRating'] = rating.rating + avgRating = RatingTrack.query.filter(RatingTrack.rated_id == self.id).value(func.avg(RatingTrack.rating)) + if avgRating: + info['averageRating'] = avgRating # transcodedContentType # transcodedSuffix @@ -300,65 +306,67 @@ class Track(Base): def sort_key(self): return (self.album.artist.name + self.album.name + ("%02i" % self.disc) + ("%02i" % self.number) + self.title).lower() -class StarredFolder(Base): - __tablename__ = 'starred_folder' +if config.getbool('features', 'star', True): + class StarredFolder(Base): + __tablename__ = 'starred_folder' - user_id = Column(UUID, ForeignKey('user.id'), primary_key = True) - starred_id = Column(UUID, ForeignKey('folder.id'), primary_key = True) - date = Column(DateTime, default = now) + user_id = Column(UUID, ForeignKey('user.id'), primary_key = True) + starred_id = Column(UUID, ForeignKey('folder.id'), primary_key = True) + date = Column(DateTime, default = now) - user = relationship('User') - starred = relationship('Folder') + user = relationship('User') + starred = relationship('Folder') -class StarredArtist(Base): - __tablename__ = 'starred_artist' + class StarredArtist(Base): + __tablename__ = 'starred_artist' - user_id = Column(UUID, ForeignKey('user.id'), primary_key = True) - starred_id = Column(UUID, ForeignKey('artist.id'), primary_key = True) - date = Column(DateTime, default = now) + user_id = Column(UUID, ForeignKey('user.id'), primary_key = True) + starred_id = Column(UUID, ForeignKey('artist.id'), primary_key = True) + date = Column(DateTime, default = now) - user = relationship('User') - starred = relationship('Artist') + user = relationship('User') + starred = relationship('Artist') -class StarredAlbum(Base): - __tablename__ = 'starred_album' + class StarredAlbum(Base): + __tablename__ = 'starred_album' - user_id = Column(UUID, ForeignKey('user.id'), primary_key = True) - starred_id = Column(UUID, ForeignKey('album.id'), primary_key = True) - date = Column(DateTime, default = now) + user_id = Column(UUID, ForeignKey('user.id'), primary_key = True) + starred_id = Column(UUID, ForeignKey('album.id'), primary_key = True) + date = Column(DateTime, default = now) - user = relationship('User') - starred = relationship('Album') + user = relationship('User') + starred = relationship('Album') -class StarredTrack(Base): - __tablename__ = 'starred_track' + class StarredTrack(Base): + __tablename__ = 'starred_track' - user_id = Column(UUID, ForeignKey('user.id'), primary_key = True) - starred_id = Column(UUID, ForeignKey('track.id'), primary_key = True) - date = Column(DateTime, default = now) + user_id = Column(UUID, ForeignKey('user.id'), primary_key = True) + starred_id = Column(UUID, ForeignKey('track.id'), primary_key = True) + date = Column(DateTime, default = now) - user = relationship('User') - starred = relationship('Track') + user = relationship('User') + starred = relationship('Track') -class RatingFolder(Base): - __tablename__ = 'rating_folder' +if config.getbool('features', 'rating', True): + class RatingFolder(Base): + __tablename__ = 'rating_folder' - user_id = Column(UUID, ForeignKey('user.id'), primary_key = True) - rated_id = Column(UUID, ForeignKey('folder.id'), primary_key = True) - rating = Column(Integer) + user_id = Column(UUID, ForeignKey('user.id'), primary_key = True) + rated_id = Column(UUID, ForeignKey('folder.id'), primary_key = True) + rating = Column(Integer) - user = relationship('User') - rated = relationship('Folder') + user = relationship('User') + rated = relationship('Folder') -class RatingTrack(Base): - __tablename__ = 'rating_track' + class RatingTrack(Base): + __tablename__ = 'rating_track' - user_id = Column(UUID, ForeignKey('user.id'), primary_key = True) - rated_id = Column(UUID, ForeignKey('track.id'), primary_key = True) - rating = Column(Integer) + user_id = Column(UUID, ForeignKey('user.id'), primary_key = True) + rated_id = Column(UUID, ForeignKey('track.id'), primary_key = True) + rating = Column(Integer) - user = relationship('User') - rated = relationship('Track') + user = relationship('User') + rated = relationship('Track') class ChatMessage(Base): __tablename__ = 'chat_message'