diff --git a/CHANGELOG.md b/CHANGELOG.md index 42e59f8..2382bde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 1.8.0-beta.3 +- Fixed playlists + ## 1.8.0-beta.2 - `search.getVideos` now returns a `Video` instance. diff --git a/lib/src/extensions/helpers_extension.dart b/lib/src/extensions/helpers_extension.dart index 836bb8e..fd68c3e 100644 --- a/lib/src/extensions/helpers_extension.dart +++ b/lib/src/extensions/helpers_extension.dart @@ -4,6 +4,10 @@ import '../reverse_engineering/cipher/cipher_operations.dart'; /// Utility for Strings. extension StringUtility on String { + /// Parses this value as int stripping the non digit characters, + /// returns null if this fails. + int parseInt() => int.tryParse(this?.stripNonDigits()); + /// Returns null if this string is whitespace. String get nullIfWhitespace => trim().isEmpty ? null : this; @@ -68,7 +72,7 @@ extension ListDecipher on Iterable { } /// List Utility. -extension ListFirst on Iterable { +extension ListUtil on Iterable { /// Returns the first element of a list or null if empty. E get firstOrNull { if (length == 0) { @@ -76,6 +80,15 @@ extension ListFirst on Iterable { } return first; } + + /// Same as [elementAt] but if the index is higher than the length returns + /// null + E elementAtSafe(int index) { + if (index >= length) { + return null; + } + return elementAt(index); + } } /// Uri utility @@ -112,6 +125,32 @@ extension GetOrNullMap on Map { } return v; } + + /// Get a value inside a map. + /// If it is null this returns null, if of another type this throws. + T getT(String key) { + var v = this[key]; + if (v == null) { + return null; + } + if (v is! T) { + throw Exception('Invalid type: ${v.runtimeType} should be $T'); + } + return v; + } + + /// Get a List>> from a map. + List> getList(String key) { + var v = this[key]; + if (v == null) { + return null; + } + if (v is! List) { + throw Exception('Invalid type: ${v.runtimeType} should be of type List'); + } + + return (v.toList() as List).cast>(); + } } /// @@ -124,3 +163,8 @@ extension UriUtils on Uri { return replace(queryParameters: query); } } + +/// Parse properties with `runs` method. +extension RunsParser on List { + String parseRuns() => this?.map((e) => e['text'])?.join() ?? ''; +} diff --git a/lib/src/playlists/playlist_client.dart b/lib/src/playlists/playlist_client.dart index 51d1c9e..9d11d37 100644 --- a/lib/src/playlists/playlist_client.dart +++ b/lib/src/playlists/playlist_client.dart @@ -1,5 +1,7 @@ +import 'package:youtube_explode_dart/src/channels/channel_id.dart'; +import 'package:youtube_explode_dart/src/reverse_engineering/responses/playlist_page.dart'; + import '../common/common.dart'; -import '../reverse_engineering/responses/responses.dart'; import '../reverse_engineering/youtube_http_client.dart'; import '../videos/video.dart'; import '../videos/video_id.dart'; @@ -17,28 +19,28 @@ class PlaylistClient { Future get(dynamic id) async { id = PlaylistId.fromString(id); - var response = await PlaylistResponse.get(_httpClient, id.value); + var response = await PlaylistPage.get(_httpClient, id.value); return Playlist( id, - response.title, - response.author, - response.description ?? '', - response.thumbnails, - Engagement(response.viewCount ?? 0, response.likeCount ?? 0, - response.dislikeCount ?? 0)); + response.initialData.title, + response.initialData.author, + response.initialData.description, + ThumbnailSet(id.value), + Engagement(response.initialData.viewCount ?? 0, null, null)); } /// Enumerates videos included in the specified playlist. Stream