mirror of
https://github.com/spl0k/supysonic.git
synced 2024-12-22 17:06:17 +00:00
Adding ratings and stars as disablable features
This commit is contained in:
parent
72f8e6b52c
commit
c6b197689a
@ -23,6 +23,7 @@ from xml.etree import ElementTree
|
|||||||
import simplejson
|
import simplejson
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
import config
|
||||||
from web import app
|
from web import app
|
||||||
from managers.user import UserManager
|
from managers.user import UserManager
|
||||||
|
|
||||||
@ -190,12 +191,27 @@ def get_entity(req, ent, param = 'id'):
|
|||||||
|
|
||||||
return True, entity
|
return True, entity
|
||||||
|
|
||||||
|
def disabled_api_feature():
|
||||||
|
return request.error_formatter(0, 'Feature disabled')
|
||||||
|
|
||||||
from .system import *
|
from .system import *
|
||||||
from .browse import *
|
from .browse import *
|
||||||
from .user import *
|
from .user import *
|
||||||
from .albums_songs import *
|
from .albums_songs import *
|
||||||
from .media 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 .chat import *
|
||||||
from .search import *
|
from .search import *
|
||||||
from .playlists import *
|
from .playlists import *
|
||||||
|
61
api/rating.py
Normal file
61
api/rating.py
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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({})
|
||||||
|
|
52
api/scrobble.py
Normal file
52
api/scrobble.py
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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({})
|
||||||
|
|
@ -18,13 +18,9 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import time
|
|
||||||
import uuid
|
|
||||||
from flask import request
|
from flask import request
|
||||||
from web import app
|
from web import app
|
||||||
from . import get_entity
|
from db import Track, StarredTrack, Album, StarredAlbum, Artist, StarredArtist, Folder, StarredFolder, session
|
||||||
from lastfm import LastFm
|
|
||||||
from db import *
|
|
||||||
|
|
||||||
@app.route('/rest/star.view', methods = [ 'GET', 'POST' ])
|
@app.route('/rest/star.view', methods = [ 'GET', 'POST' ])
|
||||||
def star():
|
def star():
|
||||||
@ -102,64 +98,3 @@ def unstar():
|
|||||||
session.commit()
|
session.commit()
|
||||||
return request.formatter({})
|
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({})
|
|
||||||
|
|
140
db.py
140
db.py
@ -151,16 +151,18 @@ class Folder(Base):
|
|||||||
if self.has_cover_art:
|
if self.has_cover_art:
|
||||||
info['coverArt'] = str(self.id)
|
info['coverArt'] = str(self.id)
|
||||||
|
|
||||||
starred = StarredFolder.query.get((user.id, self.id))
|
if config.getbool('features', 'star', True):
|
||||||
if starred:
|
starred = StarredFolder.query.get((user.id, self.id))
|
||||||
info['starred'] = starred.date.isoformat()
|
if starred:
|
||||||
|
info['starred'] = starred.date.isoformat()
|
||||||
|
|
||||||
rating = RatingFolder.query.get((user.id, self.id))
|
if config.getbool('features', 'rating', True):
|
||||||
if rating:
|
rating = RatingFolder.query.get((user.id, self.id))
|
||||||
info['userRating'] = rating.rating
|
if rating:
|
||||||
avgRating = RatingFolder.query.filter(RatingFolder.rated_id == self.id).value(func.avg(RatingFolder.rating))
|
info['userRating'] = rating.rating
|
||||||
if avgRating:
|
avgRating = RatingFolder.query.filter(RatingFolder.rated_id == self.id).value(func.avg(RatingFolder.rating))
|
||||||
info['averageRating'] = avgRating
|
if avgRating:
|
||||||
|
info['averageRating'] = avgRating
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
@ -179,9 +181,10 @@ class Artist(Base):
|
|||||||
'albumCount': len(self.albums)
|
'albumCount': len(self.albums)
|
||||||
}
|
}
|
||||||
|
|
||||||
starred = StarredArtist.query.get((user.id, self.id))
|
if config.getbool('features', 'star', True):
|
||||||
if starred:
|
starred = StarredArtist.query.get((user.id, self.id))
|
||||||
info['starred'] = starred.date.isoformat()
|
if starred:
|
||||||
|
info['starred'] = starred.date.isoformat()
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
@ -206,9 +209,10 @@ class Album(Base):
|
|||||||
if self.tracks[0].folder.has_cover_art:
|
if self.tracks[0].folder.has_cover_art:
|
||||||
info['coverArt'] = str(self.tracks[0].folder_id)
|
info['coverArt'] = str(self.tracks[0].folder_id)
|
||||||
|
|
||||||
starred = StarredAlbum.query.get((user.id, self.id))
|
if config.getbool('features', 'star', True):
|
||||||
if starred:
|
starred = StarredAlbum.query.get((user.id, self.id))
|
||||||
info['starred'] = starred.date.isoformat()
|
if starred:
|
||||||
|
info['starred'] = starred.date.isoformat()
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
@ -272,16 +276,18 @@ class Track(Base):
|
|||||||
if self.folder.has_cover_art:
|
if self.folder.has_cover_art:
|
||||||
info['coverArt'] = str(self.folder_id)
|
info['coverArt'] = str(self.folder_id)
|
||||||
|
|
||||||
starred = StarredTrack.query.get((user.id, self.id))
|
if config.getbool('features', 'star', True):
|
||||||
if starred:
|
starred = StarredTrack.query.get((user.id, self.id))
|
||||||
info['starred'] = starred.date.isoformat()
|
if starred:
|
||||||
|
info['starred'] = starred.date.isoformat()
|
||||||
|
|
||||||
rating = RatingTrack.query.get((user.id, self.id))
|
if config.getbool('features', 'rating', True):
|
||||||
if rating:
|
rating = RatingTrack.query.get((user.id, self.id))
|
||||||
info['userRating'] = rating.rating
|
if rating:
|
||||||
avgRating = RatingTrack.query.filter(RatingTrack.rated_id == self.id).value(func.avg(RatingTrack.rating))
|
info['userRating'] = rating.rating
|
||||||
if avgRating:
|
avgRating = RatingTrack.query.filter(RatingTrack.rated_id == self.id).value(func.avg(RatingTrack.rating))
|
||||||
info['averageRating'] = avgRating
|
if avgRating:
|
||||||
|
info['averageRating'] = avgRating
|
||||||
|
|
||||||
# transcodedContentType
|
# transcodedContentType
|
||||||
# transcodedSuffix
|
# transcodedSuffix
|
||||||
@ -300,65 +306,67 @@ class Track(Base):
|
|||||||
def sort_key(self):
|
def sort_key(self):
|
||||||
return (self.album.artist.name + self.album.name + ("%02i" % self.disc) + ("%02i" % self.number) + self.title).lower()
|
return (self.album.artist.name + self.album.name + ("%02i" % self.disc) + ("%02i" % self.number) + self.title).lower()
|
||||||
|
|
||||||
class StarredFolder(Base):
|
if config.getbool('features', 'star', True):
|
||||||
__tablename__ = 'starred_folder'
|
class StarredFolder(Base):
|
||||||
|
__tablename__ = 'starred_folder'
|
||||||
|
|
||||||
user_id = Column(UUID, ForeignKey('user.id'), primary_key = True)
|
user_id = Column(UUID, ForeignKey('user.id'), primary_key = True)
|
||||||
starred_id = Column(UUID, ForeignKey('folder.id'), primary_key = True)
|
starred_id = Column(UUID, ForeignKey('folder.id'), primary_key = True)
|
||||||
date = Column(DateTime, default = now)
|
date = Column(DateTime, default = now)
|
||||||
|
|
||||||
user = relationship('User')
|
user = relationship('User')
|
||||||
starred = relationship('Folder')
|
starred = relationship('Folder')
|
||||||
|
|
||||||
class StarredArtist(Base):
|
class StarredArtist(Base):
|
||||||
__tablename__ = 'starred_artist'
|
__tablename__ = 'starred_artist'
|
||||||
|
|
||||||
user_id = Column(UUID, ForeignKey('user.id'), primary_key = True)
|
user_id = Column(UUID, ForeignKey('user.id'), primary_key = True)
|
||||||
starred_id = Column(UUID, ForeignKey('artist.id'), primary_key = True)
|
starred_id = Column(UUID, ForeignKey('artist.id'), primary_key = True)
|
||||||
date = Column(DateTime, default = now)
|
date = Column(DateTime, default = now)
|
||||||
|
|
||||||
user = relationship('User')
|
user = relationship('User')
|
||||||
starred = relationship('Artist')
|
starred = relationship('Artist')
|
||||||
|
|
||||||
class StarredAlbum(Base):
|
class StarredAlbum(Base):
|
||||||
__tablename__ = 'starred_album'
|
__tablename__ = 'starred_album'
|
||||||
|
|
||||||
user_id = Column(UUID, ForeignKey('user.id'), primary_key = True)
|
user_id = Column(UUID, ForeignKey('user.id'), primary_key = True)
|
||||||
starred_id = Column(UUID, ForeignKey('album.id'), primary_key = True)
|
starred_id = Column(UUID, ForeignKey('album.id'), primary_key = True)
|
||||||
date = Column(DateTime, default = now)
|
date = Column(DateTime, default = now)
|
||||||
|
|
||||||
user = relationship('User')
|
user = relationship('User')
|
||||||
starred = relationship('Album')
|
starred = relationship('Album')
|
||||||
|
|
||||||
class StarredTrack(Base):
|
class StarredTrack(Base):
|
||||||
__tablename__ = 'starred_track'
|
__tablename__ = 'starred_track'
|
||||||
|
|
||||||
user_id = Column(UUID, ForeignKey('user.id'), primary_key = True)
|
user_id = Column(UUID, ForeignKey('user.id'), primary_key = True)
|
||||||
starred_id = Column(UUID, ForeignKey('track.id'), primary_key = True)
|
starred_id = Column(UUID, ForeignKey('track.id'), primary_key = True)
|
||||||
date = Column(DateTime, default = now)
|
date = Column(DateTime, default = now)
|
||||||
|
|
||||||
user = relationship('User')
|
user = relationship('User')
|
||||||
starred = relationship('Track')
|
starred = relationship('Track')
|
||||||
|
|
||||||
class RatingFolder(Base):
|
if config.getbool('features', 'rating', True):
|
||||||
__tablename__ = 'rating_folder'
|
class RatingFolder(Base):
|
||||||
|
__tablename__ = 'rating_folder'
|
||||||
|
|
||||||
user_id = Column(UUID, ForeignKey('user.id'), primary_key = True)
|
user_id = Column(UUID, ForeignKey('user.id'), primary_key = True)
|
||||||
rated_id = Column(UUID, ForeignKey('folder.id'), primary_key = True)
|
rated_id = Column(UUID, ForeignKey('folder.id'), primary_key = True)
|
||||||
rating = Column(Integer)
|
rating = Column(Integer)
|
||||||
|
|
||||||
user = relationship('User')
|
user = relationship('User')
|
||||||
rated = relationship('Folder')
|
rated = relationship('Folder')
|
||||||
|
|
||||||
class RatingTrack(Base):
|
class RatingTrack(Base):
|
||||||
__tablename__ = 'rating_track'
|
__tablename__ = 'rating_track'
|
||||||
|
|
||||||
user_id = Column(UUID, ForeignKey('user.id'), primary_key = True)
|
user_id = Column(UUID, ForeignKey('user.id'), primary_key = True)
|
||||||
rated_id = Column(UUID, ForeignKey('track.id'), primary_key = True)
|
rated_id = Column(UUID, ForeignKey('track.id'), primary_key = True)
|
||||||
rating = Column(Integer)
|
rating = Column(Integer)
|
||||||
|
|
||||||
user = relationship('User')
|
user = relationship('User')
|
||||||
rated = relationship('Track')
|
rated = relationship('Track')
|
||||||
|
|
||||||
class ChatMessage(Base):
|
class ChatMessage(Base):
|
||||||
__tablename__ = 'chat_message'
|
__tablename__ = 'chat_message'
|
||||||
|
Loading…
Reference in New Issue
Block a user