diff --git a/src/parser.js b/src/parser.js index 5f32e55..20bb5c9 100644 --- a/src/parser.js +++ b/src/parser.js @@ -685,7 +685,7 @@ 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); this.manifest.dateRanges[index].endDate = new Date(newDateInSeconds); } @@ -693,13 +693,20 @@ export default class Parser extends Stream { 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'() { diff --git a/test/parser.test.js b/test/parser.test.js index 25f6c14..7565b32 100644 --- a/test/parser.test.js +++ b/test/parser.test.js @@ -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', @@ -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( @@ -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',