mhdzumair commited on
Commit
e82c591
1 Parent(s): 7f40f6a

Add home page, health endpoints & organize routes & api_password in header support

Browse files
logo.png DELETED
Binary file (340 kB)
 
mediaflow_proxy/main.py CHANGED
@@ -1,182 +1,51 @@
1
  import logging
2
 
3
- from fastapi import FastAPI, Request, Depends, Security, HTTPException
4
- from fastapi.security import APIKeyQuery
5
- from pydantic import HttpUrl
 
6
 
7
  from mediaflow_proxy.configs import settings
8
- from .handlers import handle_hls_stream_proxy, proxy_stream, get_manifest, get_playlist, get_segment, get_public_ip
9
 
10
  logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
11
  app = FastAPI()
12
- api_key_query = APIKeyQuery(name="api_password", auto_error=False)
 
13
 
14
 
15
- async def verify_api_key(api_key: str = Security(api_key_query)):
16
  """
17
  Verifies the API key for the request.
18
 
19
  Args:
20
  api_key (str): The API key to validate.
 
21
 
22
  Raises:
23
  HTTPException: If the API key is invalid.
24
  """
25
- if api_key != settings.api_password:
26
- raise HTTPException(status_code=403, detail="Could not validate credentials")
27
 
 
28
 
29
- def get_proxy_headers(request: Request) -> dict:
30
- """
31
- Extracts proxy headers from the request query parameters.
32
 
33
- Args:
34
- request (Request): The incoming HTTP request.
 
35
 
36
- Returns:
37
- dict: A dictionary of proxy headers.
38
- """
39
- return {k[2:]: v for k, v in request.query_params.items() if k.startswith("h_")}
40
-
41
-
42
- @app.head("/proxy/hls")
43
- @app.get("/proxy/hls")
44
- async def hls_stream_proxy(
45
- request: Request,
46
- d: HttpUrl,
47
- headers: dict = Depends(get_proxy_headers),
48
- key_url: HttpUrl | None = None,
49
- _: str = Depends(verify_api_key),
50
- ):
51
- """
52
- Proxify HLS stream requests, fetching and processing the m3u8 playlist or streaming the content.
53
-
54
- Args:
55
- request (Request): The incoming HTTP request.
56
- d (HttpUrl): The destination URL to fetch the content from.
57
- key_url (HttpUrl, optional): The HLS Key URL to replace the original key URL. Defaults to None. (Useful for bypassing some sneaky protection)
58
- headers (dict): The headers to include in the request.
59
- _ (str): The API key to validate.
60
-
61
- Returns:
62
- Response: The HTTP response with the processed m3u8 playlist or streamed content.
63
- """
64
- destination = str(d)
65
- return await handle_hls_stream_proxy(request, destination, headers, key_url)
66
 
 
 
 
67
 
68
- @app.head("/proxy/stream")
69
- @app.get("/proxy/stream")
70
- async def proxy_stream_endpoint(
71
- request: Request, d: HttpUrl, headers: dict = Depends(get_proxy_headers), _: str = Depends(verify_api_key)
72
- ):
73
- """
74
- Proxies stream requests to the given video URL.
75
 
76
- Args:
77
- request (Request): The incoming HTTP request.
78
- d (HttpUrl): The URL of the video to stream.
79
- headers (dict): The headers to include in the request.
80
- _: str: The API key to validate.
81
 
82
- Returns:
83
- Response: The HTTP response with the streamed content.
84
- """
85
- headers.update({"range": headers.get("range", "bytes=0-")})
86
- return await proxy_stream(request.method, str(d), headers)
87
-
88
-
89
- @app.get("/proxy/mpd/manifest")
90
- async def manifest_endpoint(
91
- request: Request,
92
- d: HttpUrl,
93
- headers: dict = Depends(get_proxy_headers),
94
- key_id: str = None,
95
- key: str = None,
96
- _: str = Depends(verify_api_key),
97
- ):
98
- """
99
- Retrieves and processes the MPD manifest, converting it to an HLS manifest.
100
 
101
- Args:
102
- request (Request): The incoming HTTP request.
103
- d (HttpUrl): The URL of the MPD manifest.
104
- headers (dict): The headers to include in the request.
105
- key_id (str, optional): The DRM key ID. Defaults to None.
106
- key (str, optional): The DRM key. Defaults to None.
107
- _: str: The API key to validate.
108
-
109
- Returns:
110
- Response: The HTTP response with the HLS manifest.
111
- """
112
- return await get_manifest(request, str(d), headers, key_id, key)
113
-
114
-
115
- @app.get("/proxy/mpd/playlist")
116
- async def playlist_endpoint(
117
- request: Request,
118
- d: HttpUrl,
119
- profile_id: str,
120
- headers: dict = Depends(get_proxy_headers),
121
- key_id: str = None,
122
- key: str = None,
123
- _: str = Depends(verify_api_key),
124
- ):
125
- """
126
- Retrieves and processes the MPD manifest, converting it to an HLS playlist for a specific profile.
127
 
128
- Args:
129
- request (Request): The incoming HTTP request.
130
- d (HttpUrl): The URL of the MPD manifest.
131
- profile_id (str): The profile ID to generate the playlist for.
132
- headers (dict): The headers to include in the request.
133
- key_id (str, optional): The DRM key ID. Defaults to None.
134
- key (str, optional): The DRM key. Defaults to None.
135
- _: str: The API key to validate.
136
-
137
- Returns:
138
- Response: The HTTP response with the HLS playlist.
139
- """
140
- return await get_playlist(request, str(d), profile_id, headers, key_id, key)
141
-
142
-
143
- @app.get("/proxy/mpd/segment")
144
- async def segment_endpoint(
145
- init_url: HttpUrl,
146
- segment_url: HttpUrl,
147
- mime_type: str,
148
- headers: dict = Depends(get_proxy_headers),
149
- key_id: str = None,
150
- key: str = None,
151
- _: str = Depends(verify_api_key),
152
- ):
153
- """
154
- Retrieves and processes a media segment, decrypting it if necessary.
155
-
156
- Args:
157
- init_url (HttpUrl): The URL of the initialization segment.
158
- segment_url (HttpUrl): The URL of the media segment.
159
- mime_type (str): The MIME type of the segment.
160
- headers (dict): The headers to include in the request.
161
- key_id (str, optional): The DRM key ID. Defaults to None.
162
- key (str, optional): The DRM key. Defaults to None.
163
- _: str: The API key to validate.
164
-
165
- Returns:
166
- Response: The HTTP response with the processed segment.
167
- """
168
- return await get_segment(str(init_url), str(segment_url), mime_type, headers, key_id, key)
169
-
170
-
171
- @app.get("/proxy/ip")
172
- async def get_mediaflow_proxy_public_ip(_: str = Depends(verify_api_key)):
173
- """
174
- Retrieves the public IP address of the MediaFlow proxy server.
175
-
176
- Args:
177
- _: str: The API key to validate.
178
-
179
- Returns:
180
- Response: The HTTP response with the public IP address in the form of a JSON object. {"ip": "xxx.xxx.xxx.xxx"}
181
- """
182
- return await get_public_ip()
 
1
  import logging
2
 
3
+ from fastapi import FastAPI, Depends, Security, HTTPException
4
+ from fastapi.security import APIKeyQuery, APIKeyHeader
5
+ from starlette.responses import RedirectResponse
6
+ from starlette.staticfiles import StaticFiles
7
 
8
  from mediaflow_proxy.configs import settings
9
+ from mediaflow_proxy.routes import proxy_router
10
 
11
  logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
12
  app = FastAPI()
13
+ api_password_query = APIKeyQuery(name="api_password", auto_error=False)
14
+ api_password_header = APIKeyHeader(name="api_password", auto_error=False)
15
 
16
 
17
+ async def verify_api_key(api_key: str = Security(api_password_query), api_key_alt: str = Security(api_password_header)):
18
  """
19
  Verifies the API key for the request.
20
 
21
  Args:
22
  api_key (str): The API key to validate.
23
+ api_key_alt (str): The alternative API key to validate.
24
 
25
  Raises:
26
  HTTPException: If the API key is invalid.
27
  """
28
+ if api_key == settings.api_password or api_key_alt == settings.api_password:
29
+ return
30
 
31
+ raise HTTPException(status_code=403, detail="Could not validate credentials")
32
 
 
 
 
33
 
34
+ @app.get("/health")
35
+ async def health_check():
36
+ return {"status": "healthy"}
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
+ @app.get("/favicon.ico")
40
+ async def get_favicon():
41
+ return RedirectResponse(url="/logo.png")
42
 
 
 
 
 
 
 
 
43
 
44
+ app.include_router(proxy_router, prefix="/proxy", tags=["proxy"], dependencies=[Depends(verify_api_key)])
45
+ app.mount("/", StaticFiles(directory="static", html=True), name="static")
 
 
 
46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
+ if __name__ == "__main__":
49
+ import uvicorn
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
+ uvicorn.run(app, host="127.0.0.1", port=8888)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
mediaflow_proxy/routes.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import Request, Depends, APIRouter
2
+ from pydantic import HttpUrl
3
+
4
+ from .handlers import handle_hls_stream_proxy, proxy_stream, get_manifest, get_playlist, get_segment, get_public_ip
5
+
6
+ proxy_router = APIRouter()
7
+
8
+
9
+ def get_proxy_headers(request: Request) -> dict:
10
+ """
11
+ Extracts proxy headers from the request query parameters.
12
+
13
+ Args:
14
+ request (Request): The incoming HTTP request.
15
+
16
+ Returns:
17
+ dict: A dictionary of proxy headers.
18
+ """
19
+ return {k[2:]: v for k, v in request.query_params.items() if k.startswith("h_")}
20
+
21
+
22
+ @proxy_router.head("/hls")
23
+ @proxy_router.get("/hls")
24
+ async def hls_stream_proxy(
25
+ request: Request,
26
+ d: HttpUrl,
27
+ headers: dict = Depends(get_proxy_headers),
28
+ key_url: HttpUrl | None = None,
29
+ ):
30
+ """
31
+ Proxify HLS stream requests, fetching and processing the m3u8 playlist or streaming the content.
32
+
33
+ Args:
34
+ request (Request): The incoming HTTP request.
35
+ d (HttpUrl): The destination URL to fetch the content from.
36
+ key_url (HttpUrl, optional): The HLS Key URL to replace the original key URL. Defaults to None. (Useful for bypassing some sneaky protection)
37
+ headers (dict): The headers to include in the request.
38
+
39
+ Returns:
40
+ Response: The HTTP response with the processed m3u8 playlist or streamed content.
41
+ """
42
+ destination = str(d)
43
+ return await handle_hls_stream_proxy(request, destination, headers, key_url)
44
+
45
+
46
+ @proxy_router.head("/stream")
47
+ @proxy_router.get("/stream")
48
+ async def proxy_stream_endpoint(request: Request, d: HttpUrl, headers: dict = Depends(get_proxy_headers)):
49
+ """
50
+ Proxies stream requests to the given video URL.
51
+
52
+ Args:
53
+ request (Request): The incoming HTTP request.
54
+ d (HttpUrl): The URL of the video to stream.
55
+ headers (dict): The headers to include in the request.
56
+
57
+ Returns:
58
+ Response: The HTTP response with the streamed content.
59
+ """
60
+ headers.update({"range": headers.get("range", "bytes=0-")})
61
+ return await proxy_stream(request.method, str(d), headers)
62
+
63
+
64
+ @proxy_router.get("/mpd/manifest")
65
+ async def manifest_endpoint(
66
+ request: Request,
67
+ d: HttpUrl,
68
+ headers: dict = Depends(get_proxy_headers),
69
+ key_id: str = None,
70
+ key: str = None,
71
+ ):
72
+ """
73
+ Retrieves and processes the MPD manifest, converting it to an HLS manifest.
74
+
75
+ Args:
76
+ request (Request): The incoming HTTP request.
77
+ d (HttpUrl): The URL of the MPD manifest.
78
+ headers (dict): The headers to include in the request.
79
+ key_id (str, optional): The DRM key ID. Defaults to None.
80
+ key (str, optional): The DRM key. Defaults to None.
81
+
82
+ Returns:
83
+ Response: The HTTP response with the HLS manifest.
84
+ """
85
+ return await get_manifest(request, str(d), headers, key_id, key)
86
+
87
+
88
+ @proxy_router.get("/mpd/playlist")
89
+ async def playlist_endpoint(
90
+ request: Request,
91
+ d: HttpUrl,
92
+ profile_id: str,
93
+ headers: dict = Depends(get_proxy_headers),
94
+ key_id: str = None,
95
+ key: str = None,
96
+ ):
97
+ """
98
+ Retrieves and processes the MPD manifest, converting it to an HLS playlist for a specific profile.
99
+
100
+ Args:
101
+ request (Request): The incoming HTTP request.
102
+ d (HttpUrl): The URL of the MPD manifest.
103
+ profile_id (str): The profile ID to generate the playlist for.
104
+ headers (dict): The headers to include in the request.
105
+ key_id (str, optional): The DRM key ID. Defaults to None.
106
+ key (str, optional): The DRM key. Defaults to None.
107
+
108
+ Returns:
109
+ Response: The HTTP response with the HLS playlist.
110
+ """
111
+ return await get_playlist(request, str(d), profile_id, headers, key_id, key)
112
+
113
+
114
+ @proxy_router.get("/mpd/segment")
115
+ async def segment_endpoint(
116
+ init_url: HttpUrl,
117
+ segment_url: HttpUrl,
118
+ mime_type: str,
119
+ headers: dict = Depends(get_proxy_headers),
120
+ key_id: str = None,
121
+ key: str = None,
122
+ ):
123
+ """
124
+ Retrieves and processes a media segment, decrypting it if necessary.
125
+
126
+ Args:
127
+ init_url (HttpUrl): The URL of the initialization segment.
128
+ segment_url (HttpUrl): The URL of the media segment.
129
+ mime_type (str): The MIME type of the segment.
130
+ headers (dict): The headers to include in the request.
131
+ key_id (str, optional): The DRM key ID. Defaults to None.
132
+ key (str, optional): The DRM key. Defaults to None.
133
+
134
+ Returns:
135
+ Response: The HTTP response with the processed segment.
136
+ """
137
+ return await get_segment(str(init_url), str(segment_url), mime_type, headers, key_id, key)
138
+
139
+
140
+ @proxy_router.get("/ip")
141
+ async def get_mediaflow_proxy_public_ip():
142
+ """
143
+ Retrieves the public IP address of the MediaFlow proxy server.
144
+
145
+ Returns:
146
+ Response: The HTTP response with the public IP address in the form of a JSON object. {"ip": "xxx.xxx.xxx.xxx"}
147
+ """
148
+ return await get_public_ip()
static/index.html ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>MediaFlow Proxy</title>
7
+ <link rel="icon" href="/logo.png" type="image/x-icon">
8
+ <style>
9
+ body {
10
+ font-family: Arial, sans-serif;
11
+ line-height: 1.6;
12
+ color: #333;
13
+ max-width: 800px;
14
+ margin: 0 auto;
15
+ padding: 20px;
16
+ background-color: #f9f9f9;
17
+ }
18
+
19
+ header {
20
+ background-color: #90aacc;
21
+ color: #fff;
22
+ padding: 10px 0;
23
+ text-align: center;
24
+ }
25
+
26
+ header img {
27
+ width: 200px;
28
+ height: 200px;
29
+ vertical-align: middle;
30
+ border-radius: 15px;
31
+ }
32
+
33
+ header h1 {
34
+ display: inline;
35
+ margin-left: 20px;
36
+ font-size: 36px;
37
+ }
38
+
39
+ .feature {
40
+ background-color: #f4f4f4;
41
+ border-left: 4px solid #3498db;
42
+ padding: 10px;
43
+ margin-bottom: 10px;
44
+ }
45
+
46
+ a {
47
+ color: #3498db;
48
+ }
49
+ </style>
50
+ </head>
51
+ <body>
52
+ <header>
53
+ <img src="/logo.png" alt="MediaFlow Proxy Logo">
54
+ <h1>MediaFlow Proxy</h1>
55
+ </header>
56
+ <p>A high-performance proxy server for streaming media, supporting HTTP(S), HLS, and MPEG-DASH with real-time DRM decryption.</p>
57
+
58
+ <h2>Key Features</h2>
59
+ <div class="feature">Convert MPEG-DASH streams (DRM-protected and non-protected) to HLS</div>
60
+ <div class="feature">Support for Clear Key DRM-protected MPD DASH streams</div>
61
+ <div class="feature">Handle both live and video-on-demand (VOD) DASH streams</div>
62
+ <div class="feature">Proxy HTTP/HTTPS links with custom headers</div>
63
+ <div class="feature">Proxy and modify HLS (M3U8) streams in real-time with custom headers and key URL modifications for bypassing some sneaky restrictions.</div>
64
+ <div class="feature">Protect against unauthorized access and network bandwidth abuses</div>
65
+
66
+ <h2>Getting Started</h2>
67
+ <p>Visit the <a href="https://github.com/mhdzumair/mediaflow-proxy">GitHub repository</a> for installation instructions and documentation.</p>
68
+
69
+ <h2>Premium Hosted Service</h2>
70
+ <p>For a hassle-free experience, check out <a href="https://store.elfhosted.com/product/mediaflow-proxy">premium hosted service on ElfHosted</a>.</p>
71
+
72
+ <h2>API Documentation</h2>
73
+ <p>Explore the <a href="/docs">Swagger UI</a> for comprehensive details about the API endpoints and their usage.</p>
74
+
75
+ </body>
76
+ </html>
static/logo.png ADDED