From d3316ca220368b752131fb9d20c5b63dca6bbefa Mon Sep 17 00:00:00 2001 From: Mattia Date: Mon, 4 Oct 2021 13:00:22 +0200 Subject: [PATCH] Implement .describe() and aliases. --- CHANGELOG.md | 5 ++ analysis_options.yaml | 1 + .../clients/embedded_player_client.dart | 16 +++-- .../reverse_engineering/dash_manifest.dart | 26 +++++--- .../models/stream_info_provider.dart | 8 +++ .../player/player_response.dart | 16 +++-- .../streams/audio_only_stream_info.dart | 9 ++- lib/src/videos/streams/audio_stream_info.dart | 17 ++++- lib/src/videos/streams/framerate.dart | 2 +- lib/src/videos/streams/muxed_stream_info.dart | 15 ++++- lib/src/videos/streams/stream_info.dart | 65 ++++++++++++++++++- lib/src/videos/streams/stream_manifest.dart | 20 ++++-- lib/src/videos/streams/streams_client.dart | 65 +++++-------------- .../streams/video_only_stream_info.dart | 22 ++++++- lib/src/videos/streams/video_resolution.dart | 18 ++++- lib/src/videos/streams/video_stream_info.dart | 12 +++- lib/src/videos/video_client.dart | 10 ++- lib/src/youtube_explode_base.dart | 17 ++--- pubspec.yaml | 12 ++-- test/channel_test.dart | 2 +- test/streams_test.dart | 14 +++- 21 files changed, 261 insertions(+), 111 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e168484..388fb50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.10.8 +- Added the following aliases: yt.videos.streams (instead of yt.videos.streamsClient) and yt.videos.comments (instead of yt.videos.commentsClient) +- Re-add more test cases. +- Implement `.describe()` on List which prints a formatted list like `youtube-dl -F` option. + ## 1.10.7+1 - Fix tests. - Remove debug leftovers. diff --git a/analysis_options.yaml b/analysis_options.yaml index a98ed1a..ef2736e 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -13,3 +13,4 @@ linter: avoid_escaping_inner_quotes: false prefer_const_constructors: true avoid_positional_boolean_parameters: false + require_trailing_commas: false diff --git a/lib/src/reverse_engineering/clients/embedded_player_client.dart b/lib/src/reverse_engineering/clients/embedded_player_client.dart index 3e4fe7c..b32b74f 100644 --- a/lib/src/reverse_engineering/clients/embedded_player_client.dart +++ b/lib/src/reverse_engineering/clients/embedded_player_client.dart @@ -92,7 +92,7 @@ class _StreamInfo extends StreamInfoProvider { late final int? bitrate = root.getT('bitrate'); @override - late final String? container = mimeType?.subtype; + late final String container = codec.subtype; @override late final int? contentLength = int.tryParse( @@ -129,14 +129,20 @@ class _StreamInfo extends StreamInfoProvider { late final int? videoHeight = root.getT('height'); @override - late final String? videoQualityLabel = root.getT('qualityLabel'); + @Deprecated('Use qualityLabel') + String get videoQualityLabel => qualityLabel; + + @override + late final String qualityLabel = root.getT('qualityLabel') ?? + 'tiny'; // Not sure if 'tiny' is the correct placeholder. @override late final int? videoWidth = root.getT('width'); - late final bool isAudioOnly = mimeType?.type == 'audio'; + late final bool isAudioOnly = codec.type == 'audio'; - late final MediaType? mimeType = _getMimeType(); + @override + late final MediaType codec = _getMimeType()!; MediaType? _getMimeType() { var mime = root.getT('mimeType'); @@ -146,7 +152,7 @@ class _StreamInfo extends StreamInfoProvider { return MediaType.parse(mime); } - late final String? codecs = mimeType?.parameters['codecs']?.toLowerCase(); + late final String? codecs = codec.parameters['codecs']?.toLowerCase(); @override late final String? audioCodec = diff --git a/lib/src/reverse_engineering/dash_manifest.dart b/lib/src/reverse_engineering/dash_manifest.dart index cf982c2..8ee630f 100644 --- a/lib/src/reverse_engineering/dash_manifest.dart +++ b/lib/src/reverse_engineering/dash_manifest.dart @@ -204,17 +204,25 @@ class _StreamInfo extends StreamInfoProvider { final String url; @override - String get container => _mimetype.subtype; - - final MediaType _mimetype; - - bool get isAudioOnly => _mimetype.type == 'audio'; + final MediaType codec; @override - String? get audioCodec => isAudioOnly ? _mimetype.subtype : null; + String get container => codec.subtype; + + bool get isAudioOnly => codec.type == 'audio'; @override - String? get videoCodec => isAudioOnly ? null : _mimetype.subtype; + String? get audioCodec => isAudioOnly ? codec.subtype : null; + + @override + String? get videoCodec => isAudioOnly ? null : codec.subtype; + + @override + @Deprecated('Use qualityLabel') + String get videoQualityLabel => qualityLabel; + + @override + late final String qualityLabel = 'DASH'; @override final int? videoWidth; @@ -231,8 +239,8 @@ class _StreamInfo extends StreamInfoProvider { @override StreamSource get source => StreamSource.dash; - _StreamInfo(this.tag, this.url, this._mimetype, this.videoWidth, - this.videoHeight, this.framerate, this.fragments); + _StreamInfo(this.tag, this.url, this.codec, this.videoWidth, this.videoHeight, + this.framerate, this.fragments); } class _SegmentTimeline { diff --git a/lib/src/reverse_engineering/models/stream_info_provider.dart b/lib/src/reverse_engineering/models/stream_info_provider.dart index e6ab29c..6f4f02a 100644 --- a/lib/src/reverse_engineering/models/stream_info_provider.dart +++ b/lib/src/reverse_engineering/models/stream_info_provider.dart @@ -1,3 +1,5 @@ +import 'package:http_parser/http_parser.dart'; + import 'fragment.dart'; enum StreamSource { muxed, adaptive, dash } @@ -16,6 +18,8 @@ abstract class StreamInfoProvider { /// String get url; + MediaType get codec; + /// String? get signature => null; @@ -38,8 +42,12 @@ abstract class StreamInfoProvider { String? get videoCodec => null; /// + @Deprecated('Use qualityLabel') String? get videoQualityLabel => null; + /// + String get qualityLabel; + /// int? get videoWidth => null; diff --git a/lib/src/reverse_engineering/player/player_response.dart b/lib/src/reverse_engineering/player/player_response.dart index 2cd9880..4a33687 100644 --- a/lib/src/reverse_engineering/player/player_response.dart +++ b/lib/src/reverse_engineering/player/player_response.dart @@ -169,7 +169,7 @@ class _StreamInfo extends StreamInfoProvider { late final int? bitrate = root.getT('bitrate'); @override - late final String? container = mimeType?.subtype; + late final String container = codec.subtype; @override late final int? contentLength = int.tryParse( @@ -206,14 +206,20 @@ class _StreamInfo extends StreamInfoProvider { late final int? videoHeight = root.getT('height'); @override - late final String? videoQualityLabel = root.getT('qualityLabel'); + @Deprecated('Use qualityLabel') + String get videoQualityLabel => qualityLabel; + + @override + late final String qualityLabel = root.getT('qualityLabel') ?? + 'tiny'; // Not sure if 'tiny' is the correct placeholder. @override late final int? videoWidth = root.getT('width'); - late final bool isAudioOnly = mimeType?.type == 'audio'; + late final bool isAudioOnly = codec.type == 'audio'; - late final MediaType? mimeType = _getMimeType(); + @override + late final MediaType codec = _getMimeType()!; MediaType? _getMimeType() { var mime = root.getT('mimeType'); @@ -223,7 +229,7 @@ class _StreamInfo extends StreamInfoProvider { return MediaType.parse(mime); } - late final String? codecs = mimeType?.parameters['codecs']?.toLowerCase(); + late final String? codecs = codec.parameters['codecs']?.toLowerCase(); @override late final String? audioCodec = diff --git a/lib/src/videos/streams/audio_only_stream_info.dart b/lib/src/videos/streams/audio_only_stream_info.dart index 956995a..15f79a6 100644 --- a/lib/src/videos/streams/audio_only_stream_info.dart +++ b/lib/src/videos/streams/audio_only_stream_info.dart @@ -1,3 +1,5 @@ +import 'package:http_parser/http_parser.dart'; + import '../../reverse_engineering/models/fragment.dart'; import 'streams.dart'; @@ -10,8 +12,11 @@ class AudioOnlyStreamInfo extends AudioStreamInfo { FileSize size, Bitrate bitrate, String audioCodec, - List fragments) - : super(tag, url, container, size, bitrate, audioCodec, fragments); + List fragments, + MediaType codec, + String qualityLabel) + : super(tag, url, container, size, bitrate, audioCodec, fragments, codec, + qualityLabel); @override String toString() => 'Audio-only ($tag | $container)'; diff --git a/lib/src/videos/streams/audio_stream_info.dart b/lib/src/videos/streams/audio_stream_info.dart index 43d7369..b9334c1 100644 --- a/lib/src/videos/streams/audio_stream_info.dart +++ b/lib/src/videos/streams/audio_stream_info.dart @@ -1,3 +1,5 @@ +import 'package:http_parser/http_parser.dart'; + import '../../reverse_engineering/models/fragment.dart'; import 'streams.dart'; @@ -7,7 +9,16 @@ abstract class AudioStreamInfo extends StreamInfo { final String audioCodec; /// - AudioStreamInfo(int tag, Uri url, StreamContainer container, FileSize size, - Bitrate bitrate, this.audioCodec, List fragments) - : super(tag, url, container, size, bitrate, fragments); + AudioStreamInfo( + int tag, + Uri url, + StreamContainer container, + FileSize size, + Bitrate bitrate, + this.audioCodec, + List fragments, + MediaType codec, + String qualityLabel) + : super( + tag, url, container, size, bitrate, fragments, codec, qualityLabel); } diff --git a/lib/src/videos/streams/framerate.dart b/lib/src/videos/streams/framerate.dart index 5ee9777..675962a 100644 --- a/lib/src/videos/streams/framerate.dart +++ b/lib/src/videos/streams/framerate.dart @@ -20,7 +20,7 @@ class Framerate with Comparable, _$Framerate { bool operator <(Framerate other) => framesPerSecond < other.framesPerSecond; @override - String toString() => '$framesPerSecond FPS'; + String toString() => '${framesPerSecond}fps'; @override int compareTo(Framerate other) => diff --git a/lib/src/videos/streams/muxed_stream_info.dart b/lib/src/videos/streams/muxed_stream_info.dart index 91c8e1e..0929078 100644 --- a/lib/src/videos/streams/muxed_stream_info.dart +++ b/lib/src/videos/streams/muxed_stream_info.dart @@ -1,3 +1,5 @@ +import 'package:http_parser/src/media_type.dart'; + import '../../reverse_engineering/models/fragment.dart'; import 'audio_stream_info.dart'; import 'bitrate.dart'; @@ -32,6 +34,7 @@ class MuxedStreamInfo implements AudioStreamInfo, VideoStreamInfo { final String videoCodec; /// Video quality label, as seen on YouTube. + @Deprecated('Use qualityLabel') @override final String videoQualityLabel; @@ -51,6 +54,14 @@ class MuxedStreamInfo implements AudioStreamInfo, VideoStreamInfo { @override List get fragments => const []; + /// Stream codec. + @override + final MediaType codec; + + /// Stream codec. + @override + final String qualityLabel; + /// Initializes an instance of [MuxedStreamInfo] MuxedStreamInfo( this.tag, @@ -63,7 +74,9 @@ class MuxedStreamInfo implements AudioStreamInfo, VideoStreamInfo { this.videoQualityLabel, this.videoQuality, this.videoResolution, - this.framerate); + this.framerate, + this.codec, + this.qualityLabel); @override String toString() => 'Muxed ($tag | $videoQualityLabel | $container)'; diff --git a/lib/src/videos/streams/stream_info.dart b/lib/src/videos/streams/stream_info.dart index 0dc7ddd..f42dc1c 100644 --- a/lib/src/videos/streams/stream_info.dart +++ b/lib/src/videos/streams/stream_info.dart @@ -1,4 +1,7 @@ +import 'package:http_parser/http_parser.dart'; + import '../../reverse_engineering/models/fragment.dart'; +import '../videos.dart'; import 'bitrate.dart'; import 'filesize.dart'; import 'stream_container.dart'; @@ -24,9 +27,15 @@ abstract class StreamInfo { /// DASH streams contain multiple stream fragments. final List fragments; + /// Streams codec. + final MediaType codec; + + /// Stream quality label. + final String qualityLabel; + /// Initialize an instance of [StreamInfo]. StreamInfo(this.tag, this.url, this.container, this.size, this.bitrate, - this.fragments); + this.fragments, this.codec, this.qualityLabel); } /// Extension for Iterables of StreamInfo. @@ -38,4 +47,58 @@ extension StreamInfoIterableExt on Iterable { /// This returns new list without editing the original list. List sortByBitrate() => toList()..sort((a, b) => a.bitrate.compareTo(b.bitrate)); + + /// Print a formatted text of all the streams. Like youtube-dl -F option. + String describe() { + final column = _Column(['format code', 'extension', 'resolution', 'note']); + for (final e in this) { + column.write([ + e.tag, + e.container.name, + if (e is VideoStreamInfo) e.videoResolution else 'audio only', + e.qualityLabel, + e.bitrate, + e.codec.parameters['codecs'], + if (e is VideoStreamInfo) e.framerate, + if (e is VideoStreamInfo) 'video only', + e.size + ]); + } + return column.toString(); + } +} + +/// Utility for [StreamInfoIterableExt.describe] +class _Column { + final List header; + final List> _values = []; + + _Column(this.header); + + void write(List value) => _values + .add(value.where((e) => e != null).map((e) => e.toString()).toList()); + + @override + String toString() { + final headerLen = []; + final buffer = StringBuffer(); + for (final e in header) { + headerLen.add(e.length + 2); + buffer.write('$e '); + } + buffer.writeln(); + + for (final valueList in _values) { + for (var i = 0; i < valueList.length; i++) { + final v = valueList[i]; + if (headerLen.length <= i) { + buffer.write(', $v'); + continue; + } + buffer.write(v.padRight(headerLen[i])); + } + buffer.writeln(); + } + return buffer.toString(); + } } diff --git a/lib/src/videos/streams/stream_manifest.dart b/lib/src/videos/streams/stream_manifest.dart index d82fb37..51c50ec 100644 --- a/lib/src/videos/streams/stream_manifest.dart +++ b/lib/src/videos/streams/stream_manifest.dart @@ -16,22 +16,28 @@ class StreamManifest { /// Gets streams that contain audio /// (which includes muxed and audio-only streams). - Iterable get audio => streams.whereType(); + late final UnmodifiableListView audio = + UnmodifiableListView(streams.whereType()); /// Gets streams that contain video /// (which includes muxed and video-only streams). - Iterable get video => streams.whereType(); + late final UnmodifiableListView video = + UnmodifiableListView(streams.whereType()); /// Gets muxed streams (contain both audio and video). /// Note that muxed streams are limited in quality and don't go beyond 720p30. - Iterable get muxed => streams.whereType(); + late final UnmodifiableListView muxed = + UnmodifiableListView(streams.whereType()); /// Gets audio-only streams (no video). - Iterable get audioOnly => - streams.whereType(); + late final UnmodifiableListView audioOnly = + UnmodifiableListView(streams.whereType()); /// Gets video-only streams (no audio). /// These streams have the widest range of qualities available. - Iterable get videoOnly => - streams.whereType(); + late final UnmodifiableListView videoOnly = + UnmodifiableListView(streams.whereType()); + + @override + String toString() => streams.describe(); } diff --git a/lib/src/videos/streams/streams_client.dart b/lib/src/videos/streams/streams_client.dart index 72e5bb4..2d5b1ca 100644 --- a/lib/src/videos/streams/streams_client.dart +++ b/lib/src/videos/streams/streams_client.dart @@ -36,51 +36,6 @@ class StreamsClient { return DashManifest.get(_httpClient, dashManifestUrl); } - // Not used anymore since Youtube removed the `video_info` endpoint. -/* Future _getStreamContextFromVideoInfo(VideoId videoId) async { - var embedPage = await EmbedPage.get(_httpClient, videoId.toString()); - var playerConfig = embedPage.playerConfig; - if (playerConfig == null) { - throw VideoUnplayableException.unplayable(videoId); - } - - var playerSource = await PlayerSource.get( - _httpClient, embedPage.sourceUrl ?? playerConfig.sourceUrl); - var cipherOperations = playerSource.getCipherOperations(); - - var videoInfoResponse = await VideoInfoClient.get( - _httpClient, videoId.toString(), playerSource.sts); - var playerResponse = videoInfoResponse.playerResponse; - - var previewVideoId = playerResponse.previewVideoId; - if (!previewVideoId.isNullOrWhiteSpace) { - throw VideoRequiresPurchaseException.preview( - videoId, VideoId(previewVideoId!)); - } - - if (!playerResponse.isVideoPlayable) { - throw VideoUnplayableException.unplayable(videoId, - reason: playerResponse.videoPlayabilityError ?? ''); - } - - if (playerResponse.isLive) { - throw VideoUnplayableException.liveStream(videoId); - } - - var streamInfoProviders = [ - ...videoInfoResponse.streams, - ...playerResponse.streams - ]; - - var dashManifestUrl = playerResponse.dashManifestUrl; - if (!dashManifestUrl.isNullOrWhiteSpace) { - var dashManifest = - await _getDashManifest(Uri.parse(dashManifestUrl!), cipherOperations); - streamInfoProviders.addAll(dashManifest.streams); - } - return StreamContext(streamInfoProviders, cipherOperations); - }*/ - Future _getStreamContextFromEmbeddedClient( VideoId videoId) async { final page = await EmbeddedPlayerClient.get(_httpClient, videoId.value); @@ -196,7 +151,9 @@ class StreamsClient { videoQualityLabel, videoQuality, videoResolution, - framerate); + framerate, + streamInfo.codec, + streamInfo.qualityLabel); continue; } @@ -212,13 +169,23 @@ class StreamsClient { videoQuality, videoResolution, framerate, - streamInfo.fragments ?? const []); + streamInfo.fragments ?? const [], + streamInfo.codec, + streamInfo.qualityLabel); continue; } // Audio-only if (!audioCodec.isNullOrWhiteSpace) { - streams[tag] = AudioOnlyStreamInfo(tag, url, container, fileSize, - bitrate, audioCodec!, streamInfo.fragments ?? const []); + streams[tag] = AudioOnlyStreamInfo( + tag, + url, + container, + fileSize, + bitrate, + audioCodec!, + streamInfo.fragments ?? const [], + streamInfo.codec, + streamInfo.qualityLabel); } // #if DEBUG diff --git a/lib/src/videos/streams/video_only_stream_info.dart b/lib/src/videos/streams/video_only_stream_info.dart index 9c403e8..5fe3e11 100644 --- a/lib/src/videos/streams/video_only_stream_info.dart +++ b/lib/src/videos/streams/video_only_stream_info.dart @@ -1,3 +1,5 @@ +import 'package:http_parser/http_parser.dart'; + import '../../reverse_engineering/models/fragment.dart'; import 'bitrate.dart'; import 'filesize.dart'; @@ -20,9 +22,23 @@ class VideoOnlyStreamInfo extends VideoStreamInfo { VideoQuality videoQuality, VideoResolution videoResolution, Framerate framerate, - List fragments) - : super(tag, url, container, size, bitrate, videoCodec, videoQualityLabel, - videoQuality, videoResolution, framerate, fragments); + List fragments, + MediaType codec, + String qualityLabel) + : super( + tag, + url, + container, + size, + bitrate, + videoCodec, + videoQualityLabel, + videoQuality, + videoResolution, + framerate, + fragments, + codec, + qualityLabel); @override String toString() => 'Video-only ($tag | $videoResolution | $container)'; diff --git a/lib/src/videos/streams/video_resolution.dart b/lib/src/videos/streams/video_resolution.dart index ef551ad..b5a968f 100644 --- a/lib/src/videos/streams/video_resolution.dart +++ b/lib/src/videos/streams/video_resolution.dart @@ -1,5 +1,5 @@ /// Width and height of a video. -class VideoResolution { +class VideoResolution implements Comparable { /// Viewport width. final int width; @@ -11,4 +11,20 @@ class VideoResolution { @override String toString() => '${width}x$height'; + + @override + int compareTo(VideoResolution other) { + if (width == other.width && height == other.height) { + return 0; + } + + if (width > other.width) { + return 1; + } + + if (width == other.width && height > other.height) { + return 1; + } + return -1; + } } diff --git a/lib/src/videos/streams/video_stream_info.dart b/lib/src/videos/streams/video_stream_info.dart index 03ae5d7..6487109 100644 --- a/lib/src/videos/streams/video_stream_info.dart +++ b/lib/src/videos/streams/video_stream_info.dart @@ -1,3 +1,5 @@ +import 'package:http_parser/http_parser.dart'; + import '../../reverse_engineering/models/fragment.dart'; import 'streams.dart'; @@ -7,6 +9,7 @@ abstract class VideoStreamInfo extends StreamInfo { final String videoCodec; /// Video quality label, as seen on YouTube. + @Deprecated('Use qualityLabel') final String videoQualityLabel; /// Video quality. @@ -30,8 +33,11 @@ abstract class VideoStreamInfo extends StreamInfo { this.videoQuality, this.videoResolution, this.framerate, - List fragments) - : super(tag, url, container, size, bitrate, fragments); + List fragments, + MediaType codec, + String qualityLabel) + : super( + tag, url, container, size, bitrate, fragments, codec, qualityLabel); } /// Extensions for Iterables of [VideoStreamInfo] @@ -55,5 +61,5 @@ extension VideoStreamInfoExtension on Iterable { /// This returns new list without editing the original list. List sortByVideoQuality() => toList() ..sort((a, b) => b.framerate.compareTo(a.framerate)) - ..sort((a, b) => b.videoQuality.index.compareTo(a.videoQuality.index)); + ..sort((a, b) => b.videoResolution.compareTo(a.videoResolution)); } diff --git a/lib/src/videos/video_client.dart b/lib/src/videos/video_client.dart index be06b7e..10ed37e 100644 --- a/lib/src/videos/video_client.dart +++ b/lib/src/videos/video_client.dart @@ -14,12 +14,20 @@ class VideoClient { /// Queries related to media streams of YouTube videos. final StreamsClient streamsClient; + /// Queries related to media streams of YouTube videos. + /// Alias of [streamsClient]. + StreamsClient get streams => streamsClient; + /// Queries related to closed captions of YouTube videos. final ClosedCaptionClient closedCaptions; - /// Queries related to a YouTube video. + /// Queries related to a YouTube video comments. final CommentsClient commentsClient; + /// Queries related to a YouTube video comments. + /// Alias of [commentsClient]. + CommentsClient get comments => commentsClient; + /// Initializes an instance of [VideoClient]. VideoClient(this._httpClient) : streamsClient = StreamsClient(_httpClient), diff --git a/lib/src/youtube_explode_base.dart b/lib/src/youtube_explode_base.dart index 666a6d9..ae5144f 100644 --- a/lib/src/youtube_explode_base.dart +++ b/lib/src/youtube_explode_base.dart @@ -1,7 +1,5 @@ library youtube_explode.base; -import 'package:meta/meta.dart'; - import 'channels/channels.dart'; import 'playlists/playlist_client.dart'; import 'reverse_engineering/youtube_http_client.dart'; @@ -10,8 +8,7 @@ import 'videos/video_client.dart'; /// Library entry point. class YoutubeExplode { - @visibleForTesting - final YoutubeHttpClient httpClient; + final YoutubeHttpClient _httpClient; /// Queries related to YouTube videos. late final VideoClient videos; @@ -27,14 +24,14 @@ class YoutubeExplode { /// Initializes an instance of [YoutubeClient]. YoutubeExplode([YoutubeHttpClient? httpClient]) - : httpClient = httpClient ?? YoutubeHttpClient() { - videos = VideoClient(this.httpClient); - playlists = PlaylistClient(this.httpClient); - channels = ChannelClient(this.httpClient); - search = SearchClient(this.httpClient); + : _httpClient = httpClient ?? YoutubeHttpClient() { + videos = VideoClient(_httpClient); + playlists = PlaylistClient(_httpClient); + channels = ChannelClient(_httpClient); + search = SearchClient(_httpClient); } /// Closes the HttpClient assigned to this [YoutubeHttpClient]. /// Should be called after this is not used anymore. - void close() => httpClient.close(); + void close() => _httpClient.close(); } diff --git a/pubspec.yaml b/pubspec.yaml index 81daddb..9fbc72c 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 without the need of Youtube API Key. -version: 1.10.7+1 +version: 1.10.8 homepage: https://github.com/Hexer10/youtube_explode_dart @@ -18,10 +18,10 @@ dependencies: xml: ^5.0.2 dev_dependencies: - build_runner: ^2.0.6 + build_runner: ^2.1.4 console: ^4.1.0 - freezed: ^0.14.3 + freezed: ^0.14.5 grinder: ^0.9.0 - json_serializable: ^5.0.0 - lint: ^1.5.3 - test: ^1.17.10 + json_serializable: ^5.0.2 + lint: ^1.7.2 + test: ^1.18.2 diff --git a/test/channel_test.dart b/test/channel_test.dart index eb1d5ae..4efa640 100644 --- a/test/channel_test.dart +++ b/test/channel_test.dart @@ -50,7 +50,7 @@ void main() { .getUploads(ChannelId( 'https://www.youtube.com/channel/UCEnBXANsKmyj2r9xVyKoDiQ')) .toList(); - expect(videos.length, greaterThanOrEqualTo(79)); + expect(videos.length, greaterThanOrEqualTo(75)); }); group('Get the videos of any youtube channel', () { diff --git a/test/streams_test.dart b/test/streams_test.dart index ca468c1..d3cb3f3 100644 --- a/test/streams_test.dart +++ b/test/streams_test.dart @@ -42,9 +42,7 @@ void main() { test('Stream of age-limited video throws VideoUnplayableException', () { expect(yt!.videos.streamsClient.getManifest(VideoId('SkRSXFQerZs')), - throwsA(const TypeMatcher()), - skip: - 'Seems that this is not consistent with the CI - There is can retrieve a StreamManifest.'); + throwsA(const TypeMatcher())); }); test('Get the hls manifest of a live stream', () async { expect( @@ -65,8 +63,18 @@ void main() { group('Get specific stream of any playable video', () { for (final val in { + VideoId('9bZkp7q19f0'), //Normal + VideoId('rsAAeyAr-9Y'), //LiveStreamRecording + VideoId('V5Fsj_sCKdg'), //ContainsHighQualityStreams VideoId('AI7ULzgf8RU'), //ContainsDashManifest + VideoId('-xNN-bJQ4vI'), //Omnidirectional + VideoId('vX2vsvdq8nw'), //HighDynamicRange + VideoId('YltHGKX80Y8'), //ContainsClosedCaptions + VideoId('_kmeFXjjGfk'), //EmbedRestrictedByYouTube + VideoId('MeJVWBSsPAY'), //EmbedRestrictedByAuthor + VideoId('hySoCSoH-g8'), //AgeRestrictedEmbedRestricted VideoId('5VGm0dczmHc'), //RatingDisabled + VideoId('-xNN-bJQ4vI'), // 360° video }) { test('VideoId - ${val.value}', () async { var manifest = await yt!.videos.streamsClient.getManifest(val);