@@ -7,7 +7,6 @@ import androidx.core.content.ContextCompat
77import com.google.android.exoplayer2.ExoPlaybackException
88import com.google.android.exoplayer2.upstream.HttpDataSource
99import com.google.android.exoplayer2.upstream.Loader
10- import kotlinx.parcelize.IgnoredOnParcel
1110import kotlinx.parcelize.Parcelize
1211import org.schabi.newpipe.R
1312import org.schabi.newpipe.extractor.Info
@@ -29,70 +28,108 @@ import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentExcepti
2928import org.schabi.newpipe.ktx.isNetworkRelated
3029import org.schabi.newpipe.player.mediasource.FailedMediaSource
3130import org.schabi.newpipe.player.resolver.PlaybackResolver
31+ import java.net.UnknownHostException
3232
33+ /* *
34+ * An error has occurred in the app. This class contains plain old parcelable data that can be used
35+ * to report the error and to show it to the user along with correct action buttons.
36+ */
3337@Parcelize
3438class ErrorInfo private constructor(
3539 val stackTraces : Array <String >,
3640 val userAction : UserAction ,
37- val serviceId : Int? ,
3841 val request : String ,
42+ val serviceId : Int? ,
3943 private val message : ErrorMessage ,
44+ /* *
45+ * If `true`, a report button will be shown for this error. Otherwise the error is not something
46+ * that can really be reported (e.g. a network issue, or content not being available at all).
47+ */
48+ val isReportable : Boolean ,
49+ /* *
50+ * If `true`, the process causing this error can be retried, otherwise not.
51+ */
52+ val isRetryable : Boolean ,
53+ /* *
54+ * If present, indicates that the exception was a ReCaptchaException, and this is the URL
55+ * provided by the service that can be used to solve the ReCaptcha challenge.
56+ */
57+ val recaptchaUrl : String? ,
58+ /* *
59+ * If present, this resource can alternatively be opened in browser (useful if NewPipe is
60+ * badly broken).
61+ */
62+ val openInBrowserUrl : String? ,
4063) : Parcelable {
4164
42- // no need to store throwable, all data for report is in other variables
43- // also, the throwable might not be serializable, see TeamNewPipe/NewPipe#7302
44- @IgnoredOnParcel
45- var throwable: Throwable ? = null
46-
47- private constructor (
65+ @JvmOverloads
66+ constructor (
4867 throwable: Throwable ,
4968 userAction: UserAction ,
50- serviceId: Int? ,
51- request: String
69+ request: String ,
70+ serviceId: Int? = null ,
71+ openInBrowserUrl: String? = null ,
5272 ) : this (
5373 throwableToStringList(throwable),
5474 userAction,
55- serviceId,
5675 request,
57- getMessage(throwable, userAction, serviceId)
58- ) {
59- this .throwable = throwable
60- }
76+ serviceId,
77+ getMessage(throwable, userAction, serviceId),
78+ isReportable(throwable),
79+ isRetryable(throwable),
80+ (throwable as ? ReCaptchaException )?.url,
81+ openInBrowserUrl,
82+ )
6183
62- private constructor (
63- throwable: List <Throwable >,
84+ @JvmOverloads
85+ constructor (
86+ throwables: List <Throwable >,
6487 userAction: UserAction ,
65- serviceId: Int? ,
66- request: String
88+ request: String ,
89+ serviceId: Int? = null ,
90+ openInBrowserUrl: String? = null ,
6791 ) : this (
68- throwableListToStringList(throwable ),
92+ throwableListToStringList(throwables ),
6993 userAction,
70- serviceId,
7194 request,
72- getMessage(throwable.firstOrNull(), userAction, serviceId)
73- ) {
74- this .throwable = throwable.firstOrNull()
75- }
95+ serviceId,
96+ getMessage(throwables.firstOrNull(), userAction, serviceId),
97+ throwables.any(::isReportable),
98+ throwables.isEmpty() || throwables.any(::isRetryable),
99+ throwables.firstNotNullOfOrNull { it as ? ReCaptchaException }?.url,
100+ openInBrowserUrl,
101+ )
102+
103+ // constructor to manually build ErrorInfo when no throwable is available
104+ constructor (
105+ stackTraces: Array <String >,
106+ userAction: UserAction ,
107+ request: String ,
108+ serviceId: Int? ,
109+ @StringRes message: Int
110+ ) :
111+ this (
112+ stackTraces, userAction, request, serviceId, ErrorMessage (message),
113+ true , false , null , null
114+ )
115+
116+ // constructor with only one throwable to extract service id and openInBrowserUrl from an Info
117+ constructor (
118+ throwable: Throwable ,
119+ userAction: UserAction ,
120+ request: String ,
121+ info: Info ? ,
122+ ) :
123+ this (throwable, userAction, request, info?.serviceId, info?.url)
76124
77- // constructor to manually build ErrorInfo
78- constructor (stackTraces: Array <String >, userAction: UserAction , serviceId: Int? , request: String , @StringRes message: Int ) :
79- this (stackTraces, userAction, serviceId, request, ErrorMessage (message))
80-
81- // constructors with single throwable
82- constructor (throwable: Throwable , userAction: UserAction , request: String ) :
83- this (throwable, userAction, null , request)
84- constructor (throwable: Throwable , userAction: UserAction , request: String , serviceId: Int ) :
85- this (throwable, userAction, serviceId, request)
86- constructor (throwable: Throwable , userAction: UserAction , request: String , info: Info ? ) :
87- this (throwable, userAction, info?.serviceId, request)
88-
89- // constructors with list of throwables
90- constructor (throwable: List <Throwable >, userAction: UserAction , request: String ) :
91- this (throwable, userAction, null , request)
92- constructor (throwable: List <Throwable >, userAction: UserAction , request: String , serviceId: Int ) :
93- this (throwable, userAction, serviceId, request)
94- constructor (throwable: List <Throwable >, userAction: UserAction , request: String , info: Info ? ) :
95- this (throwable, userAction, info?.serviceId, request)
125+ // constructor with multiple throwables to extract service id and openInBrowserUrl from an Info
126+ constructor (
127+ throwables: List <Throwable >,
128+ userAction: UserAction ,
129+ request: String ,
130+ info: Info ? ,
131+ ) :
132+ this (throwables, userAction, request, info?.serviceId, info?.url)
96133
97134 fun getServiceName (): String {
98135 return getServiceName(serviceId)
@@ -205,8 +242,7 @@ class ErrorInfo private constructor(
205242 // other extractor exceptions
206243 throwable is ContentNotSupportedException ->
207244 ErrorMessage (R .string.content_not_supported)
208- // ReCaptchas should have already been handled elsewhere,
209- // but return an error message here just in case
245+ // ReCaptchas will be handled in a special way anyway
210246 throwable is ReCaptchaException ->
211247 ErrorMessage (R .string.recaptcha_request_toast)
212248 // test this at the end as many exceptions could be a subclass of IOException
@@ -234,5 +270,36 @@ class ErrorInfo private constructor(
234270 ErrorMessage (R .string.error_snackbar_message)
235271 }
236272 }
273+
274+ fun isReportable (throwable : Throwable ? ): Boolean {
275+ return when (throwable) {
276+ // we don't have an exception, so this is a manually built error, which likely
277+ // indicates that it's important and is thus reportable
278+ null -> true
279+ // the service explicitly said that content is not available (e.g. age restrictions,
280+ // video deleted, etc.), there is no use in letting users report it
281+ is ContentNotAvailableException -> false
282+ // we know the content is not supported, no need to let the user report it
283+ is ContentNotSupportedException -> false
284+ // happens often when there is no internet connection; we don't use
285+ // `throwable.isNetworkRelated` since any `IOException` would make that function
286+ // return true, but not all `IOException`s are network related
287+ is UnknownHostException -> false
288+ // by default, this is an unexpected exception, which the user could report
289+ else -> true
290+ }
291+ }
292+
293+ fun isRetryable (throwable : Throwable ? ): Boolean {
294+ return when (throwable) {
295+ // we know the content is not available, retrying won't help
296+ is ContentNotAvailableException -> false
297+ // we know the content is not supported, retrying won't help
298+ is ContentNotSupportedException -> false
299+ // by default (including if throwable is null), enable retrying (though the retry
300+ // button will be shown only if a way to perform the retry is implemented)
301+ else -> true
302+ }
303+ }
237304 }
238305}
0 commit comments