1
0
mirror of https://github.com/spl0k/supysonic.git synced 2024-09-19 19:01:03 +00:00

Add 'transcoded' info to API responses if the server is set to transcode for that client

Closes #62
This commit is contained in:
spl0k 2017-07-02 17:08:40 +02:00
parent fb79fc3f18
commit 1e1b475fe6
9 changed files with 70 additions and 35 deletions

View File

@ -0,0 +1,25 @@
# coding: utf-8
# This file is part of Supysonic.
#
# Supysonic is a Python implementation of the Subsonic server API.
# Copyright (C) 2013-2017 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 mimetypes
def get_mime(ext):
return mimetypes.guess_type('dummy.' + ext, False)[0] or config.get('mimetypes', ext) or 'application/octet-stream'

View File

@ -75,6 +75,22 @@ def authorize():
request.username = username
request.user = user
@app.before_request
def get_client_prefs():
if not request.path.startswith('/rest/'):
return
client = request.values.get('c')
prefs = store.get(ClientPrefs, (request.user.id, client))
if not prefs:
prefs = ClientPrefs()
prefs.user_id = request.user.id
prefs.client_name = client
store.add(prefs)
store.commit()
request.prefs = prefs
@app.after_request
def set_headers(response):
if not request.path.startswith('/rest/'):

View File

@ -63,7 +63,7 @@ def rand_songs():
return request.formatter({
'randomSongs': {
'song': [ t.as_subsonic_child(request.user) for t in tracks ]
'song': [ t.as_subsonic_child(request.user, request.prefs) for t in tracks ]
}
})
@ -171,7 +171,7 @@ def now_playing():
return request.formatter({
'nowPlaying': {
'entry': [ dict(
u.last_play.as_subsonic_child(request.user).items() +
u.last_play.as_subsonic_child(request.user, request.prefs).items() +
{ 'username': u.name, 'minutesAgo': (now() - u.last_play_date).seconds / 60, 'playerId': 0 }.items()
) for u in query if u.last_play_date + timedelta(seconds = u.last_play.duration * 2) > now() ]
}
@ -185,7 +185,7 @@ def get_starred():
'starred': {
'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) ]
'song': [ st.starred.as_subsonic_child(request.user, request.prefs) for st in store.find(StarredTrack, StarredTrack.user_id == User.id, User.name == request.username) ]
}
})
@ -195,7 +195,7 @@ def get_starred_id3():
'starred2': {
'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) ]
'song': [ st.starred.as_subsonic_child(request.user, request.prefs) for st in store.find(StarredTrack, StarredTrack.user_id == User.id, User.name == request.username) ]
}
})

View File

@ -97,7 +97,7 @@ def list_indexes():
'name': a.name
} for a in sorted(v, key = lambda a: a.name.lower()) ]
} for k, v in sorted(indexes.iteritems()) ],
'child': [ c.as_subsonic_child(request.user) for c in sorted(childs, key = lambda t: t.sort_key()) ]
'child': [ c.as_subsonic_child(request.user, request.prefs) for c in sorted(childs, key = lambda t: t.sort_key()) ]
}
})
@ -110,7 +110,7 @@ def show_directory():
directory = {
'id': str(res.id),
'name': res.name,
'child': [ f.as_subsonic_child(request.user) for f in sorted(res.children, key = lambda c: c.name.lower()) ] + [ t.as_subsonic_child(request.user) for t in sorted(res.tracks, key = lambda t: t.sort_key()) ]
'child': [ f.as_subsonic_child(request.user) for f in sorted(res.children, key = lambda c: c.name.lower()) ] + [ t.as_subsonic_child(request.user, request.prefs) for t in sorted(res.tracks, key = lambda t: t.sort_key()) ]
}
if not res.root:
directory['parent'] = str(res.parent_id)
@ -162,7 +162,7 @@ def album_info():
return res
info = res.as_subsonic_album(request.user)
info['song'] = [ t.as_subsonic_child(request.user) for t in sorted(res.tracks, key = lambda t: t.sort_key()) ]
info['song'] = [ t.as_subsonic_child(request.user, request.prefs) for t in sorted(res.tracks, key = lambda t: t.sort_key()) ]
return request.formatter({ 'album': info })
@ -172,7 +172,7 @@ def track_info():
if not status:
return res
return request.formatter({ 'song': res.as_subsonic_child(request.user) })
return request.formatter({ 'song': res.as_subsonic_child(request.user, request.prefs) })
@app.route('/rest/getVideos.view', methods = [ 'GET', 'POST' ])
def list_videos():

View File

@ -45,7 +45,7 @@ def stream_media():
if not status:
return res
maxBitRate, format, timeOffset, size, estimateContentLength, client = map(request.values.get, [ 'maxBitRate', 'format', 'timeOffset', 'size', 'estimateContentLength', 'c' ])
maxBitRate, format, timeOffset, size, estimateContentLength = map(request.values.get, [ 'maxBitRate', 'format', 'timeOffset', 'size', 'estimateContentLength' ])
if format:
format = format.lower()
@ -54,18 +54,10 @@ def stream_media():
dst_bitrate = res.bitrate
dst_mimetype = res.content_type
if client:
prefs = store.get(ClientPrefs, (request.user.id, client))
if not prefs:
prefs = ClientPrefs()
prefs.user_id = request.user.id
prefs.client_name = client
store.add(prefs)
if prefs.format:
dst_suffix = prefs.format
if prefs.bitrate and prefs.bitrate < dst_bitrate:
dst_bitrate = prefs.bitrate
if request.prefs.format:
dst_suffix = request.prefs.format
if request.prefs.bitrate and request.prefs.bitrate < dst_bitrate:
dst_bitrate = request.prefs.bitrate
if maxBitRate:
try:
@ -87,7 +79,9 @@ def stream_media():
if not transcoder and (not decoder or not encoder):
transcoder = config.get('transcoding', 'transcoder')
if not transcoder:
return request.error_formatter(0, 'No way to transcode from {} to {}'.format(src_suffix, dst_suffix))
message = 'No way to transcode from {} to {}'.format(src_suffix, dst_suffix)
app.logger.info(message)
return request.error_formatter(0, message)
transcoder, decoder, encoder = map(lambda x: prepare_transcoding_cmdline(x, res.path, src_suffix, dst_suffix, dst_bitrate), [ transcoder, decoder, encoder ])
try:

View File

@ -45,7 +45,7 @@ def show_playlist():
return res
info = res.as_subsonic_playlist(request.user)
info['entry'] = [ t.as_subsonic_child(request.user) for t in res.tracks ]
info['entry'] = [ t.as_subsonic_child(request.user, request.prefs) for t in res.tracks ]
return request.formatter({ 'playlist': info })
@app.route('/rest/createPlaylist.view', methods = [ 'GET', 'POST' ])

View File

@ -52,7 +52,7 @@ def old_search():
return request.formatter({ 'searchResult': {
'totalHits': folders.count() + tracks.count(),
'offset': offset,
'match': [ r.as_subsonic_child(request.user) for r in res ]
'match': [ r.as_subsonic_child(request.user) if r is Folder else r.as_subsonic_child(request.user, request.prefs) for r in res ]
}})
else:
return request.error_formatter(10, 'Missing search parameter')
@ -60,7 +60,7 @@ def old_search():
return request.formatter({ 'searchResult': {
'totalHits': query.count(),
'offset': offset,
'match': [ r.as_subsonic_child(request.user) for r in query[offset : offset + count] ]
'match': [ r.as_subsonic_child(request.user) if r is Folder else r.as_subsonic_child(request.user, request.prefs) for r in query[offset : offset + count] ]
}})
@app.route('/rest/search2.view', methods = [ 'GET', 'POST' ])
@ -89,7 +89,7 @@ def new_search():
return request.formatter({ 'searchResult2': {
'artist': [ { 'id': str(a.id), 'name': a.name } for a in artist_query ],
'album': [ f.as_subsonic_child(request.user) for f in album_query ],
'song': [ t.as_subsonic_child(request.user) for t in song_query ]
'song': [ t.as_subsonic_child(request.user, request.prefs) for t in song_query ]
}})
@app.route('/rest/search3.view', methods = [ 'GET', 'POST' ])
@ -117,6 +117,6 @@ def search_id3():
return request.formatter({ 'searchResult3': {
'artist': [ a.as_subsonic_artist(request.user) for a in artist_query ],
'album': [ a.as_subsonic_album(request.user) for a in album_query ],
'song': [ t.as_subsonic_child(request.user) for t in song_query ]
'song': [ t.as_subsonic_child(request.user, request.prefs) for t in song_query ]
}})

View File

@ -27,6 +27,8 @@ from storm.variables import Variable
import uuid, datetime, time
import os.path
from supysonic import get_mime
def now():
return datetime.datetime.now().replace(microsecond = 0)
@ -168,7 +170,7 @@ class Track(object):
folder_id = UUID()
folder = Reference(folder_id, Folder.id)
def as_subsonic_child(self, user):
def as_subsonic_child(self, user, prefs):
info = {
'id': str(self.id),
'parent': str(self.folder_id),
@ -209,8 +211,9 @@ class Track(object):
if avgRating:
info['averageRating'] = avgRating
# transcodedContentType
# transcodedSuffix
if prefs and prefs.format and prefs.format != self.suffix():
info['transcodedSuffix'] = prefs.format
info['transcodedContentType'] = get_mime(prefs.format)
return info

View File

@ -19,20 +19,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os, os.path
import time, mimetypes
import time
import mutagen
from storm.expr import ComparableExpr, compile, Like
from storm.exceptions import NotSupportedError
from supysonic import config
from supysonic import config, get_mime
from supysonic.db import Folder, Artist, Album, Track, User, PlaylistTrack
from supysonic.db import StarredFolder, StarredArtist, StarredAlbum, StarredTrack
from supysonic.db import RatingFolder, RatingTrack
def get_mime(ext):
return mimetypes.guess_type('dummy.' + ext, False)[0] or config.get('mimetypes', ext) or 'application/octet-stream'
# Hacking in support for a concatenation expression
class Concat(ComparableExpr):
__slots__ = ("left", "right", "db")