Skip to content

Commit

Permalink
Last.FM Oauth dance
Browse files Browse the repository at this point in the history
  • Loading branch information
anantn committed May 26, 2011
1 parent 1af78ed commit 707d6cd
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 14 deletions.
Binary file added data/icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/loading.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 43 additions & 0 deletions data/panel.css
@@ -0,0 +1,43 @@
body {
color: #93a1a1;
font-family: Arial, Helvetica, sans-serif;
}

.centered {
text-align: center;
}

#container {
padding: 1px;
height: 130px;
border-radius: 0.4em;
background-color: #002b36;
}

#loading {
display: block;
margin-top: 25px;
margin-left: auto;
margin-right: auto;
}

#connect {
display: block;
text-align: center;
margin-left: auto;
margin-right: auto;
margin-top: 25px;
}

#song {
padding: 2px;
height: 100px;
}

#connected {
height: 30px;
float: right;
font-size: 13px;
padding: 2px;
}

11 changes: 11 additions & 0 deletions data/panel.html
@@ -0,0 +1,11 @@
<!doctype html>
<head>
<link rel="stylesheet" href="panel.css">
</head>
<body>
<div id="container">
<img id="loading" src="loading.gif"/>
<p id="message" class="centered">Please wait...</p>
</div>
</body>
</html>
57 changes: 57 additions & 0 deletions data/panel.js
@@ -0,0 +1,57 @@

var main = document.getElementById("container");
var mesg = document.getElementById("message");
var load = document.getElementById("loading");

self.port.on("connection", function(type) {
switch (type) {
case "none":
var div = document.createElement("div");
div.id = "connect";

var wel = document.createElement("p");
wel.class = "centered";
wel.innerHTML = "Let's get you set up, shall we?";

var but = document.createElement("input");
but.id = "connector";
but.type = "button";
but.value = "Connect to Last.FM";
but.onclick = function() {
but.value = "Connecting...";
but.disabled = true;
self.port.emit("connect");
};

mesg.innerHTML = "";
div.appendChild(wel);
div.appendChild(but);
load = main.replaceChild(div, load);
break;

case "failure":
mesg.innerHTML = "Could not authenticate - Try again?";
but.value = "Connect to Last.FM";
but.disabled = false;
break;

default:
main.innerHTML = "";

var song = document.createElement("div");
song.id = "song";
song.appendChild(document.createTextNode("You're not playing any music!"));

var connected = document.createElement("div");
connected.id = "connected";
var asicon = document.createElement("img");
asicon.src = "http://cdn.last.fm/flatness/favicon.2.ico";
connected.appendChild(asicon);
connected.appendChild(document.createTextNode(" Connected as " + type));

main.appendChild(song);
main.appendChild(connected);
break;
}
});

111 changes: 111 additions & 0 deletions lib/lastfm.js
@@ -0,0 +1,111 @@
const tabs = require("tabs");
const {Cc, Ci} = require("chrome"); // Needed for MD5
const request = require("request");

var api_key = "6e91b4d40c233852d6174e300ece1930";
var api_secret = "";
var api_base = "http://ws.audioscrobbler.com/2.0/";
var api_auth = "http://www.last.fm/api/auth/";

function LFMRequest() {
this._uri = api_base;
}
LFMRequest.prototype = {
_method: function(m) {
this._params["method"] = m;
this._params["api_key"] = api_key;
},

_makeSig: function() {
let sig = "";
let keys = Object.keys(this._params);
keys.sort();

for (let i = 0; i < keys.length; i++) {
sig = sig + keys[i] + this._params[keys[i]];
}
sig += api_secret;

let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);
conv.charset = "UTF-8";

let data = conv.convertToByteArray(sig, {});
let hasher = Cc["@mozilla.org/security/hash;1"].
createInstance(Ci.nsICryptoHash);
hasher.init(hasher.MD5);
hasher.update(data, data.length);

let hash = hasher.finish(false);
function toHexString(charCode) {
return ("0" + charCode.toString(16)).slice(-2);
}

let hexed = [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
return hexed;
},

_doCall: function(cb) {
this._uri += "?";
for (let prop in this._params) {
this._uri = this._uri + prop + "=" + this._params[prop] + "&";
}
this._uri = this._uri + "api_sig=" + this._makeSig();

/* We were including format=json before forming the signature, but
* last.fm doesn't like it :( */
this._uri += "&format=json";

let req = request.Request({
url: this._uri,
onComplete: function(response) {
cb(response.json);
}
});
req.get();
},

getToken: function(cb) {
this._params = {};
this._method("auth.gettoken");
this._doCall(function(result) {
cb(result.token);
});
},

getSession: function(token, cb) {
this._params = {};
this._method("auth.getsession");
this._params["token"] = token;
this._doCall(function(result) {
if ("error" in result) {
cb(false);
} else {
cb(result.session);
}
});
}
};

function authorize(cb) {
/* Get an auth token */
let treq = new LFMRequest();
treq.getToken(function(token) {
/* Request auth from user */
let uri = api_auth + "?api_key=" + api_key + "&token=" + token;
tabs.open(uri);
tabs.on("ready", function(tab) {
if (tab.url != "http://www.last.fm/api/grantaccess") return;

/* Verify the auth token by fetching a session token */
tab.close();
let sreq = new LFMRequest();
sreq.getSession(token, function(key) {
if (!key) cb(false);
else cb(key);
});
});
});
}

exports.authorize = authorize;
54 changes: 46 additions & 8 deletions lib/main.js
@@ -1,13 +1,51 @@
const {Cc, Ci} = require("chrome"); // Needed for anchoring panel correctly

const self = require("self");
const panel = require("panel");
const lastfm = require("lastfm");
const widgets = require("widget");
const tabs = require("tabs");
const simple = require("simple-storage");


var foxpan = panel.Panel({
width: 300,
height: 150,
contentURL: self.data.url("panel.html"),
contentScriptFile: self.data.url("panel.js")
});

var widget = widgets.Widget({
id: "mozilla-link",
label: "Mozilla website",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
tabs.open("http://www.mozilla.org/");
}
id: "scrobfox",
label: "Scrobfox",
panel: foxpan,
contentURL: self.data.url("icon.png")
});

console.log("The add-on is running.");
/* Handlers for events from panel contentScript */
foxpan.port.on("connect", function() {
/* Authorize */
foxpan.hide();
lastfm.authorize(function(keys) {
if (!keys) {
foxpan.port.emit("connection", "failed");
} else {
simple.storage.lastfm = keys;
foxpan.port.emit("connection", simple.storage.lastfm.name);

/* Calling show without an anchor shows the panel in the middle */
let WM = Cc['@mozilla.org/appshell/window-mediator;1'].
getService(Ci.nsIWindowMediator);
let doc = WM.getMostRecentWindow("navigator:browser").document;
let bar = doc.getElementById("widget:" + self.id + "-scrobfox");
foxpan.show(bar);
}
});
});

/* Let's check if we have a last.fm username & auth on file */
if (!simple.storage.lastfm || !simple.storage.lastfm.name) {
foxpan.port.emit("connection", "none");
} else {
foxpan.port.emit("connection", simple.storage.lastfm.name);
}

13 changes: 7 additions & 6 deletions package.json
@@ -1,8 +1,9 @@
{
"name": "scrobfox",
"fullName": "scrobfox",
"description": "a basic add-on",
"author": "",
"license": "MPL 1.1/GPL 2.0/LGPL 2.1",
"version": "0.1"
"name": "scrobfox",
"license": "MPL 1.1/GPL 2.0/LGPL 2.1",
"author": "",
"version": "0.1",
"fullName": "scrobfox",
"id": "jid1-zcreHDyRZO9jfg",
"description": "a basic add-on"
}

0 comments on commit 707d6cd

Please sign in to comment.