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'