1
0
Fork 0
mirror of https://github.com/DanielnetoDotCom/YouPHPTube synced 2025-10-04 02:09:22 +02:00
Daniel Neto 2023-06-30 08:55:17 -03:00
parent 746e163d01
commit 1c7ea28b46
808 changed files with 316395 additions and 381162 deletions

View file

@ -72,7 +72,7 @@ QUnit.test('triggers log for non-id3/invalid data', function(assert) {
assert.deepEqual(logs, [
{level: 'warn', message: 'Skipping unrecognized metadata packet'},
{level: 'warn', message: 'Skipping unrecognized metadata packet'},
{level: 'warn', message: 'Malformed ID3 frame encountered. Skipping metadata parsing.'}
{level: 'warn', message: 'Malformed ID3 frame encountered. Skipping remaining metadata parsing.'}
], 'logs as expected.');
});
@ -518,6 +518,65 @@ QUnit.test('constructs the dispatch type', function(assert) {
assert.equal(metadataStream.dispatchType, '1503020100', 'built the dispatch type');
});
QUnit.test('should skip tag frame parsing on malformed frame, preserving previous frames', function(assert) {
var events = [],
validFrame = id3Frame('TIT2',
0x03, // utf-8
stringToCString('sample title')),
malformedFrame = id3Frame('WOAF'), // frame with size of 0B
tag = id3Tag(validFrame, malformedFrame);
metadataStream.on('data', function(event) {
events.push(event);
});
metadataStream.push({
type: 'timed-metadata',
data: new Uint8Array(tag)
})
assert.equal(events.length, 1, 'parsed 1 tag')
assert.equal(events[0].frames.length, 1, 'parsed 1 frame');
assert.equal(events[0].frames[0].key, 'TIT2');
});
QUnit.test('can parse APIC frame in web worker', function(assert) {
var worker = new MetadataStreamTestWorker(),
done = assert.async();
worker.addEventListener('message', function(e) {
assert.equal(e.data.frames[0].key, 'APIC', 'frame key is APIC');
assert.equal(e.data.frames[0].mimeType, 'image/jpeg', 'parsed MIME type is "image/jpeg"');
assert.equal(e.data.frames[0].pictureType, 0x03, 'parsed picture type is 0x03');
assert.equal(e.data.frames[0].description, 'sample description', 'parsed description');
assert.deepEqual(e.data.frames[0].pictureData, new Uint8Array(stringToInts("picture binary data")), 'parsed picture data');
assert.equal(e.data.frames[1].key, 'APIC', 'frame key is APIC');
assert.equal(e.data.frames[1].mimeType, '-->', 'parsed MIME type is "-->"');
assert.equal(e.data.frames[1].pictureType, 0x04, 'parsed picture type is 0x04');
assert.equal(e.data.frames[1].description, 'sample description 2', 'parsed description');
assert.equal(e.data.frames[1].url, 'http://example.org/cover-back.jpg', 'parsed picture data');
worker.terminate();
done();
});
worker.postMessage({
type: 'timed-metadata',
data: new Uint8Array(id3Tag(id3Frame('APIC',
0x03, // Text encoding: UTF-8
stringToCString('image/jpeg'), // MIME type + \0
0x03, // Picture type: Cover (front) [ID3v2.4.0 section 4.14]
stringToCString('sample description'), // Decription + \0
stringToInts('picture binary data')
),
id3Frame('APIC',
0x03, // Text encoding: UTF-8
stringToCString('-->'), // MIME type: link to the image [ID3v2.4.0 section 4.14] + \0
0x04, // Picture type: Cover (back) [ID3v2.4.0 section 4.14]
stringToCString('sample description 2'), // Decription + \0
stringToInts('http://example.org/cover-back.jpg')
)))
});
});
QUnit.test('can parse PRIV frames in web worker', function(assert) {
var payload = stringToInts('arbitrary'),
@ -570,6 +629,60 @@ QUnit.test('can parse TXXX frames in web worker', function(assert) {
});
});
QUnit.test('should parse text frames in web worker', function(assert) {
var worker = new MetadataStreamTestWorker(),
done = assert.async();
worker.addEventListener('message', function(e) {
assert.equal(e.data.frames.length, 2, 'got 2 frames');
assert.equal(e.data.frames[0].key, 'TIT2', 'frame key is TIT2');
assert.equal(e.data.frames[0].value, 'sample song title', 'parsed value')
assert.equal(e.data.frames[0].values.length, 1, 'parsed value is an array of size 1')
assert.equal(e.data.frames[0].values[0], 'sample song title', 'parsed a non multiple strings value')
assert.equal(e.data.frames[1].key, 'TIT3', 'frame key is TIT3');
assert.equal(e.data.frames[1].value, 'sample title 1\0sample title 2', 'parsed value')
assert.equal(e.data.frames[1].values.length, 2, 'parsed value is an array of size 2')
assert.equal(e.data.frames[1].values[0], 'sample title 1', 'parsed 1st multiple strings value')
assert.equal(e.data.frames[1].values[1], 'sample title 2', 'parsed 2nd multiple strings value')
worker.terminate();
done();
});
worker.postMessage({
type: 'timed-metadata',
data: new Uint8Array(id3Tag(id3Frame('TIT2',
0x03, // utf-8
// frames that allow different types of encoding contain terminated text [ID3v2.4.0 section 4.]
stringToCString('sample song title')),
id3Frame('TIT3',
0x03, // utf-8
// frames that allow different types of encoding contain terminated text [ID3v2.4.0 section 4.]
// text information frames supports multiple strings, stored as a terminator separated list [ID3v2.4.0 section 4.2.]
stringToCString('sample title 1'), stringToCString('sample title 2'))))
});
});
QUnit.test('should parse URL link frames in web worker', function(assert) {
var worker = new MetadataStreamTestWorker(),
done = assert.async(),
payloadBytes;
// if the payload is followed by a string termination all the following information should be ignored [ID3v2.4.0 section 4.3]
payloadBytes = stringToInts('http://example.org\0 ignored \0 part')
worker.addEventListener('message', function(e) {
assert.equal(e.data.frames[0].key, 'WOAF', 'frame key is WOAF');
assert.equal(e.data.frames[0].url, 'http://example.org', 'parsed URL')
worker.terminate();
done();
});
worker.postMessage({
type: 'timed-metadata',
data: new Uint8Array(id3Tag(id3Frame('WOAF', payloadBytes)))
});
});
QUnit.test('triggers special event after parsing a timestamp ID3 tag', function(assert) {
var
array = new Uint8Array(73),

View file

@ -5,6 +5,7 @@ var
probe = require('../lib/mp4/probe'),
mp4Helpers = require('./utils/mp4-helpers'),
box = mp4Helpers.box,
id3 = require('./utils/id3-generator'),
// defined below
moovWithoutMdhd,
@ -143,6 +144,79 @@ QUnit.test('getTimescaleFromMediaHeader gets timescale for version 0 mdhd', func
);
});
QUnit.test('can get ID3 data from a v0 EMSG box', function(assert) {
var id3Data = new Uint8Array(id3.id3Tag(id3.id3Frame('PRIV',
id3.stringToCString('priv-owner@example.com'),
id3.stringToInts('foo.bar.id3.com')))
);
var v0EmsgId3Data = mp4Helpers.generateEmsgBoxData(0, id3Data);
var emsgId3Box = new Uint8Array(box('emsg', [].slice.call(v0EmsgId3Data)));
var emsgBoxes = probe.getEmsgID3(emsgId3Box, 10);
assert.equal(emsgBoxes[0].cueTime, 20, 'got correct emsg cueTime value from v0 emsg');
assert.equal(emsgBoxes[0].duration, 0, 'got correct emsg duration value from v0 emsg');
assert.equal(emsgBoxes[0].frames[0].id, 'PRIV' , 'got correct ID3 id');
assert.equal(emsgBoxes[0].frames[0].owner, 'priv-owner@example.com', 'got correct ID3 owner');
assert.deepEqual(emsgBoxes[0].frames[0].data, new Uint8Array(id3.stringToInts('foo.bar.id3.com')), 'got correct ID3 data');
});
QUnit.test('can get ID3 data from a v1 EMSG box', function(assert) {
var id3Data = new Uint8Array(id3.id3Tag(id3.id3Frame('TXXX',
0x03, // utf-8
id3.stringToCString('foo bar'),
id3.stringToCString('{ "key": "value" }')),
[0x00, 0x00])
);
var v1EmsgId3Data = mp4Helpers.generateEmsgBoxData(1, id3Data);
var emsgId3Box = new Uint8Array(box('emsg', [].slice.call(v1EmsgId3Data)));
var emsgBoxes = probe.getEmsgID3(emsgId3Box);
assert.equal(emsgBoxes[0].cueTime, 100, 'got correct emsg cueTime value from v1 emsg');
assert.equal(emsgBoxes[0].duration, 0.01, 'got correct emsg duration value from v1 emsg');
assert.equal(emsgBoxes[0].frames[0].id, 'TXXX' , 'got correct ID3 id');
assert.equal(emsgBoxes[0].frames[0].description, 'foo bar', 'got correct ID3 description');
assert.deepEqual(JSON.parse(emsgBoxes[0].frames[0].data), { key: 'value' }, 'got correct ID3 data');
});
QUnit.test('can get ID3 data from multiple EMSG boxes', function(assert) {
var v1id3Data = new Uint8Array(id3.id3Tag(id3.id3Frame('PRIV',
id3.stringToCString('priv-owner@example.com'),
id3.stringToInts('foo.bar.id3.com')))
);
var v0id3Data = new Uint8Array(id3.id3Tag(id3.id3Frame('TXXX',
0x03, // utf-8
id3.stringToCString('foo bar'),
id3.stringToCString('{ "key": "value" }')),
[0x00, 0x00])
);
var v1EmsgId3Data = mp4Helpers.generateEmsgBoxData(1, v1id3Data);
var v1emsgId3Box = new Uint8Array(box('emsg', [].slice.call(v1EmsgId3Data)));
var v0EmsgId3Data = mp4Helpers.generateEmsgBoxData(0, v0id3Data);
var v0emsgId3Box = new Uint8Array(box('emsg', [].slice.call(v0EmsgId3Data)));
var multiBoxData = new Uint8Array(v1emsgId3Box.length + v0emsgId3Box.length);
multiBoxData.set(v1emsgId3Box);
multiBoxData.set(v0emsgId3Box, v1emsgId3Box.length);
var emsgBoxes = probe.getEmsgID3(multiBoxData);
assert.equal(emsgBoxes[0].cueTime, 100, 'got correct emsg cueTime value from v1 emsg');
assert.equal(emsgBoxes[0].duration, 0.01, 'got correct emsg duration value from v1 emsg');
assert.equal(emsgBoxes[0].frames[0].id, 'PRIV' , 'got correct ID3 id');
assert.equal(emsgBoxes[0].frames[0].owner, 'priv-owner@example.com', 'got correct ID3 owner');
assert.deepEqual(emsgBoxes[0].frames[0].data, new Uint8Array(id3.stringToInts('foo.bar.id3.com')), 'got correct ID3 data');
assert.equal(emsgBoxes[1].cueTime, 10, 'got correct emsg cueTime value from v0 emsg');
assert.equal(emsgBoxes[1].duration, 0, 'got correct emsg duration value from v0 emsg');
assert.equal(emsgBoxes[1].frames[0].id, 'TXXX' , 'got correct ID3 id');
assert.equal(emsgBoxes[1].frames[0].description, 'foo bar', 'got correct ID3 description');
assert.deepEqual(JSON.parse(emsgBoxes[1].frames[0].data),{ key: 'value' }, 'got correct ID3 data');
});
// ---------
// Test Data
// ---------

View file

@ -311,9 +311,70 @@ var sampleMoov =
0x00, 0x00, 0x00, 0x01, // entry_count
0x00, 0x00, 0x00, 0x01)))))); // chunk_offset
/**
* Generates generic emsg box data for both v0 and v1 boxes concats the messageData
* and returns the result as a Uint8Array. Passing any version other than 0 or 1 will
* return an invalid EMSG box.
*/
var generateEmsgBoxData = function(version, messageData) {
var emsgProps;
if (version === 0) {
emsgProps = new Uint8Array([
0x00, // version
0x00, 0x00, 0x00, //flags
0x75, 0x72, 0x6E, 0x3A, 0x66, 0x6F, 0x6F, 0x3A, 0x62, 0x61, 0x72, 0x3A, 0x32, 0x30, 0x32, 0x33, 0x00, // urn:foo:bar:2023\0
0x66, 0x6F, 0x6F, 0x2E, 0x62, 0x61, 0x72, 0x2E, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x00, // foo.bar.value\0
0x00, 0x00, 0x00, 0x64, // timescale = 100
0x00, 0x00, 0x03, 0xE8, // presentation_time_delta = 1000
0x00, 0x00, 0x00, 0x00, // event_duration = 0
0x00, 0x00, 0x00, 0x01 // id = 1
]);
} else if (version === 1) {
emsgProps = new Uint8Array([
0x01, // version
0x00, 0x00, 0x00, //flags
0x00, 0x00, 0x00, 0x64, // timescale = 100
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x10, // presentation_time = 10000
0x00, 0x00, 0x00, 0x01, // event_duration = 1
0x00, 0x00, 0x00, 0x02, // id = 2
0x75, 0x72, 0x6E, 0x3A, 0x66, 0x6F, 0x6F, 0x3A, 0x62, 0x61, 0x72, 0x3A, 0x32, 0x30, 0x32, 0x33, 0x00, // urn:foo:bar:2023\0
0x66, 0x6F, 0x6F, 0x2E, 0x62, 0x61, 0x72, 0x2E, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x00 // foo.bar.value\0
]);
} else if (version === 2) {
// Invalid version only
emsgProps = new Uint8Array([
0x02, // version
0x00, 0x00, 0x00, //flags
0x00, 0x00, 0x00, 0x64, // timescale = 100
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x10, // presentation_time = 10000
0x00, 0x00, 0x00, 0x01, // event_duration = 1
0x00, 0x00, 0x00, 0x02, // id = 2
0x75, 0x72, 0x6E, 0x3A, 0x66, 0x6F, 0x6F, 0x3A, 0x62, 0x61, 0x72, 0x3A, 0x32, 0x30, 0x32, 0x33, 0x00, // urn:foo:bar:2023\0
0x66, 0x6F, 0x6F, 0x2E, 0x62, 0x61, 0x72, 0x2E, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x00 // foo.bar.value\0
]);
} else {
emsgProps = new Uint8Array([
// malformed emsg data
0x00, 0x00, 0x00, 0x64, // timescale = 100
// no presentation_time
0x00, 0x00, 0x00, 0x01, // event_duration = 1
// no id
0x75, 0x72, 0x6E, 0x3A, 0x66, 0x6F, 0x6F, 0x3A, 0x62, 0x61, 0x72, 0x3A, 0x32, 0x30, 0x32, 0x33, 0x00, // urn:foo:bar:2023\0
0x66, 0x6F, 0x6F, 0x2E, 0x62, 0x61, 0x72, 0x2E, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x00 // foo.bar.value\0
]);
}
// concat the props and messageData
var retArr = new Uint8Array(emsgProps.length + messageData.length);
retArr.set(emsgProps);
retArr.set(messageData, emsgProps.length);
return retArr;
};
module.exports = {
typeBytes,
sampleMoov,
unityMatrix,
box
box,
generateEmsgBoxData
};