Spaces:
Running
Running
Fix non-stop Media playback on invalid range headers, bump version & dependencies, update doc
Browse files- README.md +7 -1
- mediaflow_proxy/handlers.py +5 -1
- mediaflow_proxy/main.py +1 -1
- mediaflow_proxy/routes.py +6 -2
- poetry.lock +46 -46
- pyproject.toml +2 -2
README.md
CHANGED
@@ -72,6 +72,12 @@ Set the following environment variables:
|
|
72 |
```
|
73 |
mediaflow-proxy
|
74 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
|
76 |
|
77 |
#### Using git & poetry
|
@@ -98,7 +104,7 @@ Set the following environment variables:
|
|
98 |
|
99 |
4. Run the FastAPI server:
|
100 |
```
|
101 |
-
poetry run uvicorn mediaflow_proxy.main:app --host 0.0.0.0 --port 8888
|
102 |
```
|
103 |
|
104 |
|
|
|
72 |
```
|
73 |
mediaflow-proxy
|
74 |
```
|
75 |
+
You can access the server at `http://localhost:8888`.
|
76 |
+
|
77 |
+
4. To run the server with uvicorn options: (Optional)
|
78 |
+
```
|
79 |
+
uvicorn mediaflow_proxy.main:app --host 0.0.0.0 --port 8888 --workers 4
|
80 |
+
```
|
81 |
|
82 |
|
83 |
#### Using git & poetry
|
|
|
104 |
|
105 |
4. Run the FastAPI server:
|
106 |
```
|
107 |
+
poetry run uvicorn mediaflow_proxy.main:app --host 0.0.0.0 --port 8888 --workers 4
|
108 |
```
|
109 |
|
110 |
|
mediaflow_proxy/handlers.py
CHANGED
@@ -97,7 +97,11 @@ async def handle_hls_stream_proxy(
|
|
97 |
streamer, hls_params.destination, proxy_headers, request, hls_params.key_url
|
98 |
)
|
99 |
|
100 |
-
|
|
|
|
|
|
|
|
|
101 |
response_headers = prepare_response_headers(response.headers, proxy_headers.response)
|
102 |
|
103 |
return EnhancedStreamingResponse(
|
|
|
97 |
streamer, hls_params.destination, proxy_headers, request, hls_params.key_url
|
98 |
)
|
99 |
|
100 |
+
content_range = proxy_headers.request.get("range", "bytes=0-")
|
101 |
+
if "NaN" in content_range:
|
102 |
+
# Handle invalid range requests "bytes=NaN-NaN"
|
103 |
+
raise HTTPException(status_code=416, detail="Invalid Range Header")
|
104 |
+
proxy_headers.request.update({"range": content_range})
|
105 |
response_headers = prepare_response_headers(response.headers, proxy_headers.response)
|
106 |
|
107 |
return EnhancedStreamingResponse(
|
mediaflow_proxy/main.py
CHANGED
@@ -82,7 +82,7 @@ app.mount("/", StaticFiles(directory=str(static_path), html=True), name="static"
|
|
82 |
def run():
|
83 |
import uvicorn
|
84 |
|
85 |
-
uvicorn.run(app, host="
|
86 |
|
87 |
|
88 |
if __name__ == "__main__":
|
|
|
82 |
def run():
|
83 |
import uvicorn
|
84 |
|
85 |
+
uvicorn.run(app, host="0.0.0.0", port=8888, log_level="info", workers=3)
|
86 |
|
87 |
|
88 |
if __name__ == "__main__":
|
mediaflow_proxy/routes.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
from typing import Annotated
|
2 |
|
3 |
-
from fastapi import Request, Depends, APIRouter, Query
|
4 |
|
5 |
from .handlers import handle_hls_stream_proxy, proxy_stream, get_manifest, get_playlist, get_segment, get_public_ip
|
6 |
from .schemas import MPDSegmentParams, MPDPlaylistParams, HLSManifestParams, ProxyStreamParams, MPDManifestParams
|
@@ -48,7 +48,11 @@ async def proxy_stream_endpoint(
|
|
48 |
Returns:
|
49 |
Response: The HTTP response with the streamed content.
|
50 |
"""
|
51 |
-
|
|
|
|
|
|
|
|
|
52 |
return await proxy_stream(request.method, stream_params, proxy_headers)
|
53 |
|
54 |
|
|
|
1 |
from typing import Annotated
|
2 |
|
3 |
+
from fastapi import Request, Depends, APIRouter, Query, HTTPException
|
4 |
|
5 |
from .handlers import handle_hls_stream_proxy, proxy_stream, get_manifest, get_playlist, get_segment, get_public_ip
|
6 |
from .schemas import MPDSegmentParams, MPDPlaylistParams, HLSManifestParams, ProxyStreamParams, MPDManifestParams
|
|
|
48 |
Returns:
|
49 |
Response: The HTTP response with the streamed content.
|
50 |
"""
|
51 |
+
content_range = proxy_headers.request.get("range", "bytes=0-")
|
52 |
+
if "nan" in content_range.casefold():
|
53 |
+
# Handle invalid range requests "bytes=NaN-NaN"
|
54 |
+
raise HTTPException(status_code=416, detail="Invalid Range Header")
|
55 |
+
proxy_headers.request.update({"range": content_range})
|
56 |
return await proxy_stream(request.method, stream_params, proxy_headers)
|
57 |
|
58 |
|
poetry.lock
CHANGED
@@ -194,13 +194,13 @@ files = [
|
|
194 |
|
195 |
[[package]]
|
196 |
name = "httpcore"
|
197 |
-
version = "1.0.
|
198 |
description = "A minimal low-level HTTP client."
|
199 |
optional = false
|
200 |
python-versions = ">=3.8"
|
201 |
files = [
|
202 |
-
{file = "httpcore-1.0.
|
203 |
-
{file = "httpcore-1.0.
|
204 |
]
|
205 |
|
206 |
[package.dependencies]
|
@@ -211,7 +211,7 @@ h11 = ">=0.13,<0.15"
|
|
211 |
asyncio = ["anyio (>=4.0,<5.0)"]
|
212 |
http2 = ["h2 (>=3,<5)"]
|
213 |
socks = ["socksio (==1.*)"]
|
214 |
-
trio = ["trio (>=0.22.0,<
|
215 |
|
216 |
[[package]]
|
217 |
name = "httpx"
|
@@ -304,43 +304,43 @@ type = ["mypy (>=1.11.2)"]
|
|
304 |
|
305 |
[[package]]
|
306 |
name = "pycryptodome"
|
307 |
-
version = "3.
|
308 |
description = "Cryptographic library for Python"
|
309 |
optional = false
|
310 |
-
python-versions = "
|
311 |
files = [
|
312 |
-
{file = "pycryptodome-3.
|
313 |
-
{file = "pycryptodome-3.
|
314 |
-
{file = "pycryptodome-3.
|
315 |
-
{file = "pycryptodome-3.
|
316 |
-
{file = "pycryptodome-3.
|
317 |
-
{file = "pycryptodome-3.
|
318 |
-
{file = "pycryptodome-3.
|
319 |
-
{file = "pycryptodome-3.
|
320 |
-
{file = "pycryptodome-3.
|
321 |
-
{file = "pycryptodome-3.
|
322 |
-
{file = "pycryptodome-3.
|
323 |
-
{file = "pycryptodome-3.
|
324 |
-
{file = "pycryptodome-3.
|
325 |
-
{file = "pycryptodome-3.
|
326 |
-
{file = "pycryptodome-3.
|
327 |
-
{file = "pycryptodome-3.
|
328 |
-
{file = "pycryptodome-3.
|
329 |
-
{file = "pycryptodome-3.
|
330 |
-
{file = "pycryptodome-3.
|
331 |
-
{file = "pycryptodome-3.
|
332 |
-
{file = "pycryptodome-3.
|
333 |
-
{file = "pycryptodome-3.
|
334 |
-
{file = "pycryptodome-3.
|
335 |
-
{file = "pycryptodome-3.
|
336 |
-
{file = "pycryptodome-3.
|
337 |
-
{file = "pycryptodome-3.
|
338 |
-
{file = "pycryptodome-3.
|
339 |
-
{file = "pycryptodome-3.
|
340 |
-
{file = "pycryptodome-3.
|
341 |
-
{file = "pycryptodome-3.
|
342 |
-
{file = "pycryptodome-3.
|
343 |
-
{file = "pycryptodome-3.
|
344 |
]
|
345 |
|
346 |
[[package]]
|
@@ -557,13 +557,13 @@ test = ["pytest", "tornado (>=4.5)", "typeguard"]
|
|
557 |
|
558 |
[[package]]
|
559 |
name = "tomli"
|
560 |
-
version = "2.0.
|
561 |
description = "A lil' TOML parser"
|
562 |
optional = false
|
563 |
-
python-versions = ">=3.
|
564 |
files = [
|
565 |
-
{file = "tomli-2.0.
|
566 |
-
{file = "tomli-2.0.
|
567 |
]
|
568 |
|
569 |
[[package]]
|
@@ -579,13 +579,13 @@ files = [
|
|
579 |
|
580 |
[[package]]
|
581 |
name = "uvicorn"
|
582 |
-
version = "0.
|
583 |
description = "The lightning-fast ASGI server."
|
584 |
optional = false
|
585 |
python-versions = ">=3.8"
|
586 |
files = [
|
587 |
-
{file = "uvicorn-0.
|
588 |
-
{file = "uvicorn-0.
|
589 |
]
|
590 |
|
591 |
[package.dependencies]
|
@@ -610,4 +610,4 @@ files = [
|
|
610 |
[metadata]
|
611 |
lock-version = "2.0"
|
612 |
python-versions = ">=3.10"
|
613 |
-
content-hash = "
|
|
|
194 |
|
195 |
[[package]]
|
196 |
name = "httpcore"
|
197 |
+
version = "1.0.6"
|
198 |
description = "A minimal low-level HTTP client."
|
199 |
optional = false
|
200 |
python-versions = ">=3.8"
|
201 |
files = [
|
202 |
+
{file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"},
|
203 |
+
{file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"},
|
204 |
]
|
205 |
|
206 |
[package.dependencies]
|
|
|
211 |
asyncio = ["anyio (>=4.0,<5.0)"]
|
212 |
http2 = ["h2 (>=3,<5)"]
|
213 |
socks = ["socksio (==1.*)"]
|
214 |
+
trio = ["trio (>=0.22.0,<1.0)"]
|
215 |
|
216 |
[[package]]
|
217 |
name = "httpx"
|
|
|
304 |
|
305 |
[[package]]
|
306 |
name = "pycryptodome"
|
307 |
+
version = "3.21.0"
|
308 |
description = "Cryptographic library for Python"
|
309 |
optional = false
|
310 |
+
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
|
311 |
files = [
|
312 |
+
{file = "pycryptodome-3.21.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:dad9bf36eda068e89059d1f07408e397856be9511d7113ea4b586642a429a4fd"},
|
313 |
+
{file = "pycryptodome-3.21.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:a1752eca64c60852f38bb29e2c86fca30d7672c024128ef5d70cc15868fa10f4"},
|
314 |
+
{file = "pycryptodome-3.21.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ba4cc304eac4d4d458f508d4955a88ba25026890e8abff9b60404f76a62c55e"},
|
315 |
+
{file = "pycryptodome-3.21.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cb087b8612c8a1a14cf37dd754685be9a8d9869bed2ffaaceb04850a8aeef7e"},
|
316 |
+
{file = "pycryptodome-3.21.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:26412b21df30b2861424a6c6d5b1d8ca8107612a4cfa4d0183e71c5d200fb34a"},
|
317 |
+
{file = "pycryptodome-3.21.0-cp27-cp27m-win32.whl", hash = "sha256:cc2269ab4bce40b027b49663d61d816903a4bd90ad88cb99ed561aadb3888dd3"},
|
318 |
+
{file = "pycryptodome-3.21.0-cp27-cp27m-win_amd64.whl", hash = "sha256:0fa0a05a6a697ccbf2a12cec3d6d2650b50881899b845fac6e87416f8cb7e87d"},
|
319 |
+
{file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6cce52e196a5f1d6797ff7946cdff2038d3b5f0aba4a43cb6bf46b575fd1b5bb"},
|
320 |
+
{file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:a915597ffccabe902e7090e199a7bf7a381c5506a747d5e9d27ba55197a2c568"},
|
321 |
+
{file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4e74c522d630766b03a836c15bff77cb657c5fdf098abf8b1ada2aebc7d0819"},
|
322 |
+
{file = "pycryptodome-3.21.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:a3804675283f4764a02db05f5191eb8fec2bb6ca34d466167fc78a5f05bbe6b3"},
|
323 |
+
{file = "pycryptodome-3.21.0-cp36-abi3-macosx_10_9_universal2.whl", hash = "sha256:2480ec2c72438430da9f601ebc12c518c093c13111a5c1644c82cdfc2e50b1e4"},
|
324 |
+
{file = "pycryptodome-3.21.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:de18954104667f565e2fbb4783b56667f30fb49c4d79b346f52a29cb198d5b6b"},
|
325 |
+
{file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de4b7263a33947ff440412339cb72b28a5a4c769b5c1ca19e33dd6cd1dcec6e"},
|
326 |
+
{file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0714206d467fc911042d01ea3a1847c847bc10884cf674c82e12915cfe1649f8"},
|
327 |
+
{file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d85c1b613121ed3dbaa5a97369b3b757909531a959d229406a75b912dd51dd1"},
|
328 |
+
{file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:8898a66425a57bcf15e25fc19c12490b87bd939800f39a03ea2de2aea5e3611a"},
|
329 |
+
{file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_2_i686.whl", hash = "sha256:932c905b71a56474bff8a9c014030bc3c882cee696b448af920399f730a650c2"},
|
330 |
+
{file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:18caa8cfbc676eaaf28613637a89980ad2fd96e00c564135bf90bc3f0b34dd93"},
|
331 |
+
{file = "pycryptodome-3.21.0-cp36-abi3-win32.whl", hash = "sha256:280b67d20e33bb63171d55b1067f61fbd932e0b1ad976b3a184303a3dad22764"},
|
332 |
+
{file = "pycryptodome-3.21.0-cp36-abi3-win_amd64.whl", hash = "sha256:b7aa25fc0baa5b1d95b7633af4f5f1838467f1815442b22487426f94e0d66c53"},
|
333 |
+
{file = "pycryptodome-3.21.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:2cb635b67011bc147c257e61ce864879ffe6d03342dc74b6045059dfbdedafca"},
|
334 |
+
{file = "pycryptodome-3.21.0-pp27-pypy_73-win32.whl", hash = "sha256:4c26a2f0dc15f81ea3afa3b0c87b87e501f235d332b7f27e2225ecb80c0b1cdd"},
|
335 |
+
{file = "pycryptodome-3.21.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d5ebe0763c982f069d3877832254f64974139f4f9655058452603ff559c482e8"},
|
336 |
+
{file = "pycryptodome-3.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ee86cbde706be13f2dec5a42b52b1c1d1cbb90c8e405c68d0755134735c8dc6"},
|
337 |
+
{file = "pycryptodome-3.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fd54003ec3ce4e0f16c484a10bc5d8b9bd77fa662a12b85779a2d2d85d67ee0"},
|
338 |
+
{file = "pycryptodome-3.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5dfafca172933506773482b0e18f0cd766fd3920bd03ec85a283df90d8a17bc6"},
|
339 |
+
{file = "pycryptodome-3.21.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:590ef0898a4b0a15485b05210b4a1c9de8806d3ad3d47f74ab1dc07c67a6827f"},
|
340 |
+
{file = "pycryptodome-3.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35e442630bc4bc2e1878482d6f59ea22e280d7121d7adeaedba58c23ab6386b"},
|
341 |
+
{file = "pycryptodome-3.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff99f952db3db2fbe98a0b355175f93ec334ba3d01bbde25ad3a5a33abc02b58"},
|
342 |
+
{file = "pycryptodome-3.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8acd7d34af70ee63f9a849f957558e49a98f8f1634f86a59d2be62bb8e93f71c"},
|
343 |
+
{file = "pycryptodome-3.21.0.tar.gz", hash = "sha256:f7787e0d469bdae763b876174cf2e6c0f7be79808af26b1da96f1a64bcf47297"},
|
344 |
]
|
345 |
|
346 |
[[package]]
|
|
|
557 |
|
558 |
[[package]]
|
559 |
name = "tomli"
|
560 |
+
version = "2.0.2"
|
561 |
description = "A lil' TOML parser"
|
562 |
optional = false
|
563 |
+
python-versions = ">=3.8"
|
564 |
files = [
|
565 |
+
{file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"},
|
566 |
+
{file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"},
|
567 |
]
|
568 |
|
569 |
[[package]]
|
|
|
579 |
|
580 |
[[package]]
|
581 |
name = "uvicorn"
|
582 |
+
version = "0.31.0"
|
583 |
description = "The lightning-fast ASGI server."
|
584 |
optional = false
|
585 |
python-versions = ">=3.8"
|
586 |
files = [
|
587 |
+
{file = "uvicorn-0.31.0-py3-none-any.whl", hash = "sha256:cac7be4dd4d891c363cd942160a7b02e69150dcbc7a36be04d5f4af4b17c8ced"},
|
588 |
+
{file = "uvicorn-0.31.0.tar.gz", hash = "sha256:13bc21373d103859f68fe739608e2eb054a816dea79189bc3ca08ea89a275906"},
|
589 |
]
|
590 |
|
591 |
[package.dependencies]
|
|
|
610 |
[metadata]
|
611 |
lock-version = "2.0"
|
612 |
python-versions = ">=3.10"
|
613 |
+
content-hash = "cfa51491c2cccf2ff0bd593a2ede5734b580e2c20f6380e6a50ea3d900c9e809"
|
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"
|
@@ -31,7 +31,7 @@ cachetools = "^5.4.0"
|
|
31 |
pydantic-settings = "^2.5.2"
|
32 |
gunicorn = "^23.0.0"
|
33 |
pycryptodome = "^3.20.0"
|
34 |
-
uvicorn = "^0.
|
35 |
|
36 |
|
37 |
[tool.poetry.group.dev.dependencies]
|
|
|
1 |
[tool.poetry]
|
2 |
name = "mediaflow-proxy"
|
3 |
+
version = "1.7.2"
|
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"
|
|
|
31 |
pydantic-settings = "^2.5.2"
|
32 |
gunicorn = "^23.0.0"
|
33 |
pycryptodome = "^3.20.0"
|
34 |
+
uvicorn = "^0.31.0"
|
35 |
|
36 |
|
37 |
[tool.poetry.group.dev.dependencies]
|