Spaces:
Paused
Paused
Refactor query parameter handling for proxy routes
Browse filesThis commit refactors the query parameter handling in the proxy routes by consolidating parameters into specific Pydantic models using the `Annotated` type for better clarity and maintainability. Additionally, extracted common setup and error-handling logic into reusable functions within `handlers.py`.
- mediaflow_proxy/handlers.py +148 -159
- mediaflow_proxy/routes.py +24 -65
- mediaflow_proxy/schemas.py +41 -1
mediaflow_proxy/handlers.py
CHANGED
|
@@ -9,6 +9,7 @@ from starlette.background import BackgroundTask
|
|
| 9 |
from .configs import settings
|
| 10 |
from .const import SUPPORTED_RESPONSE_HEADERS
|
| 11 |
from .mpd_processor import process_manifest, process_playlist, process_segment
|
|
|
|
| 12 |
from .utils.cache_utils import get_cached_mpd, get_cached_init_segment
|
| 13 |
from .utils.http_utils import (
|
| 14 |
Streamer,
|
|
@@ -24,27 +25,16 @@ from .utils.mpd_utils import pad_base64
|
|
| 24 |
logger = logging.getLogger(__name__)
|
| 25 |
|
| 26 |
|
| 27 |
-
async def
|
| 28 |
-
request: Request,
|
| 29 |
-
destination: str,
|
| 30 |
-
proxy_headers: ProxyRequestHeaders,
|
| 31 |
-
key_url: HttpUrl = None,
|
| 32 |
-
verify_ssl: bool = True,
|
| 33 |
-
use_request_proxy: bool = True,
|
| 34 |
-
):
|
| 35 |
"""
|
| 36 |
-
|
| 37 |
|
| 38 |
Args:
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
| 42 |
-
key_url (str, optional): The HLS Key URL to replace the original key URL. Defaults to None.
|
| 43 |
-
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
| 44 |
-
use_request_proxy (bool, optional): Whether to use the MediaFlow proxy configuration. Defaults to True.
|
| 45 |
|
| 46 |
Returns:
|
| 47 |
-
|
| 48 |
"""
|
| 49 |
client = httpx.AsyncClient(
|
| 50 |
follow_redirects=True,
|
|
@@ -53,68 +43,72 @@ async def handle_hls_stream_proxy(
|
|
| 53 |
proxy=settings.proxy_url if use_request_proxy else None,
|
| 54 |
verify=verify_ssl,
|
| 55 |
)
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
try:
|
| 58 |
-
if destination.endswith((".m3u", ".m3u8")):
|
| 59 |
-
return await fetch_and_process_m3u8(
|
|
|
|
|
|
|
| 60 |
|
| 61 |
-
response = await streamer.head(destination, proxy_headers.request)
|
| 62 |
if "mpegurl" in response.headers.get("content-type", "").lower():
|
| 63 |
-
return await fetch_and_process_m3u8(
|
|
|
|
|
|
|
| 64 |
|
| 65 |
proxy_headers.request.update({"range": proxy_headers.request.get("range", "bytes=0-")})
|
| 66 |
-
|
| 67 |
-
response_headers = {k: v for k, v in response.headers.multi_items() if k in SUPPORTED_RESPONSE_HEADERS}
|
| 68 |
-
|
| 69 |
-
if transfer_encoding := response_headers.get("transfer-encoding"):
|
| 70 |
-
if "chunked" not in transfer_encoding:
|
| 71 |
-
transfer_encoding += ", chunked"
|
| 72 |
-
else:
|
| 73 |
-
transfer_encoding = "chunked"
|
| 74 |
-
response_headers["transfer-encoding"] = transfer_encoding
|
| 75 |
-
response_headers.update(proxy_headers.response)
|
| 76 |
|
| 77 |
return EnhancedStreamingResponse(
|
| 78 |
-
streamer.stream_content(destination, proxy_headers.request),
|
| 79 |
status_code=response.status_code,
|
| 80 |
headers=response_headers,
|
| 81 |
background=BackgroundTask(streamer.close),
|
| 82 |
)
|
| 83 |
-
except httpx.HTTPStatusError as e:
|
| 84 |
-
await client.aclose()
|
| 85 |
-
logger.error(f"Upstream service error while handling request: {e}")
|
| 86 |
-
return Response(status_code=e.response.status_code, content=f"Upstream service error: {e}")
|
| 87 |
-
except DownloadError as e:
|
| 88 |
-
await client.aclose()
|
| 89 |
-
logger.error(f"Error downloading {destination}: {e}")
|
| 90 |
-
return Response(status_code=e.status_code, content=str(e))
|
| 91 |
except Exception as e:
|
| 92 |
await client.aclose()
|
| 93 |
-
|
| 94 |
-
return Response(status_code=502, content=f"Internal server error: {e}")
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
async def proxy_stream(
|
| 98 |
-
method: str,
|
| 99 |
-
video_url: str,
|
| 100 |
-
proxy_headers: ProxyRequestHeaders,
|
| 101 |
-
verify_ssl: bool = True,
|
| 102 |
-
use_request_proxy: bool = True,
|
| 103 |
-
):
|
| 104 |
-
"""
|
| 105 |
-
Proxies the stream request to the given video URL.
|
| 106 |
-
|
| 107 |
-
Args:
|
| 108 |
-
method (str): The HTTP method (e.g., GET, HEAD).
|
| 109 |
-
video_url (str): The URL of the video to stream.
|
| 110 |
-
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
| 111 |
-
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
| 112 |
-
use_request_proxy (bool, optional): Whether to use the MediaFlow proxy configuration. Defaults to True.
|
| 113 |
-
|
| 114 |
-
Returns:
|
| 115 |
-
Response: The HTTP response with the streamed content.
|
| 116 |
-
"""
|
| 117 |
-
return await handle_stream_request(method, video_url, proxy_headers, verify_ssl, use_request_proxy)
|
| 118 |
|
| 119 |
|
| 120 |
async def handle_stream_request(
|
|
@@ -123,39 +117,27 @@ async def handle_stream_request(
|
|
| 123 |
proxy_headers: ProxyRequestHeaders,
|
| 124 |
verify_ssl: bool = True,
|
| 125 |
use_request_proxy: bool = True,
|
| 126 |
-
):
|
| 127 |
"""
|
| 128 |
-
|
|
|
|
|
|
|
| 129 |
|
| 130 |
Args:
|
| 131 |
-
method (str): The HTTP method (e.g., GET
|
| 132 |
video_url (str): The URL of the video to stream.
|
| 133 |
-
proxy_headers (ProxyRequestHeaders):
|
| 134 |
-
verify_ssl (bool, optional): Whether to verify
|
| 135 |
-
use_request_proxy (bool, optional): Whether to use
|
| 136 |
|
| 137 |
Returns:
|
| 138 |
-
Response:
|
| 139 |
"""
|
| 140 |
-
client =
|
| 141 |
-
|
| 142 |
-
timeout=httpx.Timeout(30.0),
|
| 143 |
-
limits=httpx.Limits(max_keepalive_connections=10, max_connections=20),
|
| 144 |
-
proxy=settings.proxy_url if use_request_proxy else None,
|
| 145 |
-
verify=verify_ssl,
|
| 146 |
-
)
|
| 147 |
-
streamer = Streamer(client)
|
| 148 |
try:
|
| 149 |
response = await streamer.head(video_url, proxy_headers.request)
|
| 150 |
-
|
| 151 |
-
response_headers = {k: v for k, v in response.headers.multi_items() if k in SUPPORTED_RESPONSE_HEADERS}
|
| 152 |
-
if transfer_encoding := response_headers.get("transfer-encoding"):
|
| 153 |
-
if "chunked" not in transfer_encoding:
|
| 154 |
-
transfer_encoding += ", chunked"
|
| 155 |
-
else:
|
| 156 |
-
transfer_encoding = "chunked"
|
| 157 |
-
response_headers["transfer-encoding"] = transfer_encoding
|
| 158 |
-
response_headers.update(proxy_headers.response)
|
| 159 |
|
| 160 |
if method == "HEAD":
|
| 161 |
await streamer.close()
|
|
@@ -167,18 +149,49 @@ async def handle_stream_request(
|
|
| 167 |
status_code=response.status_code,
|
| 168 |
background=BackgroundTask(streamer.close),
|
| 169 |
)
|
| 170 |
-
except httpx.HTTPStatusError as e:
|
| 171 |
-
await client.aclose()
|
| 172 |
-
logger.error(f"Upstream service error while handling {method} request: {e}")
|
| 173 |
-
return Response(status_code=e.response.status_code, content=f"Upstream service error: {e}")
|
| 174 |
-
except DownloadError as e:
|
| 175 |
-
await client.aclose()
|
| 176 |
-
logger.error(f"Error downloading {video_url}: {e}")
|
| 177 |
-
return Response(status_code=e.status_code, content=str(e))
|
| 178 |
except Exception as e:
|
| 179 |
await client.aclose()
|
| 180 |
-
|
| 181 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
|
| 183 |
|
| 184 |
async def fetch_and_process_m3u8(
|
|
@@ -208,15 +221,8 @@ async def fetch_and_process_m3u8(
|
|
| 208 |
media_type="application/vnd.apple.mpegurl",
|
| 209 |
headers=response_headers,
|
| 210 |
)
|
| 211 |
-
except httpx.HTTPStatusError as e:
|
| 212 |
-
logger.error(f"HTTP error while fetching m3u8: {e}")
|
| 213 |
-
return Response(status_code=e.response.status_code, content=str(e))
|
| 214 |
-
except DownloadError as e:
|
| 215 |
-
logger.error(f"Error downloading m3u8: {url}")
|
| 216 |
-
return Response(status_code=502, content=str(e))
|
| 217 |
except Exception as e:
|
| 218 |
-
|
| 219 |
-
return Response(status_code=502, content=str(e))
|
| 220 |
finally:
|
| 221 |
await streamer.close()
|
| 222 |
|
|
@@ -252,35 +258,27 @@ async def handle_drm_key_data(key_id, key, drm_info):
|
|
| 252 |
|
| 253 |
async def get_manifest(
|
| 254 |
request: Request,
|
| 255 |
-
|
| 256 |
proxy_headers: ProxyRequestHeaders,
|
| 257 |
-
key_id: str = None,
|
| 258 |
-
key: str = None,
|
| 259 |
-
verify_ssl: bool = True,
|
| 260 |
-
use_request_proxy: bool = True,
|
| 261 |
):
|
| 262 |
"""
|
| 263 |
Retrieves and processes the MPD manifest, converting it to an HLS manifest.
|
| 264 |
|
| 265 |
Args:
|
| 266 |
request (Request): The incoming HTTP request.
|
| 267 |
-
|
| 268 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
| 269 |
-
key_id (str, optional): The DRM key ID. Defaults to None.
|
| 270 |
-
key (str, optional): The DRM key. Defaults to None.
|
| 271 |
-
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
| 272 |
-
use_request_proxy (bool, optional): Whether to use the MediaFlow proxy configuration. Defaults to True.
|
| 273 |
|
| 274 |
Returns:
|
| 275 |
Response: The HTTP response with the HLS manifest.
|
| 276 |
"""
|
| 277 |
try:
|
| 278 |
mpd_dict = await get_cached_mpd(
|
| 279 |
-
|
| 280 |
headers=proxy_headers.request,
|
| 281 |
-
parse_drm=not key_id and not key,
|
| 282 |
-
verify_ssl=verify_ssl,
|
| 283 |
-
use_request_proxy=use_request_proxy,
|
| 284 |
)
|
| 285 |
except DownloadError as e:
|
| 286 |
raise HTTPException(status_code=e.status_code, detail=f"Failed to download MPD: {e.message}")
|
|
@@ -290,7 +288,7 @@ async def get_manifest(
|
|
| 290 |
# For non-DRM protected MPD, we still create an HLS manifest
|
| 291 |
return await process_manifest(request, mpd_dict, proxy_headers, None, None)
|
| 292 |
|
| 293 |
-
key_id, key = await handle_drm_key_data(key_id, key, drm_info)
|
| 294 |
|
| 295 |
# check if the provided key_id and key are valid
|
| 296 |
if key_id and len(key_id) != 32:
|
|
@@ -303,75 +301,66 @@ async def get_manifest(
|
|
| 303 |
|
| 304 |
async def get_playlist(
|
| 305 |
request: Request,
|
| 306 |
-
|
| 307 |
-
profile_id: str,
|
| 308 |
proxy_headers: ProxyRequestHeaders,
|
| 309 |
-
key_id: str = None,
|
| 310 |
-
key: str = None,
|
| 311 |
-
verify_ssl: bool = True,
|
| 312 |
-
use_request_proxy: bool = True,
|
| 313 |
):
|
| 314 |
"""
|
| 315 |
Retrieves and processes the MPD manifest, converting it to an HLS playlist for a specific profile.
|
| 316 |
|
| 317 |
Args:
|
| 318 |
request (Request): The incoming HTTP request.
|
| 319 |
-
|
| 320 |
-
profile_id (str): The profile ID to generate the playlist for.
|
| 321 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
| 322 |
-
key_id (str, optional): The DRM key ID. Defaults to None.
|
| 323 |
-
key (str, optional): The DRM key. Defaults to None.
|
| 324 |
-
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
| 325 |
-
use_request_proxy (bool, optional): Whether to use the MediaFlow proxy configuration. Defaults to True.
|
| 326 |
|
| 327 |
Returns:
|
| 328 |
Response: The HTTP response with the HLS playlist.
|
| 329 |
"""
|
| 330 |
mpd_dict = await get_cached_mpd(
|
| 331 |
-
|
| 332 |
headers=proxy_headers.request,
|
| 333 |
-
parse_drm=not key_id and not key,
|
| 334 |
-
parse_segment_profile_id=profile_id,
|
| 335 |
-
verify_ssl=verify_ssl,
|
| 336 |
-
use_request_proxy=use_request_proxy,
|
| 337 |
)
|
| 338 |
-
return await process_playlist(request, mpd_dict, profile_id, proxy_headers)
|
| 339 |
|
| 340 |
|
| 341 |
async def get_segment(
|
| 342 |
-
|
| 343 |
-
segment_url: str,
|
| 344 |
-
mimetype: str,
|
| 345 |
proxy_headers: ProxyRequestHeaders,
|
| 346 |
-
key_id: str = None,
|
| 347 |
-
key: str = None,
|
| 348 |
-
verify_ssl: bool = True,
|
| 349 |
-
use_request_proxy: bool = True,
|
| 350 |
):
|
| 351 |
"""
|
| 352 |
Retrieves and processes a media segment, decrypting it if necessary.
|
| 353 |
|
| 354 |
Args:
|
| 355 |
-
|
| 356 |
-
segment_url (str): The URL of the media segment.
|
| 357 |
-
mimetype (str): The MIME type of the segment.
|
| 358 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
| 359 |
-
key_id (str, optional): The DRM key ID. Defaults to None.
|
| 360 |
-
key (str, optional): The DRM key. Defaults to None.
|
| 361 |
-
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to True.
|
| 362 |
-
use_request_proxy (bool, optional): Whether to use the MediaFlow proxy configuration. Defaults to True.
|
| 363 |
|
| 364 |
Returns:
|
| 365 |
Response: The HTTP response with the processed segment.
|
| 366 |
"""
|
| 367 |
try:
|
| 368 |
-
init_content = await get_cached_init_segment(
|
|
|
|
|
|
|
| 369 |
segment_content = await download_file_with_retry(
|
| 370 |
-
segment_url,
|
|
|
|
|
|
|
|
|
|
| 371 |
)
|
| 372 |
-
except
|
| 373 |
-
|
| 374 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 375 |
|
| 376 |
|
| 377 |
async def get_public_ip(use_request_proxy: bool = True):
|
|
|
|
| 9 |
from .configs import settings
|
| 10 |
from .const import SUPPORTED_RESPONSE_HEADERS
|
| 11 |
from .mpd_processor import process_manifest, process_playlist, process_segment
|
| 12 |
+
from .schemas import HLSManifestParams, ProxyStreamParams, MPDManifestParams, MPDPlaylistParams, MPDSegmentParams
|
| 13 |
from .utils.cache_utils import get_cached_mpd, get_cached_init_segment
|
| 14 |
from .utils.http_utils import (
|
| 15 |
Streamer,
|
|
|
|
| 25 |
logger = logging.getLogger(__name__)
|
| 26 |
|
| 27 |
|
| 28 |
+
async def setup_client_and_streamer(use_request_proxy: bool, verify_ssl: bool) -> tuple[httpx.AsyncClient, Streamer]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
"""
|
| 30 |
+
Set up an HTTP client and a streamer.
|
| 31 |
|
| 32 |
Args:
|
| 33 |
+
use_request_proxy (bool): Whether to use a proxy for the request.
|
| 34 |
+
verify_ssl (bool): Whether to verify SSL certificates.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
Returns:
|
| 37 |
+
tuple: An httpx.AsyncClient instance and a Streamer instance.
|
| 38 |
"""
|
| 39 |
client = httpx.AsyncClient(
|
| 40 |
follow_redirects=True,
|
|
|
|
| 43 |
proxy=settings.proxy_url if use_request_proxy else None,
|
| 44 |
verify=verify_ssl,
|
| 45 |
)
|
| 46 |
+
return client, Streamer(client)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def handle_exceptions(exception: Exception) -> Response:
|
| 50 |
+
"""
|
| 51 |
+
Handle exceptions and return appropriate HTTP responses.
|
| 52 |
+
|
| 53 |
+
Args:
|
| 54 |
+
exception (Exception): The exception that was raised.
|
| 55 |
+
|
| 56 |
+
Returns:
|
| 57 |
+
Response: An HTTP response corresponding to the exception type.
|
| 58 |
+
"""
|
| 59 |
+
if isinstance(exception, httpx.HTTPStatusError):
|
| 60 |
+
logger.error(f"Upstream service error while handling request: {exception}")
|
| 61 |
+
return Response(status_code=exception.response.status_code, content=f"Upstream service error: {exception}")
|
| 62 |
+
elif isinstance(exception, DownloadError):
|
| 63 |
+
logger.error(f"Error downloading content: {exception}")
|
| 64 |
+
return Response(status_code=exception.status_code, content=str(exception))
|
| 65 |
+
else:
|
| 66 |
+
logger.error(f"Internal server error while handling request: {exception}")
|
| 67 |
+
return Response(status_code=502, content=f"Internal server error: {exception}")
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
async def handle_hls_stream_proxy(
|
| 71 |
+
request: Request, hls_params: HLSManifestParams, proxy_headers: ProxyRequestHeaders
|
| 72 |
+
) -> Response:
|
| 73 |
+
"""
|
| 74 |
+
Handle HLS stream proxy requests.
|
| 75 |
+
|
| 76 |
+
This function processes HLS manifest files and streams content based on the request parameters.
|
| 77 |
+
|
| 78 |
+
Args:
|
| 79 |
+
request (Request): The incoming FastAPI request object.
|
| 80 |
+
hls_params (HLSManifestParams): Parameters for the HLS manifest.
|
| 81 |
+
proxy_headers (ProxyRequestHeaders): Headers to be used in the proxy request.
|
| 82 |
+
|
| 83 |
+
Returns:
|
| 84 |
+
Union[Response, EnhancedStreamingResponse]: Either a processed m3u8 playlist or a streaming response.
|
| 85 |
+
"""
|
| 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 |
+
)
|
| 93 |
|
| 94 |
+
response = await streamer.head(hls_params.destination, proxy_headers.request)
|
| 95 |
if "mpegurl" in response.headers.get("content-type", "").lower():
|
| 96 |
+
return await fetch_and_process_m3u8(
|
| 97 |
+
streamer, hls_params.destination, proxy_headers, request, hls_params.key_url
|
| 98 |
+
)
|
| 99 |
|
| 100 |
proxy_headers.request.update({"range": proxy_headers.request.get("range", "bytes=0-")})
|
| 101 |
+
response_headers = prepare_response_headers(response.headers, proxy_headers.response)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
|
| 103 |
return EnhancedStreamingResponse(
|
| 104 |
+
streamer.stream_content(hls_params.destination, proxy_headers.request),
|
| 105 |
status_code=response.status_code,
|
| 106 |
headers=response_headers,
|
| 107 |
background=BackgroundTask(streamer.close),
|
| 108 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
except Exception as e:
|
| 110 |
await client.aclose()
|
| 111 |
+
return handle_exceptions(e)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
|
| 113 |
|
| 114 |
async def handle_stream_request(
|
|
|
|
| 117 |
proxy_headers: ProxyRequestHeaders,
|
| 118 |
verify_ssl: bool = True,
|
| 119 |
use_request_proxy: bool = True,
|
| 120 |
+
) -> Response:
|
| 121 |
"""
|
| 122 |
+
Handle general stream requests.
|
| 123 |
+
|
| 124 |
+
This function processes both HEAD and GET requests for video streams.
|
| 125 |
|
| 126 |
Args:
|
| 127 |
+
method (str): The HTTP method (e.g., 'GET' or 'HEAD').
|
| 128 |
video_url (str): The URL of the video to stream.
|
| 129 |
+
proxy_headers (ProxyRequestHeaders): Headers to be used in the proxy request.
|
| 130 |
+
verify_ssl (bool, optional): Whether to verify SSL certificates. Defaults to True.
|
| 131 |
+
use_request_proxy (bool, optional): Whether to use a proxy for the request. Defaults to True.
|
| 132 |
|
| 133 |
Returns:
|
| 134 |
+
Union[Response, EnhancedStreamingResponse]: Either a HEAD response or a streaming response.
|
| 135 |
"""
|
| 136 |
+
client, streamer = await setup_client_and_streamer(use_request_proxy, verify_ssl)
|
| 137 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
try:
|
| 139 |
response = await streamer.head(video_url, proxy_headers.request)
|
| 140 |
+
response_headers = prepare_response_headers(response.headers, proxy_headers.response)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
|
| 142 |
if method == "HEAD":
|
| 143 |
await streamer.close()
|
|
|
|
| 149 |
status_code=response.status_code,
|
| 150 |
background=BackgroundTask(streamer.close),
|
| 151 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
except Exception as e:
|
| 153 |
await client.aclose()
|
| 154 |
+
return handle_exceptions(e)
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def prepare_response_headers(original_headers, proxy_response_headers) -> dict:
|
| 158 |
+
"""
|
| 159 |
+
Prepare response headers for the proxy response.
|
| 160 |
+
|
| 161 |
+
This function filters the original headers, ensures proper transfer encoding,
|
| 162 |
+
and merges them with the proxy response headers.
|
| 163 |
+
|
| 164 |
+
Args:
|
| 165 |
+
original_headers (httpx.Headers): The original headers from the upstream response.
|
| 166 |
+
proxy_response_headers (dict): Additional headers to be included in the proxy response.
|
| 167 |
+
|
| 168 |
+
Returns:
|
| 169 |
+
dict: The prepared headers for the proxy response.
|
| 170 |
+
"""
|
| 171 |
+
response_headers = {k: v for k, v in original_headers.multi_items() if k in SUPPORTED_RESPONSE_HEADERS}
|
| 172 |
+
transfer_encoding = response_headers.get("transfer-encoding", "")
|
| 173 |
+
if "chunked" not in transfer_encoding:
|
| 174 |
+
transfer_encoding += ", chunked" if transfer_encoding else "chunked"
|
| 175 |
+
response_headers["transfer-encoding"] = transfer_encoding
|
| 176 |
+
response_headers.update(proxy_response_headers)
|
| 177 |
+
return response_headers
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
async def proxy_stream(method: str, stream_params: ProxyStreamParams, proxy_headers: ProxyRequestHeaders):
|
| 181 |
+
"""
|
| 182 |
+
Proxies the stream request to the given video URL.
|
| 183 |
+
|
| 184 |
+
Args:
|
| 185 |
+
method (str): The HTTP method (e.g., GET, HEAD).
|
| 186 |
+
stream_params (ProxyStreamParams): The parameters for the stream request.
|
| 187 |
+
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
| 188 |
+
|
| 189 |
+
Returns:
|
| 190 |
+
Response: The HTTP response with the streamed content.
|
| 191 |
+
"""
|
| 192 |
+
return await handle_stream_request(
|
| 193 |
+
method, stream_params.destination, proxy_headers, stream_params.verify_ssl, stream_params.use_request_proxy
|
| 194 |
+
)
|
| 195 |
|
| 196 |
|
| 197 |
async def fetch_and_process_m3u8(
|
|
|
|
| 221 |
media_type="application/vnd.apple.mpegurl",
|
| 222 |
headers=response_headers,
|
| 223 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
except Exception as e:
|
| 225 |
+
return handle_exceptions(e)
|
|
|
|
| 226 |
finally:
|
| 227 |
await streamer.close()
|
| 228 |
|
|
|
|
| 258 |
|
| 259 |
async def get_manifest(
|
| 260 |
request: Request,
|
| 261 |
+
manifest_params: MPDManifestParams,
|
| 262 |
proxy_headers: ProxyRequestHeaders,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
):
|
| 264 |
"""
|
| 265 |
Retrieves and processes the MPD manifest, converting it to an HLS manifest.
|
| 266 |
|
| 267 |
Args:
|
| 268 |
request (Request): The incoming HTTP request.
|
| 269 |
+
manifest_params (MPDManifestParams): The parameters for the manifest request.
|
| 270 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
|
| 272 |
Returns:
|
| 273 |
Response: The HTTP response with the HLS manifest.
|
| 274 |
"""
|
| 275 |
try:
|
| 276 |
mpd_dict = await get_cached_mpd(
|
| 277 |
+
manifest_params.destination,
|
| 278 |
headers=proxy_headers.request,
|
| 279 |
+
parse_drm=not manifest_params.key_id and not manifest_params.key,
|
| 280 |
+
verify_ssl=manifest_params.verify_ssl,
|
| 281 |
+
use_request_proxy=manifest_params.use_request_proxy,
|
| 282 |
)
|
| 283 |
except DownloadError as e:
|
| 284 |
raise HTTPException(status_code=e.status_code, detail=f"Failed to download MPD: {e.message}")
|
|
|
|
| 288 |
# For non-DRM protected MPD, we still create an HLS manifest
|
| 289 |
return await process_manifest(request, mpd_dict, proxy_headers, None, None)
|
| 290 |
|
| 291 |
+
key_id, key = await handle_drm_key_data(manifest_params.key_id, manifest_params.key, drm_info)
|
| 292 |
|
| 293 |
# check if the provided key_id and key are valid
|
| 294 |
if key_id and len(key_id) != 32:
|
|
|
|
| 301 |
|
| 302 |
async def get_playlist(
|
| 303 |
request: Request,
|
| 304 |
+
playlist_params: MPDPlaylistParams,
|
|
|
|
| 305 |
proxy_headers: ProxyRequestHeaders,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
):
|
| 307 |
"""
|
| 308 |
Retrieves and processes the MPD manifest, converting it to an HLS playlist for a specific profile.
|
| 309 |
|
| 310 |
Args:
|
| 311 |
request (Request): The incoming HTTP request.
|
| 312 |
+
playlist_params (MPDPlaylistParams): The parameters for the playlist request.
|
|
|
|
| 313 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
|
| 315 |
Returns:
|
| 316 |
Response: The HTTP response with the HLS playlist.
|
| 317 |
"""
|
| 318 |
mpd_dict = await get_cached_mpd(
|
| 319 |
+
playlist_params.destination,
|
| 320 |
headers=proxy_headers.request,
|
| 321 |
+
parse_drm=not playlist_params.key_id and not playlist_params.key,
|
| 322 |
+
parse_segment_profile_id=playlist_params.profile_id,
|
| 323 |
+
verify_ssl=playlist_params.verify_ssl,
|
| 324 |
+
use_request_proxy=playlist_params.use_request_proxy,
|
| 325 |
)
|
| 326 |
+
return await process_playlist(request, mpd_dict, playlist_params.profile_id, proxy_headers)
|
| 327 |
|
| 328 |
|
| 329 |
async def get_segment(
|
| 330 |
+
segment_params: MPDSegmentParams,
|
|
|
|
|
|
|
| 331 |
proxy_headers: ProxyRequestHeaders,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 332 |
):
|
| 333 |
"""
|
| 334 |
Retrieves and processes a media segment, decrypting it if necessary.
|
| 335 |
|
| 336 |
Args:
|
| 337 |
+
segment_params (MPDSegmentParams): The parameters for the segment request.
|
|
|
|
|
|
|
| 338 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 339 |
|
| 340 |
Returns:
|
| 341 |
Response: The HTTP response with the processed segment.
|
| 342 |
"""
|
| 343 |
try:
|
| 344 |
+
init_content = await get_cached_init_segment(
|
| 345 |
+
segment_params.init_url, proxy_headers.request, segment_params.verify_ssl, segment_params.use_request_proxy
|
| 346 |
+
)
|
| 347 |
segment_content = await download_file_with_retry(
|
| 348 |
+
segment_params.segment_url,
|
| 349 |
+
proxy_headers.request,
|
| 350 |
+
verify_ssl=segment_params.verify_ssl,
|
| 351 |
+
use_request_proxy=segment_params.use_request_proxy,
|
| 352 |
)
|
| 353 |
+
except Exception as e:
|
| 354 |
+
return handle_exceptions(e)
|
| 355 |
+
|
| 356 |
+
return await process_segment(
|
| 357 |
+
init_content,
|
| 358 |
+
segment_content,
|
| 359 |
+
segment_params.mime_type,
|
| 360 |
+
proxy_headers,
|
| 361 |
+
segment_params.key_id,
|
| 362 |
+
segment_params.key,
|
| 363 |
+
)
|
| 364 |
|
| 365 |
|
| 366 |
async def get_public_ip(use_request_proxy: bool = True):
|
mediaflow_proxy/routes.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
| 1 |
-
from
|
| 2 |
-
|
|
|
|
| 3 |
|
| 4 |
from .handlers import handle_hls_stream_proxy, proxy_stream, get_manifest, get_playlist, get_segment, get_public_ip
|
|
|
|
| 5 |
from .utils.http_utils import get_proxy_headers, ProxyRequestHeaders
|
| 6 |
|
| 7 |
proxy_router = APIRouter()
|
|
@@ -11,144 +13,101 @@ proxy_router = APIRouter()
|
|
| 11 |
@proxy_router.get("/hls/manifest.m3u8")
|
| 12 |
async def hls_stream_proxy(
|
| 13 |
request: Request,
|
| 14 |
-
|
| 15 |
-
proxy_headers: ProxyRequestHeaders
|
| 16 |
-
key_url: HttpUrl | None = None,
|
| 17 |
-
verify_ssl: bool = False,
|
| 18 |
-
use_request_proxy: bool = True,
|
| 19 |
):
|
| 20 |
"""
|
| 21 |
Proxify HLS stream requests, fetching and processing the m3u8 playlist or streaming the content.
|
| 22 |
|
| 23 |
Args:
|
| 24 |
request (Request): The incoming HTTP request.
|
| 25 |
-
|
| 26 |
-
key_url (HttpUrl, optional): The HLS Key URL to replace the original key URL. Defaults to None. (Useful for bypassing some sneaky protection)
|
| 27 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
| 28 |
-
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to False.
|
| 29 |
-
use_request_proxy (bool, optional): Whether to use the MediaFlow proxy configuration. Defaults to True.
|
| 30 |
|
| 31 |
Returns:
|
| 32 |
Response: The HTTP response with the processed m3u8 playlist or streamed content.
|
| 33 |
"""
|
| 34 |
-
|
| 35 |
-
return await handle_hls_stream_proxy(request, destination, proxy_headers, key_url, verify_ssl, use_request_proxy)
|
| 36 |
|
| 37 |
|
| 38 |
@proxy_router.head("/stream")
|
| 39 |
@proxy_router.get("/stream")
|
| 40 |
async def proxy_stream_endpoint(
|
| 41 |
request: Request,
|
| 42 |
-
|
| 43 |
-
proxy_headers: ProxyRequestHeaders
|
| 44 |
-
verify_ssl: bool = False,
|
| 45 |
-
use_request_proxy: bool = True,
|
| 46 |
):
|
| 47 |
"""
|
| 48 |
Proxies stream requests to the given video URL.
|
| 49 |
|
| 50 |
Args:
|
| 51 |
request (Request): The incoming HTTP request.
|
| 52 |
-
|
| 53 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
| 54 |
-
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to False.
|
| 55 |
-
use_request_proxy (bool, optional): Whether to use the MediaFlow proxy configuration. Defaults to True.
|
| 56 |
|
| 57 |
Returns:
|
| 58 |
Response: The HTTP response with the streamed content.
|
| 59 |
"""
|
| 60 |
proxy_headers.request.update({"range": proxy_headers.request.get("range", "bytes=0-")})
|
| 61 |
-
return await proxy_stream(request.method,
|
| 62 |
|
| 63 |
|
| 64 |
@proxy_router.get("/mpd/manifest.m3u8")
|
| 65 |
async def manifest_endpoint(
|
| 66 |
request: Request,
|
| 67 |
-
|
| 68 |
-
proxy_headers: ProxyRequestHeaders
|
| 69 |
-
key_id: str = None,
|
| 70 |
-
key: str = None,
|
| 71 |
-
verify_ssl: bool = False,
|
| 72 |
-
use_request_proxy: bool = True,
|
| 73 |
):
|
| 74 |
"""
|
| 75 |
Retrieves and processes the MPD manifest, converting it to an HLS manifest.
|
| 76 |
|
| 77 |
Args:
|
| 78 |
request (Request): The incoming HTTP request.
|
| 79 |
-
|
| 80 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
| 81 |
-
key_id (str, optional): The DRM key ID. Defaults to None.
|
| 82 |
-
key (str, optional): The DRM key. Defaults to None.
|
| 83 |
-
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to False.
|
| 84 |
-
use_request_proxy (bool, optional): Whether to use the MediaFlow proxy configuration. Defaults to True.
|
| 85 |
|
| 86 |
Returns:
|
| 87 |
Response: The HTTP response with the HLS manifest.
|
| 88 |
"""
|
| 89 |
-
return await get_manifest(request,
|
| 90 |
|
| 91 |
|
| 92 |
@proxy_router.get("/mpd/playlist.m3u8")
|
| 93 |
async def playlist_endpoint(
|
| 94 |
request: Request,
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
proxy_headers: ProxyRequestHeaders = Depends(get_proxy_headers),
|
| 98 |
-
key_id: str = None,
|
| 99 |
-
key: str = None,
|
| 100 |
-
verify_ssl: bool = False,
|
| 101 |
-
use_request_proxy: bool = True,
|
| 102 |
):
|
| 103 |
"""
|
| 104 |
Retrieves and processes the MPD manifest, converting it to an HLS playlist for a specific profile.
|
| 105 |
|
| 106 |
Args:
|
| 107 |
request (Request): The incoming HTTP request.
|
| 108 |
-
|
| 109 |
-
profile_id (str): The profile ID to generate the playlist for.
|
| 110 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
| 111 |
-
key_id (str, optional): The DRM key ID. Defaults to None.
|
| 112 |
-
key (str, optional): The DRM key. Defaults to None.
|
| 113 |
-
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to False.
|
| 114 |
-
use_request_proxy (bool, optional): Whether to use the MediaFlow proxy configuration. Defaults to True.
|
| 115 |
|
| 116 |
Returns:
|
| 117 |
Response: The HTTP response with the HLS playlist.
|
| 118 |
"""
|
| 119 |
-
return await get_playlist(request,
|
| 120 |
|
| 121 |
|
| 122 |
@proxy_router.get("/mpd/segment")
|
| 123 |
async def segment_endpoint(
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
mime_type: str,
|
| 127 |
-
proxy_headers: ProxyRequestHeaders = Depends(get_proxy_headers),
|
| 128 |
-
key_id: str = None,
|
| 129 |
-
key: str = None,
|
| 130 |
-
verify_ssl: bool = False,
|
| 131 |
-
use_request_proxy: bool = True,
|
| 132 |
):
|
| 133 |
"""
|
| 134 |
Retrieves and processes a media segment, decrypting it if necessary.
|
| 135 |
|
| 136 |
Args:
|
| 137 |
-
|
| 138 |
-
segment_url (HttpUrl): The URL of the media segment.
|
| 139 |
-
mime_type (str): The MIME type of the segment.
|
| 140 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
| 141 |
-
key_id (str, optional): The DRM key ID. Defaults to None.
|
| 142 |
-
key (str, optional): The DRM key. Defaults to None.
|
| 143 |
-
verify_ssl (bool, optional): Whether to verify the SSL certificate of the destination. Defaults to False.
|
| 144 |
-
use_request_proxy (bool, optional): Whether to use the MediaFlow proxy configuration. Defaults to True.
|
| 145 |
|
| 146 |
Returns:
|
| 147 |
Response: The HTTP response with the processed segment.
|
| 148 |
"""
|
| 149 |
-
return await get_segment(
|
| 150 |
-
str(init_url), str(segment_url), mime_type, proxy_headers, key_id, key, verify_ssl, use_request_proxy
|
| 151 |
-
)
|
| 152 |
|
| 153 |
|
| 154 |
@proxy_router.get("/ip")
|
|
|
|
| 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
|
| 7 |
from .utils.http_utils import get_proxy_headers, ProxyRequestHeaders
|
| 8 |
|
| 9 |
proxy_router = APIRouter()
|
|
|
|
| 13 |
@proxy_router.get("/hls/manifest.m3u8")
|
| 14 |
async def hls_stream_proxy(
|
| 15 |
request: Request,
|
| 16 |
+
hls_params: Annotated[HLSManifestParams, Query()],
|
| 17 |
+
proxy_headers: Annotated[ProxyRequestHeaders, Depends(get_proxy_headers)],
|
|
|
|
|
|
|
|
|
|
| 18 |
):
|
| 19 |
"""
|
| 20 |
Proxify HLS stream requests, fetching and processing the m3u8 playlist or streaming the content.
|
| 21 |
|
| 22 |
Args:
|
| 23 |
request (Request): The incoming HTTP request.
|
| 24 |
+
hls_params (HLSPlaylistParams): The parameters for the HLS stream request.
|
|
|
|
| 25 |
proxy_headers (ProxyRequestHeaders): 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 |
+
return await handle_hls_stream_proxy(request, hls_params, proxy_headers)
|
|
|
|
| 31 |
|
| 32 |
|
| 33 |
@proxy_router.head("/stream")
|
| 34 |
@proxy_router.get("/stream")
|
| 35 |
async def proxy_stream_endpoint(
|
| 36 |
request: Request,
|
| 37 |
+
stream_params: Annotated[ProxyStreamParams, Query()],
|
| 38 |
+
proxy_headers: Annotated[ProxyRequestHeaders, Depends(get_proxy_headers)],
|
|
|
|
|
|
|
| 39 |
):
|
| 40 |
"""
|
| 41 |
Proxies stream requests to the given video URL.
|
| 42 |
|
| 43 |
Args:
|
| 44 |
request (Request): The incoming HTTP request.
|
| 45 |
+
stream_params (ProxyStreamParams): The parameters for the stream request.
|
| 46 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
|
|
|
|
|
|
| 47 |
|
| 48 |
Returns:
|
| 49 |
Response: The HTTP response with the streamed content.
|
| 50 |
"""
|
| 51 |
proxy_headers.request.update({"range": proxy_headers.request.get("range", "bytes=0-")})
|
| 52 |
+
return await proxy_stream(request.method, stream_params, proxy_headers)
|
| 53 |
|
| 54 |
|
| 55 |
@proxy_router.get("/mpd/manifest.m3u8")
|
| 56 |
async def manifest_endpoint(
|
| 57 |
request: Request,
|
| 58 |
+
manifest_params: Annotated[MPDManifestParams, Query()],
|
| 59 |
+
proxy_headers: Annotated[ProxyRequestHeaders, Depends(get_proxy_headers)],
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
):
|
| 61 |
"""
|
| 62 |
Retrieves and processes the MPD manifest, converting it to an HLS manifest.
|
| 63 |
|
| 64 |
Args:
|
| 65 |
request (Request): The incoming HTTP request.
|
| 66 |
+
manifest_params (MPDManifestParams): The parameters for the manifest request.
|
| 67 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
Returns:
|
| 70 |
Response: The HTTP response with the HLS manifest.
|
| 71 |
"""
|
| 72 |
+
return await get_manifest(request, manifest_params, proxy_headers)
|
| 73 |
|
| 74 |
|
| 75 |
@proxy_router.get("/mpd/playlist.m3u8")
|
| 76 |
async def playlist_endpoint(
|
| 77 |
request: Request,
|
| 78 |
+
playlist_params: Annotated[MPDPlaylistParams, Query()],
|
| 79 |
+
proxy_headers: Annotated[ProxyRequestHeaders, Depends(get_proxy_headers)],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
):
|
| 81 |
"""
|
| 82 |
Retrieves and processes the MPD manifest, converting it to an HLS playlist for a specific profile.
|
| 83 |
|
| 84 |
Args:
|
| 85 |
request (Request): The incoming HTTP request.
|
| 86 |
+
playlist_params (MPDPlaylistParams): The parameters for the playlist request.
|
|
|
|
| 87 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
Returns:
|
| 90 |
Response: The HTTP response with the HLS playlist.
|
| 91 |
"""
|
| 92 |
+
return await get_playlist(request, playlist_params, proxy_headers)
|
| 93 |
|
| 94 |
|
| 95 |
@proxy_router.get("/mpd/segment")
|
| 96 |
async def segment_endpoint(
|
| 97 |
+
segment_params: Annotated[MPDSegmentParams, Query()],
|
| 98 |
+
proxy_headers: Annotated[ProxyRequestHeaders, Depends(get_proxy_headers)],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
):
|
| 100 |
"""
|
| 101 |
Retrieves and processes a media segment, decrypting it if necessary.
|
| 102 |
|
| 103 |
Args:
|
| 104 |
+
segment_params (MPDSegmentParams): The parameters for the segment request.
|
|
|
|
|
|
|
| 105 |
proxy_headers (ProxyRequestHeaders): The headers to include in the request.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
Returns:
|
| 108 |
Response: The HTTP response with the processed segment.
|
| 109 |
"""
|
| 110 |
+
return await get_segment(segment_params, proxy_headers)
|
|
|
|
|
|
|
| 111 |
|
| 112 |
|
| 113 |
@proxy_router.get("/ip")
|
mediaflow_proxy/schemas.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from pydantic import BaseModel, Field, IPvAnyAddress
|
| 2 |
|
| 3 |
|
| 4 |
class GenerateUrlRequest(BaseModel):
|
|
@@ -15,3 +15,43 @@ class GenerateUrlRequest(BaseModel):
|
|
| 15 |
None, description="API password for encryption. If not provided, the URL will only be encoded."
|
| 16 |
)
|
| 17 |
ip: IPvAnyAddress | None = Field(None, description="The IP address to restrict the URL to.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic import BaseModel, Field, IPvAnyAddress, ConfigDict
|
| 2 |
|
| 3 |
|
| 4 |
class GenerateUrlRequest(BaseModel):
|
|
|
|
| 15 |
None, description="API password for encryption. If not provided, the URL will only be encoded."
|
| 16 |
)
|
| 17 |
ip: IPvAnyAddress | None = Field(None, description="The IP address to restrict the URL to.")
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class GenericParams(BaseModel):
|
| 21 |
+
model_config = ConfigDict(populate_by_name=True)
|
| 22 |
+
|
| 23 |
+
verify_ssl: bool = Field(False, description="Whether to verify the SSL certificate of the destination.")
|
| 24 |
+
use_request_proxy: bool = Field(True, description="Whether to use the MediaFlow proxy configuration.")
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class HLSManifestParams(GenericParams):
|
| 28 |
+
destination: str = Field(..., description="The URL of the HLS manifest.", alias="d")
|
| 29 |
+
key_url: str | None = Field(
|
| 30 |
+
None,
|
| 31 |
+
description="The HLS Key URL to replace the original key URL. Defaults to None. (Useful for bypassing some sneaky protection)",
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class ProxyStreamParams(GenericParams):
|
| 36 |
+
destination: str = Field(..., description="The URL of the stream.", alias="d")
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class MPDManifestParams(GenericParams):
|
| 40 |
+
destination: str = Field(..., description="The URL of the MPD manifest.", alias="d")
|
| 41 |
+
key_id: str | None = Field(None, description="The DRM key ID (optional).")
|
| 42 |
+
key: str | None = Field(None, description="The DRM key (optional).")
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class MPDPlaylistParams(GenericParams):
|
| 46 |
+
destination: str = Field(..., description="The URL of the MPD manifest.", alias="d")
|
| 47 |
+
profile_id: str = Field(..., description="The profile ID to generate the playlist for.")
|
| 48 |
+
key_id: str | None = Field(None, description="The DRM key ID (optional).")
|
| 49 |
+
key: str | None = Field(None, description="The DRM key (optional).")
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class MPDSegmentParams(GenericParams):
|
| 53 |
+
init_url: str = Field(..., description="The URL of the initialization segment.")
|
| 54 |
+
segment_url: str = Field(..., description="The URL of the media segment.")
|
| 55 |
+
mime_type: str = Field(..., description="The MIME type of the segment.")
|
| 56 |
+
key_id: str | None = Field(None, description="The DRM key ID (optional).")
|
| 57 |
+
key: str | None = Field(None, description="The DRM key (optional).")
|