def try_star(ent, starred_ent, eid): """ Stars an entity :param ent: entity class, Folder, Artist, Album or Track :param starred_ent: class used for the db representation of the starring of ent :param eid: id of the entity to star :return error dict, if any. None otherwise """ try: uid = uuid.UUID(eid) except: return { 'code': 0, 'message': 'Invalid {} id {}'.format(ent.__name__, eid) } if store.get(starred_ent, (request.user.id, uid)): return { 'code': 0, 'message': '{} {} already starred'.format(ent.__name__, eid) } e = store.get(ent, uid) if not e: return { 'code': 70, 'message': 'Unknown {} id {}'.format(ent.__name__, eid) } starred = starred_ent() starred.user_id = request.user.id starred.starred_id = uid store.add(starred) return None
def update_clients(uid, user): clients_opts = {} for key, value in request.form.iteritems(): if '_' not in key: continue parts = key.split('_') if len(parts) != 2: continue client, opt = parts if not client or not opt: continue if client not in clients_opts: clients_opts[client] = { opt: value } else: clients_opts[client][opt] = value app.logger.debug(clients_opts) for client, opts in clients_opts.iteritems(): prefs = store.get(ClientPrefs, (user.id, client)) if not prefs: continue if 'delete' in opts and opts['delete'] in [ 'on', 'true', 'checked', 'selected', '1' ]: store.remove(prefs) continue prefs.format = opts['format'] if 'format' in opts and opts['format'] else None prefs.bitrate = int(opts['bitrate']) if 'bitrate' in opts and opts['bitrate'] else None store.commit() flash('Clients preferences updated.') return user_profile(uid, user)
def update_playlist(): status, res = get_entity(request, Playlist, 'playlistId') if not status: return res if res.user_id != request.user.id and not request.user.admin: return request.error_formatter(50, "You're not allowed to delete a playlist that isn't yours") playlist = res name, comment, public = map(request.values.get, [ 'name', 'comment', 'public' ]) to_add, to_remove = map(request.values.getlist, [ 'songIdToAdd', 'songIndexToRemove' ]) try: to_add = map(uuid.UUID, to_add) to_remove = map(int, to_remove) except: return request.error_formatter(0, 'Invalid parameter') if name: playlist.name = name if comment: playlist.comment = comment if public: playlist.public = public in (True, 'True', 'true', 1, '1') for sid in to_add: track = store.get(Track, sid) if not track: return request.error_formatter(70, 'Unknown song') playlist.add(track) playlist.remove_at_indexes(to_remove) store.commit() return request.formatter({})
def update_clients(uid): clients_opts = {} for client in set(map(lambda k: k.rsplit('_', 1)[0], request.form.keys())): clients_opts[client] = { k.rsplit('_', 1)[1]: v for k, v in filter(lambda (k, v): k.startswith(client), request.form.iteritems()) } app.logger.debug(clients_opts) if uid == 'me': userid = uuid.UUID(session.get('userid')) else: if not UserManager.get(store, session.get('userid'))[1].admin or not UserManager.get(store, uid)[0] is UserManager.SUCCESS: return redirect(url_for('index')) userid = uuid.UUID(uid) for client, opts in clients_opts.iteritems(): prefs = store.get(ClientPrefs, (userid, client)) if 'delete' in opts and opts['delete'] in [ 'on', 'true', 'checked', 'selected', '1' ]: store.remove(prefs) continue prefs.format = opts['format'] if 'format' in opts and opts['format'] else None prefs.bitrate = int(opts['bitrate']) if 'bitrate' in opts and opts['bitrate'] else None store.commit() flash('Clients preferences updated.') return user_profile(uid)
def try_star(ent, starred_ent, eid): try: uid = uuid.UUID(eid) except: return 2, request.error_formatter(0, 'Invalid %s id' % ent.__name__) if store.get(starred_ent, (request.user.id, uid)): return 2, request.error_formatter(0, '%s already starred' % ent.__name__) e = store.get(ent, uid) if e: starred = starred_ent() starred.user_id = request.user.id starred.starred_id = uid store.add(starred) else: return 1, request.error_formatter(70, 'Unknown %s id' % ent.__name__) return 0, None
def create_playlist(): # Only(?) method where the android client uses form data rather than GET params playlist_id, name = map( lambda x: request.args.get(x) or request.form.get(x), ['playlistId', 'name']) # songId actually doesn't seem to be required songs = request.args.getlist('songId') or request.form.getlist('songId') try: playlist_id = uuid.UUID(playlist_id) if playlist_id else None songs = set(map(uuid.UUID, songs)) except: return request.error_formatter(0, 'Invalid parameter') if playlist_id: playlist = store.get(Playlist, playlist_id) if not playlist: return request.error_formatter(70, 'Unknwon playlist') if playlist.user_id != request.user.id and not request.user.admin: return request.error_formatter( 50, "You're not allowed to modify a playlist that isn't yours") playlist.tracks.clear() if name: playlist.name = name elif name: playlist = Playlist() playlist.user_id = request.user.id playlist.name = name store.add(playlist) else: return request.error_formatter(10, 'Missing playlist id or name') for sid in songs: track = store.get(Track, sid) if not track: return request.error_formatter(70, 'Unknown song') playlist.tracks.add(track) store.commit() return request.formatter({})
def rate(): id, rating = map(request.args.get, ['id', 'rating']) if not id or not rating: return request.error_formatter(10, 'Missing parameter') try: uid = uuid.UUID(id) rating = int(rating) except: return request.error_formatter(0, 'Invalid parameter') if not rating in xrange(6): return request.error_formatter( 0, 'rating must be between 0 and 5 (inclusive)') if rating == 0: store.find(RatingTrack, RatingTrack.user_id == request.user.id, RatingTrack.rated_id == uid).remove() store.find(RatingFolder, RatingFolder.user_id == request.user.id, RatingFolder.rated_id == uid).remove() else: rated = store.get(Track, uid) rating_ent = RatingTrack if not rated: rated = store.get(Folder, uid) rating_ent = RatingFolder if not rated: return request.error_formatter(70, 'Unknown id') rating_info = store.get(rating_ent, (request.user.id, uid)) if rating_info: rating_info.rating = rating else: rating_info = rating_ent() rating_info.user_id = request.user.id rating_info.rated_id = uid rating_info.rating = rating store.add(rating_info) store.commit() return request.formatter({})
def try_star(ent, starred_ent, eid): try: uid = uuid.UUID(eid) except: return 2, request.error_formatter(0, 'Invalid %s id' % ent.__name__) if store.get(starred_ent, (request.user.id, uid)): return 2, request.error_formatter( 0, '%s already starred' % ent.__name__) e = store.get(ent, uid) if e: starred = starred_ent() starred.user_id = request.user.id starred.starred_id = uid store.add(starred) else: return 1, request.error_formatter(70, 'Unknown %s id' % ent.__name__) return 0, None
def create_playlist(): # Only(?) method where the android client uses form data rather than GET params playlist_id, name = map(request.values.get, [ 'playlistId', 'name' ]) # songId actually doesn't seem to be required songs = request.values.getlist('songId') try: playlist_id = uuid.UUID(playlist_id) if playlist_id else None songs = map(uuid.UUID, songs) except: return request.error_formatter(0, 'Invalid parameter') if playlist_id: playlist = store.get(Playlist, playlist_id) if not playlist: return request.error_formatter(70, 'Unknwon playlist') if playlist.user_id != request.user.id and not request.user.admin: return request.error_formatter(50, "You're not allowed to modify a playlist that isn't yours") playlist.clear() if name: playlist.name = name elif name: playlist = Playlist() playlist.user_id = request.user.id playlist.name = name store.add(playlist) else: return request.error_formatter(10, 'Missing playlist id or name') for sid in songs: track = store.get(Track, sid) if not track: store.rollback() return request.error_formatter(70, 'Unknown song') playlist.add(track) store.commit() return request.formatter({})
def playlist_details(uid): try: uid = uuid.UUID(uid) if type(uid) in (str, unicode) else uid except: flash('Invalid playlist id') return redirect(url_for('playlist_index')) playlist = store.get(Playlist, uid) if not playlist: flash('Unknown playlist') return redirect(url_for('playlist_index')) return render_template('playlist.html', playlist = playlist)
def rate(): id, rating = map(request.values.get, [ 'id', 'rating' ]) if not id or not rating: return request.error_formatter(10, 'Missing parameter') try: uid = uuid.UUID(id) rating = int(rating) except: return request.error_formatter(0, 'Invalid parameter') if not rating in xrange(6): return request.error_formatter(0, 'rating must be between 0 and 5 (inclusive)') if rating == 0: store.find(RatingTrack, RatingTrack.user_id == request.user.id, RatingTrack.rated_id == uid).remove() store.find(RatingFolder, RatingFolder.user_id == request.user.id, RatingFolder.rated_id == uid).remove() else: rated = store.get(Track, uid) rating_ent = RatingTrack if not rated: rated = store.get(Folder, uid) rating_ent = RatingFolder if not rated: return request.error_formatter(70, 'Unknown id') rating_info = store.get(rating_ent, (request.user.id, uid)) if rating_info: rating_info.rating = rating else: rating_info = rating_ent() rating_info.user_id = request.user.id rating_info.rated_id = uid rating_info.rating = rating store.add(rating_info) store.commit() return request.formatter({})
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
def get_entity(req, ent, param = 'id'): eid = req.values.get(param) if not eid: return False, req.error_formatter(10, 'Missing %s id' % ent.__name__) try: eid = uuid.UUID(eid) except: return False, req.error_formatter(0, 'Invalid %s id' % ent.__name__) entity = store.get(ent, eid) if not entity: return False, (req.error_formatter(70, '%s not found' % ent.__name__), 404) return True, entity
def get_entity(req, ent, param = 'id'): eid = req.args.get(param) if not eid: return False, req.error_formatter(10, 'Missing %s id' % ent.__name__) try: eid = uuid.UUID(eid) except: return False, req.error_formatter(0, 'Invalid %s id' % ent.__name__) entity = store.get(ent, eid) if not entity: return False, (req.error_formatter(70, '%s not found' % ent.__name__), 404) return True, entity
def get_client_prefs(): if not request.path.startswith('/rest/'): return if 'c' not in request.values: return request.error_formatter(10, 'Missing required parameter') 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
def update_clients(): clients_opts = {} for client in set(map(lambda k: k.rsplit('_', 1)[0], request.form.keys())): clients_opts[client] = { k.rsplit('_', 1)[1]: v for k, v in filter(lambda (k, v): k.startswith(client), request.form.iteritems()) } app.logger.debug(clients_opts) for client, opts in clients_opts.iteritems(): prefs = store.get(ClientPrefs, (uuid.UUID(session.get('userid')), client)) if 'delete' in opts and opts['delete'] in [ 'on', 'true', 'checked', 'selected', '1' ]: store.remove(prefs) continue prefs.format = opts['format'] if 'format' in opts and opts['format'] else None prefs.bitrate = int(opts['bitrate']) if 'bitrate' in opts and opts['bitrate'] else None store.commit() flash('Clients preferences updated.') return user_profile()
def playlist_delete(uid): try: uid = uuid.UUID(uid) except: flash('Invalid playlist id') return redirect(url_for('playlist_index')) playlist = store.get(Playlist, uid) if not playlist: flash('Unknown playlist') elif str(playlist.user_id) != session.get('userid'): flash("You're not allowed to delete this playlist") else: store.remove(playlist) store.commit() flash('Playlist deleted') return redirect(url_for('playlist_index'))
def update_playlist(): status, res = get_entity(request, Playlist, 'playlistId') if not status: return res if res.user_id != request.user.id and not request.user.admin: return request.error_formatter( 50, "You're not allowed to delete a playlist that isn't yours") playlist = res name, comment, public = map(request.args.get, ['name', 'comment', 'public']) to_add, to_remove = map(request.args.getlist, ['songIdToAdd', 'songIndexToRemove']) try: to_add = set(map(uuid.UUID, to_add)) to_remove = sorted(set(map(int, to_remove))) except: return request.error_formatter(0, 'Invalid parameter') if name: playlist.name = name if comment: playlist.comment = comment if public: playlist.public = public in (True, 'True', 'true', 1, '1') tracks = list(playlist.tracks) for sid in to_add: track = store.get(Track, sid) if not track: return request.error_formatter(70, 'Unknown song') if track not in playlist.tracks: playlist.tracks.add(track) for idx in to_remove: if idx < 0 or idx >= len(tracks): return request.error_formatter(0, 'Index out of range') playlist.tracks.remove(tracks[idx]) store.commit() return request.formatter({})
def update_playlist(): status, res = get_entity(request, Playlist, "playlistId") if not status: return res if res.user_id != request.user.id and not request.user.admin: return request.error_formatter(50, "You're not allowed to delete a playlist that isn't yours") playlist = res name, comment, public = map(request.values.get, ["name", "comment", "public"]) to_add, to_remove = map(request.values.getlist, ["songIdToAdd", "songIndexToRemove"]) try: to_add = set(map(uuid.UUID, to_add)) to_remove = sorted(set(map(int, to_remove))) except: return request.error_formatter(0, "Invalid parameter") if name: playlist.name = name if comment: playlist.comment = comment if public: playlist.public = public in (True, "True", "true", 1, "1") tracks = list(playlist.tracks) for sid in to_add: track = store.get(Track, sid) if not track: return request.error_formatter(70, "Unknown song") if track not in playlist.tracks: playlist.tracks.add(track) for idx in to_remove: if idx < 0 or idx >= len(tracks): return request.error_formatter(0, "Index out of range") playlist.tracks.remove(tracks[idx]) store.commit() return request.formatter({})
def playlist_update(uid): try: uid = uuid.UUID(uid) except: flash('Invalid playlist id') return redirect(url_for('playlist_index')) playlist = store.get(Playlist, uid) if not playlist: flash('Unknown playlist') return redirect(url_for('playlist_index')) if str(playlist.user_id) != session.get('userid'): flash("You're not allowed to edit this playlist") elif not request.form.get('name'): flash('Missing playlist name') else: playlist.name = request.form.get('name') playlist.public = request.form.get('public') in (True, 'True', 1, '1', 'on', 'checked') store.commit() flash('Playlist updated.') return playlist_details(uid)
def update_playlist(): status, res = get_entity(request, Playlist, 'playlistId') if not status: return res if res.user_id != request.user.id and not request.user.admin: return request.error_formatter( 50, "You're not allowed to delete a playlist that isn't yours") playlist = res name, comment, public = map(request.values.get, ['name', 'comment', 'public']) to_add, to_remove = map(request.values.getlist, ['songIdToAdd', 'songIndexToRemove']) try: to_add = map(uuid.UUID, to_add) to_remove = map(int, to_remove) except: return request.error_formatter(0, 'Invalid parameter') if name: playlist.name = name if comment: playlist.comment = comment if public: playlist.public = public in (True, 'True', 'true', 1, '1') for sid in to_add: track = store.get(Track, sid) if not track: return request.error_formatter(70, 'Unknown song') playlist.add(track) playlist.remove_at_indexes(to_remove) store.commit() return request.formatter({})
def list_indexes(): musicFolderId = request.args.get('musicFolderId') ifModifiedSince = request.args.get('ifModifiedSince') if ifModifiedSince: try: ifModifiedSince = int(ifModifiedSince) / 1000 except: return request.error_formatter(0, 'Invalid timestamp') if musicFolderId is None: folder = store.find(Folder, Folder.root == True) else: try: mfid = uuid.UUID(musicFolderId) except: return request.error_formatter(0, 'Invalid id') folder = store.get(Folder, mfid) if not folder or (type(folder) is Folder and not folder.root): return request.error_formatter(70, 'Folder not found') last_modif = max( map(lambda f: f.last_scan, folder)) if type(folder) is not Folder else folder.last_scan if (not ifModifiedSince is None) and last_modif < ifModifiedSince: return request.formatter( {'indexes': { 'lastModified': last_modif * 1000 }}) # The XSD lies, we don't return artists but a directory structure if type(folder) is not Folder: artists = [] childs = [] for f in folder: artists += f.children childs += f.tracks else: artists = folder.children childs = folder.tracks indexes = {} for artist in artists: index = artist.name[0].upper() if index in map(str, xrange(10)): index = '#' elif index not in string.letters: index = '?' if index not in indexes: indexes[index] = [] indexes[index].append(artist) return request.formatter({ 'indexes': { 'lastModified': last_modif * 1000, 'index': [{ 'name': k, 'artist': [{ 'id': str(a.id), '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()) ] } })
def stream_media(): status, res = get_entity(request, Track) if not status: return res maxBitRate, format, timeOffset, size, estimateContentLength, client = map(request.args.get, [ 'maxBitRate', 'format', 'timeOffset', 'size', 'estimateContentLength', 'c' ]) if format: format = format.lower() src_suffix = res.suffix() dst_suffix = res.suffix() 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 maxBitRate: try: maxBitRate = int(maxBitRate) except: return request.error_formatter(0, 'Invalid bitrate value') if dst_bitrate > maxBitRate and maxBitRate != 0: dst_bitrate = maxBitRate if format and format != 'raw' and format != src_suffix: dst_suffix = format dst_mimetype = scanner.get_mime(dst_suffix) if format != 'raw' and (dst_suffix != src_suffix or dst_bitrate != res.bitrate): transcoder = config.get('transcoding', 'transcoder_{}_{}'.format(src_suffix, dst_suffix)) decoder = config.get('transcoding', 'decoder_' + src_suffix) or config.get('transcoding', 'decoder') encoder = config.get('transcoding', 'encoder_' + dst_suffix) or config.get('transcoding', 'encoder') 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)) transcoder, decoder, encoder = map(lambda x: prepare_transcoding_cmdline(x, res.path, src_suffix, dst_suffix, dst_bitrate), [ transcoder, decoder, encoder ]) try: if transcoder: proc = subprocess.Popen(transcoder, stdout = subprocess.PIPE) else: dec_proc = subprocess.Popen(decoder, stdout = subprocess.PIPE) proc = subprocess.Popen(encoder, stdin = dec_proc.stdout, stdout = subprocess.PIPE) except: return request.error_formatter(0, 'Error while running the transcoding process') def transcode(): while True: data = proc.stdout.read(8192) if not data: break yield data proc.terminate() proc.wait() app.logger.info('Transcoding track {0.id} for user {1.id}. Source: {2} at {0.bitrate}kbps. Dest: {3} at {4}kbps'.format(res, request.user, src_suffix, dst_suffix, dst_bitrate)) response = Response(transcode(), mimetype = dst_mimetype) else: response = send_file(res.path, mimetype = dst_mimetype) res.play_count = res.play_count + 1 res.last_play = now() request.user.last_play = res request.user.last_play_date = now() store.commit() return response
def list_indexes(): musicFolderId = request.values.get('musicFolderId') ifModifiedSince = request.values.get('ifModifiedSince') if ifModifiedSince: try: ifModifiedSince = int(ifModifiedSince) / 1000 except: return request.error_formatter(0, 'Invalid timestamp') if musicFolderId is None: folder = store.find(Folder, Folder.root == True) else: try: mfid = uuid.UUID(musicFolderId) except: return request.error_formatter(0, 'Invalid id') folder = store.get(Folder, mfid) if not folder or (type(folder) is Folder and not folder.root): return request.error_formatter(70, 'Folder not found') last_modif = max(map(lambda f: f.last_scan, folder)) if type(folder) is not Folder else folder.last_scan if (not ifModifiedSince is None) and last_modif < ifModifiedSince: return request.formatter({ 'indexes': { 'lastModified': last_modif * 1000 } }) # The XSD lies, we don't return artists but a directory structure if type(folder) is not Folder: artists = [] childs = [] for f in folder: artists += f.children childs += f.tracks else: artists = folder.children childs = folder.tracks indexes = {} for artist in artists: index = artist.name[0].upper() if index in map(str, xrange(10)): index = '#' elif index not in string.letters: index = '?' if index not in indexes: indexes[index] = [] indexes[index].append(artist) return request.formatter({ 'indexes': { 'lastModified': last_modif * 1000, 'index': [ { 'name': k, 'artist': [ { 'id': str(a.id), '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, request.prefs) for c in sorted(childs, key = lambda t: t.sort_key()) ] } })
def stream_media(): status, res = get_entity(request, Track) if not status: return res maxBitRate, format, timeOffset, size, estimateContentLength, client = map(request.values.get, [ 'maxBitRate', 'format', 'timeOffset', 'size', 'estimateContentLength', 'c' ]) if format: format = format.lower() src_suffix = res.suffix() dst_suffix = res.suffix() 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 maxBitRate: try: maxBitRate = int(maxBitRate) except: return request.error_formatter(0, 'Invalid bitrate value') if dst_bitrate > maxBitRate and maxBitRate != 0: dst_bitrate = maxBitRate if format and format != 'raw' and format != src_suffix: dst_suffix = format dst_mimetype = scanner.get_mime(dst_suffix) if format != 'raw' and (dst_suffix != src_suffix or dst_bitrate != res.bitrate): transcoder = config.get('transcoding', 'transcoder_{}_{}'.format(src_suffix, dst_suffix)) decoder = config.get('transcoding', 'decoder_' + src_suffix) or config.get('transcoding', 'decoder') encoder = config.get('transcoding', 'encoder_' + dst_suffix) or config.get('transcoding', 'encoder') 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)) transcoder, decoder, encoder = map(lambda x: prepare_transcoding_cmdline(x, res.path, src_suffix, dst_suffix, dst_bitrate), [ transcoder, decoder, encoder ]) try: if transcoder: dec_proc = None proc = subprocess.Popen(transcoder, stdout = subprocess.PIPE) else: dec_proc = subprocess.Popen(decoder, stdout = subprocess.PIPE) proc = subprocess.Popen(encoder, stdin = dec_proc.stdout, stdout = subprocess.PIPE) except: return request.error_formatter(0, 'Error while running the transcoding process') def transcode(): try: while True: data = proc.stdout.read(8192) if not data: break yield data except: if dec_proc != None: dec_proc.terminate() proc.terminate() if dec_proc != None: dec_proc.wait() proc.wait() app.logger.info('Transcoding track {0.id} for user {1.id}. Source: {2} at {0.bitrate}kbps. Dest: {3} at {4}kbps'.format(res, request.user, src_suffix, dst_suffix, dst_bitrate)) response = Response(transcode(), mimetype = dst_mimetype) else: response = send_file(res.path, mimetype = dst_mimetype) res.play_count = res.play_count + 1 res.last_play = now() request.user.last_play = res request.user.last_play_date = now() store.commit() return response