Skip to content

Commit ab09642

Browse files
committed
The library is asynchronous again!
When creating the BufferedBinaryAjax to enable M4A partial loading I made the error of making everything synchronous, probably not the best idea I had in the latest years :P Each reader is now responsible for loading its own data, instead of returning a range of bytes to preload using readID3Range() the reader has a loadData(data, callback) function that will load the necessary data and invoke the callback when ready. The ID4 was a bit tricky since there's a need to search the tags through the entire file. A function named loadAtom() takes care of loading only the ranges where the tags are contained.
1 parent b56cfae commit ab09642

6 files changed

Lines changed: 91 additions & 43 deletions

File tree

dist/id3-minimized.js

Lines changed: 22 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/bufferedbinaryajax.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,19 @@ var BufferedBinaryAjax = function(strUrl, fncCallback, fncError) {
152152
return blocks[~~(offset/blockSize)];
153153
}
154154

155-
function waitForBlocks(blockRange) {
155+
/**
156+
* @param {?function()} callback If a function is passed then this function will be asynchronous and the callback invoked when the blocks have been loaded, otherwise it blocks script execution until the request is completed.
157+
*/
158+
function waitForBlocks(blockRange, callback) {
156159
// Filter out already downloaded blocks or return if found out that
157160
// the entire block range has already been downloaded.
158161
while( blocks[blockRange[0]] ) {
159162
blockRange[0]++;
160-
if( blockRange[0] > blockRange[1] ) return;
163+
if( blockRange[0] > blockRange[1] ) return callback ? callback() : undefined;
161164
}
162165
while( blocks[blockRange[1]] ) {
163166
blockRange[1]--;
164-
if( blockRange[0] > blockRange[1] ) return;
167+
if( blockRange[0] > blockRange[1] ) return callback ? callback() : undefined;
165168
}
166169
var range = [blockRange[0]*blockSize, (blockRange[1]+1)*blockSize-1];
167170
//console.log("Getting: " + range[0] + " to " + range[1]);
@@ -185,12 +188,13 @@ var BufferedBinaryAjax = function(strUrl, fncCallback, fncError) {
185188
blocks[i] = block;
186189
}
187190
downloadedBytesCount += range[1] - range[0] + 1;
191+
if (callback) callback();
188192
},
189193
fncError,
190194
range,
191195
"bytes",
192196
undefined,
193-
false
197+
!!callback
194198
);
195199
}
196200

@@ -228,10 +232,11 @@ var BufferedBinaryAjax = function(strUrl, fncCallback, fncError) {
228232
* Downloads the byte range given. Useful for preloading.
229233
*
230234
* @param {Array} range Two element array that denotes the first byte to be read on the first position and the last byte to be read on the last position. A range of [2, 5] will download bytes 2,3,4 and 5.
235+
* @param {?function()} callback The function to invoke when the blocks have been downloaded, this makes this call asynchronous.
231236
*/
232-
this.loadRange = function(range) {
237+
this.loadRange = function(range, callback) {
233238
var blockRange = getBlockRangeForByteRange(range);
234-
waitForBlocks(blockRange);
239+
waitForBlocks(blockRange, callback);
235240
};
236241
}
237242

src/id3.js

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,32 @@
1111
var ID3 = ns.ID3 = {};
1212

1313
var files = [];
14+
// location of the format identifier
15+
var formatIDRange = [0, 7];
1416

17+
/**
18+
* Finds out the tag format of this data and returns the appropriate
19+
* reader.
20+
*/
1521
function getReader(data) {
1622
// FIXME: improve this detection according to the spec
1723
return data.getStringAt(4, 7) == "ftypM4A" ? ID4 :
1824
(data.getStringAt(0, 3) == "ID3" ? ID3v2 : ID3v1);
1925
}
2026

21-
function readFileDataFromAjax(url, callback) {
22-
BufferedBinaryAjax(url, function(http) {
23-
var reader = getReader(http.binaryResponse);
24-
var range = reader.readID3Range(http.binaryResponse);
25-
if( range ) http.binaryResponse.loadRange(range);
26-
if( callback ) callback(reader, http.binaryResponse);
27-
});
28-
}
27+
function readFileDataFromAjax(url, callback) {
28+
BufferedBinaryAjax(url, function(http) {
29+
var response = http.binaryResponse;
30+
31+
// preload the format identifier
32+
response.loadRange(formatIDRange, function() {
33+
var reader = getReader(response);
34+
reader.loadData(response, function() {
35+
if( callback ) callback(reader, response);
36+
});
37+
});
38+
});
39+
}
2940

3041
function readFileDataFromFileSystem(url, callback) {
3142
ReadFile(

src/id3v1.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@
3434
"Duet","Punk Rock","Drum Solo","Acapella","Euro-House","Dance Hall"
3535
];
3636

37-
ID3v1.readID3Range = function() {
38-
return [-128, 128];
37+
ID3v1.loadData = function(data, callback) {
38+
var length = data.getLength();
39+
data.loadRange([length-128-1, length], callback);
3940
}
4041

4142
ID3v1.readTagsFromData = function(data) {

src/id3v2.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,9 @@
320320
}
321321
}
322322

323-
ID3v2.readID3Range = function(data) {
324-
return [0, readSynchsafeInteger32At(6, data)];
325-
}
323+
ID3v2.loadData = function(data, callback) {
324+
data.loadRange([0, readSynchsafeInteger32At(6, data)], callback);
325+
};
326326

327327
// http://www.id3.org/id3v2.3.0
328328
ID3v2.readTagsFromData = function(data, tags) {

src/id4.js

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,39 @@
3636
'©gen': ['genre']
3737
};
3838

39-
ID4.readID3Range = function(data) {
40-
return null;
39+
ID4.loadData = function(data, callback) {
40+
// load the header of the first block
41+
data.loadRange([0, 7], function () {
42+
loadAtom(data, 0, data.getLength(), callback);
43+
});
44+
};
45+
46+
/**
47+
* Make sure that the [offset, offset+7] bytes (the block header) are
48+
* already loaded before calling this function.
49+
*/
50+
function loadAtom(data, offset, length, callback) {
51+
// 8 is the size of the atomSize and atomName fields.
52+
// When reading the current block we always read 8 more bytes in order
53+
// to also read the header of the next block.
54+
var atomSize = data.getLongAt(offset, true);
55+
if (atomSize == 0) return callback();
56+
var atomName = data.getStringAt(offset + 4, 4);
57+
58+
// Container atoms
59+
if (['moov', 'udta', 'meta', 'ilst'].indexOf(atomName) > -1)
60+
{
61+
if (atomName == 'meta') offset += 4; // next_item_id (uint32)
62+
data.loadRange([offset+8, offset+8 + 8], function() {
63+
loadAtom(data, offset + 8, atomSize - 8, callback);
64+
});
65+
} else {
66+
// Value atoms
67+
var readAtom = atomName in ID4.atom;
68+
data.loadRange([offset+(readAtom?0:atomSize), offset+atomSize + 8], function() {
69+
loadAtom(data, offset+atomSize, length, callback);
70+
});
71+
}
4172
};
4273

4374
ID4.readTagsFromData = function(data) {

0 commit comments

Comments
 (0)