openm3u8 7.0.0__cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
openm3u8/protocol.py ADDED
@@ -0,0 +1,47 @@
1
+ # Copyright 2014 Globo.com Player authors. All rights reserved.
2
+ # Modifications Copyright (c) 2026 Wurl.
3
+ # Use of this source code is governed by a MIT License
4
+ # license that can be found in the LICENSE file.
5
+
6
+ ext_m3u = "#EXTM3U"
7
+ ext_x_targetduration = "#EXT-X-TARGETDURATION"
8
+ ext_x_media_sequence = "#EXT-X-MEDIA-SEQUENCE"
9
+ ext_x_discontinuity_sequence = "#EXT-X-DISCONTINUITY-SEQUENCE"
10
+ ext_x_program_date_time = "#EXT-X-PROGRAM-DATE-TIME"
11
+ ext_x_media = "#EXT-X-MEDIA"
12
+ ext_x_playlist_type = "#EXT-X-PLAYLIST-TYPE"
13
+ ext_x_key = "#EXT-X-KEY"
14
+ ext_x_stream_inf = "#EXT-X-STREAM-INF"
15
+ ext_x_version = "#EXT-X-VERSION"
16
+ ext_x_allow_cache = "#EXT-X-ALLOW-CACHE"
17
+ ext_x_endlist = "#EXT-X-ENDLIST"
18
+ extinf = "#EXTINF"
19
+ ext_i_frames_only = "#EXT-X-I-FRAMES-ONLY"
20
+ ext_x_asset = "#EXT-X-ASSET"
21
+ ext_x_bitrate = "#EXT-X-BITRATE"
22
+ ext_x_byterange = "#EXT-X-BYTERANGE"
23
+ ext_x_i_frame_stream_inf = "#EXT-X-I-FRAME-STREAM-INF"
24
+ ext_x_discontinuity = "#EXT-X-DISCONTINUITY"
25
+ ext_x_cue_out = "#EXT-X-CUE-OUT"
26
+ ext_x_cue_out_cont = "#EXT-X-CUE-OUT-CONT"
27
+ ext_x_cue_in = "#EXT-X-CUE-IN"
28
+ ext_x_cue_span = "#EXT-X-CUE-SPAN"
29
+ ext_oatcls_scte35 = "#EXT-OATCLS-SCTE35"
30
+ ext_is_independent_segments = "#EXT-X-INDEPENDENT-SEGMENTS"
31
+ ext_x_map = "#EXT-X-MAP"
32
+ ext_x_start = "#EXT-X-START"
33
+ ext_x_server_control = "#EXT-X-SERVER-CONTROL"
34
+ ext_x_part_inf = "#EXT-X-PART-INF"
35
+ ext_x_part = "#EXT-X-PART"
36
+ ext_x_rendition_report = "#EXT-X-RENDITION-REPORT"
37
+ ext_x_skip = "#EXT-X-SKIP"
38
+ ext_x_session_data = "#EXT-X-SESSION-DATA"
39
+ ext_x_session_key = "#EXT-X-SESSION-KEY"
40
+ ext_x_preload_hint = "#EXT-X-PRELOAD-HINT"
41
+ ext_x_daterange = "#EXT-X-DATERANGE"
42
+ ext_x_gap = "#EXT-X-GAP"
43
+ ext_x_content_steering = "#EXT-X-CONTENT-STEERING"
44
+ ext_x_image_stream_inf = "#EXT-X-IMAGE-STREAM-INF"
45
+ ext_x_images_only = "#EXT-X-IMAGES-ONLY"
46
+ ext_x_tiles = "#EXT-X-TILES"
47
+ ext_x_blackout = "#EXT-X-BLACKOUT"
@@ -0,0 +1,37 @@
1
+ from openm3u8 import protocol
2
+ from openm3u8.version_matching_rules import VersionMatchingError, available_rules
3
+
4
+
5
+ def get_version(file_lines: list[str]):
6
+ for line in file_lines:
7
+ if line.startswith(protocol.ext_x_version):
8
+ version = line.split(":")[1]
9
+ return float(version)
10
+
11
+ return None
12
+
13
+
14
+ def valid_in_all_rules(
15
+ line_number: int, line: str, version: float
16
+ ) -> list[VersionMatchingError]:
17
+ errors = []
18
+ for rule in available_rules:
19
+ validator = rule(version, line_number, line)
20
+
21
+ if not validator.validate():
22
+ errors.append(validator.get_error())
23
+
24
+ return errors
25
+
26
+
27
+ def validate(file_lines: list[str]) -> list[VersionMatchingError]:
28
+ found_version = get_version(file_lines)
29
+ if found_version is None:
30
+ return []
31
+
32
+ errors = []
33
+ for number, line in enumerate(file_lines):
34
+ errors_in_line = valid_in_all_rules(number, line, found_version)
35
+ errors.extend(errors_in_line)
36
+
37
+ return errors
@@ -0,0 +1,108 @@
1
+ from dataclasses import dataclass
2
+
3
+ from openm3u8 import protocol
4
+
5
+
6
+ @dataclass
7
+ class VersionMatchingError(Exception):
8
+ line_number: int
9
+ line: str
10
+ how_to_fix: str = "Please fix the version matching error."
11
+ description: str = "There is a version matching error in the file."
12
+
13
+ def __str__(self):
14
+ return (
15
+ "Version matching error found in the file when parsing in strict mode.\n"
16
+ f"Line {self.line_number}: {self.description}\n"
17
+ f"Line content: {self.line}\n"
18
+ f"How to fix: {self.how_to_fix}"
19
+ "\n"
20
+ )
21
+
22
+
23
+ class VersionMatchRuleBase:
24
+ description: str = ""
25
+ how_to_fix: str = ""
26
+ version: float
27
+ line_number: int
28
+ line: str
29
+
30
+ def __init__(self, version: float, line_number: int, line: str) -> None:
31
+ self.version = version
32
+ self.line_number = line_number
33
+ self.line = line
34
+
35
+ def validate(self):
36
+ raise NotImplementedError
37
+
38
+ def get_error(self):
39
+ return VersionMatchingError(
40
+ line_number=self.line_number,
41
+ line=self.line,
42
+ description=self.description,
43
+ how_to_fix=self.how_to_fix,
44
+ )
45
+
46
+
47
+ class ValidIVInEXTXKEY(VersionMatchRuleBase):
48
+ description = (
49
+ "You must use at least protocol version 2 if you have IV in EXT-X-KEY."
50
+ )
51
+ how_to_fix = "Change the protocol version to 2 or higher."
52
+
53
+ def validate(self):
54
+ if protocol.ext_x_key not in self.line:
55
+ return True
56
+
57
+ if "IV" in self.line:
58
+ return self.version >= 2
59
+
60
+ return True
61
+
62
+
63
+ class ValidFloatingPointEXTINF(VersionMatchRuleBase):
64
+ description = "You must use at least protocol version 3 if you have floating point EXTINF duration values."
65
+ how_to_fix = "Change the protocol version to 3 or higher."
66
+
67
+ def validate(self):
68
+ if protocol.extinf not in self.line:
69
+ return True
70
+
71
+ chunks = self.line.replace(protocol.extinf + ":", "").split(",", 1)
72
+ duration = chunks[0]
73
+
74
+ def is_number(value: str):
75
+ try:
76
+ float(value)
77
+ return True
78
+ except ValueError:
79
+ return False
80
+
81
+ def is_floating_number(value: str):
82
+ return is_number(value) and "." in value
83
+
84
+ if is_floating_number(duration):
85
+ return self.version >= 3
86
+
87
+ return is_number(duration)
88
+
89
+
90
+ class ValidEXTXBYTERANGEOrEXTXIFRAMESONLY(VersionMatchRuleBase):
91
+ description = "You must use at least protocol version 4 if you have EXT-X-BYTERANGE or EXT-X-IFRAME-ONLY."
92
+ how_to_fix = "Change the protocol version to 4 or higher."
93
+
94
+ def validate(self):
95
+ if (
96
+ protocol.ext_x_byterange not in self.line
97
+ and protocol.ext_i_frames_only not in self.line
98
+ ):
99
+ return True
100
+
101
+ return self.version >= 4
102
+
103
+
104
+ available_rules: list[type[VersionMatchRuleBase]] = [
105
+ ValidIVInEXTXKEY,
106
+ ValidFloatingPointEXTINF,
107
+ ValidEXTXBYTERANGEOrEXTXIFRAMESONLY,
108
+ ]
@@ -0,0 +1,122 @@
1
+ Metadata-Version: 2.4
2
+ Name: openm3u8
3
+ Version: 7.0.0
4
+ Summary: Python m3u8 parser
5
+ Author: wurl.com
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/openwurl/openm3u8
8
+ Project-URL: Repository, https://github.com/openwurl/openm3u8
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: backports-datetime-fromisoformat; python_version < "3.11"
17
+ Provides-Extra: dev
18
+ Requires-Dist: bottle; extra == "dev"
19
+ Requires-Dist: pytest>=9.0.1; extra == "dev"
20
+ Requires-Dist: pytest-cov>=7.0.0; extra == "dev"
21
+ Dynamic: license-file
22
+
23
+ # openm3u8
24
+
25
+ `openm3u8` is a Python library for parsing .m3u8 files, which are used as [HTTP Live Streaming (HLS)](https://tools.ietf.org/html/rfc8216) playlists.
26
+
27
+ This project is a fork of [m3u8](https://github.com/globocom/m3u8/) and is maintained by [Wurl](https://www.wurl.com/).
28
+
29
+ # Documentation
30
+
31
+ ## Loading a playlist
32
+
33
+ To load a playlist into an object from uri, file path or directly from
34
+ string, use the `load/loads` functions:
35
+
36
+ ```python
37
+ import openm3u8
38
+
39
+ playlist = openm3u8.load('http://videoserver.com/playlist.m3u8')
40
+ print(playlist.segments)
41
+ print(playlist.target_duration)
42
+
43
+ # if you already have the content as string, use
44
+
45
+ playlist = openm3u8.loads('#EXTM3U8 ... etc ... ')
46
+ ```
47
+
48
+ ## Dumping a playlist
49
+
50
+ To dump a playlist from an object to the console or a file, use the
51
+ `dump/dumps` functions:
52
+
53
+ ``` python
54
+ import openm3u8
55
+
56
+ playlist = openm3u8.load('http://videoserver.com/playlist.m3u8')
57
+ print(playlist.dumps())
58
+
59
+ # if you want to write a file from its content
60
+
61
+ playlist.dump('playlist.m3u8')
62
+ ```
63
+
64
+ # Supported tags
65
+
66
+ - [\#EXT-X-TARGETDURATION](https://tools.ietf.org/html/rfc8216#section-4.3.3.1)
67
+ - [\#EXT-X-MEDIA-SEQUENCE](https://tools.ietf.org/html/rfc8216#section-4.3.3.2)
68
+ - [\#EXT-X-DISCONTINUITY-SEQUENCE](https://tools.ietf.org/html/rfc8216#section-4.3.3.3)
69
+ - [\#EXT-X-PROGRAM-DATE-TIME](https://tools.ietf.org/html/rfc8216#section-4.3.2.6)
70
+ - [\#EXT-X-MEDIA](https://tools.ietf.org/html/rfc8216#section-4.3.4.1)
71
+ - [\#EXT-X-PLAYLIST-TYPE](https://tools.ietf.org/html/rfc8216#section-4.3.3.5)
72
+ - [\#EXT-X-KEY](https://tools.ietf.org/html/rfc8216#section-4.3.2.4)
73
+ - [\#EXT-X-STREAM-INF](https://tools.ietf.org/html/rfc8216#section-4.3.4.2)
74
+ - [\#EXT-X-VERSION](https://tools.ietf.org/html/rfc8216#section-4.3.1.2)
75
+ - [\#EXT-X-ALLOW-CACHE](https://datatracker.ietf.org/doc/html/draft-pantos-http-live-streaming-07#section-3.3.6)
76
+ - [\#EXT-X-ENDLIST](https://tools.ietf.org/html/rfc8216#section-4.3.3.4)
77
+ - [\#EXTINF](https://tools.ietf.org/html/rfc8216#section-4.3.2.1)
78
+ - [\#EXT-X-I-FRAMES-ONLY](https://tools.ietf.org/html/rfc8216#section-4.3.3.6)
79
+ - [\#EXT-X-BITRATE](https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-4.4.4.8)
80
+ - [\#EXT-X-BYTERANGE](https://tools.ietf.org/html/rfc8216#section-4.3.2.2)
81
+ - [\#EXT-X-I-FRAME-STREAM-INF](https://tools.ietf.org/html/rfc8216#section-4.3.4.3)
82
+ - [\#EXT-X-IMAGES-ONLY](https://github.com/image-media-playlist/spec/blob/master/image_media_playlist_v0_4.pdf)
83
+ - [\#EXT-X-IMAGE-STREAM-INF](https://github.com/image-media-playlist/spec/blob/master/image_media_playlist_v0_4.pdf)
84
+ - [\#EXT-X-TILES](https://github.com/image-media-playlist/spec/blob/master/image_media_playlist_v0_4.pdf)
85
+ - [\#EXT-X-DISCONTINUITY](https://tools.ietf.org/html/rfc8216#section-4.3.2.3)
86
+ - \#EXT-X-CUE-OUT
87
+ - \#EXT-X-CUE-OUT-CONT
88
+ - \#EXT-X-CUE-IN
89
+ - \#EXT-X-CUE-SPAN
90
+ - \#EXT-OATCLS-SCTE35
91
+ - [\#EXT-X-INDEPENDENT-SEGMENTS](https://tools.ietf.org/html/rfc8216#section-4.3.5.1)
92
+ - [\#EXT-X-MAP](https://tools.ietf.org/html/rfc8216#section-4.3.2.5)
93
+ - [\#EXT-X-START](https://tools.ietf.org/html/rfc8216#section-4.3.5.2)
94
+ - [\#EXT-X-SERVER-CONTROL](https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-4.4.3.8)
95
+ - [\#EXT-X-PART-INF](https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-4.4.3.7)
96
+ - [\#EXT-X-PART](https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-4.4.4.9)
97
+ - [\#EXT-X-RENDITION-REPORT](https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-4.4.5.4)
98
+ - [\#EXT-X-SKIP](https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-4.4.5.2)
99
+ - [\#EXT-X-SESSION-DATA](https://tools.ietf.org/html/rfc8216#section-4.3.4.4)
100
+ - [\#EXT-X-PRELOAD-HINT](https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis-09#section-4.4.5.3)
101
+ - [\#EXT-X-SESSION-KEY](https://tools.ietf.org/html/rfc8216#section-4.3.4.5)
102
+ - [\#EXT-X-DATERANGE](https://tools.ietf.org/html/rfc8216#section-4.3.2.7)
103
+ - [\#EXT-X-GAP](https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-05#section-4.4.2.7)
104
+ - [\#EXT-X-CONTENT-STEERING](https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-10#section-4.4.6.64)
105
+
106
+ # Running Tests
107
+
108
+ ``` bash
109
+ $ ./runtests
110
+ ```
111
+
112
+ # Contributing
113
+
114
+ All contributions are welcome, but we will merge a pull request if, and
115
+ only if, it
116
+
117
+ - Has tests
118
+ - Follows the code conventions
119
+
120
+ If you plan to implement a new feature or something that will take more
121
+ than a few minutes, please open an issue to make sure we don't work on
122
+ the same thing.
@@ -0,0 +1,15 @@
1
+ openm3u8/__init__.py,sha256=v4GY4sr6ufBdoGjFYbdIoDoHkXlNplxno2iPSHCpBXk,2860
2
+ openm3u8/_m3u8_parser.abi3.so,sha256=xyYMgcu3EVgm8uKnlavwZ9Tf2UMvcwKJAkEYcWWleDo,325128
3
+ openm3u8/_m3u8_parser.c,sha256=KwU4azWqDVq_SpScj73IVRaVNABeFRDgLBewjgfRIgk,106559
4
+ openm3u8/httpclient.py,sha256=0RIbdGTfcV-KHAxgG8ltTryz-glLDO9nNKdgYulBeCA,1270
5
+ openm3u8/mixins.py,sha256=a-RYcXjHnBPOUQJtCcpwDCb8jYS0TdEm8Xw2OeLih5k,1461
6
+ openm3u8/model.py,sha256=4D0CwTCC-fyLn-x1UHta7lD87VWWXpgZlX_j2GaR_Vs,56023
7
+ openm3u8/parser.py,sha256=kLIqa5OFIqoQDLRLLffaDRG3u2rYDYGTnS6qaN7vzkw,23963
8
+ openm3u8/protocol.py,sha256=QfMsRc08oHmfzlU8vGhYmIQrRkC0bhSq5y6I8O7AjGo,1826
9
+ openm3u8/version_matching.py,sha256=oHfB1xpzUJy8aWJHM1H0deKbNMloRONAWOe1RYYc4DM,996
10
+ openm3u8/version_matching_rules.py,sha256=rRbZbGQVlCLbkfuEm_MMWAXB4X3CaU2JEQgkbLjkq-0,3012
11
+ openm3u8-7.0.0.dist-info/METADATA,sha256=W8r_Hz-txJkg0UIdP6_pvYulHHeyYZ4aymY-bplnIJI,5312
12
+ openm3u8-7.0.0.dist-info/WHEEL,sha256=aSgG0F4rGPZtV0iTEIfy6dtHq6g67Lze3uLfk0vWn88,151
13
+ openm3u8-7.0.0.dist-info/top_level.txt,sha256=P-N4OU6emEeHAOPxyhnugxA3SCm_wy7OyMs3-19K1s4,9
14
+ openm3u8-7.0.0.dist-info/RECORD,,
15
+ openm3u8-7.0.0.dist-info/licenses/LICENSE,sha256=BOqZtQXlr2aLgBsu-ZvorKCFd6gnLvTmYm_wxe06Dxk,1199
@@ -0,0 +1,6 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: false
4
+ Tag: cp312-cp312-manylinux_2_17_x86_64
5
+ Tag: cp312-cp312-manylinux2014_x86_64
6
+
@@ -0,0 +1,13 @@
1
+ openm3u8 is licensed under the MIT License:
2
+
3
+ The MIT License
4
+
5
+ Copyright (c) 2012 globo.com webmedia@corp.globo.com
6
+
7
+ Modifications Copyright (c) 2026 Wurl (solutions@wurl.com)
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ openm3u8