Implement #40
This commit is contained in:
parent
de6caf2949
commit
995738680c
|
@ -1,3 +1,6 @@
|
|||
## 1.4.1
|
||||
- Implement `getUploadsFromPage` to a channel uploaded videos directly from the YouTube page.
|
||||
|
||||
## 1.4.0
|
||||
- Add ChannelId property to Video class.
|
||||
- Implement `thumbnails` for playlists. The playlist's thumbnail is the same as the thumbnail of its first video. If the playlist is empty, then this property is `null`.
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
import 'package:youtube_explode_dart/src/channels/channel_video.dart';
|
||||
import 'package:youtube_explode_dart/src/channels/video_sorting.dart';
|
||||
import 'package:youtube_explode_dart/src/reverse_engineering/responses/channel_upload_page.dart';
|
||||
|
||||
import '../extensions/helpers_extension.dart';
|
||||
import '../playlists/playlists.dart';
|
||||
import '../reverse_engineering/responses/responses.dart';
|
||||
|
@ -50,8 +54,34 @@ class ChannelClient {
|
|||
}
|
||||
|
||||
/// Enumerates videos uploaded by the specified channel.
|
||||
Stream<Video> getUploads(ChannelId id) {
|
||||
var playlistId = 'UU${id.value.substringAfter('UC')}';
|
||||
/// If you want a full list of uploads see [getUploadsFromPage]
|
||||
Stream<Video> getUploads(dynamic channelId) {
|
||||
channelId = ChannelId.fromString(channelId);
|
||||
var playlistId = 'UU${channelId.value.substringAfter('UC')}';
|
||||
return PlaylistClient(_httpClient).getVideos(PlaylistId(playlistId));
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ class ChannelVideo with EquatableMixin {
|
|||
ChannelVideo(this.videoId, this.videoTitle);
|
||||
|
||||
@override
|
||||
String toString() => '(ChannelVideo) $videoId ($videoTitle)';
|
||||
String toString() => '[ChannelVideo] $videoTitle ($videoId)';
|
||||
|
||||
@override
|
||||
List<Object> get props => [videoId];
|
||||
|
|
|
@ -5,3 +5,4 @@ export 'channel_client.dart';
|
|||
export 'channel_id.dart';
|
||||
export 'channel_video.dart';
|
||||
export 'username.dart';
|
||||
export 'video_sorting.dart';
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/// Metadata about video are sorted with [ChannelClient.getUploadsFromPage]
|
||||
class VideoSorting {
|
||||
/// Code used to fetch the video.
|
||||
/// Used internally.
|
||||
final String code;
|
||||
|
||||
/// Sort from the newest video
|
||||
static const VideoSorting newest = VideoSorting._('dd');
|
||||
|
||||
/// Sort from the oldest video.
|
||||
static const oldest = VideoSorting._('da');
|
||||
|
||||
/// Sort from the most popular video.
|
||||
static const popularity = VideoSorting._('p');
|
||||
|
||||
const VideoSorting._(this.code);
|
||||
}
|
|
@ -9,7 +9,7 @@ import '../../retry.dart';
|
|||
import '../../videos/videos.dart';
|
||||
import '../youtube_http_client.dart';
|
||||
|
||||
class ChannelWatchPage {
|
||||
class ChannelUploadPage {
|
||||
final String channelId;
|
||||
final Document _root;
|
||||
|
||||
|
@ -47,21 +47,34 @@ class ChannelWatchPage {
|
|||
return str.substring(0, lastI + 1);
|
||||
}
|
||||
|
||||
ChannelWatchPage(this._root, this.channelId);
|
||||
ChannelUploadPage(this._root, this.channelId, [_InitialData initialData])
|
||||
: _initialData = initialData;
|
||||
|
||||
Future<ChannelWatchPage> nextPage() {}
|
||||
|
||||
static Future<ChannelWatchPage> get(
|
||||
YoutubeHttpClient httpClient, String channelId) {
|
||||
Future<ChannelUploadPage> nextPage(YoutubeHttpClient httpClient) {
|
||||
if (initialData.continuation.isEmpty) {
|
||||
return Future.value(null);
|
||||
}
|
||||
var url =
|
||||
'https://www.youtube.com/channel/$channelId/videos?view=0&sort=dd&flow=grid';
|
||||
'https://www.youtube.com/browse_ajax?ctoken=${initialData.continuation}&continuation=${initialData.continuation}&itct=${initialData.clickTrackingParams}';
|
||||
return retry(() async {
|
||||
var raw = await httpClient.getString(url);
|
||||
return ChannelWatchPage.parse(raw, channelId);
|
||||
return ChannelUploadPage(
|
||||
null, channelId, _InitialData(json.decode(raw)[1]));
|
||||
});
|
||||
}
|
||||
|
||||
ChannelWatchPage.parse(String raw, this.channelId)
|
||||
static Future<ChannelUploadPage> get(
|
||||
YoutubeHttpClient httpClient, String channelId, String sorting) {
|
||||
assert(sorting != null);
|
||||
var url =
|
||||
'https://www.youtube.com/channel/$channelId/videos?view=0&sort=$sorting&flow=grid';
|
||||
return retry(() async {
|
||||
var raw = await httpClient.getString(url);
|
||||
return ChannelUploadPage.parse(raw, channelId);
|
||||
});
|
||||
}
|
||||
|
||||
ChannelUploadPage.parse(String raw, this.channelId)
|
||||
: _root = parser.parse(raw);
|
||||
}
|
||||
|
||||
|
@ -118,10 +131,11 @@ class _InitialData {
|
|||
return null;
|
||||
}
|
||||
|
||||
List<dynamic> get uploads => _uploads ??= getContentContext(_root)
|
||||
List<ChannelVideo> get uploads => _uploads ??= getContentContext(_root)
|
||||
?.map(_parseContent)
|
||||
?.where((e) => e != null)
|
||||
?.toList();
|
||||
?.toList()
|
||||
?.cast<ChannelVideo>();
|
||||
|
||||
String get continuation => _continuation ??=
|
||||
getContinuationContext(_root)?.getValue('continuation') ?? '';
|
||||
|
|
|
@ -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.4.0
|
||||
version: 1.4.1
|
||||
homepage: https://github.com/Hexer10/youtube_explode_dart
|
||||
|
||||
environment:
|
||||
|
|
Loading…
Reference in New Issue