Better exception handling

This commit is contained in:
Mattia 2021-07-15 23:43:47 +02:00
parent 7e1723786f
commit 89c073fd51
9 changed files with 59 additions and 87 deletions

View File

@ -3,25 +3,16 @@ import 'package:http/http.dart';
import 'youtube_explode_exception.dart';
/// Exception thrown when a fatal failure occurs.
class FatalFailureException implements YoutubeExplodeException {
/// Description message
@override
final String message;
class FatalFailureException extends YoutubeExplodeException {
/// Initializes an instance of [FatalFailureException]
FatalFailureException(this.message);
FatalFailureException(String message) : super(message);
/// Initializes an instance of [FatalFailureException] with a [Response]
FatalFailureException.httpRequest(BaseResponse response)
: message = '''
FatalFailureException.httpRequest(BaseResponse response) : super('''
Failed to perform an HTTP request to YouTube due to a fatal failure.
In most cases, this error indicates that YouTube most likely changed something, which broke the library.
If this issue persists, please report it on the project's GitHub page.
Request: ${response.request}
Response: (${response.statusCode})
''';
@override
String toString() =>
'$runtimeType: $message'; // ignore: no_runtimetype_tostring
''');
}

View File

@ -3,26 +3,17 @@ import 'package:http/http.dart';
import 'youtube_explode_exception.dart';
/// Exception thrown when a fatal failure occurs.
class RequestLimitExceededException implements YoutubeExplodeException {
/// Description message
@override
final String message;
class RequestLimitExceededException extends YoutubeExplodeException {
/// Initializes an instance of [RequestLimitExceededException]
RequestLimitExceededException(this.message);
RequestLimitExceededException(String message) : super(message);
/// Initializes an instance of [RequestLimitExceeded] with a [Response]
RequestLimitExceededException.httpRequest(BaseResponse response)
: message = '''
RequestLimitExceededException.httpRequest(BaseResponse response) : super('''
Failed to perform an HTTP request to YouTube because of rate limiting.
This error indicates that YouTube thinks there were too many requests made from this IP and considers it suspicious.
To resolve this error, please wait some time and try again -or- try injecting an HttpClient that has cookies for an authenticated user.
Unfortunately, there's nothing the library can do to work around this error.
Request: ${response.request}
Response: $response
''';
@override
String toString() =>
'$runtimeType: $message'; // ignore: no_runtimetype_tostring
''');
}

View File

@ -3,8 +3,6 @@
import '../../youtube_explode_dart.dart';
/// Exception thrown when the Item Section is missing from a search request.
class SearchItemSectionException implements YoutubeExplodeException {
@override
// TODO: implement message
String get message => 'Failed to find the item section.';
class SearchItemSectionException extends YoutubeExplodeException {
SearchItemSectionException() : super('Failed to find the item section.');
}

View File

@ -3,25 +3,17 @@ import 'package:http/http.dart';
import 'youtube_explode_exception.dart';
/// Exception thrown when a fatal failure occurs.
class TransientFailureException implements YoutubeExplodeException {
@override
final String message;
class TransientFailureException extends YoutubeExplodeException {
/// Initializes an instance of [TransientFailureException]
TransientFailureException(this.message);
TransientFailureException(String message) : super(message);
/// Initializes an instance of [TransientFailureException] with a [Response]
TransientFailureException.httpRequest(BaseResponse response)
: message = '''
TransientFailureException.httpRequest(BaseResponse response) : super('''
Failed to perform an HTTP request to YouTube due to a transient failure.
In most cases, this error indicates that the problem is on YouTube's side and this is not a bug in the library.
To resolve this error, please wait some time and try again.
If this issue persists, please report it on the project's GitHub page.
Request: ${response.request}
Response: $response
''';
@override
String toString() =>
'$runtimeType: $message'; // ignore: no_runtimetype_tostring
''');
}

View File

@ -2,22 +2,13 @@ import '../videos/video_id.dart';
import 'video_unplayable_exception.dart';
/// Exception thrown when the requested video requires purchase.
class VideoRequiresPurchaseException implements VideoUnplayableException {
/// Description message
@override
final String message;
class VideoRequiresPurchaseException extends VideoUnplayableException {
/// VideoId instance
final VideoId previewVideoId;
/// Initializes an instance of [VideoRequiresPurchaseException].
VideoRequiresPurchaseException.preview(VideoId videoId, this.previewVideoId)
: message =
'Video `$videoId` is unplayable because it requires purchase.\n'
'Streams are not available for this video.\n'
'There is a preview video available: `$previewVideoId`.';
@override
String toString() =>
'$runtimeType: $message'; // ignore: no_runtimetype_tostring
: super('Video `$videoId` is unplayable because it requires purchase.\n'
'Streams are not available for this video.\n'
'There is a preview video available: `$previewVideoId`.');
}

View File

@ -4,24 +4,16 @@ import 'exceptions.dart';
/// Thrown when a video is not available and cannot be processed.
/// This can happen because the video does not exist, is deleted,
/// is private, or due to other reasons.
class VideoUnavailableException implements VideoUnplayableException {
/// Description message
@override
final String message;
class VideoUnavailableException extends VideoUnplayableException {
/// Initializes an instance of [VideoUnavailableException]
VideoUnavailableException(this.message);
VideoUnavailableException(String message) : super(message);
/// Initializes an instance of [VideoUnplayableException] with a [VideoId]
VideoUnavailableException.unavailable(VideoId videoId)
: message = 'Video \'$videoId\' is unavailable\n'
: super('Video \'$videoId\' is unavailable\n'
'In most cases, this error indicates that the video doesn\'t exist, ' // ignore: lines_longer_than_80_chars
'is private, or has been taken down.\n'
'If you can however open this video in your browser in incognito mode, ' // ignore: lines_longer_than_80_chars
'it most likely means that YouTube changed something, which broke this library.\n' // ignore: lines_longer_than_80_chars
'Please report this issue on GitHub in that case.';
@override
String toString() =>
'$runtimeType: $message'; // ignore: no_runtimetype_tostring
'Please report this issue on GitHub in that case.');
}

View File

@ -2,35 +2,26 @@ import '../videos/video_id.dart';
import 'youtube_explode_exception.dart';
/// Exception thrown when the requested video is unplayable.
class VideoUnplayableException implements YoutubeExplodeException {
/// Description message
@override
final String message;
class VideoUnplayableException extends YoutubeExplodeException {
/// Initializes an instance of [VideoUnplayableException]
VideoUnplayableException(this.message);
VideoUnplayableException(String message) : super(message);
/// Initializes an instance of [VideoUnplayableException] with a [VideoId]
VideoUnplayableException.unplayable(VideoId videoId, {String reason = ''})
: message = 'Video \'$videoId\' is unplayable.\n'
: super('Video \'$videoId\' is unplayable.\n'
'Streams are not available for this video.\n'
'In most cases, this error indicates that there are \n'
'some restrictions in place that prevent watching this video.\n'
'Reason: $reason';
'Reason: $reason');
/// Initializes an instance of [VideoUnplayableException] with a [VideoId]
VideoUnplayableException.liveStream(VideoId videoId)
: message = 'Video \'$videoId\' is an ongoing live stream.\n'
: super('Video \'$videoId\' is an ongoing live stream.\n'
'Streams are not available for this video.\n'
'Please wait until the live stream finishes and try again.';
'Please wait until the live stream finishes and try again.');
/// Initializes an instance of [VideoUnplayableException] with a [VideoId]
VideoUnplayableException.notLiveStream(VideoId videoId)
: message = 'Video \'$videoId\' is not an ongoing live stream.\n'
'Live stream manifest is not available for this video';
@override
// ignore:
String toString() =>
'$runtimeType: $message'; // ignore: no_runtimetype_tostring
: super('Video \'$videoId\' is not an ongoing live stream.\n'
'Live stream manifest is not available for this video');
}

View File

@ -3,6 +3,28 @@ abstract class YoutubeExplodeException implements Exception {
/// Generic message.
final String message;
/// Addition exceptions thrown.
final List<YoutubeExplodeException> _others = [];
/// Add another exception to the stack.
void combine(YoutubeExplodeException e) => _others.add(e);
///
YoutubeExplodeException(this.message);
@override
String toString() {
if (_others.isEmpty) {
return '$runtimeType: $message';
}
final buffer = StringBuffer('$runtimeType: $message\n\n');
buffer.writeln('Additionally these exceptions where thrown in the stack');
for (final e in _others) {
buffer.writeln('---');
buffer.writeln(e.toString());
buffer.writeln('---');
}
return buffer.toString();
}
}

View File

@ -226,9 +226,13 @@ class StreamsClient {
try {
var context = await _getStreamContextFromVideoInfo(videoId);
return _getManifest(context);
} on YoutubeExplodeException {
var context = await _getStreamContextFromWatchPage(videoId);
return _getManifest(context);
} on YoutubeExplodeException catch (e) {
try {
var context = await _getStreamContextFromWatchPage(videoId);
return _getManifest(context);
} on YoutubeExplodeException catch (e1) {
throw e..combine(e1);
}
}
}