2012-10-20 16:05:37 +00:00
|
|
|
# coding: utf-8
|
|
|
|
|
2014-03-02 17:31:32 +00:00
|
|
|
# This file is part of Supysonic.
|
|
|
|
#
|
|
|
|
# Supysonic is a Python implementation of the Subsonic server API.
|
|
|
|
# Copyright (C) 2013, 2014 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/>.
|
|
|
|
|
2012-10-20 16:05:37 +00:00
|
|
|
from flask import request
|
2014-03-27 21:50:30 +00:00
|
|
|
from storm.expr import Desc, Avg, Min, Max
|
|
|
|
from storm.info import ClassAlias
|
2012-10-20 16:05:37 +00:00
|
|
|
import random
|
|
|
|
import uuid
|
|
|
|
|
2014-03-27 21:50:30 +00:00
|
|
|
from web import app, store
|
|
|
|
from db import Folder, Artist, Album, Track, RatingFolder, StarredFolder, StarredArtist, StarredAlbum, StarredTrack, User
|
|
|
|
from db import now
|
2012-12-09 17:39:27 +00:00
|
|
|
|
2012-11-22 13:51:43 +00:00
|
|
|
@app.route('/rest/getRandomSongs.view', methods = [ 'GET', 'POST' ])
|
2012-10-20 16:05:37 +00:00
|
|
|
def rand_songs():
|
|
|
|
size = request.args.get('size', '10')
|
|
|
|
genre, fromYear, toYear, musicFolderId = map(request.args.get, [ 'genre', 'fromYear', 'toYear', 'musicFolderId' ])
|
|
|
|
|
|
|
|
try:
|
|
|
|
size = int(size) if size else 10
|
|
|
|
fromYear = int(fromYear) if fromYear else None
|
|
|
|
toYear = int(toYear) if toYear else None
|
|
|
|
fid = uuid.UUID(musicFolderId) if musicFolderId else None
|
|
|
|
except:
|
2012-10-20 18:23:38 +00:00
|
|
|
return request.error_formatter(0, 'Invalid parameter format')
|
2012-10-20 16:05:37 +00:00
|
|
|
|
2014-03-27 21:50:30 +00:00
|
|
|
query = store.find(Track)
|
2012-10-20 16:05:37 +00:00
|
|
|
if fromYear:
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.find(Track.year >= fromYear)
|
2012-10-20 16:05:37 +00:00
|
|
|
if toYear:
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.find(Track.year <= toYear)
|
2012-10-20 16:05:37 +00:00
|
|
|
if genre:
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.find(Track.genre == genre)
|
2012-10-20 16:05:37 +00:00
|
|
|
if fid:
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.find(Track.root_folder_id == fid)
|
2013-09-23 10:43:56 +00:00
|
|
|
count = query.count()
|
2012-10-20 16:05:37 +00:00
|
|
|
|
2013-09-23 10:43:56 +00:00
|
|
|
if not count:
|
2012-10-20 16:05:37 +00:00
|
|
|
return request.formatter({ 'randomSongs': {} })
|
|
|
|
|
2013-09-23 10:43:56 +00:00
|
|
|
tracks = []
|
|
|
|
for _ in xrange(size):
|
|
|
|
x = random.choice(xrange(count))
|
2014-03-27 21:50:30 +00:00
|
|
|
tracks.append(query[x])
|
2013-09-23 10:43:56 +00:00
|
|
|
|
2012-10-20 16:05:37 +00:00
|
|
|
return request.formatter({
|
|
|
|
'randomSongs': {
|
2013-09-23 10:43:56 +00:00
|
|
|
'song': [ t.as_subsonic_child(request.user) for t in tracks ]
|
2012-10-20 16:05:37 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2012-12-09 17:39:27 +00:00
|
|
|
@app.route('/rest/getAlbumList.view', methods = [ 'GET', 'POST' ])
|
|
|
|
def album_list():
|
2013-06-13 16:17:33 +00:00
|
|
|
ltype, size, offset = map(request.args.get, [ 'type', 'size', 'offset' ])
|
2012-12-09 17:39:27 +00:00
|
|
|
try:
|
|
|
|
size = int(size) if size else 10
|
|
|
|
offset = int(offset) if offset else 0
|
|
|
|
except:
|
|
|
|
return request.error_formatter(0, 'Invalid parameter format')
|
|
|
|
|
2014-03-27 21:50:30 +00:00
|
|
|
query = store.find(Folder, Track.folder_id == Folder.id)
|
2012-12-09 17:39:27 +00:00
|
|
|
if ltype == 'random':
|
2013-09-23 10:43:56 +00:00
|
|
|
albums = []
|
|
|
|
count = query.count()
|
2014-02-26 21:41:59 +00:00
|
|
|
|
|
|
|
if not count:
|
|
|
|
return request.formatter({ 'albumList': {} })
|
|
|
|
|
2013-09-23 10:43:56 +00:00
|
|
|
for _ in xrange(size):
|
|
|
|
x = random.choice(xrange(count))
|
2014-03-27 21:50:30 +00:00
|
|
|
albums.append(query[x])
|
2013-09-23 10:43:56 +00:00
|
|
|
|
2012-12-09 17:39:27 +00:00
|
|
|
return request.formatter({
|
|
|
|
'albumList': {
|
2013-09-23 10:43:56 +00:00
|
|
|
'album': [ a.as_subsonic_child(request.user) for a in albums ]
|
2012-12-09 17:39:27 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
elif ltype == 'newest':
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.order_by(Desc(Folder.created)).config(distinct = True)
|
2012-12-09 17:39:27 +00:00
|
|
|
elif ltype == 'highest':
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.find(RatingFolder.rated_id == Folder.id).group_by(Folder.id).order_by(Desc(Avg(RatingFolder.rating)))
|
2012-12-09 17:39:27 +00:00
|
|
|
elif ltype == 'frequent':
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.group_by(Folder.id).order_by(Desc(Avg(Track.play_count)))
|
2012-12-09 17:39:27 +00:00
|
|
|
elif ltype == 'recent':
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.group_by(Folder.id).order_by(Desc(Max(Track.last_play)))
|
2012-12-09 17:39:27 +00:00
|
|
|
elif ltype == 'starred':
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.find(StarredFolder.starred_id == Folder.id, User.id == StarredFolder.user_id, User.name == request.username)
|
2012-12-09 17:39:27 +00:00
|
|
|
elif ltype == 'alphabeticalByName':
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.order_by(Folder.name).config(distinct = True)
|
2012-12-09 17:39:27 +00:00
|
|
|
elif ltype == 'alphabeticalByArtist':
|
2014-03-27 21:50:30 +00:00
|
|
|
parent = ClassAlias(Folder)
|
|
|
|
query = query.find(Folder.parent_id == parent.id).order_by(parent.name, Folder.name).config(distinct = True)
|
2012-12-09 17:39:27 +00:00
|
|
|
else:
|
|
|
|
return request.error_formatter(0, 'Unknown search type')
|
|
|
|
|
|
|
|
return request.formatter({
|
|
|
|
'albumList': {
|
2014-03-27 21:50:30 +00:00
|
|
|
'album': [ f.as_subsonic_child(request.user) for f in query[offset:offset+size] ]
|
2012-12-09 17:39:27 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2013-06-10 15:36:30 +00:00
|
|
|
@app.route('/rest/getAlbumList2.view', methods = [ 'GET', 'POST' ])
|
2013-06-12 19:54:46 +00:00
|
|
|
def album_list_id3():
|
2013-06-13 16:17:33 +00:00
|
|
|
ltype, size, offset = map(request.args.get, [ 'type', 'size', 'offset' ])
|
2013-06-10 15:36:30 +00:00
|
|
|
try:
|
|
|
|
size = int(size) if size else 10
|
|
|
|
offset = int(offset) if offset else 0
|
|
|
|
except:
|
|
|
|
return request.error_formatter(0, 'Invalid parameter format')
|
|
|
|
|
2014-03-27 21:50:30 +00:00
|
|
|
query = store.find(Album)
|
2013-06-10 15:36:30 +00:00
|
|
|
if ltype == 'random':
|
2013-09-23 10:43:56 +00:00
|
|
|
albums = []
|
|
|
|
count = query.count()
|
2014-02-26 21:41:59 +00:00
|
|
|
|
|
|
|
if not count:
|
|
|
|
return request.formatter({ 'albumList2': {} })
|
|
|
|
|
2013-09-23 10:43:56 +00:00
|
|
|
for _ in xrange(size):
|
|
|
|
x = random.choice(xrange(count))
|
2014-03-27 21:50:30 +00:00
|
|
|
albums.append(query[x])
|
2013-09-23 10:43:56 +00:00
|
|
|
|
2013-06-10 15:36:30 +00:00
|
|
|
return request.formatter({
|
2013-09-23 11:00:51 +00:00
|
|
|
'albumList2': {
|
2013-09-23 10:43:56 +00:00
|
|
|
'album': [ a.as_subsonic_album(request.user) for a in albums ]
|
2013-06-10 15:36:30 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
elif ltype == 'newest':
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.find(Track.album_id == Album.id).group_by(Album.id).order_by(Desc(Min(Track.created)))
|
2013-06-10 15:36:30 +00:00
|
|
|
elif ltype == 'frequent':
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.find(Track.album_id == Album.id).group_by(Album.id).order_by(Desc(Avg(Track.play_count)))
|
2013-06-10 15:36:30 +00:00
|
|
|
elif ltype == 'recent':
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.find(Track.album_id == Album.id).group_by(Album.id).order_by(Desc(Max(Track.last_play)))
|
2013-06-10 15:36:30 +00:00
|
|
|
elif ltype == 'starred':
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.find(StarredAlbum.starred_id == Album.id, User.id == StarredAlbum.user_id, User.name == request.username)
|
2013-06-10 15:36:30 +00:00
|
|
|
elif ltype == 'alphabeticalByName':
|
|
|
|
query = query.order_by(Album.name)
|
|
|
|
elif ltype == 'alphabeticalByArtist':
|
2014-03-27 21:50:30 +00:00
|
|
|
query = query.find(Artist.id == Album.artist_id).order_by(Artist.name, Album.name)
|
2013-06-10 15:36:30 +00:00
|
|
|
else:
|
|
|
|
return request.error_formatter(0, 'Unknown search type')
|
|
|
|
|
|
|
|
return request.formatter({
|
|
|
|
'albumList2': {
|
2014-03-27 21:50:30 +00:00
|
|
|
'album': [ f.as_subsonic_album(request.user) for f in query[offset:offset+size] ]
|
2013-06-10 15:36:30 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2013-06-10 18:56:20 +00:00
|
|
|
@app.route('/rest/getNowPlaying.view', methods = [ 'GET', 'POST' ])
|
|
|
|
def now_playing():
|
2014-03-27 21:50:30 +00:00
|
|
|
# TODO test this, test this, test this
|
|
|
|
query = store.find(User, Track.id == User.last_play_id, ((Track.duration * 2) + User.last_play_date) < now())
|
2013-07-15 18:30:33 +00:00
|
|
|
|
2013-06-10 18:56:20 +00:00
|
|
|
return request.formatter({
|
|
|
|
'nowPlaying': {
|
|
|
|
'entry': [ dict(
|
2013-06-13 16:44:56 +00:00
|
|
|
u.last_play.as_subsonic_child(request.user).items() +
|
2013-06-10 18:56:20 +00:00
|
|
|
{ 'username': u.name, 'minutesAgo': (now() - u.last_play_date).seconds / 60, 'playerId': 0 }.items()
|
|
|
|
) for u in query ]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2013-06-12 19:29:42 +00:00
|
|
|
@app.route('/rest/getStarred.view', methods = [ 'GET', 'POST' ])
|
|
|
|
def get_starred():
|
2014-03-27 21:50:30 +00:00
|
|
|
folders = store.find(StarredFolder, StarredFolder.user_id == User.id, User.name == request.username)
|
|
|
|
|
2013-06-12 19:29:42 +00:00
|
|
|
return request.formatter({
|
|
|
|
'starred': {
|
2014-03-27 21:50:30 +00:00
|
|
|
'artist': [ { 'id': str(sf.starred_id), 'name': sf.starred.name } for sf in folders.find(Folder.parent_id == StarredFolder.starred_id, Track.folder_id == Folder.id).config(distinct = True) ],
|
|
|
|
'album': [ sf.starred.as_subsonic_child(request.user) for sf in folders.find(Track.folder_id == StarredFolder.starred_id).config(distinct = True) ],
|
|
|
|
'song': [ st.starred.as_subsonic_child(request.user) for st in store.find(StarredTrack, StarredTrack.user_id == User.id, User.name == request.username) ]
|
2013-06-12 19:29:42 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
@app.route('/rest/getStarred2.view', methods = [ 'GET', 'POST' ])
|
|
|
|
def get_starred_id3():
|
|
|
|
return request.formatter({
|
|
|
|
'starred2': {
|
2014-03-27 21:50:30 +00:00
|
|
|
'artist': [ sa.starred.as_subsonic_artist(request.user) for sa in store.find(StarredArtist, StarredArtist.user_id == User.id, User.name == request.username) ],
|
|
|
|
'album': [ sa.starred.as_subsonic_album(request.user) for sa in store.find(StarredAlbum, StarredAlbum.user_id == User.id, User.name == request.username) ],
|
|
|
|
'song': [ st.starred.as_subsonic_child(request.user) for st in store.find(StarredTrack, StarredTrack.user_id == User.id, User.name == request.username) ]
|
2013-06-12 19:29:42 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|