compuglobal 0.3.6__tar.gz → 0.3.8__tar.gz
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.
- {compuglobal-0.3.6 → compuglobal-0.3.8}/PKG-INFO +9 -4
- {compuglobal-0.3.6 → compuglobal-0.3.8}/README.rst +7 -2
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/__init__.py +3 -1
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/aio.py +33 -7
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/api/discovery.py +4 -2
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/api/endpoint.py +9 -6
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/api/media.py +3 -3
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/api/metadata.py +1 -1
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/models/frame.py +2 -4
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/models/screencap.py +26 -3
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/models/subtitle.py +3 -2
- compuglobal-0.3.8/compuglobal/models/timestamp.py +76 -0
- {compuglobal-0.3.6 → compuglobal-0.3.8}/pyproject.toml +1 -1
- {compuglobal-0.3.6 → compuglobal-0.3.8}/LICENSE +0 -0
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/api/__init__.py +0 -0
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/api/client.py +0 -0
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/api/config.py +0 -0
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/errors.py +0 -0
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/models/__init__.py +0 -0
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/models/base.py +0 -0
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/models/comic.py +0 -0
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/models/episode.py +0 -0
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/models/font.py +0 -0
- {compuglobal-0.3.6 → compuglobal-0.3.8}/compuglobal/models/stream.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: compuglobal
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.8
|
|
4
4
|
Summary: Python wrapper for the CGHMC API (Frinkiac, Morbotron Master Of All Science and more.
|
|
5
5
|
Keywords: The Simpsons,Futurama,Rick and Morty,West Wing,Frinkiac,Morbotron,Master of all Science,Capital Beat,API,API Wrapper,CompuGlobal,CompuGlobalHyperMegaNet,CGHMC
|
|
6
6
|
Author: MitchellAW
|
|
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: aiohttp>=3.
|
|
15
|
+
Requires-Dist: aiohttp>=3.14.0
|
|
16
16
|
Requires-Dist: pydantic>=2.13.0
|
|
17
17
|
Project-URL: Documentation, https://compuglobal.readthedocs.io/en/latest/
|
|
18
18
|
Project-URL: Source, https://github.com/MitchellAW/CompuGlobal
|
|
@@ -25,10 +25,15 @@ CompuGlobal
|
|
|
25
25
|
|
|
26
26
|
.. image:: https://img.shields.io/pypi/v/compuglobal.svg
|
|
27
27
|
:target: https://pypi.python.org/pypi/compuglobal
|
|
28
|
+
|
|
28
29
|
.. image:: https://img.shields.io/pypi/pyversions/compuglobal.svg
|
|
29
30
|
:target: https://pypi.python.org/pypi/compuglobal
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
|
|
32
|
+
.. image:: https://img.shields.io/github/actions/workflow/status/MitchellAW/CompuGlobal/test.yml?label=tests
|
|
33
|
+
:target: https://github.com/MitchellAW/CompuGlobal/actions/workflows/test.yml
|
|
34
|
+
|
|
35
|
+
.. image:: https://img.shields.io/github/license/mitchellaw/compuglobal
|
|
36
|
+
:target: https://github.com/MitchellAW/CompuGlobal/blob/main/LICENSE
|
|
32
37
|
|
|
33
38
|
Python wrapper for all endpoints of the following undocumented APIs:
|
|
34
39
|
|
|
@@ -6,10 +6,15 @@ CompuGlobal
|
|
|
6
6
|
|
|
7
7
|
.. image:: https://img.shields.io/pypi/v/compuglobal.svg
|
|
8
8
|
:target: https://pypi.python.org/pypi/compuglobal
|
|
9
|
+
|
|
9
10
|
.. image:: https://img.shields.io/pypi/pyversions/compuglobal.svg
|
|
10
11
|
:target: https://pypi.python.org/pypi/compuglobal
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
.. image:: https://img.shields.io/github/actions/workflow/status/MitchellAW/CompuGlobal/test.yml?label=tests
|
|
14
|
+
:target: https://github.com/MitchellAW/CompuGlobal/actions/workflows/test.yml
|
|
15
|
+
|
|
16
|
+
.. image:: https://img.shields.io/github/license/mitchellaw/compuglobal
|
|
17
|
+
:target: https://github.com/MitchellAW/CompuGlobal/blob/main/LICENSE
|
|
13
18
|
|
|
14
19
|
Python wrapper for all endpoints of the following undocumented APIs:
|
|
15
20
|
|
|
@@ -16,11 +16,12 @@ from compuglobal.models.frame import Frame, FrameResult
|
|
|
16
16
|
from compuglobal.models.screencap import Screencap, ScreencapMoment
|
|
17
17
|
from compuglobal.models.stream import Stream, StreamOverlay
|
|
18
18
|
from compuglobal.models.subtitle import Subtitle
|
|
19
|
+
from compuglobal.models.timestamp import Timestamp
|
|
19
20
|
|
|
20
21
|
__title__ = "compuglobal"
|
|
21
22
|
__author__ = "MitchellAW"
|
|
22
23
|
__license__ = "MIT"
|
|
23
|
-
__version__ = "0.3.
|
|
24
|
+
__version__ = "0.3.8"
|
|
24
25
|
|
|
25
26
|
__all__ = [
|
|
26
27
|
"APIPageStatusError",
|
|
@@ -47,4 +48,5 @@ __all__ = [
|
|
|
47
48
|
"Stream",
|
|
48
49
|
"StreamOverlay",
|
|
49
50
|
"Subtitle",
|
|
51
|
+
"Timestamp",
|
|
50
52
|
]
|
|
@@ -91,13 +91,22 @@ class AsyncCompuGlobalAPI:
|
|
|
91
91
|
caption = await self.client.handle_request(request)
|
|
92
92
|
return Screencap.model_validate(caption)
|
|
93
93
|
|
|
94
|
-
async def search(
|
|
94
|
+
async def search(
|
|
95
|
+
self,
|
|
96
|
+
search_text: str,
|
|
97
|
+
season_minimum: int | None = None,
|
|
98
|
+
season_maximum: int | None = None,
|
|
99
|
+
) -> list[FrameResult]:
|
|
95
100
|
"""Perform a search of the given search text and returns a list of all the Frames.
|
|
96
101
|
|
|
97
102
|
Parameters
|
|
98
103
|
----------
|
|
99
104
|
search_text : str
|
|
100
105
|
The search text to query
|
|
106
|
+
season_minimum: int | None, optional
|
|
107
|
+
The minimum season allowed in the search results
|
|
108
|
+
season_maximum: int | None, optional
|
|
109
|
+
The maximum season allowed in the search results
|
|
101
110
|
|
|
102
111
|
Returns
|
|
103
112
|
-------
|
|
@@ -110,9 +119,11 @@ class AsyncCompuGlobalAPI:
|
|
|
110
119
|
Raises an error if no search results are found
|
|
111
120
|
|
|
112
121
|
"""
|
|
113
|
-
|
|
122
|
+
optional_params = {"smin": season_minimum, "smax": season_maximum}
|
|
123
|
+
query = {"q": search_text}
|
|
124
|
+
query |= {k: v for k, v in optional_params.items() if v is not None}
|
|
114
125
|
|
|
115
|
-
request = self.discovery.SEARCH.build_request(self.client.base_url, query=
|
|
126
|
+
request = self.discovery.SEARCH.build_request(self.client.base_url, query=query)
|
|
116
127
|
search_results = await self.client.handle_request(request)
|
|
117
128
|
|
|
118
129
|
if len(search_results) > 0:
|
|
@@ -120,13 +131,22 @@ class AsyncCompuGlobalAPI:
|
|
|
120
131
|
|
|
121
132
|
raise NoSearchResultsFoundError
|
|
122
133
|
|
|
123
|
-
async def search_for_screencap(
|
|
134
|
+
async def search_for_screencap(
|
|
135
|
+
self,
|
|
136
|
+
search_text: str,
|
|
137
|
+
season_minimum: int | None = None,
|
|
138
|
+
season_maximum: int | None = None,
|
|
139
|
+
) -> Screencap:
|
|
124
140
|
"""Perform a search of the given search text and returns the top result.
|
|
125
141
|
|
|
126
142
|
Parameters
|
|
127
143
|
----------
|
|
128
144
|
search_text : str
|
|
129
145
|
The search text to query
|
|
146
|
+
season_minimum: int | None, optional
|
|
147
|
+
The minimum season allowed in the search
|
|
148
|
+
season_maximum: int | None, optional
|
|
149
|
+
The maximum season allowed in the search
|
|
130
150
|
|
|
131
151
|
Returns
|
|
132
152
|
-------
|
|
@@ -134,11 +154,15 @@ class AsyncCompuGlobalAPI:
|
|
|
134
154
|
The screencap of the top search result
|
|
135
155
|
|
|
136
156
|
"""
|
|
137
|
-
search_results = await self.search(search_text)
|
|
157
|
+
search_results = await self.search(search_text, season_minimum=season_minimum, season_maximum=season_maximum)
|
|
138
158
|
result = search_results[0]
|
|
139
159
|
return await self.get_screencap(result.key, result.timestamp)
|
|
140
160
|
|
|
141
|
-
async def get_random_screencap(
|
|
161
|
+
async def get_random_screencap(
|
|
162
|
+
self,
|
|
163
|
+
season_minimum: int | None = None,
|
|
164
|
+
season_maximum: int | None = None,
|
|
165
|
+
) -> Screencap:
|
|
142
166
|
"""Get a random TV Show screencap.
|
|
143
167
|
|
|
144
168
|
Returns
|
|
@@ -147,7 +171,9 @@ class AsyncCompuGlobalAPI:
|
|
|
147
171
|
A random screencap object.
|
|
148
172
|
|
|
149
173
|
"""
|
|
150
|
-
|
|
174
|
+
optional_params = {"smin": season_minimum, "smax": season_maximum}
|
|
175
|
+
query = {k: v for k, v in optional_params.items() if v is not None}
|
|
176
|
+
request = self.discovery.RANDOM.build_request(self.client.base_url, query=query)
|
|
151
177
|
random = await self.client.handle_request(request)
|
|
152
178
|
return Screencap.model_validate(random)
|
|
153
179
|
|
|
@@ -26,7 +26,7 @@ class DiscoveryAPI:
|
|
|
26
26
|
CAPTION = Endpoint(
|
|
27
27
|
path="/api/caption",
|
|
28
28
|
method=RequestMethod.GET,
|
|
29
|
-
|
|
29
|
+
required_query_params=frozenset({"e", "t", "nearby"}),
|
|
30
30
|
)
|
|
31
31
|
|
|
32
32
|
DISCOVER = Endpoint(
|
|
@@ -37,6 +37,7 @@ class DiscoveryAPI:
|
|
|
37
37
|
RANDOM = Endpoint(
|
|
38
38
|
path="/api/random",
|
|
39
39
|
method=RequestMethod.GET,
|
|
40
|
+
optional_query_params=frozenset({"smin", "smax"}),
|
|
40
41
|
)
|
|
41
42
|
|
|
42
43
|
NAVIGATOR = Endpoint(
|
|
@@ -47,7 +48,8 @@ class DiscoveryAPI:
|
|
|
47
48
|
SEARCH = Endpoint(
|
|
48
49
|
path="/api/search",
|
|
49
50
|
method=RequestMethod.GET,
|
|
50
|
-
|
|
51
|
+
required_query_params=frozenset({"q"}),
|
|
52
|
+
optional_query_params=frozenset({"smin", "smax"}),
|
|
51
53
|
)
|
|
52
54
|
|
|
53
55
|
FRAMES = Endpoint(
|
|
@@ -52,11 +52,13 @@ class Endpoint:
|
|
|
52
52
|
Attributes
|
|
53
53
|
----------
|
|
54
54
|
path: str
|
|
55
|
-
The url path
|
|
55
|
+
The url path containing any path parameter names
|
|
56
56
|
method: RequestMethod, optional
|
|
57
57
|
The HTTP RequestMethod for the request (GET/POST/PUT)
|
|
58
|
-
|
|
59
|
-
The query parameters to use in the request
|
|
58
|
+
required_query_params: dict[str, Any] | None, optional
|
|
59
|
+
The required query parameters to use in the request
|
|
60
|
+
optional_query_params: dict[str, Any] | None, optional
|
|
61
|
+
Optional query params that can be used in the request
|
|
60
62
|
body_model: type[BaseModel], optional
|
|
61
63
|
The pydantic model for the json body
|
|
62
64
|
|
|
@@ -64,7 +66,8 @@ class Endpoint:
|
|
|
64
66
|
|
|
65
67
|
path: str
|
|
66
68
|
method: RequestMethod = RequestMethod.GET
|
|
67
|
-
|
|
69
|
+
required_query_params: frozenset[str] = frozenset()
|
|
70
|
+
optional_query_params: frozenset[str] = frozenset()
|
|
68
71
|
body_model: type[BaseModel] | None = None
|
|
69
72
|
|
|
70
73
|
def build_url(
|
|
@@ -178,8 +181,8 @@ class Endpoint:
|
|
|
178
181
|
Raises error if contains missing or unexpected params from the definition of the endpoint
|
|
179
182
|
|
|
180
183
|
"""
|
|
181
|
-
missing = set(self.
|
|
182
|
-
unexpected = query.keys() - self.
|
|
184
|
+
missing = set(self.required_query_params - query.keys())
|
|
185
|
+
unexpected = query.keys() - (self.required_query_params | self.optional_query_params)
|
|
183
186
|
|
|
184
187
|
if missing:
|
|
185
188
|
raise ValueError(
|
|
@@ -33,13 +33,13 @@ class MediaAPI:
|
|
|
33
33
|
COMIC_PANEL = Endpoint(
|
|
34
34
|
path="/comic/img",
|
|
35
35
|
method=RequestMethod.GET,
|
|
36
|
-
|
|
36
|
+
required_query_params=frozenset({"b64"}),
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
COMIC_STRIP = Endpoint(
|
|
40
40
|
path="/comic/img",
|
|
41
41
|
method=RequestMethod.GET,
|
|
42
|
-
|
|
42
|
+
required_query_params=frozenset({"b64", "layout"}),
|
|
43
43
|
)
|
|
44
44
|
|
|
45
45
|
RENDER_GIF = Endpoint(
|
|
@@ -56,7 +56,7 @@ class MediaAPI:
|
|
|
56
56
|
DETECT_LOOP = Endpoint(
|
|
57
57
|
path="/api/detect-loop",
|
|
58
58
|
method=RequestMethod.GET,
|
|
59
|
-
|
|
59
|
+
required_query_params=frozenset(
|
|
60
60
|
{"episode", "start", "end"},
|
|
61
61
|
),
|
|
62
62
|
)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from pydantic import Field
|
|
4
4
|
|
|
5
5
|
from compuglobal.models.base import BaseCompuGlobalModel
|
|
6
|
+
from compuglobal.models.timestamp import Timestamp
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class Frame(BaseCompuGlobalModel):
|
|
@@ -32,10 +33,7 @@ class Frame(BaseCompuGlobalModel):
|
|
|
32
33
|
A readable timestamp for the frame in format `mm:ss`.
|
|
33
34
|
|
|
34
35
|
"""
|
|
35
|
-
|
|
36
|
-
minutes = int(seconds / 60)
|
|
37
|
-
seconds -= minutes * 60
|
|
38
|
-
return f"{minutes}:{seconds:02d}"
|
|
36
|
+
return Timestamp.get_real_timestamp(timestamp=self.timestamp)
|
|
39
37
|
|
|
40
38
|
def __str__(self) -> str:
|
|
41
39
|
"""Get the string representation of the Frame.
|
|
@@ -6,6 +6,7 @@ from compuglobal.models.base import BaseCompuGlobalModel
|
|
|
6
6
|
from compuglobal.models.episode import EpisodeMetadata
|
|
7
7
|
from compuglobal.models.frame import Frame
|
|
8
8
|
from compuglobal.models.subtitle import Subtitle
|
|
9
|
+
from compuglobal.models.timestamp import Timestamp
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class ScreencapMoment(BaseCompuGlobalModel):
|
|
@@ -29,6 +30,17 @@ class ScreencapMoment(BaseCompuGlobalModel):
|
|
|
29
30
|
content: str = Field(alias="Content")
|
|
30
31
|
title: str = Field(alias="Title")
|
|
31
32
|
|
|
33
|
+
def get_real_timestamp(self) -> str:
|
|
34
|
+
"""Get a readable timestamp for the moments timestamp in format `mm:ss`.
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
str
|
|
39
|
+
A readable timestamp in format `mm:ss`.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
return Timestamp.get_real_timestamp(self.timestamp)
|
|
43
|
+
|
|
32
44
|
|
|
33
45
|
class Screencap(BaseCompuGlobalModel):
|
|
34
46
|
"""A Screencap of an episode at a point in time of a TV Show.
|
|
@@ -58,7 +70,7 @@ class Screencap(BaseCompuGlobalModel):
|
|
|
58
70
|
max_timestamp: int = Field(alias="MaxTimestamp", ge=0)
|
|
59
71
|
|
|
60
72
|
def get_real_timestamp(self) -> str:
|
|
61
|
-
"""Get a readable timestamp for the frame in format
|
|
73
|
+
"""Get a readable timestamp for the frame in format `mm:ss`.
|
|
62
74
|
|
|
63
75
|
Returns
|
|
64
76
|
-------
|
|
@@ -66,10 +78,21 @@ class Screencap(BaseCompuGlobalModel):
|
|
|
66
78
|
A readable timestamp for the frame in format `mm:ss`.
|
|
67
79
|
|
|
68
80
|
"""
|
|
69
|
-
return self.frame.
|
|
81
|
+
return Timestamp.get_real_timestamp(timestamp=self.frame.timestamp)
|
|
82
|
+
|
|
83
|
+
def get_duration(self) -> int:
|
|
84
|
+
"""Get duration of screencap subtitles in milliseconds.
|
|
85
|
+
|
|
86
|
+
Returns
|
|
87
|
+
-------
|
|
88
|
+
int
|
|
89
|
+
Duration in milliseconds
|
|
90
|
+
|
|
91
|
+
"""
|
|
92
|
+
return Timestamp.get_subtitles_duration(self.subtitles)
|
|
70
93
|
|
|
71
94
|
def captions(self) -> list[str]:
|
|
72
|
-
"""Get a list of captions for the screencap from all
|
|
95
|
+
"""Get a list of captions for the screencap from all subtitles.
|
|
73
96
|
|
|
74
97
|
Returns
|
|
75
98
|
-------
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from pydantic import Field
|
|
4
4
|
|
|
5
5
|
from compuglobal.models.base import BaseCompuGlobalModel
|
|
6
|
+
from compuglobal.models.timestamp import Timestamp
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class Subtitle(BaseCompuGlobalModel):
|
|
@@ -41,7 +42,7 @@ class Subtitle(BaseCompuGlobalModel):
|
|
|
41
42
|
Returns
|
|
42
43
|
-------
|
|
43
44
|
int
|
|
44
|
-
The duration in
|
|
45
|
+
The duration in milliseconds.
|
|
45
46
|
|
|
46
47
|
"""
|
|
47
|
-
return self.end_timestamp
|
|
48
|
+
return Timestamp.get_duration(start_timestamp=self.start_timestamp, end_timestamp=self.end_timestamp)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Timestamp module with helper class for working with timestamps."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from compuglobal.models.subtitle import Subtitle
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Timestamp:
|
|
12
|
+
"""Helper class for working with timestamps."""
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def get_minutes_seconds(milliseconds: int) -> tuple[int, int]:
|
|
16
|
+
"""Get minutes and seconds from milliseconds.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
milliseconds : int
|
|
21
|
+
The length of time in milliseconds
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
tuple[int, int]
|
|
26
|
+
The minutes, and seconds as a tuple
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
seconds = int(milliseconds / 1000)
|
|
30
|
+
minutes = int(seconds / 60)
|
|
31
|
+
seconds -= minutes * 60
|
|
32
|
+
return minutes, seconds
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def get_real_timestamp(timestamp: int) -> str:
|
|
36
|
+
"""Get a readable timestamp for the frame in format `mm:ss`.
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
str
|
|
41
|
+
A readable timestamp for the frame in format `mm:ss`.
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
minutes, seconds = Timestamp.get_minutes_seconds(timestamp)
|
|
45
|
+
return f"{minutes}:{seconds:02d}"
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def get_duration(start_timestamp: int, end_timestamp: int) -> int:
|
|
49
|
+
"""Get the duration of the subtitle in milliseconds.
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
int
|
|
54
|
+
The duration in milliseconds.
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
return end_timestamp - start_timestamp
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def get_subtitles_duration(subtitles: list[Subtitle]) -> int:
|
|
61
|
+
"""Get the duration between the start of the earliest subtitle, and the end of the latest subtitle.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
subtitles : list[Subtitle]
|
|
66
|
+
The subtitles
|
|
67
|
+
|
|
68
|
+
Returns
|
|
69
|
+
-------
|
|
70
|
+
int
|
|
71
|
+
The duration in milliseconds
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
start_timestamp = min(subtitle.start_timestamp for subtitle in subtitles)
|
|
75
|
+
end_timestamp = max(subtitle.end_timestamp for subtitle in subtitles)
|
|
76
|
+
return Timestamp.get_duration(start_timestamp, end_timestamp)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|