This commit is contained in:
2022-09-30 05:39:11 +00:00
parent 41ee9463ae
commit 4687fa49bc
11418 changed files with 1312504 additions and 0 deletions

35
buildfiles/app/node_modules/lastfm/lib/lastfm/index.js generated vendored Normal file
View File

@ -0,0 +1,35 @@
var RecentTracksStream = require("./recenttracks-stream"),
LastFmSession = require("./lastfm-session"),
LastFmUpdate = require("./lastfm-update"),
LastFmInfo = require("./lastfm-info"),
LastFmRequest = require("./lastfm-request");
var LastFmNode = exports.LastFmNode = function(options) {
options = options || {};
this.url = "/2.0";
this.host = "ws.audioscrobbler.com";
this.format = "json";
this.secret = options.secret;
this.api_key = options.api_key;
this.useragent = options.useragent || "lastfm-node";
};
LastFmNode.prototype.request = function(method, params) {
return new LastFmRequest(this, method, params);
};
LastFmNode.prototype.stream = function(user, options) {
return new RecentTracksStream(this, user, options);
};
LastFmNode.prototype.session = function(user, key) {
return new LastFmSession(this, user, key);
};
LastFmNode.prototype.info = function(type, options) {
return new LastFmInfo(this, type, options);
};
LastFmNode.prototype.update = function(method, session, options) {
return new LastFmUpdate(this, method, session, options);
};

View File

@ -0,0 +1,45 @@
var EventEmitter = require("events").EventEmitter
, _ = require("underscore");
var LastFmBase = function() {
EventEmitter.call(this);
};
LastFmBase.prototype = Object.create(EventEmitter.prototype);
LastFmBase.prototype.registerHandlers = function(handlers) {
if (typeof handlers !== "object") {
return;
}
var that = this;
_(handlers).each(function(value, key) {
that.on(key, value);
});
};
var defaultBlacklist = ["error", "success", "handlers"];
LastFmBase.prototype.filterParameters = function(parameters, blacklist) {
var filteredParams = {};
_(parameters).each(function(value, key) {
if (isBlackListed(key)) {
return;
}
filteredParams[key] = value;
});
return filteredParams;
function isBlackListed(name) {
return _(defaultBlacklist).include(name) || _(blacklist).include(name);
}
};
LastFmBase.prototype.scheduleCallback = function(callback, delay) {
return setTimeout(callback, delay);
};
LastFmBase.prototype.cancelCallback = function(identifier) {
clearTimeout(identifier);
};
module.exports = LastFmBase;

View File

@ -0,0 +1,43 @@
var LastFmBase = require("./lastfm-base");
var LastFmInfo = function(lastfm, type, options) {
var that = this;
options = options || {};
LastFmBase.call(this);
registerEventHandlers(options);
requestInfo(type, options);
function registerEventHandlers(options) {
that.registerHandlers(options.handlers);
}
function requestInfo(type, options) {
if (!type) {
that.emit("error", new Error("Item type not specified"));
return;
}
var params = that.filterParameters(options)
, method = type + ".getinfo"
, request = lastfm.request(method, params);
request.on("success", success);
request.on("error", error);
}
function success(response) {
if (response[type]) {
that.emit("success", response[type]);
return;
}
that.emit("error", new Error("Unexpected error"));
}
function error(error) {
that.emit("error", error);
}
};
LastFmInfo.prototype = Object.create(LastFmBase.prototype);
module.exports = LastFmInfo;

View File

@ -0,0 +1,144 @@
if (global.GENTLY_HIJACK) require = GENTLY_HIJACK.hijack(require);
var http = require("http")
, querystring = require('querystring')
, _ = require("underscore")
, crypto = require("crypto")
, LastFmBase = require("./lastfm-base");
var WRITE_METHODS = ["album.addtags", "album.removetag", "album.share",
"artist.addtags", "artist.removetag", "artist.share", "artist.shout",
"event.attend", "event.share", "event.shout",
"library.addalbum", "library.addartist", "library.addtrack",
"library.removealbum", "library.removeartist", "library.removetrack", "library.removescrobble",
"playlist.addtrack", "playlist.create",
"radio.tune",
"track.addtags", "track.ban", "track.love", "track.removetag",
"track.scrobble", "track.share", "track.unban", "track.unlove",
"track.updatenowplaying",
"user.shout"],
SIGNED_METHODS = ["auth.getmobilesession", "auth.getsession", "auth.gettoken",
"radio.getplaylist",
"user.getrecentstations", "user.getrecommendedartists", "user.getrecommendedevents"];
var LastFmRequest = module.exports = function(lastfm, method, params) {
var that = this;
LastFmBase.call(this);
params = params || {};
that.registerHandlers(params.handlers);
sendRequest(lastfm.host, lastfm.url, params);
function sendRequest(host, url, params) {
var httpVerb = isWriteRequest() ? "POST" : "GET"
var requestParams = buildRequestParams(params);
var data = querystring.stringify(requestParams);
if (httpVerb == "GET") {
url += "?" + data;
}
var options = {
host: host,
port: 80,
path: url,
method: httpVerb,
headers: requestHeaders(httpVerb, host, data)
};
var req = http.request(options, chunkedResponse);
req.on("error", function(error) {
that.emit("error", error);
});
if (httpVerb == "POST") {
req.write(data);
}
req.end()
}
function buildRequestParams(params) {
var requestParams = that.filterParameters(params, ["signed", "write"]);
requestParams.method = method;
requestParams.api_key = requestParams.api_key || lastfm.api_key;
requestParams.format = requestParams.format || lastfm.format;
if (params.track && typeof params.track === "object") {
requestParams.artist = params.track.artist["#text"];
requestParams.track = params.track.name;
if (params.track.mbid) {
requestParams.mbid = params.track.mbid;
}
if (params.track.album) {
requestParams.album = requestParams.album || params.track.album["#text"];
}
}
if (requiresSignature()) {
requestParams.api_sig = createSignature(requestParams, lastfm.secret);
}
return requestParams;
}
function requiresSignature() {
return params.signed || isWriteRequest() || isSignedMethod(method);
}
function isWriteRequest() {
return params.write || isWriteMethod(method);
}
function isSignedMethod(method) {
return method && _(SIGNED_METHODS).include(method.toLowerCase());
}
function isWriteMethod(method) {
return method && _(WRITE_METHODS).include(method.toLowerCase());
}
function requestHeaders(httpVerb, host, data) {
var headers = {
"User-Agent": lastfm.useragent
};
if (httpVerb == "POST") {
headers["Content-Length"] = data.length;
headers["Content-Type"] = "application/x-www-form-urlencoded";
}
return headers;
}
function chunkedResponse(response) {
var data = "";
response.on("data", function(chunk) {
data += chunk.toString("utf8");
});
response.on("end", function() {
if (lastfm.format !== "json") {
that.emit("success", data);
return;
}
try {
var json = JSON.parse(data);
if (json.error) {
that.emit("error", json);
return;
}
that.emit("success", json);
}
catch(e) {
that.emit("error", e)
}
});
}
function createSignature(params, secret) {
var sig = "";
Object.keys(params).sort().forEach(function(key) {
if (key != "format") {
var value = typeof params[key] !== "undefined" && params[key] !== null ? params[key] : "";
sig += key + value;
}
});
sig += secret;
return crypto.createHash("md5").update(sig, "utf8").digest("hex");
}
};
LastFmRequest.prototype = Object.create(LastFmBase.prototype);

View File

@ -0,0 +1,72 @@
var LastFmBase = require("./lastfm-base");
var LastFmSession = function(lastfm, user, key) {
var that = this;
LastFmBase.call(this);
user = user || "";
key = key || "";
this.user = user;
this.key = key;
this.authorise = function(token, options) {
authorise(token, options);
};
this.isAuthorised = function() {
return isAuthorised();
}
function authorise(token, options) {
options = options || { };
registerEventHandlers(options);
validateToken(token);
}
function registerEventHandlers(options) {
that.registerHandlers(options.handlers);
}
function validateToken(token) {
if (!token) {
that.emit("error", new Error("No token supplied"));
return;
}
var params = { token: token },
request = lastfm.request("auth.getsession", params);
request.on("success", authoriseSession);
request.on("error", bubbleError);
}
function isAuthorised() {
return that.key && that.key !== '';
}
function authoriseSession(result) {
if (!result.session) {
that.emit("error", new Error("Unexpected error"));
return;
}
setSessionDetails(result.session);
that.emit("authorised", that);
}
function setSessionDetails(session) {
that.user = session.name;
that.key = session.key;
}
function bubbleError(error) {
that.emit("error", error);
}
};
LastFmSession.prototype = Object.create(LastFmBase.prototype);
module.exports = LastFmSession;

View File

@ -0,0 +1,104 @@
var _ = require("underscore")
, LastFmBase = require("./lastfm-base")
, retryOnErrors = [
11, // Service offline
16, // Temporarily unavailable
29 // Rate limit exceeded
]
, retrySchedule = [
10 * 1000, // 10 seconds
30 * 1000, // 30 seconds
60 * 1000, // 1 minute
5 * 60 * 1000, // 5 minutes
15 * 60 * 1000, // 15 minutes
30 * 60 * 1000 // 30 minutes
];
var LastFmUpdate = function(lastfm, method, session, options) {
var that = this;
options = options || { };
LastFmBase.call(this);
registerEventHandlers(options);
if (!session.isAuthorised()) {
this.emit("error", {
error: 4,
message: "Authentication failed"
});
return;
}
if (method !== "scrobble" && method !== "nowplaying") {
return;
}
update(method, options);
function registerEventHandlers(options) {
that.registerHandlers(options.handlers);
}
function update(method, options) {
if (method == "scrobble" && !options.timestamp) {
that.emit("error", {
error: 6,
message: "Invalid parameters - Timestamp is required for scrobbling"
});
return;
}
var retryCount = 0
, params = buildRequestParams(options)
, requestMethod = method == "scrobble" ? "track.scrobble" : "track.updateNowPlaying";
makeRequest();
function makeRequest() {
var request = lastfm.request(requestMethod, params);
request.on("error", errorCallback);
request.on("success", successCallback);
}
function successCallback(response) {
if (response) {
that.emit("success", options.track);
}
}
function errorCallback(error) {
if (shouldBeRetried(error)) {
var delay = delayFor(retryCount++)
, retry = {
error: error.error,
message: error.message,
delay: delay
};
that.emit("retrying", retry);
that.scheduleCallback(makeRequest, delay);
return;
}
bubbleError(error);
}
function shouldBeRetried(error) {
return method == "scrobble" && _(retryOnErrors).include(error.error)
}
}
function bubbleError(error) {
that.emit("error", error);
}
function buildRequestParams(params) {
var requestParams = that.filterParameters(params);
requestParams.sk = session.key;
return requestParams;
}
function delayFor(retryCount) {
var index = Math.min(retryCount, retrySchedule.length - 1);
return retrySchedule[index];
}
}
LastFmUpdate.prototype = Object.create(LastFmBase.prototype);
module.exports = LastFmUpdate;

View File

@ -0,0 +1,111 @@
var LastFmBase = require("./lastfm-base");
var RecentTracksStream = module.exports = function(lastfm, user, options) {
var that = this;
LastFmBase.call(this);
options = options || {};
var rate = 10
, isStreaming = false
, timeout
, lastPlay = null
, nowPlaying = null;
registerEventHandlers(options);
if (options.autostart) {
start();
}
this.start = function() {
start();
}
this.stop = function() {
stop();
}
this.isStreaming = function() {
return isStreaming;
}
function registerEventHandlers(options) {
that.registerHandlers(options.handlers);
}
function start() {
isStreaming = true;
check();
function check() {
var request = lastfm.request("user.getrecenttracks", {
user: user,
limit: 1
});
request.on("success", handleSuccess);
request.on("error", bubbleError);
if (isStreaming) {
timeout = that.scheduleCallback(check, rate * 1000);
}
}
function handleSuccess(data) {
if (!data || !data.recenttracks || !data.recenttracks.track) {
that.emit("error", new Error("Unexpected response"));
return;
}
var tracks = data.recenttracks.track;
if (tracks instanceof Array) {
processNowPlaying(tracks[0]);
processLastPlay(tracks[1]);
return;
}
var track = tracks;
if (track["@attr"] && track["@attr"]["nowplaying"]) {
processNowPlaying(track);
return;
}
processLastPlay(track);
if (nowPlaying) {
that.emit("stoppedPlaying", nowPlaying);
nowPlaying = null;
}
}
function bubbleError(error) {
that.emit("error", error);
}
}
function processNowPlaying(track) {
var sameTrack = (nowPlaying && nowPlaying.name == track.name);
if (!sameTrack) {
nowPlaying = track;
that.emit("nowPlaying", track);
}
}
function processLastPlay(track) {
if (!lastPlay) {
lastPlay = track;
that.emit("lastPlayed", track);
return;
}
var sameTrack = (lastPlay.name == track.name);
if (!sameTrack) {
lastPlay = track;
that.emit("scrobbled", track);
}
}
function stop() {
that.cancelCallback(timeout);
isStreaming = false;
}
};
RecentTracksStream.prototype = Object.create(LastFmBase.prototype);