diff --git a/CHANGELOG.md b/CHANGELOG.md index f5b1114..e3fbcff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,4 +65,8 @@ - Workaround (#15): Now when a video is not available a `VideoUnavailable` exception is thrown - Removed disable_polymer parameter when requests ( https://github.com/Tyrrrz/YoutubeExplode/issues/341 ) -- Removed `dart:io` dependency \ No newline at end of file +- Removed `dart:io` dependency + +## 0.0.16 + +- When a video is not available(403) a `VideoStreamUnavailableException` \ No newline at end of file diff --git a/README.md b/README.md index 2d97f75..a8b3545 100644 --- a/README.md +++ b/README.md @@ -119,4 +119,4 @@ More features are provided through extensions. [MediaStreamsInfoSet]: https://pub.dev/documentation/youtube_explode_dart/latest/youtube_explode/MediaStreamInfoSet-class.html [VidExample]: https://github.com/Hexer10/youtube_explode_dart/blob/master/example/video_download.dart [API]: https://pub.dev/documentation/youtube_explode_dart/latest/youtube_explode/youtube_explode-library.html -[Examples][https://github.com/Hexer10/youtube_explode_dart/tree/master/example] \ No newline at end of file +[Examples]: [https://github.com/Hexer10/youtube_explode_dart/tree/master/example] \ No newline at end of file diff --git a/lib/src/exceptions/exceptions.dart b/lib/src/exceptions/exceptions.dart index f730d40..c9e5c3d 100644 --- a/lib/src/exceptions/exceptions.dart +++ b/lib/src/exceptions/exceptions.dart @@ -2,5 +2,6 @@ library youtube_explode.exceptions; export 'unrecognized_structure_exception.dart'; export 'video_requires_purchase_exception.dart'; +export 'video_stream_unavailable_exception.dart'; export 'video_unavailable_exception.dart'; export 'video_unplayable_exception.dart'; diff --git a/lib/src/exceptions/video_stream_unavailable_exception.dart b/lib/src/exceptions/video_stream_unavailable_exception.dart new file mode 100644 index 0000000..a387f13 --- /dev/null +++ b/lib/src/exceptions/video_stream_unavailable_exception.dart @@ -0,0 +1,20 @@ + + +/// Thrown when a video stream is not available +/// and returns a status code not equal to 200 OK. +class VideoStreamUnavailableException implements Exception { + + /// The returned status code. + final int statusCode; + + /// Url + final Uri url; + + /// Initializes an instance of [VideoStreamUnavailableException] + VideoStreamUnavailableException(this.statusCode, this.url); + + @override + String toString() => 'VideoStreamUnavailableException: ' + 'The video stream in not availble (status code: $statusCode).\n' + 'Url: $url'; +} \ No newline at end of file diff --git a/lib/src/extensions/channel_extension.dart b/lib/src/extensions/channel_extension.dart index eb6b291..3e196ac 100644 --- a/lib/src/extensions/channel_extension.dart +++ b/lib/src/extensions/channel_extension.dart @@ -22,11 +22,11 @@ extension ChannelExtension on YoutubeExplode { channelId, 'channelId', 'Invalid YouTube channel id'); } - var channelPageHtml = await getChannelPage(channelId); - var channelTitle = channelPageHtml + var channelPage = await getChannelPage(channelId); + var channelTitle = channelPage .querySelector('meta[property="og:title"]') .attributes['content']; - var channelImage = channelPageHtml + var channelImage = channelPage .querySelector('meta[property="og:image"]') .attributes['content']; @@ -34,18 +34,20 @@ extension ChannelExtension on YoutubeExplode { } /// Get a channel id from a username. - /// Might not work properly. + /// Returns null if the username is not found. Future getChannelId(String username) async { if (!validateUsername(username)) { throw ArgumentError.value( username, 'username', 'Invalid YouTube username'); } - var userPageHtml = await _getUserPageHtml(username); + var userPage = await _getUserPage(username); + if (userPage == null) { + return null; + } - var channelUrl = userPageHtml - .querySelector('meta[property="og:url"]') - .attributes['content']; + var channelUrl = + userPage.querySelector('meta[property="og:url"]').attributes['content']; return channelUrl.replaceFirst('/channel/', ''); } @@ -85,11 +87,14 @@ extension ChannelExtension on YoutubeExplode { return html.parse(raw); } - Future _getUserPageHtml(String username) async { + Future _getUserPage(String username) async { var url = 'https://www.youtube.com/user/$username?hl=en'; - var raw = (await client.get(url)).body; + var req = await client.get(url); + if (req.statusCode != 200) { + return null; + } - return html.parse(raw); + return html.parse(req); } /// Returns true if [username] is a valid Youtube username. diff --git a/lib/src/extensions/download_extension.dart b/lib/src/extensions/download_extension.dart index ac419c1..7e9e8f5 100644 --- a/lib/src/extensions/download_extension.dart +++ b/lib/src/extensions/download_extension.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:http/http.dart' as http; +import '../exceptions/exceptions.dart'; import '../models/models.dart'; /// Download extension for [MediaStreamInfo] @@ -12,6 +13,11 @@ extension DownloadExtension on MediaStreamInfo { /// The download is split in multiple requests using the `range` parameter. /// Stream> downloadStream() async* { + var req = await http.head(url); + if (req.statusCode != 200) { + throw VideoStreamUnavailableException(req.statusCode, url); + } + var maxSize = _rateBypassExp.hasMatch(url.toString()) ? 9898989 : size + 1; var total = 0; diff --git a/lib/src/extensions/helpers_extension.dart b/lib/src/extensions/helpers_extension.dart index 2a704da..be74c5a 100644 --- a/lib/src/extensions/helpers_extension.dart +++ b/lib/src/extensions/helpers_extension.dart @@ -21,7 +21,7 @@ extension StringUtility on String { /// List decipher utility. extension ListDecipher on List { - /// Apply the every CipherOperation on the [signature] + /// Apply every CipherOperation on the [signature] String decipher(String signature) { for (var operation in this) { signature = operation.decipher(signature); diff --git a/lib/src/youtube_explode_base.dart b/lib/src/youtube_explode_base.dart index 100ee55..5d2756b 100644 --- a/lib/src/youtube_explode_base.dart +++ b/lib/src/youtube_explode_base.dart @@ -203,8 +203,7 @@ class YoutubeExplode { } Future _getPlayerConfigEmbed(String videoId) async { - var req = await client.get( - 'https://www.youtube.com/embed/$videoId?&hl=en'); + var req = await client.get('https://www.youtube.com/embed/$videoId?&hl=en'); if (req.statusCode != 200) { return null; } @@ -297,17 +296,17 @@ class YoutubeExplode { } Future _getPlayerConfigWatchPage(String videoId) async { - var videoWatchPageHtml = await getVideoWatchPage(videoId); - if (videoWatchPageHtml == null) { + var videoWatchPage = await getVideoWatchPage(videoId); + if (videoWatchPage == null) { return null; } - var playerConfigScript = videoWatchPageHtml + var playerConfigScript = videoWatchPage .querySelectorAll('script') .map((e) => e.text) .firstWhere((e) => e.contains('ytplayer.config =')); if (playerConfigScript == null) { var errorReason = - videoWatchPageHtml.querySelector('#unavailable-message').text.trim(); + videoWatchPage.querySelector('#unavailable-message').text.trim(); throw VideoUnplayableException(videoId, errorReason); } @@ -458,8 +457,7 @@ class YoutubeExplode { /// Returns the video watch page document. Future getVideoWatchPage(String videoId) async { - var url = - 'https://youtube.com/watch?v=$videoId&bpctr=9999999999&hl=en'; + var url = 'https://youtube.com/watch?v=$videoId&bpctr=9999999999&hl=en'; var req = await client.get(url); if (req.statusCode != 200) { return null; diff --git a/pubspec.yaml b/pubspec.yaml index 5dbff9f..d2c30f4 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. Supports several API functions. -version: 0.0.15 +version: 0.0.16 homepage: https://github.com/Hexer10/youtube_explode_dart environment: