-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add utility for processing DASH segment template URIs
see: #129 Signed-off-by: Casey Occhialini <1508707+littlespex@users.noreply.github.com>
- Loading branch information
1 parent
4ae7ed0
commit 35176c1
Showing
9 changed files
with
873 additions
and
150 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { processUriTemplate } from './dash/processUriTemplate.js'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
const re = /\$(RepresentationID|Number|SubNumber|Bandwidth|Time)?(?:%0([0-9]+)([diouxX]))?\$/g; | ||
|
||
/** | ||
* Process a URI template used in `SegmentTemplate` nodes. | ||
* | ||
* @param uriTemplate - URI template to process. | ||
* @param representationId - Representation ID. | ||
* @param number - Number. | ||
* @param subNumber - Sub-number. | ||
* @param bandwidth - Bandwidth. | ||
* @param time - Time. | ||
* | ||
* @returns Processed URI template. | ||
* | ||
* @group DASH | ||
* @beta | ||
* | ||
* @example | ||
* ```ts | ||
* const uriTemplate = ; | ||
* const result = processUriTemplate( | ||
* 'http://example.com/$RepresentationID$/$Number$/$SubNumber$/$Bandwidth$/$Time%02d$/$$', | ||
* 'rep1', | ||
* 1, | ||
* 2, | ||
* 3, | ||
* 4, | ||
* ); | ||
* console.log(result); | ||
* // -> 'http://example.com/rep1/1/2/3/04/$' | ||
* ``` | ||
*/ | ||
export function processUriTemplate( | ||
uriTemplate: string, | ||
representationId: string | null | undefined, | ||
number: number | null | undefined, | ||
subNumber: number | null | undefined, | ||
bandwidth: number | null | undefined, | ||
time: number | null | undefined, | ||
) { | ||
const uri = uriTemplate.replace(re, (match, name, widthStr, format) => { | ||
let value: string | number | null | undefined; | ||
|
||
switch (name) { | ||
case undefined: // $$ case | ||
return '$'; | ||
|
||
case 'RepresentationID': | ||
value = representationId; | ||
break; | ||
|
||
case 'Number': | ||
value = number; | ||
break; | ||
|
||
case 'SubNumber': | ||
value = subNumber; | ||
break; | ||
|
||
case 'Bandwidth': | ||
value = bandwidth; | ||
break; | ||
|
||
case 'Time': | ||
value = time ? Math.round(time) : time; | ||
break; | ||
|
||
default: | ||
value = null; | ||
} | ||
|
||
if (value == null) { | ||
return match; | ||
} | ||
|
||
let valueString: string; | ||
|
||
switch (format) { | ||
case undefined: // Happens if there is no format specifier. | ||
case 'd': | ||
case 'i': | ||
case 'u': | ||
valueString = value.toString(); | ||
break; | ||
|
||
case 'o': | ||
valueString = value.toString(8); | ||
break; | ||
|
||
case 'x': | ||
valueString = value.toString(16); | ||
break; | ||
|
||
case 'X': | ||
valueString = value.toString(16).toUpperCase(); | ||
break; | ||
|
||
default: | ||
valueString = value.toString(); | ||
break; | ||
} | ||
|
||
const width = parseInt(widthStr, 10) || 1; | ||
return valueString.padStart(width, '0'); | ||
}); | ||
|
||
return uri; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
import { processUriTemplate } from '@svta/common-media-library'; | ||
import { equal } from 'node:assert'; | ||
import { describe, it } from 'node:test'; | ||
|
||
describe('processUriTemplate', () => { | ||
it('handles a single RepresentationID identifier', () => { | ||
equal( | ||
processUriTemplate( | ||
'/example/$RepresentationID$.mp4', | ||
'100', null, null, null, null, | ||
), | ||
'/example/100.mp4', | ||
); | ||
|
||
// RepresentationID cannot use a width specifier. | ||
equal( | ||
processUriTemplate( | ||
'/example/$RepresentationID%01d$.mp4', | ||
'100', null, null, null, null, | ||
), | ||
'/example/100.mp4', | ||
); | ||
|
||
equal( | ||
processUriTemplate( | ||
'/example/$RepresentationID$.mp4', | ||
null, null, null, null, null, | ||
), | ||
'/example/$RepresentationID$.mp4', | ||
); | ||
}); | ||
|
||
it('handles a single Number identifier', () => { | ||
equal( | ||
processUriTemplate( | ||
'/example/$Number$.mp4', | ||
null, 100, null, null, null, | ||
), | ||
'/example/100.mp4', | ||
); | ||
|
||
equal( | ||
processUriTemplate( | ||
'/example/$Number%05d$.mp4', | ||
null, 100, null, null, null, | ||
), | ||
'/example/00100.mp4', | ||
); | ||
|
||
equal( | ||
processUriTemplate( | ||
'/example/$Number$.mp4', | ||
null, null, null, null, null, | ||
), | ||
'/example/$Number$.mp4', | ||
); | ||
}); | ||
|
||
it('handles a single SubNumber identifier', () => { | ||
equal( | ||
processUriTemplate( | ||
'/example/$SubNumber$.mp4', | ||
null, null, 100, null, null, | ||
), | ||
'/example/100.mp4', | ||
); | ||
|
||
equal( | ||
processUriTemplate( | ||
'/example/$SubNumber%05d$.mp4', | ||
null, null, 100, null, null, | ||
), | ||
'/example/00100.mp4', | ||
); | ||
|
||
equal( | ||
processUriTemplate( | ||
'/example/$SubNumber$.mp4', | ||
null, null, null, null, null, | ||
), | ||
'/example/$SubNumber$.mp4', | ||
); | ||
}); | ||
|
||
it('handles a single Bandwidth identifier', () => { | ||
equal( | ||
processUriTemplate( | ||
'/example/$Bandwidth$.mp4', | ||
null, null, null, 100, null, | ||
), | ||
'/example/100.mp4', | ||
); | ||
|
||
equal( | ||
processUriTemplate( | ||
'/example/$Bandwidth%05d$.mp4', | ||
null, null, null, 100, null, | ||
), | ||
'/example/00100.mp4', | ||
); | ||
|
||
equal( | ||
processUriTemplate( | ||
'/example/$Bandwidth$.mp4', | ||
null, null, null, null, null, | ||
), | ||
'/example/$Bandwidth$.mp4', | ||
); | ||
}); | ||
|
||
it('handles a single Time identifier', () => { | ||
equal( | ||
processUriTemplate( | ||
'/example/$Time$.mp4', | ||
null, null, null, null, 100, | ||
), | ||
'/example/100.mp4', | ||
); | ||
|
||
equal( | ||
processUriTemplate( | ||
'/example/$Time%05d$.mp4', | ||
null, null, null, null, 100, | ||
), | ||
'/example/00100.mp4', | ||
); | ||
|
||
equal( | ||
processUriTemplate( | ||
'/example/$Time$.mp4', | ||
null, null, null, null, null, | ||
), | ||
'/example/$Time$.mp4', | ||
); | ||
}); | ||
|
||
it('handles rounding errors for calculated Times', () => { | ||
equal( | ||
processUriTemplate( | ||
'/example/$Time$.mp4', | ||
null, null, null, null, 100.0001, | ||
), | ||
'/example/100.mp4', | ||
); | ||
|
||
equal( | ||
processUriTemplate( | ||
'/example/$Time%05d$.mp4', | ||
null, null, null, null, 99.9999, | ||
), | ||
'/example/00100.mp4', | ||
); | ||
}); | ||
|
||
it('handles multiple identifiers', () => { | ||
equal( | ||
processUriTemplate( | ||
'/example/$RepresentationID$_$Number$_$SubNumber$_$Bandwidth$_$Time$.mp4', | ||
'1', 2, 3, 4, 5, | ||
), | ||
'/example/1_2_3_4_5.mp4', | ||
); | ||
|
||
// No spaces. | ||
equal( | ||
processUriTemplate( | ||
'/example/$RepresentationID$$Number$$SubNumber$$Bandwidth$$Time$.mp4', | ||
'1', 2, 3, 4, 5, | ||
), | ||
'/example/12345.mp4', | ||
); | ||
|
||
// Different order. | ||
equal( | ||
processUriTemplate( | ||
'/example/$SubNumber$_$Bandwidth$_$Time$_$RepresentationID$_$Number$.mp4', | ||
'1', 2, 3, 4, 5, | ||
), | ||
'/example/3_4_5_1_2.mp4', | ||
); | ||
|
||
// Single width. | ||
equal( | ||
processUriTemplate( | ||
'$RepresentationID$_$Number%01d$_$SubNumber%01d$_$Bandwidth%01d$_$Time%01d$', | ||
'1', 2, 3, 4, 500, | ||
), | ||
'1_2_3_4_500', | ||
); | ||
|
||
// Different widths. | ||
equal( | ||
processUriTemplate( | ||
'$RepresentationID$_$Number%02d$_$SubNumber%02d$_$Bandwidth%02d$_$Time%02d$', | ||
'1', 2, 3, 4, 5, | ||
), | ||
'1_02_03_04_05', | ||
); | ||
|
||
// Double $$. | ||
equal( | ||
processUriTemplate( | ||
'$$/$RepresentationID$$$$Number$$$$SubNumber$$$$Bandwidth$$$$Time$$$.$$', | ||
'1', 2, 3, 4, 5, | ||
), | ||
'$/1$2$3$4$5$.$', | ||
); | ||
}); | ||
|
||
it('handles invalid identifiers', () => { | ||
equal( | ||
processUriTemplate( | ||
'/example/$Garbage$.mp4', | ||
'1', 2, 3, 4, 5, | ||
), | ||
'/example/$Garbage$.mp4', | ||
); | ||
|
||
equal( | ||
processUriTemplate( | ||
'/example/$Time.mp4', | ||
'1', 2, 3, 4, 5, | ||
), | ||
'/example/$Time.mp4', | ||
); | ||
}); | ||
|
||
it('handles non-decimal format specifiers', () => { | ||
equal( | ||
processUriTemplate( | ||
'/$Number%05x$_$Number%01X$_$Number%01u$_$Number%01o$.mp4', | ||
'', 180, 0, 0, 0, | ||
), | ||
'/000b4_B4_180_264.mp4', | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.