Spaces:
Paused
Paused
Add streaming progress feature with tqdm
Browse filesIntegrate streaming progress tracking using tqdm_asyncio in the `stream_content` method, controlled by the new `enable_streaming_progress` setting. Update configuration to include this new setting and ensure the progress bar closes properly after streaming.
- README.md +1 -0
- mediaflow_proxy/configs.py +1 -0
- mediaflow_proxy/utils/http_utils.py +33 -2
README.md
CHANGED
|
@@ -41,6 +41,7 @@ Set the following environment variables:
|
|
| 41 |
|
| 42 |
- `API_PASSWORD`: Required. Protects against unauthorized access and API network abuses.
|
| 43 |
- `PROXY_URL`: Optional. HTTP/HTTPS/SOCKS5 proxy URL for forwarding network requests.
|
|
|
|
| 44 |
|
| 45 |
## Installation
|
| 46 |
|
|
|
|
| 41 |
|
| 42 |
- `API_PASSWORD`: Required. Protects against unauthorized access and API network abuses.
|
| 43 |
- `PROXY_URL`: Optional. HTTP/HTTPS/SOCKS5 proxy URL for forwarding network requests.
|
| 44 |
+
- `ENABLE_STREAMING_PROGRESS`: Optional. Enable streaming progress logging. Default is `false`.
|
| 45 |
|
| 46 |
## Installation
|
| 47 |
|
mediaflow_proxy/configs.py
CHANGED
|
@@ -4,6 +4,7 @@ from pydantic_settings import BaseSettings
|
|
| 4 |
class Settings(BaseSettings):
|
| 5 |
api_password: str # The password for accessing the API endpoints.
|
| 6 |
proxy_url: str | None = None # The URL of the proxy server to route requests through.
|
|
|
|
| 7 |
|
| 8 |
class Config:
|
| 9 |
env_file = ".env"
|
|
|
|
| 4 |
class Settings(BaseSettings):
|
| 5 |
api_password: str # The password for accessing the API endpoints.
|
| 6 |
proxy_url: str | None = None # The URL of the proxy server to route requests through.
|
| 7 |
+
enable_streaming_progress: bool = False # Whether to enable streaming progress tracking.
|
| 8 |
|
| 9 |
class Config:
|
| 10 |
env_file = ".env"
|
mediaflow_proxy/utils/http_utils.py
CHANGED
|
@@ -14,6 +14,7 @@ from starlette.concurrency import iterate_in_threadpool
|
|
| 14 |
from starlette.requests import Request
|
| 15 |
from starlette.types import Receive, Send, Scope
|
| 16 |
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
|
|
|
|
| 17 |
|
| 18 |
from mediaflow_proxy.configs import settings
|
| 19 |
from mediaflow_proxy.const import SUPPORTED_REQUEST_HEADERS
|
|
@@ -80,6 +81,7 @@ class Streamer:
|
|
| 80 |
"""
|
| 81 |
self.client = client
|
| 82 |
self.response = None
|
|
|
|
| 83 |
|
| 84 |
async def stream_content(self, url: str, headers: dict):
|
| 85 |
"""
|
|
@@ -94,8 +96,34 @@ class Streamer:
|
|
| 94 |
"""
|
| 95 |
async with self.client.stream("GET", url, headers=headers, follow_redirects=True) as self.response:
|
| 96 |
self.response.raise_for_status()
|
| 97 |
-
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
async def head(self, url: str, headers: dict):
|
| 101 |
"""
|
|
@@ -137,6 +165,9 @@ class Streamer:
|
|
| 137 |
"""
|
| 138 |
if self.response:
|
| 139 |
await self.response.aclose()
|
|
|
|
|
|
|
|
|
|
| 140 |
await self.client.aclose()
|
| 141 |
|
| 142 |
|
|
|
|
| 14 |
from starlette.requests import Request
|
| 15 |
from starlette.types import Receive, Send, Scope
|
| 16 |
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
|
| 17 |
+
from tqdm.asyncio import tqdm as tqdm_asyncio
|
| 18 |
|
| 19 |
from mediaflow_proxy.configs import settings
|
| 20 |
from mediaflow_proxy.const import SUPPORTED_REQUEST_HEADERS
|
|
|
|
| 81 |
"""
|
| 82 |
self.client = client
|
| 83 |
self.response = None
|
| 84 |
+
self.progress_bar = None
|
| 85 |
|
| 86 |
async def stream_content(self, url: str, headers: dict):
|
| 87 |
"""
|
|
|
|
| 96 |
"""
|
| 97 |
async with self.client.stream("GET", url, headers=headers, follow_redirects=True) as self.response:
|
| 98 |
self.response.raise_for_status()
|
| 99 |
+
|
| 100 |
+
if settings.enable_streaming_progress:
|
| 101 |
+
with tqdm_asyncio(
|
| 102 |
+
total=self.get_content_length(),
|
| 103 |
+
initial=self.get_start_byte(headers),
|
| 104 |
+
unit="B",
|
| 105 |
+
unit_scale=True,
|
| 106 |
+
unit_divisor=1024,
|
| 107 |
+
desc="MediaFlow Proxy Streaming",
|
| 108 |
+
) as self.progress_bar:
|
| 109 |
+
async for chunk in self.response.aiter_bytes():
|
| 110 |
+
yield chunk
|
| 111 |
+
self.progress_bar.update(len(chunk))
|
| 112 |
+
else:
|
| 113 |
+
async for chunk in self.response.aiter_bytes():
|
| 114 |
+
yield chunk
|
| 115 |
+
|
| 116 |
+
def get_content_length(self) -> int:
|
| 117 |
+
content_range = self.response.headers.get("Content-Range")
|
| 118 |
+
if content_range:
|
| 119 |
+
return int(content_range.split("/")[-1])
|
| 120 |
+
return int(self.response.headers.get("Content-Length", 0))
|
| 121 |
+
|
| 122 |
+
@staticmethod
|
| 123 |
+
def get_start_byte(headers: dict) -> int:
|
| 124 |
+
range_header = headers.get("range")
|
| 125 |
+
start_byte = int(range_header.split("=")[-1].split("-")[0])
|
| 126 |
+
return start_byte
|
| 127 |
|
| 128 |
async def head(self, url: str, headers: dict):
|
| 129 |
"""
|
|
|
|
| 165 |
"""
|
| 166 |
if self.response:
|
| 167 |
await self.response.aclose()
|
| 168 |
+
if self.progress_bar:
|
| 169 |
+
self.progress_bar.close()
|
| 170 |
+
logger.info("Stopped the current streaming session")
|
| 171 |
await self.client.aclose()
|
| 172 |
|
| 173 |
|