├── .gitignore ├── index.js ├── test ├── mocha.opts └── test.js ├── .travis.yml ├── LICENSE ├── package.json ├── README.md └── lib └── musicbrainz.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | exports = require('./lib/musicbrainz'); 2 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require chai 2 | --reporter spec 3 | --ui bdd 4 | --timeout 10000 5 | --recursive 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.11" 4 | - "0.10" 5 | - "0.8" 6 | - "0.6" 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2011 Max Kueng (http://maxkueng.com/) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "musicbrainz", 3 | "description": "A MusicBrainz XML Web Service Version 2 client", 4 | "keywords": [ 5 | "musicbrainz", 6 | "metadata", 7 | "mp3", 8 | "flac", 9 | "ogg", 10 | "music", 11 | "tagging", 12 | "api" 13 | ], 14 | "version": "0.2.7", 15 | "homepage": "http://maxkueng.com/node-musicbrainz", 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/maxkueng/node-musicbrainz.git" 19 | }, 20 | "author": "Max Kueng (http://maxkueng.com/)", 21 | "contributors": [ 22 | "Alex Ehrnschwender (https://github.com/alexanderscott)", 23 | "Sam (https://github.com/samcday)", 24 | "Marco Godoy (https://github.com/ghostnumber7)" 25 | ], 26 | "directories": { 27 | "lib": "./lib" 28 | }, 29 | "dependencies": { 30 | "request": "^2.88.2", 31 | "timetrickle": "~0.1.1", 32 | "xml2js": "0.1.x" 33 | }, 34 | "devDependencies": { 35 | "mocha": "~1.15.1", 36 | "chai": "~1.8.1" 37 | }, 38 | "scripts": { 39 | "test": "node_modules/.bin/mocha" 40 | }, 41 | "engines": [ 42 | "node >=0.4.0" 43 | ], 44 | "main": "./lib/musicbrainz", 45 | "licenses": [ 46 | { 47 | "type": "MIT", 48 | "url": "https://github.com/maxkueng/node-musicbrainz/raw/master/LICENSE" 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MusicBrainz API for node.js 2 | =========================== 3 | 4 | [![Build Status](https://secure.travis-ci.org/maxkueng/node-musicbrainz.png?branch=master)](http://travis-ci.org/maxkueng/node-musicbrainz) 5 | 6 | This is a [MusicBrainz][mb] [XML Web Service Version 2][mbwsv2] client written in JavaScript for [node.js][node]. 7 | 8 | It's a work in progress. Currently supports lookups with an MBID or DiscId, as well as searches for artist, release, or recording. 9 | 10 | ### Contribute 11 | 12 | Want to help make node-musicbrainz better or fix a bug? Please read [How To Contribute][contribute] in the wiki. 13 | 14 | Supported resources 15 | ------------------- 16 | 17 | - DiscId 18 | - Artist (recordings, releases, release-groups, works) 19 | - Label (releases) 20 | - Recording (artists, releases) 21 | - Release (artists, labels, recordings, release-groups) 22 | - Release Group (artists, releases) 23 | - Work 24 | 25 | Lookup Examples 26 | --------------- 27 | 28 | ```javascript 29 | var mb = require('musicbrainz'); 30 | ``` 31 | 32 | Looking up a release with linked artists: 33 | 34 | ```javascript 35 | mb.lookupRelease('283821f3-522a-45ca-a669-d74d0b4fb93a', ['artists'], function (error, release) { 36 | console.log(release); 37 | }); 38 | ``` 39 | 40 | The same but different: 41 | 42 | ```javascript 43 | var Release = mb.Release; 44 | 45 | var release = new Release('283821f3-522a-45ca-a669-d74d0b4fb93a'); 46 | release.load(['artists'], function () { 47 | console.log(release); 48 | }); 49 | ``` 50 | 51 | Search Examples 52 | --------------- 53 | 54 | ```javascript 55 | mb.searchArtists('The White Stripes', {}, function(err, artists){ 56 | console.log(artists); 57 | }); 58 | mb.searchRecordings('Seven Nation Army', { artist: 'The White Stripes' }, function(err, recordings){ 59 | console.log(recordings); 60 | }); 61 | mb.searchReleases('Elephant', { country: 'US' }, function(err, releases){ 62 | console.log(releases); 63 | }); 64 | ``` 65 | 66 | Configuration 67 | ------------- 68 | 69 | If you run your own MusicBrainz server you can set a custom `baseURI` 70 | and and rate limit options. In the following example the API endpoint is 71 | "http://myMusicBrainzServer.org/ws/2/" and the rate limit allows 5 72 | requests per 2 seconds. 73 | 74 | ```javascript 75 | var mb = require('musicbrainz'); 76 | mb.configure({ 77 | baseURI: 'http://myMusicBrainzServer.org/ws/2/', 78 | rateLimit: { 79 | requests: 5, 80 | interval: 2000 81 | } 82 | }); 83 | ``` 84 | 85 | Caching Lookups with Redis 86 | -------------------------- 87 | 88 | ```javascript 89 | var mb = require('musicbrainz'); 90 | var redis = require('redis'); 91 | 92 | mb.lookupCache = function (uri, force, callback, lookup) { 93 | var key = 'lookup:' + uri; 94 | var r = redis.createClient(); 95 | 96 | r.on('connect', function () { 97 | r.get(key, function (err, reply) { 98 | if (!err && reply) { 99 | callback(null, JSON.parse(reply)); 100 | 101 | } else { 102 | lookup(function (err, resource) { 103 | callback(err, resource); 104 | 105 | if (err) { r.quit(); return; } 106 | 107 | r.set(key, JSON.stringify(resource), function (err, reply) { 108 | r.quit(); 109 | }); 110 | 111 | }); 112 | } 113 | }); 114 | }); 115 | }; 116 | ``` 117 | 118 | Contributors 119 | ------------ 120 | 121 | - Max Kueng (http://maxkueng.com/) 122 | - Alex Ehrnschwender (http://alexehrnschwender.com/) 123 | - Sam (https://github.com/samcday) 124 | - Marco Godoy (https://github.com/ghostnumber7) 125 | 126 | License 127 | ------- 128 | 129 | MIT License 130 | 131 | Copyright (c) 2011 Max Kueng (http://maxkueng.com/) 132 | 133 | Permission is hereby granted, free of charge, to any person obtaining 134 | a copy of this software and associated documentation files (the 135 | "Software"), to deal in the Software without restriction, including 136 | without limitation the rights to use, copy, modify, merge, publish, 137 | distribute, sublicense, and/or sell copies of the Software, and to 138 | permit persons to whom the Software is furnished to do so, subject to 139 | the following conditions: 140 | 141 | The above copyright notice and this permission notice shall be 142 | included in all copies or substantial portions of the Software. 143 | 144 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 145 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 146 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 147 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 148 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 149 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 150 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 151 | 152 | 153 | [node]: http://nodejs.org/ 154 | [mb]: http://musicbrainz.org/ 155 | [mbwsv2]: http://musicbrainz.org/doc/XML_Web_Service/Version_2 156 | [contribute]: https://github.com/maxkueng/node-musicbrainz/wiki/Contribute 157 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var expect = require('chai').expect, 4 | mb = require('../lib/musicbrainz'), 5 | 6 | Release = mb.Release, 7 | ReleaseGroup = mb.ReleaseGroup, 8 | Recording = mb.Recording, 9 | Artist = mb.Artist, 10 | Label = mb.Label, 11 | Work = mb.Work, 12 | Disc = mb.Disc; 13 | 14 | 15 | var testReleaseMbid = '1d9f2d0e-f81d-4ee9-90b4-d2fa8c1f13f0', 16 | testReleaseGroupMbid = 'eda96b07-480c-3c97-9223-1ead72289dd2', 17 | testRecordingMbid = '066c13ca-e62b-4b49-ba4e-290e23723e0e', 18 | testArtistMbid = '11ae9fbb-f3d7-4a47-936f-4c0a04d3b3b5', 19 | testLabelMbid = '46f0f4cd-8aab-4b33-b698-f459faf64190', 20 | testWorkMbid = '15d89e06-c8b4-3170-8258-570f0b811273', 21 | testDiscId = '5RCTaO1Vd7Lv4pwVqo6kk7UVGzs-'; 22 | 23 | var testBadReleaseMbid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 24 | testBadReleaseGroupMbid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 25 | testBadRecordingMbid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 26 | testBadArtistMbid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 27 | testBadLabelMbid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 28 | testBadWorkMbid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 29 | testBadDiscId = 'discIddiscIddiscId'; 30 | 31 | var testReleaseQuery = 'Elephant', 32 | testReleaseSpecialCharsQuery = 'I\'m Not a Fan...But the Kids Like It!', 33 | testReleaseGroupQuery = '', 34 | testRecordingQuery = 'Fell In Love With A Girl', 35 | testArtistQuery = 'The White Stripes', 36 | testLabelQuery = 'Warp', 37 | testWorkQuery = 'XX'; 38 | 39 | var testBadReleaseQuery = 'Uyguyg uiygu ygui', 40 | testBadReleaseGroupQuery = 'Ugb uhbu yvgg uyv', 41 | testBadRecordingQuery = '0976087078', 42 | testBadArtistQuery = 'OIouiyhi ughiug', 43 | testBadLabelQuery = 'OIjhkopk p;ko', 44 | testBadWorkQuery = 'OIjhiouhguhuh uh u'; 45 | 46 | 47 | describe('mb', function() { 48 | 49 | describe('#configure', function () { 50 | it('shouldn\'t crash', function (done) { 51 | mb.configure({ 52 | baseURI: 'http://musicbrainz.org/ws/2/', 53 | rateLimit: { 54 | requests: 2, 55 | interval: 2000 56 | } 57 | }); 58 | 59 | done(); 60 | }); 61 | }); 62 | 63 | describe('#lookupRelease()', function(){ 64 | it('should find an release by MBID', function (done) { 65 | mb.lookupRelease(testReleaseMbid, null, function (err, release) { 66 | if (err) { throw err; } 67 | 68 | expect(release).to.be.an.instanceof(Release); 69 | expect(release).to.have.property('id').that.equals(testReleaseMbid); 70 | done(); 71 | }); 72 | }); 73 | 74 | it('should return an error with a bad MBID', function (done) { 75 | mb.lookupRelease(testBadReleaseMbid, null, function (err, release) { 76 | expect(err).to.be.an.instanceof(Error); 77 | done(); 78 | }); 79 | }); 80 | 81 | it('should return an error when MBID is not present', function (done) { 82 | mb.lookupRelease( '', [], function(err, release){ 83 | expect(err).to.be.an.instanceof(Error); 84 | done(); 85 | }); 86 | }); 87 | }); 88 | 89 | describe('#lookupReleaseGroup()', function(){ 90 | it('should find an release group by MBID', function (done) { 91 | mb.lookupReleaseGroup(testReleaseGroupMbid, null, function (err, releaseGroup) { 92 | if (err) { throw err; } 93 | 94 | expect(releaseGroup).to.be.an.instanceof(ReleaseGroup); 95 | expect(releaseGroup).to.have.property('id').that.equals(testReleaseGroupMbid); 96 | done(); 97 | }); 98 | }); 99 | 100 | it('should return an error with a bad MBID', function (done) { 101 | mb.lookupReleaseGroup(testBadReleaseMbid, null, function (err, releaseGroup) { 102 | expect(err).to.be.an.instanceof(Error); 103 | done(); 104 | }); 105 | }); 106 | 107 | it('should return an error when MBID is not present', function (done) { 108 | mb.lookupReleaseGroup( '', [], function(err, releaseGroup){ 109 | expect(err).to.be.an.instanceof(Error); 110 | done(); 111 | }); 112 | }); 113 | 114 | }); 115 | 116 | 117 | describe('#lookupRecording()', function(){ 118 | it('should find an recording by MBID', function (done) { 119 | mb.lookupRecording(testRecordingMbid, null, function (err, recording) { 120 | if (err) { throw err; } 121 | 122 | expect(recording).to.be.an.instanceof(Recording); 123 | expect(recording).to.have.property('id').that.equals(testRecordingMbid); 124 | done(); 125 | }); 126 | }); 127 | 128 | it('should return an error with a bad MBID', function (done) { 129 | mb.lookupRecording(testBadReleaseMbid, null, function (err, recording) { 130 | expect(err).to.be.an.instanceof(Error); 131 | done(); 132 | }); 133 | }); 134 | 135 | it('should return an error when MBID is not present', function (done) { 136 | mb.lookupRecording( '', [], function(err, recording){ 137 | expect(err).to.be.an.instanceof(Error); 138 | done(); 139 | }); 140 | }); 141 | 142 | }); 143 | 144 | describe('#lookupArtist()', function(){ 145 | it('should find an artist by MBID', function (done) { 146 | mb.lookupArtist(testArtistMbid, null, function (err, artist) { 147 | if (err) { throw err; } 148 | 149 | expect(artist).to.be.an.instanceof(Artist); 150 | expect(artist).to.have.property('id').that.equals(testArtistMbid); 151 | done(); 152 | }); 153 | }); 154 | 155 | it('should return an error with a bad MBID', function (done) { 156 | mb.lookupArtist(testBadArtistMbid, null, function (err, artist) { 157 | expect(err).to.be.an.instanceof(Error); 158 | done(); 159 | }); 160 | }); 161 | 162 | it('should return an error when MBID is not present', function (done) { 163 | mb.lookupArtist( '', [], function(err, artist){ 164 | expect(err).to.be.an.instanceof(Error); 165 | done(); 166 | }); 167 | }); 168 | 169 | it ('should include aliases when requested and present', function(done) { 170 | mb.lookupArtist('1bc41dff-5397-4c53-bb50-469d2c277197', ['aliases'], function(err, artist) { 171 | if (err) { throw err; } 172 | 173 | expect(artist.aliases).to.have.length.above(0); 174 | done(); 175 | }); 176 | }); 177 | }); 178 | 179 | describe('#lookupLabel()', function(){ 180 | 181 | it('should find a label by MBID', function (done) { 182 | mb.lookupLabel(testLabelMbid, null, function (err, label) { 183 | if (err) { throw err; } 184 | 185 | expect(label).to.be.an.instanceof(Label); 186 | expect(label).to.have.property('id').that.equals(testLabelMbid); 187 | done(); 188 | }); 189 | }); 190 | 191 | it('should return an error with a bad MBID', function (done) { 192 | mb.lookupLabel(testBadLabelMbid, null, function (err, label) { 193 | expect(err).to.be.an.instanceof(Error); 194 | done(); 195 | }); 196 | }); 197 | 198 | it('should return an error when MBID is not present', function (done) { 199 | mb.lookupLabel( '', [], function(err, label){ 200 | expect(err).to.be.an.instanceof(Error); 201 | done(); 202 | }); 203 | }); 204 | 205 | }); 206 | 207 | describe('#lookupWork()', function(){ 208 | 209 | it('should find a work by MBID', function (done) { 210 | mb.lookupWork(testWorkMbid, null, function (err, work) { 211 | if (err) { throw err; } 212 | 213 | expect(work).to.be.an.instanceof(Work); 214 | expect(work).to.have.property('id').that.equals(testWorkMbid); 215 | done(); 216 | }); 217 | }); 218 | 219 | it('should return an error with a bad MBID', function (done) { 220 | mb.lookupWork(testBadWorkMbid, null, function (err, work) { 221 | expect(err).to.be.an.instanceof(Error); 222 | done(); 223 | }); 224 | }); 225 | 226 | it('should return an error when mbid is not present', function (done) { 227 | mb.lookupWork( '', [], function(err, work){ 228 | expect(err).to.be.an.instanceof(Error); 229 | done(); 230 | }); 231 | }); 232 | 233 | }); 234 | 235 | describe('#lookupDiscId()', function(){ 236 | it('should find a disc its DiscId', function (done) { 237 | mb.lookupDiscId(testDiscId, null, function (err, disc) { 238 | if (err) { throw err; } 239 | 240 | expect(disc).to.be.an.instanceof(Disc); 241 | expect(disc).to.have.property('id').that.equals(testDiscId); 242 | done(); 243 | }); 244 | }); 245 | 246 | it('should return an error with a bad MBID', function (done) { 247 | mb.lookupDiscId(testBadDiscId, null, function (err, disc) { 248 | expect(err).to.be.an.instanceof(Error); 249 | done(); 250 | }); 251 | }); 252 | 253 | it('should return an error when mbid is not present', function (done) { 254 | mb.lookupDiscId( '', [], function(err, disc){ 255 | expect(err).to.be.an.instanceof(Error); 256 | done(); 257 | }); 258 | }); 259 | 260 | }); 261 | 262 | describe('#searchReleases()', function(){ 263 | it('should return an array of releases from a valid query', function (done) { 264 | mb.searchReleases( testReleaseQuery, {}, function (err, result) { 265 | if (err) { throw err; } 266 | expect(result).to.be.instanceof(Array); 267 | 268 | if (result.length) { 269 | expect(result[0]).to.be.instanceof(Release); 270 | } 271 | done(); 272 | }); 273 | }); 274 | 275 | it('should return an empty array from a bad query', function (done) { 276 | mb.searchReleases( testBadReleaseQuery, {}, function (err, result) { 277 | if (err) { throw err; } 278 | expect(result).to.be.instanceof(Array); 279 | expect(result).to.be.empty; 280 | done(); 281 | }); 282 | }); 283 | 284 | it('should return an error when query is empty', function (done) { 285 | mb.searchReleases( '', {}, function (err, result) { 286 | expect(err).to.be.an.instanceof(Error); 287 | done(); 288 | }); 289 | }); 290 | 291 | it('should return an array of a single release from a valid query with 1 result', function(done) { 292 | mb.searchReleases( "\"From Parts Unknown\"", { 293 | reid: "7b396f47-71e4-4624-b1e4-92f125b720a1", 294 | }, function (err, result) { 295 | if (err) { throw err; } 296 | expect(result).to.be.instanceof(Array); 297 | 298 | expect(result.length).to.be.eql(1); 299 | expect(result[0]).to.be.instanceof(Release); 300 | done(); 301 | }); 302 | }); 303 | 304 | 305 | it('should return a release even with special characters in the name', function(done) { 306 | mb.searchReleases( testReleaseSpecialCharsQuery, {}, function (err, result) { 307 | if (err) { throw err; } 308 | expect(result).to.be.instanceof(Array); 309 | expect(result[0]).to.be.instanceof(Release); 310 | 311 | done(); 312 | }); 313 | }); 314 | 315 | }); 316 | 317 | describe('#searchReleaseGroups()', function(){ 318 | it('should return an array of release groups from a valid query', function (done) { 319 | mb.searchReleaseGroups( '"I Shall Exterminate Everything Around Me That Restricts Me From Being the Master"', {}, function (err, result) { 320 | if (err) { throw err; } 321 | expect(result).to.be.instanceof(Array); 322 | 323 | if (result.length) { 324 | expect(result[0]).to.be.instanceof(ReleaseGroup); 325 | } 326 | done(); 327 | }); 328 | }); 329 | 330 | it('should return an empty array from a bad query', function (done) { 331 | mb.searchReleaseGroups( testBadReleaseQuery, {}, function (err, result) { 332 | if (err) { throw err; } 333 | expect(result).to.be.instanceof(Array); 334 | expect(result).to.be.empty; 335 | done(); 336 | }); 337 | }); 338 | 339 | it('should return an error when query is empty', function (done) { 340 | mb.searchReleaseGroups( '', {}, function (err, result) { 341 | expect(err).to.be.an.instanceof(Error); 342 | done(); 343 | }); 344 | }); 345 | 346 | it('should return a release even with special characters in the name', function(done) { 347 | mb.searchReleaseGroups( '"Ænima"', {}, function (err, result) { 348 | if (err) { throw err; } 349 | expect(result).to.be.instanceof(Array); 350 | expect(result[0]).to.be.instanceof(ReleaseGroup); 351 | done(); 352 | }); 353 | }); 354 | 355 | }); 356 | 357 | describe('#searchRecordings()', function(){ 358 | it('should return an array of recordings from a valid query', function (done) { 359 | mb.searchRecordings( testRecordingQuery, {}, function (err, result) { 360 | if (err) { throw err; } 361 | expect(result).to.be.instanceof(Array); 362 | 363 | if (result.length) { 364 | expect(result[0]).to.be.instanceof(Recording); 365 | } 366 | done(); 367 | }); 368 | }); 369 | 370 | it('should return an empty array from a bad query', function (done) { 371 | mb.searchRecordings( testBadRecordingQuery, {}, function (err, result) { 372 | if (err) { throw err; } 373 | expect(result).to.be.instanceof(Array); 374 | expect(result).to.be.empty; 375 | done(); 376 | }); 377 | }); 378 | 379 | it('should return an array of a single recording from a valid query with 1 result', function(done) { 380 | mb.searchRecordings( "\"Heart On\"", { 381 | reid: "0bfb1ff1-a34f-4224-8e90-674aaaa8ad6a", 382 | }, function (err, result) { 383 | if (err) { throw err; } 384 | expect(result).to.be.instanceof(Array); 385 | 386 | expect(result.length).to.be.eql(1); 387 | expect(result[0]).to.be.instanceof(Recording); 388 | done(); 389 | }); 390 | }); 391 | 392 | it('should return an error when query is empty', function (done) { 393 | mb.searchRecordings( '', {}, function (err, result) { 394 | expect(err).to.be.an.instanceof(Error); 395 | done(); 396 | }); 397 | }); 398 | 399 | }); 400 | 401 | 402 | describe('#searchArtists()', function(){ 403 | it('should return an array of artists from a valid query', function (done) { 404 | mb.searchArtists( testArtistQuery, {}, function (err, result) { 405 | if (err) { throw err; } 406 | expect(result).to.be.instanceof(Array); 407 | 408 | if (result.length) { 409 | expect(result[0]).to.be.instanceof(Artist); 410 | } 411 | done(); 412 | }); 413 | }); 414 | 415 | it('should return an array of a single artist from a valid query with 1 result', function(done) { 416 | mb.searchArtists( "\"Eagles of Death Metal\"", {}, function (err, result) { 417 | if (err) { throw err; } 418 | expect(result).to.be.instanceof(Array); 419 | 420 | expect(result.length).to.be.eql(1); 421 | expect(result[0]).to.be.instanceof(Artist); 422 | done(); 423 | }); 424 | }); 425 | 426 | it('should return an empty array from a bad query', function (done) { 427 | mb.searchArtists( testBadArtistQuery, {}, function (err, result) { 428 | if (err) { throw err; } 429 | expect(result).to.be.instanceof(Array); 430 | expect(result).to.be.empty; 431 | done(); 432 | }); 433 | }); 434 | 435 | it('should return an error when query is empty', function (done) { 436 | mb.searchArtists( '', {}, function (err, result) { 437 | expect(err).to.be.an.instanceof(Error); 438 | done(); 439 | }); 440 | }); 441 | 442 | it ('should include aliases in results', function(done) { 443 | mb.searchArtists('"The Dillinger Escape Plan"', {}, function(err, result) { 444 | if (err) { throw err; } 445 | 446 | expect(result[0].aliases).to.have.length.above(0); 447 | done(); 448 | }); 449 | }); 450 | 451 | }); 452 | 453 | }); 454 | 455 | 456 | //describe('Release', function(){ }); 457 | //describe('ReleaseGroup', function(){ }); 458 | //describe('Recording', function(){ }); 459 | //describe('Artist', function(){ }); 460 | //describe('Label', function(){ }); 461 | //describe('Work', function(){ }); 462 | //describe('Disc', function(){ }); 463 | -------------------------------------------------------------------------------- /lib/musicbrainz.js: -------------------------------------------------------------------------------- 1 | // vim: ts=4:sw=4:noexpandtab 2 | 3 | 'use strict'; 4 | 5 | var request = require('request'), 6 | xml2js = require('xml2js'), 7 | trickle = require('timetrickle'), 8 | querystring = require('querystring'), 9 | os = require('os'), 10 | timers = require('timers'), 11 | limit = trickle(1, 1000); 12 | 13 | var VERSION = '0.2.4'; 14 | var mbBaseURI = 'http://musicbrainz.org/ws/2/'; 15 | 16 | var mb = exports; 17 | 18 | exports.configure = function (options) { 19 | if (options.baseURI) { mbBaseURI = options.baseURI; } 20 | if (options.rateLimit) { 21 | var requests = options.rateLimit.requests || 1; 22 | var interval = options.rateLimit.interval || 1000; 23 | 24 | limit = trickle(requests, interval); 25 | } 26 | }; 27 | 28 | var AliasesLinkedEntities = function () { 29 | var self = this; 30 | this.setDataFields(['aliases']); 31 | this.aliases = []; 32 | this._linkedEntities.push('aliases'); 33 | 34 | this.loadAliases = function (data) { 35 | if (typeof data['alias-list'] !== 'undefined') { 36 | var aliasList = data['alias-list'].alias; 37 | for (var j = 0 ; j < aliasList.length; j++) { 38 | self.aliases.push(aliasList[j]["#"]); 39 | } 40 | } 41 | }; 42 | 43 | this._readDataFunctions.aliases = this.loadAliases; 44 | 45 | return this; 46 | }; 47 | 48 | var ReleaseLinkedEntities = function () { 49 | var self = this; 50 | this.setDataFields(['releases']); 51 | this.releases = []; 52 | this._linkedEntities.push('releases'); 53 | 54 | this.loadReleaseList = function (data) { 55 | if (typeof data['release-list'] !== 'undefined') { 56 | var releases = data['release-list'].release || []; 57 | if (data['release-list']['@'].count === 1) releases = [releases]; 58 | 59 | for (var i = 0; i < releases.length; i++) { 60 | var release = new Release(releases[i]['@'].id); 61 | release.setProperty('title', releases[i].title); 62 | release.setProperty('status', releases[i].status); 63 | release.setProperty('quality', releases[i].quality); 64 | if (typeof releases[i]['text-representation'] !== 'undefined') { 65 | release.setProperty('script', releases[i]['text-representation'].script); 66 | release.setProperty('language', releases[i]['text-representation'].language); 67 | } 68 | release.setProperty('date', releases[i].date); 69 | release.setProperty('country', releases[i].country); 70 | release.setProperty('barcode', releases[i].barcode); 71 | release.setProperty('asin', releases[i].asin); 72 | 73 | if (typeof releases[i]['medium-list'] !== 'undefined') { 74 | var mediums = releases[i]['medium-list'].medium; 75 | if (releases[i]['medium-list']['@'].count <= 1) mediums = [mediums]; 76 | 77 | for (var ii = 0; ii < mediums.length; ii++) { 78 | var medium = new Medium(); 79 | medium.setProperty('title', mediums[ii].title); 80 | medium.setProperty('position', mediums[ii].position); 81 | medium.setProperty('format', mediums[ii].format); 82 | 83 | if (typeof mediums[ii]['disc-list'] !== 'undefined') { 84 | if (typeof mediums[ii]['disc-list'].disc !== 'undefined') { 85 | var discs = mediums[ii]['disc-list'].disc; 86 | if (mediums[ii]['disc-list']['@'].count <= 1) discs = [discs]; 87 | 88 | for (var iii = 0; iii < discs.length; iii++) { 89 | var d = new Disc(discs[iii]['@'].id); 90 | d.setProperty('sectors', discs[iii].sectors); 91 | 92 | medium.discs.push(d); 93 | } 94 | 95 | release.mediums.push(medium); 96 | } 97 | } 98 | } 99 | } 100 | 101 | self.releases.push(release); 102 | } 103 | } 104 | 105 | self._loadedLinkedEntities.push('releases'); 106 | }; 107 | 108 | this._readDataFunctions['releases'] = this.loadReleaseList; 109 | 110 | return this; 111 | }; 112 | 113 | var ReleaseGroupLinkedEntities = function () { 114 | var self = this; 115 | this.setDataFields(['releaseGroups']); 116 | this.releaseGroups = []; 117 | this._linkedEntities.push('release-groups'); 118 | 119 | this.loadReleaseGroupList = function (data) { 120 | var releaseGroups = []; 121 | if (typeof data['release-group'] !== 'undefined') { 122 | releaseGroups = data['release-group']; 123 | if (!Array.isArray(releaseGroups)) releaseGroups = [releaseGroups]; 124 | 125 | } else if (typeof data['release-group-list'] !== 'undefined' && typeof data['release-group-list']['release-group'] !== 'undefined') { 126 | releaseGroups = data['release-group-list']['release-group']; 127 | if (data['release-group-list']['@'].count <= 1) releaseGroups = [releaseGroups]; 128 | 129 | } 130 | 131 | for (var i = 0; i < releaseGroups.length; i++) { 132 | var releaseGroup = new ReleaseGroup(releaseGroups[i]['@'].id); 133 | releaseGroup.setProperty('type', releaseGroups[i]['@'].type); 134 | releaseGroup.setProperty('title', releaseGroups[i].title); 135 | releaseGroup.setProperty('firstReleaseDate', releaseGroups[i]['first-release-date']); 136 | releaseGroup.setProperty('primaryType', releaseGroups[i]['primary-type']); 137 | 138 | if (typeof releaseGroups[i]['secondary-type-list'] !== 'undefined') { 139 | if (typeof releaseGroups[i]['secondary-type-list']['secondary-type'] !== 'undefined') { 140 | var secondaryTypes = releaseGroups[i]['secondary-type-list']['secondary-type']; 141 | if (!Array.isArray(secondaryTypes)) secondaryTypes = [secondaryTypes]; 142 | 143 | releaseGroup.setProperty('secondaryTypes', secondaryTypes); 144 | } 145 | } 146 | 147 | self.releaseGroups.push(releaseGroup); 148 | } 149 | 150 | self._loadedLinkedEntities.push('release-groups'); 151 | }; 152 | 153 | this._readDataFunctions['release-groups'] = this.loadReleaseGroupList; 154 | 155 | return this; 156 | }; 157 | 158 | var LabelLinkedEntities = function () { 159 | var self = this; 160 | this.setDataFields(['labelInfo']); 161 | this.labelInfo = []; 162 | this._linkedEntities.push('labels'); 163 | 164 | this.loadLabelInfoList = function (data) { 165 | if (typeof data['label-info-list'] !== 'undefined') { 166 | if (typeof data['label-info-list']['label-info'] !== 'undefined') { 167 | var labelInfos = data['label-info-list']['label-info']; 168 | if (data['label-info-list']['@'].count <= 1) labelInfos = [labelInfos]; 169 | 170 | for (var i = 0; i < labelInfos.length; i++) { 171 | var labelInfo = new LabelInfo(); 172 | labelInfo.setProperty('catalogNumber', labelInfos[i]['catalog-number']); 173 | 174 | if (typeof labelInfos[i].label !== 'undefined') { 175 | var label = new Label(labelInfos[i].label['@'].id); 176 | label.setProperty('name', labelInfos[i].label.name); 177 | label.setProperty('sortName', labelInfos[i].label['sort-name']); 178 | label.setProperty('labelCode', labelInfos[i].label['label-code']); 179 | 180 | labelInfo.label = label; 181 | } 182 | 183 | self.labelInfo.push(labelInfo); 184 | } 185 | } 186 | } 187 | 188 | self._loadedLinkedEntities.push('labels'); 189 | }; 190 | 191 | this._readDataFunctions['labels'] = this.loadLabelInfoList; 192 | 193 | return this; 194 | }; 195 | 196 | var RecordingLinkedEntities = function () { 197 | var self = this; 198 | this.setDataFields(['mediums', 'recordings']); 199 | this.mediums = []; 200 | this.recordings = []; 201 | this._linkedEntities.push('recordings'); 202 | this._linkedEntities.push('discids'); 203 | 204 | this.getMediumByDiscId = function (discId) { 205 | if (!discId) return null; 206 | 207 | for (var i = 0; i < this.mediums.length; i++) { 208 | var medium = this.mediums[i]; 209 | for (var ii = 0; ii < medium.discs.length; ii++) { 210 | var disc = medium.discs[ii]; 211 | if (disc.id === discId) return medium; 212 | } 213 | } 214 | 215 | return null; 216 | }; 217 | 218 | this.loadMediumList = function (data, linkedEntities) { 219 | if (typeof data['medium-list'] !== 'undefined') { 220 | var mediums = data['medium-list'].medium; 221 | if (data['medium-list']['@'].count <= 1) mediums = [mediums]; 222 | 223 | for (var i = 0; i < mediums.length; i++) { 224 | var medium = new Medium(); 225 | medium.setProperty('title', mediums[i].title); 226 | medium.setProperty('position', mediums[i].position); 227 | medium.setProperty('format', mediums[i].format); 228 | 229 | if (typeof mediums[i]['disc-list'] !== 'undefined') { 230 | if (typeof mediums[i]['disc-list'].disc !== 'undefined') { 231 | var discs = mediums[i]['disc-list'].disc; 232 | if (mediums[i]['disc-list']['@'].count <= 1) discs = [discs]; 233 | 234 | for (var ii = 0; ii < discs.length; ii++) { 235 | var d = new Disc(discs[ii]['@'].id); 236 | d.setProperty('sectors', discs[ii].sectors); 237 | 238 | medium.discs.push(d); 239 | } 240 | } 241 | } 242 | 243 | if (typeof mediums[i]['track-list'] !== 'undefined') { 244 | var tracks = mediums[i]['track-list'].track; 245 | if (mediums[i]['track-list']['@'].count <= 1) tracks = [tracks]; 246 | 247 | for (var j = 0; j < tracks.length; j++) { 248 | var track = new Track(); 249 | track.setProperty('position', tracks[j].position); 250 | track.setProperty('length', tracks[j].length); 251 | 252 | if (typeof tracks[j].recording !== 'undefined') { 253 | var recording = new Recording(tracks[j].recording['@'].id); 254 | recording.setProperty('title', tracks[j].recording.title); 255 | recording.setProperty('length', tracks[j].recording.length); 256 | 257 | recording.readData(linkedEntities, tracks[j].recording); 258 | 259 | track.recording = recording; 260 | } 261 | 262 | medium.tracks.push(track); 263 | 264 | } 265 | } 266 | 267 | self.mediums.push(medium); 268 | } 269 | } 270 | 271 | if (typeof data['recording-list'] !== 'undefined') { 272 | var recordings = data['recording-list'].recording; 273 | if (data['recording-list']['@'].count <= 1) recordings = [recordings]; 274 | 275 | for (var k = 0; k < recordings.length; k++) { 276 | var recording = new Recording(recordings[k]['@'].id); 277 | recording.setProperty('title', recordings[k].title); 278 | recording.setProperty('length', recordings[k].length); 279 | 280 | self.recordings.push(recording); 281 | } 282 | } 283 | 284 | self._loadedLinkedEntities.push('recordings'); 285 | self._loadedLinkedEntities.push('discids'); 286 | }; 287 | 288 | this._readDataFunctions['recordings'] = this.loadMediumList; 289 | 290 | return this; 291 | }; 292 | 293 | var ArtistLinkedEntities = function () { 294 | var self = this; 295 | this.setDataFields(['artist']); 296 | this.artistCredits = []; 297 | this._linkedEntities.push('artists'); 298 | 299 | this.loadArtistCredits = function (data) { 300 | if (typeof data['artist-credit'] !== 'undefined') { 301 | if (typeof data['artist-credit']['name-credit'] !== 'undefined') { 302 | var nameCredits = data['artist-credit']['name-credit']; 303 | if (!Array.isArray(nameCredits)) nameCredits = [nameCredits]; 304 | 305 | for (var i = 0; i < nameCredits.length; i++) { 306 | var artistData = nameCredits[i].artist; 307 | var artist = new Artist(artistData['@'].id); 308 | artist.setProperty('name', artistData.name); 309 | artist.setProperty('sortName', artistData['sort-name']); 310 | 311 | var nameCredit = { 312 | 'joinphrase' : (typeof nameCredits[i]['@'] !== 'undefined' && typeof nameCredits[i]['@'].joinphrase !== 'undefined' ? nameCredits[i]['@'].joinphrase : ''), 313 | 'artist' : artist 314 | }; 315 | 316 | self.artistCredits.push(nameCredit); 317 | } 318 | } 319 | } 320 | 321 | self._loadedLinkedEntities.push('artists'); 322 | }; 323 | 324 | this.artistCreditsString = function () { 325 | var str = ''; 326 | for (var i = 0; i < self.artistCredits.length; i++) { 327 | str += self.artistCredits[i].artist.name + self.artistCredits[i].joinphrase; 328 | } 329 | 330 | return str; 331 | }; 332 | 333 | this.artistCreditsSortString = function () { 334 | var str = ''; 335 | for (var i = 0; i < self.artistCredits.length; i++) { 336 | str += self.artistCredits[i].artist.sortName + self.artistCredits[i].joinphrase; 337 | } 338 | 339 | return str; 340 | }; 341 | 342 | this._readDataFunctions['artists'] = this.loadArtistCredits; 343 | this._readDataFunctions['artist-credits'] = this.loadArtistCredits; 344 | 345 | return this; 346 | }; 347 | 348 | var ArtistRels = function () { 349 | var self = this; 350 | this.setDataFields(['artistRels']); 351 | this.artistRels = []; 352 | this._linkedEntities.push('artist-rels'); 353 | 354 | this.loadArtistRels = function (data) { 355 | if (typeof data['relation-list'] !== 'undefined') { 356 | var relationLists = data['relation-list']; 357 | if (!Array.isArray(relationLists)) relationLists = [relationLists]; 358 | 359 | for (var i = 0; i < relationLists.length; i++) { 360 | if (relationLists[i]['@']['target-type'] != 'artist') continue; 361 | 362 | var relations = relationLists[i].relation; 363 | if (!Array.isArray(relations)) relations = [relations]; 364 | 365 | for (var ii = 0; ii < relations.length; ii++) { 366 | var relation = new ArtistRel(); 367 | relation.setProperty('type', relations[ii]['@'].type); 368 | 369 | if (typeof relations[ii]['attribute-list'] !== 'undefined') { 370 | var attributes = relations[ii]['attribute-list'].attribute; 371 | if (!Array.isArray(attributes)) attributes = [attributes]; 372 | 373 | var attrs = []; 374 | for (var iii = 0; iii < attributes.length; iii++) { 375 | attrs.push(attributes[iii]); 376 | } 377 | relation.setProperty('attributes', attrs); 378 | } 379 | 380 | var artist = new Artist(relations[ii].artist['@'].id); 381 | artist.setProperty('name', relations[ii].artist.name); 382 | artist.setProperty('sortName', relations[ii].artist['sort-name']); 383 | artist.setProperty('ipi', relations[ii].artist.ipi); 384 | 385 | relation.setProperty('artist', artist); 386 | 387 | self.artistRels.push(relation); 388 | } 389 | } 390 | } 391 | 392 | self._loadedLinkedEntities.push('artist-rels'); 393 | }; 394 | 395 | this._readDataFunctions['artist-rels'] = this.loadArtistRels; 396 | 397 | this.getArtistRelsByType = function (type) { 398 | if (!type) return null; 399 | 400 | var rels = []; 401 | 402 | for (var i = 0; i < this.artistRels.length; i++) { 403 | var rel = this.artistRels[i]; 404 | if (rel.type == type) rels.push(rel); 405 | } 406 | 407 | return rels; 408 | }; 409 | }; 410 | 411 | var WorkRels = function () { 412 | var self = this; 413 | this.setDataFields(['workRels']); 414 | this.workRels = []; 415 | this._linkedEntities.push('work-rels'); 416 | 417 | this.loadWorkRels = function (data) { 418 | if (typeof data['relation-list'] !== 'undefined') { 419 | var relationLists = data['relation-list']; 420 | if (!Array.isArray(relationLists)) relationLists = [relationLists]; 421 | 422 | for (var i = 0; i < relationLists.length; i++) { 423 | if (relationLists[i]['@']['target-type'] != 'work') continue; 424 | 425 | var relations = relationLists[i].relation; 426 | if (!Array.isArray(relations)) relations = [relations]; 427 | 428 | for (var ii = 0; ii < relations.length; ii++) { 429 | var relation = new WorkRel(); 430 | relation.type = relations[ii]['@'].type; 431 | 432 | if (typeof relations[ii]['attribute-list'] !== 'undefined') { 433 | var attributes = relations[ii]['attribute-list'].attribute; 434 | if (!Array.isArray(attributes)) attributes = [attributes]; 435 | 436 | for (var iii = 0; iii < attributes.length; iii++) { 437 | relation.attributes.push(attributes[iii]); 438 | } 439 | } 440 | 441 | var work = new Work(relations[ii].work['@'].id); 442 | work.title = relations[ii].work.title; 443 | relation.work = work; 444 | 445 | self.workRels.push(relation); 446 | } 447 | } 448 | } 449 | 450 | self._loadedLinkedEntities.push('work-rels'); 451 | }; 452 | 453 | this._readDataFunctions['work-rels'] = this.loadWorkRels; 454 | 455 | this.getWorkRelByType = function (type) { 456 | if (!type) return null; 457 | 458 | for (var i = 0; i < this.workRels.length; i++) { 459 | var rel = this.workRels[i]; 460 | if (rel.type == type) return rel; 461 | } 462 | 463 | return null; 464 | }; 465 | }; 466 | 467 | var WorkLinkedEntities = function () { 468 | var self = this; 469 | this.setDataFields(['works']); 470 | this.works = []; 471 | this._linkedEntities.push('works'); 472 | 473 | this.loadWorkList = function (data) { 474 | if (typeof data['work-list'] !== 'undefined') { 475 | var works = data['work-list'].work; 476 | if (data['work-list']['@'].count <= 1) works = [works]; 477 | 478 | for (var i = 0; i < works.length; i++) { 479 | var work = new Work(works[i]['@'].id); 480 | work.setProperty('title', works[i].title); 481 | 482 | self.works.push(work); 483 | } 484 | } 485 | 486 | self._loadedLinkedEntities.push('works'); 487 | }; 488 | 489 | this._readDataFunctions['works'] = this.loadWorkList; 490 | 491 | return this; 492 | }; 493 | 494 | var Entity = function () { 495 | this.setProperty = function (attr, value) { 496 | if (typeof value === 'undefined') return false; 497 | this[attr] = value; 498 | return true; 499 | }; 500 | 501 | this._dataProperties = []; 502 | 503 | this.data = function () { 504 | var data = {}; 505 | for (var i = 0; i < this._dataProperties.length; i++) { 506 | var prop = this._dataProperties[i]; 507 | if (this.hasOwnProperty(prop)) { 508 | if (Array.isArray(this[prop])) { 509 | data[prop] = []; 510 | for (var ii = 0; ii < this[prop].length; ii++) { 511 | if (this[prop][ii] && typeof this[prop][ii].data == 'function') { 512 | data[prop][ii] = this[prop][ii].data(); 513 | } else { 514 | data[prop][ii] = this[prop][ii]; 515 | } 516 | } 517 | 518 | } else { 519 | if (this[prop] && typeof this[prop] == 'function') { 520 | data[prop] = this[prop].data(); 521 | } else { 522 | data[prop] = this[prop]; 523 | } 524 | } 525 | } 526 | } 527 | 528 | return data; 529 | }; 530 | 531 | this.setDataFields = function (fields) { 532 | for (var i = 0; i < fields.length; i++) { 533 | this._dataProperties.push(fields[i]); 534 | } 535 | }; 536 | 537 | this.isComplete = function () { 538 | for (var i = 0; i < this._dataProperties.length; i++) { 539 | var prop = this._dataProperties[i]; 540 | if (this.hasOwnProperty(prop) && !this[prop]) return false; 541 | } 542 | 543 | return true; 544 | }; 545 | }; 546 | 547 | var Resource = function (mbid) { 548 | Entity.call(this); 549 | this.setDataFields(['id']); 550 | this._loaded = false; 551 | this._linkedEntities = []; 552 | this._loadedLinkedEntities = []; 553 | this._readDataFunctions = []; 554 | this.id = mbid; 555 | 556 | this.load = function (linkedEntities, force, callback) { 557 | if (typeof callback === 'undefined') { 558 | callback = force; 559 | force = false; 560 | } 561 | 562 | if ( !linkedEntities ) linkedEntities = []; 563 | 564 | if (!force && this.loaded(linkedEntities)) { 565 | if (typeof callback == 'function') callback(); 566 | return; 567 | } 568 | 569 | if (typeof this._lookup !== 'function') return; 570 | 571 | var self = this; 572 | this._lookup(this.id, linkedEntities, force, function (err, resource) { 573 | if ( !err ) { 574 | for (var i in self) { 575 | self[i] = resource[i]; 576 | } 577 | } 578 | 579 | if (typeof callback == 'function') callback(err); 580 | }); 581 | }; 582 | 583 | this.update = function (callback) { 584 | this.load(this._loadedLinkedEntities, true, function () { 585 | if (typeof callback == 'function') callback(); 586 | }); 587 | }; 588 | 589 | this.loaded = function (linkedEntities) { 590 | if ( !this._loaded ) return false; 591 | if (typeof linkedEntities === 'undefined') linkedEntities = this._linkedEntities; 592 | 593 | for (var i = 0; i < linkedEntities.length; i++) { 594 | if (this._loadedLinkedEntities.indexOf(linkedEntities[i]) < 0) return false; 595 | } 596 | 597 | return true; 598 | }; 599 | 600 | this.readData = function (linkedEntities, data) { 601 | for (var i in linkedEntities) { 602 | if (typeof this._readDataFunctions[linkedEntities[i]] === 'function') { 603 | this._readDataFunctions[linkedEntities[i]](data, linkedEntities); 604 | } 605 | } 606 | }; 607 | 608 | return this; 609 | }; 610 | 611 | var Disc = function (discId) { 612 | Resource.call(this, discId); 613 | ReleaseLinkedEntities.call(this); 614 | 615 | this._lookup = mb.lookupDiscId; 616 | }; 617 | 618 | var Release = function (mbid) { 619 | Resource.call(this, mbid); 620 | ReleaseGroupLinkedEntities.call(this); 621 | LabelLinkedEntities.call(this); 622 | RecordingLinkedEntities.call(this); 623 | ArtistLinkedEntities.call(this); 624 | 625 | this.setDataFields(['title', 'status', 'packaging', 'quality', 626 | 'language', 'script', 'date', 'country', 'barcode', 'asin']); 627 | 628 | this.title = null; 629 | this.status = null; 630 | this.packaging = null; 631 | this.quality = null; 632 | this.language = null; 633 | this.script = null; 634 | this.date = null; 635 | this.country = null; 636 | this.barcode = null; 637 | this.asin = null; 638 | 639 | this._lookup = mb.lookupRelease; 640 | }; 641 | 642 | var Medium = function () { 643 | Entity.call(this); 644 | 645 | this.setDataFields(['title', 'position', 'format', 'discs', 'tracks']); 646 | 647 | this.title = null; 648 | this.position = null; 649 | this.format = null; 650 | this.discs = []; 651 | this.tracks = []; 652 | 653 | this.getTrackByPosition = function (pos) { 654 | if (!pos) return null; 655 | 656 | for (var i = 0; i < this.tracks.length; i++) { 657 | var track = this.tracks[i]; 658 | if (track.position === pos) return track; 659 | } 660 | 661 | return null; 662 | }; 663 | }; 664 | 665 | var Track = function () { 666 | Entity.call(this); 667 | this.setDataFields(['position', 'length', 'recording']); 668 | 669 | this.position = null; 670 | this.length = null; 671 | this.recording = null; 672 | }; 673 | 674 | var Recording = function (mbid) { 675 | Resource.call(this, mbid); 676 | ReleaseLinkedEntities.call(this); 677 | ArtistLinkedEntities.call(this); 678 | ArtistRels.call(this); 679 | WorkRels.call(this); 680 | 681 | this.setDataFields(['title', 'length', 'artist', 'producer', 'vocal']); 682 | 683 | this.title = null; 684 | this.length = null; 685 | this.artist = null; 686 | this.producer = null; 687 | this.vocal = null; 688 | 689 | this._lookup = mb.lookupRecording; 690 | }; 691 | 692 | var ReleaseGroup = function (mbid) { 693 | Resource.call(this, mbid); 694 | ReleaseLinkedEntities.call(this); 695 | ArtistLinkedEntities.call(this); 696 | 697 | this.setDataFields(['type', 'title', 'primaryType', 'secondaryTypes', 'firstReleaseDate']); 698 | 699 | this.type = null; 700 | this.title = null; 701 | this.primaryType = null; 702 | this.secondaryTypes = []; 703 | this.firstReleaseDate = null; 704 | 705 | this._lookup = mb.lookupReleaseGroup; 706 | }; 707 | 708 | var ArtistRel = function () { 709 | Entity.call(this); 710 | this.setDataFields(['type', 'attributes', 'artist']); 711 | this.type = null; 712 | this.attributes = []; 713 | this.artist = null; 714 | }; 715 | 716 | var Artist = function (mbid) { 717 | Resource.call(this, mbid); 718 | ReleaseLinkedEntities.call(this); 719 | ReleaseGroupLinkedEntities.call(this); 720 | RecordingLinkedEntities.call(this); 721 | AliasesLinkedEntities.call(this); 722 | WorkLinkedEntities.call(this); 723 | 724 | this.setDataFields(['type', 'name', 'sortName', 'lifeSpan', 'ipi', 'country', 'gender']); 725 | 726 | this.type = null; 727 | this.ipi = null; 728 | this.name = null; 729 | this.sortName = null; 730 | this.country = null; 731 | this.gender = null; 732 | this.lifeSpan = { 733 | 'begin' : null, 734 | 'end' : null 735 | }; 736 | 737 | this._lookup = mb.lookupArtist; 738 | }; 739 | 740 | var LabelInfo = function () { 741 | Entity.call(this); 742 | this.setDataFields(['catalogNumber', 'label']); 743 | this.catalogNumber = null; 744 | this.label = null; 745 | }; 746 | 747 | var Label = function (mbid) { 748 | Resource.call(this, mbid); 749 | ReleaseLinkedEntities.call(this); 750 | 751 | this.setDataFields(['name', 'sortName', 'labelCode', 'country', 'lifeSpan']); 752 | 753 | this.name = null; 754 | this.sortName = null; 755 | this.labelCode = null; 756 | this.country = null; 757 | this.lifeSpan = { 758 | 'begin' : null, 759 | 'end' : null 760 | }; 761 | 762 | this._lookup = mb.lookupLabel; 763 | }; 764 | 765 | var WorkRel = function () { 766 | Entity.call(this); 767 | this.setDataFields(['type', 'attributes', 'work']); 768 | this.type = null; 769 | this.attributes = []; 770 | this.work = null; 771 | }; 772 | 773 | var Work = function (mbid) { 774 | Resource.call(this, mbid); 775 | this.setDataFields(['type', 'title']); 776 | 777 | this.type = null; 778 | this.title = null; 779 | 780 | this._lookup = mb.lookupWork; 781 | }; 782 | 783 | mb.lookupCache = function (uri, force, callback, lookup) { 784 | lookup(callback); 785 | }; 786 | 787 | mb.searchCache = function (uri, force, callback, search) { 788 | search(function (err, resource) { 789 | callback(err, resource); 790 | }); 791 | }; 792 | 793 | mb.userAgent = function () { 794 | return 'node-musicbrainz/' + VERSION + ' (node/' + process.version + '; ' + 795 | os.type() + '/' + os.release() + ')'; 796 | }; 797 | 798 | mb.lookup = function (resource, mbid, inc, force, callback) { 799 | if (typeof callback === 'undefined') { 800 | callback = force; 801 | force = false; 802 | } 803 | 804 | 805 | var uri = mbBaseURI + resource + '/' + mbid; 806 | if (inc && inc instanceof Array) uri += '?inc=' + inc.join('+'); 807 | 808 | var count = 0; 809 | var lookup = function (callback) { 810 | limit(1, function (err) { 811 | count++; 812 | request({ 813 | 'method' : 'GET', 814 | 'uri' : uri, 815 | 'headers' : { 816 | 'User-Agent': mb.userAgent() 817 | } 818 | 819 | }, function (err, response, body) { 820 | if (err) { return callback(err, null); } 821 | 822 | // If the service is busy, we'll try again later 823 | if (response.statusCode == 503) { 824 | timers.setTimeout(lookup, 2000, callback); 825 | return; 826 | } 827 | 828 | var parser = new xml2js.Parser(); 829 | 830 | if (!err && response.statusCode == 200) { 831 | parser.addListener('end', function(result) { 832 | if (typeof callback == 'function') { return callback(null, result); } 833 | }); 834 | parser.parseString(body); 835 | 836 | } else { 837 | parser.addListener('end', function(result) { 838 | if (typeof callback == 'function') { 839 | var err = new Error(result.text); 840 | err.statusCode = response.statusCode; 841 | return callback(err, null); 842 | } 843 | }); 844 | parser.parseString(body); 845 | } 846 | }); 847 | 848 | }); 849 | }; 850 | 851 | mb.lookupCache(uri, force, callback, lookup); 852 | }; 853 | 854 | mb.search = function(resource, query, filter, force, callback){ 855 | if (typeof callback === 'undefined') { 856 | callback = force; 857 | force = false; 858 | } 859 | 860 | var filterArr = [], 861 | filterStr = '', 862 | uri = mbBaseURI + resource + '?', 863 | uriToAdd = ''; 864 | 865 | // Go through the rest of the filters 866 | if(filter instanceof Object) { 867 | for(var key in filter){ 868 | if (key === 'limit' || key === 'offset') { 869 | uriToAdd += !uriToAdd.length ? '' : '&'; 870 | uriToAdd += key + '=' + filter[key]; 871 | } else { 872 | filterArr.push(key + ':' + encodeURIComponent(filter[key])); 873 | } 874 | } 875 | filterStr = filterArr.join( encodeURIComponent(' AND ') ); 876 | } 877 | 878 | // Set query 879 | uriToAdd += !uriToAdd.length ? '' : '&'; 880 | uriToAdd += 'query='; 881 | 882 | if(query && query.length > 0 && filterStr.length > 0){ 883 | uriToAdd += encodeURIComponent(query).replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1") + encodeURIComponent(' AND ') + filterStr; 884 | } else if(!query || query.length === 0){ 885 | uriToAdd += filterStr; 886 | } else { 887 | uriToAdd += encodeURIComponent(query).replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); 888 | } 889 | 890 | // Finally add to the uri 891 | uri += uriToAdd || ''; 892 | 893 | var count = 0; 894 | var search = function (callback) { 895 | limit(1, function (err) { 896 | count++; 897 | request({ 898 | 'method' : 'GET', 899 | 'uri' : uri, 900 | 'headers' : { 901 | 'User-Agent': mb.userAgent() 902 | } 903 | 904 | }, function (err, response, body) { 905 | if (err) { callback(err, null); return; } 906 | 907 | // If the service is busy, we'll try again later 908 | if (response.statusCode == 503) { 909 | timers.setTimeout(search, 2000, callback); 910 | return; 911 | } 912 | 913 | var parser = new xml2js.Parser(); 914 | 915 | if (!err && response.statusCode == 200) { 916 | parser.addListener('end', function(result) { 917 | if (typeof callback == 'function') callback(false, result); 918 | }); 919 | parser.parseString(body); 920 | 921 | } else { 922 | parser.addListener('end', function(result) { 923 | if (typeof callback == 'function') { 924 | var err = new Error(result.text); 925 | err.statusCode = response.statusCode; 926 | callback(err, null); 927 | } 928 | }); 929 | parser.parseString(body); 930 | } 931 | }); 932 | 933 | }); 934 | }; 935 | 936 | mb.searchCache(uri, force, callback, search); 937 | }; 938 | 939 | mb.lookupDiscId = function (discId, linkedEntities, force, callback) { 940 | if (typeof callback === 'undefined') { 941 | callback = force; 942 | force = false; 943 | } 944 | 945 | mb.lookup('discid', discId, ['recordings'], function (err, data) { 946 | if (err) { 947 | callback(err, data); 948 | return; 949 | } 950 | 951 | data = data.disc; 952 | 953 | var disc = new Disc(data['@'].id); 954 | disc.setProperty('sectors', data.sectors); 955 | 956 | disc.readData(['releases'], data); 957 | 958 | if (typeof callback == 'function') callback(false, disc); 959 | }); 960 | 961 | }; 962 | 963 | mb.lookupRelease = function (mbid, linkedEntities, force, callback) { 964 | if (typeof callback === 'undefined') { 965 | callback = force; 966 | force = false; 967 | } 968 | 969 | if ( !linkedEntities ) linkedEntities = []; 970 | 971 | mb.lookup('release', mbid, linkedEntities, force, function (err, data) { 972 | if (err) { return callback(err); } 973 | 974 | data = data.release; 975 | 976 | var release = new Release(data['@'].id); 977 | release.setProperty('title', data.title); 978 | release.setProperty('status', data.status); 979 | release.setProperty('packaging', data.packaging); 980 | release.setProperty('quality', data.quality); 981 | if (typeof data['text-representation'] !== 'undefined') { 982 | release.setProperty('language', data['text-representation'].language); 983 | release.setProperty('script', data['text-representation'].script); 984 | } 985 | release.setProperty('date', data.date); 986 | release.setProperty('country', data.country); 987 | release.setProperty('barcode', data.barcode); 988 | release.setProperty('asin', data.asin); 989 | 990 | release.readData(linkedEntities, data); 991 | 992 | release._loaded = true; 993 | 994 | if (typeof callback == 'function') callback(false, release); 995 | }); 996 | }; 997 | 998 | mb.lookupReleaseGroup = function (mbid, linkedEntities, force, callback) { 999 | if (typeof callback === 'undefined') { 1000 | callback = force; 1001 | force = false; 1002 | } 1003 | 1004 | if ( !linkedEntities ) linkedEntities = []; 1005 | 1006 | mb.lookup('release-group', mbid, linkedEntities, function (err, data) { 1007 | if (err) { 1008 | callback(err, data); 1009 | return; 1010 | } 1011 | 1012 | data = data['release-group']; 1013 | 1014 | var releaseGroup = new ReleaseGroup(data['@'].id); 1015 | releaseGroup.setProperty('type', data['@'].type); 1016 | releaseGroup.setProperty('title', data.title); 1017 | releaseGroup.setProperty('firstReleaseDate', data['first-release-date']); 1018 | releaseGroup.setProperty('primaryType', data['primary-type']); 1019 | 1020 | if (typeof data['secondary-type-list'] !== 'undefined') { 1021 | if (typeof data['secondary-type-list']['secondary-type'] !== 'undefined') { 1022 | var secondaryTypes = data['secondary-type-list']['secondary-type']; 1023 | if (!Array.isArray(secondaryTypes)) secondaryTypes = [secondaryTypes]; 1024 | 1025 | releaseGroup.setProperty('secondaryTypes', secondaryTypes); 1026 | } 1027 | } 1028 | 1029 | releaseGroup.readData(linkedEntities, data); 1030 | 1031 | releaseGroup._loaded = true; 1032 | 1033 | if (typeof callback == 'function') callback(false, releaseGroup); 1034 | }); 1035 | }; 1036 | 1037 | mb.lookupRecording = function (mbid, linkedEntities, force, callback) { 1038 | if (typeof callback === 'undefined') { 1039 | callback = force; 1040 | force = false; 1041 | } 1042 | 1043 | if ( !linkedEntities ) linkedEntities = []; 1044 | 1045 | mb.lookup('recording', mbid, linkedEntities, function (err, data) { 1046 | if (err) { 1047 | callback(err, data); 1048 | return; 1049 | } 1050 | 1051 | data = data.recording; 1052 | 1053 | var recording = new Recording(data['@'].id); 1054 | recording.setProperty('title', data.title); 1055 | recording.setProperty('length', data.length); 1056 | 1057 | if (typeof data['relation-list'] !== 'undefined') { 1058 | var relationList = data['relation-list']; 1059 | if (typeof relationList.length === 'undefined') relationList = [relationList]; 1060 | 1061 | for (var i = 0; i < relationList.length; i++) { 1062 | switch (relationList[i]['@']['target-type']) { 1063 | case 'artist': { 1064 | var relations = relationList[i].relation; 1065 | if (typeof relations.length === 'undefined') relations = [relations]; 1066 | 1067 | for (var ii = 0; ii < relations.length; ii++) { 1068 | var artist = new Artist(relations[ii].artist['@'].id); 1069 | artist.setProperty('name', relations[ii].artist.name); 1070 | artist.setProperty('sortName', relations[ii].artist['sort-name']); 1071 | 1072 | recording[relations[ii]['@'].type] = artist; 1073 | } 1074 | break; 1075 | } 1076 | } 1077 | } 1078 | } 1079 | 1080 | recording.readData(linkedEntities, data); 1081 | 1082 | recording._loaded = true; 1083 | 1084 | if (typeof callback == 'function') callback(false, recording); 1085 | }); 1086 | }; 1087 | 1088 | mb.lookupArtist = function (mbid, linkedEntities, force, callback) { 1089 | if (typeof callback === 'undefined') { 1090 | callback = force; 1091 | force = false; 1092 | } 1093 | 1094 | if ( !linkedEntities ) linkedEntities = []; 1095 | 1096 | mb.lookup('artist', mbid, linkedEntities, function (err, data) { 1097 | if (err) { 1098 | callback(err, data); 1099 | return; 1100 | } 1101 | 1102 | data = data.artist; 1103 | 1104 | var artist = new Artist(data['@'].id); 1105 | artist.setProperty('type', data['@'].type); 1106 | artist.setProperty('name', data.name); 1107 | artist.setProperty('sortName', data['sort-name']); 1108 | artist.setProperty('country', data.country); 1109 | artist.setProperty('gender', data.gender); 1110 | if (typeof data['life-span'] !== 'undefined') { 1111 | artist.setProperty('lifeSpan', { 1112 | 'begin' : data['life-span'].begin, 1113 | 'end' : data['life-span'].end 1114 | }); 1115 | } 1116 | 1117 | artist.readData(linkedEntities, data); 1118 | 1119 | artist._loaded = true; 1120 | 1121 | if (typeof callback == 'function') callback(false, artist); 1122 | }); 1123 | }; 1124 | 1125 | mb.lookupLabel = function (mbid, linkedEntities, force, callback) { 1126 | if (typeof callback === 'undefined') { 1127 | callback = force; 1128 | force = false; 1129 | } 1130 | 1131 | if ( !linkedEntities ) linkedEntities = []; 1132 | 1133 | mb.lookup('label', mbid, linkedEntities, function (err, data) { 1134 | if (err) { 1135 | callback(err, data); 1136 | return; 1137 | } 1138 | 1139 | data = data.label; 1140 | 1141 | var label = new Label(data['@'].id); 1142 | label.setProperty('type', data['@'].type); 1143 | label.setProperty('name', data.name); 1144 | label.setProperty('sortName', data['sort-name']); 1145 | label.setProperty('country', data.country); 1146 | label.setProperty('labelCode', data['label-code']); 1147 | if (typeof data['life-span'] !== 'undefined') { 1148 | label.setProperty('lifeSpan', { 1149 | 'begin' : data['life-span'].begin, 1150 | 'end' : data['life-span'].end 1151 | }); 1152 | } 1153 | 1154 | label.readData(linkedEntities, data); 1155 | 1156 | label._loaded = true; 1157 | 1158 | if (typeof callback == 'function') callback(false, label); 1159 | }); 1160 | }; 1161 | 1162 | mb.lookupWork = function (mbid, linkedEntities, force, callback) { 1163 | if (typeof callback === 'undefined') { 1164 | callback = force; 1165 | force = false; 1166 | } 1167 | 1168 | if ( !linkedEntities ) linkedEntities = []; 1169 | 1170 | mb.lookup('work', mbid, linkedEntities, function (err, data) { 1171 | if (err) { 1172 | callback(err, data); 1173 | return; 1174 | } 1175 | 1176 | var work = new Work(data.work['@'].id); 1177 | work.setProperty('type', data.work['@'].type); 1178 | work.setProperty('title', data.work.title); 1179 | 1180 | work.readData(linkedEntities, data); 1181 | 1182 | work._loaded = true; 1183 | 1184 | if (typeof callback == 'function') callback(false, work); 1185 | }); 1186 | }; 1187 | 1188 | 1189 | mb.searchReleases = function (query, filter, force, callback) { 1190 | if (typeof callback === 'undefined') { 1191 | callback = force; 1192 | force = false; 1193 | } 1194 | 1195 | mb.search('release', query, filter, force, function (err, data) { 1196 | if (err) { 1197 | callback(err, data); 1198 | return; 1199 | } 1200 | 1201 | var releases = []; 1202 | 1203 | if ('release-list' in data && 'release' in data['release-list'] && !Array.isArray(data['release-list'].release)) { 1204 | data['release-list'].release = [data['release-list'].release]; 1205 | } 1206 | 1207 | if ('release-list' in data && 'release' in data['release-list'] && data['release-list']['release'].length > 0) { 1208 | data = data['release-list']['release']; 1209 | 1210 | for(var i=0; i < data.length; i++){ 1211 | 1212 | var release = new Release(data[i]['@'].id); 1213 | release.setProperty('title', data[i].title); 1214 | release.setProperty('status', data[i].status); 1215 | release.setProperty('packaging', data[i].packaging); 1216 | release.setProperty('quality', data[i].quality); 1217 | if (typeof data[i]['text-representation'] !== 'undefined') { 1218 | release.setProperty('language', data[i]['text-representation'].language); 1219 | release.setProperty('script', data[i]['text-representation'].script); 1220 | } 1221 | release.setProperty('date', data[i].date); 1222 | release.setProperty('country', data[i].country); 1223 | release.setProperty('barcode', data[i].barcode); 1224 | release.setProperty('asin', data[i].asin); 1225 | 1226 | //release.readData(linkedEntities, data); 1227 | 1228 | release._loaded = true; 1229 | 1230 | releases.push(release); 1231 | } 1232 | } 1233 | 1234 | if (typeof callback == 'function') callback(false, releases); 1235 | }); 1236 | 1237 | }; 1238 | 1239 | 1240 | mb.searchReleaseGroups = function (query, filter, force, callback) { 1241 | if (typeof callback === 'undefined') { 1242 | callback = force; 1243 | force = false; 1244 | } 1245 | 1246 | mb.search('release-group', query, filter, force, function (err, data) { 1247 | if (err) { 1248 | callback(err, data); 1249 | return; 1250 | } 1251 | 1252 | var releaseGroups = []; 1253 | 1254 | if ('release-group-list' in data && 'release-group' in data['release-group-list'] && !Array.isArray(data['release-group-list']['release-group'])) { 1255 | data['release-group-list']['release-group'] = [data['release-group-list']['release-group']]; 1256 | } 1257 | 1258 | if ('release-group-list' in data && 'release-group' in data['release-group-list'] && data['release-group-list']['release-group'].length > 0) { 1259 | data = data['release-group-list']['release-group']; 1260 | 1261 | for(var i=0; i < data.length; i++){ 1262 | var releaseGroup = new ReleaseGroup(data[i]['@'].id); 1263 | releaseGroup.setProperty('type', data[i]['@'].type); 1264 | releaseGroup.setProperty('title', data[i].title); 1265 | releaseGroup.setProperty('firstReleaseDate', data[i]['first-release-date']); 1266 | releaseGroup.setProperty('primaryType', data[i]['primary-type']); 1267 | 1268 | if (typeof data[i]['secondary-type-list'] !== 'undefined') { 1269 | if (typeof data[i]['secondary-type-list']['secondary-type'] !== 'undefined') { 1270 | var secondaryTypes = data[i]['secondary-type-list']['secondary-type']; 1271 | if (!Array.isArray(secondaryTypes)) secondaryTypes = [secondaryTypes]; 1272 | 1273 | releaseGroup.setProperty('secondaryTypes', secondaryTypes); 1274 | } 1275 | } 1276 | 1277 | releaseGroup.readData(['artists', 'releases'], data[i]); 1278 | 1279 | releaseGroup.searchScore = parseInt(data[i]['@']['ext:score'], 10); 1280 | 1281 | releaseGroups.push(releaseGroup); 1282 | } 1283 | } 1284 | 1285 | if (typeof callback == 'function') callback(false, releaseGroups); 1286 | }); 1287 | 1288 | }; 1289 | 1290 | mb.searchRecordings = function (query, filter, force, callback) { 1291 | if (typeof callback === 'undefined') { 1292 | callback = force; 1293 | force = false; 1294 | } 1295 | 1296 | mb.search('recording', query, filter, function (err, data) { 1297 | if (err) { 1298 | callback(err, data); 1299 | return; 1300 | } 1301 | 1302 | 1303 | var recordings = []; 1304 | 1305 | if ('recording-list' in data && 'recording' in data['recording-list'] && !Array.isArray(data['recording-list'].recording)) { 1306 | data['recording-list'].recording = [data['recording-list'].recording]; 1307 | } 1308 | 1309 | if ('recording-list' in data && 'recording' in data['recording-list'] && data['recording-list']['recording'].length > 0) { 1310 | data = data['recording-list']['recording']; 1311 | 1312 | for(var i = 0; i < data.length; i++){ 1313 | 1314 | var recording = new Recording(data[i]['@'].id); 1315 | recording.setProperty('title', data[i].title); 1316 | recording.setProperty('length', data[i].length); 1317 | 1318 | if (typeof data[i]['relation-list'] !== 'undefined') { 1319 | var relationList = data[i]['relation-list']; 1320 | if (typeof relationList.length === 'undefined') relationList = [relationList]; 1321 | 1322 | for (var ii = 0; ii < relationList.length; ii++) { 1323 | switch (relationList[ii]['@']['target-type']) { 1324 | case 'artist': { 1325 | var relations = relationList[ii].relation; 1326 | if (typeof relations.length === 'undefined') relations = [relations]; 1327 | 1328 | for (var iii = 0; iii < relations.length; iii++) { 1329 | var artist = new Artist(relations[iii].artist['@'].id); 1330 | artist.setProperty('name', relations[iii].artist.name); 1331 | artist.setProperty('sortName', relations[iii].artist['sort-name']); 1332 | 1333 | recording[relations[iii]['@'].type] = artist; 1334 | } 1335 | break; 1336 | } 1337 | } 1338 | } 1339 | } 1340 | 1341 | //recording.readData(linkedEntities, data); 1342 | recording._loaded = true; 1343 | recordings.push(recording); 1344 | } 1345 | } 1346 | 1347 | if (typeof callback == 'function') callback(false, recordings); 1348 | }); 1349 | }; 1350 | 1351 | mb.searchArtists = function (query, filter, force, callback) { 1352 | if (typeof callback === 'undefined') { 1353 | callback = force; 1354 | force = false; 1355 | } 1356 | 1357 | mb.search('artist', query, filter, function (err, data) { 1358 | if (err) { 1359 | callback(err, data); 1360 | return; 1361 | } 1362 | 1363 | var artists = []; 1364 | 1365 | if ('artist-list' in data && 'artist' in data['artist-list'] && !Array.isArray(data['artist-list'].artist)) { 1366 | data['artist-list'].artist = [data['artist-list'].artist]; 1367 | } 1368 | 1369 | if ('artist-list' in data && 'artist' in data['artist-list'] && data['artist-list']['artist'].length > 0) { 1370 | var artistList = data['artist-list']['artist']; 1371 | 1372 | for(var i=0; i < artistList.length; i++){ 1373 | 1374 | var artist = new Artist(artistList[i]['@'].id); 1375 | artist.setProperty('type', artistList[i]['@'].type); 1376 | artist.setProperty('name', artistList[i].name); 1377 | artist.setProperty('sortName', artistList[i]['sort-name']); 1378 | artist.setProperty('country', artistList[i].country); 1379 | if (typeof artistList[i]['life-span'] !== 'undefined') { 1380 | artist.setProperty('lifeSpan', { 1381 | 'begin' : artistList[i]['life-span'].begin, 1382 | 'end' : artistList[i]['life-span'].end 1383 | }); 1384 | } 1385 | 1386 | if (typeof artistList[i]['alias-list'] !== 'undefined') { 1387 | var aliasList = artistList[i]['alias-list'].alias; 1388 | var aliases = []; 1389 | for (var j = 0 ; j < aliasList.length; j++) { 1390 | aliases.push(aliasList[j]["#"]); 1391 | } 1392 | artist.setProperty('aliases', aliases); 1393 | } 1394 | 1395 | //artist.readData(linkedEntities, data); 1396 | artist._loaded = true; 1397 | artists.push(artist); 1398 | } 1399 | } 1400 | 1401 | if (typeof callback == 'function') callback(false, artists); 1402 | }); 1403 | }; 1404 | 1405 | mb.VERSION = VERSION; 1406 | 1407 | mb.Release = Release; 1408 | mb.ReleaseGroup = ReleaseGroup; 1409 | mb.Recording = Recording; 1410 | mb.Artist = Artist; 1411 | mb.Label = Label; 1412 | mb.Work = Work; 1413 | mb.Disc = Disc; 1414 | --------------------------------------------------------------------------------