function LastFM(user) { var lastfm_api_key = "15f7532dff0b8d84635c757f9f18aaa3"; var lastfm_secret="3ddf4cb9937e015ca4f30296a918a2b0"; var logged_in = false; var username = user; var token = ""; var self=this; var queue = new Array(); var throttle = null; var throttleTime = 500; var backofftimer; function startlogin() { self.login($('#configpanel input[name="lfmuser"]').val()); } function logout() { prefs.save({lastfm_session_key: ''}); logged_in = false; uiLoginBind(); } function uiLoginBind() { if (!logged_in) { $('.lastfmlogin-required').removeClass('notenabled').addClass('notenabled'); $('#lastfmloginbutton').off('click').on('click', startlogin).html(language.gettext('config_loginbutton')); } else { $('.lastfmlogin-required').removeClass('notenabled'); $('#lastfmloginbutton').off('click').on('click', logout).html(language.gettext('button_logout')); } } if (prefs.lastfm_session_key !== "" || typeof lastfm_session_key !== 'undefined') { logged_in = true; } uiLoginBind(); function speedBackUp() { throttleTime = 500; } function setThrottling(t) { clearTimeout(backofftimer); throttleTime = t; backofftimer = setTimeout(speedBackUp, 90000); } this.showloveban = function(flag) { if (logged_in && flag) { $("#lastfm").show(); } else { $("#lastfm").hide(); } } this.isLoggedIn = function() { return logged_in; } this.getLanguage = function() { switch (prefs.lastfmlang) { case "default": return null; break; case "interface": return interfaceLanguage; break; case "browser": return browserLanguage; break; case "user": if (prefs.user_lang != "") { return prefs.user_lang; } else { return null; } break; } } this.username = function() { return username; } this.login = function (user, pass) { username = user; var options = {api_key: lastfm_api_key, method: "auth.getToken"}; var keys = getKeys(options); var it = ""; for(var key in keys) { it = it+keys[key]+options[keys[key]]; } it = it+lastfm_secret; options.api_sig = hex_md5(it); options.format = 'json'; var url = "https://ws.audioscrobbler.com/2.0/"; var adder = "?"; var keys = getKeys(options); for(var key in keys) { url=url+adder+keys[key]+"="+options[keys[key]]; adder = "&"; } $.get(url, function(data) { token = data.token; debug.log("LASTFM","Token",token); var lfmlog = new popup({ css: { width: 600, height: 400 }, fitheight: true, title: language.gettext("lastfm_loginwindow") }); var mywin = lfmlog.create(); mywin.append('
'); $("#lfmlogintable").append(''+language.gettext("lastfm_login1")+''); $("#lfmlogintable").append(''+language.gettext("lastfm_login2")+''); $("#lfmlogintable").append(''+ ''); $("#lfmlogintable").append(''+language.gettext("lastfm_login3")+''); lfmlog.addCloseButton('OK',lastfm.finishlogin); lfmlog.setWindowToContentsSize(); lfmlog.open(); }); } this.finishlogin = function() { debug.log("LAST.FM","Finishing login"); LastFMSignedRequest( { token: token, format: 'json', api_key: lastfm_api_key, method: "auth.getSession" }, function(data) { debug.log("LASTFM","Got Session Key : ",data); var lastfm_session_key = data.session.key; logged_in = true; prefs.save({ lastfm_session_key: lastfm_session_key, lastfm_user: username }); uiLoginBind(); }, function(data) { alert(language.gettext("lastfm_loginfailed")); } ); return true; } this.flushReqids = function() { clearTimeout(throttle); for (var i = queue.length-1; i >= 0; i--) { if (queue[i].flag == false && queue[i].reqid) { queue.splice(i, 1); } } throttle = setTimeout(lastfm.getRequest, throttleTime); } this.formatBio = function(bio, link) { debug.trace("LASTFM"," Formatting Bio"); if (bio) { bio = bio.replace(/\n/g, "

"); bio = bio.replace(/($2'); return bio.fixDodgyLinks(); } else { return ""; } } function LastFMGetRequest(options, cache, success, fail, reqid) { options.format = "json"; options.cache = cache; queue.push({url: options, success: success, fail: fail, flag: false, cache: cache, reqid: reqid, retries: 0}); if (throttle == null && queue.length == 1) { lastfm.getRequest(); } } function addGetOptions(options, method) { options.api_key = lastfm_api_key; options.autocorrect = prefs.lastfm_autocorrect ? 1 : 0; options.method = method; } var getKeys = function(obj) { var keys = []; for(var key in obj){ keys.push(key); } keys.sort(); return keys; } this.getRequest = function() { var req = queue[0]; clearTimeout(throttle); if (req) { if (req.flag) { debug.trace("LASTFM","Request pulled from queue is already being handled!") return; } queue[0].flag = true; debug.trace("LASTFM","Taking next request from queue",req.url); if (req.url == "POST") { debug.log("LASTFM", "Handling POST request via queue"); $.ajax({ method: "POST", url: "https://ws.audioscrobbler.com/2.0/", data: req.options, dataType: 'json', timeout: 5000 }) .done(function(data) { throttle = setTimeout(lastfm.getRequest, throttleTime); req = queue.shift(); debug.log("LASTFM", req.options.method,"request success"); if (data.error) { debug.blurt("LASTFM","Last FM signed request failed with status",data.error.message); req.fail(data); } else { req.success(data); } }) .fail(function(xhr,status,err) { req = queue.shift(); debug.error("LASTFM",req.options.method,"request error",status,err); if (xhr.responseJSON) { var errorcode = xhr.responseJSON.error; var errortext = xhr.responseJSON.message; debug.error("LASTFM", 'Error Code',errorcode,"Message",errortext); switch (errorcode) { case 4: case 9: case 10: case 14: case 26: debug.error("LASTFM","We are not authenticated. Logging Out"); logout(); req.fail(null); break; case 29: debug.warn("LASTFM","Rate Limit Exceeded. Slowing Down"); setThrottling(throttleTime*2); // Fall through default: if (req.retries < 3) { debug.log("LASTFM","Retrying..."); req.retries++; req.flag = false; queue.unshift(req); } else { req.fail(null); } } } throttle = setTimeout(lastfm.getRequest, throttleTime); }); } else { var getit = $.ajax({ method: 'POST', url: 'browser/backends/getlfmdata.php', data: req.url, dataType: 'json' }) .done(function(data) { var c = getit.getResponseHeader('Pragma'); debug.debug("LASTFM","Request success",c,data); if (c == "From Cache") { throttle = setTimeout(lastfm.getRequest, 100); } else { throttle = setTimeout(lastfm.getRequest, throttleTime); } req = queue.shift(); debug.debug("LASTFM","Calling success callback"); if (req.reqid || req.reqid === 0) { req.success(data, req.reqid); } else { req.success(data); } }) .fail(function(xhr,status,err) { throttle = setTimeout(lastfm.getRequest, throttleTime); req = queue.shift(); debug.warn("LASTFM", "Get Request Failed",xhr,status,err); var errormessage = language.gettext('lastfm_error'); if (xhr.responseJSON) { var errorcode = xhr.responseJSON.error; var errortext = xhr.responseJSON.message; errormessage += '
'+errortext; if (errorcode == 29) { debug.warn("LASTFM","Rate Limit Exceeded. Slowing Down"); setThrottling(throttleTime*2); } } else { errormessage += ' ('+xhr.status+' '+err+')'; } if (req.reqid || req.reqid === 0) { req.fail(null, req.reqid); } else { req.fail({error: 1, message: errormessage}); } }); } } else { throttle = null; } } function LastFMSignedRequest(options, success, fail) { // We've passed an object but we need the properties to be in alphabetical order var keys = getKeys(options); var it = ""; for(var key in keys) { if (keys[key] != 'format' && keys[key] != 'callback') { it = it+keys[key]+options[keys[key]]; } } it = it+lastfm_secret; options.api_sig = hex_md5(it); queue.push({ url: "POST", options: options, success: success, fail: fail, flag: false, retries : 0 }); if (throttle == null && queue.length == 1) { lastfm.getRequest(); } } function addSetOptions(options, method) { options.format = 'json'; options.api_key = lastfm_api_key; options.sk = prefs.lastfm_session_key; options.method = method; } this.track = { love : function(options,callback) { if (logged_in) { addSetOptions(options, "track.love"); LastFMSignedRequest( options, function() { infobar.notify(language.gettext("label_loved")+" "+options.track); callback(true); }, function() { infobar.error(language.gettext("label_lovefailed")); } ); } }, unlove : function(options,callback,callback2) { if (logged_in) { addSetOptions(options, "track.unlove"); LastFMSignedRequest( options, function() { infobar.notify(language.gettext("label_unloved")+" "+options.track); callback(false); }, function() { infobar.error(language.gettext("label_unlovefailed")); } ); } }, getInfo : function(options, callback, failcallback, reqid) { // If we're logged in to Last.FM we don't use the cache for this request // because it contains 'userloved', which we might update if (username != "") { options.username = username; } addGetOptions(options, "track.getInfo"); if (self.getLanguage()) { options.lang = self.getLanguage(); } LastFMGetRequest( options, !logged_in, callback, failcallback, reqid ); }, getTags: function(options, callback, failcallback, reqid) { if (username != "") { options.user = username; } addGetOptions(options, "track.getTags"); LastFMGetRequest( options, !logged_in, callback, failcallback, reqid ); }, getSimilar: function(options, callback, failcallback) { addGetOptions(options, "track.getSimilar"); LastFMGetRequest( options, true, callback, failcallback ); }, addTags : function(options, callback, failcallback) { if (logged_in) { addSetOptions(options, "track.addTags"); LastFMSignedRequest( options, function() { callback("track", options.tags) }, function() { failcallback("track", options.tags) } ); } }, removeTag: function(options, callback, failcallback) { if (logged_in) { addSetOptions(options, "track.removeTag"); LastFMSignedRequest( options, function() { callback("track", options.tag); }, function() { failcallback("track", options.tag); } ); } }, updateNowPlaying : function(options) { if (logged_in && prefs.lastfm_scrobbling) { addSetOptions(options, "track.updateNowPlaying"); LastFMSignedRequest( options, function() { }, function() { debug.warn("LAST FM","Failed to update Now Playing",options) } ); } }, scrobble : function(options) { if (logged_in && prefs.lastfm_scrobbling) { debug.log("LAST FM","Last.FM is scrobbling"); addSetOptions(options, "track.scrobble"); LastFMSignedRequest( options, function() { }, function() { infobar.error(language.gettext("label_scrobblefailed")+" "+options.track) } ); } }, } this.album = { getInfo: function(options, callback, failcallback) { addGetOptions(options, "album.getInfo"); if (username != "") { options.username = username } options.autocorrect = prefs.lastfm_autocorrect ? 1 : 0; if (self.getLanguage()) { options.lang = self.getLanguage(); } debug.mark("LASTFM","album.getInfo",options); LastFMGetRequest( options, true, callback, failcallback ); }, getTags: function(options, callback, failcallback) { addGetOptions(options, "album.getTags"); if (username != "") { options.user = username } debug.mark("LASTFM","album.getTags",options); LastFMGetRequest( options, !logged_in, callback, failcallback ); }, addTags : function(options, callback, failcallback) { if (logged_in) { addSetOptions(options, "album.addTags"); LastFMSignedRequest( options, function() { callback("album", options.tags) }, function() { failcallback("album", options.tags) } ); } }, removeTag: function(options, callback, failcallback) { if (logged_in) { addSetOptions(options, "album.removeTag"); LastFMSignedRequest( options, function() { callback("album", options.tag); }, function() { failcallback("album", options.tag); } ); } } } this.artist = { getInfo: function(options, callback, failcallback, reqid) { addGetOptions(options, "artist.getInfo"); if (username != "") { options.username = username } if (self.getLanguage()) { options.lang = self.getLanguage(); } LastFMGetRequest( options, true, callback, failcallback, reqid ); }, getTags: function(options, callback, failcallback) { if (username != "") { options.user = username } addGetOptions(options, "artist.getTags"); LastFMGetRequest( options, !logged_in, callback, failcallback ); }, addTags : function(options, callback, failcallback) { if (logged_in) { addSetOptions(options, "artist.addTags"); LastFMSignedRequest( options, function() { callback("artist", options.tags) }, function() { failcallback("artist", options.tags) } ); } }, removeTag: function(options, callback, failcallback) { if (logged_in) { addSetOptions(options, "artist.removeTag"); LastFMSignedRequest( options, function() { callback("artist", options.tag); }, function() { failcallback("artist", options.tag); } ); } }, getSimilar: function(options, callback, failcallback) { addGetOptions(options, "artist.getSimilar"); LastFMGetRequest( options, true, callback, failcallback, 1 ) } } this.user = { getArtistTracks: function(artist, perpage, page, callback, failcallback) { var options = { user: username, page: page, limit: perpage, artist: artist }; addGetOptions(options, "user.getArtistTracks"); LastFMGetRequest( options, false, callback, failcallback, 1 ) }, getTopArtists: function(options, callback, failcallback) { options.user = username; addGetOptions(options, "user.getTopArtists"); LastFMGetRequest( options, false, callback, failcallback, 1 ) }, getTopTracks: function(options, callback, failcallback) { options.user = username; addGetOptions(options, "user.getTopTracks"); LastFMGetRequest( options, false, callback, failcallback, 1 ) }, getRecentTracks: function(options, callback, failcallback) { options.user = username; options.extended = 1; addGetOptions(options, "user.getRecentTracks"); LastFMGetRequest( options, false, callback, failcallback, 1 ) } } this.library = { getArtists: function(perpage, page, callback, failcallback) { var options = { user: username, page: page, limit: perpage }; addGetOptions(options, "library.getArtists"); LastFMGetRequest( options, false, callback, failcallback, 1 ) } } }