@@ -818,6 +818,7 @@ fileSequence8.m4s
818818 startCC : 10 , // w/o disco-sequence incremented
819819 endCC : 11 ,
820820 } ) ;
821+ expect ( details2 . fragments [ 0 ] ) . to . include ( { sn : 102 , cc : 11 } ) ;
821822 expect ( details2 . fragmentHint ) . to . include ( { sn : 109 , cc : 12 } ) ;
822823 expect ( mergedSequence1 ) . to . equal (
823824 '102-11,103-11,104-11,105-11,106-11,107-11,108-11' ,
@@ -845,13 +846,171 @@ fileSequence8.m4s
845846 startCC : 11 , // w/ disco-sequence incremented
846847 endCC : 11 ,
847848 } ) ;
848- expect ( details2 . fragmentHint ) . to . include ( { sn : 109 , cc : 12 } ) ;
849+ expect ( details3 . fragments [ 0 ] ) . to . include ( { sn : 102 , cc : 11 } ) ;
850+ expect ( details3 . fragmentHint ) . to . include ( { sn : 109 , cc : 12 } ) ;
849851 expect ( mergedSequence2 ) . to . equal (
850852 '102-11,103-11,104-11,105-11,106-11,107-11,108-11' ,
851853 ) ;
852854 expect ( details3 . playlistParsingError ) . to . be . null ;
853855 } ) ;
854856
857+ it ( 'handles delta Playlist updates with discontinuities and parts and no sequence change (missing PDT)' , function ( ) {
858+ const playlist = `#EXTM3U
859+ #EXT-X-VERSION:10
860+ #EXT-X-INDEPENDENT-SEGMENTS
861+ #EXT-X-TARGETDURATION:3
862+ #EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=3.126000,CAN-SKIP-UNTIL=20.0
863+ #EXT-X-PART-INF:PART-TARGET=1.000000
864+ #EXT-X-MEDIA-SEQUENCE:0
865+ #EXT-X-DISCONTINUITY-SEQUENCE:0
866+ #EXT-X-DATERANGE:ID="nmss-daterange",START-DATE="2025-05-22T07:59:38.809Z"
867+
868+ #EXT-X-MAP:URI="720p_0_0.m4s"
869+ #EXTINF:2.500000,
870+ 720p_0.m4v
871+ #EXTINF:2.500000,
872+ 720p_1.m4v
873+ #EXTINF:2.500000,
874+ 720p_2.m4v
875+ #EXTINF:2.184667,
876+ #EXT-X-DISCONTINUITY
877+ #EXT-X-MAP:URI="720p_0_1.m4s
878+ #EXTINF:2.500000,
879+ 720p_3.m4v
880+ #EXTINF:2.500000,
881+ 720p_4.m4v
882+ #EXTINF:2.500000,
883+ 720p_5.m4v
884+ #EXTINF:2.500000,
885+ 720p_6.m4v
886+ #EXTINF:2.500000,
887+ 720p_7.m4v
888+ #EXTINF:2.500000,
889+ 720p_8.m4v
890+ #EXT-X-PART:DURATION=1.000000,URI="720p_9_0.m4v",INDEPENDENT=YES
891+ #EXT-X-PART:DURATION=1.000000,URI="720p_9_1.m4v"
892+ #EXT-X-PART:DURATION=0.500000,URI="720p_9_2.m4v"
893+ #EXTINF:2.500000,
894+ 720p_9.m4v
895+ #EXT-X-PART:DURATION=1.000000,URI="720p_10_0.m4v",INDEPENDENT=YES
896+ #EXT-X-PART:DURATION=1.000000,URI="720p_10_1.m4v"
897+ #EXT-X-PART:DURATION=0.500000,URI="720p_10_2.m4v"
898+ #EXTINF:2.500000,
899+ 720p_10.m4v
900+ #EXT-X-PART:DURATION=1.000000,URI="720p_11_0.m4v",INDEPENDENT=YES
901+ #EXT-X-PART:DURATION=1.000000,URI="720p_11_1.m4v"
902+ #EXT-X-PART:DURATION=0.500000,URI="720p_11_2.m4v"
903+ #EXTINF:2.500000,
904+ 720p_11.m4v
905+ #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v",INDEPENDENT=YES
906+ #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v"
907+ #EXT-X-PRELOAD-HINT:TYPE=PART,URI="720p_12_2.m4v"` ;
908+ const deltaUpdate = `#EXTM3U
909+ #EXT-X-VERSION:10
910+ #EXT-X-INDEPENDENT-SEGMENTS
911+ #EXT-X-TARGETDURATION:3
912+ #EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=3.126000,CAN-SKIP-UNTIL=20.0
913+ #EXT-X-PART-INF:PART-TARGET=1.000000
914+ #EXT-X-MEDIA-SEQUENCE:0
915+ #EXT-X-DISCONTINUITY-SEQUENCE:0
916+ #EXT-X-DATERANGE:ID="nmss-daterange",START-DATE="2025-05-22T07:59:38.809Z"
917+ #EXT-X-SKIP:SKIPPED-SEGMENTS=4
918+
919+ #EXT-X-MAP:URI="720p_0_1.m4s"
920+ 720p_4.m4v
921+ #EXTINF:2.500000,
922+ 720p_5.m4v
923+ #EXTINF:2.500000,
924+ 720p_6.m4v
925+ #EXTINF:2.500000,
926+ 720p_7.m4v
927+ #EXTINF:2.500000,
928+ 720p_8.m4v
929+ #EXT-X-PART:DURATION=1.000000,URI="720p_9_0.m4v",INDEPENDENT=YES
930+ #EXT-X-PART:DURATION=1.000000,URI="720p_9_1.m4v"
931+ #EXT-X-PART:DURATION=0.500000,URI="720p_9_2.m4v"
932+ #EXTINF:2.500000,
933+ 720p_9.m4v
934+ #EXT-X-PART:DURATION=1.000000,URI="720p_10_0.m4v",INDEPENDENT=YES
935+ #EXT-X-PART:DURATION=1.000000,URI="720p_10_1.m4v"
936+ #EXT-X-PART:DURATION=0.500000,URI="720p_10_2.m4v"
937+ #EXTINF:2.500000,
938+ 720p_10.m4v
939+ #EXT-X-PART:DURATION=1.000000,URI="720p_11_0.m4v",INDEPENDENT=YES
940+ #EXT-X-PART:DURATION=1.000000,URI="720p_11_1.m4v"
941+ #EXT-X-PART:DURATION=0.500000,URI="720p_11_2.m4v"
942+ #EXTINF:2.500000,
943+ 720p_11.m4v
944+ #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v",INDEPENDENT=YES
945+ #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v"
946+ #EXT-X-PART:DURATION=1.000000,URI="720p_12_0.m4v"
947+ #EXTINF:2.500000,
948+ 720p_12.m4v
949+ #EXT-X-PRELOAD-HINT:TYPE=PART,URI="720p_13_0.m4` ;
950+
951+ const details1 = parseLevelPlaylist ( playlist ) ;
952+ const details2 = parseLevelPlaylist ( deltaUpdate ) ;
953+ expect ( details1 , 'details1' ) . to . include ( {
954+ live : true ,
955+ canSkipUntil : 20 ,
956+ totalduration : 32 ,
957+ startSN : 0 ,
958+ endSN : 11 ,
959+ fragmentStart : 0 ,
960+ lastPartSn : 12 ,
961+ lastPartIndex : 1 ,
962+ startCC : 0 ,
963+ endCC : 1 ,
964+ } ) ;
965+
966+ expect (
967+ details2 ,
968+ 'delta w/o disco seq incremented before merging' ,
969+ ) . to . include ( {
970+ live : true ,
971+ skippedSegments : 4 ,
972+ canSkipUntil : 20 ,
973+ totalduration : 32 ,
974+ startSN : 0 ,
975+ endSN : 12 ,
976+ lastPartSn : 12 ,
977+ lastPartIndex : 2 ,
978+ startCC : 0 ,
979+ endCC : 0 , // endCC of delta playlist will not be incremented until merged
980+ } ) ;
981+ expect (
982+ details2 . fragments ,
983+ 'delta w/o disco seq incremented fragments' ,
984+ ) . to . have . lengthOf ( 13 ) ;
985+
986+ // This delta update does not increment discontinuity-sequence (discontinuity tag would appear before first segment)
987+ details2 . reloaded ( details1 ) ;
988+ expect ( details2 , 'delta w/o disco seq incremented reloaded' ) . to . include ( {
989+ misses : 0 ,
990+ advanced : true ,
991+ updated : true ,
992+ } ) ;
993+ // discontinuity sequence numbers (frag.cc) should be carried over
994+ mergeDetails ( details1 , details2 ) ;
995+ const mergedSequence1 = getFragmentSequenceNumbers ( details2 ) ;
996+ expect (
997+ details2 ,
998+ `delta w/o disco seq incremented merged (${ mergedSequence1 } )` ,
999+ ) . to . include ( {
1000+ skippedSegments : 4 ,
1001+ deltaUpdateFailed : false ,
1002+ startSN : 0 ,
1003+ endSN : 12 ,
1004+ startCC : 0 ,
1005+ endCC : 1 , // incremented after merging
1006+ } ) ;
1007+ expect ( details2 . fragmentHint ) . to . include ( { sn : 13 , cc : 1 } ) ;
1008+ expect ( mergedSequence1 ) . to . equal (
1009+ '0-0,1-0,2-0,3-1,4-1,5-1,6-1,7-1,8-1,9-1,10-1,11-1,12-1' ,
1010+ ) ;
1011+ expect ( details2 . playlistParsingError ) . to . be . null ;
1012+ } ) ;
1013+
8551014 it ( 'handles delta Playlist updates with multiple skip tags and removed date ranges' , function ( ) {
8561015 const playlist = `#EXTM3U
8571016#EXT-X-TARGETDURATION:6
0 commit comments