Fixed DLHD extractor#239
Conversation
WalkthroughAdds DLHD fingerprint/key-header tooling and AES-128 key flow to DLHDExtractor, introduces server lookup and m3u8 construction paths, and updates proxy and M3U8 processing to propagate DLHD key parameters and inject computed dynamic headers for key requests. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Extractor as DLHDExtractor
participant Server as DLHD Server
participant Proxy as Proxy Router
participant M3U8 as M3U8Processor
Client->>Extractor: extract(url)
Extractor->>Extractor: _extract_session_data(iframe_url)
Extractor->>Server: GET server_lookup_url (with channel headers)
Server-->>Extractor: server_key
Extractor->>Extractor: _build_m3u8_url(server_key, channel_key)
Extractor->>Server: GET m3u8_url
Server-->>Extractor: m3u8_content (contains key URLs)
Extractor->>Client: stream_data (includes dlhd_key_params)
Client->>Proxy: GET key_url?dlhd_salt=...
Proxy->>Proxy: compute_key_headers(key_url, dlhd_salt)
Proxy->>Proxy: inject X-Key-Timestamp/Nonce/Fingerprint/Key-Path (+ Authorization if present)
Proxy->>M3U8: proxy_url(key_url, use_full_url=true, is_playlist=false)
M3U8-->>Proxy: /stream endpoint URL
Proxy->>Server: GET key URL with dynamic headers
Server-->>Proxy: key bytes
Proxy-->>Client: proxied key response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Fix all issues with AI agents
Verify each finding against the current code and only fix it if needed.
In `@mediaflow_proxy/extractors/dlhd.py`:
- Around line 19-20: The current global call
logging.getLogger("asyncio").setLevel(logging.CRITICAL) silences all asyncio
logs; change this to a narrower fix: replace the Italian comment with an English
one and either (A) wrap the suppression in a platform guard (e.g., only run on
Windows via sys.platform) and limit it to that scope, or (B) implement a custom
logging.Filter attached to logging.getLogger("asyncio") that only filters
messages related to ConnectionResetError (or the exact message text) instead of
raising the logger level globally; alternatively consider catching
ConnectionResetError at the call-site instead of muting asyncio logs. Ensure
references to logging.getLogger("asyncio").setLevel and ConnectionResetError in
dlhd.py are updated accordingly.
- Around line 116-125: The proof-of-work loop in
mediaflow_proxy/extractors/dlhd.py leaves nonce at its initial value 0 if no
candidate is found after 100000 iterations, causing incorrect output; after the
for-loop that iterates i and computes prefix_value, detect the no-result case
(nonce still 0 and/or loop completed without break) and either raise a
descriptive exception or emit a warning via the module logger before returning,
so callers know the PoW failed; update the return path that yields (ts, nonce,
key_path) to handle this failure mode and reference the nonce variable, the for
i in range(100000) loop, and the md5/prefix_value check when implementing the
change.
- Line 570: The variable channel_key assigned as channel_key =
f"premium{channel_id}" is unused and causing a lint failure; remove that
assignment from the surrounding function or block (where channel_id is
referenced) so the unused variable is eliminated, or if it was intended to be
used, replace its usage points with the string f"premium{channel_id}" or
refactor to actually use channel_key — reference the symbol channel_key (and
channel_id) in dlhd.py to locate and remove or properly utilize the unused
assignment.
- Around line 496-558: The regexes in _extract_lovecdn_stream (m3u8_patterns and
url_pattern) are too broad and may pick up unrelated m3u8 links; after you find
a candidate stream_url (or each match in the loops for m3u8_patterns and the
fallback url_pattern) parse it with urlparse and validate its domain against an
allowlist of expected CDN hosts (e.g. lovecdn.ru, newkso.ru, known mirror
domains) or check netloc.endswith(...) for trusted suffixes; if the domain is
not allowed, skip that match and continue searching, only set stream_url when a
match passes the domain validation and otherwise fall back to other heuristics
or raise the ExtractorError. Ensure you reference stream_url, m3u8_patterns,
url_pattern, iframe_url and iframe_content when applying the validation so the
logic integrates into the existing search/construct/fallback flow.
In `@mediaflow_proxy/routes/proxy.py`:
- Around line 514-533: compute_key_headers in mediaflow_proxy.extractors.dlhd is
CPU-bound and currently runs synchronously in proxy.py causing event-loop
blocking; change proxy.py to call compute_key_headers via an executor (e.g.,
await asyncio.to_thread or loop.run_in_executor) instead of calling it directly,
and modify compute_key_headers to return the fingerprint as a 4th element so you
can remove the redundant compute_fingerprint() call in proxy.py; after awaiting
the threaded call, unpack ts, nonce, key_path, fingerprint and update
proxy_headers.request (and Authorization if dlhd_token) as before and keep the
existing logger.info call.
- Around line 197-203: The code is embedding sensitive values (dlhd_channel_salt
and dlhd_auth_token) into query_dict which makes dlhd_salt and dlhd_token appear
in URLs; instead, stop adding these secrets to query parameters and propagate
them securely—either attach the token as an Authorization or custom header when
building the proxied request (use headers rather than query params) or, if
necessary, encrypt the values using the existing encryption_handler before
inserting into query_dict (and decrypt server-side), and keep dlhd_iframe_url
non-sensitive or move it to a safe session/store; update the logic around
query_dict, dlhd_result, dlhd_channel_salt, dlhd_auth_token, dlhd_iframe_url and
any URL-building code so secrets are never placed raw in query strings.
In `@mediaflow_proxy/utils/extractor_helpers.py`:
- Line 11: The import line brings in quote which is unused; remove the unused
symbol by changing the import from "from urllib.parse import urlparse, quote" to
only import urlparse (i.e., drop "quote") so the unused-name lint error is
resolved; look for the import statement that references quote in
extractor_helpers.py (symbol: quote) and update it accordingly.
- Line 88: The log call in extractor_helpers.py uses an unnecessary f-string:
change the logger.info(f"DLHD key params extracted for dynamic header
computation") to a regular string literal (logger.info("DLHD key params
extracted for dynamic header computation")) to remove the extraneous f-prefix in
the logging statement inside the function where DLHD key params are extracted.
- Around line 81-91: The cache is storing the mutable dict `result` by reference
into `_dlhd_extraction_cache[destination]["data"]`, and subsequent in-place
mutations (pop/inject of `dlhd_key_params` → `dlhd_channel_salt`,
`dlhd_auth_token`, `dlhd_iframe_url`) will corrupt the shared cached object; fix
by ensuring you cache an immutable copy instead of the original `result` (e.g.,
shallow or deep copy) or perform the pop/inject on a new dict and store that
copy in `_dlhd_extraction_cache` so downstream callers cannot mutate the cached
entry; reference the variables `_dlhd_extraction_cache`, `result`,
`destination`, and the dlhd_* keys in your change.
In `@mediaflow_proxy/utils/m3u8_processor.py`:
- Around line 691-695: The DLHD key detection is duplicated between
process_key_line and proxy_url; add a small helper function (e.g.
_is_dlhd_key_url) that accepts the request/query_params and the uri (or its url
string) and returns the boolean currently computed by "dlhd_salt" in
query_params and "/key/" in uri.geturl(); replace the inline checks in
process_key_line (where is_dlhd_key_request is computed) and in proxy_url (where
the same check decides the /stream endpoint) to call this helper so the
detection logic is centralized and consistent.
- Around line 817-821: The current logic in m3u8_processor.py that computes
proxy_url by doing self.mediaflow_proxy_url.replace("/hls/manifest.m3u8",
"/stream") is brittle (it silently no-ops if the path changes); instead
construct the /stream URL from the request router (e.g., use
request.url_for("proxy_stream_endpoint") or the equivalent named route) when
is_dlhd_key is true, falling back to a safe deterministic join of base URL and
"/stream" only if url_for is unavailable; update the block that references
is_dlhd_key, proxy_url and self.mediaflow_proxy_url to call the router-based URL
builder so the /stream endpoint is resolved reliably.
🧹 Nitpick comments (3)
🤖 Fix all nitpicks with AI agents
Verify each finding against the current code and only fix it if needed. In `@mediaflow_proxy/extractors/dlhd.py`: - Around line 496-558: The regexes in _extract_lovecdn_stream (m3u8_patterns and url_pattern) are too broad and may pick up unrelated m3u8 links; after you find a candidate stream_url (or each match in the loops for m3u8_patterns and the fallback url_pattern) parse it with urlparse and validate its domain against an allowlist of expected CDN hosts (e.g. lovecdn.ru, newkso.ru, known mirror domains) or check netloc.endswith(...) for trusted suffixes; if the domain is not allowed, skip that match and continue searching, only set stream_url when a match passes the domain validation and otherwise fall back to other heuristics or raise the ExtractorError. Ensure you reference stream_url, m3u8_patterns, url_pattern, iframe_url and iframe_content when applying the validation so the logic integrates into the existing search/construct/fallback flow. In `@mediaflow_proxy/utils/m3u8_processor.py`: - Around line 691-695: The DLHD key detection is duplicated between process_key_line and proxy_url; add a small helper function (e.g. _is_dlhd_key_url) that accepts the request/query_params and the uri (or its url string) and returns the boolean currently computed by "dlhd_salt" in query_params and "/key/" in uri.geturl(); replace the inline checks in process_key_line (where is_dlhd_key_request is computed) and in proxy_url (where the same check decides the /stream endpoint) to call this helper so the detection logic is centralized and consistent. - Around line 817-821: The current logic in m3u8_processor.py that computes proxy_url by doing self.mediaflow_proxy_url.replace("/hls/manifest.m3u8", "/stream") is brittle (it silently no-ops if the path changes); instead construct the /stream URL from the request router (e.g., use request.url_for("proxy_stream_endpoint") or the equivalent named route) when is_dlhd_key is true, falling back to a safe deterministic join of base URL and "/stream" only if url_for is unavailable; update the block that references is_dlhd_key, proxy_url and self.mediaflow_proxy_url to call the router-based URL builder so the /stream endpoint is resolved reliably.mediaflow_proxy/extractors/dlhd.py (1)
496-558:_extract_lovecdn_streamhas overly broad regex patterns that may match unrelated URLs.The fallback pattern on Line 533 (
https?://[^\s"'<>]+\.m3u8[^\s"'<>]*) will match any m3u8 URL in the entire HTML, including ads, tracking pixels, or unrelated streams. The earlier patterns (Lines 503-506) are similarly broad. This could lead to incorrect stream URL extraction.Consider adding domain validation after matching to ensure the URL belongs to the expected CDN.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mediaflow_proxy/extractors/dlhd.py` around lines 496 - 558, The regexes in _extract_lovecdn_stream (m3u8_patterns and url_pattern) are too broad and may pick up unrelated m3u8 links; after you find a candidate stream_url (or each match in the loops for m3u8_patterns and the fallback url_pattern) parse it with urlparse and validate its domain against an allowlist of expected CDN hosts (e.g. lovecdn.ru, newkso.ru, known mirror domains) or check netloc.endswith(...) for trusted suffixes; if the domain is not allowed, skip that match and continue searching, only set stream_url when a match passes the domain validation and otherwise fall back to other heuristics or raise the ExtractorError. Ensure you reference stream_url, m3u8_patterns, url_pattern, iframe_url and iframe_content when applying the validation so the logic integrates into the existing search/construct/fallback flow.mediaflow_proxy/utils/m3u8_processor.py (2)
691-695: DLHD key detection is duplicated betweenprocess_key_lineandproxy_url.Line 693 checks
"dlhd_salt" in query_params and "/key/" in uri.geturl()to decideis_playlist, thenproxy_url(Line 818) performs the exact same check again to pick the/streamendpoint. Consider extracting a small helper (e.g._is_dlhd_key_url) to keep the detection logic in one place.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mediaflow_proxy/utils/m3u8_processor.py` around lines 691 - 695, The DLHD key detection is duplicated between process_key_line and proxy_url; add a small helper function (e.g. _is_dlhd_key_url) that accepts the request/query_params and the uri (or its url string) and returns the boolean currently computed by "dlhd_salt" in query_params and "/key/" in uri.geturl(); replace the inline checks in process_key_line (where is_dlhd_key_request is computed) and in proxy_url (where the same check decides the /stream endpoint) to call this helper so the detection logic is centralized and consistent.
817-821: Fragile string replacement for building the/streamendpoint URL.
self.mediaflow_proxy_url.replace("/hls/manifest.m3u8", "/stream")silently returns the original string if the substring isn't found. This is brittle if the route path ever changes. Usingrequest.url_for("proxy_stream_endpoint")(or a similar named route) would be more robust.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mediaflow_proxy/utils/m3u8_processor.py` around lines 817 - 821, The current logic in m3u8_processor.py that computes proxy_url by doing self.mediaflow_proxy_url.replace("/hls/manifest.m3u8", "/stream") is brittle (it silently no-ops if the path changes); instead construct the /stream URL from the request router (e.g., use request.url_for("proxy_stream_endpoint") or the equivalent named route) when is_dlhd_key is true, falling back to a safe deterministic join of base URL and "/stream" only if url_for is unavailable; update the block that references is_dlhd_key, proxy_url and self.mediaflow_proxy_url to call the router-based URL builder so the /stream endpoint is resolved reliably.
| # Silenzia l'errore ConnectionResetError su Windows | ||
| logging.getLogger("asyncio").setLevel(logging.CRITICAL) |
There was a problem hiding this comment.
Global suppression of asyncio logging to CRITICAL is overly broad.
This silences all asyncio warnings and errors application-wide, not just the ConnectionResetError on Windows mentioned in the (Italian) comment. This can hide real issues in production. Consider a narrower filter or moving this to a platform-specific guard.
Also, the comment on Line 19 is in Italian — please use English for consistency.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@mediaflow_proxy/extractors/dlhd.py` around lines 19 - 20, The current global
call logging.getLogger("asyncio").setLevel(logging.CRITICAL) silences all
asyncio logs; change this to a narrower fix: replace the Italian comment with an
English one and either (A) wrap the suppression in a platform guard (e.g., only
run on Windows via sys.platform) and limit it to that scope, or (B) implement a
custom logging.Filter attached to logging.getLogger("asyncio") that only filters
messages related to ConnectionResetError (or the exact message text) instead of
raising the logger level globally; alternatively consider catching
ConnectionResetError at the call-site instead of muting asyncio logs. Ensure
references to logging.getLogger("asyncio").setLevel and ConnectionResetError in
dlhd.py are updated accordingly.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
mediaflow_proxy/extractors/dlhd.py (1)
443-447: 🛠️ Refactor suggestion | 🟠 MajorMove
import jsonto module level.The
jsonimport on Line 445 is inside atryblock within an async method. This is a stdlib module and should be imported at the top of the file for clarity and consistency.Proposed fix
At the top of the file (e.g., after line 4):
+import json import loggingThen remove line 445:
content = await response.read() response.raise_for_status() - import json - auth_data = json.loads(content.decode("utf-8"))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mediaflow_proxy/extractors/dlhd.py` around lines 443 - 447, The local "import json" inside the async block that reads and parses response content should be moved to the module level: add "import json" at the top of mediaflow_proxy/extractors/dlhd and remove the in-function import near the response.read()/json.loads call so the async method (the block that does response.read(), response.raise_for_status(), and auth_data = json.loads(...)) uses the top-level json import instead of importing inside the function.
🤖 Fix all issues with AI agents
Verify each finding against the current code and only fix it if needed.
In `@mediaflow_proxy/extractors/dlhd.py`:
- Line 1: The file fails Ruff formatting; run the formatter (e.g., `ruff
format`) on mediaflow_proxy/extractors/dlhd.py to fix import/order/whitespace
issues (the current contents show only `import hashlib`), save the changes, and
re-run CI so the file passes Ruff checks.
- Around line 596-603: The call to _extract_key_url(m3u8_url, iframe_url) is
performing an unnecessary network request whose result (key_url) is only logged
and never used; either remove that call or propagate the value by adding key_url
into the extractor's returned dict/object so it is consumed downstream. Update
the code in the block that builds m3u8_url (uses _build_m3u8_url) to either
delete the await self._extract_key_url(...) line and its log, or modify the
extractor's return structure (where session_data or the final result is
assembled) to include the key_url field and ensure any callers of this extractor
expect/handle that new key_url property. Ensure you only pick one approach and
remove the dead network call if you choose to drop key_url.
- Around line 636-667: The extract() function currently catches all exceptions
and re-raises a new ExtractorError, which discards the original traceback;
change the exception handling so that when wrapping non-ExtractorError
exceptions you use "raise ExtractorError(... ) from e" to preserve the exception
chain, and if the caught exception is already an ExtractorError simply re-raise
it (or don't catch it) so the original traceback is kept; update the except
block that surrounds calls to _extract_direct_stream, _extract_via_iframe and
the extract_channel_id logic to implement this behavior.
- Around line 496-558: The regexes in _extract_lovecdn_stream are too permissive
(patterns list including source[:\s]+['"]([^'"]+)['"] and the fallback
url_pattern) and can match unrelated JS keys or analytics .m3u8 links; tighten
them by restricting captures to likely HLS contexts (e.g., require hls, m3u8,
playlist, or quality hints in the same token), prefer patterns that include
surrounding context like hlsManifestUrl|file|src\s*[:=]\s*['"][^'"]+\.m3u8 and
by filtering fallback matches (url_pattern) to prefer same-origin hosts or known
CDN domains and to validate the URL (e.g., endswith .m3u8 or contains playlist
keywords) before assigning stream_url; update the loop that sets stream_url and
the fallback selection logic so stream_url is only set when the candidate passes
these stricter checks.
- Around line 560-634: _extract_direct_stream currently uses a single hardcoded
iframe_domains list containing "lefttoplay.xyz" and hardcodes "dlhd.link" as the
main_url passed to _extract_session_data; make iframe domain list and the
main_url configurable (e.g., via class attributes or constructor params) so more
fallback domains can be added without changing code, update references to
iframe_domains and the call to
_extract_session_data(self._extract_session_data(iframe_url, "dlhd.link")) to
use the configurable main domain value, and ensure any defaults preserve current
behavior but can be overridden by configuration or environment variables.
- Around line 80-131: In compute_key_headers(), replace direct hashlib/hmac
calls with the FIPS-safe wrappers: import tlshashlib and tlshmac from
mediaflow_proxy.utils, change hmac.new(..., hashlib.sha256) to tlshmac.new(...,
tlshashlib.sha256), change hashlib.md5(...) to tlshashlib.md5(...), and change
any direct hashlib.sha256() usage to tlshashlib.sha256() (affects the HMAC
computation and the MD5 proof-of-work section); leave calls to
compute_fingerprint() and compute_key_path(...) unchanged but ensure
compute_key_path uses the same tlshashlib/tlshmac types if it constructs hashes.
- Around line 443-447: The local "import json" inside the async block that reads
and parses response content should be moved to the module level: add "import
json" at the top of mediaflow_proxy/extractors/dlhd and remove the in-function
import near the response.read()/json.loads call so the async method (the block
that does response.read(), response.raise_for_status(), and auth_data =
json.loads(...)) uses the top-level json import instead of importing inside the
function.
🧹 Nitpick comments (4)
🤖 Fix all nitpicks with AI agents
Verify each finding against the current code and only fix it if needed. In `@mediaflow_proxy/extractors/dlhd.py`: - Around line 636-667: The extract() function currently catches all exceptions and re-raises a new ExtractorError, which discards the original traceback; change the exception handling so that when wrapping non-ExtractorError exceptions you use "raise ExtractorError(... ) from e" to preserve the exception chain, and if the caught exception is already an ExtractorError simply re-raise it (or don't catch it) so the original traceback is kept; update the except block that surrounds calls to _extract_direct_stream, _extract_via_iframe and the extract_channel_id logic to implement this behavior. - Around line 496-558: The regexes in _extract_lovecdn_stream are too permissive (patterns list including source[:\s]+['"]([^'"]+)['"] and the fallback url_pattern) and can match unrelated JS keys or analytics .m3u8 links; tighten them by restricting captures to likely HLS contexts (e.g., require hls, m3u8, playlist, or quality hints in the same token), prefer patterns that include surrounding context like hlsManifestUrl|file|src\s*[:=]\s*['"][^'"]+\.m3u8 and by filtering fallback matches (url_pattern) to prefer same-origin hosts or known CDN domains and to validate the URL (e.g., endswith .m3u8 or contains playlist keywords) before assigning stream_url; update the loop that sets stream_url and the fallback selection logic so stream_url is only set when the candidate passes these stricter checks. - Around line 560-634: _extract_direct_stream currently uses a single hardcoded iframe_domains list containing "lefttoplay.xyz" and hardcodes "dlhd.link" as the main_url passed to _extract_session_data; make iframe domain list and the main_url configurable (e.g., via class attributes or constructor params) so more fallback domains can be added without changing code, update references to iframe_domains and the call to _extract_session_data(self._extract_session_data(iframe_url, "dlhd.link")) to use the configurable main domain value, and ensure any defaults preserve current behavior but can be overridden by configuration or environment variables. - Around line 80-131: In compute_key_headers(), replace direct hashlib/hmac calls with the FIPS-safe wrappers: import tlshashlib and tlshmac from mediaflow_proxy.utils, change hmac.new(..., hashlib.sha256) to tlshmac.new(..., tlshashlib.sha256), change hashlib.md5(...) to tlshashlib.md5(...), and change any direct hashlib.sha256() usage to tlshashlib.sha256() (affects the HMAC computation and the MD5 proof-of-work section); leave calls to compute_fingerprint() and compute_key_path(...) unchanged but ensure compute_key_path uses the same tlshashlib/tlshmac types if it constructs hashes.mediaflow_proxy/extractors/dlhd.py (4)
636-667:extract()catchesExceptionand re-wraps, hiding the original traceback.Line 666–667 catches all exceptions (including the
ExtractorErroralready raised by the inner methods) and re-wraps them in a newExtractorError. This strips the original traceback/chain. Consider usingraise ExtractorError(...) from eor lettingExtractorErrorpropagate unwrapped.Proposed fix
except Exception as e: - raise ExtractorError(f"Extraction failed: {str(e)}") + raise ExtractorError(f"Extraction failed: {str(e)}") from e🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mediaflow_proxy/extractors/dlhd.py` around lines 636 - 667, The extract() function currently catches all exceptions and re-raises a new ExtractorError, which discards the original traceback; change the exception handling so that when wrapping non-ExtractorError exceptions you use "raise ExtractorError(... ) from e" to preserve the exception chain, and if the caught exception is already an ExtractorError simply re-raise it (or don't catch it) so the original traceback is kept; update the except block that surrounds calls to _extract_direct_stream, _extract_via_iframe and the extract_channel_id logic to implement this behavior.
496-558:_extract_lovecdn_streamregex patterns are very broad and may match unintended strings.Lines 502–506 use generic patterns like
source[:\s]+["\']([^"\']+)["\']which could match JavaScript object keys, comments, or other non-stream content. The fallback on Lines 532–536 (https?://[^\s"\'<>]+\.m3u8[^\s"\'<>]*) grabs the first URL containing.m3u8from the entire page — this is fragile and could pick up analytics or unrelated URLs.Given the fallback nature of this method, this may be acceptable, but be aware it can produce incorrect stream URLs on pages with multiple
.m3u8references.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mediaflow_proxy/extractors/dlhd.py` around lines 496 - 558, The regexes in _extract_lovecdn_stream are too permissive (patterns list including source[:\s]+['"]([^'"]+)['"] and the fallback url_pattern) and can match unrelated JS keys or analytics .m3u8 links; tighten them by restricting captures to likely HLS contexts (e.g., require hls, m3u8, playlist, or quality hints in the same token), prefer patterns that include surrounding context like hlsManifestUrl|file|src\s*[:=]\s*['"][^'"]+\.m3u8 and by filtering fallback matches (url_pattern) to prefer same-origin hosts or known CDN domains and to validate the URL (e.g., endswith .m3u8 or contains playlist keywords) before assigning stream_url; update the loop that sets stream_url and the fallback selection logic so stream_url is only set when the candidate passes these stricter checks.
560-634:_extract_direct_streamhas a single hardcoded iframe domain.
iframe_domains(Line 566–568) contains only"lefttoplay.xyz". The loop construct is fine for future expansion, but if this domain goes down, the entire direct extraction path fails with no alternatives. Consider whether additional domains should be listed or whether this should be configurable.Also, Line 575 hardcodes
"dlhd.link"as themain_urlfor the Referer header — if the upstream site changes domain, this will silently break.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mediaflow_proxy/extractors/dlhd.py` around lines 560 - 634, _extract_direct_stream currently uses a single hardcoded iframe_domains list containing "lefttoplay.xyz" and hardcodes "dlhd.link" as the main_url passed to _extract_session_data; make iframe domain list and the main_url configurable (e.g., via class attributes or constructor params) so more fallback domains can be added without changing code, update references to iframe_domains and the call to _extract_session_data(self._extract_session_data(iframe_url, "dlhd.link")) to use the configurable main domain value, and ensure any defaults preserve current behavior but can be overridden by configuration or environment variables.
80-131: Usetlshashlib.md5()andtlshmac.new()for FIPS-mode compatibility.The codebase provides FIPS-safe wrappers in
mediaflow_proxy.utils.tlshashlibandmediaflow_proxy.utils.tlshmac. Lines 51, 113, and 120 callhashlib.md5(),hmac.new()withhashlib.sha256, andhashlib.sha256()directly, which fail in FIPS mode. Replace:
- Line 120:
hashlib.md5()→tlshashlib.md5()- Line 113:
hmac.new(..., hashlib.sha256)→tlshmac.new(..., tlshashlib.sha256)- Line 51:
hashlib.sha256()→tlshashlib.sha256()(for consistency)Add imports at the top:
from mediaflow_proxy.utils import tlshashlib, tlshmac🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mediaflow_proxy/extractors/dlhd.py` around lines 80 - 131, In compute_key_headers(), replace direct hashlib/hmac calls with the FIPS-safe wrappers: import tlshashlib and tlshmac from mediaflow_proxy.utils, change hmac.new(..., hashlib.sha256) to tlshmac.new(..., tlshashlib.sha256), change hashlib.md5(...) to tlshashlib.md5(...), and change any direct hashlib.sha256() usage to tlshashlib.sha256() (affects the HMAC computation and the MD5 proof-of-work section); leave calls to compute_fingerprint() and compute_key_path(...) unchanged but ensure compute_key_path uses the same tlshashlib/tlshmac types if it constructs hashes.
|
Wait before merging, as I will resolve some of the CodeRabbit proposed fixes |
- Updated compute_key_headers return type from tuple[int, int, str] to tuple[int, int, str, str] to include the fingerprint as a 4th element. - Changed the synchronous call to use asyncio.to_thread to run the CPU-bound function in a thread executor, preventing event-loop blocking.
There was a problem hiding this comment.
🧹 Nitpick comments (3)
mediaflow_proxy/extractors/dlhd.py (2)
517-584:_extract_direct_stream— single iframe domain is fragile but acceptable.Line 523 hardcodes
iframe_domains = ["lefttoplay.xyz"]— the loop structure is ready for multiple domains but only one is provided. This is fine for now; the fallback to_extract_via_iframeprovides resilience.One substantive note: Line 536 logs
session_data['channel_key']atINFOlevel. Whilechannel_keyis not a secret per se, consider usingDEBUGlevel for values extracted from authentication flows to reduce exposure in production logs.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mediaflow_proxy/extractors/dlhd.py` around lines 517 - 584, The log in _extract_direct_stream currently emits session_data['channel_key'] at INFO (logger.info(f"Got session data from {iframe_domain}: channel_key={session_data['channel_key']}")), which exposes auth flow values; change this to a DEBUG-level log (logger.debug(...)) and keep the same message/content so the information is available in debug logs but not in production INFO logs; update the specific logger.info call that references session_data['channel_key'] to logger.debug in the _extract_direct_stream function.
453-515:_extract_lovecdn_stream— endpoint is hardcoded to"hls_key_proxy"regardless of stream type.Line 504 sets
endpoint = "hls_key_proxy"unconditionally, and the preceding comment on Line 503 says "Determine endpoint based on the stream domain" — but there's no actual conditional logic. If this is alwayshls_key_proxy, simplify by removing the variable and the misleading comment, or implement the domain-based logic if it was intended.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mediaflow_proxy/extractors/dlhd.py` around lines 453 - 515, The code in _extract_lovecdn_stream currently sets endpoint = "hls_key_proxy" unconditionally despite the "Determine endpoint based on the stream domain" comment; update this by parsing the stream_url host (use urlparse(stream_url).netloc) and select an endpoint based on that host (e.g., for known CDNs like "lovecdn.ru" or "newkso.ru" set endpoint = "hls_key_proxy", for other hosts set a fallback like "proxy" or "direct"); replace the hardcoded assignment to endpoint with this conditional mapping and keep logging of the chosen endpoint (refer to _extract_lovecdn_stream, stream_url, iframe_url, and endpoint).mediaflow_proxy/utils/m3u8_processor.py (1)
819-843: DLHD key URL routing and segment extension detection are clean.The branching correctly separates DLHD key URLs (routed to
/stream) from regular segments (routed to/hls/segment.{ext}). Theh_rangeremoval is correctly scoped to segment requests only.One minor note: the string replacement on Line 823 (
self.mediaflow_proxy_url.replace("/hls/manifest.m3u8", "/stream")) is brittle if the manifest route path ever changes. Consider deriving the/streamURL viarequest.url_for("proxy_stream_endpoint")for consistency, similar to howmediaflow_proxy_urlis built.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mediaflow_proxy/utils/m3u8_processor.py` around lines 819 - 843, The current DLHD branch builds the /stream target by doing string replace on self.mediaflow_proxy_url (self.mediaflow_proxy_url.replace("/hls/manifest.m3u8", "/stream")), which is brittle; change that branch to construct the stream URL using the same URL helper used elsewhere (e.g., call request.url_for("proxy_stream_endpoint") or the function used to build mediaflow_proxy_url) and assign that to proxy_url instead of doing a string replace; update the code in the is_dlhd_key branch where proxy_url is set so it uses request.url_for("proxy_stream_endpoint") (or the equivalent helper) to produce the /stream URL consistently with mediaflow_proxy_url construction.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@mediaflow_proxy/extractors/dlhd.py`:
- Around line 110-119: The proof-of-work loop in dlhd.py leaves nonce at 0 if no
valid nonce is found after 100000 attempts, which can silently produce incorrect
results; update the logic in the block containing the nonce loop (variables:
nonce, combined, md5_hash, prefix_value) to detect the no-match case after the
loop and handle it explicitly—either raise a descriptive exception (e.g.,
ValueError or a custom PoWFailure) or return a clear error result instead of
using nonce==0 silently; include a short log message with context (resource,
number, ts) before raising/returning so callers can diagnose the failure.
- Around line 19-20: The file currently silences all asyncio logs by calling
logging.getLogger("asyncio").setLevel(logging.CRITICAL) with an Italian comment;
remove this broad suppression and replace it with a targeted fix: delete or
avoid using logging.getLogger("asyncio").setLevel(logging.CRITICAL), replace the
Italian comment with an English one, and either (a) implement targeted exception
handling around the code that experiences ConnectionResetError (catch
ConnectionResetError where it occurs), or (b) add a logging.Filter that
suppresses only ConnectionResetError noise for the "asyncio" logger instead of
raising the whole logger level—refer to the exact call
logging.getLogger("asyncio").setLevel(logging.CRITICAL) to locate the change and
the ConnectionResetError handling site to add the scoped try/except.
In `@mediaflow_proxy/routes/proxy.py`:
- Around line 197-203: The code is placing sensitive values from dlhd_result
(dlhd_channel_salt, dlhd_auth_token, dlhd_iframe_url) into query_dict which
exposes them in URLs; instead, stop adding dlhd_channel_salt and dlhd_auth_token
to query parameters and instead attach them to secure headers on the outgoing
request (e.g., set headers like "X-DLHD-Salt" and "Authorization: Bearer
<token>" or similar) within the same function that builds the proxied request,
leaving dlhd_iframe_url only if non-sensitive and required; update the code
paths that consume query_dict to read these values from headers and ensure
logging redacts these header values when logged.
---
Nitpick comments:
In `@mediaflow_proxy/extractors/dlhd.py`:
- Around line 517-584: The log in _extract_direct_stream currently emits
session_data['channel_key'] at INFO (logger.info(f"Got session data from
{iframe_domain}: channel_key={session_data['channel_key']}")), which exposes
auth flow values; change this to a DEBUG-level log (logger.debug(...)) and keep
the same message/content so the information is available in debug logs but not
in production INFO logs; update the specific logger.info call that references
session_data['channel_key'] to logger.debug in the _extract_direct_stream
function.
- Around line 453-515: The code in _extract_lovecdn_stream currently sets
endpoint = "hls_key_proxy" unconditionally despite the "Determine endpoint based
on the stream domain" comment; update this by parsing the stream_url host (use
urlparse(stream_url).netloc) and select an endpoint based on that host (e.g.,
for known CDNs like "lovecdn.ru" or "newkso.ru" set endpoint = "hls_key_proxy",
for other hosts set a fallback like "proxy" or "direct"); replace the hardcoded
assignment to endpoint with this conditional mapping and keep logging of the
chosen endpoint (refer to _extract_lovecdn_stream, stream_url, iframe_url, and
endpoint).
In `@mediaflow_proxy/utils/m3u8_processor.py`:
- Around line 819-843: The current DLHD branch builds the /stream target by
doing string replace on self.mediaflow_proxy_url
(self.mediaflow_proxy_url.replace("/hls/manifest.m3u8", "/stream")), which is
brittle; change that branch to construct the stream URL using the same URL
helper used elsewhere (e.g., call request.url_for("proxy_stream_endpoint") or
the function used to build mediaflow_proxy_url) and assign that to proxy_url
instead of doing a string replace; update the code in the is_dlhd_key branch
where proxy_url is set so it uses request.url_for("proxy_stream_endpoint") (or
the equivalent helper) to produce the /stream URL consistently with
mediaflow_proxy_url construction.
|
Ready for review |
|
Nice |
Summary by CodeRabbit