Spaces:
Running
Running
Fix key URL in m3u8 & update libs & bump version
Browse files- mediaflow_proxy/handlers.py +4 -4
- mediaflow_proxy/utils/http_utils.py +1 -1
- mediaflow_proxy/utils/m3u8_processor.py +3 -5
- poetry.lock +33 -33
- pyproject.toml +2 -2
mediaflow_proxy/handlers.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
import base64
|
2 |
import logging
|
|
|
3 |
|
4 |
import httpx
|
5 |
from fastapi import Request, Response, HTTPException
|
6 |
-
from pydantic import HttpUrl
|
7 |
from starlette.background import BackgroundTask
|
8 |
|
9 |
from .configs import settings
|
@@ -86,7 +86,7 @@ async def handle_hls_stream_proxy(
|
|
86 |
client, streamer = await setup_client_and_streamer(hls_params.use_request_proxy, hls_params.verify_ssl)
|
87 |
|
88 |
try:
|
89 |
-
if hls_params.destination.endswith((".m3u", ".m3u8")):
|
90 |
return await fetch_and_process_m3u8(
|
91 |
streamer, hls_params.destination, proxy_headers, request, hls_params.key_url
|
92 |
)
|
@@ -199,7 +199,7 @@ async def proxy_stream(method: str, stream_params: ProxyStreamParams, proxy_head
|
|
199 |
|
200 |
|
201 |
async def fetch_and_process_m3u8(
|
202 |
-
streamer: Streamer, url: str, proxy_headers: ProxyRequestHeaders, request: Request, key_url:
|
203 |
):
|
204 |
"""
|
205 |
Fetches and processes the m3u8 playlist, converting it to an HLS playlist.
|
@@ -209,7 +209,7 @@ async def fetch_and_process_m3u8(
|
|
209 |
url (str): The URL of the m3u8 playlist.
|
210 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
211 |
request (Request): The incoming HTTP request.
|
212 |
-
key_url (
|
213 |
|
214 |
Returns:
|
215 |
Response: The HTTP response with the processed m3u8 playlist.
|
|
|
1 |
import base64
|
2 |
import logging
|
3 |
+
from urllib.parse import urlparse
|
4 |
|
5 |
import httpx
|
6 |
from fastapi import Request, Response, HTTPException
|
|
|
7 |
from starlette.background import BackgroundTask
|
8 |
|
9 |
from .configs import settings
|
|
|
86 |
client, streamer = await setup_client_and_streamer(hls_params.use_request_proxy, hls_params.verify_ssl)
|
87 |
|
88 |
try:
|
89 |
+
if urlparse(hls_params.destination).path.endswith((".m3u", ".m3u8")):
|
90 |
return await fetch_and_process_m3u8(
|
91 |
streamer, hls_params.destination, proxy_headers, request, hls_params.key_url
|
92 |
)
|
|
|
199 |
|
200 |
|
201 |
async def fetch_and_process_m3u8(
|
202 |
+
streamer: Streamer, url: str, proxy_headers: ProxyRequestHeaders, request: Request, key_url: str = None
|
203 |
):
|
204 |
"""
|
205 |
Fetches and processes the m3u8 playlist, converting it to an HLS playlist.
|
|
|
209 |
url (str): The URL of the m3u8 playlist.
|
210 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
211 |
request (Request): The incoming HTTP request.
|
212 |
+
key_url (str, optional): The HLS Key URL to replace the original key URL. Defaults to None.
|
213 |
|
214 |
Returns:
|
215 |
Response: The HTTP response with the processed m3u8 playlist.
|
mediaflow_proxy/utils/http_utils.py
CHANGED
@@ -370,7 +370,7 @@ class EnhancedStreamingResponse(Response):
|
|
370 |
|
371 |
await send({"type": "http.response.body", "body": b"", "more_body": False})
|
372 |
except Exception as e:
|
373 |
-
logger.
|
374 |
|
375 |
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
376 |
async with anyio.create_task_group() as task_group:
|
|
|
370 |
|
371 |
await send({"type": "http.response.body", "body": b"", "more_body": False})
|
372 |
except Exception as e:
|
373 |
+
logger.exception(f"Error in stream_response: {str(e)}")
|
374 |
|
375 |
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
376 |
async with anyio.create_task_group() as task_group:
|
mediaflow_proxy/utils/m3u8_processor.py
CHANGED
@@ -1,14 +1,12 @@
|
|
1 |
import re
|
2 |
from urllib import parse
|
3 |
|
4 |
-
from pydantic import HttpUrl
|
5 |
-
|
6 |
from mediaflow_proxy.utils.crypto_utils import encryption_handler
|
7 |
from mediaflow_proxy.utils.http_utils import encode_mediaflow_proxy_url, get_original_scheme
|
8 |
|
9 |
|
10 |
class M3U8Processor:
|
11 |
-
def __init__(self, request, key_url:
|
12 |
"""
|
13 |
Initializes the M3U8Processor with the request and URL prefix.
|
14 |
|
@@ -17,7 +15,7 @@ class M3U8Processor:
|
|
17 |
key_url (HttpUrl, optional): The URL of the key server. Defaults to None.
|
18 |
"""
|
19 |
self.request = request
|
20 |
-
self.key_url = key_url
|
21 |
self.mediaflow_proxy_url = str(request.url_for("hls_stream_proxy").replace(scheme=get_original_scheme(request)))
|
22 |
|
23 |
async def process_m3u8(self, content: str, base_url: str) -> str:
|
@@ -58,7 +56,7 @@ class M3U8Processor:
|
|
58 |
original_uri = uri_match.group(1)
|
59 |
uri = parse.urlparse(original_uri)
|
60 |
if self.key_url:
|
61 |
-
uri = uri._replace(scheme=self.key_url.scheme, netloc=self.key_url.
|
62 |
new_uri = await self.proxy_url(uri.geturl(), base_url)
|
63 |
line = line.replace(f'URI="{original_uri}"', f'URI="{new_uri}"')
|
64 |
return line
|
|
|
1 |
import re
|
2 |
from urllib import parse
|
3 |
|
|
|
|
|
4 |
from mediaflow_proxy.utils.crypto_utils import encryption_handler
|
5 |
from mediaflow_proxy.utils.http_utils import encode_mediaflow_proxy_url, get_original_scheme
|
6 |
|
7 |
|
8 |
class M3U8Processor:
|
9 |
+
def __init__(self, request, key_url: str = None):
|
10 |
"""
|
11 |
Initializes the M3U8Processor with the request and URL prefix.
|
12 |
|
|
|
15 |
key_url (HttpUrl, optional): The URL of the key server. Defaults to None.
|
16 |
"""
|
17 |
self.request = request
|
18 |
+
self.key_url = parse.urlparse(key_url) if key_url else None
|
19 |
self.mediaflow_proxy_url = str(request.url_for("hls_stream_proxy").replace(scheme=get_original_scheme(request)))
|
20 |
|
21 |
async def process_m3u8(self, content: str, base_url: str) -> str:
|
|
|
56 |
original_uri = uri_match.group(1)
|
57 |
uri = parse.urlparse(original_uri)
|
58 |
if self.key_url:
|
59 |
+
uri = uri._replace(scheme=self.key_url.scheme, netloc=self.key_url.netloc)
|
60 |
new_uri = await self.proxy_url(uri.geturl(), base_url)
|
61 |
line = line.replace(f'URI="{original_uri}"', f'URI="{new_uri}"')
|
62 |
return line
|
poetry.lock
CHANGED
@@ -35,33 +35,33 @@ trio = ["trio (>=0.26.1)"]
|
|
35 |
|
36 |
[[package]]
|
37 |
name = "black"
|
38 |
-
version = "24.
|
39 |
description = "The uncompromising code formatter."
|
40 |
optional = false
|
41 |
-
python-versions = ">=3.
|
42 |
files = [
|
43 |
-
{file = "black-24.
|
44 |
-
{file = "black-24.
|
45 |
-
{file = "black-24.
|
46 |
-
{file = "black-24.
|
47 |
-
{file = "black-24.
|
48 |
-
{file = "black-24.
|
49 |
-
{file = "black-24.
|
50 |
-
{file = "black-24.
|
51 |
-
{file = "black-24.
|
52 |
-
{file = "black-24.
|
53 |
-
{file = "black-24.
|
54 |
-
{file = "black-24.
|
55 |
-
{file = "black-24.
|
56 |
-
{file = "black-24.
|
57 |
-
{file = "black-24.
|
58 |
-
{file = "black-24.
|
59 |
-
{file = "black-24.
|
60 |
-
{file = "black-24.
|
61 |
-
{file = "black-24.
|
62 |
-
{file = "black-24.
|
63 |
-
{file = "black-24.
|
64 |
-
{file = "black-24.
|
65 |
]
|
66 |
|
67 |
[package.dependencies]
|
@@ -75,7 +75,7 @@ typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
|
|
75 |
|
76 |
[package.extras]
|
77 |
colorama = ["colorama (>=0.4.3)"]
|
78 |
-
d = ["aiohttp (>=3.
|
79 |
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
80 |
uvloop = ["uvloop (>=0.15.2)"]
|
81 |
|
@@ -579,13 +579,13 @@ files = [
|
|
579 |
|
580 |
[[package]]
|
581 |
name = "uvicorn"
|
582 |
-
version = "0.31.
|
583 |
description = "The lightning-fast ASGI server."
|
584 |
optional = false
|
585 |
python-versions = ">=3.8"
|
586 |
files = [
|
587 |
-
{file = "uvicorn-0.31.
|
588 |
-
{file = "uvicorn-0.31.
|
589 |
]
|
590 |
|
591 |
[package.dependencies]
|
@@ -598,16 +598,16 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)",
|
|
598 |
|
599 |
[[package]]
|
600 |
name = "xmltodict"
|
601 |
-
version = "0.
|
602 |
description = "Makes working with XML feel like you are working with JSON"
|
603 |
optional = false
|
604 |
-
python-versions = ">=3.
|
605 |
files = [
|
606 |
-
{file = "xmltodict-0.
|
607 |
-
{file = "xmltodict-0.
|
608 |
]
|
609 |
|
610 |
[metadata]
|
611 |
lock-version = "2.0"
|
612 |
python-versions = ">=3.10"
|
613 |
-
content-hash = "
|
|
|
35 |
|
36 |
[[package]]
|
37 |
name = "black"
|
38 |
+
version = "24.10.0"
|
39 |
description = "The uncompromising code formatter."
|
40 |
optional = false
|
41 |
+
python-versions = ">=3.9"
|
42 |
files = [
|
43 |
+
{file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"},
|
44 |
+
{file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"},
|
45 |
+
{file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"},
|
46 |
+
{file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"},
|
47 |
+
{file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"},
|
48 |
+
{file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"},
|
49 |
+
{file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"},
|
50 |
+
{file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"},
|
51 |
+
{file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"},
|
52 |
+
{file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"},
|
53 |
+
{file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"},
|
54 |
+
{file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"},
|
55 |
+
{file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"},
|
56 |
+
{file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"},
|
57 |
+
{file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"},
|
58 |
+
{file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"},
|
59 |
+
{file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"},
|
60 |
+
{file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"},
|
61 |
+
{file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"},
|
62 |
+
{file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"},
|
63 |
+
{file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"},
|
64 |
+
{file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"},
|
65 |
]
|
66 |
|
67 |
[package.dependencies]
|
|
|
75 |
|
76 |
[package.extras]
|
77 |
colorama = ["colorama (>=0.4.3)"]
|
78 |
+
d = ["aiohttp (>=3.10)"]
|
79 |
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
80 |
uvloop = ["uvloop (>=0.15.2)"]
|
81 |
|
|
|
579 |
|
580 |
[[package]]
|
581 |
name = "uvicorn"
|
582 |
+
version = "0.31.1"
|
583 |
description = "The lightning-fast ASGI server."
|
584 |
optional = false
|
585 |
python-versions = ">=3.8"
|
586 |
files = [
|
587 |
+
{file = "uvicorn-0.31.1-py3-none-any.whl", hash = "sha256:adc42d9cac80cf3e51af97c1851648066841e7cfb6993a4ca8de29ac1548ed41"},
|
588 |
+
{file = "uvicorn-0.31.1.tar.gz", hash = "sha256:f5167919867b161b7bcaf32646c6a94cdbd4c3aa2eb5c17d36bb9aa5cfd8c493"},
|
589 |
]
|
590 |
|
591 |
[package.dependencies]
|
|
|
598 |
|
599 |
[[package]]
|
600 |
name = "xmltodict"
|
601 |
+
version = "0.14.1"
|
602 |
description = "Makes working with XML feel like you are working with JSON"
|
603 |
optional = false
|
604 |
+
python-versions = ">=3.6"
|
605 |
files = [
|
606 |
+
{file = "xmltodict-0.14.1-py2.py3-none-any.whl", hash = "sha256:3ef4a7b71c08f19047fcbea572e1d7f4207ab269da1565b5d40e9823d3894e63"},
|
607 |
+
{file = "xmltodict-0.14.1.tar.gz", hash = "sha256:338c8431e4fc554517651972d62f06958718f6262b04316917008e8fd677a6b0"},
|
608 |
]
|
609 |
|
610 |
[metadata]
|
611 |
lock-version = "2.0"
|
612 |
python-versions = ">=3.10"
|
613 |
+
content-hash = "82b614db25311cde2b5ae15465b537fcb82c4c9cef5817fe45f030d1ea588646"
|
pyproject.toml
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
[tool.poetry]
|
2 |
name = "mediaflow-proxy"
|
3 |
-
version = "1.7.
|
4 |
description = "A high-performance proxy server for streaming media, supporting HTTP(S), HLS, and MPEG-DASH with real-time DRM decryption."
|
5 |
authors = ["mhdzumair <[email protected]>"]
|
6 |
readme = "README.md"
|
@@ -26,7 +26,7 @@ python = ">=3.10"
|
|
26 |
fastapi = "0.115.0"
|
27 |
httpx = {extras = ["socks"], version = "^0.27.2"}
|
28 |
tenacity = "^9.0.0"
|
29 |
-
xmltodict = "^0.
|
30 |
cachetools = "^5.4.0"
|
31 |
pydantic-settings = "^2.5.2"
|
32 |
gunicorn = "^23.0.0"
|
|
|
1 |
[tool.poetry]
|
2 |
name = "mediaflow-proxy"
|
3 |
+
version = "1.7.3"
|
4 |
description = "A high-performance proxy server for streaming media, supporting HTTP(S), HLS, and MPEG-DASH with real-time DRM decryption."
|
5 |
authors = ["mhdzumair <[email protected]>"]
|
6 |
readme = "README.md"
|
|
|
26 |
fastapi = "0.115.0"
|
27 |
httpx = {extras = ["socks"], version = "^0.27.2"}
|
28 |
tenacity = "^9.0.0"
|
29 |
+
xmltodict = "^0.14.0"
|
30 |
cachetools = "^5.4.0"
|
31 |
pydantic-settings = "^2.5.2"
|
32 |
gunicorn = "^23.0.0"
|