Last active
April 11, 2016 05:33
-
-
Save aylarov/ee7f509ca182e8437b2abed9d415f2f1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require(Modules.Player); | |
require(Modules.Conference); | |
(function () { | |
var keys = [{ | |
username: "android", | |
password: "AC7IBG09A3DTSYM4R41UJWL07VLN8JI7", | |
deviceId: "android-generic", | |
decrypt: "R=U!LH$O2B#", | |
encrypt: "6#26FRL$ZWD" | |
}, { | |
username: "iphone", | |
password: "P2E4FC0EAD3*878N92B2CDp34I0B1@388137C", | |
deviceId: "IP01", | |
decrypt: "20zE1E47BE57$51", | |
encrypt: "721^26xE22776" | |
}, { | |
username: "palm", | |
password: "IUC7IBG09A3JTSYM4N11UJWL07VLH8JP0", | |
deviceId: "pre", | |
decrypt: "E#U$MY$O2B=", | |
encrypt: "%526CBL$ZU3" | |
}, { | |
username: "winmo", | |
password: "ED227E10a628EB0E8Pm825Dw7114AC39", | |
deviceId: "VERIZON_MOTOQ9C", | |
decrypt: "7D671jt0C5E5d251", | |
encrypt: "v93C8C2s12E0EBD" | |
}]; | |
/** | |
* Pandora API | |
* As described at https://6xq.net/pandora-apidoc/ | |
*/ | |
Pandora = function () { | |
var pandoraSecureURL = "https://tuner.pandora.com/services/json/"; | |
var pandoraURL = "http://tuner.pandora.com/services/json/"; | |
var syncTime, partnerId, partnerAuthToken, userId, userAuthToken, userPPID; | |
this.userId = function () { | |
return userId; | |
}; | |
this.unixTs = function () { | |
return (Math.floor(Date.now() / 1000)); | |
}; | |
this.calculateSyncTime = function (time) { | |
syncTime = blowfish.decrypt(time, keys[0].decrypt, { | |
cipherMode: 0, | |
outputType: 1 | |
}); | |
syncTime = syncTime.slice(4); | |
//which we parse into a number | |
syncTime = parseInt(syncTime, 10); | |
//and we derive an offset to use in the future | |
syncTime = this.unixTs() - syncTime; | |
}; | |
this.httpRequest = function (url, options, secure) { | |
return new Promise(function (resolve, reject) { | |
url = (secure === false ? pandoraURL : pandoraSecureURL) + url; | |
Logger.write("Requesting URL: " + url); | |
Net.httpRequest(url, function (res) { | |
if (res.code == 200) { | |
resolve(res); | |
} else { | |
reject(res); | |
} | |
}, options); | |
}); | |
}; | |
/** | |
* Partner login required before user login | |
*/ | |
this.partnerLogin = function () { | |
return new Promise(function (resolve, reject) { | |
var data = { | |
username: keys[0].username, | |
password: keys[0].password, | |
deviceModel: keys[0].deviceId, | |
version: "5", | |
includeUrls: true | |
}; | |
var opts = new Net.HttpRequestOptions(); | |
opts.method = "POST"; | |
opts.postData = JSON.stringify(data); | |
opts.headers = [ | |
"User-Agent: vox", | |
"Content-Type: text/plain" | |
]; | |
this.httpRequest("?method=auth.partnerLogin", opts).then(function (res) { | |
try { | |
var result = JSON.parse(res.text); | |
//Logger.write(JSON.stringify(res.text, null, 2)); | |
if (result.stat != "ok") reject(); | |
syncTime = result.result.syncTime; | |
this.calculateSyncTime(syncTime); | |
Logger.write("offset is " + syncTime); | |
partnerId = result.result.partnerId; | |
partnerAuthToken = result.result.partnerAuthToken; | |
resolve(result.result); | |
} catch (e) { | |
reject(); | |
} | |
}.bind(this)).catch(function (err) { | |
reject(err); | |
}); | |
}.bind(this)); | |
}; | |
this.login = function (username, password) { | |
return new Promise(function (resolve, reject) { | |
this.partnerLogin().then(function (res) { | |
var data = { | |
"loginType": "user", | |
"username": username, | |
"password": password, | |
"partnerAuthToken": partnerAuthToken, | |
"syncTime": this.unixTs() + syncTime | |
}; | |
var opts = new Net.HttpRequestOptions(); | |
opts.method = "POST"; | |
opts.postData = blowfish.encrypt(JSON.stringify(data), keys[0].encrypt, { | |
cipherMode: 0, | |
outputType: 1 | |
}); | |
opts.headers = [ | |
"User-Agent: vox", | |
"Content-Type: text/plain" | |
]; | |
Logger.write("POST DATA: " + opts.postData); | |
this.httpRequest("?method=auth.userLogin&auth_token=" + encodeURIComponent(partnerAuthToken) + "&partner_id=" + partnerId, | |
opts) | |
.then(function (res) { | |
var result = JSON.parse(res.text); | |
Logger.write(JSON.stringify(res.text, null, 2)); | |
if (result.stat != "ok") reject(JSON.stringify(result, null, 2)); | |
else { | |
userId = result.result.userId; | |
userAuthToken = result.result.userAuthToken; | |
userPPID = result.result.userPPID; | |
resolve(result.result); | |
} | |
}).catch(function (e) { | |
Logger.write("http request error: " + JSON.stringify(e)); | |
reject(e); | |
}); | |
}.bind(this)).catch(function (err) { | |
reject(); | |
}); | |
}.bind(this)); | |
}; | |
this.callPandoraMethod = function (method, data) { | |
return new Promise(function (resolve, reject) { | |
data.userAuthToken = userAuthToken; | |
data.syncTime = this.unixTs() + syncTime; | |
var opts = new Net.HttpRequestOptions(); | |
opts.method = "POST"; | |
opts.postData = blowfish.encrypt(JSON.stringify(data), keys[0].encrypt, { | |
cipherMode: 0, | |
outputType: 1 | |
}); | |
opts.headers = [ | |
"User-Agent: vox", | |
"Content-Type: text/plain" | |
]; | |
this.httpRequest("?method=" + method + "&auth_token=" + encodeURIComponent(userAuthToken) + "&partner_id=" + partnerId + "&user_id=" + userId, opts, false) | |
.then(function (res) { | |
try { | |
var result = JSON.parse(res.text); | |
//Logger.write(JSON.stringify(res.text, null, 2)); | |
if (result.stat != "ok") reject(result); | |
else resolve(result.result); | |
} catch (e) { | |
reject(e); | |
} | |
}.bind(this)) | |
.catch(function (err) { | |
reject(err); | |
}); | |
}.bind(this)); | |
}; | |
}; | |
})(); | |
function getPlaylist(stationToken) { | |
for (var i = 0; i < calls.length; i++) { | |
calls[i].sendMessage(JSON.stringify({ | |
status: "loading_playlist" | |
})); | |
} | |
p.callPandoraMethod("station.getPlaylist", { | |
"stationToken": stationToken, | |
"additionalAudioUrl": "HTTP_128_MP3" | |
}).then(function (res) { | |
//Logger.write(JSON.stringify(res, null, 2)); | |
tracks = []; | |
for (var i = 0; i < res.items.length; i++) { | |
if (typeof res.items[i].songName != "undefined") | |
tracks.push({ | |
songName: res.items[i].songName, | |
artistName: res.items[i].artistName, | |
albumName: res.items[i].albumName, | |
trackToken: res.items[i].trackToken, | |
trackURL: res.items[0].additionalAudioUrl | |
}); | |
} | |
trackToken = res.items[0].trackToken; | |
for (var i = 0; i < calls.length; i++) { | |
calls[i].sendMessage(JSON.stringify({ | |
status: "playback", | |
tracks: [ tracks[0] ], | |
trackToken: trackToken, | |
stationToken: stationToken | |
})); | |
} | |
//call.startPlayback(res.items[0].additionalAudioUrl); | |
if (typeof player != "undefined") player.stop(); | |
player = VoxEngine.createURLPlayer(res.items[0].additionalAudioUrl); | |
player.sendMediaTo(hdconf); | |
player.addEventListener(PlayerEvents.PlaybackFinished, function (e) { | |
getPlaylist(stationToken); | |
}); | |
}).catch(function (err) { | |
VoxEngine.terminate(); | |
}); | |
} | |
var call, calls = [], | |
tracks = [], | |
trackToken, | |
p, stations = [], | |
stationToken, player, | |
hdconf, | |
PANDORA_USERNAME = "", | |
PANDORA_PASSWORD = ""; | |
VoxEngine.addEventListener(AppEvents.CallAlerting, function (e) { | |
Logger.write("Inbound call"); | |
e.call.addEventListener(CallEvents.Connected, function (e) { | |
calls.push(e.call); | |
for (var i = 0; i < calls.length; i++) { | |
calls[i].sendMessage(JSON.stringify({ | |
status: 'listeners_info', | |
listenersCount: calls.length | |
})); | |
} | |
if (typeof hdconf == "undefined") { | |
hdconf = VoxEngine.createConference({ | |
hd_audio: true | |
}); | |
hdconf.sendMediaTo(e.call); | |
e.call.sendMessage(JSON.stringify({ | |
status: "auth" | |
})); | |
} else { | |
if (stations.length !== 0) { | |
e.call.sendMessage(JSON.stringify({ | |
status: "stations_list", | |
stations: stations, | |
stationToken: stationToken | |
})); | |
} | |
if (tracks.length !== 0) { | |
e.call.sendMessage(JSON.stringify({ | |
status: "playback", | |
tracks: [ tracks[0] ], | |
trackToken: trackToken, | |
stationToken: stationToken | |
})); | |
} | |
hdconf.sendMediaTo(e.call); | |
} | |
}); | |
e.call.addEventListener(CallEvents.Disconnected, function (e) { | |
for (var i = 0; i < calls.length; i++) { | |
calls[i].sendMessage(JSON.stringify({ | |
status: 'listeners_info', | |
listenersCount: calls.length | |
})); | |
} | |
var index = calls.indexOf(e.call); | |
if (index > -1) { | |
calls.splice(index, 1); | |
} | |
if (calls.length === 0) VoxEngine.terminate(); | |
}); | |
e.call.addEventListener(CallEvents.MessageReceived, function (e) { | |
try { | |
var result = JSON.parse(e.text); | |
if (result.action == "getStationList") { | |
p.callPandoraMethod("user.getStationList", {}).then(function (res) { | |
for (var i = 0; i < res.stations.length; i++) { | |
if (typeof res.stations[i].stationName != "undefined") stations.push({ | |
stationName: res.stations[i].stationName, | |
stationToken: res.stations[i].stationToken | |
}); | |
} | |
e.call.sendMessage(JSON.stringify({ | |
status: "stations_list", | |
stations: stations | |
})); | |
}).catch(function (err) { | |
Logger.write("getStationList failed"); | |
Logger.write(JSON.stringify(err, null, 2)); | |
VoxEngine.terminate(); | |
}); | |
} else if (result.action == "login") { | |
p = new Pandora(); | |
p.login(PANDORA_USERNAME, PANDORA_PASSWORD).then(function (res) { | |
Logger.write("Authorized successfully. User ID: " + p.userId()); | |
e.call.sendMessage(JSON.stringify({ | |
status: "loading_stations" | |
})); | |
}).catch(function (e) { | |
Logger.write("Login failed"); | |
VoxEngine.terminate(); | |
}); | |
} else if (result.action == "getPlaylist") { | |
stationToken = result.stationToken; | |
getPlaylist(result.stationToken); | |
} else if (result.action == "callUser") { | |
var outbound_call = VoxEngine.callUser(result.username, "Pandora"); | |
outbound_call.addEventListener(CallEvents.Connected, function (e) { | |
e.call.sendMessage(JSON.stringify({ | |
status: "user_call_connected" | |
})); | |
hdconf.sendMediaTo(outbound_call); | |
}); | |
outbound_call.addEventListener(CallEvents.Failed, function (e) { | |
e.call.sendMessage(JSON.stringify({ | |
status: "user_call_failed" | |
})); | |
}); | |
outbound_call.addEventListener(CallEvents.Disconnected, function (e) { | |
e.call.sendMessage(JSON.stringify({ | |
status: "user_call_disconnected" | |
})); | |
}); | |
} | |
} catch (e) { | |
Logger.write(JSON.stringify(e)); | |
} | |
}); | |
/*call.addEventListener(CallEvents.PlaybackFinished, function (e) { | |
getPlaylist(stationToken); | |
});*/ | |
e.call.answer(); | |
}); | |
VoxEngine.addEventListener(AppEvents.Started, function (e) { | |
Logger.write("Application started"); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment