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