Merge pull request #12578 from Stypox/better-error-messages

This commit is contained in:
Stypox 2025-09-04 13:18:40 +02:00 committed by GitHub
commit a3ddd616f9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
74 changed files with 217 additions and 208 deletions

View file

@ -58,20 +58,13 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.StreamingService.LinkType;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException;
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.GeographicRestrictionException;
import org.schabi.newpipe.extractor.exceptions.PaidContentException;
import org.schabi.newpipe.extractor.exceptions.PrivateContentException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException;
import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.ktx.ExceptionUtils;
import org.schabi.newpipe.local.dialog.PlaylistDialog;
import org.schabi.newpipe.player.PlayerType;
import org.schabi.newpipe.player.helper.PlayerHelper;
@ -279,28 +272,11 @@ public class RouterActivity extends AppCompatActivity {
final Intent intent = new Intent(context, ReCaptchaActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} else if (errorInfo.getThrowable() != null
&& ExceptionUtils.isNetworkRelated(errorInfo.getThrowable())) {
Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof AgeRestrictedContentException) {
Toast.makeText(context, R.string.restricted_video_no_stream,
Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof GeographicRestrictionException) {
Toast.makeText(context, R.string.georestricted_content, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof PaidContentException) {
Toast.makeText(context, R.string.paid_content, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof PrivateContentException) {
Toast.makeText(context, R.string.private_content, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof SoundCloudGoPlusContentException) {
Toast.makeText(context, R.string.soundcloud_go_plus_content,
Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof YoutubeMusicPremiumContentException) {
Toast.makeText(context, R.string.youtube_music_premium_content,
Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof ContentNotAvailableException) {
Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof ContentNotSupportedException) {
Toast.makeText(context, R.string.content_not_supported, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof ContentNotAvailableException
|| errorInfo.getThrowable() instanceof ContentNotSupportedException) {
// this exception does not usually indicate a problem that should be reported,
// so just show a toast instead of the notification
Toast.makeText(context, errorInfo.getMessage(context), Toast.LENGTH_LONG).show();
} else {
ErrorUtil.createNotification(context, errorInfo);
}

View file

@ -36,7 +36,7 @@ public class AcraReportSender implements ReportSender {
ErrorUtil.openActivity(context, new ErrorInfo(
new String[]{report.getString(ReportField.STACK_TRACE)},
UserAction.UI_ERROR,
ErrorInfo.SERVICE_NONE,
null,
"ACRA report",
R.string.app_ui_crash));
}

View file

@ -115,7 +115,7 @@ public class ErrorActivity extends AppCompatActivity {
// normal bugreport
buildInfo(errorInfo);
activityErrorBinding.errorMessageView.setText(errorInfo.getMessageStringId());
activityErrorBinding.errorMessageView.setText(errorInfo.getMessage(this));
activityErrorBinding.errorView.setText(formErrorText(errorInfo.getStackTraces()));
// print stack trace once again for debugging:

View file

@ -1,26 +1,42 @@
package org.schabi.newpipe.error
import android.content.Context
import android.os.Parcelable
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import com.google.android.exoplayer2.ExoPlaybackException
import com.google.android.exoplayer2.upstream.HttpDataSource
import com.google.android.exoplayer2.upstream.Loader
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.Info
import org.schabi.newpipe.extractor.ServiceList
import org.schabi.newpipe.extractor.ServiceList.YouTube
import org.schabi.newpipe.extractor.exceptions.AccountTerminatedException
import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException
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.GeographicRestrictionException
import org.schabi.newpipe.extractor.exceptions.PaidContentException
import org.schabi.newpipe.extractor.exceptions.PrivateContentException
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException
import org.schabi.newpipe.extractor.exceptions.SignInConfirmNotBotException
import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException
import org.schabi.newpipe.extractor.exceptions.UnsupportedContentInCountryException
import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException
import org.schabi.newpipe.ktx.isNetworkRelated
import org.schabi.newpipe.util.ServiceHelper
import org.schabi.newpipe.player.mediasource.FailedMediaSource
import org.schabi.newpipe.player.resolver.PlaybackResolver
@Parcelize
class ErrorInfo(
class ErrorInfo private constructor(
val stackTraces: Array<String>,
val userAction: UserAction,
val serviceName: String,
val serviceId: Int?,
val request: String,
val messageStringId: Int
private val message: ErrorMessage,
) : Parcelable {
// no need to store throwable, all data for report is in other variables
@ -31,14 +47,14 @@ class ErrorInfo(
private constructor(
throwable: Throwable,
userAction: UserAction,
serviceName: String,
serviceId: Int?,
request: String
) : this(
throwableToStringList(throwable),
userAction,
serviceName,
serviceId,
request,
getMessageStringId(throwable, userAction)
getMessage(throwable, userAction, serviceId)
) {
this.throwable = throwable
}
@ -46,70 +62,176 @@ class ErrorInfo(
private constructor(
throwable: List<Throwable>,
userAction: UserAction,
serviceName: String,
serviceId: Int?,
request: String
) : this(
throwableListToStringList(throwable),
userAction,
serviceName,
serviceId,
request,
getMessageStringId(throwable.firstOrNull(), userAction)
getMessage(throwable.firstOrNull(), userAction, serviceId)
) {
this.throwable = throwable.firstOrNull()
}
// constructor to manually build ErrorInfo
constructor(stackTraces: Array<String>, userAction: UserAction, serviceId: Int?, request: String, @StringRes message: Int) :
this(stackTraces, userAction, serviceId, request, ErrorMessage(message))
// constructors with single throwable
constructor(throwable: Throwable, userAction: UserAction, request: String) :
this(throwable, userAction, SERVICE_NONE, request)
this(throwable, userAction, null, request)
constructor(throwable: Throwable, userAction: UserAction, request: String, serviceId: Int) :
this(throwable, userAction, ServiceHelper.getNameOfServiceById(serviceId), request)
this(throwable, userAction, serviceId, request)
constructor(throwable: Throwable, userAction: UserAction, request: String, info: Info?) :
this(throwable, userAction, getInfoServiceName(info), request)
this(throwable, userAction, info?.serviceId, request)
// constructors with list of throwables
constructor(throwable: List<Throwable>, userAction: UserAction, request: String) :
this(throwable, userAction, SERVICE_NONE, request)
this(throwable, userAction, null, request)
constructor(throwable: List<Throwable>, userAction: UserAction, request: String, serviceId: Int) :
this(throwable, userAction, ServiceHelper.getNameOfServiceById(serviceId), request)
this(throwable, userAction, serviceId, request)
constructor(throwable: List<Throwable>, userAction: UserAction, request: String, info: Info?) :
this(throwable, userAction, getInfoServiceName(info), request)
this(throwable, userAction, info?.serviceId, request)
fun getServiceName(): String {
return getServiceName(serviceId)
}
fun getMessage(context: Context): String {
return message.getString(context)
}
companion object {
const val SERVICE_NONE = "none"
@Parcelize
class ErrorMessage(
@StringRes
private val stringRes: Int,
private vararg val formatArgs: String,
) : Parcelable {
fun getString(context: Context): String {
return if (formatArgs.isEmpty()) {
// use ContextCompat.getString() just in case context is not AppCompatActivity
ContextCompat.getString(context, 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)
}
}
}
const val SERVICE_NONE = "<unknown_service>"
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
?: SERVICE_NONE
fun throwableToStringList(throwable: Throwable) = arrayOf(throwable.stackTraceToString())
fun throwableListToStringList(throwableList: List<Throwable>) =
throwableList.map { it.stackTraceToString() }.toTypedArray()
private fun getInfoServiceName(info: Info?) =
if (info == null) SERVICE_NONE else ServiceHelper.getNameOfServiceById(info.serviceId)
@StringRes
private fun getMessageStringId(
fun getMessage(
throwable: Throwable?,
action: UserAction
): Int {
action: UserAction?,
serviceId: Int?,
): ErrorMessage {
return when {
throwable is AccountTerminatedException -> R.string.account_terminated
throwable is ContentNotAvailableException -> R.string.content_not_available
throwable != null && throwable.isNetworkRelated -> R.string.network_error
throwable is ContentNotSupportedException -> R.string.content_not_supported
throwable is ExtractionException -> R.string.parsing_error
// player exceptions
// some may be IOException, so do these checks before isNetworkRelated!
throwable is ExoPlaybackException -> {
when (throwable.type) {
ExoPlaybackException.TYPE_SOURCE -> R.string.player_stream_failure
ExoPlaybackException.TYPE_UNEXPECTED -> R.string.player_recoverable_failure
else -> R.string.player_unrecoverable_failure
val cause = throwable.cause
when {
cause is HttpDataSource.InvalidResponseCodeException -> {
if (cause.responseCode == 403) {
if (serviceId == YouTube.serviceId) {
ErrorMessage(R.string.youtube_player_http_403)
} else {
ErrorMessage(R.string.player_http_403)
}
} else {
ErrorMessage(R.string.player_http_invalid_status, cause.responseCode.toString())
}
}
cause is Loader.UnexpectedLoaderException && cause.cause is ExtractionException ->
getMessage(throwable, action, serviceId)
throwable.type == ExoPlaybackException.TYPE_SOURCE ->
ErrorMessage(R.string.player_stream_failure)
throwable.type == ExoPlaybackException.TYPE_UNEXPECTED ->
ErrorMessage(R.string.player_recoverable_failure)
else ->
ErrorMessage(R.string.player_unrecoverable_failure)
}
}
action == UserAction.UI_ERROR -> R.string.app_ui_crash
action == UserAction.REQUESTED_COMMENTS -> R.string.error_unable_to_load_comments
action == UserAction.SUBSCRIPTION_CHANGE -> R.string.subscription_change_failed
action == UserAction.SUBSCRIPTION_UPDATE -> R.string.subscription_update_failed
action == UserAction.LOAD_IMAGE -> R.string.could_not_load_thumbnails
action == UserAction.DOWNLOAD_OPEN_DIALOG -> R.string.could_not_setup_download_menu
else -> R.string.general_error
throwable is FailedMediaSource.FailedMediaSourceException ->
getMessage(throwable.cause, action, serviceId)
throwable is PlaybackResolver.ResolverException ->
ErrorMessage(R.string.player_stream_failure)
// content not available exceptions
throwable is AccountTerminatedException ->
throwable.message
?.takeIf { reason -> !reason.isEmpty() }
?.let { reason ->
ErrorMessage(
R.string.account_terminated_service_provides_reason,
getServiceName(serviceId),
reason
)
}
?: ErrorMessage(R.string.account_terminated)
throwable is AgeRestrictedContentException ->
ErrorMessage(R.string.restricted_video_no_stream)
throwable is GeographicRestrictionException ->
ErrorMessage(R.string.georestricted_content)
throwable is PaidContentException ->
ErrorMessage(R.string.paid_content)
throwable is PrivateContentException ->
ErrorMessage(R.string.private_content)
throwable is SoundCloudGoPlusContentException ->
ErrorMessage(R.string.soundcloud_go_plus_content)
throwable is UnsupportedContentInCountryException ->
ErrorMessage(R.string.unsupported_content_in_country)
throwable is YoutubeMusicPremiumContentException ->
ErrorMessage(R.string.youtube_music_premium_content)
throwable is SignInConfirmNotBotException ->
ErrorMessage(R.string.sign_in_confirm_not_bot_error, getServiceName(serviceId))
throwable is ContentNotAvailableException ->
ErrorMessage(R.string.content_not_available)
// other extractor exceptions
throwable is ContentNotSupportedException ->
ErrorMessage(R.string.content_not_supported)
// ReCaptchas should have already been handled elsewhere,
// but return an error message here just in case
throwable is ReCaptchaException ->
ErrorMessage(R.string.recaptcha_request_toast)
// test this at the end as many exceptions could be a subclass of IOException
throwable != null && throwable.isNetworkRelated ->
ErrorMessage(R.string.network_error)
// an extraction exception unrelated to the network
// is likely an issue with parsing the website
throwable is ExtractionException ->
ErrorMessage(R.string.parsing_error)
// user actions (in case the exception is null or unrecognizable)
action == UserAction.UI_ERROR ->
ErrorMessage(R.string.app_ui_crash)
action == UserAction.REQUESTED_COMMENTS ->
ErrorMessage(R.string.error_unable_to_load_comments)
action == UserAction.SUBSCRIPTION_CHANGE ->
ErrorMessage(R.string.subscription_change_failed)
action == UserAction.SUBSCRIPTION_UPDATE ->
ErrorMessage(R.string.subscription_update_failed)
action == UserAction.LOAD_IMAGE ->
ErrorMessage(R.string.could_not_load_thumbnails)
action == UserAction.DOWNLOAD_OPEN_DIALOG ->
ErrorMessage(R.string.could_not_setup_download_menu)
else ->
ErrorMessage(R.string.error_snackbar_message)
}
}
}

View file

@ -14,21 +14,11 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.Disposable
import org.schabi.newpipe.MainActivity
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.exceptions.AccountTerminatedException
import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException
import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException
import org.schabi.newpipe.extractor.exceptions.PaidContentException
import org.schabi.newpipe.extractor.exceptions.PrivateContentException
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException
import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException
import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException
import org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty
import org.schabi.newpipe.ktx.animate
import org.schabi.newpipe.ktx.isInterruptedCaused
import org.schabi.newpipe.ktx.isNetworkRelated
import org.schabi.newpipe.util.ServiceHelper
import org.schabi.newpipe.util.external_communication.ShareUtils
import java.util.concurrent.TimeUnit
@ -106,20 +96,6 @@ class ErrorPanelHelper(
errorRetryButton.isVisible = retryShouldBeShown
showAndSetOpenInBrowserButtonAction(errorInfo)
} else if (errorInfo.throwable is AccountTerminatedException) {
errorTextView.setText(R.string.account_terminated)
if (!isNullOrEmpty((errorInfo.throwable as AccountTerminatedException).message)) {
errorServiceInfoTextView.text = context.resources.getString(
R.string.service_provides_reason,
ServiceHelper.getSelectedService(context)?.serviceInfo?.name ?: "<unknown>"
)
errorServiceInfoTextView.isVisible = true
errorServiceExplanationTextView.text =
(errorInfo.throwable as AccountTerminatedException).message
errorServiceExplanationTextView.isVisible = true
}
} else {
showAndSetErrorButtonAction(
R.string.error_snackbar_action
@ -127,7 +103,7 @@ class ErrorPanelHelper(
ErrorUtil.openActivity(context, errorInfo)
}
errorTextView.setText(getExceptionDescription(errorInfo.throwable))
errorTextView.text = errorInfo.getMessage(context)
if (errorInfo.throwable !is ContentNotAvailableException &&
errorInfo.throwable !is ContentNotSupportedException
@ -192,27 +168,5 @@ class ErrorPanelHelper(
companion object {
val TAG: String = ErrorPanelHelper::class.simpleName!!
val DEBUG: Boolean = MainActivity.DEBUG
@StringRes
fun getExceptionDescription(throwable: Throwable?): Int {
return when (throwable) {
is AgeRestrictedContentException -> R.string.restricted_video_no_stream
is GeographicRestrictionException -> R.string.georestricted_content
is PaidContentException -> R.string.paid_content
is PrivateContentException -> R.string.private_content
is SoundCloudGoPlusContentException -> R.string.soundcloud_go_plus_content
is YoutubeMusicPremiumContentException -> R.string.youtube_music_premium_content
is ContentNotAvailableException -> R.string.content_not_available
is ContentNotSupportedException -> R.string.content_not_supported
else -> {
// show retry button only for content which is not unavailable or unsupported
if (throwable != null && throwable.isNetworkRelated) {
R.string.network_error
} else {
R.string.error_snackbar_message
}
}
}
}
}
}

View file

@ -122,7 +122,7 @@ class ErrorUtil {
)
.setSmallIcon(R.drawable.ic_bug_report)
.setContentTitle(context.getString(R.string.error_report_notification_title))
.setContentText(context.getString(errorInfo.messageStringId))
.setContentText(errorInfo.getMessage(context))
.setAutoCancel(true)
.setContentIntent(
PendingIntentCompat.getActivity(
@ -156,10 +156,10 @@ class ErrorUtil {
// fallback to showing a notification if no root view is available
createNotification(context, errorInfo)
} else {
Snackbar.make(rootView, R.string.error_snackbar_message, Snackbar.LENGTH_LONG)
Snackbar.make(rootView, errorInfo.getMessage(context), Snackbar.LENGTH_LONG)
.setActionTextColor(Color.YELLOW)
.setAction(context.getString(R.string.error_snackbar_action).uppercase()) {
openActivity(context, errorInfo)
context.startActivity(getErrorActivityIntent(context, errorInfo))
}.show()
}
}

View file

@ -33,7 +33,9 @@ public enum UserAction {
SHARE_TO_NEWPIPE("share to newpipe"),
CHECK_FOR_NEW_APP_VERSION("check for new app version"),
OPEN_INFO_ITEM_DIALOG("open info item dialog"),
GETTING_MAIN_SCREEN_TAB("getting main screen tab");
GETTING_MAIN_SCREEN_TAB("getting main screen tab"),
PLAY_ON_POPUP("play on popup"),
SUBSCRIPTIONS("loading subscriptions");
private final String message;

View file

@ -89,7 +89,7 @@ public class SubscriptionsImportFragment extends BaseFragment {
if (supportedSources.isEmpty() && currentServiceId != Constants.NO_SERVICE_ID) {
ErrorUtil.showSnackbar(activity,
new ErrorInfo(new String[]{}, UserAction.SUBSCRIPTION_IMPORT_EXPORT,
ServiceHelper.getNameOfServiceById(currentServiceId),
currentServiceId,
"Service does not support importing subscriptions",
R.string.general_error));
activity.finish();

View file

@ -17,6 +17,7 @@ import io.reactivex.rxjava3.schedulers.Schedulers
import org.schabi.newpipe.MainActivity
import org.schabi.newpipe.NewPipeDatabase
import org.schabi.newpipe.R
import org.schabi.newpipe.error.ErrorInfo
import org.schabi.newpipe.extractor.InfoItem.InfoType
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler
@ -84,7 +85,7 @@ class MediaBrowserPlaybackPreparer(
},
{ throwable ->
Log.e(TAG, "Failed to start playback of media ID [$mediaId]", throwable)
onPrepareError()
onPrepareError(throwable)
}
)
}
@ -115,9 +116,9 @@ class MediaBrowserPlaybackPreparer(
)
}
private fun onPrepareError() {
private fun onPrepareError(throwable: Throwable) {
setMediaSessionError.accept(
ContextCompat.getString(context, R.string.error_snackbar_message),
ErrorInfo.getMessage(throwable, null, null).getString(context),
PlaybackStateCompat.ERROR_CODE_APP_ERROR
)
}

View file

@ -1,14 +1,13 @@
package org.schabi.newpipe.util.text;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorPanelHelper;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.ErrorUtil;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
@ -158,19 +157,13 @@ public final class InternalUrlsHandler {
disposables.add(single.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(info -> {
final PlayQueue playQueue =
new SinglePlayQueue(info, seconds * 1000L);
final PlayQueue playQueue = new SinglePlayQueue(info, seconds * 1000L);
NavigationHelper.playOnPopupPlayer(context, playQueue, false);
}, throwable -> {
if (DEBUG) {
Log.e(TAG, "Could not play on popup: " + url, throwable);
}
new AlertDialog.Builder(context)
.setTitle(R.string.player_stream_failure)
.setMessage(
ErrorPanelHelper.Companion.getExceptionDescription(throwable))
.setPositiveButton(R.string.ok, null)
.show();
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:
// otherwise it will resort to showing a notification, so we are safe here.
ErrorUtil.showSnackbar(context, errorInfo);
}));
return true;
}

View file

@ -563,11 +563,11 @@ public class MissionAdapter extends Adapter<ViewHolder> implements Handler.Callb
}
request.append("]");
String service;
Integer service;
try {
service = NewPipe.getServiceByUrl(mission.source).getServiceInfo().getName();
service = NewPipe.getServiceByUrl(mission.source).getServiceId();
} catch (Exception e) {
service = ErrorInfo.SERVICE_NONE;
service = null;
}
ErrorUtil.createNotification(mContext,