mirror of
https://github.com/TeamNewPipe/NewPipe.git
synced 2025-10-03 17:59:41 +02:00
Merge branch 'dev' into refactor
This commit is contained in:
commit
2ee7cc4344
14 changed files with 177 additions and 133 deletions
|
@ -58,10 +58,7 @@ import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.StreamingService.LinkType;
|
import org.schabi.newpipe.extractor.StreamingService.LinkType;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
|
||||||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
|
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
|
||||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
|
@ -253,7 +250,8 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
showUnsupportedUrlDialog(url);
|
showUnsupportedUrlDialog(url);
|
||||||
}
|
}
|
||||||
}, throwable -> handleError(this, new ErrorInfo(throwable,
|
}, throwable -> handleError(this, new ErrorInfo(throwable,
|
||||||
UserAction.SHARE_TO_NEWPIPE, "Getting service from url: " + url))));
|
UserAction.SHARE_TO_NEWPIPE, "Getting service from url: " + url,
|
||||||
|
null, url))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -262,23 +260,19 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
* @param errorInfo the error information
|
* @param errorInfo the error information
|
||||||
*/
|
*/
|
||||||
private static void handleError(final Context context, final ErrorInfo errorInfo) {
|
private static void handleError(final Context context, final ErrorInfo errorInfo) {
|
||||||
if (errorInfo.getThrowable() != null) {
|
if (errorInfo.getRecaptchaUrl() != null) {
|
||||||
errorInfo.getThrowable().printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorInfo.getThrowable() instanceof ReCaptchaException) {
|
|
||||||
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
||||||
// Starting ReCaptcha Challenge Activity
|
// Starting ReCaptcha Challenge Activity
|
||||||
final Intent intent = new Intent(context, ReCaptchaActivity.class);
|
final Intent intent = new Intent(context, ReCaptchaActivity.class);
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
intent.putExtra(ReCaptchaActivity.RECAPTCHA_URL_EXTRA, errorInfo.getRecaptchaUrl());
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
} else if (errorInfo.getThrowable() instanceof ContentNotAvailableException
|
} else if (errorInfo.isReportable()) {
|
||||||
|| errorInfo.getThrowable() instanceof ContentNotSupportedException) {
|
ErrorUtil.createNotification(context, errorInfo);
|
||||||
|
} else {
|
||||||
// this exception does not usually indicate a problem that should be reported,
|
// this exception does not usually indicate a problem that should be reported,
|
||||||
// so just show a toast instead of the notification
|
// so just show a toast instead of the notification
|
||||||
Toast.makeText(context, errorInfo.getMessage(context), Toast.LENGTH_LONG).show();
|
Toast.makeText(context, errorInfo.getMessage(context), Toast.LENGTH_LONG).show();
|
||||||
} else {
|
|
||||||
ErrorUtil.createNotification(context, errorInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context instanceof RouterActivity) {
|
if (context instanceof RouterActivity) {
|
||||||
|
@ -641,7 +635,8 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
finish();
|
finish();
|
||||||
}, throwable -> handleError(this, new ErrorInfo(throwable,
|
}, throwable -> handleError(this, new ErrorInfo(throwable,
|
||||||
UserAction.SHARE_TO_NEWPIPE, "Starting info activity: " + currentUrl)))
|
UserAction.SHARE_TO_NEWPIPE, "Starting info activity: " + currentUrl,
|
||||||
|
null, currentUrl)))
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -828,10 +823,10 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
})
|
})
|
||||||
)),
|
)),
|
||||||
throwable -> runOnVisible(ctx -> handleError(ctx, new ErrorInfo(
|
throwable -> runOnVisible(ctx -> handleError(ctx, new ErrorInfo(
|
||||||
throwable,
|
throwable, UserAction.REQUESTED_STREAM,
|
||||||
UserAction.REQUESTED_STREAM,
|
|
||||||
"Tried to add " + currentUrl + " to a playlist",
|
"Tried to add " + currentUrl + " to a playlist",
|
||||||
((RouterActivity) ctx).currentService.getServiceId())
|
((RouterActivity) ctx).currentService.getServiceId(),
|
||||||
|
currentUrl)
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -971,7 +966,7 @@ public class RouterActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
}, throwable -> handleError(this, new ErrorInfo(throwable, finalUserAction,
|
}, throwable -> handleError(this, new ErrorInfo(throwable, finalUserAction,
|
||||||
choice.url + " opened with " + choice.playerChoice,
|
choice.url + " opened with " + choice.playerChoice,
|
||||||
choice.serviceId)));
|
choice.serviceId, choice.url)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -389,8 +389,7 @@ public class DownloadDialog extends DialogFragment
|
||||||
}
|
}
|
||||||
}, throwable -> ErrorUtil.showSnackbar(context,
|
}, throwable -> ErrorUtil.showSnackbar(context,
|
||||||
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
|
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
|
||||||
"Downloading video stream size",
|
"Downloading video stream size", currentInfo))));
|
||||||
currentInfo.getServiceId()))));
|
|
||||||
disposables.add(StreamInfoWrapper.fetchMoreInfoForWrapper(getWrappedAudioStreams())
|
disposables.add(StreamInfoWrapper.fetchMoreInfoForWrapper(getWrappedAudioStreams())
|
||||||
.subscribe(result -> {
|
.subscribe(result -> {
|
||||||
if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()
|
if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()
|
||||||
|
@ -399,8 +398,7 @@ public class DownloadDialog extends DialogFragment
|
||||||
}
|
}
|
||||||
}, throwable -> ErrorUtil.showSnackbar(context,
|
}, throwable -> ErrorUtil.showSnackbar(context,
|
||||||
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
|
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
|
||||||
"Downloading audio stream size",
|
"Downloading audio stream size", currentInfo))));
|
||||||
currentInfo.getServiceId()))));
|
|
||||||
disposables.add(StreamInfoWrapper.fetchMoreInfoForWrapper(wrappedSubtitleStreams)
|
disposables.add(StreamInfoWrapper.fetchMoreInfoForWrapper(wrappedSubtitleStreams)
|
||||||
.subscribe(result -> {
|
.subscribe(result -> {
|
||||||
if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()
|
if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()
|
||||||
|
@ -409,8 +407,7 @@ public class DownloadDialog extends DialogFragment
|
||||||
}
|
}
|
||||||
}, throwable -> ErrorUtil.showSnackbar(context,
|
}, throwable -> ErrorUtil.showSnackbar(context,
|
||||||
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
|
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
|
||||||
"Downloading subtitle stream size",
|
"Downloading subtitle stream size", currentInfo))));
|
||||||
currentInfo.getServiceId()))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupAudioTrackSpinner() {
|
private void setupAudioTrackSpinner() {
|
||||||
|
|
|
@ -36,8 +36,8 @@ public class AcraReportSender implements ReportSender {
|
||||||
ErrorUtil.openActivity(context, new ErrorInfo(
|
ErrorUtil.openActivity(context, new ErrorInfo(
|
||||||
new String[]{report.getString(ReportField.STACK_TRACE)},
|
new String[]{report.getString(ReportField.STACK_TRACE)},
|
||||||
UserAction.UI_ERROR,
|
UserAction.UI_ERROR,
|
||||||
null,
|
|
||||||
"ACRA report",
|
"ACRA report",
|
||||||
|
null,
|
||||||
R.string.app_ui_crash));
|
R.string.app_ui_crash));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import androidx.core.content.ContextCompat
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException
|
import com.google.android.exoplayer2.ExoPlaybackException
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource
|
import com.google.android.exoplayer2.upstream.HttpDataSource
|
||||||
import com.google.android.exoplayer2.upstream.Loader
|
import com.google.android.exoplayer2.upstream.Loader
|
||||||
import kotlinx.parcelize.IgnoredOnParcel
|
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.schabi.newpipe.R
|
import org.schabi.newpipe.R
|
||||||
import org.schabi.newpipe.extractor.Info
|
import org.schabi.newpipe.extractor.Info
|
||||||
|
@ -29,70 +28,108 @@ import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentExcepti
|
||||||
import org.schabi.newpipe.ktx.isNetworkRelated
|
import org.schabi.newpipe.ktx.isNetworkRelated
|
||||||
import org.schabi.newpipe.player.mediasource.FailedMediaSource
|
import org.schabi.newpipe.player.mediasource.FailedMediaSource
|
||||||
import org.schabi.newpipe.player.resolver.PlaybackResolver
|
import org.schabi.newpipe.player.resolver.PlaybackResolver
|
||||||
|
import java.net.UnknownHostException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An error has occurred in the app. This class contains plain old parcelable data that can be used
|
||||||
|
* to report the error and to show it to the user along with correct action buttons.
|
||||||
|
*/
|
||||||
@Parcelize
|
@Parcelize
|
||||||
class ErrorInfo private constructor(
|
class ErrorInfo private constructor(
|
||||||
val stackTraces: Array<String>,
|
val stackTraces: Array<String>,
|
||||||
val userAction: UserAction,
|
val userAction: UserAction,
|
||||||
val serviceId: Int?,
|
|
||||||
val request: String,
|
val request: String,
|
||||||
|
val serviceId: Int?,
|
||||||
private val message: ErrorMessage,
|
private val message: ErrorMessage,
|
||||||
|
/**
|
||||||
|
* If `true`, a report button will be shown for this error. Otherwise the error is not something
|
||||||
|
* that can really be reported (e.g. a network issue, or content not being available at all).
|
||||||
|
*/
|
||||||
|
val isReportable: Boolean,
|
||||||
|
/**
|
||||||
|
* If `true`, the process causing this error can be retried, otherwise not.
|
||||||
|
*/
|
||||||
|
val isRetryable: Boolean,
|
||||||
|
/**
|
||||||
|
* If present, indicates that the exception was a ReCaptchaException, and this is the URL
|
||||||
|
* provided by the service that can be used to solve the ReCaptcha challenge.
|
||||||
|
*/
|
||||||
|
val recaptchaUrl: String?,
|
||||||
|
/**
|
||||||
|
* If present, this resource can alternatively be opened in browser (useful if NewPipe is
|
||||||
|
* badly broken).
|
||||||
|
*/
|
||||||
|
val openInBrowserUrl: String?,
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
// no need to store throwable, all data for report is in other variables
|
@JvmOverloads
|
||||||
// also, the throwable might not be serializable, see TeamNewPipe/NewPipe#7302
|
constructor(
|
||||||
@IgnoredOnParcel
|
|
||||||
var throwable: Throwable? = null
|
|
||||||
|
|
||||||
private constructor(
|
|
||||||
throwable: Throwable,
|
throwable: Throwable,
|
||||||
userAction: UserAction,
|
userAction: UserAction,
|
||||||
serviceId: Int?,
|
request: String,
|
||||||
request: String
|
serviceId: Int? = null,
|
||||||
|
openInBrowserUrl: String? = null,
|
||||||
) : this(
|
) : this(
|
||||||
throwableToStringList(throwable),
|
throwableToStringList(throwable),
|
||||||
userAction,
|
userAction,
|
||||||
serviceId,
|
|
||||||
request,
|
request,
|
||||||
getMessage(throwable, userAction, serviceId)
|
serviceId,
|
||||||
) {
|
getMessage(throwable, userAction, serviceId),
|
||||||
this.throwable = throwable
|
isReportable(throwable),
|
||||||
}
|
isRetryable(throwable),
|
||||||
|
(throwable as? ReCaptchaException)?.url,
|
||||||
|
openInBrowserUrl,
|
||||||
|
)
|
||||||
|
|
||||||
private constructor(
|
@JvmOverloads
|
||||||
throwable: List<Throwable>,
|
constructor(
|
||||||
|
throwables: List<Throwable>,
|
||||||
userAction: UserAction,
|
userAction: UserAction,
|
||||||
serviceId: Int?,
|
request: String,
|
||||||
request: String
|
serviceId: Int? = null,
|
||||||
|
openInBrowserUrl: String? = null,
|
||||||
) : this(
|
) : this(
|
||||||
throwableListToStringList(throwable),
|
throwableListToStringList(throwables),
|
||||||
userAction,
|
userAction,
|
||||||
serviceId,
|
|
||||||
request,
|
request,
|
||||||
getMessage(throwable.firstOrNull(), userAction, serviceId)
|
serviceId,
|
||||||
) {
|
getMessage(throwables.firstOrNull(), userAction, serviceId),
|
||||||
this.throwable = throwable.firstOrNull()
|
throwables.any(::isReportable),
|
||||||
}
|
throwables.isEmpty() || throwables.any(::isRetryable),
|
||||||
|
throwables.firstNotNullOfOrNull { it as? ReCaptchaException }?.url,
|
||||||
|
openInBrowserUrl,
|
||||||
|
)
|
||||||
|
|
||||||
// constructor to manually build ErrorInfo
|
// constructor to manually build ErrorInfo when no throwable is available
|
||||||
constructor(stackTraces: Array<String>, userAction: UserAction, serviceId: Int?, request: String, @StringRes message: Int) :
|
constructor(
|
||||||
this(stackTraces, userAction, serviceId, request, ErrorMessage(message))
|
stackTraces: Array<String>,
|
||||||
|
userAction: UserAction,
|
||||||
|
request: String,
|
||||||
|
serviceId: Int?,
|
||||||
|
@StringRes message: Int
|
||||||
|
) :
|
||||||
|
this(
|
||||||
|
stackTraces, userAction, request, serviceId, ErrorMessage(message),
|
||||||
|
true, false, null, null
|
||||||
|
)
|
||||||
|
|
||||||
// constructors with single throwable
|
// constructor with only one throwable to extract service id and openInBrowserUrl from an Info
|
||||||
constructor(throwable: Throwable, userAction: UserAction, request: String) :
|
constructor(
|
||||||
this(throwable, userAction, null, request)
|
throwable: Throwable,
|
||||||
constructor(throwable: Throwable, userAction: UserAction, request: String, serviceId: Int) :
|
userAction: UserAction,
|
||||||
this(throwable, userAction, serviceId, request)
|
request: String,
|
||||||
constructor(throwable: Throwable, userAction: UserAction, request: String, info: Info?) :
|
info: Info?,
|
||||||
this(throwable, userAction, info?.serviceId, request)
|
) :
|
||||||
|
this(throwable, userAction, request, info?.serviceId, info?.url)
|
||||||
|
|
||||||
// constructors with list of throwables
|
// constructor with multiple throwables to extract service id and openInBrowserUrl from an Info
|
||||||
constructor(throwable: List<Throwable>, userAction: UserAction, request: String) :
|
constructor(
|
||||||
this(throwable, userAction, null, request)
|
throwables: List<Throwable>,
|
||||||
constructor(throwable: List<Throwable>, userAction: UserAction, request: String, serviceId: Int) :
|
userAction: UserAction,
|
||||||
this(throwable, userAction, serviceId, request)
|
request: String,
|
||||||
constructor(throwable: List<Throwable>, userAction: UserAction, request: String, info: Info?) :
|
info: Info?,
|
||||||
this(throwable, userAction, info?.serviceId, request)
|
) :
|
||||||
|
this(throwables, userAction, request, info?.serviceId, info?.url)
|
||||||
|
|
||||||
fun getServiceName(): String {
|
fun getServiceName(): String {
|
||||||
return getServiceName(serviceId)
|
return getServiceName(serviceId)
|
||||||
|
@ -205,8 +242,7 @@ class ErrorInfo private constructor(
|
||||||
// other extractor exceptions
|
// other extractor exceptions
|
||||||
throwable is ContentNotSupportedException ->
|
throwable is ContentNotSupportedException ->
|
||||||
ErrorMessage(R.string.content_not_supported)
|
ErrorMessage(R.string.content_not_supported)
|
||||||
// ReCaptchas should have already been handled elsewhere,
|
// ReCaptchas will be handled in a special way anyway
|
||||||
// but return an error message here just in case
|
|
||||||
throwable is ReCaptchaException ->
|
throwable is ReCaptchaException ->
|
||||||
ErrorMessage(R.string.recaptcha_request_toast)
|
ErrorMessage(R.string.recaptcha_request_toast)
|
||||||
// test this at the end as many exceptions could be a subclass of IOException
|
// test this at the end as many exceptions could be a subclass of IOException
|
||||||
|
@ -234,5 +270,36 @@ class ErrorInfo private constructor(
|
||||||
ErrorMessage(R.string.error_snackbar_message)
|
ErrorMessage(R.string.error_snackbar_message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isReportable(throwable: Throwable?): Boolean {
|
||||||
|
return when (throwable) {
|
||||||
|
// we don't have an exception, so this is a manually built error, which likely
|
||||||
|
// indicates that it's important and is thus reportable
|
||||||
|
null -> true
|
||||||
|
// the service explicitly said that content is not available (e.g. age restrictions,
|
||||||
|
// video deleted, etc.), there is no use in letting users report it
|
||||||
|
is ContentNotAvailableException -> false
|
||||||
|
// we know the content is not supported, no need to let the user report it
|
||||||
|
is ContentNotSupportedException -> false
|
||||||
|
// happens often when there is no internet connection; we don't use
|
||||||
|
// `throwable.isNetworkRelated` since any `IOException` would make that function
|
||||||
|
// return true, but not all `IOException`s are network related
|
||||||
|
is UnknownHostException -> false
|
||||||
|
// by default, this is an unexpected exception, which the user could report
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isRetryable(throwable: Throwable?): Boolean {
|
||||||
|
return when (throwable) {
|
||||||
|
// we know the content is not available, retrying won't help
|
||||||
|
is ContentNotAvailableException -> false
|
||||||
|
// we know the content is not supported, retrying won't help
|
||||||
|
is ContentNotSupportedException -> false
|
||||||
|
// by default (including if throwable is null), enable retrying (though the retry
|
||||||
|
// button will be shown only if a way to perform the retry is implemented)
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package org.schabi.newpipe.error
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
@ -14,11 +13,7 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.rxjava3.disposables.Disposable
|
import io.reactivex.rxjava3.disposables.Disposable
|
||||||
import org.schabi.newpipe.MainActivity
|
import org.schabi.newpipe.MainActivity
|
||||||
import org.schabi.newpipe.R
|
import org.schabi.newpipe.R
|
||||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException
|
|
||||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException
|
|
||||||
import org.schabi.newpipe.ktx.animate
|
import org.schabi.newpipe.ktx.animate
|
||||||
import org.schabi.newpipe.ktx.isInterruptedCaused
|
|
||||||
import org.schabi.newpipe.util.external_communication.ShareUtils
|
import org.schabi.newpipe.util.external_communication.ShareUtils
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
@ -68,50 +63,32 @@ class ErrorPanelHelper(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showError(errorInfo: ErrorInfo) {
|
fun showError(errorInfo: ErrorInfo) {
|
||||||
|
|
||||||
if (errorInfo.throwable != null && errorInfo.throwable!!.isInterruptedCaused) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.w(TAG, "onError() isInterruptedCaused! = [$errorInfo.throwable]")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureDefaultVisibility()
|
ensureDefaultVisibility()
|
||||||
|
errorTextView.text = errorInfo.getMessage(context)
|
||||||
|
|
||||||
if (errorInfo.throwable is ReCaptchaException) {
|
if (errorInfo.recaptchaUrl != null) {
|
||||||
errorTextView.setText(R.string.recaptcha_request_toast)
|
showAndSetErrorButtonAction(R.string.recaptcha_solve) {
|
||||||
|
|
||||||
showAndSetErrorButtonAction(
|
|
||||||
R.string.recaptcha_solve
|
|
||||||
) {
|
|
||||||
// Starting ReCaptcha Challenge Activity
|
// Starting ReCaptcha Challenge Activity
|
||||||
val intent = Intent(context, ReCaptchaActivity::class.java)
|
val intent = Intent(context, ReCaptchaActivity::class.java)
|
||||||
intent.putExtra(
|
intent.putExtra(ReCaptchaActivity.RECAPTCHA_URL_EXTRA, errorInfo.recaptchaUrl)
|
||||||
ReCaptchaActivity.RECAPTCHA_URL_EXTRA,
|
|
||||||
(errorInfo.throwable as ReCaptchaException).url
|
|
||||||
)
|
|
||||||
fragment.startActivityForResult(intent, ReCaptchaActivity.RECAPTCHA_REQUEST)
|
fragment.startActivityForResult(intent, ReCaptchaActivity.RECAPTCHA_REQUEST)
|
||||||
errorActionButton.setOnClickListener(null)
|
errorActionButton.setOnClickListener(null)
|
||||||
}
|
}
|
||||||
|
} else if (errorInfo.isReportable) {
|
||||||
errorRetryButton.isVisible = retryShouldBeShown
|
showAndSetErrorButtonAction(R.string.error_snackbar_action) {
|
||||||
showAndSetOpenInBrowserButtonAction(errorInfo)
|
|
||||||
} else {
|
|
||||||
showAndSetErrorButtonAction(
|
|
||||||
R.string.error_snackbar_action
|
|
||||||
) {
|
|
||||||
ErrorUtil.openActivity(context, errorInfo)
|
ErrorUtil.openActivity(context, errorInfo)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
errorTextView.text = errorInfo.getMessage(context)
|
if (errorInfo.isRetryable) {
|
||||||
|
|
||||||
if (errorInfo.throwable !is ContentNotAvailableException &&
|
|
||||||
errorInfo.throwable !is ContentNotSupportedException
|
|
||||||
) {
|
|
||||||
// show retry button only for content which is not unavailable or unsupported
|
|
||||||
errorRetryButton.isVisible = retryShouldBeShown
|
errorRetryButton.isVisible = retryShouldBeShown
|
||||||
}
|
}
|
||||||
showAndSetOpenInBrowserButtonAction(errorInfo)
|
|
||||||
|
if (errorInfo.openInBrowserUrl != null) {
|
||||||
|
errorOpenInBrowserButton.isVisible = true
|
||||||
|
errorOpenInBrowserButton.setOnClickListener {
|
||||||
|
ShareUtils.openUrlInBrowser(context, errorInfo.openInBrowserUrl)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setRootVisible()
|
setRootVisible()
|
||||||
|
@ -129,15 +106,6 @@ class ErrorPanelHelper(
|
||||||
errorActionButton.setOnClickListener(listener)
|
errorActionButton.setOnClickListener(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showAndSetOpenInBrowserButtonAction(
|
|
||||||
errorInfo: ErrorInfo
|
|
||||||
) {
|
|
||||||
errorOpenInBrowserButton.isVisible = true
|
|
||||||
errorOpenInBrowserButton.setOnClickListener {
|
|
||||||
ShareUtils.openUrlInBrowser(context, errorInfo.request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun showTextError(errorString: String) {
|
fun showTextError(errorString: String) {
|
||||||
ensureDefaultVisibility()
|
ensureDefaultVisibility()
|
||||||
|
|
||||||
|
|
|
@ -771,7 +771,7 @@ class VideoDetailFragment :
|
||||||
},
|
},
|
||||||
{ throwable ->
|
{ throwable ->
|
||||||
showError(
|
showError(
|
||||||
ErrorInfo(throwable, UserAction.REQUESTED_STREAM, url ?: "no url", serviceId)
|
ErrorInfo(throwable, UserAction.REQUESTED_STREAM, url ?: "no url", serviceId, url)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1460,7 +1460,7 @@ class VideoDetailFragment :
|
||||||
|
|
||||||
if (!info.errors.isEmpty()) {
|
if (!info.errors.isEmpty()) {
|
||||||
showSnackBarError(
|
showSnackBarError(
|
||||||
ErrorInfo(info.errors, UserAction.REQUESTED_STREAM, info.url, info)
|
ErrorInfo(info.errors, UserAction.REQUESTED_STREAM, "Some info not extracted: " + info.url, info)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ public abstract class BaseListInfoFragment<I extends InfoItem, L extends ListInf
|
||||||
handleResult(result);
|
handleResult(result);
|
||||||
}, throwable ->
|
}, throwable ->
|
||||||
showError(new ErrorInfo(throwable, errorUserAction,
|
showError(new ErrorInfo(throwable, errorUserAction,
|
||||||
"Start loading: " + url, serviceId)));
|
"Start loading: " + url, serviceId, url)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -184,7 +184,7 @@ public abstract class BaseListInfoFragment<I extends InfoItem, L extends ListInf
|
||||||
handleNextItems(infoItemsPage);
|
handleNextItems(infoItemsPage);
|
||||||
}, (@NonNull Throwable throwable) ->
|
}, (@NonNull Throwable throwable) ->
|
||||||
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(throwable,
|
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(throwable,
|
||||||
errorUserAction, "Loading more items: " + url, serviceId)));
|
errorUserAction, "Loading more items: " + url, serviceId, url)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void forbidDownwardFocusScroll() {
|
private void forbidDownwardFocusScroll() {
|
||||||
|
@ -210,7 +210,7 @@ public abstract class BaseListInfoFragment<I extends InfoItem, L extends ListInf
|
||||||
|
|
||||||
if (!result.getErrors().isEmpty()) {
|
if (!result.getErrors().isEmpty()) {
|
||||||
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(result.getErrors(), errorUserAction,
|
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(result.getErrors(), errorUserAction,
|
||||||
"Get next items of: " + url, serviceId));
|
"Get next items of: " + url, serviceId, url));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +250,7 @@ public abstract class BaseListInfoFragment<I extends InfoItem, L extends ListInf
|
||||||
|
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(result.getErrors(),
|
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(result.getErrors(),
|
||||||
errorUserAction, "Start loading: " + url, serviceId));
|
errorUserAction, "Start loading: " + url, serviceId, url));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -583,7 +583,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
||||||
isLoading.set(false);
|
isLoading.set(false);
|
||||||
handleResult(result);
|
handleResult(result);
|
||||||
}, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_CHANNEL,
|
}, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_CHANNEL,
|
||||||
url == null ? "No URL" : url, serviceId)));
|
url == null ? "No URL" : url, serviceId, url)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -54,6 +54,7 @@ import org.schabi.newpipe.extractor.MetaInfo;
|
||||||
import org.schabi.newpipe.extractor.NewPipe;
|
import org.schabi.newpipe.extractor.NewPipe;
|
||||||
import org.schabi.newpipe.extractor.Page;
|
import org.schabi.newpipe.extractor.Page;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||||
import org.schabi.newpipe.extractor.search.SearchExtractor;
|
import org.schabi.newpipe.extractor.search.SearchExtractor;
|
||||||
import org.schabi.newpipe.extractor.search.SearchInfo;
|
import org.schabi.newpipe.extractor.search.SearchInfo;
|
||||||
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeSearchQueryHandlerFactory;
|
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeSearchQueryHandlerFactory;
|
||||||
|
@ -940,7 +941,21 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
infoListAdapter.clearStreamItemList();
|
infoListAdapter.clearStreamItemList();
|
||||||
showEmptyState();
|
showEmptyState();
|
||||||
} else {
|
} else {
|
||||||
showError(new ErrorInfo(exception, UserAction.SEARCHED, searchString, serviceId));
|
showError(new ErrorInfo(exception, UserAction.SEARCHED, searchString, serviceId,
|
||||||
|
getOpenInBrowserUrlForErrors()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private String getOpenInBrowserUrlForErrors() {
|
||||||
|
if (TextUtils.isEmpty(searchString)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return service.getSearchQHFactory().getUrl(searchString,
|
||||||
|
Arrays.asList(contentFilter), sortFilter);
|
||||||
|
} catch (final NullPointerException | ParsingException ignored) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1028,7 +1043,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
&& !(exceptions.size() == 1
|
&& !(exceptions.size() == 1
|
||||||
&& exceptions.get(0) instanceof SearchExtractor.NothingFoundException)) {
|
&& exceptions.get(0) instanceof SearchExtractor.NothingFoundException)) {
|
||||||
showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED,
|
showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED,
|
||||||
searchString, serviceId));
|
searchString, serviceId, getOpenInBrowserUrlForErrors()));
|
||||||
}
|
}
|
||||||
|
|
||||||
searchSuggestion = result.getSearchSuggestion();
|
searchSuggestion = result.getSearchSuggestion();
|
||||||
|
@ -1101,13 +1116,14 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
||||||
// whose results are handled here, but let's check it anyway
|
// whose results are handled here, but let's check it anyway
|
||||||
if (nextPage == null) {
|
if (nextPage == null) {
|
||||||
showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED,
|
showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED,
|
||||||
"\"" + searchString + "\" → nextPage == null", serviceId));
|
"\"" + searchString + "\" → nextPage == null", serviceId,
|
||||||
|
getOpenInBrowserUrlForErrors()));
|
||||||
} else {
|
} else {
|
||||||
showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED,
|
showSnackBarError(new ErrorInfo(result.getErrors(), UserAction.SEARCHED,
|
||||||
"\"" + searchString + "\" → pageUrl: " + nextPage.getUrl() + ", "
|
"\"" + searchString + "\" → pageUrl: " + nextPage.getUrl() + ", "
|
||||||
+ "pageIds: " + nextPage.getIds() + ", "
|
+ "pageIds: " + nextPage.getIds() + ", "
|
||||||
+ "pageCookies: " + nextPage.getCookies(),
|
+ "pageCookies: " + nextPage.getCookies(),
|
||||||
serviceId));
|
serviceId, getOpenInBrowserUrlForErrors()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,8 +85,8 @@ public class SubscriptionsImportFragment extends BaseFragment {
|
||||||
if (supportedSources.isEmpty() && currentServiceId != Constants.NO_SERVICE_ID) {
|
if (supportedSources.isEmpty() && currentServiceId != Constants.NO_SERVICE_ID) {
|
||||||
ErrorUtil.showSnackbar(activity,
|
ErrorUtil.showSnackbar(activity,
|
||||||
new ErrorInfo(new String[]{}, UserAction.SUBSCRIPTION_IMPORT_EXPORT,
|
new ErrorInfo(new String[]{}, UserAction.SUBSCRIPTION_IMPORT_EXPORT,
|
||||||
currentServiceId,
|
|
||||||
"Service does not support importing subscriptions",
|
"Service does not support importing subscriptions",
|
||||||
|
currentServiceId,
|
||||||
R.string.general_error));
|
R.string.general_error));
|
||||||
activity.finish();
|
activity.finish();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1283,7 +1283,8 @@ public final class Player implements PlaybackListener, Listener {
|
||||||
UserAction.PLAY_STREAM,
|
UserAction.PLAY_STREAM,
|
||||||
"Loading failed for [" + currentMetadata.getTitle()
|
"Loading failed for [" + currentMetadata.getTitle()
|
||||||
+ "]: " + currentMetadata.getStreamUrl(),
|
+ "]: " + currentMetadata.getStreamUrl(),
|
||||||
currentMetadata.getServiceId());
|
currentMetadata.getServiceId(),
|
||||||
|
currentMetadata.getStreamUrl());
|
||||||
ErrorUtil.createNotification(context, errorInfo);
|
ErrorUtil.createNotification(context, errorInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1499,7 +1500,7 @@ public final class Player implements PlaybackListener, Listener {
|
||||||
errorInfo = new ErrorInfo(error, UserAction.PLAY_STREAM,
|
errorInfo = new ErrorInfo(error, UserAction.PLAY_STREAM,
|
||||||
"Player error[type=" + error.getErrorCodeName()
|
"Player error[type=" + error.getErrorCodeName()
|
||||||
+ "] occurred while playing " + currentMetadata.getStreamUrl(),
|
+ "] occurred while playing " + currentMetadata.getStreamUrl(),
|
||||||
currentMetadata.getServiceId());
|
currentMetadata.getServiceId(), currentMetadata.getStreamUrl());
|
||||||
}
|
}
|
||||||
ErrorUtil.createNotification(context, errorInfo);
|
ErrorUtil.createNotification(context, errorInfo);
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ public final class SparseItemUtil {
|
||||||
callback.accept(result);
|
callback.accept(result);
|
||||||
}, throwable -> ErrorUtil.createNotification(context,
|
}, throwable -> ErrorUtil.createNotification(context,
|
||||||
new ErrorInfo(throwable, UserAction.REQUESTED_STREAM,
|
new ErrorInfo(throwable, UserAction.REQUESTED_STREAM,
|
||||||
"Loading stream info: " + url, serviceId)
|
"Loading stream info: " + url, serviceId, url)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,10 +160,10 @@ public final class InternalUrlsHandler {
|
||||||
final PlayQueue playQueue = new SinglePlayQueue(info, seconds * 1000L);
|
final PlayQueue playQueue = new SinglePlayQueue(info, seconds * 1000L);
|
||||||
NavigationHelper.playOnPopupPlayer(context, playQueue, false);
|
NavigationHelper.playOnPopupPlayer(context, playQueue, false);
|
||||||
}, throwable -> {
|
}, throwable -> {
|
||||||
final var errorInfo = new ErrorInfo(throwable, UserAction.PLAY_ON_POPUP, url);
|
|
||||||
// This will only show a snackbar if the passed context has a root view:
|
// This will only show a snackbar if the passed context has a root view:
|
||||||
// otherwise it will resort to showing a notification, so we are safe here.
|
// otherwise it will resort to showing a notification, so we are safe here.
|
||||||
ErrorUtil.showSnackbar(context, errorInfo);
|
ErrorUtil.showSnackbar(context,
|
||||||
|
new ErrorInfo(throwable, UserAction.PLAY_ON_POPUP, url, null, url));
|
||||||
}));
|
}));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -572,7 +572,7 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
|
||||||
|
|
||||||
ErrorUtil.createNotification(mContext,
|
ErrorUtil.createNotification(mContext,
|
||||||
new ErrorInfo(ErrorInfo.Companion.throwableToStringList(mission.errObject), action,
|
new ErrorInfo(ErrorInfo.Companion.throwableToStringList(mission.errObject), action,
|
||||||
service, request.toString(), reason));
|
request.toString(), service, reason));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearFinishedDownloads(boolean delete) {
|
public void clearFinishedDownloads(boolean delete) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue