feat: Add Be and tbd skill, also added Roadmap file

This commit is contained in:
2026-05-10 16:32:12 -04:00
parent 3500ade13f
commit 0bb8885802
29587 changed files with 10611695 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
const assert = require('assert');
const bufferIsEqual = require('arraybuffer-equal');
const deepStrictEqual = require('deep-strict-equal');
import AttrList from '../../../src/utils/attr-list';
describe('AttrList', () => {
it('constructor() supports empty arguments', () => {
assert.deepEqual(new AttrList(), {});
assert.deepEqual(new AttrList({}), {});
assert.deepEqual(new AttrList(undefined), {});
});
it('constructor() supports object argument', () => {
const obj = { VALUE: "42" };
const list = new AttrList(obj);
assert.strictEqual(list.decimalInteger('VALUE'), 42);
assert.strictEqual(Object.keys(list).length, 1);
});
it('parses valid decimalInteger attribute', () => {
assert.strictEqual(new AttrList('INT=42').decimalInteger('INT'), 42);
assert.strictEqual(new AttrList('INT=0').decimalInteger('INT'), 0);
assert.strictEqual(new AttrList('INT="42"').decimalInteger('INT'), 42);
});
it('parses attribute with leading space', () => {
assert.strictEqual(new AttrList(' INT=42').decimalInteger('INT'), 42);
assert.strictEqual(new AttrList(' INT=0').decimalInteger('INT'), 0);
assert.strictEqual(new AttrList(' INT="42"').decimalInteger('INT'), 42);
});
it('parses attribute with trailing space', () => {
assert.strictEqual(new AttrList('INT =42').decimalInteger('INT'), 42);
assert.strictEqual(new AttrList('INT =0').decimalInteger('INT'), 0);
assert.strictEqual(new AttrList('INT ="42"').decimalInteger('INT'), 42);
});
it('parses valid hexadecimalInteger attribute', () => {
assert.strictEqual(new AttrList('HEX=0x42').hexadecimalIntegerAsNumber('HEX'), 0x42);
assert.strictEqual(new AttrList('HEX=0X42').hexadecimalIntegerAsNumber('HEX'), 0x42);
assert.strictEqual(new AttrList('HEX=0x0').hexadecimalIntegerAsNumber('HEX'), 0);
assert.strictEqual(new AttrList('HEX="0x42"').hexadecimalIntegerAsNumber('HEX'), 0x42);
});
it('parses valid decimalFloatingPoint attribute', () => {
assert.strictEqual(new AttrList('FLOAT=0.42').decimalFloatingPoint('FLOAT'), 0.42);
assert.strictEqual(new AttrList('FLOAT=-0.42').decimalFloatingPoint('FLOAT'), -0.42);
assert.strictEqual(new AttrList('FLOAT=0').decimalFloatingPoint('FLOAT'), 0);
assert.strictEqual(new AttrList('FLOAT="0.42"').decimalFloatingPoint('FLOAT'), 0.42);
});
it('parses valid quotedString attribute', () => {
assert.strictEqual(new AttrList('STRING="hi"').STRING, 'hi');
assert.strictEqual(new AttrList('STRING=""').STRING, '');
});
it('parses exotic quotedString attribute', () => {
const list = new AttrList('STRING="hi,ENUM=OK,RES=4x2"');
assert.strictEqual(list.STRING, 'hi,ENUM=OK,RES=4x2');
assert.strictEqual(Object.keys(list).length, 1);
});
it('parses valid enumeratedString attribute', () => {
assert.strictEqual(new AttrList('ENUM=OK').enumeratedString('ENUM'), 'OK');
assert.strictEqual(new AttrList('ENUM="OK"').enumeratedString('ENUM'), 'OK');
});
it('parses exotic enumeratedString attribute', () => {
assert.strictEqual(new AttrList('ENUM=1').enumeratedString('ENUM'), '1');
assert.strictEqual(new AttrList('ENUM=A=B').enumeratedString('ENUM'), 'A=B');
assert.strictEqual(new AttrList('ENUM=A=B=C').enumeratedString('ENUM'), 'A=B=C');
const list = new AttrList('ENUM1=A=B=C,ENUM2=42');
assert.strictEqual(list.enumeratedString('ENUM1'), 'A=B=C');
assert.strictEqual(list.enumeratedString('ENUM2'), '42');
});
it('parses valid decimalResolution attribute', () => {
assert(deepStrictEqual(new AttrList('RES=400x200').decimalResolution('RES'), { width:400, height:200 }));
assert(deepStrictEqual(new AttrList('RES=0x0').decimalResolution('RES'), { width:0, height:0 }));
assert(deepStrictEqual(new AttrList('RES="400x200"').decimalResolution('RES'), { width:400, height:200 }));
});
it('handles invalid decimalResolution attribute', () => {
assert(deepStrictEqual(new AttrList('RES=400x-200').decimalResolution('RES'), undefined));
assert(deepStrictEqual(new AttrList('RES=400.5x200').decimalResolution('RES'), undefined));
assert(deepStrictEqual(new AttrList('RES=400x200.5').decimalResolution('RES'), undefined));
assert(deepStrictEqual(new AttrList('RES=400').decimalResolution('RES'), undefined));
assert(deepStrictEqual(new AttrList('RES=400x').decimalResolution('RES'), undefined));
assert(deepStrictEqual(new AttrList('RES=x200').decimalResolution('RES'), undefined));
assert(deepStrictEqual(new AttrList('RES=x').decimalResolution('RES'), undefined));
});
it('parses multiple attributes', () => {
const list = new AttrList('INT=42,HEX=0x42,FLOAT=0.42,STRING="hi",ENUM=OK,RES=4x2');
assert.strictEqual(list.decimalInteger('INT'), 42);
assert.strictEqual(list.hexadecimalIntegerAsNumber('HEX'), 0x42);
assert.strictEqual(list.decimalFloatingPoint('FLOAT'), 0.42);
assert.strictEqual(list.STRING, 'hi');
assert.strictEqual(list.enumeratedString('ENUM'), 'OK');
assert(deepStrictEqual(list.decimalResolution('RES'), { width:4, height:2 }));
assert.strictEqual(Object.keys(list).length, 6);
});
it('handles missing attributes', () => {
const list = new AttrList();
assert(isNaN(list.decimalInteger('INT')));
assert(isNaN(list.hexadecimalIntegerAsNumber('HEX')));
assert(isNaN(list.decimalFloatingPoint('FLOAT')));
assert.strictEqual(list.STRING, undefined);
assert.strictEqual(list.enumeratedString('ENUM'), undefined);
assert.strictEqual(list.decimalResolution('RES'), undefined);
assert.strictEqual(Object.keys(list).length, 0);
});
it('parses dashed attribute names', () => {
const list = new AttrList('INT-VALUE=42,H-E-X=0x42,-FLOAT=0.42,STRING-="hi",ENUM=OK');
assert.strictEqual(list.decimalInteger('INT-VALUE'), 42);
assert.strictEqual(list.hexadecimalIntegerAsNumber('H-E-X'), 0x42);
assert.strictEqual(list.decimalFloatingPoint('-FLOAT'), 0.42);
assert.strictEqual(list['STRING-'], 'hi');
assert.strictEqual(list.enumeratedString('ENUM'), 'OK');
assert.strictEqual(Object.keys(list).length, 5);
});
it('handles hexadecimalInteger conversions', () => {
const list = new AttrList('HEX1=0x0123456789abcdef0123456789abcdef,HEX2=0x123,HEX3=0x0');
assert(bufferIsEqual(list.hexadecimalInteger('HEX1').buffer, new Uint8Array([0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef]).buffer));
assert(bufferIsEqual(list.hexadecimalInteger('HEX2').buffer, new Uint8Array([0x01,0x23]).buffer));
assert(bufferIsEqual(list.hexadecimalInteger('HEX3').buffer, new Uint8Array([0x0]).buffer));
});
it('returns infinity on large number conversions', () => {
const list = new AttrList('VAL=12345678901234567890,HEX=0x0123456789abcdef0123456789abcdef');
assert.strictEqual(list.decimalInteger('VAL'), Infinity);
assert.strictEqual(list.hexadecimalIntegerAsNumber('HEX'), Infinity);
});
});

View File

@@ -0,0 +1,35 @@
var assert = require("assert");
import BinarySearch from '../../../src/utils/binary-search';
describe('binary search util', function() {
describe('search helper', function () {
var list = null;
var buildComparisonFunction = function(itemToSearchFor) {
return function(candidate) {
if (candidate < itemToSearchFor) {
return 1;
}
else if (candidate > itemToSearchFor) {
return -1;
}
return 0;
};
}
beforeEach(function() {
list = [4, 8, 15, 16, 23, 42];
});
it('finds the element if it is present', function () {
for(var i=0; i<list.length; i++) {
var item = list[i];
var foundItem = BinarySearch.search(list, buildComparisonFunction(item));
assert.strictEqual(foundItem, item);
}
});
it('does not find the element if it is not present', function () {
var item = 1000;
var foundItem = BinarySearch.search(list, buildComparisonFunction(item));
assert.strictEqual(foundItem, null);
});
});
});

View File

@@ -0,0 +1,285 @@
const assert = require('assert');
import { shouldAlignOnDiscontinuities, findDiscontinuousReferenceFrag, adjustPts, alignDiscontinuities } from '../../../src/utils/discontinuities';
const mockReferenceFrag = {
start: 20,
startPTS: 20,
endPTS: 24,
duration: 4,
cc: 0,
};
const mockFrags = [
{
start: 0,
startPTS: 0,
endPTS: 4,
duration: 4,
cc: 0,
},
{
start: 4,
startPTS: 4,
endPTS: 8,
duration: 4,
cc: 1
},
{
start: 8,
startPTS: 8,
endPTS: 16,
duration: 8,
cc: 1
}
];
describe('level-helper', function () {
it ('adjusts level fragments with overlapping CC range using a reference fragment', function () {
const details = {
fragments: mockFrags.slice(0),
PTSKnown: false
};
const expected = [
{
start: 20,
startPTS: 20,
endPTS: 24,
duration: 4,
cc: 0
},
{
start: 24,
startPTS: 24,
endPTS: 28,
duration: 4,
cc: 1
},
{
start: 28,
startPTS: 28,
endPTS: 36,
duration: 8,
cc: 1
}
];
adjustPts(mockReferenceFrag.start, details);
assert.deepEqual(expected, details.fragments);
assert.equal(true, details.PTSKnown);
});
it ('adjusts level fragments without overlapping CC range but with programDateTime info', function () {
const lastFrag = { cc : 0 };
const lastLevel = {
details : {
PTSKnown : true,
programDateTime : new Date('2017-08-28 00:00:00'),
fragments : [
{
start: 20,
startPTS: 20,
endPTS: 24,
duration: 4,
cc: 0,
},
{
start: 24,
startPTS: 24,
endPTS: 28,
duration: 4,
cc: 1
},
{
start: 28,
startPTS: 28,
endPTS: 36,
duration: 8,
cc: 1
}
]
}
};
var details = {
fragments: [
{
start: 0,
startPTS: 0,
endPTS: 4,
duration: 4,
cc: 2,
},
{
start: 4,
startPTS: 4,
endPTS: 8,
duration: 4,
cc: 2
},
{
start: 8,
startPTS: 8,
endPTS: 16,
duration: 8,
cc: 3
}
],
PTSKnown: false,
programDateTime : new Date('2017-08-28 00:00:50'),
startCC : 2,
endCC : 3
};
var detailsExpected = {
fragments : [
{
start: 70,
startPTS: 70,
endPTS: 74,
duration: 4,
cc: 2
},
{
start: 74,
startPTS: 74,
endPTS: 78,
duration: 4,
cc: 2
},
{
start: 78,
startPTS: 78,
endPTS: 86,
duration: 8,
cc: 3
}
],
PTSKnown: true,
programDateTime : new Date('2017-08-28 00:00:50'),
startCC : 2,
endCC : 3
};
alignDiscontinuities(lastFrag,lastLevel,details);
assert.deepEqual(detailsExpected,details);
});
it('finds the first fragment in an array which matches the CC of the first fragment in another array', function () {
const prevDetails = {
fragments: [mockReferenceFrag, { cc: 1 }]
};
const curDetails = {
fragments: mockFrags
};
const expected = mockReferenceFrag;
const actual = findDiscontinuousReferenceFrag(prevDetails, curDetails);
assert.equal(expected, actual);
});
it('returns undefined if there are no frags in the previous level', function () {
const expected = undefined;
const actual = findDiscontinuousReferenceFrag({ fragments: [] }, { fragments: mockFrags });
assert.equal(expected, actual);
});
it('returns undefined if there are no matching frags in the previous level', function () {
const expected = undefined;
const actual = findDiscontinuousReferenceFrag({ fragments: [{ cc: 10 }] }, { fragments: mockFrags });
assert.equal(expected, actual);
});
it('returns undefined if there are no frags in the current level', function () {
const expected = undefined;
const actual = findDiscontinuousReferenceFrag({ fragments: [{ cc: 0 }] }, { fragments: [] });
assert.equal(expected, actual);
});
it('should align current level when CC increases within the level', function () {
const lastLevel = {
details: {}
};
const curDetails = {
startCC: 0,
endCC: 1
};
const actual = shouldAlignOnDiscontinuities(null, lastLevel, curDetails);
assert.equal(true, actual);
});
it('should align current level when CC increases from last frag to current level', function () {
const lastLevel = {
details: {}
};
const lastFrag = {
cc: 0
};
const curDetails = {
startCC: 1,
endCC: 1
};
const actual = shouldAlignOnDiscontinuities(lastFrag, lastLevel, curDetails);
assert.equal(true, actual);
});
it('should not align when there is no CC increase', function () {
const lastLevel = {
details: {}
};
const curDetails = {
startCC: 1,
endCC: 1
};
const lastFrag = {
cc: 1
};
const actual = shouldAlignOnDiscontinuities(lastFrag, lastLevel, curDetails);
assert.equal(false, actual);
});
it('should not align when there is no previous level', function () {
const curDetails = {
startCC: 1,
endCC: 1
};
const lastFrag = {
cc: 1
};
const actual = shouldAlignOnDiscontinuities(lastFrag, null, curDetails);
assert.equal(false, actual);
});
it('should not align when there are no previous level details', function () {
const lastLevel = {
};
const curDetails = {
startCC: 1,
endCC: 1
};
const lastFrag = {
cc: 1
};
const actual = shouldAlignOnDiscontinuities(lastFrag, lastLevel, curDetails);
assert.equal(false, actual);
});
it('should not align when there are no current level details', function () {
const lastLevel = {
details: {}
};
const lastFrag = {
cc: 1
};
const actual = shouldAlignOnDiscontinuities(lastFrag, lastLevel, null);
assert.equal(false, actual);
});
});

View File

@@ -0,0 +1,68 @@
const assert = require('assert');
import OutputFilter from '../../../src/utils/output-filter';
describe('OutputFilter', () => {
let createMockTimelineController = () => {
let callCount = 0;
let lastCueArguments = null;
let captionsTrackCalled = false;
return {
addCues: (trackName, startTime, endTime, screen) => {
lastCueArguments = { trackName, startTime, endTime, screen };
callCount++;
},
createCaptionsTrack: (track) => {
captionsTrackCalled = true;
},
getCallCount: () => callCount,
getLastCueAdded: () => lastCueArguments,
didCaptionsTrackInvoke: () => captionsTrackCalled,
}
}
let timelineController, outputFilter;
beforeEach(function() {
timelineController = createMockTimelineController();
outputFilter = new OutputFilter(timelineController, 1);
});
it('handles new cue without dispatching', () => {
outputFilter.newCue(0, 1, {});
let lastCueAdded = timelineController.getLastCueAdded();
assert.strictEqual(lastCueAdded, null);
assert.strictEqual(timelineController.getCallCount(), 0);
assert.strictEqual(timelineController.didCaptionsTrackInvoke(), true);
});
it('handles single cue and dispatch', () => {
let lastScreen = {};
outputFilter.newCue(0, 1, lastScreen);
outputFilter.dispatchCue();
let lastCueAdded = timelineController.getLastCueAdded();
assert.strictEqual(lastCueAdded.screen, lastScreen);
assert.strictEqual(timelineController.getCallCount(), 1);
});
it('handles multiple cues and dispatch', () => {
outputFilter.newCue(0, 1, {});
outputFilter.newCue(1, 2, {});
let lastScreen = {};
outputFilter.newCue(3, 4, lastScreen);
outputFilter.dispatchCue();
let lastCueAdded = timelineController.getLastCueAdded();
assert.strictEqual(timelineController.getCallCount(), 1);
assert.strictEqual(lastCueAdded.startTime, 0);
assert.strictEqual(lastCueAdded.endTime, 4);
assert.strictEqual(lastCueAdded.screen, lastScreen);
});
it('does not dispatch empty cues', () => {
outputFilter.newCue(0, 1, {});
assert.strictEqual(timelineController.getCallCount(), 0);
outputFilter.dispatchCue();
assert.strictEqual(timelineController.getCallCount(), 1);
outputFilter.dispatchCue();
assert.strictEqual(timelineController.getCallCount(), 1);
});
});