From c7bbbf0d24c7dc11bf47aa7ac5af40424a3a731b Mon Sep 17 00:00:00 2001 From: Hexah Date: Wed, 3 Jun 2020 23:02:21 +0200 Subject: [PATCH] Working v5 --- example/example.dart | 6 +- lib/src/{channel => channels}/channel.dart | 0 .../{channel => channels}/channel_client.dart | 5 +- lib/src/{channel => channels}/channel_id.dart | 0 lib/src/channels/channels.dart | 6 + lib/src/{channel => channels}/username.dart | 0 lib/src/common/common.dart | 4 + .../engagement.dart} | 4 +- lib/src/{models => common}/thumbnail_set.dart | 0 .../exceptions/fatal_failure_exception.dart | 2 +- .../request_limit_exceeded_exception.dart | 9 +- .../transient_failure_exception.dart | 2 +- .../video_unavailable_exception.dart | 2 +- .../exceptions/youtube_explode_exception.dart | 1 + lib/src/extensions/caption_extension.dart | 102 ------ lib/src/extensions/channel_extension.dart | 159 ---------- lib/src/extensions/download_extension.dart | 32 -- lib/src/extensions/extensions.dart | 8 - lib/src/extensions/helpers_extension.dart | 2 + lib/src/extensions/playlist_extension.dart | 156 ---------- lib/src/extensions/search_extension.dart | 58 ---- lib/src/models/channel.dart | 19 -- .../closed_captions/closed_caption.dart | 30 -- .../closed_captions/closed_caption_track.dart | 18 -- .../models/media_streams/audio_encoding.dart | 11 - .../media_streams/audio_stream_info.dart | 18 -- lib/src/models/media_streams/container.dart | 11 - .../media_streams/media_stream_info.dart | 27 -- .../media_streams/media_stream_info_set.dart | 34 -- .../media_streams/muxed_stream_info.dart | 43 --- .../models/media_streams/video_encoding.dart | 21 -- .../models/media_streams/video_quality.dart | 35 --- .../media_streams/video_resolution.dart | 19 -- .../media_streams/video_stream_info.dart | 47 --- lib/src/models/models.dart | 24 -- lib/src/models/player_configuration.dart | 58 ---- lib/src/models/playlist.dart | 35 --- lib/src/models/playlist_type.dart | 30 -- lib/src/models/video.dart | 62 ---- lib/src/models/video_id.dart | 70 ----- lib/src/parser.dart | 290 ------------------ lib/src/playlists/playlist.dart | 29 ++ lib/src/playlists/playlist_client.dart | 64 ++++ lib/src/playlists/playlist_id.dart | 87 ++++++ lib/src/playlists/playlists.dart | 5 + lib/src/retry.dart | 2 +- .../reverse_engineering/cipher/cipher.dart | 113 ------- lib/src/reverse_engineering/heuristics.dart | 2 +- .../responses/channel_page.dart | 10 +- .../closed_caption_track_response.dart | 10 +- .../responses/dash_manifest.dart | 2 +- .../responses/embed_page.dart | 6 +- .../responses/player_response.dart | 15 +- .../responses/player_source.dart | 14 +- .../responses/playerlist_response.dart | 14 +- .../responses/responses.dart | 4 +- .../responses/video_info_response.dart | 15 +- .../responses/watch_page.dart | 18 +- .../reverse_engineering.dart | 4 - .../youtube_http_client.dart | 42 ++- .../closed_captions/closed_caption.dart | 32 ++ .../closed_caption_client.dart | 45 +++ .../closed_caption_manifest.dart | 25 ++ .../closed_captions/closed_caption_part.dart | 15 + .../closed_captions/closed_caption_track.dart | 14 + .../closed_caption_track_info.dart | 5 +- .../closed_captions/closed_captions.dart | 6 + .../closed_captions/language.dart | 0 lib/src/videos/streams/stream_context.dart | 3 +- lib/src/videos/streams/streams.dart | 4 +- ...stream_client.dart => streams_client.dart} | 20 +- lib/src/videos/video.dart | 54 ++++ lib/src/videos/video_client.dart | 40 +++ lib/src/videos/video_id.dart | 2 +- lib/src/videos/videos.dart | 2 + lib/src/youtube_explode_base.dart | 35 +++ lib/youtube_explode_dart.dart | 7 +- tools/test.dart | 16 +- 78 files changed, 602 insertions(+), 1639 deletions(-) rename lib/src/{channel => channels}/channel.dart (100%) rename lib/src/{channel => channels}/channel_client.dart (93%) rename lib/src/{channel => channels}/channel_id.dart (100%) create mode 100644 lib/src/channels/channels.dart rename lib/src/{channel => channels}/username.dart (100%) create mode 100644 lib/src/common/common.dart rename lib/src/{models/statistics.dart => common/engagement.dart} (84%) rename lib/src/{models => common}/thumbnail_set.dart (100%) delete mode 100644 lib/src/extensions/caption_extension.dart delete mode 100644 lib/src/extensions/channel_extension.dart delete mode 100644 lib/src/extensions/download_extension.dart delete mode 100644 lib/src/extensions/extensions.dart delete mode 100644 lib/src/extensions/playlist_extension.dart delete mode 100644 lib/src/extensions/search_extension.dart delete mode 100644 lib/src/models/channel.dart delete mode 100644 lib/src/models/closed_captions/closed_caption.dart delete mode 100644 lib/src/models/closed_captions/closed_caption_track.dart delete mode 100644 lib/src/models/media_streams/audio_encoding.dart delete mode 100644 lib/src/models/media_streams/audio_stream_info.dart delete mode 100644 lib/src/models/media_streams/container.dart delete mode 100644 lib/src/models/media_streams/media_stream_info.dart delete mode 100644 lib/src/models/media_streams/media_stream_info_set.dart delete mode 100644 lib/src/models/media_streams/muxed_stream_info.dart delete mode 100644 lib/src/models/media_streams/video_encoding.dart delete mode 100644 lib/src/models/media_streams/video_quality.dart delete mode 100644 lib/src/models/media_streams/video_resolution.dart delete mode 100644 lib/src/models/media_streams/video_stream_info.dart delete mode 100644 lib/src/models/models.dart delete mode 100644 lib/src/models/player_configuration.dart delete mode 100644 lib/src/models/playlist.dart delete mode 100644 lib/src/models/playlist_type.dart delete mode 100644 lib/src/models/video.dart delete mode 100644 lib/src/models/video_id.dart delete mode 100644 lib/src/parser.dart create mode 100644 lib/src/playlists/playlist.dart create mode 100644 lib/src/playlists/playlist_client.dart create mode 100644 lib/src/playlists/playlist_id.dart create mode 100644 lib/src/playlists/playlists.dart delete mode 100644 lib/src/reverse_engineering/cipher/cipher.dart delete mode 100644 lib/src/reverse_engineering/reverse_engineering.dart create mode 100644 lib/src/videos/closed_captions/closed_caption.dart create mode 100644 lib/src/videos/closed_captions/closed_caption_client.dart create mode 100644 lib/src/videos/closed_captions/closed_caption_manifest.dart create mode 100644 lib/src/videos/closed_captions/closed_caption_part.dart create mode 100644 lib/src/videos/closed_captions/closed_caption_track.dart rename lib/src/{models => videos}/closed_captions/closed_caption_track_info.dart (87%) create mode 100644 lib/src/videos/closed_captions/closed_captions.dart rename lib/src/{models => videos}/closed_captions/language.dart (100%) rename lib/src/videos/streams/{stream_client.dart => streams_client.dart} (94%) create mode 100644 lib/src/videos/video.dart create mode 100644 lib/src/videos/video_client.dart create mode 100644 lib/src/youtube_explode_base.dart diff --git a/example/example.dart b/example/example.dart index 31c572d..08a8cf3 100644 --- a/example/example.dart +++ b/example/example.dart @@ -1,12 +1,8 @@ import 'package:youtube_explode_dart/youtube_explode_dart.dart'; Future main() async { - // Parse the video id from the url. - var id = YoutubeExplode.parseVideoId( - 'https://www.youtube.com/watch?v=bo_efYhYU2A'); - var yt = YoutubeExplode(); - var video = await yt.getVideo(id); + var video = await yt.videos.get(VideoId('https://www.youtube.com/watch?v=bo_efYhYU2A')); print('Title: ${video.title}'); diff --git a/lib/src/channel/channel.dart b/lib/src/channels/channel.dart similarity index 100% rename from lib/src/channel/channel.dart rename to lib/src/channels/channel.dart diff --git a/lib/src/channel/channel_client.dart b/lib/src/channels/channel_client.dart similarity index 93% rename from lib/src/channel/channel_client.dart rename to lib/src/channels/channel_client.dart index 896875e..eb883d3 100644 --- a/lib/src/channel/channel_client.dart +++ b/lib/src/channels/channel_client.dart @@ -1,9 +1,10 @@ -import '../reverse_engineering/reverse_engineering.dart'; +import '../extensions/helpers_extension.dart'; +import '../reverse_engineering/responses/responses.dart'; +import '../reverse_engineering/youtube_http_client.dart'; import '../videos/video_id.dart'; import 'channel.dart'; import 'channel_id.dart'; import 'username.dart'; -import '../extensions/helpers_extension.dart'; /// Queries related to YouTube channels. class ChannelClient { diff --git a/lib/src/channel/channel_id.dart b/lib/src/channels/channel_id.dart similarity index 100% rename from lib/src/channel/channel_id.dart rename to lib/src/channels/channel_id.dart diff --git a/lib/src/channels/channels.dart b/lib/src/channels/channels.dart new file mode 100644 index 0000000..391ad92 --- /dev/null +++ b/lib/src/channels/channels.dart @@ -0,0 +1,6 @@ +library youtube_explode.channels; + +export 'channel.dart'; +export 'channel_client.dart'; +export 'channel_id.dart'; +export 'username.dart'; \ No newline at end of file diff --git a/lib/src/channel/username.dart b/lib/src/channels/username.dart similarity index 100% rename from lib/src/channel/username.dart rename to lib/src/channels/username.dart diff --git a/lib/src/common/common.dart b/lib/src/common/common.dart new file mode 100644 index 0000000..7a2a987 --- /dev/null +++ b/lib/src/common/common.dart @@ -0,0 +1,4 @@ +library youtube_explode.common; + +export 'engagement.dart'; +export 'thumbnail_set.dart'; diff --git a/lib/src/models/statistics.dart b/lib/src/common/engagement.dart similarity index 84% rename from lib/src/models/statistics.dart rename to lib/src/common/engagement.dart index 05822aa..0591084 100644 --- a/lib/src/models/statistics.dart +++ b/lib/src/common/engagement.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; /// User activity statistics. -class Statistics extends Equatable { +class Engagement extends Equatable { /// View count. final int viewCount; @@ -12,7 +12,7 @@ class Statistics extends Equatable { final int dislikeCount; /// Initializes an instance of [Statistics] - const Statistics(this.viewCount, this.likeCount, this.dislikeCount); + const Engagement(this.viewCount, this.likeCount, this.dislikeCount); /// Average user rating in stars (1 star to 5 stars). num get avgRating { diff --git a/lib/src/models/thumbnail_set.dart b/lib/src/common/thumbnail_set.dart similarity index 100% rename from lib/src/models/thumbnail_set.dart rename to lib/src/common/thumbnail_set.dart diff --git a/lib/src/exceptions/fatal_failure_exception.dart b/lib/src/exceptions/fatal_failure_exception.dart index e9d519d..41106e6 100644 --- a/lib/src/exceptions/fatal_failure_exception.dart +++ b/lib/src/exceptions/fatal_failure_exception.dart @@ -12,7 +12,7 @@ class FatalFailureException implements YoutubeExplodeException { FatalFailureException(this.message); /// Initializes an instance of [FatalFailureException] with a [Response] - FatalFailureException.httpRequest(Response response) + FatalFailureException.httpRequest(BaseResponse response) : message = ''' 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. diff --git a/lib/src/exceptions/request_limit_exceeded_exception.dart b/lib/src/exceptions/request_limit_exceeded_exception.dart index 779e1a4..148c9fb 100644 --- a/lib/src/exceptions/request_limit_exceeded_exception.dart +++ b/lib/src/exceptions/request_limit_exceeded_exception.dart @@ -3,16 +3,17 @@ import 'package:http/http.dart'; import 'youtube_explode_exception.dart'; /// Exception thrown when a fatal failure occurs. -class RequestLimitExceeded implements YoutubeExplodeException { +class RequestLimitExceededException implements YoutubeExplodeException { /// Description message + @override final String message; - /// Initializes an instance of [RequestLimitExceeded] - RequestLimitExceeded(this.message); + /// Initializes an instance of [RequestLimitExceededException] + RequestLimitExceededException(this.message); /// Initializes an instance of [RequestLimitExceeded] with a [Response] - RequestLimitExceeded.httpRequest(Response response) + RequestLimitExceededException.httpRequest(BaseResponse response) : message = ''' 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. diff --git a/lib/src/exceptions/transient_failure_exception.dart b/lib/src/exceptions/transient_failure_exception.dart index 269640a..bc5d1f3 100644 --- a/lib/src/exceptions/transient_failure_exception.dart +++ b/lib/src/exceptions/transient_failure_exception.dart @@ -10,7 +10,7 @@ class TransientFailureException implements YoutubeExplodeException { TransientFailureException(this.message); /// Initializes an instance of [TransientFailureException] with a [Response] - TransientFailureException.httpRequest(Response response) + TransientFailureException.httpRequest(BaseResponse response) : message = ''' 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. diff --git a/lib/src/exceptions/video_unavailable_exception.dart b/lib/src/exceptions/video_unavailable_exception.dart index 7ad9856..c8655b8 100644 --- a/lib/src/exceptions/video_unavailable_exception.dart +++ b/lib/src/exceptions/video_unavailable_exception.dart @@ -1,5 +1,5 @@ +import '../videos/video_id.dart'; import 'exceptions.dart'; -import '../models/models.dart'; /// Thrown when a video is not available and cannot be processed. /// This can happen because the video does not exist, is deleted, diff --git a/lib/src/exceptions/youtube_explode_exception.dart b/lib/src/exceptions/youtube_explode_exception.dart index e8df27d..8c1958a 100644 --- a/lib/src/exceptions/youtube_explode_exception.dart +++ b/lib/src/exceptions/youtube_explode_exception.dart @@ -1,5 +1,6 @@ /// Parent class for domain exceptions thrown by [YoutubeExplode] abstract class YoutubeExplodeException implements Exception { final String message; + YoutubeExplodeException(this.message); } diff --git a/lib/src/extensions/caption_extension.dart b/lib/src/extensions/caption_extension.dart deleted file mode 100644 index c198053..0000000 --- a/lib/src/extensions/caption_extension.dart +++ /dev/null @@ -1,102 +0,0 @@ -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'; - -/// Caption extension for [YoutubeExplode] -extension CaptionExtension on YoutubeExplode { - /// Gets all available closed caption track infos for given video. - /// Returns an empty list of no caption is available. - Future> getVideoClosedCaptionTrackInfos( - String videoId) async { - if (!YoutubeExplode.validateVideoId(videoId)) { - throw ArgumentError.value(videoId, 'videoId', 'Invalid video id'); - } - - var videoInfoDic = await getVideoInfoDictionary(videoId); - - var playerResponseJson = json.decode(videoInfoDic['player_response']); - - var playAbility = playerResponseJson['playabilityStatus']; - - if (playAbility['status'].toLowerCase() == 'error') { - throw VideoUnavailableException(videoId); - } - - var captionTracks = playerResponseJson['captions']; - - if (captionTracks == null) { - return const []; - } - - var trackInfos = []; - for (var trackJson in captionTracks['playerCaptionsTracklistRenderer'] - ['captionTracks']) { - var url = Uri.parse(trackJson['baseUrl']); - - var query = Map.from(url.queryParameters); - query['format'] = '3'; - - url = url.replace(queryParameters: query); - - var languageCode = trackJson['languageCode']; - var languageName = trackJson['name']['simpleText']; - var language = Language(languageCode, languageName); - - var isAutoGenerated = trackJson['vssId'].toLowerCase().startsWith('a.'); - - trackInfos.add(ClosedCaptionTrackInfo(url, language, - isAutoGenerated: isAutoGenerated)); - } - return trackInfos; - } - - Future _getClosedCaptionTrackXml(Uri url) async { - var raw = (await client.get(url)).body; - - return xml.parse(raw); - } - - /// Gets the closed caption track associated with given metadata. - Future getClosedCaptionTrack( - ClosedCaptionTrackInfo info) async { - var trackXml = await _getClosedCaptionTrackXml(info.url); - - var captions = []; - for (var captionXml in trackXml.findElements('p')) { - var text = captionXml.text; - if (text.isNullOrWhiteSpace) { - continue; - } - - var offset = - Duration(milliseconds: int.parse(captionXml.getAttribute('t'))); - var duration = Duration( - milliseconds: int.parse(captionXml.getAttribute('d') ?? '-1')); - - captions.add(ClosedCaption(text, offset, duration)); - } - - return ClosedCaptionTrack(info, captions); - } -} - -/// Extension for List of [ClosedCaptions] -extension CaptionListExtension on List { - /// Get the [ClosedCaption] displayed at [time]. - /// [time] can be an [int] (time in seconds) or a [Duration]. - ClosedCaption getByTime(dynamic time) { - Duration duration; - if (time is int) { - duration = Duration(seconds: time); - } else { - duration = time; - } - - return firstWhere((e) => e.start <= duration && duration <= e.end); - } -} diff --git a/lib/src/extensions/channel_extension.dart b/lib/src/extensions/channel_extension.dart deleted file mode 100644 index 3e196ac..0000000 --- a/lib/src/extensions/channel_extension.dart +++ /dev/null @@ -1,159 +0,0 @@ -import 'package:html/dom.dart'; -import 'package:html/parser.dart' as html; - -import '../models/models.dart'; -import '../youtube_explode_base.dart'; -import 'helpers_extension.dart'; -import 'playlist_extension.dart'; - -/// Channel extension for [YoutubeExplode] -extension ChannelExtension on YoutubeExplode { - static final _usernameRegMatchExp = - RegExp(r'youtube\..+?/user/(.*?)(?:\?|&|/|$)'); - - static final _idRegMatchExp = - RegExp(r'youtube\..+?/channel/(.*?)(?:\?|&|/|$)'); - - /// Returns the [Channel] associated with the given channelId. - /// Throws an [ArgumentError] if the channel id is not valid. - Future getChannel(String channelId) async { - if (!validateChannelId(channelId)) { - throw ArgumentError.value( - channelId, 'channelId', 'Invalid YouTube channel id'); - } - - var channelPage = await getChannelPage(channelId); - var channelTitle = channelPage - .querySelector('meta[property="og:title"]') - .attributes['content']; - var channelImage = channelPage - .querySelector('meta[property="og:image"]') - .attributes['content']; - - return Channel(channelId, channelTitle, Uri.parse(channelImage)); - } - - /// Get a channel id from a username. - /// 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 userPage = await _getUserPage(username); - if (userPage == null) { - return null; - } - - var channelUrl = - userPage.querySelector('meta[property="og:url"]').attributes['content']; - - return channelUrl.replaceFirst('/channel/', ''); - } - - /// Returns all the videos uploaded by a channel up to [maxPages] count. - Future> getChannelUploads(String channelId, - [int maxPages = 5]) async { - if (!validateChannelId(channelId)) { - throw ArgumentError.value( - channelId, 'channelId', 'Invalid YouTube channel id'); - } - - var playlistId = 'UU${channelId.replaceFirst('UC', '')}'; - var playlist = await getPlaylist(playlistId, maxPages); - - return playlist.videos; - } - - /// Returns the channel id for a given video. - Future getChannelIdFromVideo(String videoId) async { - if (!YoutubeExplode.validateVideoId(videoId)) { - throw ArgumentError.value(videoId, 'videoId', 'Invalid YouTube video id'); - } - var watchPage = await getVideoWatchPage(videoId); - var href = watchPage - .querySelector('.yt-user-info') - .querySelector('a') - .attributes['href']; - return href.replaceFirst('/channel/', ''); - } - - /// Returns the channel page document. - Future getChannelPage(String channelId) async { - var url = 'https://www.youtube.com/channel/$channelId?hl=en'; - var raw = (await client.get(url)).body; - - return html.parse(raw); - } - - Future _getUserPage(String username) async { - var url = 'https://www.youtube.com/user/$username?hl=en'; - var req = await client.get(url); - if (req.statusCode != 200) { - return null; - } - - return html.parse(req); - } - - /// Returns true if [username] is a valid Youtube username. - static bool validateUsername(String username) { - if (username.isNullOrWhiteSpace) { - return false; - } - - if (username.length > 20) { - return false; - } - - return !RegExp(r'[^0-9a-zA-Z]').hasMatch(username); - } - - /// Parses a username from an url. - /// Returns null if the username is not found. - static String parseUsername(String url) { - if (url.isNullOrWhiteSpace) { - return null; - } - - var regMatch = _usernameRegMatchExp.firstMatch(url)?.group(1); - if (!regMatch.isNullOrWhiteSpace && validateUsername(regMatch)) { - return regMatch; - } - return null; - } - - /// Returns true if [channelId] is a valid Youtube channel id. - static bool validateChannelId(String channelId) { - if (channelId.isNullOrWhiteSpace) { - return false; - } - - channelId = channelId.toLowerCase(); - - if (!channelId.startsWith('uc')) { - return false; - } - - if (channelId.length != 24) { - return false; - } - - return !RegExp(r'[^0-9a-zA-Z_\-]').hasMatch(channelId); - } - - /// Parses a channel id from an url. - /// Returns null if the username is not found. - static String parseChannelId(String url) { - if (url.isNullOrWhiteSpace) { - return null; - } - - var regMatch = _idRegMatchExp.firstMatch(url)?.group(1); - if (!regMatch.isNullOrWhiteSpace && validateChannelId(regMatch)) { - return regMatch; - } - return null; - } -} diff --git a/lib/src/extensions/download_extension.dart b/lib/src/extensions/download_extension.dart deleted file mode 100644 index 7e9e8f5..0000000 --- a/lib/src/extensions/download_extension.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'dart:async'; - -import 'package:http/http.dart' as http; - -import '../exceptions/exceptions.dart'; -import '../models/models.dart'; - -/// Download extension for [MediaStreamInfo] -extension DownloadExtension on MediaStreamInfo { - static final _rateBypassExp = RegExp('ratebypass[=/]yes'); - - /// Returns the stream of this media stream object. - /// 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; - - for (var i = 1; total < size; i++) { - var req = http.Request('get', url); - req.headers['range'] = 'bytes=$total-${total + maxSize}'; - var resp = await req.send(); - yield* resp.stream; - total += maxSize + 1; - } - } -} diff --git a/lib/src/extensions/extensions.dart b/lib/src/extensions/extensions.dart deleted file mode 100644 index 9fe065c..0000000 --- a/lib/src/extensions/extensions.dart +++ /dev/null @@ -1,8 +0,0 @@ -library youtube_explode.extensions; - -export 'caption_extension.dart'; -export 'channel_extension.dart'; -export 'download_extension.dart'; -export 'helpers_extension.dart'; -export 'playlist_extension.dart'; -export 'search_extension.dart'; diff --git a/lib/src/extensions/helpers_extension.dart b/lib/src/extensions/helpers_extension.dart index 2135cf1..475bddc 100644 --- a/lib/src/extensions/helpers_extension.dart +++ b/lib/src/extensions/helpers_extension.dart @@ -1,3 +1,5 @@ +library _youtube_explode.extensions; + import '../reverse_engineering/cipher/cipher_operations.dart'; /// Utility for Strings. diff --git a/lib/src/extensions/playlist_extension.dart b/lib/src/extensions/playlist_extension.dart deleted file mode 100644 index a65425a..0000000 --- a/lib/src/extensions/playlist_extension.dart +++ /dev/null @@ -1,156 +0,0 @@ -import 'dart:convert'; - -import '../models/models.dart'; -import '../parser.dart' as parser; -import '../youtube_explode_base.dart'; -import 'helpers_extension.dart'; - -/// Playlist extension for [YoutubeExplode] -extension PlaylistExtension on YoutubeExplode { - static final _regMatchExp = - RegExp(r'youtube\..+?/playlist.*?list=(.*?)(?:&|/|$)'); - static final _compositeMatchExp = RegExp( - r'https://www.youtube.com/watch?v=b8m9zhNAgKs&list=PL9tY0BWXOZFuFEG_GtOBZ8-8wbkH-NVAr'); - static final _shortCompositeMatchExp = - RegExp(r'youtu\.be/.*?/.*?list=(.*?)(?:&|/|$)'); - static final _embedCompositeMatchExp = - RegExp(r'youtube\..+?/embed/.*?/.*?list=(.*?)(?:&|/|$)'); - - Future> _getPlayListJson( - String playlistId, int index) async { - var url = - 'https://youtube.com/list_ajax?style=json&action_get_list=1&list=$playlistId&index=$index&hl=en'; - var raw = (await client.get(url)).body; - - return json.decode(raw); - } - - /// Returns a [Future] that completes with a [Playlist]. - /// If the id is not valid an [ArgumentError] is thrown. - Future getPlaylist(String playlistId, [int maxPages = 500]) async { - if (!validatePlaylistId(playlistId)) { - throw ArgumentError.value(playlistId, 'videoId', 'Invalid video id'); - } - - Map playlistJson; - var page = 1; - var index = 0; - var videoIds = []; - var videos =