initial version of simple rdio exporter

This commit is contained in:
eleith 2013-05-27 04:12:53 -07:00
commit dc81bbe681
3 changed files with 271 additions and 0 deletions

105
om.js Normal file
View File

@ -0,0 +1,105 @@
var qs = require("querystring"),
url = require("url"),
crypto = require("crypto");
module.exports = om;
function om(consumer, urlString, params, token, method, realm, timestamp, nonce) {
params = params || [];
method = (method || "POST").toUpperCase();
// Coerce params to array of [key, value] pairs.
if (!Array.isArray(params)) {
var paramsArray = [];
for (var key in params) {
paramsArray.push([key, params[key]]);
}
params = paramsArray;
}
var parsed = url.parse(urlString, true);
// Add query params.
if (parsed.query) {
for (var key in parsed.query) {
params.push([key, parsed.query[key]]);
}
}
// Generate nonce and timestamp if they weren't provided
if (typeof timestamp == "undefined" || timestamp == null) {
timestamp = Math.round(new Date().getTime() / 1000).toString();
}
if (typeof nonce == "undefined" || nonce == null) {
nonce = Math.round(Math.random() * 1000000).toString();
}
// Add OAuth params.
params.push(["oauth_version", "1.0"]);
params.push(["oauth_timestamp", timestamp]);
params.push(["oauth_nonce", nonce]);
params.push(["oauth_signature_method", "HMAC-SHA1"]);
params.push(["oauth_consumer_key", consumer[0]]);
// Calculate the hmac key.
var hmacKey = consumer[1] + "&";
// If a token was provided, add it to the params and hmac key.
if (typeof token != "undefined" && token != null) {
params.push(["oauth_token", token[0]]);
hmacKey += token[1];
}
// Sort lexicographically, first by key then by value.
params.sort();
// Calculate the OAuth signature.
var paramsString = params.map(function (param) {
return qs.escape(param[0]) + "=" + qs.escape(param[1]);
}).join("&");
var urlBase = url.format({
protocol: parsed.protocol || "http:",
hostname: parsed.hostname.toLowerCase(),
pathname: parsed.pathname
});
var signatureBase = [
method,
qs.escape(urlBase),
qs.escape(paramsString)
].join("&");
var hmac = crypto.createHmac("sha1", hmacKey);
hmac.update(signatureBase);
var oauthSignature = hmac.digest("base64");
// Build the Authorization header.
var headerParams = [];
if (realm) {
headerParams.push(["realm", realm]);
}
headerParams.push(["oauth_signature", oauthSignature]);
// Restrict header params to oauth_* subset.
var oauthParams = ["oauth_version", "oauth_timestamp", "oauth_nonce",
"oauth_signature_method", "oauth_signature", "oauth_consumer_key",
"oauth_token"];
params.forEach(function (param) {
if (oauthParams.indexOf(param[0]) != -1) {
headerParams.push(param);
}
});
var header = "OAuth " + headerParams.map(function (param) {
return param[0] + '="' + param[1] + '"';
}).join(", ");
return header;
}

124
rdio.js Normal file
View File

@ -0,0 +1,124 @@
var qs = require("querystring"),
url = require("url"),
http = require("http"),
om = require("./om");
module.exports = Rdio;
function Rdio(consumer, token) {
this.consumer = consumer;
this.token = token;
}
Rdio.prototype.beginAuthentication = function beginAuthentication(callbackUrl, callback) {
var self = this;
this._signedPost("http://api.rdio.com/oauth/request_token", {
oauth_callback: callbackUrl
}, function (err, body) {
if (err) {
callback(err, null);
} else {
var parsed = qs.parse(body);
var token = [parsed.oauth_token, parsed.oauth_token_secret];
var authUrl = parsed.login_url + "?oauth_token=" + parsed.oauth_token;
// Save the token.
self.token = token;
// Call the callback with a URL the app can use to auth.
callback(null, authUrl);
}
});
};
Rdio.prototype.completeAuthentication = function completeAuthentication(verifier, callback) {
var self = this;
this._signedPost("http://api.rdio.com/oauth/access_token", {
oauth_verifier: verifier
}, function (err, body) {
if (err) {
callback(err);
} else {
var parsed = qs.parse(body);
var token = [parsed.oauth_token, parsed.oauth_token_secret];
// Save the token.
self.token = token;
// Call the callback.
callback(null);
}
});
};
Rdio.prototype.call = function call(method, params, callback) {
if (typeof params == "function") {
callback = params;
params = null;
}
var copy = {};
if (params) {
for (var param in params) {
copy[param] = params[param];
}
}
copy.method = method;
this._signedPost("http://api.rdio.com/1/", copy, function (err, body) {
if (err) {
callback(err);
} else {
callback(null, JSON.parse(body));
}
});
};
Rdio.prototype._signedPost = function signedPost(urlString, params, callback) {
var auth = om(this.consumer, urlString, params, this.token);
var parsed = url.parse(urlString);
var content = qs.stringify(params);
var req = http.request({
method: "POST",
host: parsed.host,
port: parsed.port || "80",
path: parsed.pathname,
headers: {
"Authorization": auth,
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": content.length.toString()
}
}, function (res) {
var body = "";
res.setEncoding("utf8");
res.on("data", function (chunk) {
body += chunk;
});
res.on("end", function () {
var data = {};
try {
data = JSON.parse(body);
} catch(e) {
data.status = 'error';
data.message = body;
}
if (data.status === 'error') {
callback(data.message);
} else {
callback(null, body);
}
});
});
req.on("error", function (err) {
callback(err);
});
req.end(content);
};

42
xport.js Normal file
View File

@ -0,0 +1,42 @@
var Rdio = require("./rdio");
var rdio = new Rdio(["your-rdio-api-key", "your-rdio-api-secret"]);
var readline = require("readline");
var fs = require("fs");
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question("what username do you want to export all tracks for? ", function(vanityName) {
rdio.call("findUser", {vanityName:vanityName}, function(err, data) {
if(!err) {
if(data.status == "ok") {
var user_key = data.result.key;
rdio.call("getTracksInCollection", {user:user_key}, function(err, data) {
if(!err) {
if(data.status == "ok") {
var fd = fs.openSync("rdio-track-export." + vanityName + ".csv", "w");
fs.writeSync(fd, "Artist,Album,Track");
for(var i = 0; i < data.result.length; i++) {
var track = data.result[i];
fs.writeSync(fd,
"\"" + track.artist.replace(/"/g, '""') +
"\",\"" + track.album.replace(/"/g, '""') +
"\",\"" + track.name.replace(/"/g, '""') + "\"\n");
}
rl.close();
} else {
console.error("no tracks found!?");
rl.close();
}
}
});
} else {
console.error(vanityName + " not found or is not public");
rl.close();
}
}
});
});