Repository: globocom/m3u8

Issue:
Base Paths/URIs not applied to some types
Looks like some types that are derived from `BasePathMixin` (at least `PartialSegment`, `InitializationSegment`, `RenditionReport`) do not have their base_path or base_uri updated as they are not considered in the `M3U8` setter methods.

I will put a PR together soon.

Comments:

Patch:
diff --git a/m3u8/model.py b/m3u8/model.py
index 48bfd46..a5206ad 100644
--- a/m3u8/model.py
+++ b/m3u8/model.py
@@ -220,7 +220,9 @@ class M3U8(object):
         self._base_uri = new_base_uri
         self.media.base_uri = new_base_uri
         self.playlists.base_uri = new_base_uri
+        self.iframe_playlists.base_uri = new_base_uri
         self.segments.base_uri = new_base_uri
+        self.rendition_reports.base_uri = new_base_uri
         for key in self.keys:
             if key:
                 key.base_uri = new_base_uri
@@ -249,6 +251,8 @@ class M3U8(object):
         self.media.base_path = self._base_path
         self.segments.base_path = self._base_path
         self.playlists.base_path = self._base_path
+        self.iframe_playlists.base_path = self._base_path
+        self.rendition_reports.base_path = self._base_path
 
 
     def add_playlist(self, playlist):
@@ -411,7 +415,7 @@ class Segment(BasePathMixin):
         self.uri = uri
         self.duration = duration
         self.title = title
-        self.base_uri = base_uri
+        self._base_uri = base_uri
         self.byterange = byterange
         self.program_date_time = program_date_time
         self.current_program_date_time = current_program_date_time
@@ -422,9 +426,9 @@ class Segment(BasePathMixin):
         self.scte35 = scte35
         self.scte35_duration = scte35_duration
         self.key = keyobject
-        self.parts = PartialSegmentList( [ PartialSegment(base_uri=self.base_uri, **partial) for partial in parts ] if parts else [] )
+        self.parts = PartialSegmentList( [ PartialSegment(base_uri=self._base_uri, **partial) for partial in parts ] if parts else [] )
         if init_section is not None:
-            self.init_section = InitializationSection(self.base_uri, **init_section)
+            self.init_section = InitializationSection(self._base_uri, **init_section)
         else:
             self.init_section = None
 
@@ -492,6 +496,27 @@ class Segment(BasePathMixin):
     def __str__(self):
         return self.dumps(None)
 
+    @property
+    def base_path(self):
+        return super(Segment, self).base_path
+
+    @base_path.setter
+    def base_path(self, newbase_path):
+        super(Segment, self.__class__).base_path.fset(self, newbase_path)
+        self.parts.base_path = newbase_path
+        if self.init_section is not None:
+            self.init_section.base_path = newbase_path
+
+    @property
+    def base_uri(self):
+        return self._base_uri
+
+    @base_uri.setter
+    def base_uri(self, newbase_uri):
+        self._base_uri = newbase_uri
+        self.parts.base_uri = newbase_uri
+        if self.init_section is not None:
+            self.init_section.base_uri = newbase_uri
 
 class SegmentList(list, GroupedBasePathMixin):
 


Test patch:
diff --git a/tests/test_model.py b/tests/test_model.py
index f3523a3..a9d3ca1 100755
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -1099,15 +1099,17 @@ def test_add_rendition_report_to_playlist():
 
     obj.add_rendition_report(
         RenditionReport(
-            base_uri='',
+            base_uri=None,
             uri='../1M/waitForMSN.php',
             last_msn=273,
             last_part=0
         )
     )
 
+    obj.base_path = 'http://localhost/test'
+
     result = obj.dumps()
-    expected = '#EXT-X-RENDITION-REPORT:URI="../1M/waitForMSN.php",LAST-MSN=273,LAST-PART=0'
+    expected = '#EXT-X-RENDITION-REPORT:URI="http://localhost/test/waitForMSN.php",LAST-MSN=273,LAST-PART=0'
 
     assert expected in result
 
@@ -1119,7 +1121,7 @@ def test_add_part_to_segment():
 
     obj.add_part(
         PartialSegment(
-            '',
+            None,
             'filePart271.0.ts',
             0.33334
         )
@@ -1188,6 +1190,38 @@ def test_endswith_newline():
 
     assert manifest.endswith('#EXT-X-ENDLIST\n')
 
+def test_init_section_base_path_update():
+    obj = m3u8.M3U8(playlists.MULTIPLE_MAP_URI_PLAYLIST)
+
+    assert obj.segments[0].init_section.uri == 'init1.mp4'
+
+    obj.base_path = 'http://localhost/base_path'
+    obj.base_uri = 'http://localhost/base_uri'
+
+    assert obj.segments[0].init_section.uri == 'http://localhost/base_path/init1.mp4'
+    assert obj.segments[0].init_section.base_uri == 'http://localhost/base_uri'
+
+def test_iframe_playlists_base_path_update():
+    obj = m3u8.M3U8(playlists.VARIANT_PLAYLIST_WITH_IFRAME_PLAYLISTS)
+
+    assert obj.iframe_playlists[0].uri == 'video-800k-iframes.m3u8'
+    assert obj.iframe_playlists[0].base_uri == None
+
+    obj.base_path = 'http://localhost/base_path'
+    obj.base_uri = 'http://localhost/base_uri'
+
+    assert obj.iframe_playlists[0].uri == 'http://localhost/base_path/video-800k-iframes.m3u8'
+    assert obj.iframe_playlists[0].base_uri == 'http://localhost/base_uri'
+
+def test_partial_segment_base_path_update():
+    obj = m3u8.M3U8(playlists.LOW_LATENCY_DELTA_UPDATE_PLAYLIST)
+
+    obj.base_path = 'http://localhost/base_path'
+    obj.base_uri = 'http://localhost/base_uri'
+
+    assert obj.segments[2].parts[0].uri == 'http://localhost/base_path/filePart271.0.ts'
+    assert obj.segments[2].parts[0].base_uri == 'http://localhost/base_uri'
+
 # custom asserts