From 89c073fd51dc7ef79a3729266079e05f0b47fbfa Mon Sep 17 00:00:00 2001 From: Mattia Date: Thu, 15 Jul 2021 23:43:47 +0200 Subject: [PATCH] Better exception handling --- .../exceptions/fatal_failure_exception.dart | 17 +++---------- .../request_limit_exceeded_exception.dart | 17 +++---------- .../search_item_section_exception.dart | 6 ++--- .../transient_failure_exception.dart | 16 +++--------- .../video_requires_purchase_exception.dart | 17 +++---------- .../video_unavailable_exception.dart | 16 +++--------- .../video_unplayable_exception.dart | 25 ++++++------------- .../exceptions/youtube_explode_exception.dart | 22 ++++++++++++++++ lib/src/videos/streams/streams_client.dart | 10 +++++--- 9 files changed, 59 insertions(+), 87 deletions(-) diff --git a/lib/src/exceptions/fatal_failure_exception.dart b/lib/src/exceptions/fatal_failure_exception.dart index 82e3742..e23cae0 100644 --- a/lib/src/exceptions/fatal_failure_exception.dart +++ b/lib/src/exceptions/fatal_failure_exception.dart @@ -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 +'''); } diff --git a/lib/src/exceptions/request_limit_exceeded_exception.dart b/lib/src/exceptions/request_limit_exceeded_exception.dart index f3bcaa5..0aa5ab6 100644 --- a/lib/src/exceptions/request_limit_exceeded_exception.dart +++ b/lib/src/exceptions/request_limit_exceeded_exception.dart @@ -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 +'''); } diff --git a/lib/src/exceptions/search_item_section_exception.dart b/lib/src/exceptions/search_item_section_exception.dart index 0ffeb28..eb56a38 100644 --- a/lib/src/exceptions/search_item_section_exception.dart +++ b/lib/src/exceptions/search_item_section_exception.dart @@ -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.'); } diff --git a/lib/src/exceptions/transient_failure_exception.dart b/lib/src/exceptions/transient_failure_exception.dart index 1cc8356..975178c 100644 --- a/lib/src/exceptions/transient_failure_exception.dart +++ b/lib/src/exceptions/transient_failure_exception.dart @@ -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 +'''); } diff --git a/lib/src/exceptions/video_requires_purchase_exception.dart b/lib/src/exceptions/video_requires_purchase_exception.dart index edecc28..6dde72e 100644 --- a/lib/src/exceptions/video_requires_purchase_exception.dart +++ b/lib/src/exceptions/video_requires_purchase_exception.dart @@ -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`.'); } diff --git a/lib/src/exceptions/video_unavailable_exception.dart b/lib/src/exceptions/video_unavailable_exception.dart index bea276c..ac45270 100644 --- a/lib/src/exceptions/video_unavailable_exception.dart +++ b/lib/src/exceptions/video_unavailable_exception.dart @@ -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.'); } diff --git a/lib/src/exceptions/video_unplayable_exception.dart b/lib/src/exceptions/video_unplayable_exception.dart index c8b0174..0f22e20 100644 --- a/lib/src/exceptions/video_unplayable_exception.dart +++ b/lib/src/exceptions/video_unplayable_exception.dart @@ -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'); } diff --git a/lib/src/exceptions/youtube_explode_exception.dart b/lib/src/exceptions/youtube_explode_exception.dart index 618040f..fb18a9f 100644 --- a/lib/src/exceptions/youtube_explode_exception.dart +++ b/lib/src/exceptions/youtube_explode_exception.dart @@ -3,6 +3,28 @@ abstract class YoutubeExplodeException implements Exception { /// Generic message. final String message; + /// Addition exceptions thrown. + final List _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(); + } } diff --git a/lib/src/videos/streams/streams_client.dart b/lib/src/videos/streams/streams_client.dart index 83fd8de..9d71654 100644 --- a/lib/src/videos/streams/streams_client.dart +++ b/lib/src/videos/streams/streams_client.dart @@ -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); + } } }