mirror of
https://github.com/spl0k/supysonic.git
synced 2024-12-23 01:16:18 +00:00
API: marked explicitly unsupported methods/parameters as such
This commit is contained in:
parent
adb4e7e89b
commit
a6b894c586
82
docs/api.md
82
docs/api.md
@ -69,15 +69,15 @@ or with version 1.8.0.
|
|||||||
| [`getCaptions`](#getcaptions) | 1.15.0 | 🔴 |
|
| [`getCaptions`](#getcaptions) | 1.15.0 | 🔴 |
|
||||||
| [`getCoverArt`](#getcoverart) | | ✔️ |
|
| [`getCoverArt`](#getcoverart) | | ✔️ |
|
||||||
| [`getLyrics`](#getlyrics) | | ✔️ |
|
| [`getLyrics`](#getlyrics) | | ✔️ |
|
||||||
| [`getAvatar`](#getavatar) | | 🔴 |
|
| [`getAvatar`](#getavatar) | | ❌ |
|
||||||
| [`star`](#star) | | ✔️ |
|
| [`star`](#star) | | ✔️ |
|
||||||
| [`unstar`](#unstar) | | ✔️ |
|
| [`unstar`](#unstar) | | ✔️ |
|
||||||
| [`setRating`](#setrating) | | ✔️ |
|
| [`setRating`](#setrating) | | ✔️ |
|
||||||
| [`scrobble`](#scrobble) | | ✔️ |
|
| [`scrobble`](#scrobble) | | ✔️ |
|
||||||
| [`getShares`](#getshares) | | 🔴 |
|
| [`getShares`](#getshares) | | ❌ |
|
||||||
| [`createShare`](#createshare) | | 🔴 |
|
| [`createShare`](#createshare) | | ❌ |
|
||||||
| [`updateShare`](#updateshare) | | 🔴 |
|
| [`updateShare`](#updateshare) | | ❌ |
|
||||||
| [`deleteShare`](#deleteshare) | | 🔴 |
|
| [`deleteShare`](#deleteshare) | | ❌ |
|
||||||
| [`getPodcasts`](#getpodcasts) | | ❔ |
|
| [`getPodcasts`](#getpodcasts) | | ❔ |
|
||||||
| [`getNewestPodcasts`](#getnewestpodcasts) | 1.14.0 | ❔ |
|
| [`getNewestPodcasts`](#getnewestpodcasts) | 1.14.0 | ❔ |
|
||||||
| [`refreshPodcasts`](#refreshpodcasts) | 1.9.0 | ❔ |
|
| [`refreshPodcasts`](#refreshpodcasts) | 1.9.0 | ❔ |
|
||||||
@ -432,8 +432,8 @@ No parameter
|
|||||||
| `id` | | ✔️ |
|
| `id` | | ✔️ |
|
||||||
| `maxBitRate` | | ✔️ |
|
| `maxBitRate` | | ✔️ |
|
||||||
| `format` | | ✔️ |
|
| `format` | | ✔️ |
|
||||||
| `timeOffset` | | 🔴 |
|
| `timeOffset` | | ❌ |
|
||||||
| `size` | | 🔴 |
|
| `size` | | ❌ |
|
||||||
| `estimateContentLength` | | 📅 |
|
| `estimateContentLength` | | 📅 |
|
||||||
| `converted` | 1.15.0 | 🔴 |
|
| `converted` | 1.15.0 | 🔴 |
|
||||||
|
|
||||||
@ -478,11 +478,11 @@ No parameter
|
|||||||
| `title` | | ✔️ |
|
| `title` | | ✔️ |
|
||||||
|
|
||||||
#### `getAvatar`
|
#### `getAvatar`
|
||||||
🔴
|
❌
|
||||||
|
|
||||||
| Parameter | Vers. | |
|
| Parameter | Vers. | |
|
||||||
|------------|-------|---|
|
|------------|-------|---|
|
||||||
| `username` | | 🔴 |
|
| `username` | | ❌ |
|
||||||
|
|
||||||
### Media annotation
|
### Media annotation
|
||||||
|
|
||||||
@ -524,33 +524,33 @@ No parameter
|
|||||||
### Sharing
|
### Sharing
|
||||||
|
|
||||||
#### `getShares`
|
#### `getShares`
|
||||||
🔴
|
❌
|
||||||
No parameter
|
No parameter
|
||||||
|
|
||||||
#### `createShare`
|
#### `createShare`
|
||||||
🔴
|
❌
|
||||||
|
|
||||||
| Parameter | Vers. | |
|
| Parameter | Vers. | |
|
||||||
|---------------|-------|---|
|
|---------------|-------|---|
|
||||||
| `id` | | 🔴 |
|
| `id` | | ❌ |
|
||||||
| `description` | | 🔴 |
|
| `description` | | ❌ |
|
||||||
| `expires` | | 🔴 |
|
| `expires` | | ❌ |
|
||||||
|
|
||||||
#### `updateShare`
|
#### `updateShare`
|
||||||
🔴
|
❌
|
||||||
|
|
||||||
| Parameter | Vers. | |
|
| Parameter | Vers. | |
|
||||||
|---------------|-------|---|
|
|---------------|-------|---|
|
||||||
| `id` | | 🔴 |
|
| `id` | | ❌ |
|
||||||
| `description` | | 🔴 |
|
| `description` | | ❌ |
|
||||||
| `expires` | | 🔴 |
|
| `expires` | | ❌ |
|
||||||
|
|
||||||
#### `deleteShare`
|
#### `deleteShare`
|
||||||
🔴
|
❌
|
||||||
|
|
||||||
| Parameter | Vers. | |
|
| Parameter | Vers. | |
|
||||||
|-----------|-------|---|
|
|-----------|-------|---|
|
||||||
| `id` | | 🔴 |
|
| `id` | | ❌ |
|
||||||
|
|
||||||
### Podcast
|
### Podcast
|
||||||
|
|
||||||
@ -687,19 +687,19 @@ No parameter
|
|||||||
| `username` | | ✔️ |
|
| `username` | | ✔️ |
|
||||||
| `password` | | ✔️ |
|
| `password` | | ✔️ |
|
||||||
| `email` | | ✔️ |
|
| `email` | | ✔️ |
|
||||||
| `ldapAuthenticated` | | ❔ |
|
| `ldapAuthenticated` | | |
|
||||||
| `adminRole` | | ✔️ |
|
| `adminRole` | | ✔️ |
|
||||||
| `settingsRole` | | ❔ |
|
| `settingsRole` | | |
|
||||||
| `streamRole` | | ❔ |
|
| `streamRole` | | |
|
||||||
| `jukeboxRole` | | 📅 |
|
| `jukeboxRole` | | 📅 |
|
||||||
| `downloadRole` | | ❔ |
|
| `downloadRole` | | |
|
||||||
| `uploadRole` | | ❔ |
|
| `uploadRole` | | |
|
||||||
| `playlistRole` | | ❔ |
|
| `playlistRole` | | |
|
||||||
| `coverArtRole` | | ❔ |
|
| `coverArtRole` | | |
|
||||||
| `commentRole` | | ❔ |
|
| `commentRole` | | |
|
||||||
| `podcastRole` | | ❔ |
|
| `podcastRole` | | |
|
||||||
| `shareRole` | | 🔴 |
|
| `shareRole` | | |
|
||||||
| `videoConversionRole` | 1.14.0 | 🔴 |
|
| `videoConversionRole` | 1.14.0 | |
|
||||||
| `musicFolderId` | 1.12.0 | 📅 |
|
| `musicFolderId` | 1.12.0 | 📅 |
|
||||||
|
|
||||||
#### `updateUser`
|
#### `updateUser`
|
||||||
@ -710,18 +710,18 @@ No parameter
|
|||||||
| `username` | 1.10.2 | 📅 |
|
| `username` | 1.10.2 | 📅 |
|
||||||
| `password` | 1.10.2 | 📅 |
|
| `password` | 1.10.2 | 📅 |
|
||||||
| `email` | 1.10.2 | 📅 |
|
| `email` | 1.10.2 | 📅 |
|
||||||
| `ldapAuthenticated` | 1.10.2 | ❔ |
|
| `ldapAuthenticated` | 1.10.2 | |
|
||||||
| `adminRole` | 1.10.2 | 📅 |
|
| `adminRole` | 1.10.2 | 📅 |
|
||||||
| `settingsRole` | 1.10.2 | ❔ |
|
| `settingsRole` | 1.10.2 | |
|
||||||
| `streamRole` | 1.10.2 | ❔ |
|
| `streamRole` | 1.10.2 | |
|
||||||
| `jukeboxRole` | 1.10.2 | 📅 |
|
| `jukeboxRole` | 1.10.2 | 📅 |
|
||||||
| `downloadRole` | 1.10.2 | ❔ |
|
| `downloadRole` | 1.10.2 | |
|
||||||
| `uploadRole` | 1.10.2 | ❔ |
|
| `uploadRole` | 1.10.2 | |
|
||||||
| `coverArtRole` | 1.10.2 | ❔ |
|
| `coverArtRole` | 1.10.2 | |
|
||||||
| `commentRole` | 1.10.2 | ❔ |
|
| `commentRole` | 1.10.2 | |
|
||||||
| `podcastRole` | 1.10.2 | ❔ |
|
| `podcastRole` | 1.10.2 | |
|
||||||
| `shareRole` | 1.10.2 | 🔴 |
|
| `shareRole` | 1.10.2 | |
|
||||||
| `videoConversionRole` | 1.14.0 | 🔴 |
|
| `videoConversionRole` | 1.14.0 | |
|
||||||
| `musicFolderId` | 1.12.0 | 📅 |
|
| `musicFolderId` | 1.12.0 | 📅 |
|
||||||
| `maxBitRate` | 1.13.0 | 📅 |
|
| `maxBitRate` | 1.13.0 | 📅 |
|
||||||
|
|
||||||
|
@ -88,4 +88,5 @@ from .annotation import *
|
|||||||
from .chat import *
|
from .chat import *
|
||||||
from .search import *
|
from .search import *
|
||||||
from .playlists import *
|
from .playlists import *
|
||||||
|
from .unsupported import *
|
||||||
|
|
||||||
|
@ -139,7 +139,3 @@ def track_info():
|
|||||||
res = get_entity(Track)
|
res = get_entity(Track)
|
||||||
return request.formatter('song', res.as_subsonic_child(request.user, request.client))
|
return request.formatter('song', res.as_subsonic_child(request.user, request.client))
|
||||||
|
|
||||||
@api.route('/getVideos.view', methods = [ 'GET', 'POST' ])
|
|
||||||
def list_videos():
|
|
||||||
return request.formatter.error(0, 'Video streaming not supported'), 501
|
|
||||||
|
|
||||||
|
@ -38,5 +38,5 @@ def generic_error(e): # pragma: nocover
|
|||||||
#@api.errorhandler(404)
|
#@api.errorhandler(404)
|
||||||
@api.route('/<path:invalid>', methods = [ 'GET', 'POST' ]) # blueprint 404 workaround
|
@api.route('/<path:invalid>', methods = [ 'GET', 'POST' ]) # blueprint 404 workaround
|
||||||
def not_found(*args, **kwargs):
|
def not_found(*args, **kwargs):
|
||||||
return GenericError('Not implemented'), 501
|
return GenericError('Unknown method'), 404
|
||||||
|
|
||||||
|
@ -34,6 +34,11 @@ class GenericError(SubsonicAPIException):
|
|||||||
class ServerError(GenericError):
|
class ServerError(GenericError):
|
||||||
code = 500
|
code = 500
|
||||||
|
|
||||||
|
class UnsupportedParameter(GenericError):
|
||||||
|
def __init__(self, parameter, *args, **kwargs):
|
||||||
|
message = "Unsupported parameter '{}'".format(parameter)
|
||||||
|
super(UnsupportedParameter, self).__init__(message, *args, **kwargs)
|
||||||
|
|
||||||
class MissingParameter(SubsonicAPIException):
|
class MissingParameter(SubsonicAPIException):
|
||||||
api_code = 10
|
api_code = 10
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ from ..db import Track, Album, Artist, Folder, User, ClientPrefs, now
|
|||||||
from ..py23 import dict
|
from ..py23 import dict
|
||||||
|
|
||||||
from . import api, get_entity
|
from . import api, get_entity
|
||||||
from .exceptions import GenericError, MissingParameter, NotFound, ServerError
|
from .exceptions import GenericError, MissingParameter, NotFound, ServerError, UnsupportedParameter
|
||||||
|
|
||||||
def prepare_transcoding_cmdline(base_cmdline, input_file, input_format, output_format, output_bitrate):
|
def prepare_transcoding_cmdline(base_cmdline, input_file, input_format, output_format, output_bitrate):
|
||||||
if not base_cmdline:
|
if not base_cmdline:
|
||||||
@ -39,7 +39,12 @@ def prepare_transcoding_cmdline(base_cmdline, input_file, input_format, output_f
|
|||||||
def stream_media():
|
def stream_media():
|
||||||
res = get_entity(Track)
|
res = get_entity(Track)
|
||||||
|
|
||||||
maxBitRate, format, timeOffset, size, estimateContentLength = map(request.values.get, [ 'maxBitRate', 'format', 'timeOffset', 'size', 'estimateContentLength' ])
|
if 'timeOffset' in request.values:
|
||||||
|
raise UnsupportedParameter('timeOffset')
|
||||||
|
if 'size' in request.values:
|
||||||
|
raise UnsupportedParameter('size')
|
||||||
|
|
||||||
|
maxBitRate, format, estimateContentLength = map(request.values.get, [ 'maxBitRate', 'format', 'estimateContentLength' ])
|
||||||
if format:
|
if format:
|
||||||
format = format.lower()
|
format = format.lower()
|
||||||
|
|
||||||
@ -94,7 +99,7 @@ def stream_media():
|
|||||||
if not data:
|
if not data:
|
||||||
break
|
break
|
||||||
yield data
|
yield data
|
||||||
except:
|
except: # pragma: nocover
|
||||||
if dec_proc != None:
|
if dec_proc != None:
|
||||||
dec_proc.terminate()
|
dec_proc.terminate()
|
||||||
proc.terminate()
|
proc.terminate()
|
||||||
@ -184,10 +189,10 @@ def lyrics():
|
|||||||
title = root.find('cl:LyricSong', namespaces = ns).text,
|
title = root.find('cl:LyricSong', namespaces = ns).text,
|
||||||
_value_ = root.find('cl:Lyric', namespaces = ns).text
|
_value_ = root.find('cl:Lyric', namespaces = ns).text
|
||||||
))
|
))
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e: # pragma: nocover
|
||||||
current_app.logger.warning('Error while requesting the ChartLyrics API: ' + str(e))
|
current_app.logger.warning('Error while requesting the ChartLyrics API: ' + str(e))
|
||||||
|
|
||||||
return request.formatter('lyrics', dict())
|
return request.formatter('lyrics', dict()) # pragma: nocover
|
||||||
|
|
||||||
def read_file_as_unicode(path):
|
def read_file_as_unicode(path):
|
||||||
""" Opens a file trying with different encodings and returns the contents as a unicode string """
|
""" Opens a file trying with different encodings and returns the contents as a unicode string """
|
||||||
|
22
supysonic/api/unsupported.py
Normal file
22
supysonic/api/unsupported.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
# This file is part of Supysonic.
|
||||||
|
# Supysonic is a Python implementation of the Subsonic server API.
|
||||||
|
#
|
||||||
|
# Copyright (C) 2018 Alban 'spl0k' Féron
|
||||||
|
#
|
||||||
|
# Distributed under terms of the GNU AGPLv3 license.
|
||||||
|
|
||||||
|
from . import api
|
||||||
|
from .exceptions import GenericError
|
||||||
|
|
||||||
|
methods = (
|
||||||
|
'getVideos', 'getAvatar', 'getShares', 'createShare', 'updateShare', 'deleteShare',
|
||||||
|
)
|
||||||
|
|
||||||
|
def unsupported():
|
||||||
|
return GenericError('Not supported by Supysonic'), 501
|
||||||
|
|
||||||
|
for m in methods:
|
||||||
|
api.add_url_rule('/{}.view'.format(m), 'unsupported', unsupported, methods = [ 'GET', 'POST' ])
|
||||||
|
|
@ -128,13 +128,23 @@ class ApiSetupTestCase(TestBase):
|
|||||||
self.assertIn('license', json['subsonic-response'])
|
self.assertIn('license', json['subsonic-response'])
|
||||||
|
|
||||||
def test_not_implemented(self):
|
def test_not_implemented(self):
|
||||||
# Access to not implemented endpoint
|
# Access to not implemented/unknown endpoint
|
||||||
rv = self.client.get('/rest/not-implemented', query_string = { 'u': 'alice', 'p': 'Alic3', 'c': 'tests' })
|
rv = self.client.get('/rest/unknown', query_string = { 'u': 'alice', 'p': 'Alic3', 'c': 'tests' })
|
||||||
|
self.assertEqual(rv.status_code, 404)
|
||||||
|
self.assertIn('status="failed"', rv.data)
|
||||||
|
self.assertIn('code="0"', rv.data)
|
||||||
|
|
||||||
|
rv = self.client.post('/rest/unknown', data = { 'u': 'alice', 'p': 'Alic3', 'c': 'tests' })
|
||||||
|
self.assertEqual(rv.status_code, 404)
|
||||||
|
self.assertIn('status="failed"', rv.data)
|
||||||
|
self.assertIn('code="0"', rv.data)
|
||||||
|
|
||||||
|
rv = self.client.get('/rest/getVideos.view', query_string = { 'u': 'alice', 'p': 'Alic3', 'c': 'tests' })
|
||||||
self.assertEqual(rv.status_code, 501)
|
self.assertEqual(rv.status_code, 501)
|
||||||
self.assertIn('status="failed"', rv.data)
|
self.assertIn('status="failed"', rv.data)
|
||||||
self.assertIn('code="0"', rv.data)
|
self.assertIn('code="0"', rv.data)
|
||||||
|
|
||||||
rv = self.client.post('/rest/not-implemented', data = { 'u': 'alice', 'p': 'Alic3', 'c': 'tests' })
|
rv = self.client.post('/rest/getVideos.view', data = { 'u': 'alice', 'p': 'Alic3', 'c': 'tests' })
|
||||||
self.assertEqual(rv.status_code, 501)
|
self.assertEqual(rv.status_code, 501)
|
||||||
self.assertIn('status="failed"', rv.data)
|
self.assertIn('status="failed"', rv.data)
|
||||||
self.assertIn('code="0"', rv.data)
|
self.assertIn('code="0"', rv.data)
|
||||||
|
@ -57,6 +57,8 @@ class MediaTestCase(ApiTestBase):
|
|||||||
self._make_request('stream', { 'id': str(uuid.uuid4()) }, error = 70)
|
self._make_request('stream', { 'id': str(uuid.uuid4()) }, error = 70)
|
||||||
self._make_request('stream', { 'id': str(self.folderid) }, error = 70)
|
self._make_request('stream', { 'id': str(self.folderid) }, error = 70)
|
||||||
self._make_request('stream', { 'id': str(self.trackid), 'maxBitRate': 'string' }, error = 0)
|
self._make_request('stream', { 'id': str(self.trackid), 'maxBitRate': 'string' }, error = 0)
|
||||||
|
self._make_request('stream', { 'id': str(self.trackid), 'timeOffset': 2 }, error = 0)
|
||||||
|
self._make_request('stream', { 'id': str(self.trackid), 'size': '640x480' }, error = 0)
|
||||||
|
|
||||||
rv = self.client.get('/rest/stream.view', query_string = { 'u': 'alice', 'p': 'Alic3', 'c': 'tests', 'id': str(self.trackid) })
|
rv = self.client.get('/rest/stream.view', query_string = { 'u': 'alice', 'p': 'Alic3', 'c': 'tests', 'id': str(self.trackid) })
|
||||||
self.assertEqual(rv.status_code, 200)
|
self.assertEqual(rv.status_code, 200)
|
||||||
|
Loading…
Reference in New Issue
Block a user