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/__init__.py +113 -0
- openm3u8/_m3u8_parser.abi3.so +0 -0
- openm3u8/_m3u8_parser.c +3122 -0
- openm3u8/httpclient.py +36 -0
- openm3u8/mixins.py +52 -0
- openm3u8/model.py +1694 -0
- openm3u8/parser.py +786 -0
- openm3u8/protocol.py +47 -0
- openm3u8/version_matching.py +37 -0
- openm3u8/version_matching_rules.py +108 -0
- openm3u8-7.0.0.dist-info/METADATA +122 -0
- openm3u8-7.0.0.dist-info/RECORD +15 -0
- openm3u8-7.0.0.dist-info/WHEEL +6 -0
- openm3u8-7.0.0.dist-info/licenses/LICENSE +13 -0
- openm3u8-7.0.0.dist-info/top_level.txt +1 -0
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,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
|