From 7e36349220adeec6dd89c9a648cd1463f5b1269a Mon Sep 17 00:00:00 2001 From: Mattia Date: Sat, 17 Oct 2020 14:45:42 +0200 Subject: [PATCH] Fully implement channel_about_page --- lib/src/channels/channel_client.dart | 45 +++++++++++++++++++ lib/src/channels/channel_link.dart | 3 ++ .../responses/channel_about_page.dart | 19 +++++--- .../generated/channel_about_page_id.g.dart | 3 ++ test/channel_about_test.dart | 29 ++++++++++++ 5 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 test/channel_about_test.dart diff --git a/lib/src/channels/channel_client.dart b/lib/src/channels/channel_client.dart index 8ad79eb..5619401 100644 --- a/lib/src/channels/channel_client.dart +++ b/lib/src/channels/channel_client.dart @@ -1,3 +1,6 @@ +import 'package:youtube_explode_dart/src/channels/channels.dart'; +import 'package:youtube_explode_dart/src/reverse_engineering/responses/channel_about_page.dart'; + import '../extensions/helpers_extension.dart'; import '../playlists/playlists.dart'; import '../reverse_engineering/responses/channel_upload_page.dart'; @@ -40,6 +43,48 @@ class ChannelClient { channelPage.channelLogoUrl); } + /// Gets the info found on a YouTube Channel About page. + /// [id] must be either a [ChannelId] or a string + /// which is parsed to a [ChannelId] + Future getAboutPage(dynamic id) async { + id = ChannelId.fromString(id); + + var channelAboutPage = await ChannelAboutPage.get(_httpClient, id.value); + var iData = channelAboutPage.initialData; + assert(iData != null); + return ChannelAbout( + id.description, + id.viewCount, + id.joinDate, + id.title, + id.avatar + .map((e) => ChannelThumbnail(Uri.parse(e.url), e.height, e.width)), + id.country, + id.channelLinks); + } + + /// Gets the info found on a YouTube Channel About page. + /// [username] must be either a [Username] or a string + /// which is parsed to a [Username] + Future getAboutPageByUsername(dynamic username) async { + username = Username.fromString(username); + + var channelAboutPage = + await ChannelAboutPage.getByUsername(_httpClient, username.value); + var id = channelAboutPage.initialData; + assert(id != null); + return ChannelAbout( + id.description, + id.viewCount, + id.joinDate, + id.title, + id.avatar + .map((e) => ChannelThumbnail(Uri.parse(e.url), e.height, e.width)) + .toList(), + id.country, + id.channelLinks); + } + /// Gets the metadata associated with the channel /// that uploaded the specified video. Future getByVideo(dynamic videoId) async { diff --git a/lib/src/channels/channel_link.dart b/lib/src/channels/channel_link.dart index 05ca665..85146eb 100644 --- a/lib/src/channels/channel_link.dart +++ b/lib/src/channels/channel_link.dart @@ -17,4 +17,7 @@ class ChannelLink with EquatableMixin { @override List get props => [title, url, icon]; + + @override + String toString() => 'Link: $title ($url): $icon'; } diff --git a/lib/src/reverse_engineering/responses/channel_about_page.dart b/lib/src/reverse_engineering/responses/channel_about_page.dart index 588a846..4baff98 100644 --- a/lib/src/reverse_engineering/responses/channel_about_page.dart +++ b/lib/src/reverse_engineering/responses/channel_about_page.dart @@ -124,10 +124,14 @@ class _InitialData { String get description => content.description.simpleText; List get channelLinks { - return content.primaryLinks.map((e) => ChannelLink( - e.title.simpleText, - extractUrl(e.navigationEndpoint.urlEndpoint.url), - Uri.parse(e.icon.thumbnails.first.url))); + return content.primaryLinks + .map((e) => ChannelLink( + e.title.simpleText, + extractUrl(e.navigationEndpoint?.commandMetadata?.webCommandMetadata + ?.url ?? + e.navigationEndpoint.urlEndpoint.url), + Uri.parse(e.icon.thumbnails.first.url))) + .toList(); } int get viewCount => @@ -137,12 +141,13 @@ class _InitialData { String get title => content.title.simpleText; - dynamic get avatar => content.avatar.thumbnails; + List get avatar => content.avatar.thumbnails; + + String get country => content.country.simpleText; - /// todo: continue from here with more data! String parseRuns(List runs) => runs?.map((e) => e.text)?.join() ?? ''; Uri extractUrl(String text) => - Uri.parse(Uri.decodeFull(_urlExp.firstMatch(text).group(1))); + Uri.parse(Uri.decodeFull(_urlExp.firstMatch(text)?.group(1) ?? '')); } diff --git a/lib/src/reverse_engineering/responses/generated/channel_about_page_id.g.dart b/lib/src/reverse_engineering/responses/generated/channel_about_page_id.g.dart index 91f0860..400feed 100644 --- a/lib/src/reverse_engineering/responses/generated/channel_about_page_id.g.dart +++ b/lib/src/reverse_engineering/responses/generated/channel_about_page_id.g.dart @@ -1921,6 +1921,9 @@ class AvatarThumbnail { "width": width == null ? null : width, "height": height == null ? null : height, }; + + @override + String toString() => '${toJson()}'; } class DismissButtonClass { diff --git a/test/channel_about_test.dart b/test/channel_about_test.dart new file mode 100644 index 0000000..03c0364 --- /dev/null +++ b/test/channel_about_test.dart @@ -0,0 +1,29 @@ +import 'package:test/test.dart'; +import 'package:youtube_explode_dart/youtube_explode_dart.dart'; + +void main() { + group('Channel About', () { + YoutubeExplode yt; + setUp(() { + yt = YoutubeExplode(); + }); + + tearDown(() { + yt.close(); + }); + + test('GetAboutPageOfChannel', () async { + var channelUrl = + 'https://www.youtube.com/user/FavijTV'; + var channel = await yt.channels.getAboutPageByUsername(channelUrl); + expect(channel.country, 'Italy'); + expect(channel.thumbnails, isNotEmpty); + expect(channel.channelLinks, isNotEmpty); + expect(channel.description, isNotEmpty); + expect(channel.joinDate, isNotEmpty); + expect(channel.title, 'FavijTV'); + expect(channel.viewCount, greaterThanOrEqualTo(3631224938)); + + }); + }); +}