From f853d5f55165ee58287d3a3f6b40cb23bad38ff0 Mon Sep 17 00:00:00 2001 From: AbsurdlyLongUsername <22662897+absurdlylongusername@users.noreply.github.com> Date: Fri, 6 Mar 2026 11:56:41 +0000 Subject: [PATCH 1/5] Add Sign in confirm not a bot issue URL to the exception error message Make error panel and error activity exception message URLs clickable via extension method Change ErrorMessage.getString to getText and return CharSequence, and use getText with formatArgs to preserve styling (i.e. URLs) (cherry picked from commit f4c86caea95224dc2caf968e9b6ce5791a31c723) --- .../org/schabi/newpipe/error/ErrorActivity.kt | 3 +- .../org/schabi/newpipe/error/ErrorInfo.kt | 20 ++++++++----- .../schabi/newpipe/error/ErrorPanelHelper.kt | 5 ++-- .../MediaBrowserPlaybackPreparer.kt | 4 +-- .../newpipe/util/text/TextViewExtensions.kt | 29 +++++++++++++++++++ app/src/main/res/values/strings.xml | 2 +- 6 files changed, 50 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/util/text/TextViewExtensions.kt diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.kt b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.kt index 5dd0755c5..c68a2cfd1 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.kt @@ -25,6 +25,7 @@ import org.schabi.newpipe.databinding.ActivityErrorBinding import org.schabi.newpipe.util.Localization import org.schabi.newpipe.util.ThemeHelper import org.schabi.newpipe.util.external_communication.ShareUtils +import org.schabi.newpipe.util.text.setTextWithLinks /** * This activity is used to show error details and allow reporting them in various ways. @@ -100,7 +101,7 @@ class ErrorActivity : AppCompatActivity() { // normal bugreport buildInfo(errorInfo) - binding.errorMessageView.text = errorInfo.getMessage(this) + binding.errorMessageView.setTextWithLinks(errorInfo.getMessage(this)) binding.errorView.text = formErrorText(errorInfo.stackTraces) // print stack trace once again for debugging: diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt index cd48fb298..1b0d1d322 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt @@ -29,6 +29,7 @@ import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentExcepti import org.schabi.newpipe.ktx.isNetworkRelated import org.schabi.newpipe.player.mediasource.FailedMediaSource import org.schabi.newpipe.player.resolver.PlaybackResolver +import org.schabi.newpipe.util.text.getText /** * An error has occurred in the app. This class contains plain old parcelable data that can be used @@ -135,8 +136,8 @@ class ErrorInfo private constructor( return getServiceName(serviceId) } - fun getMessage(context: Context): String { - return message.getString(context) + fun getMessage(context: Context): CharSequence { + return message.getText(context) } companion object { @@ -146,20 +147,23 @@ class ErrorInfo private constructor( private val stringRes: Int, private vararg val formatArgs: String ) : Parcelable { - fun getString(context: Context): String { + fun getText(context: Context): CharSequence { + // Ensure locale aware context via ContextCompat.getContextForLanguage() (just in case context is not AppCompatActivity) + val ctx = ContextCompat.getContextForLanguage(context) return if (formatArgs.isEmpty()) { - // use ContextCompat.getString() just in case context is not AppCompatActivity - ContextCompat.getString(context, stringRes) + ctx.getText(stringRes) } else { // ContextCompat.getString() with formatArgs does not exist, so we just // replicate its source code but with formatArgs - ContextCompat.getContextForLanguage(context).getString(stringRes, *formatArgs) + ctx.resources.getText(stringRes, *formatArgs) } } } const val SERVICE_NONE = "" + const val SIGN_IN_CONFIRM_NOT_BOT_ISSUE_URL = "https://github.com/TeamNewPipe/NewPipe/issues/11139" + private fun getServiceName(serviceId: Int?) = // not using getNameOfServiceById since we want to accept a nullable serviceId and we // want to default to SERVICE_NONE ServiceList.all().firstOrNull { it.serviceId == serviceId }?.serviceInfo?.name @@ -247,7 +251,9 @@ class ErrorInfo private constructor( ErrorMessage(R.string.youtube_music_premium_content) throwable is SignInConfirmNotBotException -> - ErrorMessage(R.string.sign_in_confirm_not_bot_error, getServiceName(serviceId)) + ErrorMessage(R.string.sign_in_confirm_not_bot_error, + getServiceName(serviceId), + SIGN_IN_CONFIRM_NOT_BOT_ISSUE_URL) throwable is ContentNotAvailableException -> ErrorMessage(R.string.content_not_available) diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt b/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt index 023d13e9d..8136c78d8 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt @@ -16,6 +16,7 @@ import org.schabi.newpipe.MainActivity import org.schabi.newpipe.R import org.schabi.newpipe.ktx.animate import org.schabi.newpipe.util.external_communication.ShareUtils +import org.schabi.newpipe.util.text.setTextWithLinks class ErrorPanelHelper( private val fragment: Fragment, @@ -64,7 +65,7 @@ class ErrorPanelHelper( fun showError(errorInfo: ErrorInfo) { ensureDefaultVisibility() - errorTextView.text = errorInfo.getMessage(context) + errorTextView.setTextWithLinks(errorInfo.getMessage(context)) if (errorInfo.recaptchaUrl != null) { showAndSetErrorButtonAction(R.string.recaptcha_solve) { @@ -109,7 +110,7 @@ class ErrorPanelHelper( fun showTextError(errorString: String) { ensureDefaultVisibility() - errorTextView.text = errorString + errorTextView.setTextWithLinks(errorString) setRootVisible() } diff --git a/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt b/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt index 890c83cfa..c0a2f9668 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt +++ b/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt @@ -49,7 +49,7 @@ import org.schabi.newpipe.util.NavigationHelper */ class MediaBrowserPlaybackPreparer( private val context: Context, - private val setMediaSessionError: BiConsumer, // error string, error code + private val setMediaSessionError: BiConsumer, // error string, error code private val clearMediaSessionError: Runnable, private val onPrepare: Consumer ) : PlaybackPreparer { @@ -118,7 +118,7 @@ class MediaBrowserPlaybackPreparer( private fun onPrepareError(throwable: Throwable) { setMediaSessionError.accept( - ErrorInfo.getMessage(throwable, null, null).getString(context), + ErrorInfo.getMessage(throwable, null, null).getText(context), PlaybackStateCompat.ERROR_CODE_APP_ERROR ) } diff --git a/app/src/main/java/org/schabi/newpipe/util/text/TextViewExtensions.kt b/app/src/main/java/org/schabi/newpipe/util/text/TextViewExtensions.kt new file mode 100644 index 000000000..d2efbf541 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/text/TextViewExtensions.kt @@ -0,0 +1,29 @@ +package org.schabi.newpipe.util.text + +import android.content.res.Resources +import android.text.SpannableString +import android.text.method.LinkMovementMethod +import android.text.util.Linkify +import android.util.Patterns +import android.widget.TextView +import androidx.annotation.StringRes +import androidx.core.text.parseAsHtml +import androidx.core.text.toHtml +import androidx.core.text.toSpanned + +/** + * Takes in a CharSequence [text] + * and makes raw HTTP URLs and HTML anchor tags clickable + */ +fun TextView.setTextWithLinks(text: CharSequence) { + val spanned = SpannableString(text) + // Using the pattern overload of addLinks since the one with the int masks strips all spans from the text before applying new ones + Linkify.addLinks(spanned, Patterns.WEB_URL, null) + this.text = spanned + this.movementMethod = LinkMovementMethod.getInstance() +} + +/** + * Gets text from string resource with [id] while preserving styling and allowing string format value substitution of [formatArgs] + */ +fun Resources.getText(@StringRes id: Int, vararg formatArgs: Any?): CharSequence = getText(id).toSpanned().toHtml().format(*formatArgs).parseAsHtml() diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5ecdb5e97..e3e15b776 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -878,7 +878,7 @@ HTTP error 403 received from server while playing, likely caused by streaming URL expiration or an IP ban HTTP error %1$s received from server while playing HTTP error 403 received from server while playing, likely caused by an IP ban or streaming URL deobfuscation issues - %1$s refused to provide data, asking for a login to confirm the requester is not a bot.\n\nYour IP might have been temporarily banned by %1$s, you can wait some time or switch to a different IP (for example by turning on/off a VPN, or by switching from WiFi to mobile data). + %1$s refused to provide data, asking for a login to confirm the requester is not a bot.\n\nYour IP might have been temporarily banned by %1$s, you can wait some time or switch to a different IP (for example by turning on/off a VPN, or by switching from WiFi to mobile data).\n\nPlease see Issue 11139 for more information This content is not available for the currently selected content country.\n\nChange your selection from \"Settings > Content > Default content country\". In August 2025, Google announced that as of September 2026, installing apps will require developer verification for all Android apps on certified devices, including those installed outside of the Play Store. Since the developers of NewPipe do not agree to this requirement, NewPipe will no longer work on certified Android devices after that time. Details From 5eea1f36ad477bba3a2cadc449ec23ed0bd2810f Mon Sep 17 00:00:00 2001 From: AbsurdlyLongUsername <22662897+absurdlylongusername@users.noreply.github.com> Date: Fri, 6 Mar 2026 12:55:09 +0000 Subject: [PATCH 2/5] Maybe this fix ktlint errors? (cherry picked from commit 18fdf936c214189162af7b069ef70e8bf09529d4) --- app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt index 1b0d1d322..367931435 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt @@ -251,9 +251,11 @@ class ErrorInfo private constructor( ErrorMessage(R.string.youtube_music_premium_content) throwable is SignInConfirmNotBotException -> - ErrorMessage(R.string.sign_in_confirm_not_bot_error, + ErrorMessage( + R.string.sign_in_confirm_not_bot_error, getServiceName(serviceId), - SIGN_IN_CONFIRM_NOT_BOT_ISSUE_URL) + SIGN_IN_CONFIRM_NOT_BOT_ISSUE_URL + ) throwable is ContentNotAvailableException -> ErrorMessage(R.string.content_not_available) From e88a9dfda8e4936a211259011c4ae0ddb5e2ab9b Mon Sep 17 00:00:00 2001 From: AbsurdlyLongUsername <22662897+absurdlylongusername@users.noreply.github.com> Date: Sat, 7 Mar 2026 19:13:02 +0000 Subject: [PATCH 3/5] Change URL to FAQ (cherry picked from commit e45e1ac42dc31255f1ebe3342dc5b0b57c435f3b) --- app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt | 4 ++-- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt index 367931435..82f7d84bf 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt @@ -162,7 +162,7 @@ class ErrorInfo private constructor( const val SERVICE_NONE = "" - const val SIGN_IN_CONFIRM_NOT_BOT_ISSUE_URL = "https://github.com/TeamNewPipe/NewPipe/issues/11139" + const val YOUTUBE_IP_BAN_FAQ_URL = "https://newpipe.net/FAQ/#ip-banned-youtube" private fun getServiceName(serviceId: Int?) = // not using getNameOfServiceById since we want to accept a nullable serviceId and we // want to default to SERVICE_NONE @@ -254,7 +254,7 @@ class ErrorInfo private constructor( ErrorMessage( R.string.sign_in_confirm_not_bot_error, getServiceName(serviceId), - SIGN_IN_CONFIRM_NOT_BOT_ISSUE_URL + YOUTUBE_IP_BAN_FAQ_URL ) throwable is ContentNotAvailableException -> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e3e15b776..e5e568796 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -878,7 +878,7 @@ HTTP error 403 received from server while playing, likely caused by streaming URL expiration or an IP ban HTTP error %1$s received from server while playing HTTP error 403 received from server while playing, likely caused by an IP ban or streaming URL deobfuscation issues - %1$s refused to provide data, asking for a login to confirm the requester is not a bot.\n\nYour IP might have been temporarily banned by %1$s, you can wait some time or switch to a different IP (for example by turning on/off a VPN, or by switching from WiFi to mobile data).\n\nPlease see Issue 11139 for more information + %1$s refused to provide data, asking for a login to confirm the requester is not a bot.\n\nYour IP might have been temporarily banned by %1$s, you can wait some time or switch to a different IP (for example by turning on/off a VPN, or by switching from WiFi to mobile data).\n\nPlease see the FAQ for more information This content is not available for the currently selected content country.\n\nChange your selection from \"Settings > Content > Default content country\". In August 2025, Google announced that as of September 2026, installing apps will require developer verification for all Android apps on certified devices, including those installed outside of the Play Store. Since the developers of NewPipe do not agree to this requirement, NewPipe will no longer work on certified Android devices after that time. Details From 74bd0634bbfd50906f7724b904370200146a527c Mon Sep 17 00:00:00 2001 From: AbsurdlyLongUsername <22662897+absurdlylongusername@users.noreply.github.com> Date: Sat, 7 Mar 2026 23:38:51 +0000 Subject: [PATCH 4/5] Change to FAQ entry instead (cherry picked from commit 07091a2b99dbc9864162c7a14f2fe3efb0f6944d) --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e5e568796..b7ad216b3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -878,7 +878,7 @@ HTTP error 403 received from server while playing, likely caused by streaming URL expiration or an IP ban HTTP error %1$s received from server while playing HTTP error 403 received from server while playing, likely caused by an IP ban or streaming URL deobfuscation issues - %1$s refused to provide data, asking for a login to confirm the requester is not a bot.\n\nYour IP might have been temporarily banned by %1$s, you can wait some time or switch to a different IP (for example by turning on/off a VPN, or by switching from WiFi to mobile data).\n\nPlease see the FAQ for more information + %1$s refused to provide data, asking for a login to confirm the requester is not a bot.\n\nYour IP might have been temporarily banned by %1$s, you can wait some time or switch to a different IP (for example by turning on/off a VPN, or by switching from WiFi to mobile data).\n\nPlease see this FAQ entry for more information This content is not available for the currently selected content country.\n\nChange your selection from \"Settings > Content > Default content country\". In August 2025, Google announced that as of September 2026, installing apps will require developer verification for all Android apps on certified devices, including those installed outside of the Play Store. Since the developers of NewPipe do not agree to this requirement, NewPipe will no longer work on certified Android devices after that time. Details From 8101fd77cc28438ad00755bd0ac13fb0b4b02b0d Mon Sep 17 00:00:00 2001 From: tobigr Date: Sun, 8 Mar 2026 18:54:56 +0100 Subject: [PATCH 5/5] Complete sentence. (cherry picked from commit 40585641fa9c1a59a4b26df3283eb02888197caa) --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b7ad216b3..99dd1a74c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -878,7 +878,7 @@ HTTP error 403 received from server while playing, likely caused by streaming URL expiration or an IP ban HTTP error %1$s received from server while playing HTTP error 403 received from server while playing, likely caused by an IP ban or streaming URL deobfuscation issues - %1$s refused to provide data, asking for a login to confirm the requester is not a bot.\n\nYour IP might have been temporarily banned by %1$s, you can wait some time or switch to a different IP (for example by turning on/off a VPN, or by switching from WiFi to mobile data).\n\nPlease see this FAQ entry for more information + %1$s refused to provide data, asking for a login to confirm the requester is not a bot.\n\nYour IP might have been temporarily banned by %1$s, you can wait some time or switch to a different IP (for example by turning on/off a VPN, or by switching from WiFi to mobile data).\n\nPlease see this FAQ entry for more information. This content is not available for the currently selected content country.\n\nChange your selection from \"Settings > Content > Default content country\". In August 2025, Google announced that as of September 2026, installing apps will require developer verification for all Android apps on certified devices, including those installed outside of the Play Store. Since the developers of NewPipe do not agree to this requirement, NewPipe will no longer work on certified Android devices after that time. Details