Spaces:
Running
Running
Add Support for play expired or self-signed SSL certificates server streams
Browse files- README.md +8 -1
- mediaflow_proxy/handlers.py +41 -11
- mediaflow_proxy/routes.py +17 -6
- mediaflow_proxy/utils/cache_utils.py +6 -4
- mediaflow_proxy/utils/http_utils.py +5 -2
- mediaflow_proxy/utils/mpd_utils.py +2 -2
README.md
CHANGED
@@ -30,7 +30,7 @@ MediaFlow Proxy is a powerful and flexible solution for proxifying various types
|
|
30 |
- Retrieve public IP address of the MediaFlow Proxy server for use with Debrid services
|
31 |
- Support for HTTP/HTTPS/SOCKS5 proxy forwarding
|
32 |
- Protect against unauthorized access and network bandwidth abuses
|
33 |
-
|
34 |
|
35 |
## Configuration
|
36 |
|
@@ -157,6 +157,13 @@ Once the server is running, for more details on the available endpoints and thei
|
|
157 |
mpv "http://localhost:8888/proxy/stream?d=https://jsoncompare.org/LearningContainer/SampleFiles/Video/MP4/sample-mp4-file.mp4&api_password=your_password"
|
158 |
```
|
159 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
#### Proxy HLS Stream with Headers
|
161 |
|
162 |
```bash
|
|
|
30 |
- Retrieve public IP address of the MediaFlow Proxy server for use with Debrid services
|
31 |
- Support for HTTP/HTTPS/SOCKS5 proxy forwarding
|
32 |
- Protect against unauthorized access and network bandwidth abuses
|
33 |
+
- Support for play expired or self-signed SSL certificates server streams
|
34 |
|
35 |
## Configuration
|
36 |
|
|
|
157 |
mpv "http://localhost:8888/proxy/stream?d=https://jsoncompare.org/LearningContainer/SampleFiles/Video/MP4/sample-mp4-file.mp4&api_password=your_password"
|
158 |
```
|
159 |
|
160 |
+
#### Proxy HTTPS self-signed certificate Stream
|
161 |
+
|
162 |
+
```bash
|
163 |
+
mpv "http://localhost:8888/proxy/stream?d=https://self-signed.badssl.com/&api_password=your_password&verify_ssl=false"
|
164 |
+
```
|
165 |
+
|
166 |
+
|
167 |
#### Proxy HLS Stream with Headers
|
168 |
|
169 |
```bash
|
mediaflow_proxy/handlers.py
CHANGED
@@ -24,7 +24,9 @@ from .utils.mpd_utils import pad_base64
|
|
24 |
logger = logging.getLogger(__name__)
|
25 |
|
26 |
|
27 |
-
async def handle_hls_stream_proxy(
|
|
|
|
|
28 |
"""
|
29 |
Handles the HLS stream proxy request, fetching and processing the m3u8 playlist or streaming the content.
|
30 |
|
@@ -33,6 +35,7 @@ async def handle_hls_stream_proxy(request: Request, destination: str, headers: d
|
|
33 |
destination (str): The destination URL to fetch the content from.
|
34 |
headers (dict): The headers to include in the request.
|
35 |
key_url (str, optional): The HLS Key URL to replace the original key URL. Defaults to None.
|
|
|
36 |
|
37 |
Returns:
|
38 |
Response: The HTTP response with the processed m3u8 playlist or streamed content.
|
@@ -42,6 +45,7 @@ async def handle_hls_stream_proxy(request: Request, destination: str, headers: d
|
|
42 |
timeout=httpx.Timeout(30.0),
|
43 |
limits=httpx.Limits(max_keepalive_connections=10, max_connections=20),
|
44 |
proxy=settings.proxy_url,
|
|
|
45 |
)
|
46 |
streamer = Streamer(client)
|
47 |
try:
|
@@ -83,7 +87,7 @@ async def handle_hls_stream_proxy(request: Request, destination: str, headers: d
|
|
83 |
return Response(status_code=502, content=f"Internal server error: {e}")
|
84 |
|
85 |
|
86 |
-
async def proxy_stream(method: str, video_url: str, headers: dict):
|
87 |
"""
|
88 |
Proxies the stream request to the given video URL.
|
89 |
|
@@ -91,14 +95,15 @@ async def proxy_stream(method: str, video_url: str, headers: dict):
|
|
91 |
method (str): The HTTP method (e.g., GET, HEAD).
|
92 |
video_url (str): The URL of the video to stream.
|
93 |
headers (dict): The headers to include in the request.
|
|
|
94 |
|
95 |
Returns:
|
96 |
Response: The HTTP response with the streamed content.
|
97 |
"""
|
98 |
-
return await handle_stream_request(method, video_url, headers)
|
99 |
|
100 |
|
101 |
-
async def handle_stream_request(method: str, video_url: str, headers: dict):
|
102 |
"""
|
103 |
Handles the stream request, fetching the content from the video URL and streaming it.
|
104 |
|
@@ -106,6 +111,7 @@ async def handle_stream_request(method: str, video_url: str, headers: dict):
|
|
106 |
method (str): The HTTP method (e.g., GET, HEAD).
|
107 |
video_url (str): The URL of the video to stream.
|
108 |
headers (dict): The headers to include in the request.
|
|
|
109 |
|
110 |
Returns:
|
111 |
Response: The HTTP response with the streamed content.
|
@@ -115,6 +121,7 @@ async def handle_stream_request(method: str, video_url: str, headers: dict):
|
|
115 |
timeout=httpx.Timeout(30.0),
|
116 |
limits=httpx.Limits(max_keepalive_connections=10, max_connections=20),
|
117 |
proxy=settings.proxy_url,
|
|
|
118 |
)
|
119 |
streamer = Streamer(client)
|
120 |
try:
|
@@ -223,7 +230,9 @@ async def handle_drm_key_data(key_id, key, drm_info):
|
|
223 |
return key_id, key
|
224 |
|
225 |
|
226 |
-
async def get_manifest(
|
|
|
|
|
227 |
"""
|
228 |
Retrieves and processes the MPD manifest, converting it to an HLS manifest.
|
229 |
|
@@ -233,12 +242,15 @@ async def get_manifest(request: Request, mpd_url: str, headers: dict, key_id: st
|
|
233 |
headers (dict): The headers to include in the request.
|
234 |
key_id (str, optional): The DRM key ID. Defaults to None.
|
235 |
key (str, optional): The DRM key. Defaults to None.
|
|
|
236 |
|
237 |
Returns:
|
238 |
Response: The HTTP response with the HLS manifest.
|
239 |
"""
|
240 |
try:
|
241 |
-
mpd_dict = await get_cached_mpd(
|
|
|
|
|
242 |
except DownloadError as e:
|
243 |
raise HTTPException(status_code=e.status_code, detail=f"Failed to download MPD: {e.message}")
|
244 |
drm_info = mpd_dict.get("drmInfo", {})
|
@@ -259,7 +271,13 @@ async def get_manifest(request: Request, mpd_url: str, headers: dict, key_id: st
|
|
259 |
|
260 |
|
261 |
async def get_playlist(
|
262 |
-
request: Request,
|
|
|
|
|
|
|
|
|
|
|
|
|
263 |
):
|
264 |
"""
|
265 |
Retrieves and processes the MPD manifest, converting it to an HLS playlist for a specific profile.
|
@@ -271,18 +289,29 @@ async def get_playlist(
|
|
271 |
headers (dict): The headers to include in the request.
|
272 |
key_id (str, optional): The DRM key ID. Defaults to None.
|
273 |
key (str, optional): The DRM key. Defaults to None.
|
|
|
274 |
|
275 |
Returns:
|
276 |
Response: The HTTP response with the HLS playlist.
|
277 |
"""
|
278 |
mpd_dict = await get_cached_mpd(
|
279 |
-
mpd_url,
|
|
|
|
|
|
|
|
|
280 |
)
|
281 |
return await process_playlist(request, mpd_dict, profile_id)
|
282 |
|
283 |
|
284 |
async def get_segment(
|
285 |
-
init_url: str,
|
|
|
|
|
|
|
|
|
|
|
|
|
286 |
):
|
287 |
"""
|
288 |
Retrieves and processes a media segment, decrypting it if necessary.
|
@@ -294,13 +323,14 @@ async def get_segment(
|
|
294 |
headers (dict): The headers to include in the request.
|
295 |
key_id (str, optional): The DRM key ID. Defaults to None.
|
296 |
key (str, optional): The DRM key. Defaults to None.
|
|
|
297 |
|
298 |
Returns:
|
299 |
Response: The HTTP response with the processed segment.
|
300 |
"""
|
301 |
try:
|
302 |
-
init_content = await get_cached_init_segment(init_url, headers)
|
303 |
-
segment_content = await download_file_with_retry(segment_url, headers)
|
304 |
except DownloadError as e:
|
305 |
raise HTTPException(status_code=e.status_code, detail=f"Failed to download segment: {e.message}")
|
306 |
return await process_segment(init_content, segment_content, mimetype, key_id, key)
|
|
|
24 |
logger = logging.getLogger(__name__)
|
25 |
|
26 |
|
27 |
+
async def handle_hls_stream_proxy(
|
28 |
+
request: Request, destination: str, headers: dict, key_url: HttpUrl = None, verify_ssl: bool = True
|
29 |
+
):
|
30 |
"""
|
31 |
Handles the HLS stream proxy request, fetching and processing the m3u8 playlist or streaming the content.
|
32 |
|
|
|
35 |
destination (str): The destination URL to fetch the content from.
|
36 |
headers (dict): The headers to include in the request.
|
37 |
key_url (str, optional): The HLS Key URL to replace the original key URL. Defaults to None.
|
38 |
+
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
39 |
|
40 |
Returns:
|
41 |
Response: The HTTP response with the processed m3u8 playlist or streamed content.
|
|
|
45 |
timeout=httpx.Timeout(30.0),
|
46 |
limits=httpx.Limits(max_keepalive_connections=10, max_connections=20),
|
47 |
proxy=settings.proxy_url,
|
48 |
+
verify=verify_ssl,
|
49 |
)
|
50 |
streamer = Streamer(client)
|
51 |
try:
|
|
|
87 |
return Response(status_code=502, content=f"Internal server error: {e}")
|
88 |
|
89 |
|
90 |
+
async def proxy_stream(method: str, video_url: str, headers: dict, verify_ssl: bool = True):
|
91 |
"""
|
92 |
Proxies the stream request to the given video URL.
|
93 |
|
|
|
95 |
method (str): The HTTP method (e.g., GET, HEAD).
|
96 |
video_url (str): The URL of the video to stream.
|
97 |
headers (dict): The headers to include in the request.
|
98 |
+
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
99 |
|
100 |
Returns:
|
101 |
Response: The HTTP response with the streamed content.
|
102 |
"""
|
103 |
+
return await handle_stream_request(method, video_url, headers, verify_ssl)
|
104 |
|
105 |
|
106 |
+
async def handle_stream_request(method: str, video_url: str, headers: dict, verify_ssl: bool = True):
|
107 |
"""
|
108 |
Handles the stream request, fetching the content from the video URL and streaming it.
|
109 |
|
|
|
111 |
method (str): The HTTP method (e.g., GET, HEAD).
|
112 |
video_url (str): The URL of the video to stream.
|
113 |
headers (dict): The headers to include in the request.
|
114 |
+
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
115 |
|
116 |
Returns:
|
117 |
Response: The HTTP response with the streamed content.
|
|
|
121 |
timeout=httpx.Timeout(30.0),
|
122 |
limits=httpx.Limits(max_keepalive_connections=10, max_connections=20),
|
123 |
proxy=settings.proxy_url,
|
124 |
+
verify=verify_ssl,
|
125 |
)
|
126 |
streamer = Streamer(client)
|
127 |
try:
|
|
|
230 |
return key_id, key
|
231 |
|
232 |
|
233 |
+
async def get_manifest(
|
234 |
+
request: Request, mpd_url: str, headers: dict, key_id: str = None, key: str = None, verify_ssl: bool = True
|
235 |
+
):
|
236 |
"""
|
237 |
Retrieves and processes the MPD manifest, converting it to an HLS manifest.
|
238 |
|
|
|
242 |
headers (dict): The headers to include in the request.
|
243 |
key_id (str, optional): The DRM key ID. Defaults to None.
|
244 |
key (str, optional): The DRM key. Defaults to None.
|
245 |
+
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
246 |
|
247 |
Returns:
|
248 |
Response: The HTTP response with the HLS manifest.
|
249 |
"""
|
250 |
try:
|
251 |
+
mpd_dict = await get_cached_mpd(
|
252 |
+
mpd_url, headers=headers, parse_drm=not key_id and not key, verify_ssl=verify_ssl
|
253 |
+
)
|
254 |
except DownloadError as e:
|
255 |
raise HTTPException(status_code=e.status_code, detail=f"Failed to download MPD: {e.message}")
|
256 |
drm_info = mpd_dict.get("drmInfo", {})
|
|
|
271 |
|
272 |
|
273 |
async def get_playlist(
|
274 |
+
request: Request,
|
275 |
+
mpd_url: str,
|
276 |
+
profile_id: str,
|
277 |
+
headers: dict,
|
278 |
+
key_id: str = None,
|
279 |
+
key: str = None,
|
280 |
+
verify_ssl: bool = True,
|
281 |
):
|
282 |
"""
|
283 |
Retrieves and processes the MPD manifest, converting it to an HLS playlist for a specific profile.
|
|
|
289 |
headers (dict): The headers to include in the request.
|
290 |
key_id (str, optional): The DRM key ID. Defaults to None.
|
291 |
key (str, optional): The DRM key. Defaults to None.
|
292 |
+
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
293 |
|
294 |
Returns:
|
295 |
Response: The HTTP response with the HLS playlist.
|
296 |
"""
|
297 |
mpd_dict = await get_cached_mpd(
|
298 |
+
mpd_url,
|
299 |
+
headers=headers,
|
300 |
+
parse_drm=not key_id and not key,
|
301 |
+
parse_segment_profile_id=profile_id,
|
302 |
+
verify_ssl=verify_ssl,
|
303 |
)
|
304 |
return await process_playlist(request, mpd_dict, profile_id)
|
305 |
|
306 |
|
307 |
async def get_segment(
|
308 |
+
init_url: str,
|
309 |
+
segment_url: str,
|
310 |
+
mimetype: str,
|
311 |
+
headers: dict,
|
312 |
+
key_id: str = None,
|
313 |
+
key: str = None,
|
314 |
+
verify_ssl: bool = True,
|
315 |
):
|
316 |
"""
|
317 |
Retrieves and processes a media segment, decrypting it if necessary.
|
|
|
323 |
headers (dict): The headers to include in the request.
|
324 |
key_id (str, optional): The DRM key ID. Defaults to None.
|
325 |
key (str, optional): The DRM key. Defaults to None.
|
326 |
+
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
327 |
|
328 |
Returns:
|
329 |
Response: The HTTP response with the processed segment.
|
330 |
"""
|
331 |
try:
|
332 |
+
init_content = await get_cached_init_segment(init_url, headers, verify_ssl)
|
333 |
+
segment_content = await download_file_with_retry(segment_url, headers, verify_ssl=verify_ssl)
|
334 |
except DownloadError as e:
|
335 |
raise HTTPException(status_code=e.status_code, detail=f"Failed to download segment: {e.message}")
|
336 |
return await process_segment(init_content, segment_content, mimetype, key_id, key)
|
mediaflow_proxy/routes.py
CHANGED
@@ -14,6 +14,7 @@ async def hls_stream_proxy(
|
|
14 |
d: HttpUrl,
|
15 |
headers: dict = Depends(get_proxy_headers),
|
16 |
key_url: HttpUrl | None = None,
|
|
|
17 |
):
|
18 |
"""
|
19 |
Proxify HLS stream requests, fetching and processing the m3u8 playlist or streaming the content.
|
@@ -23,17 +24,20 @@ async def hls_stream_proxy(
|
|
23 |
d (HttpUrl): The destination URL to fetch the content from.
|
24 |
key_url (HttpUrl, optional): The HLS Key URL to replace the original key URL. Defaults to None. (Useful for bypassing some sneaky protection)
|
25 |
headers (dict): The headers to include in the request.
|
|
|
26 |
|
27 |
Returns:
|
28 |
Response: The HTTP response with the processed m3u8 playlist or streamed content.
|
29 |
"""
|
30 |
destination = str(d)
|
31 |
-
return await handle_hls_stream_proxy(request, destination, headers, key_url)
|
32 |
|
33 |
|
34 |
@proxy_router.head("/stream")
|
35 |
@proxy_router.get("/stream")
|
36 |
-
async def proxy_stream_endpoint(
|
|
|
|
|
37 |
"""
|
38 |
Proxies stream requests to the given video URL.
|
39 |
|
@@ -41,12 +45,13 @@ async def proxy_stream_endpoint(request: Request, d: HttpUrl, headers: dict = De
|
|
41 |
request (Request): The incoming HTTP request.
|
42 |
d (HttpUrl): The URL of the video to stream.
|
43 |
headers (dict): The headers to include in the request.
|
|
|
44 |
|
45 |
Returns:
|
46 |
Response: The HTTP response with the streamed content.
|
47 |
"""
|
48 |
headers.update({"range": headers.get("range", "bytes=0-")})
|
49 |
-
return await proxy_stream(request.method, str(d), headers)
|
50 |
|
51 |
|
52 |
@proxy_router.get("/mpd/manifest")
|
@@ -56,6 +61,7 @@ async def manifest_endpoint(
|
|
56 |
headers: dict = Depends(get_proxy_headers),
|
57 |
key_id: str = None,
|
58 |
key: str = None,
|
|
|
59 |
):
|
60 |
"""
|
61 |
Retrieves and processes the MPD manifest, converting it to an HLS manifest.
|
@@ -66,11 +72,12 @@ async def manifest_endpoint(
|
|
66 |
headers (dict): The headers to include in the request.
|
67 |
key_id (str, optional): The DRM key ID. Defaults to None.
|
68 |
key (str, optional): The DRM key. Defaults to None.
|
|
|
69 |
|
70 |
Returns:
|
71 |
Response: The HTTP response with the HLS manifest.
|
72 |
"""
|
73 |
-
return await get_manifest(request, str(d), headers, key_id, key)
|
74 |
|
75 |
|
76 |
@proxy_router.get("/mpd/playlist")
|
@@ -81,6 +88,7 @@ async def playlist_endpoint(
|
|
81 |
headers: dict = Depends(get_proxy_headers),
|
82 |
key_id: str = None,
|
83 |
key: str = None,
|
|
|
84 |
):
|
85 |
"""
|
86 |
Retrieves and processes the MPD manifest, converting it to an HLS playlist for a specific profile.
|
@@ -92,11 +100,12 @@ async def playlist_endpoint(
|
|
92 |
headers (dict): The headers to include in the request.
|
93 |
key_id (str, optional): The DRM key ID. Defaults to None.
|
94 |
key (str, optional): The DRM key. Defaults to None.
|
|
|
95 |
|
96 |
Returns:
|
97 |
Response: The HTTP response with the HLS playlist.
|
98 |
"""
|
99 |
-
return await get_playlist(request, str(d), profile_id, headers, key_id, key)
|
100 |
|
101 |
|
102 |
@proxy_router.get("/mpd/segment")
|
@@ -107,6 +116,7 @@ async def segment_endpoint(
|
|
107 |
headers: dict = Depends(get_proxy_headers),
|
108 |
key_id: str = None,
|
109 |
key: str = None,
|
|
|
110 |
):
|
111 |
"""
|
112 |
Retrieves and processes a media segment, decrypting it if necessary.
|
@@ -118,11 +128,12 @@ async def segment_endpoint(
|
|
118 |
headers (dict): The headers to include in the request.
|
119 |
key_id (str, optional): The DRM key ID. Defaults to None.
|
120 |
key (str, optional): The DRM key. Defaults to None.
|
|
|
121 |
|
122 |
Returns:
|
123 |
Response: The HTTP response with the processed segment.
|
124 |
"""
|
125 |
-
return await get_segment(str(init_url), str(segment_url), mime_type, headers, key_id, key)
|
126 |
|
127 |
|
128 |
@proxy_router.get("/ip")
|
|
|
14 |
d: HttpUrl,
|
15 |
headers: dict = Depends(get_proxy_headers),
|
16 |
key_url: HttpUrl | None = None,
|
17 |
+
verify_ssl: bool = True,
|
18 |
):
|
19 |
"""
|
20 |
Proxify HLS stream requests, fetching and processing the m3u8 playlist or streaming the content.
|
|
|
24 |
d (HttpUrl): The destination URL to fetch the content from.
|
25 |
key_url (HttpUrl, optional): The HLS Key URL to replace the original key URL. Defaults to None. (Useful for bypassing some sneaky protection)
|
26 |
headers (dict): The headers to include in the request.
|
27 |
+
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
28 |
|
29 |
Returns:
|
30 |
Response: The HTTP response with the processed m3u8 playlist or streamed content.
|
31 |
"""
|
32 |
destination = str(d)
|
33 |
+
return await handle_hls_stream_proxy(request, destination, headers, key_url, verify_ssl)
|
34 |
|
35 |
|
36 |
@proxy_router.head("/stream")
|
37 |
@proxy_router.get("/stream")
|
38 |
+
async def proxy_stream_endpoint(
|
39 |
+
request: Request, d: HttpUrl, headers: dict = Depends(get_proxy_headers), verify_ssl: bool = True
|
40 |
+
):
|
41 |
"""
|
42 |
Proxies stream requests to the given video URL.
|
43 |
|
|
|
45 |
request (Request): The incoming HTTP request.
|
46 |
d (HttpUrl): The URL of the video to stream.
|
47 |
headers (dict): The headers to include in the request.
|
48 |
+
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
49 |
|
50 |
Returns:
|
51 |
Response: The HTTP response with the streamed content.
|
52 |
"""
|
53 |
headers.update({"range": headers.get("range", "bytes=0-")})
|
54 |
+
return await proxy_stream(request.method, str(d), headers, verify_ssl)
|
55 |
|
56 |
|
57 |
@proxy_router.get("/mpd/manifest")
|
|
|
61 |
headers: dict = Depends(get_proxy_headers),
|
62 |
key_id: str = None,
|
63 |
key: str = None,
|
64 |
+
verify_ssl: bool = True,
|
65 |
):
|
66 |
"""
|
67 |
Retrieves and processes the MPD manifest, converting it to an HLS manifest.
|
|
|
72 |
headers (dict): The headers to include in the request.
|
73 |
key_id (str, optional): The DRM key ID. Defaults to None.
|
74 |
key (str, optional): The DRM key. Defaults to None.
|
75 |
+
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
76 |
|
77 |
Returns:
|
78 |
Response: The HTTP response with the HLS manifest.
|
79 |
"""
|
80 |
+
return await get_manifest(request, str(d), headers, key_id, key, verify_ssl)
|
81 |
|
82 |
|
83 |
@proxy_router.get("/mpd/playlist")
|
|
|
88 |
headers: dict = Depends(get_proxy_headers),
|
89 |
key_id: str = None,
|
90 |
key: str = None,
|
91 |
+
verify_ssl: bool = True,
|
92 |
):
|
93 |
"""
|
94 |
Retrieves and processes the MPD manifest, converting it to an HLS playlist for a specific profile.
|
|
|
100 |
headers (dict): The headers to include in the request.
|
101 |
key_id (str, optional): The DRM key ID. Defaults to None.
|
102 |
key (str, optional): The DRM key. Defaults to None.
|
103 |
+
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
104 |
|
105 |
Returns:
|
106 |
Response: The HTTP response with the HLS playlist.
|
107 |
"""
|
108 |
+
return await get_playlist(request, str(d), profile_id, headers, key_id, key, verify_ssl)
|
109 |
|
110 |
|
111 |
@proxy_router.get("/mpd/segment")
|
|
|
116 |
headers: dict = Depends(get_proxy_headers),
|
117 |
key_id: str = None,
|
118 |
key: str = None,
|
119 |
+
verify_ssl: bool = True,
|
120 |
):
|
121 |
"""
|
122 |
Retrieves and processes a media segment, decrypting it if necessary.
|
|
|
128 |
headers (dict): The headers to include in the request.
|
129 |
key_id (str, optional): The DRM key ID. Defaults to None.
|
130 |
key (str, optional): The DRM key. Defaults to None.
|
131 |
+
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
132 |
|
133 |
Returns:
|
134 |
Response: The HTTP response with the processed segment.
|
135 |
"""
|
136 |
+
return await get_segment(str(init_url), str(segment_url), mime_type, headers, key_id, key, verify_ssl)
|
137 |
|
138 |
|
139 |
@proxy_router.get("/ip")
|
mediaflow_proxy/utils/cache_utils.py
CHANGED
@@ -14,7 +14,7 @@ init_segment_cache = TTLCache(maxsize=100, ttl=3600) # 1 hour default TTL
|
|
14 |
|
15 |
|
16 |
async def get_cached_mpd(
|
17 |
-
mpd_url: str, headers: dict, parse_drm: bool, parse_segment_profile_id: str | None = None
|
18 |
) -> dict:
|
19 |
"""
|
20 |
Retrieves and caches the MPD manifest, parsing it if not already cached.
|
@@ -24,6 +24,7 @@ async def get_cached_mpd(
|
|
24 |
headers (dict): The headers to include in the request.
|
25 |
parse_drm (bool): Whether to parse DRM information.
|
26 |
parse_segment_profile_id (str, optional): The profile ID to parse segments for. Defaults to None.
|
|
|
27 |
|
28 |
Returns:
|
29 |
dict: The parsed MPD manifest data.
|
@@ -33,7 +34,7 @@ async def get_cached_mpd(
|
|
33 |
logger.info(f"Using cached MPD for {mpd_url}")
|
34 |
return parse_mpd_dict(mpd_cache[mpd_url]["mpd"], mpd_url, parse_drm, parse_segment_profile_id)
|
35 |
|
36 |
-
mpd_dict = parse_mpd(await download_file_with_retry(mpd_url, headers))
|
37 |
parsed_mpd_dict = parse_mpd_dict(mpd_dict, mpd_url, parse_drm, parse_segment_profile_id)
|
38 |
current_time = datetime.datetime.now(datetime.UTC)
|
39 |
expiration_time = current_time + datetime.timedelta(seconds=parsed_mpd_dict.get("minimumUpdatePeriod", 300))
|
@@ -41,18 +42,19 @@ async def get_cached_mpd(
|
|
41 |
return parsed_mpd_dict
|
42 |
|
43 |
|
44 |
-
async def get_cached_init_segment(init_url: str, headers: dict) -> bytes:
|
45 |
"""
|
46 |
Retrieves and caches the initialization segment.
|
47 |
|
48 |
Args:
|
49 |
init_url (str): The URL of the initialization segment.
|
50 |
headers (dict): The headers to include in the request.
|
|
|
51 |
|
52 |
Returns:
|
53 |
bytes: The initialization segment content.
|
54 |
"""
|
55 |
if init_url not in init_segment_cache:
|
56 |
-
init_content = await download_file_with_retry(init_url, headers)
|
57 |
init_segment_cache[init_url] = init_content
|
58 |
return init_segment_cache[init_url]
|
|
|
14 |
|
15 |
|
16 |
async def get_cached_mpd(
|
17 |
+
mpd_url: str, headers: dict, parse_drm: bool, parse_segment_profile_id: str | None = None, verify_ssl: bool = True
|
18 |
) -> dict:
|
19 |
"""
|
20 |
Retrieves and caches the MPD manifest, parsing it if not already cached.
|
|
|
24 |
headers (dict): The headers to include in the request.
|
25 |
parse_drm (bool): Whether to parse DRM information.
|
26 |
parse_segment_profile_id (str, optional): The profile ID to parse segments for. Defaults to None.
|
27 |
+
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
28 |
|
29 |
Returns:
|
30 |
dict: The parsed MPD manifest data.
|
|
|
34 |
logger.info(f"Using cached MPD for {mpd_url}")
|
35 |
return parse_mpd_dict(mpd_cache[mpd_url]["mpd"], mpd_url, parse_drm, parse_segment_profile_id)
|
36 |
|
37 |
+
mpd_dict = parse_mpd(await download_file_with_retry(mpd_url, headers, verify_ssl=verify_ssl))
|
38 |
parsed_mpd_dict = parse_mpd_dict(mpd_dict, mpd_url, parse_drm, parse_segment_profile_id)
|
39 |
current_time = datetime.datetime.now(datetime.UTC)
|
40 |
expiration_time = current_time + datetime.timedelta(seconds=parsed_mpd_dict.get("minimumUpdatePeriod", 300))
|
|
|
42 |
return parsed_mpd_dict
|
43 |
|
44 |
|
45 |
+
async def get_cached_init_segment(init_url: str, headers: dict, verify_ssl: bool = True) -> bytes:
|
46 |
"""
|
47 |
Retrieves and caches the initialization segment.
|
48 |
|
49 |
Args:
|
50 |
init_url (str): The URL of the initialization segment.
|
51 |
headers (dict): The headers to include in the request.
|
52 |
+
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
53 |
|
54 |
Returns:
|
55 |
bytes: The initialization segment content.
|
56 |
"""
|
57 |
if init_url not in init_segment_cache:
|
58 |
+
init_content = await download_file_with_retry(init_url, headers, verify_ssl=verify_ssl)
|
59 |
init_segment_cache[init_url] = init_content
|
60 |
return init_segment_cache[init_url]
|
mediaflow_proxy/utils/http_utils.py
CHANGED
@@ -137,7 +137,7 @@ class Streamer:
|
|
137 |
await self.client.aclose()
|
138 |
|
139 |
|
140 |
-
async def download_file_with_retry(url: str, headers: dict, timeout: float = 10.0):
|
141 |
"""
|
142 |
Downloads a file with retry logic.
|
143 |
|
@@ -145,6 +145,7 @@ async def download_file_with_retry(url: str, headers: dict, timeout: float = 10.
|
|
145 |
url (str): The URL of the file to download.
|
146 |
headers (dict): The headers to include in the request.
|
147 |
timeout (float, optional): The request timeout. Defaults to 10.0.
|
|
|
148 |
|
149 |
Returns:
|
150 |
bytes: The downloaded file content.
|
@@ -152,7 +153,9 @@ async def download_file_with_retry(url: str, headers: dict, timeout: float = 10.
|
|
152 |
Raises:
|
153 |
DownloadError: If the download fails after retries.
|
154 |
"""
|
155 |
-
async with httpx.AsyncClient(
|
|
|
|
|
156 |
try:
|
157 |
response = await fetch_with_retry(client, "GET", url, headers)
|
158 |
return response.content
|
|
|
137 |
await self.client.aclose()
|
138 |
|
139 |
|
140 |
+
async def download_file_with_retry(url: str, headers: dict, timeout: float = 10.0, verify_ssl: bool = True):
|
141 |
"""
|
142 |
Downloads a file with retry logic.
|
143 |
|
|
|
145 |
url (str): The URL of the file to download.
|
146 |
headers (dict): The headers to include in the request.
|
147 |
timeout (float, optional): The request timeout. Defaults to 10.0.
|
148 |
+
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
149 |
|
150 |
Returns:
|
151 |
bytes: The downloaded file content.
|
|
|
153 |
Raises:
|
154 |
DownloadError: If the download fails after retries.
|
155 |
"""
|
156 |
+
async with httpx.AsyncClient(
|
157 |
+
follow_redirects=True, timeout=timeout, proxy=settings.proxy_url, verify=verify_ssl
|
158 |
+
) as client:
|
159 |
try:
|
160 |
response = await fetch_with_retry(client, "GET", url, headers)
|
161 |
return response.content
|
mediaflow_proxy/utils/mpd_utils.py
CHANGED
@@ -10,12 +10,12 @@ import xmltodict
|
|
10 |
logger = logging.getLogger(__name__)
|
11 |
|
12 |
|
13 |
-
def parse_mpd(mpd_content: str) -> dict:
|
14 |
"""
|
15 |
Parses the MPD content into a dictionary.
|
16 |
|
17 |
Args:
|
18 |
-
mpd_content (str): The MPD content
|
19 |
|
20 |
Returns:
|
21 |
dict: The parsed MPD content as a dictionary.
|
|
|
10 |
logger = logging.getLogger(__name__)
|
11 |
|
12 |
|
13 |
+
def parse_mpd(mpd_content: str | bytes) -> dict:
|
14 |
"""
|
15 |
Parses the MPD content into a dictionary.
|
16 |
|
17 |
Args:
|
18 |
+
mpd_content (str | bytes): The MPD content to parse.
|
19 |
|
20 |
Returns:
|
21 |
dict: The parsed MPD content as a dictionary.
|