2020-10-17 14:45:42 +02:00
|
|
|
import 'package:youtube_explode_dart/src/channels/channels.dart';
|
|
|
|
import 'package:youtube_explode_dart/src/reverse_engineering/responses/channel_about_page.dart';
|
|
|
|
|
2020-06-03 23:02:21 +02:00
|
|
|
import '../extensions/helpers_extension.dart';
|
2020-06-05 16:17:08 +02:00
|
|
|
import '../playlists/playlists.dart';
|
2020-07-16 20:02:54 +02:00
|
|
|
import '../reverse_engineering/responses/channel_upload_page.dart';
|
2020-06-03 23:02:21 +02:00
|
|
|
import '../reverse_engineering/responses/responses.dart';
|
|
|
|
import '../reverse_engineering/youtube_http_client.dart';
|
2020-06-05 16:17:08 +02:00
|
|
|
import '../videos/video.dart';
|
2020-06-03 13:18:37 +02:00
|
|
|
import '../videos/video_id.dart';
|
|
|
|
import 'channel.dart';
|
|
|
|
import 'channel_id.dart';
|
2020-07-16 20:02:54 +02:00
|
|
|
import 'channel_video.dart';
|
2020-06-03 13:18:37 +02:00
|
|
|
import 'username.dart';
|
2020-07-16 20:02:54 +02:00
|
|
|
import 'video_sorting.dart';
|
2020-06-03 13:18:37 +02:00
|
|
|
|
|
|
|
/// Queries related to YouTube channels.
|
2020-04-18 23:22:13 +02:00
|
|
|
class ChannelClient {
|
2020-06-03 13:18:37 +02:00
|
|
|
final YoutubeHttpClient _httpClient;
|
|
|
|
|
|
|
|
/// Initializes an instance of [ChannelClient]
|
|
|
|
ChannelClient(this._httpClient);
|
|
|
|
|
|
|
|
/// Gets the metadata associated with the specified channel.
|
2020-06-05 20:08:04 +02:00
|
|
|
/// [id] must be either a [ChannelId] or a string
|
|
|
|
/// which is parsed to a [ChannelId]
|
|
|
|
Future<Channel> get(dynamic id) async {
|
2020-06-26 15:30:16 +02:00
|
|
|
id = ChannelId.fromString(id);
|
2020-06-03 13:18:37 +02:00
|
|
|
var channelPage = await ChannelPage.get(_httpClient, id.value);
|
|
|
|
|
|
|
|
return Channel(id, channelPage.channelTitle, channelPage.channelLogoUrl);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the metadata associated with the channel of the specified user.
|
2020-06-05 20:08:04 +02:00
|
|
|
/// [username] must be either a [Username] or a string
|
|
|
|
/// which is parsed to a [Username]
|
|
|
|
Future<Channel> getByUsername(dynamic username) async {
|
|
|
|
username = Username.fromString(username);
|
|
|
|
|
2020-06-03 13:18:37 +02:00
|
|
|
var channelPage =
|
|
|
|
await ChannelPage.getByUsername(_httpClient, username.value);
|
|
|
|
return Channel(ChannelId(channelPage.channelId), channelPage.channelTitle,
|
|
|
|
channelPage.channelLogoUrl);
|
|
|
|
}
|
|
|
|
|
2020-10-17 14:45:42 +02:00
|
|
|
/// 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<ChannelAbout> 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<ChannelAbout> 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);
|
|
|
|
}
|
|
|
|
|
2020-06-03 13:18:37 +02:00
|
|
|
/// Gets the metadata associated with the channel
|
|
|
|
/// that uploaded the specified video.
|
2020-06-05 20:08:04 +02:00
|
|
|
Future<Channel> getByVideo(dynamic videoId) async {
|
|
|
|
videoId = VideoId.fromString(videoId);
|
2020-06-03 13:18:37 +02:00
|
|
|
var videoInfoResponse =
|
|
|
|
await VideoInfoResponse.get(_httpClient, videoId.value);
|
2020-06-06 11:28:36 +02:00
|
|
|
var playerResponse = videoInfoResponse.playerResponse;
|
2020-06-03 13:18:37 +02:00
|
|
|
|
2020-06-06 11:28:36 +02:00
|
|
|
var channelId = playerResponse.videoChannelId;
|
2020-06-10 00:08:16 +02:00
|
|
|
return get(ChannelId(channelId));
|
2020-06-03 13:18:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Enumerates videos uploaded by the specified channel.
|
2020-07-12 18:24:22 +02:00
|
|
|
/// If you want a full list of uploads see [getUploadsFromPage]
|
|
|
|
Stream<Video> getUploads(dynamic channelId) {
|
|
|
|
channelId = ChannelId.fromString(channelId);
|
2020-07-12 18:31:16 +02:00
|
|
|
var playlistId = 'UU${(channelId.value as String).substringAfter('UC')}';
|
2020-06-05 16:17:08 +02:00
|
|
|
return PlaylistClient(_httpClient).getVideos(PlaylistId(playlistId));
|
2020-06-03 13:18:37 +02:00
|
|
|
}
|
2020-07-12 18:24:22 +02:00
|
|
|
|
|
|
|
/// Enumerates videos uploaded by the specified channel.
|
|
|
|
/// This fetches thru all the uploads pages of the channel so it is
|
|
|
|
/// recommended to use _.take_ (or any other method) to limit the
|
|
|
|
/// search result. Every page has 30 results.
|
|
|
|
///
|
|
|
|
/// Note that this endpoint provides less info about each video
|
|
|
|
/// (only the Title and VideoId).
|
|
|
|
Stream<ChannelVideo> getUploadsFromPage(dynamic channelId,
|
|
|
|
[VideoSorting videoSorting = VideoSorting.newest]) async* {
|
|
|
|
channelId = ChannelId.fromString(channelId);
|
|
|
|
var page = await ChannelUploadPage.get(
|
|
|
|
_httpClient, channelId.value, videoSorting.code);
|
|
|
|
yield* Stream.fromIterable(page.initialData.uploads);
|
|
|
|
|
|
|
|
// ignore: literal_only_boolean_expressions
|
|
|
|
while (true) {
|
|
|
|
page = await page.nextPage(_httpClient);
|
|
|
|
if (page == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
yield* Stream.fromIterable(page.initialData.uploads);
|
|
|
|
}
|
|
|
|
}
|
2020-06-03 13:18:37 +02:00
|
|
|
}
|