Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: merge dateRange tags with same IDs and no conflicting attributes #175

Merged
merged 4 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -685,21 +685,28 @@ export default class Parser extends Stream {
}
if (dateRange.duration && dateRange.endDate) {
const startDate = dateRange.startDate;
const newDateInSeconds = startDate.setSeconds(startDate.getSeconds() + dateRange.duration);
const newDateInSeconds = startDate.getTime() + (dateRange.duration * 1000);
Copy link
Contributor Author

@harisha-swaminathan harisha-swaminathan Aug 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't know why this was startDate.setSeconds(startDate.getSeconds() + dateRange.duration);
I think the setSeconds was a typo and was meant to be `getSeconds()'.

For more context on this block of code, the spec mentions
If a Date Range contains both a DURATION attribute and an END-DATE attribute, the value of the END-DATE attribute MUST be equal to the value of the START-DATE attribute plus the value of the DURATION attribute.
https://datatracker.ietf.org/doc/html/draft-pantos-http-live-streaming-23#section-4.3.2.7


this.manifest.dateRanges[index].endDate = new Date(newDateInSeconds);
}
if (!dateRangeTags[dateRange.id]) {
dateRangeTags[dateRange.id] = dateRange;
} else {
for (const attribute in dateRangeTags[dateRange.id]) {
if (dateRangeTags[dateRange.id][attribute] !== dateRange[attribute]) {
if (!!dateRange[attribute] && JSON.stringify(dateRangeTags[dateRange.id][attribute]) !== JSON.stringify(dateRange[attribute])) {
this.trigger('warn', {
message: 'EXT-X-DATERANGE tags with the same ID in a playlist must have the same attributes and same attribute values'
message: 'EXT-X-DATERANGE tags with the same ID in a playlist must have the same attributes values'
});
break;
}
}
// if tags with the same ID do not have conflicting attributes, merge them
const dateRangeWithSameId = this.manifest.dateRanges.findIndex((dateRangeToFind) => dateRangeToFind.id === dateRange.id);

this.manifest.dateRanges[dateRangeWithSameId] = Object.assign(this.manifest.dateRanges[dateRangeWithSameId], dateRange);
dateRangeTags[dateRange.id] = Object.assign(dateRangeTags[dateRange.id], dateRange);
// after merging, delete the duplicate dateRange that was added last
this.manifest.dateRanges.pop();
}
},
'independent-segments'() {
Expand Down
34 changes: 30 additions & 4 deletions test/parser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,7 @@ QUnit.module('m3u8s', function(hooks) {
);
});

QUnit.test('warns when playlist has multiple #EXT-X-DATERANGE tag same ID but different attribute names and values', function(assert) {
QUnit.test('warns when playlist has multiple #EXT-X-DATERANGE tag same ID but different attribute values', function(assert) {
this.parser.push([
'#EXT-X-VERSION:3',
'#EXT-X-MEDIA-SEQUENCE:0',
Expand All @@ -1041,12 +1041,12 @@ QUnit.module('m3u8s', function(hooks) {
'#EXT-X-ENDLIST',
'#EXT-X-PROGRAM-DATE-TIME:2017-07-31T20:35:35.053+00:00',
'#EXT-X-DATERANGE:ID="12345",START-DATE="2023-04-13T18:16:15.840000Z",END-ON-NEXT=YES,CLASS="CLASSATTRIBUTE"',
'#EXT-X-DATERANGE:ID="12345",START-DATE="2023-04-13T18:16:20.840000Z"'
'#EXT-X-DATERANGE:ID="12345",START-DATE="2023-04-13T18:16:15.840000Z",CLASS="CLASSATTRIBUTE1"'
].join('\n'));
this.parser.end();

const warnings = [
'EXT-X-DATERANGE tags with the same ID in a playlist must have the same attributes and same attribute values'
'EXT-X-DATERANGE tags with the same ID in a playlist must have the same attributes values'
];

assert.deepEqual(
Expand Down Expand Up @@ -1095,7 +1095,33 @@ QUnit.module('m3u8s', function(hooks) {
);
});

QUnit.test(' playlist with multiple ext-x-daterange ', function(assert) {
QUnit.test('playlist with multiple ext-x-daterange with same ID but no conflicting attributes', function(assert) {
const expectedDateRange = {
id: '12345',
scte35In: '0xFC30200FFF2',
scte35Out: '0xFC30200FFF2',
startDate: new Date('2023-04-13T18:16:15.840000Z'),
class: 'CLASSATTRIBUTE'
};

this.parser.push([
'#EXT-X-VERSION:3',
'#EXT-X-MEDIA-SEQUENCE:0',
'#EXT-X-DISCONTINUITY-SEQUENCE:0',
'#EXTINF:10,',
'media-00001.ts',
'#EXT-X-ENDLIST',
'#EXT-X-PROGRAM-DATE-TIME:2017-07-31T20:35:35.053+00:00',
'#EXT-X-DATERANGE:ID="12345",SCTE35-IN=0xFC30200FFF2,START-DATE="2023-04-13T18:16:15.840000Z",CLASS="CLASSATTRIBUTE"',
'#EXT-X-DATERANGE:ID="12345",SCTE35-OUT=0xFC30200FFF2,START-DATE="2023-04-13T18:16:15.840000Z"'
].join('\n'));
this.parser.end();
assert.equal(this.parser.manifest.dateRanges.length, 1, 'two dateranges with same ID are merged');
assert.deepEqual(this.parser.manifest.dateRanges[0], expectedDateRange);

});

QUnit.test('playlist with multiple ext-x-daterange ', function(assert) {
this.parser.push([
' #EXTM3U',
'#EXT-X-VERSION:6',
Expand Down