1
0
mirror of https://github.com/spl0k/supysonic.git synced 2024-12-22 17:06:17 +00:00

Get on my pony

This commit is contained in:
spl0k 2017-12-21 23:29:00 +01:00
parent 6daedc6919
commit 53fd4865cb
13 changed files with 115 additions and 125 deletions

View File

@ -18,15 +18,15 @@
# 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 binascii
import simplejson import simplejson
import uuid import uuid
import binascii
from flask import request, current_app as app from flask import request, current_app as app
from pony.orm import db_session, ObjectNotFound
from xml.dom import minidom from xml.dom import minidom
from xml.etree import ElementTree from xml.etree import ElementTree
from ..web import store
from ..managers.user import UserManager from ..managers.user import UserManager
@app.before_request @app.before_request
@ -70,7 +70,7 @@ def authorize():
error = request.error_formatter(40, 'Unauthorized'), 401 error = request.error_formatter(40, 'Unauthorized'), 401
if request.authorization: if request.authorization:
status, user = UserManager.try_auth(store, request.authorization.username, request.authorization.password) status, user = UserManager.try_auth(request.authorization.username, request.authorization.password)
if status == UserManager.SUCCESS: if status == UserManager.SUCCESS:
request.username = request.authorization.username request.username = request.authorization.username
request.user = user request.user = user
@ -81,7 +81,7 @@ def authorize():
return error return error
password = decode_password(password) password = decode_password(password)
status, user = UserManager.try_auth(store, username, password) status, user = UserManager.try_auth(username, password)
if status != UserManager.SUCCESS: if status != UserManager.SUCCESS:
return error return error
@ -97,13 +97,11 @@ def get_client_prefs():
return request.error_formatter(10, 'Missing required parameter') return request.error_formatter(10, 'Missing required parameter')
client = request.values.get('c') client = request.values.get('c')
prefs = store.get(ClientPrefs, (request.user.id, client)) with db_session:
if not prefs: try:
prefs = ClientPrefs() prefs = ClientPrefs[request.user.id, client]
prefs.user_id = request.user.id except ObjectNotFound:
prefs.client_name = client prefs = ClientPrefs(user = User[request.user.id], client_name = client)
store.add(prefs)
store.commit()
request.prefs = prefs request.prefs = prefs

View File

@ -23,12 +23,9 @@ import uuid
from datetime import timedelta from datetime import timedelta
from flask import request, current_app as app from flask import request, current_app as app
from storm.expr import Desc, Avg, Min, Max
from storm.info import ClassAlias
from ..db import Folder, Artist, Album, Track, RatingFolder, StarredFolder, StarredArtist, StarredAlbum, StarredTrack, User from ..db import Folder, Artist, Album, Track, RatingFolder, StarredFolder, StarredArtist, StarredAlbum, StarredTrack, User
from ..db import now from ..db import now
from ..web import store
@app.route('/rest/getRandomSongs.view', methods = [ 'GET', 'POST' ]) @app.route('/rest/getRandomSongs.view', methods = [ 'GET', 'POST' ])
def rand_songs(): def rand_songs():

View File

@ -27,7 +27,6 @@ from ..db import Track, Album, Artist, Folder
from ..db import StarredTrack, StarredAlbum, StarredArtist, StarredFolder from ..db import StarredTrack, StarredAlbum, StarredArtist, StarredFolder
from ..db import RatingTrack, RatingFolder from ..db import RatingTrack, RatingFolder
from ..lastfm import LastFm from ..lastfm import LastFm
from ..web import store
from . import get_entity from . import get_entity

View File

@ -24,7 +24,6 @@ import uuid
from flask import request, current_app as app from flask import request, current_app as app
from ..db import Folder, Artist, Album, Track from ..db import Folder, Artist, Album, Track
from ..web import store
from . import get_entity from . import get_entity

View File

@ -19,9 +19,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from flask import request, current_app as app from flask import request, current_app as app
from pony.orm import db_session
from ..db import ChatMessage from ..db import ChatMessage, User
from ..web import store
@app.route('/rest/getChatMessages.view', methods = [ 'GET', 'POST' ]) @app.route('/rest/getChatMessages.view', methods = [ 'GET', 'POST' ])
def get_chat(): def get_chat():
@ -31,11 +31,12 @@ def get_chat():
except: except:
return request.error_formatter(0, 'Invalid parameter') return request.error_formatter(0, 'Invalid parameter')
query = store.find(ChatMessage).order_by(ChatMessage.time) with db_session:
if since: query = ChatMessage.select().order_by(ChatMessage.time)
query = query.find(ChatMessage.time > since) if since:
query = query.filter(lambda m: m.time > since)
return request.formatter({ 'chatMessages': { 'chatMessage': [ msg.responsize() for msg in query ] }}) return request.formatter({ 'chatMessages': { 'chatMessage': [ msg.responsize() for msg in query ] }})
@app.route('/rest/addChatMessage.view', methods = [ 'GET', 'POST' ]) @app.route('/rest/addChatMessage.view', methods = [ 'GET', 'POST' ])
def add_chat_message(): def add_chat_message():
@ -43,10 +44,8 @@ def add_chat_message():
if not msg: if not msg:
return request.error_formatter(10, 'Missing message') return request.error_formatter(10, 'Missing message')
chat = ChatMessage() with db_session:
chat.user_id = request.user.id ChatMessage(user = User[request.user.id], message = msg)
chat.message = msg
store.add(chat)
store.commit()
return request.formatter({}) return request.formatter({})

View File

@ -29,7 +29,6 @@ from PIL import Image
from xml.etree import ElementTree from xml.etree import ElementTree
from .. import scanner from .. import scanner
from ..web import store
from ..db import Track, Album, Artist, Folder, User, ClientPrefs, now from ..db import Track, Album, Artist, Folder, User, ClientPrefs, now
from . import get_entity from . import get_entity

View File

@ -21,10 +21,8 @@
import uuid import uuid
from flask import request, current_app as app from flask import request, current_app as app
from storm.expr import Or
from ..db import Playlist, User, Track from ..db import Playlist, User, Track
from ..web import store
from . import get_entity from . import get_entity

View File

@ -20,10 +20,9 @@
from datetime import datetime from datetime import datetime
from flask import request, current_app as app from flask import request, current_app as app
from storm.info import ClassAlias from pony.orm import db_session, select
from ..db import Folder, Track, Artist, Album from ..db import Folder, Track, Artist, Album
from ..web import store
@app.route('/rest/search.view', methods = [ 'GET', 'POST' ]) @app.route('/rest/search.view', methods = [ 'GET', 'POST' ])
def old_search(): def old_search():
@ -38,34 +37,36 @@ def old_search():
min_date = datetime.fromtimestamp(newer_than) min_date = datetime.fromtimestamp(newer_than)
if artist: if artist:
parent = ClassAlias(Folder) query = select(t.folder.parent for t in Track if artist in t.folder.parent.name and t.folder.parent.created > min_date)
query = store.find(parent, Folder.parent_id == parent.id, Track.folder_id == Folder.id, parent.name.contains_string(artist), parent.created > min_date).config(distinct = True)
elif album: elif album:
query = store.find(Folder, Track.folder_id == Folder.id, Folder.name.contains_string(album), Folder.created > min_date).config(distinct = True) query = select(t.folder for t in Track if album in t.folder.name and t.folder.created > min_date)
elif title: elif title:
query = store.find(Track, Track.title.contains_string(title), Track.created > min_date) query = Track.select(lambda t: title in t.title and t.created > min_date)
elif anyf: elif anyf:
folders = store.find(Folder, Folder.name.contains_string(anyf), Folder.created > min_date) folders = Folder.select(lambda f: anyf in f.name and f.created > min_date)
tracks = store.find(Track, Track.title.contains_string(anyf), Track.created > min_date) tracks = Track.select(lambda t: anyf in t.title and t.created > min_date)
res = list(folders[offset : offset + count]) with db_session:
if offset + count > folders.count(): res = folders[offset : offset + count]
toff = max(0, offset - folders.count()) fcount = folders.count()
tend = offset + count - folders.count() if offset + count > fcount:
res += list(tracks[toff : tend]) toff = max(0, offset - fcount)
tend = offset + count - fcount
res += tracks[toff : tend]
return request.formatter({ 'searchResult': { return request.formatter({ 'searchResult': {
'totalHits': folders.count() + tracks.count(), 'totalHits': folders.count() + tracks.count(),
'offset': offset, 'offset': offset,
'match': [ r.as_subsonic_child(request.user) if isinstance(r, Folder) else r.as_subsonic_child(request.user, request.prefs) for r in res ] 'match': [ r.as_subsonic_child(request.user) if isinstance(r, Folder) else r.as_subsonic_child(request.user, request.prefs) for r in res ]
}}) }})
else: else:
return request.error_formatter(10, 'Missing search parameter') return request.error_formatter(10, 'Missing search parameter')
return request.formatter({ 'searchResult': { with db_session:
'totalHits': query.count(), return request.formatter({ 'searchResult': {
'offset': offset, 'totalHits': query.count(),
'match': [ r.as_subsonic_child(request.user) if isinstance(r, Folder) else r.as_subsonic_child(request.user, request.prefs) for r in query[offset : offset + count] ] 'offset': offset,
}}) 'match': [ r.as_subsonic_child(request.user) if isinstance(r, 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' ]) @app.route('/rest/search2.view', methods = [ 'GET', 'POST' ])
def new_search(): def new_search():
@ -85,16 +86,16 @@ def new_search():
if not query: if not query:
return request.error_formatter(10, 'Missing query parameter') return request.error_formatter(10, 'Missing query parameter')
parent = ClassAlias(Folder) with db_session:
artist_query = store.find(parent, Folder.parent_id == parent.id, Track.folder_id == Folder.id, parent.name.contains_string(query)).config(distinct = True, offset = artist_offset, limit = artist_count) artists = select(t.folder.parent for t in Track if query in t.folder.parent.name).limit(artist_count, artist_offset)
album_query = store.find(Folder, Track.folder_id == Folder.id, Folder.name.contains_string(query)).config(distinct = True, offset = album_offset, limit = album_count) albums = select(t.folder for t in Track if query in t.folder.name).limit(album_count, album_offset)
song_query = store.find(Track, Track.title.contains_string(query))[song_offset : song_offset + song_count] songs = Track.select(lambda t: query in t.title).limit(song_count, song_offset)
return request.formatter({ 'searchResult2': { return request.formatter({ 'searchResult2': {
'artist': [ { 'id': str(a.id), 'name': a.name } for a in artist_query ], 'artist': [ { 'id': str(a.id), 'name': a.name } for a in artists ],
'album': [ f.as_subsonic_child(request.user) for f in album_query ], 'album': [ f.as_subsonic_child(request.user) for f in albums ],
'song': [ t.as_subsonic_child(request.user, request.prefs) for t in song_query ] 'song': [ t.as_subsonic_child(request.user, request.prefs) for t in songs ]
}}) }})
@app.route('/rest/search3.view', methods = [ 'GET', 'POST' ]) @app.route('/rest/search3.view', methods = [ 'GET', 'POST' ])
def search_id3(): def search_id3():
@ -114,13 +115,14 @@ def search_id3():
if not query: if not query:
return request.error_formatter(10, 'Missing query parameter') return request.error_formatter(10, 'Missing query parameter')
artist_query = store.find(Artist, Artist.name.contains_string(query))[artist_offset : artist_offset + artist_count] with db_session:
album_query = store.find(Album, Album.name.contains_string(query))[album_offset : album_offset + album_count] artists = Artist.select(lambda a: query in a.name).limit(artist_count, artist_offset)
song_query = store.find(Track, Track.title.contains_string(query))[song_offset : song_offset + song_count] albums = Album.select(lambda a: query in a.name).limit(album_count, album_offset)
songs = Track.select(lambda t: query in t.title).limit(song_count, song_offset)
return request.formatter({ 'searchResult3': { return request.formatter({ 'searchResult3': {
'artist': [ a.as_subsonic_artist(request.user) for a in artist_query ], 'artist': [ a.as_subsonic_artist(request.user) for a in artists ],
'album': [ a.as_subsonic_album(request.user) for a in album_query ], 'album': [ a.as_subsonic_album(request.user) for a in albums ],
'song': [ t.as_subsonic_child(request.user, request.prefs) for t in song_query ] 'song': [ t.as_subsonic_child(request.user, request.prefs) for t in songs ]
}}) }})

View File

@ -19,10 +19,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from flask import request, current_app as app from flask import request, current_app as app
from pony.orm import db_session
from ..db import User from ..db import User
from ..managers.user import UserManager from ..managers.user import UserManager
from ..web import store
from . import decode_password from . import decode_password
@ -35,7 +35,8 @@ def user_info():
if username != request.username and not request.user.admin: if username != request.username and not request.user.admin:
return request.error_formatter(50, 'Admin restricted') return request.error_formatter(50, 'Admin restricted')
user = store.find(User, User.name == username).one() with db_session:
user = User.get(name = username)
if user is None: if user is None:
return request.error_formatter(70, 'Unknown user') return request.error_formatter(70, 'Unknown user')
@ -46,7 +47,8 @@ def users_info():
if not request.user.admin: if not request.user.admin:
return request.error_formatter(50, 'Admin restricted') return request.error_formatter(50, 'Admin restricted')
return request.formatter({ 'users': { 'user': [ u.as_subsonic_user() for u in store.find(User) ] } }) with db_session:
return request.formatter({ 'users': { 'user': [ u.as_subsonic_user() for u in User.select() ] } })
@app.route('/rest/createUser.view', methods = [ 'GET', 'POST' ]) @app.route('/rest/createUser.view', methods = [ 'GET', 'POST' ])
def user_add(): def user_add():
@ -59,7 +61,7 @@ def user_add():
admin = True if admin in (True, 'True', 'true', 1, '1') else False admin = True if admin in (True, 'True', 'true', 1, '1') else False
password = decode_password(password) password = decode_password(password)
status = UserManager.add(store, username, password, email, admin) status = UserManager.add(username, password, email, admin)
if status == UserManager.NAME_EXISTS: if status == UserManager.NAME_EXISTS:
return request.error_formatter(0, 'There is already a user with that username') return request.error_formatter(0, 'There is already a user with that username')
@ -74,11 +76,12 @@ def user_del():
if not username: if not username:
return request.error_formatter(10, 'Missing parameter') return request.error_formatter(10, 'Missing parameter')
user = store.find(User, User.name == username).one() with db_session:
if not user: user = User.get(name = username)
if user is None:
return request.error_formatter(70, 'Unknown user') return request.error_formatter(70, 'Unknown user')
status = UserManager.delete(store, user.id) status = UserManager.delete(user.id)
if status != UserManager.SUCCESS: if status != UserManager.SUCCESS:
return request.error_formatter(0, UserManager.error_str(status)) return request.error_formatter(0, UserManager.error_str(status))
@ -94,7 +97,7 @@ def user_changepass():
return request.error_formatter(50, 'Admin restricted') return request.error_formatter(50, 'Admin restricted')
password = decode_password(password) password = decode_password(password)
status = UserManager.change_password2(store, username, password) status = UserManager.change_password2(username, password)
if status != UserManager.SUCCESS: if status != UserManager.SUCCESS:
code = 0 code = 0
if status == UserManager.NO_SUCH_USER: if status == UserManager.NO_SUCH_USER:

View File

@ -130,8 +130,8 @@ class Album(db.Entity):
'created': min(self.tracks.created).isoformat() 'created': min(self.tracks.created).isoformat()
} }
track_with_cover = self.tracks.select(lambda t: t.folder.has_cover_art)[:1][0] track_with_cover = self.tracks.select(lambda t: t.folder.has_cover_art).first()
if track_with_cover: if track_with_cover is not None:
info['coverArt'] = str(track_with_cover.folder.id) info['coverArt'] = str(track_with_cover.folder.id)
try: try:

View File

@ -11,7 +11,10 @@
import unittest import unittest
from . import base, managers, api, frontend from . import base
from . import managers
from . import api
from . import frontend
def suite(): def suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()

View File

@ -12,6 +12,8 @@
import time import time
import unittest import unittest
from pony.orm import db_session, commit
from supysonic.db import Folder, Artist, Album, Track from supysonic.db import Folder, Artist, Album, Track
from .apitestbase import ApiTestBase from .apitestbase import ApiTestBase
@ -20,53 +22,44 @@ class SearchTestCase(ApiTestBase):
def setUp(self): def setUp(self):
super(SearchTestCase, self).setUp() super(SearchTestCase, self).setUp()
root = Folder() with db_session:
root.root = True root = Folder(root = True, name = 'Root folder', path = 'tests/assets')
root.name = 'Root folder'
root.path = 'tests/assets'
self.store.add(root)
for letter in 'ABC': for letter in 'ABC':
folder = Folder() folder = Folder(name = letter + 'rtist', path = 'tests/assets/{}rtist'.format(letter), parent = root)
folder.name = letter + 'rtist' artist = Artist(name = letter + 'rtist')
folder.path = 'tests/assets/{}rtist'.format(letter)
folder.parent = root
artist = Artist() for lether in 'AB':
artist.name = letter + 'rtist' afolder = Folder(
name = letter + lether + 'lbum',
path = 'tests/assets/{0}rtist/{0}{1}lbum'.format(letter, lether),
parent = folder
)
for lether in 'AB': album = Album(name = letter + lether + 'lbum', artist = artist)
afolder = Folder()
afolder.name = letter + lether + 'lbum'
afolder.path = 'tests/assets/{0}rtist/{0}{1}lbum'.format(letter, lether)
afolder.parent = folder
album = Album() for num, song in enumerate([ 'One', 'Two', 'Three' ]):
album.name = letter + lether + 'lbum' track = Track(
album.artist = artist disc = 1,
number = num,
title = song,
duration = 2,
album = album,
artist = artist,
bitrate = 320,
path = 'tests/assets/{0}rtist/{0}{1}lbum/{2}'.format(letter, lether, song),
content_type = 'audio/mpeg',
last_modification = 0,
root_folder = root,
folder = afolder
)
for num, song in enumerate([ 'One', 'Two', 'Three' ]): commit()
track = Track()
track.disc = 1
track.number = num
track.title = song
track.duration = 2
track.album = album
track.artist = artist
track.bitrate = 320
track.path = 'tests/assets/{0}rtist/{0}{1}lbum/{2}'.format(letter, lether, song)
track.content_type = 'audio/mpeg'
track.last_modification = 0
track.root_folder = root
track.folder = afolder
self.store.add(track)
self.store.commit() self.assertEqual(Folder.select().count(), 10)
self.assertEqual(Artist.select().count(), 3)
self.assertEqual(self.store.find(Folder).count(), 10) self.assertEqual(Album.select().count(), 6)
self.assertEqual(self.store.find(Artist).count(), 3) self.assertEqual(Track.select().count(), 18)
self.assertEqual(self.store.find(Album).count(), 6)
self.assertEqual(self.store.find(Track).count(), 18)
def __track_as_pseudo_unique_str(self, elem): def __track_as_pseudo_unique_str(self, elem):
return elem.get('artist') + elem.get('album') + elem.get('title') return elem.get('artist') + elem.get('album') + elem.get('title')

View File

@ -68,8 +68,8 @@ class TestBase(unittest.TestCase):
release_database() release_database()
app = create_application(config) app = create_application(config)
#self.__ctx = app.app_context() self.__ctx = app.app_context()
#self.__ctx.push() self.__ctx.push()
self.client = app.test_client() self.client = app.test_client()
@ -83,7 +83,7 @@ class TestBase(unittest.TestCase):
return False return False
def tearDown(self): def tearDown(self):
#self.__ctx.pop() self.__ctx.pop()
release_database() release_database()
shutil.rmtree(self.__dir) shutil.rmtree(self.__dir)
os.remove(self.__dbfile) os.remove(self.__dbfile)