From 21df24abfdd2d518445820ae6139ddd2a67f5672 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 4 Feb 2025 11:22:50 +0100 Subject: [PATCH] Detect when WebView is broken and return null poToken Some old Android devices have a broken WebView implementation, that can't execute the poToken code. This is now detected and the getWebClientPoToken return null instead of throwing an error in such a case, to allow the extractor to try to extract the video data even without a poToken. --- .../newpipe/util/potoken/PoTokenException.kt | 10 ++++++++ .../util/potoken/PoTokenProviderImpl.kt | 18 ++++++++++++-- .../newpipe/util/potoken/PoTokenWebView.kt | 24 +++++++++++++++++-- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenException.kt b/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenException.kt index 896c53a68..879f8f3e6 100644 --- a/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenException.kt +++ b/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenException.kt @@ -1,3 +1,13 @@ package org.schabi.newpipe.util.potoken class PoTokenException(message: String) : Exception(message) + +// to be thrown if the WebView provided by the system is broken +class BadWebViewException(message: String) : Exception(message) + +fun buildExceptionForJsError(error: String): Exception { + return if (error.contains("SyntaxError")) + BadWebViewException(error) + else + PoTokenException(error) +} diff --git a/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenProviderImpl.kt b/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenProviderImpl.kt index 96c8b583e..ac3a9f402 100644 --- a/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenProviderImpl.kt +++ b/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenProviderImpl.kt @@ -15,6 +15,7 @@ import org.schabi.newpipe.util.DeviceUtils object PoTokenProviderImpl : PoTokenProvider { val TAG = PoTokenProviderImpl::class.simpleName private val webViewSupported by lazy { DeviceUtils.supportsWebView() } + private var webViewBadImpl = false // whether the system has a bad WebView implementation private object WebPoTokenGenLock private var webPoTokenVisitorData: String? = null @@ -22,11 +23,24 @@ object PoTokenProviderImpl : PoTokenProvider { private var webPoTokenGenerator: PoTokenGenerator? = null override fun getWebClientPoToken(videoId: String): PoTokenResult? { - if (!webViewSupported) { + if (!webViewSupported || webViewBadImpl) { return null } - return getWebClientPoToken(videoId = videoId, forceRecreate = false) + try { + return getWebClientPoToken(videoId = videoId, forceRecreate = false) + } catch (e: RuntimeException) { + // RxJava's Single wraps exceptions into RuntimeErrors, so we need to unwrap them here + when (val cause = e.cause) { + is BadWebViewException -> { + Log.e(TAG, "Could not obtain poToken because WebView is broken", e) + webViewBadImpl = true + return null + } + null -> throw e + else -> throw cause // includes PoTokenException + } + } } /** diff --git a/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenWebView.kt b/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenWebView.kt index e951bdd60..504ab0b0b 100644 --- a/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenWebView.kt +++ b/app/src/main/java/org/schabi/newpipe/util/potoken/PoTokenWebView.kt @@ -5,7 +5,9 @@ import android.os.Build import android.os.Handler import android.os.Looper import android.util.Log +import android.webkit.ConsoleMessage import android.webkit.JavascriptInterface +import android.webkit.WebChromeClient import android.webkit.WebView import androidx.annotation.MainThread import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers @@ -40,6 +42,24 @@ class PoTokenWebView private constructor( // so that we can run async functions and get back the result webView.addJavascriptInterface(this, JS_INTERFACE) + + webView.webChromeClient = object : WebChromeClient() { + override fun onConsoleMessage(m: ConsoleMessage): Boolean { + if (m.message().contains("Uncaught")) { + // There should not be any uncaught errors while executing the code, because + // everything that can fail is guarded by try-catch. Therefore, this likely + // indicates that there was a syntax error in the code, i.e. the WebView only + // supports a really old version of JS. + + val fmt = "\"${m.message()}\", source: ${m.sourceId()} (${m.lineNumber()})" + Log.e(TAG, "This WebView implementation is broken: $fmt") + + // This can only happen during initialization, where there is no try-catch + onInitializationErrorCloseAndCancel(BadWebViewException(fmt)) + } + return super.onConsoleMessage(m) + } + } } /** @@ -117,7 +137,7 @@ class PoTokenWebView private constructor( if (BuildConfig.DEBUG) { Log.e(TAG, "Initialization error from JavaScript: $error") } - onInitializationErrorCloseAndCancel(PoTokenException(error)) + onInitializationErrorCloseAndCancel(buildExceptionForJsError(error)) } /** @@ -223,7 +243,7 @@ class PoTokenWebView private constructor( if (BuildConfig.DEBUG) { Log.e(TAG, "obtainPoToken error from JavaScript: $error") } - popPoTokenEmitter(identifier)?.onError(PoTokenException(error)) + popPoTokenEmitter(identifier)?.onError(buildExceptionForJsError(error)) } /**