Skip to content

Commit 4440a43

Browse files
committed
Merge pull request #20 from phillipj/fix-xml-parsing
Fix XML parsing and enable HTTPS requests
2 parents 91a004c + 2765464 commit 4440a43

4 files changed

Lines changed: 55 additions & 17 deletions

File tree

lib/api.js

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ var Q = require('q');
66
var xml2js = require('xml2js');
77
var headers = require('plex-api-headers');
88

9+
var xmlToJSON = Q.denodeify(xml2js.parseString);
10+
911
var uri = require('./uri');
1012

1113
var PLEX_SERVER_PORT = 32400;
@@ -35,7 +37,7 @@ function PlexAPI(options, deprecatedPort) {
3537
console.warn('PlexAPI constuctor port argument is deprecated, use an options object instead.');
3638
}
3739

38-
this.serverUrl = 'http://' + hostname + ':' + this.port;
40+
this.serverUrl = hostname + ':' + this.port;
3941
this._initializeAuthenticator();
4042
}
4143

@@ -88,7 +90,7 @@ PlexAPI.prototype.find = function find(relativeUrl, criterias) {
8890
PlexAPI.prototype._request = function _request(relativeUrl, method, parseResponse) {
8991
var self = this;
9092
var deferred = Q.defer();
91-
var reqUrl = generateRelativeUrl.call(this, relativeUrl);
93+
var reqUrl = this._generateRelativeUrl(relativeUrl);
9294
var reqOpts = {
9395
url: url.parse(reqUrl),
9496
encoding: null,
@@ -104,6 +106,9 @@ PlexAPI.prototype._request = function _request(relativeUrl, method, parseRespons
104106
if (err) {
105107
return deferred.reject(err);
106108
}
109+
110+
var resolveValue = body;
111+
107112
if (response.statusCode === 401) {
108113
if (self.authenticator === undefined) {
109114
return deferred.reject(new Error('Plex Server denied request, you must provide a way to authenticate! ' +
@@ -116,6 +121,7 @@ PlexAPI.prototype._request = function _request(relativeUrl, method, parseRespons
116121
})
117122
);
118123
}
124+
119125
if (response.statusCode !== 200) {
120126
return deferred.reject(new Error('Plex Server didnt respond with status code 200, response code: ' + response.statusCode));
121127
}
@@ -124,19 +130,17 @@ PlexAPI.prototype._request = function _request(relativeUrl, method, parseRespons
124130
// releasing socket back to the agent connection pool: http://nodejs.org/api/http.html#http_agent_maxsockets
125131
response.on('data', function onData() {});
126132

127-
if (parseResponse) {
128-
if (response.headers['content-type'] === 'application/json') {
129-
return deferred.resolve(JSON.parse(body.toString('utf8')));
130-
}
131-
if (response.headers['content-type'].indexOf('xml') > -1) {
132-
return deferred.resolve(xml2js.parseString(body.toString('utf8'), {
133-
object: true
134-
}));
135-
}
136-
return deferred.resolve(body);
137-
} else {
133+
if (!parseResponse) {
138134
return deferred.resolve();
139135
}
136+
137+
if (response.headers['content-type'] === 'application/json') {
138+
resolveValue = JSON.parse(body.toString('utf8'));
139+
} else if (response.headers['content-type'].indexOf('xml') > -1) {
140+
resolveValue = xmlToJSON(body.toString('utf8'), { attrkey: 'attributes' });
141+
}
142+
143+
return deferred.resolve(resolveValue);
140144
});
141145

142146
return deferred.promise;
@@ -179,6 +183,14 @@ PlexAPI.prototype._initializeAuthenticator = function _initializeAuthenticator()
179183
}
180184
};
181185

186+
PlexAPI.prototype._generateRelativeUrl = function _generateRelativeUrl(relativeUrl) {
187+
return this._serverScheme() + this.serverUrl + relativeUrl;
188+
};
189+
190+
PlexAPI.prototype._serverScheme = function _serverScheme() {
191+
return this.port === 443 ? 'https://' : 'http://';
192+
};
193+
182194
function filterChildrenByCriterias(children, criterias) {
183195
var context = {
184196
criterias: criterias || {}
@@ -196,8 +208,5 @@ function criteriasMatchChild(child) {
196208
}, true);
197209
}
198210

199-
function generateRelativeUrl(relativeUrl) {
200-
return this.serverUrl + relativeUrl;
201-
}
202211

203212
module.exports = PlexAPI;

test/query-test.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,25 @@ describe('query()', function() {
8989
});
9090
});
9191
});
92+
93+
describe('XML responses', function() {
94+
it('should convert XML to a JSON object', function() {
95+
var plexTvApi = new PlexAPI({
96+
hostname: 'plex.tv',
97+
port: 443
98+
});
99+
100+
server.stop();
101+
server.start({
102+
schemeAndHost: 'https://plex.tv',
103+
port: 443,
104+
contentType: 'application/xml'
105+
});
106+
107+
return plexTvApi.query('/devices.xml').then(function(result) {
108+
expect(result.MediaContainer).to.be.an('object');
109+
expect(result.MediaContainer.attributes.publicAddress).to.equal('47.1.2.4');
110+
});
111+
});
112+
});
92113
});

test/samples/devices.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<MediaContainer publicAddress="47.1.2.4">
3+
<Device name="Node.js App" publicAddress="46.1.2.3" product="Node.js App" productVersion="1.0" platform="Node.js" platformVersion="v3.0.0" device="darwin" model="" vendor="" provides="controller" clientIdentifier="758b6938-907052f04d99" version="1.0" id="33847205" token="kiYsW" createdAt="1440669823" lastSeenAt="1440669824" screenResolution="" screenDensity="">
4+
</Device>
5+
<Device name="Yet another Node.js App" publicAddress="46.1.2.4" product="Node.js App" productVersion="1.0" platform="Node.js" platformVersion="v3.0.0" device="darwin" model="" vendor="" provides="controller" clientIdentifier="2d404e6b-4286-da65748a1ed4" version="1.0" id="33847195" token="ofaq" createdAt="1440669789" lastSeenAt="1440669790" screenResolution="" screenDensity="">
6+
</Device>
7+
</MediaContainer>

test/server.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ function replaceActualPathToRoot(path) {
3535
module.exports = {
3636
start: function start(options) {
3737
options = options || {};
38+
options.schemeAndHost = options.schemeAndHost || 'http://localhost';
3839
options.port = options.port || PLEX_SERVER_PORT;
3940
options.contentType = options.contentType || 'application/json';
4041
respondWith = 'content';
4142

42-
var scope = nock('http://localhost:' + options.port, {
43+
var scope = nock(options.schemeAndHost + ':' + options.port, {
4344
reqheaders: options.reqheaders
4445
})
4546
.defaultReplyHeaders({

0 commit comments

Comments
 (0)