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'; import 'youtube_explode_exception.dart';
/// Exception thrown when a fatal failure occurs. /// Exception thrown when a fatal failure occurs.
class FatalFailureException implements YoutubeExplodeException { class FatalFailureException extends YoutubeExplodeException {
/// Description message
@override
final String message;
/// Initializes an instance of [FatalFailureException] /// Initializes an instance of [FatalFailureException]
FatalFailureException(this.message); FatalFailureException(String message) : super(message);
/// Initializes an instance of [FatalFailureException] with a [Response] /// Initializes an instance of [FatalFailureException] with a [Response]
FatalFailureException.httpRequest(BaseResponse response) FatalFailureException.httpRequest(BaseResponse response) : super('''
: message = '''
Failed to perform an HTTP request to YouTube due to a fatal failure. 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. 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. If this issue persists, please report it on the project's GitHub page.
Request: ${response.request} Request: ${response.request}
Response: (${response.statusCode}) 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'; import 'youtube_explode_exception.dart';
/// Exception thrown when a fatal failure occurs. /// Exception thrown when a fatal failure occurs.
class RequestLimitExceededException implements YoutubeExplodeException { class RequestLimitExceededException extends YoutubeExplodeException {
/// Description message
@override
final String message;
/// Initializes an instance of [RequestLimitExceededException] /// Initializes an instance of [RequestLimitExceededException]
RequestLimitExceededException(this.message); RequestLimitExceededException(String message) : super(message);
/// Initializes an instance of [RequestLimitExceeded] with a [Response] /// Initializes an instance of [RequestLimitExceeded] with a [Response]
RequestLimitExceededException.httpRequest(BaseResponse response) RequestLimitExceededException.httpRequest(BaseResponse response) : super('''
: message = '''
Failed to perform an HTTP request to YouTube because of rate limiting. 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. 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. 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. Unfortunately, there's nothing the library can do to work around this error.
Request: ${response.request} Request: ${response.request}
Response: $response Response: $response
'''; ''');
@override
String toString() =>
'$runtimeType: $message'; // ignore: no_runtimetype_tostring
} }

View File

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

View File

@ -3,25 +3,17 @@ import 'package:http/http.dart';
import 'youtube_explode_exception.dart'; import 'youtube_explode_exception.dart';
/// Exception thrown when a fatal failure occurs. /// Exception thrown when a fatal failure occurs.
class TransientFailureException implements YoutubeExplodeException { class TransientFailureException extends YoutubeExplodeException {
@override
final String message;
/// Initializes an instance of [TransientFailureException] /// Initializes an instance of [TransientFailureException]
TransientFailureException(this.message); TransientFailureException(String message) : super(message);
/// Initializes an instance of [TransientFailureException] with a [Response] /// Initializes an instance of [TransientFailureException] with a [Response]
TransientFailureException.httpRequest(BaseResponse response) TransientFailureException.httpRequest(BaseResponse response) : super('''
: message = '''
Failed to perform an HTTP request to YouTube due to a transient failure. 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. 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. To resolve this error, please wait some time and try again.
If this issue persists, please report it on the project's GitHub page. If this issue persists, please report it on the project's GitHub page.
Request: ${response.request} Request: ${response.request}
Response: $response 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'; import 'video_unplayable_exception.dart';
/// Exception thrown when the requested video requires purchase. /// Exception thrown when the requested video requires purchase.
class VideoRequiresPurchaseException implements VideoUnplayableException { class VideoRequiresPurchaseException extends VideoUnplayableException {
/// Description message
@override
final String message;
/// VideoId instance /// VideoId instance
final VideoId previewVideoId; final VideoId previewVideoId;
/// Initializes an instance of [VideoRequiresPurchaseException]. /// Initializes an instance of [VideoRequiresPurchaseException].
VideoRequiresPurchaseException.preview(VideoId videoId, this.previewVideoId) VideoRequiresPurchaseException.preview(VideoId videoId, this.previewVideoId)
: message = : super('Video `$videoId` is unplayable because it requires purchase.\n'
'Video `$videoId` is unplayable because it requires purchase.\n' 'Streams are not available for this video.\n'
'Streams are not available for this video.\n' 'There is a preview video available: `$previewVideoId`.');
'There is a preview video available: `$previewVideoId`.';
@override
String toString() =>
'$runtimeType: $message'; // ignore: no_runtimetype_tostring
} }

View File

@ -4,24 +4,16 @@ import 'exceptions.dart';
/// Thrown when a video is not available and cannot be processed. /// Thrown when a video is not available and cannot be processed.
/// This can happen because the video does not exist, is deleted, /// This can happen because the video does not exist, is deleted,
/// is private, or due to other reasons. /// is private, or due to other reasons.
class VideoUnavailableException implements VideoUnplayableException { class VideoUnavailableException extends VideoUnplayableException {
/// Description message
@override
final String message;
/// Initializes an instance of [VideoUnavailableException] /// Initializes an instance of [VideoUnavailableException]
VideoUnavailableException(this.message); VideoUnavailableException(String message) : super(message);
/// Initializes an instance of [VideoUnplayableException] with a [VideoId] /// Initializes an instance of [VideoUnplayableException] with a [VideoId]
VideoUnavailableException.unavailable(VideoId 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 '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' '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 '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 '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.'; 'Please report this issue on GitHub in that case.');
@override
String toString() =>
'$runtimeType: $message'; // ignore: no_runtimetype_tostring
} }

View File

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

View File

@ -3,6 +3,28 @@ abstract class YoutubeExplodeException implements Exception {
/// Generic message. /// Generic message.
final String 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); 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 { try {
var context = await _getStreamContextFromVideoInfo(videoId); var context = await _getStreamContextFromVideoInfo(videoId);
return _getManifest(context); return _getManifest(context);
} on YoutubeExplodeException { } on YoutubeExplodeException catch (e) {
var context = await _getStreamContextFromWatchPage(videoId); try {
return _getManifest(context); var context = await _getStreamContextFromWatchPage(videoId);
return _getManifest(context);
} on YoutubeExplodeException catch (e1) {
throw e..combine(e1);
}
} }
} }