diff --git a/api/media.py b/api/media.py index 54516b5..561e2e5 100755 --- a/api/media.py +++ b/api/media.py @@ -1,11 +1,14 @@ # coding: utf-8 from flask import request, send_file -import Image -from web import app -from db import Track, Folder -import config import os.path, uuid +import Image +from time import time as now + +import config +from web import app +from db import Track, Folder, User +from lastfm import LastFm @app.route('/rest/stream.view', methods = [ 'GET', 'POST' ]) def stream_media(): @@ -78,3 +81,34 @@ def cover_art(): im.save(path, 'JPEG') return send_file(path) +@app.route('/rest/scrobble.view', methods = [ 'GET', 'POST' ]) +def scrobble(): + tid, time, submission, u = map(request.args.get, [ 'id', 'time', 'submission', 'u' ]) + if not tid: + return request.error_formatter(10, 'Missing file id') + try: + tid = uuid.UUID(tid) + except: + return request.error_formatter(0, 'Invalid file id') + track = Track.query.get(tid) + if not track: + return request.error_formatter(70, 'File not found') + + if time: + try: + time = int(time) / 1000 + except: + return request.error_formatter(0, 'Invalid time value') + else: + time = int(now()) + + user = User.query.filter(User.name == u).one() + lfm = LastFm(user, app.logger) + + if submission in (None, '', True, 'true', 'True', 1, '1'): + lfm.scrobble(track, time) + else: + lfm.now_playing(track) + + return request.formatter({}) + diff --git a/api/user.py b/api/user.py index 9e1c945..a24f72f 100755 --- a/api/user.py +++ b/api/user.py @@ -18,7 +18,7 @@ def user_info(): 'user': { 'username': user.name, 'email': user.mail, - 'scrobblingEnabled': False, + 'scrobblingEnabled': user.lastfm_session is not None and user.lastfm_status, 'adminRole': user.admin, 'settingsRole': False, 'downloadRole': False, diff --git a/lastfm.py b/lastfm.py new file mode 100755 index 0000000..82e2247 --- /dev/null +++ b/lastfm.py @@ -0,0 +1,78 @@ +# coding: utf-8 + +import requests, hashlib +import config +from db import session + +class LastFm: + def __init__(self, user, logger): + self.__user = user + self.__api_key = config.get('LASTFM_KEY') + self.__api_secret = config.get('LASTFM_SECRET') + self.__enabled = self.__api_key is not None + self.__logger = logger + + def link_account(self, token): + if not self.__enabled: + return False, 'No API key set' + + res = self.__api_request(False, method = 'auth.getSession', token = token) + if 'error' in res: + return False, 'Error %i: %s' % (res['error'], res['message']) + else: + self.__user.lastfm_session = res['session']['key'] + self.__user.lastfm_status = True + session.commit() + return True, 'OK' + + def unlink_account(self): + self.__user.lastfm_session = None + self.__user.lastfm_status = True + session.commit() + + def now_playing(self, track): + if not self.__enabled: + return + + res = self.__api_request(True, method = 'track.updateNowPlaying', artist = track.album.artist.name, track = track.title, album = track.album.name, + trackNumber = track.number, duration = track.duration) + + def scrobble(self, track, ts): + if not self.__enabled: + return + + res = self.__api_request(True, method = 'track.scrobble', artist = track.album.artist.name, track = track.title, album = track.album.name, + timestamp = ts, trackNumber = track.number, duration = track.duration) + + def __api_request(self, write, **kwargs): + if not self.__enabled: + return + + if write: + if not self.__user.lastfm_session or not self.__user.lastfm_status: + return + kwargs['sk'] = self.__user.lastfm_session + + kwargs['api_key'] = self.__api_key + + sig_str = '' + for k, v in sorted(kwargs.iteritems()): + sig_str += k + str(v) + sig = hashlib.md5(sig_str + self.__api_secret).hexdigest() + + kwargs['api_sig'] = sig + kwargs['format'] = 'json' + + if write: + r = requests.post('http://ws.audioscrobbler.com/2.0/', data = kwargs) + else: + r = requests.get('http://ws.audioscrobbler.com/2.0/', params = kwargs) + + if 'error' in r.json: + if r.json['error'] in (9, '9'): + self.__user.lastfm_status = False + session.commit() + self.__logger.warn('LastFM error %i: %s' % (r.json['error'], r.json['message'])) + + return r.json + diff --git a/user.py b/user.py index 348e54b..26ed09b 100755 --- a/user.py +++ b/user.py @@ -1,12 +1,12 @@ # coding: utf-8 from flask import request, session, flash, render_template, redirect, url_for -import requests, hashlib from web import app from user_manager import UserManager from db import User, session as db_sess import config +from lastfm import LastFm @app.before_request def check_admin(): @@ -114,37 +114,16 @@ def lastfm_reg(): flash('Missing LastFM auth token') return redirect(url_for('user_profile')) - p = { - 'api_key': config.get('LASTFM_KEY'), - 'method': 'auth.getSession', - 'token': token - } - sig_str = '' - for k, v in sorted(p.iteritems()): - sig_str += k + v - sig = hashlib.md5(sig_str + config.get('LASTFM_SECRET')).hexdigest() - - p['api_sig'] = sig - p['format'] = 'json' - - r = requests.get('http://ws.audioscrobbler.com/2.0/', params = p) - if 'error' in r.json: - flash('Error %i: %s' % (r.json['error'], r.json['message'])) - else: - user = UserManager.get(session.get('userid'))[1] - user.lastfm_session = r.json['session']['key'] - user.lastfm_status = True - db_sess.commit() - flash('Successfully linked LastFM account') + lfm = LastFm(UserManager.get(session.get('userid'))[1], app.logger) + status, error = lfm.link_account(token) + flash(error if not status else 'Successfully linked LastFM account') return redirect(url_for('user_profile')) @app.route('/user/lastfm/unlink') def lastfm_unreg(): - user = UserManager.get(session.get('userid'))[1] - user.lastfm_session = None - user.lastfm_status = True - db_sess.commit() + lfm = LastFm(UserManager.get(session.get('userid'))[1], app.logger) + lfm.unlink_account() flash('Unliked LastFM account') return redirect(url_for('user_profile'))