Repository: streamlink/streamlink
Pull request #1897 (2018-07-03 10:51:40)

stream.dash: fix bug where timeline_segments.t can be None
Fixes the issue reported by @back-to in #1895 


Patch:
diff --git a/src/streamlink/stream/dash_manifest.py b/src/streamlink/stream/dash_manifest.py
index e7389088..a227e894 100644
--- a/src/streamlink/stream/dash_manifest.py
+++ b/src/streamlink/stream/dash_manifest.py
@@ -630,7 +630,8 @@ class SegmentTimeline(MPDNode):
     def segments(self):
         t = 0
         for tsegment in self.timeline_segments:
-            t = t or tsegment.t
+            if t == 0 and tsegment.t is not None:
+                t = tsegment.t
             # check the start time from MPD
             for repeated_i in range(tsegment.r + 1):
                 yield self.TimelineSegment(t, tsegment.d)


Test patch:
diff --git a/tests/resources/dash/test_8.mpd b/tests/resources/dash/test_8.mpd
new file mode 100644
index 00000000..26201ce4
--- /dev/null
+++ b/tests/resources/dash/test_8.mpd
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="urn:mpeg:dash:schema:mpd:2011"
+ xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
+ minBufferTime="PT1S"
+ profiles="urn:mpeg:dash:profile:isoff-main:2011"
+ type="static"
+ mediaPresentationDuration="PT2920S">
+  <Period id="1" duration="PT2920S" start="PT0S">
+    <AdaptationSet id="0" mimeType="video/mp4" contentType="video" segmentAlignment="true" startWithSAP="1">
+      <Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"/>
+      <SegmentTemplate presentationTimeOffset="0" media="video-time=$Time$-$Bandwidth$-0.m4s?z32=CENSORED_SESSION" initialization="video-$Bandwidth$-0.mp4?z32=CENSORED_SESSION" timescale="1000" >
+        <SegmentTimeline>
+          <S d="4000" r="729"/>
+        </SegmentTimeline>
+      </SegmentTemplate>
+      <Representation id="0" codecs="avc1.4d4020" width="1024" height="576" frameRate="50" bandwidth="2799000"/>
+      <Representation id="1" codecs="avc1.4d401e" width="768" height="432" frameRate="25" bandwidth="1300000"/>
+      <Representation id="2" codecs="avc1.4d4015" width="512" height="288" frameRate="25" bandwidth="700000"/>
+      <Representation id="3" codecs="avc1.4d4015" width="512" height="288" frameRate="25" bandwidth="500000"/>
+    </AdaptationSet>
+    <AdaptationSet id="1" lang="de" mimeType="audio/mp4" contentType="audio" codecs="mp4a.40.2" segmentAlignment="true" startWithSAP="1">
+      <AudioChannelConfiguration schemeIdUri="urn:mpeg:mpegB:cicp:ChannelConfiguration" value="2"/>
+      <SegmentTemplate presentationTimeOffset="0" media="audio-time=$Time$-$Bandwidth$-0.m4s?z32=CENSORED_SESSION" initialization="audio-$Bandwidth$-0.mp4?z32=CENSORED_SESSION" timescale="1000" >
+        <SegmentTimeline>
+          <S d="3200" r="911"/>
+          <S d="1600"/>
+        </SegmentTimeline>
+      </SegmentTemplate>
+      <Representation id="4" bandwidth="128000"/>
+    </AdaptationSet>
+  </Period>
+</MPD>
\ No newline at end of file
diff --git a/tests/streams/test_dash_parser.py b/tests/streams/test_dash_parser.py
index 2eba726c..2aa3801a 100644
--- a/tests/streams/test_dash_parser.py
+++ b/tests/streams/test_dash_parser.py
@@ -200,3 +200,23 @@ class TestMPDParser(unittest.TestCase):
                                           'http://test.se/video/1013000.mp4',
                                           'http://test.se/video/1014000.mp4',
                                           'http://test.se/video/1015000.mp4'])
+
+
+    def test_tsegment_t_is_none_1895(self):
+        """
+            Verify the fix for https://github.com/streamlink/streamlink/issues/1895
+        """
+        with xml("dash/test_8.mpd") as mpd_xml:
+            mpd = MPD(mpd_xml, base_url="http://test.se/", url="http://test.se/manifest.mpd")
+
+            segments = mpd.periods[0].adaptationSets[0].representations[0].segments()
+            init_segment = next(segments)
+            self.assertEqual(init_segment.url, "http://test.se/video-2799000-0.mp4?z32=CENSORED_SESSION")
+
+            video_segments = [x.url for x in itertools.islice(segments, 3)]
+            self.assertSequenceEqual(video_segments,
+                                     ['http://test.se/video-time=0-2799000-0.m4s?z32=CENSORED_SESSION',
+                                      'http://test.se/video-time=4000-2799000-0.m4s?z32=CENSORED_SESSION',
+                                      'http://test.se/video-time=8000-2799000-0.m4s?z32=CENSORED_SESSION',
+                                      ])
+