mStream/modules/scribble.js
2018-04-05 00:37:08 -04:00

406 lines
12 KiB
JavaScript

// Scrobbler code shamelessly stolen from
// https://github.com/dittodhole/node-scribble-js
/**/// GLOBALS
var http = require('http')
, crypto = require('crypto')
, querystring = require('querystring')
/**/// Public: Scribble
/**///
/**/// Args
/**/// api_key - Last.fm API account key
/**/// api_secret - Last.fm API secret
/**/// username - Last.fm account
/**/// password - Last.fm password
/**///
/**/// Returns
/**/// return - A scribble
var Scribble = function (api_key, api_secret, username, password) {
this.apiKey = api_key
this.apiSecret = api_secret
this.username = username
this.password = password
this.sessionKey = null
this.users = {}
}
Scribble.prototype.addUser = function (username, password) {
this.users[username] = {
password: password,
sessionKey: null
}
}
/**/// Public: Love
/**///
/**/// Args
/**/// song - song object. artist, track keys
Scribble.prototype.Love = function (song, username, callback) {
var self = this
if (self.users[username].sessionKey == null) {
self.MakeSession(username, function (sk) {
postLove(self, song, sk, username, callback)
})
} else {
postLove(self, song, self.users[username].sessionKey, username, callback)
}
}
/**/// Public: Scrobble
/**///
/**/// Args
/**/// song - song object. artist, track keys
Scribble.prototype.Scrobble = function (song, username, callback) {
var self = this
if (self.users[username].sessionKey == null) {
self.MakeSession(username, function (sk) {
postScrobble(self, song, sk, username, callback)
})
} else {
postScrobble(self, song, self.users[username].sessionKey, username, callback)
}
}
/**/// Public: NowPlaying
/**///
/**/// Args
/**/// song - song object. artist, track keys
Scribble.prototype.NowPlaying = function (song, username, callback) {
var self = this
if (self.users[username].sessionKey == null) {
self.MakeSession(username, function (sk) {
postNowPlaying(self, song, sk, username, callback)
})
} else {
postNowPlaying(self, song, self.users[username].sessionKey, username, callback)
}
}
/**/// Public: MakeSession
/**///
/**/// Args
/**/// callback - optional callback function
/**///
/**/// Returns
/**/// return - a session key and optional callback
Scribble.prototype.MakeSession = function (username, callback) {
var self = this
var password = this.users[username].password;
var token = makeHash(username + makeHash(password))
, apiSig = makeHash('api_key' + this.apiKey + 'authToken' + token + 'methodauth.getMobileSessionusername' + username + this.apiSecret)
, path = '/2.0/?method=auth.getMobileSession&' +
'username=' + username +
'&authToken=' + token +
'&api_key=' + this.apiKey +
'&api_sig=' + apiSig + '&format=json'
sendGet(path, function (ret) {
self.users[username].sessionKey = ret.session.key
if (typeof (callback) == 'function') {
callback(ret.session.key)
}
})
}
/**/// Public: GetArtistInfo
/**///
/**/// Args
/**/// artist - artist string
/**/// callback - callback function
/**///
/**/// Returns
/**/// return - object of artist summary
Scribble.prototype.GetArtistInfo = function (artist, callback) {
var path = '/2.0/?method=artist.getInfo&artist=' + artist + '&api_key=' + this.apiKey + '&format=json'
sendGet(path, function (ret) {
if (typeof (callback) == 'function')
callback(ret)
})
}
/**/// Public: GetSimilarArtists
/**///
/**/// Args
/**/// artist - artist string
/**/// callback - callback function
/**/// amt - optional amount of returns
/**///
/**/// Returns
/**/// return - object of similar artists
Scribble.prototype.GetSimilarArtists = function (artist, callback, amt) {
var amt = amt || 50
, path = '/2.0/?method=artist.getSimilar&artist=' + artist + '&api_key=' + this.apiKey + '&format=json&limit=' + amt
sendGet(path, function (ret) {
if (typeof (callback) == 'function')
callback(ret)
})
}
/**/// Public: GetArtistEvents
/**///
/**/// Args
/**/// song - song object. artist, track keys
/**/// callback - callback function
/**/// amt - optional amount of returns
/**///
/**/// Returns
/**/// return - object of artist events
Scribble.prototype.GetArtistEvents = function (artist, callback, amt) {
var amt = amt || 50
, path = '/2.0/?method=artist.getevents&artist=' + artist + '&api_key=' + this.apiKey + '&format=json&limit=' + amt
sendGet(path, function (ret) {
if (typeof (callback) == 'function')
callback(ret)
})
}
/**/// Public: GetArtistTopAlbums
/**///
/**/// Args
/**/// song - song object. artist, track keys
/**/// callback - callback function
/**/// amt - optional amount of returns
/**///
/**/// Returns
/**/// return - object of artist top albums
Scribble.prototype.GetArtistTopAlbums = function (artist, callback, amt) {
var amt = amt || 50
, path = '/2.0/?method=artist.gettopalbums&artist=' + artist + '&api_key=' + this.apiKey + '&format=json&limit=' + amt
sendGet(path, function (ret) {
if (typeof (callback) == 'function')
callback(ret)
})
}
/**/// Public: GetArtistTopTracks
/**///
/**/// Args
/**/// song - song object. artist, track keys
/**/// callback - callback function
/**/// amt - optional amount of returns
/**///
/**/// Returns
/**/// return - object of artist top tracks
Scribble.prototype.GetArtistTopTracks = function (artist, callback, amt) {
var amt = amt || 50
, path = '/2.0/?method=artist.gettoptracks&artist=' + artist + '&api_key=' + this.apiKey + '&format=json&limit=' + amt
sendGet(path, function (ret) {
if (typeof (callback) == 'function')
callback(ret)
})
}
/**/// Public: GetSimilarSongs
/**///
/**/// Args
/**/// song - song object. artist, track keys
/**/// callback - callback function
/**/// amt - optional amount of returns
/**///
/**/// Returns
/**/// return - object of similar songs
Scribble.prototype.GetSimilarSongs = function (song, callback, amt) {
var amt = amt || 50
, path = '/2.0/?method=track.getSimilar&artist=' + song.artist + '&track=' + song.track + '&api_key=' + this.apiKey + '&format=json&limit=' + amt
sendGet(path, function (ret) {
if (typeof (callback) == 'function')
callback(ret)
})
}
/**/// Public: GetTrackInfo
/**///
/**/// Args
/**/// song - song object. artist, track keys
/**/// callback - callback function
/**///
/**/// Returns
/**/// return - object of track info
Scribble.prototype.GetTrackInfo = function (song, callback) {
var path = '/2.0/?method=track.getInfo&api_key=' + this.apiKey + '&artist=' + encodeURIComponent(song.artist) + '&track=' + encodeURIComponent(song.track) + '&format=json'
sendGet(path, function (ret) {
if (typeof (callback) == 'function')
callback(ret)
})
}
/**/// Public: GetAlbumInfo
/**///
/**/// Args
/**/// song - song object. artist, track, album keys
/**/// callback - callback function
/**///
/**/// Returns
/**/// return - object of album information
Scribble.prototype.GetAlbumInfo = function (song, callback) {
song.album = song.album.replace(/\s/g, '%20')
var path = '2.0/?method=album.getinfo&api_key=' + this.apiKey + '&artist=' + song.artist + '&album=' + song.album + '&format=json'
sendGet(path, function (ret) {
if (typeof (callback) == 'function')
callback(ret)
})
}
/**/// Private: postLove
/**///
/**/// Args
/**/// self - your Scribble object
/**/// song - song object. artist, track keys
/**/// sk - optional session key
/**/// callback - callback function
/**///
/**/// Notes
/**/// note - Build and send love request
function postLove(self, song, sk, username, callback) {
if (sk && self.users[username].sessionKey == null) {
self.users[username].sessionKey = sk
}
var apiSig = makeHash('album' + (song.album || '_') + 'api_key' + self.apiKey + 'artist' + song.artist + 'methodtrack.lovesk' + self.users[username].sessionKey + 'track' + song.track + self.apiSecret)
, post_data = querystring.stringify({
method: 'track.love',
api_key: self.apiKey,
sk: self.users[username].sessionKey,
api_sig: apiSig,
artist: song.artist,
track: song.track,
album: song.album || '_'
})
sendPost(post_data, callback)
}
/**/// Private: postNowPlaying
/**///
/**/// Args
/**/// self - your Scribble object
/**/// song - song object. artist, track keys
/**/// sk - optional session key
/**/// callback - callback function
/**///
/**/// Notes
/**/// note - Build and send now playing request
function postNowPlaying(self, song, sk, username, callback) {
if (sk && self.users[username].sessionKey == null) {
self.users[username].sessionKey = sk
}
var dur = (song.duration) ? 'duration' + song.duration : ''
, apiSig = makeHash('album' + (song.album || '_') + 'api_key' + self.apiKey + 'artist' + song.artist + dur + 'methodtrack.updateNowPlayingsk' + self.users[username].sessionKey + 'track' + song.track + self.apiSecret)
, post_data = querystring.stringify({
method: 'track.updateNowPlaying',
artist: song.artist,
track: song.track,
album: song.album || '_',
duration: song.duration,
api_key: self.apiKey,
api_sig: apiSig,
sk: self.users[username].sessionKey
})
sendPost(post_data, callback)
}
/**/// Private: postScrobble
/**///
/**/// Args
/**/// self - your Scribble object
/**/// song - song object. artist, track keys
/**/// sk - optional session key
/**/// callback - callback function
/**///
/**/// Notes
/**/// note - Build and send scrobble request
function postScrobble(self, song, sk, username, callback) {
if (sk && self.users[username].sessionKey == null) {
self.users[username].sessionKey = sk
}
var now = new Date().getTime()
, timestamp = Math.floor(now / 1000)
, apiSig = makeHash('album' + (song.album || '_') + 'api_key' + self.apiKey + 'artist' + song.artist + 'methodtrack.scrobblesk' + self.users[username].sessionKey + 'timestamp' + timestamp + 'track' + song.track + self.apiSecret)
, post_data = querystring.stringify({
method: 'track.scrobble',
api_key: self.apiKey,
sk: self.users[username].sessionKey,
api_sig: apiSig,
timestamp: timestamp,
artist: song.artist,
track: song.track,
album: song.album || '_'
})
sendPost(post_data, callback)
}
/**/// Private: sendPost
/**///
/**/// Args
/**/// data - POST data object
/**/// callback - callback function
/**///
/**/// Returns
/**/// console - POST response from API
/**///
/**/// Notes
/**/// note - Send POST requests to Last.fm
function sendPost(data, callback) {
var options = {
host: 'ws.audioscrobbler.com',
path: '/2.0/',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': data.length
}
}
, doPOST = http.request(options, function (request) {
var reqReturn = ''
request.setEncoding('utf8')
request.on('data', function (chunk) {
reqReturn += chunk
})
request.on('end', function () {
if (typeof (callback) == 'function')
callback(reqReturn)
})
}).on('error', function (err) {
// TODO
})
doPOST.write(data)
doPOST.end()
}
/**/// Public: sendGet
/**///
/**/// Args
/**/// path - html path for API call
/**/// callback - callback function
/**///
/**/// Returns
/**/// return - callback function with return value from API call
function sendGet(path, callback) {
var response = ''
, apiCall = {
host: 'ws.audioscrobbler.com',
port: 80,
path: path
}
http.get(apiCall, function (res) {
res.on('data', function (chunk) {
try {
response += chunk
} catch (err) {
// TODO
}
})
res.on('end', function () {
try {
var ret = JSON.parse(response)
//var ret = response
if (typeof (callback) == 'function')
callback(ret)
} catch (err) {
// TODO
console.log(err)
console.log('[INVALID RETURN] the return was invalid JSON: ' + err)
}
})
}).on('error', function (err) {
console.log(err.message)
})
}
/**/// Private: makeHash
/**///
/**/// Args
/**/// input - string input to hash
/**///
/**/// Returns
/**/// return - md5 hash of the input string
function makeHash(input) {
return crypto.createHash('md5').update(input, 'utf8').digest("hex")
}
module.exports = Scribble