Fix plugin scraper runtime + HubCloud/HubDrive playback resolution - #528
Conversation
Scraper plugins (e.g. 4KHDHub via HubCloud) sometimes return a final playback URL without any request headers, even though the same host required a Referer during the plugin's own link resolution. ExoPlayer then fails extractor sniffing (NoDeclaredBrand) because it receives an HTML interstitial/anti-bot page instead of the actual video. Add a fallback in StreamRepository.resolveStreamInternal that injects a default Referer/Origin for a small list of known-gated hosts (hubcloud, hubdrive) only when the addon/plugin didn't already supply a Referer, so this never overrides explicit values.
Verified against live HubCloud servers: the "10Gbps" link chain redirects through a Cloudflare Worker and lands on an HTML page (gamerxyt.com/dl.php?link=<real-video-url>) whose own URL already carries the real direct link as a query parameter — a browser follows it via client-side JS, but ExoPlayer/OkHttp just get the HTML and fail extractor sniffing. Extend redirect resolution to gated hosts (reusing the Referer fix's host list) and unwrap the "link"/"url" query parameter from the resolved URL when present, so playback uses the real direct link. Confirmed live: the extracted link serves content-type: video/mkv directly (no further gating needed on this specific CDN).
Three runtime-side fixes so CloudStream-style scraper plugins that return HubCloud/HubDrive links resolve to a playable file, without touching any plugin code: - PluginRuntime: add setTimeout/clearTimeout/setInterval/clearInterval polyfills. The QuickJS runtime lacked them, so any plugin that wraps fetch in `setTimeout(() => controller.abort(), ms)` threw ReferenceError, the fetch helper swallowed it, every request returned null, and the scraper produced zero results. - StreamRepository: add a HubCloud/HubDrive resolver. A drive/video page URL is followed through its `var url` chain to the links page and reduced to a direct media file (Cloudflare R2 / FSL / PixelServer). Login/nav pages resolve to null so the source is skipped instead of handing ExoPlayer an HTML page (NoDeclaredBrand). - PlayerViewModel: during failover, a null resolution now skips the candidate instead of probing the raw URL, which would look reachable (HTTP 200 HTML) and then fail extractor sniffing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Findings [P1] The 8-second timeout is not enforced. StreamRepository.kt (line 3230) uses blocking OkHttp calls inside withTimeout. Those calls cannot be cancelled until they return, and the chain performs up to three requests. A slow HubCloud host could stall playback for tens of seconds. Use cancellable OkHttp calls or a client with a real callTimeout. [P2] The timer polyfill never runs callbacks. PluginRuntime.kt (line 1244) makes setTimeout exist but never executes it. Plugins using timer-based delays or retries can wait until the 60-second plugin timeout. [P2] The immediate failover fix is not connected to active playback. The changed helper has no call sites, while actual selection still falls back to the raw HubCloud webpage at PlayerViewModel.kt (line 2291). [P2] Signed direct URLs are written to logs. StreamRepository.kt (line 3508) logs complete R2 URLs, potentially including temporary tokens. Remove or redact query parameters. Verdict |
…ut, redact logs - Gate unwrapEmbeddedLinkParam behind isEmbeddedLinkLandingHost so ?url=/?link= is only unwrapped on known anti-leech landing hosts (gamerxyt/hubcloud/ hubdrive/hubcdn/dl.php), never on arbitrary proxy/auth URLs. - Enforce a real per-call timeout in the HubCloud resolver via call.timeout() (4s each); the outer withTimeout cannot interrupt a blocking OkHttp execute(). - Redact query strings from all HubFix logs (redactUrlForLog) so signed R2/FSL tokens no longer reach logcat. - Revert the PlayerViewModel failover tweak: findFirstReachableStreamInAddon is dead code, so the change was never wired in. The resolver's connected path (resolveStreamInternal) is unchanged; existing tryAdvanceToNextStream handles auto-advance on unresolvable sources. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks for the thorough review — all five addressed in f968daa. [P1] Global URL unwrapping — Fixed. [P1] 8s timeout not enforced — Fixed. The resolver's blocking OkHttp calls now set a per-call [P2] Signed URLs in logs — Fixed. All HubFix logs go through [P2] Failover fix not connected — Correct; that was dead code ( [P2] Timer never runs callbacks — Intentional. The runtime is synchronous (okhttp blocks the JS thread, no event loop between calls), and the dominant real-world pattern is Will add targeted resolver/timer unit tests next. |
|
Thanks for the updates. I rechecked the latest revision against current main. The signed-URL logging and broad global unwrapping issues are substantially improved, and the dead failover change was removed correctly. Two merge blockers remain: [P1] The URL gate is still too broad. In [P1] The 8-second timeout is still not enforced for generic redirect resolution. The timer implementation remains an intentional compatibility shim rather than a real timer: callbacks never execute, so delay/retry-based plugins can still hang. I consider that a documented P2 limitation rather than the main blocker, but targeted resolver/timer tests would still be valuable. Verification: the PR merges cleanly into current main, |
…ect timeout - Match HubCloud/HubDrive/landing hosts by exact second-level label (registrableLabel) instead of host.contains(), so look-alikes like hubcloud.evil.com are rejected. /dl.php stays a separate path signal. Applied to both isEmbeddedLinkLandingHost and isHubCloudPageUrl. - Set a real per-call timeout on resolveRedirectedPlaybackUrl's blocking OkHttp call (call.timeout()), since the enclosing withTimeout cannot interrupt a blocking execute(). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Round 2 pushed in 2db14e1 — both remaining P1s fixed. [P1] URL gate too broad — Fixed. Host matching no longer uses [P1] Redirect timeout unenforced — Fixed. Both compile clean ( |
…lection Extract the pure URL-classification helpers (registrableLabel, isHubCloudPageUrl, isEmbeddedLinkLandingHost, pickHubCloudDirectLink, redactUrlForLog) to top-level internal functions so they can be tested without the repository or network, matching the existing usesSlowAggregatorTimeout pattern. HubCloudResolverTest covers: second-level-label matching, look-alike domain rejection (hubcloud.evil.com), direct-endpoint pass-through, proxy-URL non-unwrapping, R2>FSL>Pixel selection priority, and query-string redaction. All green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Added the requested tests in c888e3c. Extracted the pure URL helpers to top-level
That locks in both P1 gates and the log redaction. Ready for another look. |
Runtime-side fixes so CloudStream-style scraper plugins that return HubCloud/HubDrive links play, without touching any plugin code.
var urlchain to the links page and reduced to a direct media file (Cloudflare R2 / FSL / PixelServer). Login/nav pages resolve to null so the source is skipped instead of handing ExoPlayer an HTML page (NoDeclaredBrand).Verified on the Android TV emulator: 4KHDHub returns results and a real Cloudflare R2 .mkv reaches ExoPlayer. (HEVC 10-bit decode is a device-decoder limitation, out of scope.)