Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

spotify: update sendPong, handle MP3_160_ENC #120

Open
wants to merge 49 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
47f6dcc
new getStreamURL method for track
dcarr622 Dec 30, 2014
d39d8db
fix merge
dcarr622 Dec 30, 2014
984d066
Merge branch 'master' of github.com:TooTallNate/node-spotify-web
dcarr622 Feb 15, 2015
2f98d76
Pass in a Callback function to get URL
peetahzee Feb 16, 2015
338840f
FacebookLogin conveniecne function
peetahzee Feb 22, 2015
4431b43
up version
peetahzee Feb 22, 2015
9fb4d41
update ping pong url
dcarr622 May 15, 2015
44cbbde
log trackuri failure
dcarr622 May 15, 2015
afb6e30
use rootlist instead of publishedrootlist
dcarr622 May 26, 2015
6b865aa
update pingpong
dcarr622 Jun 13, 2015
8f5970a
handle pong response correctly
dcarr622 Jun 13, 2015
d33f528
Merge branch 'master' of https://github.com/TooTallNate/node-spotify-web
dcarr622 Jul 22, 2015
2123c29
Merge branch 'master' of https://github.com/TooTallNate/node-spotify-web
dcarr622 Jul 23, 2015
3535cc0
remove speaker
dcarr622 Jul 23, 2015
b6c7578
Disabled SNI on websocket connections to fix #111
fuzeman Aug 20, 2015
e86b444
Disabled SNI on websocket connections to fix #111
fuzeman Aug 20, 2015
3d98f06
remove extra character
dcarr622 Aug 20, 2015
127eadd
Updated metadata protocol buffer and request version
fuzeman Aug 21, 2015
8b3cecf
Changed client `userAgent`
fuzeman Aug 21, 2015
0ac4a85
Updated track restriction parsing
fuzeman Aug 21, 2015
35c1150
The `Track` get/metadata method didn't work correctly with protobuf.js
fuzeman Aug 21, 2015
06743c7
merge fuzeman fixes
dcarr622 Aug 21, 2015
75fdd0c
Fixed bug with restriction country checks
fuzeman Aug 21, 2015
6b41612
Merge remote-tracking branch 'fuzeman/issue/111' into fuzeman-fixes
dcarr622 Aug 21, 2015
3a394e4
check for res before res.uri
dcarr622 Aug 21, 2015
ecf81ae
don't log null trackuri
dcarr622 Aug 23, 2015
7fc5cad
fix track uri null handling
dcarr622 Aug 23, 2015
3df30fd
correct ping endpoint
dcarr622 Aug 24, 2015
7abe979
fix pong response
dcarr622 Aug 24, 2015
6eac3dc
use rootlist instead of publishedrootlist
dcarr622 Aug 25, 2015
cc98127
Merge pull request #1 from sciencepro/master
longle255 Sep 9, 2015
fb76dc8
fixed ping/pong
romualdr Oct 10, 2015
29afd6e
Add spotify.library
bogsen Nov 25, 2015
793384f
Merge remote-tracking branch 'fuzeman/issue/111'
mattyway Dec 20, 2015
dc74316
Updated metadata proto fields
mattyway Dec 20, 2015
db3c957
Removed optional protobuf package
mattyway Dec 20, 2015
f79dbd9
Merge pull request #1 from Bogdacutu/patch-1
Jan 7, 2016
64fbd36
update sendPong
Jan 8, 2016
9841e21
Merge branch 'master' of https://github.com/theSmallNothing/node-spot…
Jan 9, 2016
c0db30b
Merge pull request #4 from Shigawire/theSmallNothing-master
Jan 9, 2016
0baf32f
Intercept errors if spotify web service is down
Jan 9, 2016
750edcc
add new image key sizes for new metadata protobuf
Jan 12, 2016
f44b5dc
spotify: handle MP3_160_ENC
Apr 30, 2016
3f3de04
spotify: remove unmaintained protobuf dependency
May 1, 2016
e7158c0
Merge branch 'master' of git://github.com/mattyway/node-spotify-web
May 2, 2016
d98214c
Fix for trying to fetch a whole album / playlist at once.
Lordmau5 May 3, 2016
39979fe
Fix library method not being able to fetch saved tracks
Lordmau5 May 3, 2016
adf5c72
Fix indentation
Lordmau5 May 3, 2016
77e5dc5
Merge pull request #1 from Lordmau5/master
May 3, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
login.js
node_modules
?.js
*.mp3
123 changes: 123 additions & 0 deletions lib/crypto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@

/**
* Module dependencies.
*/

var crypto = require("crypto");
var util = require('./util');
var inherits = require('util').inherits;
var Transform = require('stream').Transform;
var debug = require('debug')('spotify-web:crypto');

/**
* Generates stream key based off `album_art` result.
**/

exports.generateStreamKey = function(songKey) {
var param1 = util.hex2bin(songKey);

var swfKey = util.hex2bin("ed02752011affd6290ea42cf73fe0b99");

var keys = ["8cb926e087917795914a339035fa3bc6",
"a3285211c9d1f2364e4237c7b47cc71d",
"72ed1f6317d1923b94ebfd8a3d867c97"];

var finalKey = param1.slice(-16);

for(var i=0,j=0;i<keys.length;i+=1,j+=32) {
var msg = param1.slice(j, j+16);
var correctHmac = param1.slice(j+16, j+32);
var checkHmac = crypto.createHmac("sha1", util.hex2bin(keys[i]))
.update(msg)
.digest("binary");

if (checkHmac.indexOf(correctHmac) == 0) {
var finalHmac = crypto.createHmac("sha1", swfKey)
.update(msg)
.digest("binary");

var result = "";
for(var k=0;k<finalKey.length;k+=1)
result += String.fromCharCode(finalHmac[k].charCodeAt(0) ^ finalKey[k].charCodeAt(0));
return util.bin2hex(result);
}
}
}

/**
* Stream decrypts encrypted MP3_160_ENC files
*
* @param {String} songKey song key given in `trackUri` result
**/

exports.EncryptedStream = function(songKey, opts) {
if (!(this instanceof exports.EncryptedStream)) {
return new exports.EncryptedStream(songKey, opts);
}

if (!opts) opts = {};
opts.objectMode = true;
Transform.call(this, opts);

// required for RC4
this.box = [];
this.x = 0;
this.y = 0;

var key = util.hex2bin(exports.generateStreamKey(songKey));

debug("key(%j)", exports.generateStreamKey(songKey));

// initialize scheduling
for(var i=0;i<256;i+=1)
this.box[i] = i;

this.x = 0;
for(var i=0;i<256;i+=1) {
this.x = (this.x +
this.box[i] +
key.charCodeAt(i % key.length)) % 256;

var t = this.box[this.x];
this.box[this.x] = this.box[i];
this.box[i] = t;
}

// play 4096 pick-up
this.x = 0;
this.y = 0;
for(var i=0;i<4096;i+=1) {
this.x = (this.x + 1) % 256;
this.y = (this.y + this.box[this.x]) % 256;

var t = this.box[this.x];
this.box[this.x] = this.box[this.y];
this.box[this.y] = t;
}

debug([this.x, this.y, this.box]);
}
inherits(exports.EncryptedStream, Transform);

exports.EncryptedStream.prototype._transform = function _transform(obj, encoding, callback) {
try {
debug("_transform(%j)", obj.length);

var res = new Buffer(obj.length);
for(var i=0;i<obj.length;i+=1) {
this.x = (this.x + 1) % 256;
this.y = (this.y + this.box[this.x]) % 256;

var t = this.box[this.x];
this.box[this.x] = this.box[this.y];
this.box[this.y] = t;

res[i] = obj[i] ^ this.box[(this.box[this.x] + this.box[this.y]) % 256];
}

this.push(res);
callback();
} catch(e) {
callback(e);
}
}
93 changes: 33 additions & 60 deletions lib/schemas.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,13 @@ var fs = require('fs');
var path = require('path');
var debug = require('debug')('spotify-web:schemas');

try {
var protobuf = require('protobuf');
var protobufjs = null;
} catch(e) {
var protobuf = null;
var protobufjs = require('protobufjs');
}
var protobufjs = require('protobufjs');

/**
* Protocol Buffer schemas.
*/

var library = (protobuf) ? 'protobuf' : 'protobufjs';
var library = 'protobufjs';

var protoPath = path.resolve(__dirname, '..', 'proto');

Expand Down Expand Up @@ -50,25 +44,15 @@ var loadPackage = function(id) {
}
if (!Array.isArray(mapping)) mapping = [mapping];

if (protobuf) {
// Protobuf works with compiled .desc files rather than .proto files and doesn't support imports
// Therefore, we load in each schema into an array and check each of them when looking for a message object
packageCache[id]= mapping.map(function(schema) {
return new protobuf.Schema(fs.readFileSync(path.resolve(protoPath, schema + '.desc')));
});
return packageCache[id];

} else { // protobufjs
// Generate a proto string with import statements
var proto = mapping.map(function(schema) {
return 'import "' + schema + '.proto";';
}).join('\n');

// Load the generated import file, and return the built package
var builder = protobufjs.protoFromString(proto, new protobufjs.Builder(), {root: protoPath, file: id+'_generated_import.proto'});
packageCache[id] = builder.build("spotify." + id + ".proto");
return packageCache[id];
}
// Generate a proto string with import statements
var proto = mapping.map(function(schema) {
return 'import "' + schema + '.proto";';
}).join('\n');

// Load the generated import file, and return the built package
var builder = protobufjs.protoFromString(proto, new protobufjs.Builder(), {root: protoPath, file: id+'_generated_import.proto'});
packageCache[id] = builder.build("spotify." + id + ".proto");
return packageCache[id];
};

var loadMessage = module.exports.build = function(packageId, messageId) {
Expand All @@ -77,44 +61,33 @@ var loadMessage = module.exports.build = function(packageId, messageId) {
var packageObj = loadPackage(packageId);
var messageObj = null;

if (protobuf) {
var identifier = "spotify." + packageId + ".proto." + messageId;

// Loop though each loaded schema looking for the message
for (var i = 0; i < packageObj.length; i++) {
messageObj = packageObj[i][identifier];
if (messageObj) break;
}

} else { // protobufjs
// Load the message directly
messageObj = packageObj[messageId];
// Load the message directly
messageObj = packageObj[messageId];

// Add wrapper functions
messageObj.parse = function protobufjs_parse_wrapper() {
debug('protobufjs_parse_wrapper(%j)', arguments);
// Add wrapper functions
messageObj.parse = function protobufjs_parse_wrapper() {
debug('protobufjs_parse_wrapper(%j)', arguments);

// Call the message object decode function with the arguments
var message = messageObj.decode.apply(null, arguments);
// Convert the object keys to camel case, ByteBuffers to Node Buffers and then return the parsed object
return convertByteBuffersToNodeBuffers(reCamelCase(message));
}
messageObj.serialize = function protobufjs_serialize_wrapper() {
debug('protobufjs_serialize_wrapper(%j)', arguments);
// Call the message object decode function with the arguments
var message = messageObj.decode.apply(null, arguments);
// Convert the object keys to camel case, ByteBuffers to Node Buffers and then return the parsed object
return convertByteBuffersToNodeBuffers(reCamelCase(message));
}
messageObj.serialize = function protobufjs_serialize_wrapper() {
debug('protobufjs_serialize_wrapper(%j)', arguments);

// Convert any camel cased properties in the arguments to underscored properties
Array.prototype.map.call(arguments, function (argument) {
return deCamelCase(argument);
});
// Convert any camel cased properties in the arguments to underscored properties
Array.prototype.map.call(arguments, function (argument) {
return deCamelCase(argument);
});

// Call the message object constructor with the modified arguments
var message = Object.create(messageObj.prototype);
message = messageObj.apply(message, arguments) || message;
// Call the message object constructor with the modified arguments
var message = Object.create(messageObj.prototype);
message = messageObj.apply(message, arguments) || message;

// Return the node Buffer object containing the serialised data
return message.encodeNB();
};
}
// Return the node Buffer object containing the serialised data
return message.encodeNB();
};

return messageObj;
};
Expand Down
Loading