From 7e01cfbb4c05a5e021a4bcc4d5300c75522a0d39 Mon Sep 17 00:00:00 2001 From: Hexah Date: Sun, 23 Feb 2020 21:00:35 +0100 Subject: [PATCH] Implement Custom Exceptions. Closes #7 --- CHANGELOG.md | 7 ++++++- lib/src/cipher/cipher.dart | 10 ++++++++-- lib/src/exceptions/exceptions.dart | 4 ++++ .../unrecognized_structure_exception.dart | 18 ++++++++++++++++++ .../video_requires_purchase_exception.dart | 12 ++++++++++++ .../video_unavailable_exception.dart | 14 ++++++++++++++ .../exceptions/video_unplayable_exception.dart | 13 +++++++++++++ lib/src/extensions/caption_extension.dart | 6 ++++-- lib/src/models/closed_caption_track_info.dart | 11 +++++------ lib/src/youtube_explode_base.dart | 5 +++-- lib/youtube_explode_dart.dart | 1 + pubspec.yaml | 2 +- 12 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 lib/src/exceptions/exceptions.dart create mode 100644 lib/src/exceptions/unrecognized_structure_exception.dart create mode 100644 lib/src/exceptions/video_requires_purchase_exception.dart create mode 100644 lib/src/exceptions/video_unavailable_exception.dart create mode 100644 lib/src/exceptions/video_unplayable_exception.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 84036a2..7b55cce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,4 +17,9 @@ ## 0.0.5 -- Implement Search Api (`SearchExtension`) \ No newline at end of file +- Implement Search Api (`SearchExtension`) + +## 0.0.6 + +- Implement Caption Api ('CaptionExtension`) +- Add Custom Exceptions \ No newline at end of file diff --git a/lib/src/cipher/cipher.dart b/lib/src/cipher/cipher.dart index 1dca045..3bf6dcb 100644 --- a/lib/src/cipher/cipher.dart +++ b/lib/src/cipher/cipher.dart @@ -1,6 +1,8 @@ library youtube_explode.cipher; import 'package:http/http.dart' as http; + +import '../exceptions/exceptions.dart'; import '../extensions/helpers_extension.dart'; import 'cipher_operations.dart'; @@ -27,7 +29,9 @@ Future> getCipherOperations( var deciphererFuncName = _deciphererFuncNameExp.firstMatch(raw)?.group(1); if (deciphererFuncName.isNullOrWhiteSpace) { - throw Exception('Could not find decipherer name.'); + throw UnrecognizedStructureException( + 'Could not find decipherer name. Please report this issue on GitHub.', + raw); } var exp = RegExp(r'(?!h\.)' @@ -35,7 +39,9 @@ Future> getCipherOperations( r'=function\(\w+\)\{(.*?)\}'); var decipherFuncBody = exp.firstMatch(raw)?.group(1); if (decipherFuncBody.isNullOrWhiteSpace) { - throw Exception('Could not find decipherer body.'); + throw UnrecognizedStructureException( + 'Could not find decipherer body. Please report this issue on GitHub.', + raw); } var deciphererFuncBodyStatements = decipherFuncBody.split(';'); diff --git a/lib/src/exceptions/exceptions.dart b/lib/src/exceptions/exceptions.dart new file mode 100644 index 0000000..f14a92f --- /dev/null +++ b/lib/src/exceptions/exceptions.dart @@ -0,0 +1,4 @@ +export 'unrecognized_structure_exception.dart'; +export 'video_requires_purchase_exception.dart'; +export 'video_unavailable_exception.dart'; +export 'video_unplayable_exception.dart'; \ No newline at end of file diff --git a/lib/src/exceptions/unrecognized_structure_exception.dart b/lib/src/exceptions/unrecognized_structure_exception.dart new file mode 100644 index 0000000..097ca21 --- /dev/null +++ b/lib/src/exceptions/unrecognized_structure_exception.dart @@ -0,0 +1,18 @@ +/// Thrown when YoutubeExplode fails to extract required information. +/// This usually happens when YouTube makes changes that break YoutubeExplode. +class UnrecognizedStructureException implements FormatException { + ///A message describing the format error. + @override + final String message; + + /// The actual source input which caused the error. + @override + final String source; + + /// Initializes an instance of [UnrecognizedStructureException] + const UnrecognizedStructureException([this.message, this.source]); + + /// Unimplemented + @override + int get offset => throw UnsupportedError('Offset not supported'); +} diff --git a/lib/src/exceptions/video_requires_purchase_exception.dart b/lib/src/exceptions/video_requires_purchase_exception.dart new file mode 100644 index 0000000..d069f99 --- /dev/null +++ b/lib/src/exceptions/video_requires_purchase_exception.dart @@ -0,0 +1,12 @@ +/// Thrown when a video is not playable because it requires purchase. +class VideoRequiresPurchaseException implements Exception { + /// ID of the preview video. + final String previewVideoId; + + /// Initializes an instance of [VideoRequiresPurchaseException] + const VideoRequiresPurchaseException(this.previewVideoId); + + @override + String toString() => 'VideoRequiresPurchaseException: The video ' + '$previewVideoId requires a purchase'; +} diff --git a/lib/src/exceptions/video_unavailable_exception.dart b/lib/src/exceptions/video_unavailable_exception.dart new file mode 100644 index 0000000..0cf57e7 --- /dev/null +++ b/lib/src/exceptions/video_unavailable_exception.dart @@ -0,0 +1,14 @@ +/// 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 Exception { + /// ID of the video. + final String videoId; + + /// Initializes an instance of [VideoUnavailableException] + const VideoUnavailableException(this.videoId); + + @override + String toString() => + 'VideoUnavailableException: Video $videoId is unavailable.'; +} diff --git a/lib/src/exceptions/video_unplayable_exception.dart b/lib/src/exceptions/video_unplayable_exception.dart new file mode 100644 index 0000000..eebab2d --- /dev/null +++ b/lib/src/exceptions/video_unplayable_exception.dart @@ -0,0 +1,13 @@ +/// Thrown when a video is not playable and its streams cannot be resolved. +/// This can happen because the video requires purchase, +/// is blocked in your country, is controversial, or due to other reasons. +class VideoUnplayableException { + /// ID of the video. + final String videoId; + + /// Initializes an instance of [VideoUnplayableException] + const VideoUnplayableException(this.videoId); + + String toString() => + 'VideoUnplayableException: Video $videoId couldn\'t be played'; +} diff --git a/lib/src/extensions/caption_extension.dart b/lib/src/extensions/caption_extension.dart index 6ff3668..9ccf1e0 100644 --- a/lib/src/extensions/caption_extension.dart +++ b/lib/src/extensions/caption_extension.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:xml/xml.dart' as xml; +import '../exceptions/exceptions.dart'; import '../models/models.dart'; import '../youtube_explode_base.dart'; import 'helpers_extension.dart'; @@ -22,7 +23,7 @@ extension CaptionExtension on YoutubeExplode { var playAbility = playerResponseJson['playabilityStatus']; if (playAbility['status'].toLowerCase() == 'error') { - throw Exception('Video [$videoId] is unavailable'); + throw VideoUnavailableException(videoId); } var trackInfos = []; @@ -41,7 +42,8 @@ extension CaptionExtension on YoutubeExplode { var isAutoGenerated = trackJson['vssId'].toLowerCase().startsWith('a.'); - trackInfos.add(ClosedCaptionTrackInfo(url, language, isAutoGenerated)); + trackInfos.add(ClosedCaptionTrackInfo(url, language, + isAutoGenerated: isAutoGenerated)); } return trackInfos; } diff --git a/lib/src/models/closed_caption_track_info.dart b/lib/src/models/closed_caption_track_info.dart index 2284265..c7db22f 100644 --- a/lib/src/models/closed_caption_track_info.dart +++ b/lib/src/models/closed_caption_track_info.dart @@ -2,16 +2,15 @@ import 'models.dart'; /// Metadata associated with a certain [ClosedCaptionTrack] class ClosedCaptionTrackInfo { - /// Manifest URL of the associated track. final Uri url; -/// Language of the associated track. + /// Language of the associated track. final Language language; -/// Whether the associated track was automatically generated. + /// Whether the associated track was automatically generated. final bool isAutoGenerated; -/// Initializes an instance of [ClosedCaptionTrackInfo] - const ClosedCaptionTrackInfo(this.url, this.language, this.isAutoGenerated); -} \ No newline at end of file + /// Initializes an instance of [ClosedCaptionTrackInfo] + const ClosedCaptionTrackInfo(this.url, this.language, {this.isAutoGenerated}); +} diff --git a/lib/src/youtube_explode_base.dart b/lib/src/youtube_explode_base.dart index 3f2342b..d852fb5 100644 --- a/lib/src/youtube_explode_base.dart +++ b/lib/src/youtube_explode_base.dart @@ -6,6 +6,7 @@ import 'package:http/http.dart' as http; import 'package:http_parser/http_parser.dart' show MediaType; import 'cipher/cipher.dart'; +import 'exceptions/exceptions.dart'; import 'extensions/extensions.dart'; import 'models/models.dart'; import 'parser.dart' as parser; @@ -212,7 +213,7 @@ class YoutubeExplode { var playAbility = playerResponseJson['playabilityStatus']; if (playAbility['status'].toString().toLowerCase() == 'error') { - throw Exception('Video [$videoId] is unavailable'); + throw VideoUnavailableException(videoId); } var errorReason = playAbility['reason'] as String; @@ -288,7 +289,7 @@ class YoutubeExplode { ?.toLowerCase() as String; if (status.isNullOrWhiteSpace || status == 'error') { - throw Exception('Video [$videoId] is unavailable'); + throw VideoUnavailableException(videoId); } var details = playerResponseJson['videoDetails']; diff --git a/lib/youtube_explode_dart.dart b/lib/youtube_explode_dart.dart index bb63240..80056a3 100644 --- a/lib/youtube_explode_dart.dart +++ b/lib/youtube_explode_dart.dart @@ -1,5 +1,6 @@ library youtube_explode; +export 'src/exceptions/exceptions.dart'; export 'src/extensions/extensions.dart' hide StringUtility, ListDecipher, ListFirst; // Hide helper extensions. export 'src/models/models.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index d17875a..eacf8a2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: youtube_explode_dart description: A port in dart of the youtube explode library. Support serveral API functions. -version: 0.0.5 +version: 0.0.6 homepage: https://github.com/Hexer10/youtube_explode_dart environment: