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

Code style

This commit is contained in:
Alban Féron 2021-11-28 17:15:05 +01:00
parent c3f911b3f4
commit 799bfa3dde
No known key found for this signature in database
GPG Key ID: 8CE0313646D16165
22 changed files with 262 additions and 257 deletions

View File

@ -36,7 +36,7 @@ def api_routing(endpoint):
@api.before_request @api.before_request
def set_formatter(): def set_formatter():
"""Return a function to create the response.""" """Return a function to create the response."""
f, callback = map(request.values.get, ["f", "callback"]) f, callback = map(request.values.get, ("f", "callback"))
if f == "jsonp": if f == "jsonp":
request.formatter = JSONPFormatter(callback) request.formatter = JSONPFormatter(callback)
elif f == "json": elif f == "json":

View File

@ -29,7 +29,7 @@ from .exceptions import GenericError, NotFound
def rand_songs(): def rand_songs():
size = request.values.get("size", "10") size = request.values.get("size", "10")
genre, fromYear, toYear, musicFolderId = map( genre, fromYear, toYear, musicFolderId = map(
request.values.get, ["genre", "fromYear", "toYear", "musicFolderId"] request.values.get, ("genre", "fromYear", "toYear", "musicFolderId")
) )
size = int(size) if size else 10 size = int(size) if size else 10
@ -57,12 +57,12 @@ def rand_songs():
return request.formatter( return request.formatter(
"randomSongs", "randomSongs",
dict( {
song=[ "song": [
t.as_subsonic_child(request.user, request.client) t.as_subsonic_child(request.user, request.client)
for t in query.without_distinct().random(size) for t in query.without_distinct().random(size)
] ]
), },
) )
@ -70,7 +70,7 @@ def rand_songs():
def album_list(): def album_list():
ltype = request.values["type"] ltype = request.values["type"]
size, offset = map(request.values.get, ["size", "offset"]) size, offset = map(request.values.get, ("size", "offset"))
size = int(size) if size else 10 size = int(size) if size else 10
offset = int(offset) if offset else 0 offset = int(offset) if offset else 0
@ -78,12 +78,12 @@ def album_list():
if ltype == "random": if ltype == "random":
return request.formatter( return request.formatter(
"albumList", "albumList",
dict( {
album=[ "album": [
a.as_subsonic_child(request.user) a.as_subsonic_child(request.user)
for a in distinct(query.random(size)) for a in distinct(query.random(size))
] ]
), },
) )
elif ltype == "newest": elif ltype == "newest":
query = query.sort_by(desc(Folder.created)).distinct() query = query.sort_by(desc(Folder.created)).distinct()
@ -123,9 +123,11 @@ def album_list():
return request.formatter( return request.formatter(
"albumList", "albumList",
dict( {
album=[f.as_subsonic_child(request.user) for f in query.limit(size, offset)] "album": [
), f.as_subsonic_child(request.user) for f in query.limit(size, offset)
]
},
) )
@ -133,7 +135,7 @@ def album_list():
def album_list_id3(): def album_list_id3():
ltype = request.values["type"] ltype = request.values["type"]
size, offset = map(request.values.get, ["size", "offset"]) size, offset = map(request.values.get, ("size", "offset"))
size = int(size) if size else 10 size = int(size) if size else 10
offset = int(offset) if offset else 0 offset = int(offset) if offset else 0
@ -141,7 +143,7 @@ def album_list_id3():
if ltype == "random": if ltype == "random":
return request.formatter( return request.formatter(
"albumList2", "albumList2",
dict(album=[a.as_subsonic_album(request.user) for a in query.random(size)]), {"album": [a.as_subsonic_album(request.user) for a in query.random(size)]},
) )
elif ltype == "newest": elif ltype == "newest":
query = query.order_by(lambda a: desc(min(a.tracks.created))) query = query.order_by(lambda a: desc(min(a.tracks.created)))
@ -177,9 +179,11 @@ def album_list_id3():
return request.formatter( return request.formatter(
"albumList2", "albumList2",
dict( {
album=[f.as_subsonic_album(request.user) for f in query.limit(size, offset)] "album": [
), f.as_subsonic_album(request.user) for f in query.limit(size, offset)
]
},
) )
@ -187,14 +191,14 @@ def album_list_id3():
def songs_by_genre(): def songs_by_genre():
genre = request.values["genre"] genre = request.values["genre"]
count, offset = map(request.values.get, ["count", "offset"]) count, offset = map(request.values.get, ("count", "offset"))
count = int(count) if count else 10 count = int(count) if count else 10
offset = int(offset) if offset else 0 offset = int(offset) if offset else 0
query = select(t for t in Track if t.genre == genre).limit(count, offset) query = select(t for t in Track if t.genre == genre).limit(count, offset)
return request.formatter( return request.formatter(
"songsByGenre", "songsByGenre",
dict(song=[t.as_subsonic_child(request.user, request.client) for t in query]), {"song": [t.as_subsonic_child(request.user, request.client) for t in query]},
) )
@ -207,17 +211,17 @@ def now_playing():
return request.formatter( return request.formatter(
"nowPlaying", "nowPlaying",
dict( {
entry=[ "entry": [
dict( {
u.last_play.as_subsonic_child(request.user, request.client), **u.last_play.as_subsonic_child(request.user, request.client),
username=u.name, "username": u.name,
minutesAgo=(now() - u.last_play_date).seconds / 60, "minutesAgo": (now() - u.last_play_date).seconds / 60,
playerId=0, "playerId": 0,
) }
for u in query for u in query
] ]
), },
) )
@ -227,22 +231,22 @@ def get_starred():
return request.formatter( return request.formatter(
"starred", "starred",
dict( {
artist=[ "artist": [
sf.as_subsonic_artist(request.user) sf.as_subsonic_artist(request.user)
for sf in folders.filter(lambda f: count(f.tracks) == 0) for sf in folders.filter(lambda f: count(f.tracks) == 0)
], ],
album=[ "album": [
sf.as_subsonic_child(request.user) sf.as_subsonic_child(request.user)
for sf in folders.filter(lambda f: count(f.tracks) > 0) for sf in folders.filter(lambda f: count(f.tracks) > 0)
], ],
song=[ "song": [
st.as_subsonic_child(request.user, request.client) st.as_subsonic_child(request.user, request.client)
for st in select( for st in select(
s.starred for s in StarredTrack if s.user.id == request.user.id s.starred for s in StarredTrack if s.user.id == request.user.id
) )
], ],
), },
) )
@ -250,24 +254,24 @@ def get_starred():
def get_starred_id3(): def get_starred_id3():
return request.formatter( return request.formatter(
"starred2", "starred2",
dict( {
artist=[ "artist": [
sa.as_subsonic_artist(request.user) sa.as_subsonic_artist(request.user)
for sa in select( for sa in select(
s.starred for s in StarredArtist if s.user.id == request.user.id s.starred for s in StarredArtist if s.user.id == request.user.id
) )
], ],
album=[ "album": [
sa.as_subsonic_album(request.user) sa.as_subsonic_album(request.user)
for sa in select( for sa in select(
s.starred for s in StarredAlbum if s.user.id == request.user.id s.starred for s in StarredAlbum if s.user.id == request.user.id
) )
], ],
song=[ "song": [
st.as_subsonic_child(request.user, request.client) st.as_subsonic_child(request.user, request.client)
for st in select( for st in select(
s.starred for s in StarredTrack if s.user.id == request.user.id s.starred for s in StarredTrack if s.user.id == request.user.id
) )
], ],
), },
) )

View File

@ -54,7 +54,7 @@ def unstar_single(cls, starcls, eid):
def handle_star_request(func): def handle_star_request(func):
id, albumId, artistId = map(request.values.getlist, ["id", "albumId", "artistId"]) id, albumId, artistId = map(request.values.getlist, ("id", "albumId", "artistId"))
if not id and not albumId and not artistId: if not id and not albumId and not artistId:
raise MissingParameter("id, albumId or artistId") raise MissingParameter("id, albumId or artistId")
@ -174,7 +174,7 @@ def rate():
@api_routing("/scrobble") @api_routing("/scrobble")
def scrobble(): def scrobble():
res = get_entity(Track) res = get_entity(Track)
t, submission = map(request.values.get, ["time", "submission"]) t, submission = map(request.values.get, ("time", "submission"))
t = int(t) / 1000 if t else int(time.time()) t = int(t) / 1000 if t else int(time.time())
lfm = LastFm(current_app.config["LASTFM"], request.user) lfm = LastFm(current_app.config["LASTFM"], request.user)

View File

@ -20,12 +20,12 @@ from . import get_entity, get_entity_id, api_routing
def list_folders(): def list_folders():
return request.formatter( return request.formatter(
"musicFolders", "musicFolders",
dict( {
musicFolder=[ "musicFolder": [
dict(id=str(f.id), name=f.name) {"id": str(f.id), "name": f.name}
for f in Folder.select(lambda f: f.root).order_by(Folder.name) for f in Folder.select(lambda f: f.root).order_by(Folder.name)
] ]
), },
) )
@ -66,13 +66,14 @@ def list_indexes():
folders = [folder] folders = [folder]
last_modif = max(map(lambda f: f.last_scan, folders)) last_modif = max(f.last_scan for f in folders)
if ifModifiedSince is not None and last_modif < ifModifiedSince: if ifModifiedSince is not None and last_modif < ifModifiedSince:
return request.formatter( return request.formatter(
"indexes", "indexes",
dict( {
lastModified=last_modif * 1000, ignoredArticles=ignored_articles_str() "lastModified": last_modif * 1000,
), "ignoredArticles": ignored_articles_str(),
},
) )
# The XSD lies, we don't return artists but a directory structure # The XSD lies, we don't return artists but a directory structure
@ -82,7 +83,7 @@ def list_indexes():
artists += f.children.select()[:] artists += f.children.select()[:]
children += f.tracks.select()[:] children += f.tracks.select()[:]
indexes = dict() indexes = {}
pattern = build_ignored_articles_pattern() pattern = build_ignored_articles_pattern()
for artist in artists: for artist in artists:
name = artist.name name = artist.name
@ -101,24 +102,24 @@ def list_indexes():
return request.formatter( return request.formatter(
"indexes", "indexes",
dict( {
lastModified=last_modif * 1000, "lastModified": last_modif * 1000,
ignoredArticles=ignored_articles_str(), "ignoredArticles": ignored_articles_str(),
index=[ "index": [
dict( {
name=k, "name": k,
artist=[ "artist": [
a.as_subsonic_artist(request.user) a.as_subsonic_artist(request.user)
for a, _ in sorted(v, key=lambda t: t[1].lower()) for a, _ in sorted(v, key=lambda t: t[1].lower())
], ],
) }
for k, v in sorted(indexes.items()) for k, v in sorted(indexes.items())
], ],
child=[ "child": [
c.as_subsonic_child(request.user, request.client) c.as_subsonic_child(request.user, request.client)
for c in sorted(children, key=lambda t: t.sort_key()) for c in sorted(children, key=lambda t: t.sort_key())
], ],
), },
) )
@ -134,21 +135,21 @@ def show_directory():
def list_genres(): def list_genres():
return request.formatter( return request.formatter(
"genres", "genres",
dict( {
genre=[ "genre": [
dict(value=genre, songCount=sc, albumCount=ac) {"value": genre, "songCount": sc, "albumCount": ac}
for genre, sc, ac in select( for genre, sc, ac in select(
(t.genre, count(), count(t.album)) for t in Track if t.genre (t.genre, count(), count(t.album)) for t in Track if t.genre
) )
] ]
), },
) )
@api_routing("/getArtists") @api_routing("/getArtists")
def list_artists(): def list_artists():
# According to the API page, there are no parameters? # According to the API page, there are no parameters?
indexes = dict() indexes = {}
pattern = build_ignored_articles_pattern() pattern = build_ignored_articles_pattern()
for artist in Artist.select(): for artist in Artist.select():
name = artist.name or "?" name = artist.name or "?"
@ -167,19 +168,19 @@ def list_artists():
return request.formatter( return request.formatter(
"artists", "artists",
dict( {
ignoredArticles=ignored_articles_str(), "ignoredArticles": ignored_articles_str(),
index=[ "index": [
dict( {
name=k, "name": k,
artist=[ "artist": [
a.as_subsonic_artist(request.user) a.as_subsonic_artist(request.user)
for a, _ in sorted(v, key=lambda t: t[1].lower()) for a, _ in sorted(v, key=lambda t: t[1].lower())
], ],
) }
for k, v in sorted(indexes.items()) for k, v in sorted(indexes.items())
], ],
), },
) )

View File

@ -21,7 +21,7 @@ def get_chat():
query = query.filter(lambda m: m.time > since) query = query.filter(lambda m: m.time > since)
return request.formatter( return request.formatter(
"chatMessages", dict(chatMessage=[msg.responsize() for msg in query]) "chatMessages", {"chatMessage": [msg.responsize() for msg in query]}
) )

View File

@ -114,11 +114,12 @@ class AggregateException(SubsonicAPIException):
codes = {exc.api_code for exc in self.exceptions} codes = {exc.api_code for exc in self.exceptions}
errors = [ errors = [
dict(code=exc.api_code, message=exc.message) for exc in self.exceptions {"code": exc.api_code, "message": exc.message} for exc in self.exceptions
] ]
rv = request.formatter( rv = request.formatter(
"error", dict(code=list(codes)[0] if len(codes) == 1 else 0, error=errors) "error",
{"code": next(iter(codes)) if len(codes) == 1 else 0, "error": errors},
) )
# rv.status_code = self.code # rv.status_code = self.code
return rv return rv

View File

@ -16,7 +16,7 @@ class BaseFormatter:
raise NotImplementedError() raise NotImplementedError()
def make_error(self, code, message): def make_error(self, code, message):
return self.make_response("error", dict(code=code, message=message)) return self.make_response("error", {"code": code, "message": message})
def make_empty(self): def make_empty(self):
return self.make_response(None, None) return self.make_response(None, None)
@ -78,7 +78,7 @@ class JSONPFormatter(JSONBaseFormatter):
def make_response(self, elem, data): def make_response(self, elem, data):
if not self.__callback: if not self.__callback:
return jsonify( return jsonify(
self._subsonicify("error", dict(code=10, message="Missing callback")) self._subsonicify("error", {"code": 10, "message": "Missing callback"})
) )
rv = self._subsonicify(elem, data) rv = self._subsonicify(elem, data)
@ -100,7 +100,7 @@ class XMLFormatter(BaseFormatter):
""" """
if not isinstance(dictionary, dict): if not isinstance(dictionary, dict):
raise TypeError("Expecting a dict") raise TypeError("Expecting a dict")
if not all(map(lambda x: isinstance(x, str), dictionary)): if not all(isinstance(x, str) for x in dictionary):
raise TypeError("Dictionary keys must be strings") raise TypeError("Dictionary keys must be strings")
for name, value in dictionary.items(): for name, value in dictionary.items():

View File

@ -79,12 +79,12 @@ def jukebox_control():
except DaemonUnavailableError: except DaemonUnavailableError:
raise GenericError("Jukebox unavaliable") raise GenericError("Jukebox unavaliable")
rv = dict( rv = {
currentIndex=status.index, "currentIndex": status.index,
playing=status.playing, "playing": status.playing,
gain=status.gain, "gain": status.gain,
position=status.position, "position": status.position,
) }
if action == "get": if action == "get":
playlist = [] playlist = []
for path in status.playlist: for path in status.playlist:

View File

@ -73,7 +73,7 @@ def stream_media():
raise UnsupportedParameter("size") raise UnsupportedParameter("size")
maxBitRate, request_format, estimateContentLength = map( maxBitRate, request_format, estimateContentLength = map(
request.values.get, ["maxBitRate", "format", "estimateContentLength"] request.values.get, ("maxBitRate", "format", "estimateContentLength")
) )
if request_format: if request_format:
request_format = request_format.lower() request_format = request_format.lower()
@ -378,7 +378,7 @@ def cover_art():
def lyrics_response_for_track(track, lyrics): def lyrics_response_for_track(track, lyrics):
return request.formatter( return request.formatter(
"lyrics", "lyrics",
dict(artist=track.album.artist.name, title=track.title, value=lyrics), {"artist": track.album.artist.name, "title": track.title, "value": lyrics},
) )
@ -419,7 +419,7 @@ def lyrics():
).hexdigest() ).hexdigest()
cache_key = "lyrics-{}".format(unique) cache_key = "lyrics-{}".format(unique)
lyrics = dict() lyrics = {}
try: try:
lyrics = json.loads( lyrics = json.loads(
zlib.decompress(current_app.cache.get_value(cache_key)).decode("utf-8") zlib.decompress(current_app.cache.get_value(cache_key)).decode("utf-8")
@ -434,11 +434,11 @@ def lyrics():
root = ElementTree.fromstring(r.content) root = ElementTree.fromstring(r.content)
ns = {"cl": "http://api.chartlyrics.com/"} ns = {"cl": "http://api.chartlyrics.com/"}
lyrics = dict( lyrics = {
artist=root.find("cl:LyricArtist", namespaces=ns).text, "artist": root.find("cl:LyricArtist", namespaces=ns).text,
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,
) }
current_app.cache.set( current_app.cache.set(
cache_key, zlib.compress(json.dumps(lyrics).encode("utf-8"), 9) cache_key, zlib.compress(json.dumps(lyrics).encode("utf-8"), 9)

View File

@ -36,7 +36,7 @@ def list_playlists():
return request.formatter( return request.formatter(
"playlists", "playlists",
dict(playlist=[p.as_subsonic_playlist(request.user) for p in query]), {"playlist": [p.as_subsonic_playlist(request.user) for p in query]},
) )
@ -55,7 +55,7 @@ def show_playlist():
@api_routing("/createPlaylist") @api_routing("/createPlaylist")
def create_playlist(): def create_playlist():
playlist_id, name = map(request.values.get, ["playlistId", "name"]) playlist_id, name = map(request.values.get, ("playlistId", "name"))
# songId actually doesn't seem to be required # songId actually doesn't seem to be required
songs = request.values.getlist("songId") songs = request.values.getlist("songId")
playlist_id = uuid.UUID(playlist_id) if playlist_id else None playlist_id = uuid.UUID(playlist_id) if playlist_id else None
@ -99,9 +99,9 @@ def update_playlist():
raise Forbidden() raise Forbidden()
playlist = res playlist = res
name, comment, public = map(request.values.get, ["name", "comment", "public"]) name, comment, public = map(request.values.get, ("name", "comment", "public"))
to_add, to_remove = map( to_add, to_remove = map(
request.values.getlist, ["songIdToAdd", "songIndexToRemove"] request.values.getlist, ("songIdToAdd", "songIndexToRemove")
) )
if name: if name:

View File

@ -18,7 +18,7 @@ def get_radio_stations():
query = RadioStation.select().sort_by(RadioStation.name) query = RadioStation.select().sort_by(RadioStation.name)
return request.formatter( return request.formatter(
"internetRadioStations", "internetRadioStations",
dict(internetRadioStation=[p.as_subsonic_station() for p in query]), {"internetRadioStation": [p.as_subsonic_station() for p in query]},
) )
@ -28,7 +28,7 @@ def create_radio_station():
raise Forbidden() raise Forbidden()
stream_url, name, homepage_url = map( stream_url, name, homepage_url = map(
request.values.get, ["streamUrl", "name", "homepageUrl"] request.values.get, ("streamUrl", "name", "homepageUrl")
) )
if stream_url and name: if stream_url and name:
@ -47,7 +47,7 @@ def update_radio_station():
res = get_entity(RadioStation) res = get_entity(RadioStation)
stream_url, name, homepage_url = map( stream_url, name, homepage_url = map(
request.values.get, ["streamUrl", "name", "homepageUrl"] request.values.get, ("streamUrl", "name", "homepageUrl")
) )
if stream_url and name: if stream_url and name:
res.stream_url = stream_url res.stream_url = stream_url

View File

@ -28,10 +28,10 @@ def startScan():
raise ServerError(str(e)) raise ServerError(str(e))
return request.formatter( return request.formatter(
"scanStatus", "scanStatus",
dict( {
scanning="true" if scanned is not None else "false", "scanning": scanned is not None,
count=scanned if scanned is not None else 0, "count": scanned or 0,
), },
) )
@ -46,8 +46,8 @@ def getScanStatus():
raise ServerError(str(e)) raise ServerError(str(e))
return request.formatter( return request.formatter(
"scanStatus", "scanStatus",
dict( {
scanning="true" if scanned is not None else "false", "scanning": scanned is not None,
count=scanned if scanned is not None else 0, "count": scanned or 0,
), },
) )

View File

@ -20,7 +20,7 @@ from .exceptions import MissingParameter
def old_search(): def old_search():
artist, album, title, anyf, count, offset, newer_than = map( artist, album, title, anyf, count, offset, newer_than = map(
request.values.get, request.values.get,
["artist", "album", "title", "any", "count", "offset", "newerThan"], ("artist", "album", "title", "any", "count", "offset", "newerThan"),
) )
count = int(count) if count else 20 count = int(count) if count else 20
@ -54,32 +54,32 @@ def old_search():
return request.formatter( return request.formatter(
"searchResult", "searchResult",
dict( {
totalHits=folders.count() + tracks.count(), "totalHits": folders.count() + tracks.count(),
offset=offset, "offset": offset,
match=[ "match": [
r.as_subsonic_child(request.user) r.as_subsonic_child(request.user)
if isinstance(r, Folder) if isinstance(r, Folder)
else r.as_subsonic_child(request.user, request.client) else r.as_subsonic_child(request.user, request.client)
for r in res for r in res
], ],
), },
) )
else: else:
raise MissingParameter("search") raise MissingParameter("search")
return request.formatter( return request.formatter(
"searchResult", "searchResult",
dict( {
totalHits=query.count(), "totalHits": query.count(),
offset=offset, "offset": offset,
match=[ "match": [
r.as_subsonic_child(request.user) r.as_subsonic_child(request.user)
if isinstance(r, Folder) if isinstance(r, Folder)
else r.as_subsonic_child(request.user, request.client) else r.as_subsonic_child(request.user, request.client)
for r in query[offset : offset + count] for r in query[offset : offset + count]
], ],
), },
) )
@ -95,14 +95,14 @@ def new_search():
song_offset, song_offset,
) = map( ) = map(
request.values.get, request.values.get,
[ (
"artistCount", "artistCount",
"artistOffset", "artistOffset",
"albumCount", "albumCount",
"albumOffset", "albumOffset",
"songCount", "songCount",
"songOffset", "songOffset",
], ),
) )
artist_count = int(artist_count) if artist_count else 20 artist_count = int(artist_count) if artist_count else 20
@ -147,14 +147,14 @@ def search_id3():
song_offset, song_offset,
) = map( ) = map(
request.values.get, request.values.get,
[ (
"artistCount", "artistCount",
"artistOffset", "artistOffset",
"albumCount", "albumCount",
"albumOffset", "albumOffset",
"songCount", "songCount",
"songOffset", "songOffset",
], ),
) )
artist_count = int(artist_count) if artist_count else 20 artist_count = int(artist_count) if artist_count else 20

View File

@ -18,4 +18,4 @@ def ping():
@api_routing("/getLicense") @api_routing("/getLicense")
def license(): def license():
return request.formatter("license", dict(valid=True)) return request.formatter("license", {"valid": True})

View File

@ -43,7 +43,7 @@ def user_info():
@admin_only @admin_only
def users_info(): def users_info():
return request.formatter( return request.formatter(
"users", dict(user=[u.as_subsonic_user() for u in User.select()]) "users", {"user": [u.as_subsonic_user() for u in User.select()]}
) )
@ -107,7 +107,7 @@ def user_edit():
UserManager.change_password2(user, password) UserManager.change_password2(user, password)
email, admin, jukebox = map( email, admin, jukebox = map(
request.values.get, ["email", "adminRole", "jukeboxRole"] request.values.get, ("email", "adminRole", "jukeboxRole")
) )
if email is not None: if email is not None:
user.mail = email user.mail = email

View File

@ -92,13 +92,13 @@ class Folder(PathMixin, db.Entity):
ratings = Set(lambda: RatingFolder) ratings = Set(lambda: RatingFolder)
def as_subsonic_child(self, user): def as_subsonic_child(self, user):
info = dict( info = {
id=str(self.id), "id": str(self.id),
isDir=True, "isDir": True,
title=self.name, "title": self.name,
album=self.name, "album": self.name,
created=self.created.isoformat(), "created": self.created.isoformat(),
) }
if not self.root: if not self.root:
info["parent"] = str(self.parent.id) info["parent"] = str(self.parent.id)
info["artist"] = self.parent.name info["artist"] = self.parent.name
@ -129,7 +129,7 @@ class Folder(PathMixin, db.Entity):
return info return info
def as_subsonic_artist(self, user): # "Artist" type in XSD def as_subsonic_artist(self, user): # "Artist" type in XSD
info = dict(id=str(self.id), name=self.name) info = {"id": str(self.id), "name": self.name}
try: try:
starred = StarredFolder[user.id, self.id] starred = StarredFolder[user.id, self.id]
@ -140,10 +140,10 @@ class Folder(PathMixin, db.Entity):
return info return info
def as_subsonic_directory(self, user, client): # "Directory" type in XSD def as_subsonic_directory(self, user, client): # "Directory" type in XSD
info = dict( info = {
id=str(self.id), "id": str(self.id),
name=self.name, "name": self.name,
child=[ "child": [
f.as_subsonic_child(user) f.as_subsonic_child(user)
for f in self.children.order_by(lambda c: c.name.lower()) for f in self.children.order_by(lambda c: c.name.lower())
] ]
@ -151,7 +151,7 @@ class Folder(PathMixin, db.Entity):
t.as_subsonic_child(user, client) t.as_subsonic_child(user, client)
for t in sorted(self.tracks, key=lambda t: t.sort_key()) for t in sorted(self.tracks, key=lambda t: t.sort_key())
], ],
) }
if not self.root: if not self.root:
info["parent"] = str(self.parent.id) info["parent"] = str(self.parent.id)
@ -183,12 +183,12 @@ class Artist(db.Entity):
stars = Set(lambda: StarredArtist) stars = Set(lambda: StarredArtist)
def as_subsonic_artist(self, user): def as_subsonic_artist(self, user):
info = dict( info = {
id=str(self.id), "id": str(self.id),
name=self.name, "name": self.name,
# coverArt # coverArt
albumCount=self.albums.count(), "albumCount": self.albums.count(),
) }
try: try:
starred = StarredArtist[user.id, self.id] starred = StarredArtist[user.id, self.id]
@ -217,15 +217,15 @@ class Album(db.Entity):
stars = Set(lambda: StarredAlbum) stars = Set(lambda: StarredAlbum)
def as_subsonic_album(self, user): # "AlbumID3" type in XSD def as_subsonic_album(self, user): # "AlbumID3" type in XSD
info = dict( info = {
id=str(self.id), "id": str(self.id),
name=self.name, "name": self.name,
artist=self.artist.name, "artist": self.artist.name,
artistId=str(self.artist.id), "artistId": str(self.artist.id),
songCount=self.tracks.count(), "songCount": self.tracks.count(),
duration=sum(self.tracks.duration), "duration": sum(self.tracks.duration),
created=min(self.tracks.created).isoformat(), "created": min(self.tracks.created).isoformat(),
) }
track_with_cover = self.tracks.select( track_with_cover = self.tracks.select(
lambda t: t.folder.cover_art is not None lambda t: t.folder.cover_art is not None
@ -253,8 +253,8 @@ class Album(db.Entity):
return info return info
def sort_key(self): def sort_key(self):
year = min(map(lambda t: t.year if t.year else 9999, self.tracks)) year = min(t.year if t.year else 9999 for t in self.tracks)
return "%i%s" % (year, self.name.lower()) return f"{year}{self.name.lower()}"
@classmethod @classmethod
def prune(cls): def prune(cls):
@ -297,27 +297,27 @@ class Track(PathMixin, db.Entity):
ratings = Set(lambda: RatingTrack) ratings = Set(lambda: RatingTrack)
def as_subsonic_child(self, user, prefs): def as_subsonic_child(self, user, prefs):
info = dict( info = {
id=str(self.id), "id": str(self.id),
parent=str(self.folder.id), "parent": str(self.folder.id),
isDir=False, "isDir": False,
title=self.title, "title": self.title,
album=self.album.name, "album": self.album.name,
artist=self.artist.name, "artist": self.artist.name,
track=self.number, "track": self.number,
size=os.path.getsize(self.path) if os.path.isfile(self.path) else -1, "size": os.path.getsize(self.path) if os.path.isfile(self.path) else -1,
contentType=self.mimetype, "contentType": self.mimetype,
suffix=self.suffix(), "suffix": self.suffix(),
duration=self.duration, "duration": self.duration,
bitRate=self.bitrate, "bitRate": self.bitrate,
path=self.path[len(self.root_folder.path) + 1 :], "path": self.path[len(self.root_folder.path) + 1 :],
isVideo=False, "isVideo": False,
discNumber=self.disc, "discNumber": self.disc,
created=self.created.isoformat(), "created": self.created.isoformat(),
albumId=str(self.album.id), "albumId": str(self.album.id),
artistId=str(self.artist.id), "artistId": str(self.artist.id),
type="music", "type": "music",
) }
if self.year: if self.year:
info["year"] = self.year info["year"] = self.year
@ -362,22 +362,16 @@ class Track(PathMixin, db.Entity):
return mimetypes.guess_type(self.path, False)[0] or "application/octet-stream" return mimetypes.guess_type(self.path, False)[0] or "application/octet-stream"
def duration_str(self): def duration_str(self):
ret = "%02i:%02i" % ((self.duration % 3600) / 60, self.duration % 60) ret = "{:02}:{:02}".format((self.duration % 3600) / 60, self.duration % 60)
if self.duration >= 3600: if self.duration >= 3600:
ret = "%02i:%s" % (self.duration / 3600, ret) ret = "{:02}:{}".format(self.duration / 3600, ret)
return ret return ret
def suffix(self): def suffix(self):
return os.path.splitext(self.path)[1][1:].lower() return os.path.splitext(self.path)[1][1:].lower()
def sort_key(self): def sort_key(self):
return ( return f"{self.album.artist.name}{self.album.name}{self.disc:02}{self.number:02}{self.title}".lower()
self.album.artist.name
+ self.album.name
+ ("%02i" % self.disc)
+ ("%02i" % self.number)
+ self.title
).lower()
class User(db.Entity): class User(db.Entity):
@ -412,22 +406,22 @@ class User(db.Entity):
track_ratings = Set(lambda: RatingTrack, lazy=True) track_ratings = Set(lambda: RatingTrack, lazy=True)
def as_subsonic_user(self): def as_subsonic_user(self):
return dict( return {
username=self.name, "username": self.name,
email=self.mail, "email": self.mail,
scrobblingEnabled=self.lastfm_session is not None and self.lastfm_status, "scrobblingEnabled": self.lastfm_session is not None and self.lastfm_status,
adminRole=self.admin, "adminRole": self.admin,
settingsRole=True, "settingsRole": True,
downloadRole=True, "downloadRole": True,
uploadRole=False, "uploadRole": False,
playlistRole=True, "playlistRole": True,
coverArtRole=False, "coverArtRole": False,
commentRole=False, "commentRole": False,
podcastRole=False, "podcastRole": False,
streamRole=True, "streamRole": True,
jukeboxRole=self.admin or self.jukebox, "jukeboxRole": self.admin or self.jukebox,
shareRole=False, "shareRole": False,
) }
class ClientPrefs(db.Entity): class ClientPrefs(db.Entity):
@ -507,9 +501,11 @@ class ChatMessage(db.Entity):
message = Required(str, 512) message = Required(str, 512)
def responsize(self): def responsize(self):
return dict( return {
username=self.user.name, time=self.time * 1000, message=self.message "username": self.user.name,
) "time": self.time * 1000,
"message": self.message,
}
class Playlist(db.Entity): class Playlist(db.Entity):
@ -525,17 +521,17 @@ class Playlist(db.Entity):
def as_subsonic_playlist(self, user): def as_subsonic_playlist(self, user):
tracks = self.get_tracks() tracks = self.get_tracks()
info = dict( info = {
id=str(self.id), "id": str(self.id),
name=self.name "name": self.name
if self.user.id == user.id if self.user.id == user.id
else "[{}] {}".format(self.user.name, self.name), else "[{}] {}".format(self.user.name, self.name),
owner=self.user.name, "owner": self.user.name,
public=self.public, "public": self.public,
songCount=len(tracks), "songCount": len(tracks),
duration=sum(map(lambda t: t.duration, tracks)), "duration": sum(t.duration for t in tracks),
created=self.created.isoformat(), "created": self.created.isoformat(),
) }
if self.comment: if self.comment:
info["comment"] = self.comment info["comment"] = self.comment
return info return info
@ -556,7 +552,7 @@ class Playlist(db.Entity):
should_fix = True should_fix = True
if should_fix: if should_fix:
self.tracks = ",".join(map(lambda t: str(t.id), tracks)) self.tracks = ",".join(str(t.id) for t in tracks)
db.commit() db.commit()
return tracks return tracks
@ -597,12 +593,12 @@ class RadioStation(db.Entity):
created = Required(datetime, precision=0, default=now) created = Required(datetime, precision=0, default=now)
def as_subsonic_station(self): def as_subsonic_station(self):
info = dict( info = {
id=str(self.id), "id": str(self.id),
streamUrl=self.stream_url, "streamUrl": self.stream_url,
name=self.name, "name": self.name,
homePageUrl=self.homepage_url, "homePageUrl": self.homepage_url,
) }
return info return info
@ -622,28 +618,28 @@ def parse_uri(database_uri):
elif path[0] == "/": elif path[0] == "/":
path = path[1:] path = path[1:]
return dict(provider="sqlite", filename=path, create_db=True, **args) return {"provider": "sqlite", "filename": path, "create_db": True, **args}
elif uri.scheme in ("postgres", "postgresql"): elif uri.scheme in ("postgres", "postgresql"):
return dict( return {
provider="postgres", "provider": "postgres",
user=uri.username, "user": uri.username,
password=uri.password, "password": uri.password,
host=uri.hostname, "host": uri.hostname,
dbname=uri.path[1:], "dbname": uri.path[1:],
**args **args,
) }
elif uri.scheme == "mysql": elif uri.scheme == "mysql":
args.setdefault("charset", "utf8mb4") args.setdefault("charset", "utf8mb4")
args.setdefault("binary_prefix", True) args.setdefault("binary_prefix", True)
return dict( return {
provider="mysql", "provider": "mysql",
user=uri.username, "user": uri.username,
passwd=uri.password, "passwd": uri.password,
host=uri.hostname, "host": uri.hostname,
db=uri.path[1:], "db": uri.path[1:],
**args **args,
) }
return dict() return {}
def execute_sql_resource_script(respath): def execute_sql_resource_script(respath):

View File

@ -43,7 +43,7 @@ def add_folder_form():
@admin_only @admin_only
def add_folder_post(): def add_folder_post():
error = False error = False
(name, path) = map(request.form.get, ["name", "path"]) name, path = map(request.form.get, ("name", "path"))
if name in (None, ""): if name in (None, ""):
flash("The name is required.") flash("The name is required.")
error = True error = True
@ -59,7 +59,7 @@ def add_folder_post():
flash(str(e), "error") flash(str(e), "error")
return render_template("addfolder.html") return render_template("addfolder.html")
flash("Folder '%s' created. You should now run a scan" % name) flash(f"Folder '{name}' created. You should now run a scan")
return redirect(url_for("frontend.folder_index")) return redirect(url_for("frontend.folder_index"))

View File

@ -73,7 +73,7 @@ def user_profile(uid, user):
@frontend.route("/user/<uid>", methods=["POST"]) @frontend.route("/user/<uid>", methods=["POST"])
@me_or_uuid @me_or_uuid
def update_clients(uid, user): def update_clients(uid, user):
clients_opts = dict() clients_opts = {}
for key, value in request.form.items(): for key, value in request.form.items():
if "_" not in key: if "_" not in key:
continue continue
@ -85,7 +85,7 @@ def update_clients(uid, user):
continue continue
if client not in clients_opts: if client not in clients_opts:
clients_opts[client] = dict([(opt, value)]) clients_opts[client] = {opt: value}
else: else:
clients_opts[client][opt] = value clients_opts[client][opt] = value
logger.debug(clients_opts) logger.debug(clients_opts)
@ -157,9 +157,9 @@ def change_username_post(uid):
if user.name != username or user.admin != admin: if user.name != username or user.admin != admin:
user.name = username user.name = username
user.admin = admin user.admin = admin
flash("User '%s' updated." % username) flash(f"User '{username}' updated.")
else: else:
flash("No changes for '%s'." % username) flash(f"No changes for '{username}'.")
return redirect(url_for("frontend.user_profile", uid=uid)) return redirect(url_for("frontend.user_profile", uid=uid))
@ -195,7 +195,7 @@ def change_password_post(uid, user):
flash("The current password is required") flash("The current password is required")
error = True error = True
new, confirm = map(request.form.get, ["new", "confirm"]) new, confirm = map(request.form.get, ("new", "confirm"))
if not new: if not new:
flash("The new password is required") flash("The new password is required")
@ -231,7 +231,7 @@ def add_user_post():
error = False error = False
args = request.form.copy() args = request.form.copy()
(name, passwd, passwd_confirm) = map( (name, passwd, passwd_confirm) = map(
args.pop, ["user", "passwd", "passwd_confirm"], [None] * 3 args.pop, ("user", "passwd", "passwd_confirm"), (None,) * 3
) )
if not name: if not name:
flash("The name is required.") flash("The name is required.")
@ -246,7 +246,7 @@ def add_user_post():
if not error: if not error:
try: try:
UserManager.add(name, passwd, **args) UserManager.add(name, passwd, **args)
flash("User '%s' successfully added" % name) flash(f"User '{name}' successfully added")
return redirect(url_for("frontend.user_index")) return redirect(url_for("frontend.user_index"))
except ValueError as e: except ValueError as e:
flash(str(e), "error") flash(str(e), "error")
@ -302,7 +302,7 @@ def login():
if request.method == "GET": if request.method == "GET":
return render_template("login.html") return render_template("login.html")
name, password = map(request.form.get, ["user", "password"]) name, password = map(request.form.get, ("user", "password"))
error = False error = False
if not name: if not name:
flash("Missing user name") flash("Missing user name")

View File

@ -30,7 +30,7 @@ class LastFm:
if not res: if not res:
return False, "Error connecting to LastFM" return False, "Error connecting to LastFM"
elif "error" in res: elif "error" in res:
return False, "Error %i: %s" % (res["error"], res["message"]) return False, f"Error {res['error']}: {res['message']}"
else: else:
self.__user.lastfm_session = res["session"]["key"] self.__user.lastfm_session = res["session"]["key"]
self.__user.lastfm_status = True self.__user.lastfm_status = True
@ -107,6 +107,6 @@ class LastFm:
if "error" in json: if "error" in json:
if json["error"] in (9, "9"): if json["error"] in (9, "9"):
self.__user.lastfm_status = False self.__user.lastfm_status = False
logger.warning("LastFM error %i: %s" % (json["error"], json["message"])) logger.warning("LastFM error %i: %s", json["error"], json["message"])
return json return json

View File

@ -400,9 +400,12 @@ class Scanner(Thread):
created = datetime.fromtimestamp(os.path.getmtime(path)) created = datetime.fromtimestamp(os.path.getmtime(path))
children.append( children.append(
dict( {
root=False, name=os.path.basename(path), path=path, created=created "root": False,
) "name": os.path.basename(path),
"path": path,
"created": created,
}
) )
path = os.path.dirname(path) path = os.path.dirname(path)

View File

@ -31,9 +31,9 @@ class SupysonicWatcherEventHandler(PatternMatchingEventHandler):
def __init__(self, extensions): def __init__(self, extensions):
patterns = None patterns = None
if extensions: if extensions:
patterns = list(map(lambda e: "*." + e.lower(), extensions.split())) + list( patterns = ["*." + e.lower() for e in extensions.split()] + [
map(lambda e: "*" + e, covers.EXTENSIONS) "*" + e for e in covers.EXTENSIONS
) ]
super().__init__(patterns=patterns, ignore_directories=True) super().__init__(patterns=patterns, ignore_directories=True)
def dispatch(self, event): def dispatch(self, event):
@ -132,7 +132,7 @@ class ScannerProcessingQueue(Thread):
self.__timeout = delay self.__timeout = delay
self.__cond = Condition() self.__cond = Condition()
self.__timer = None self.__timer = None
self.__queue = dict() self.__queue = {}
self.__running = True self.__running = True
def run(self): def run(self):

View File

@ -172,7 +172,7 @@ class PlaylistTestCase(ApiTestBase):
"createPlaylist", "createPlaylist",
{ {
"name": "songs", "name": "songs",
"songId": list(map(lambda s: songs[s], ["Three", "One", "Two"])), "songId": [songs[s] for s in ("Three", "One", "Two")],
}, },
skip_post=True, skip_post=True,
) )