youtube_explode/lib/src/videos/comments/comments_client.dart

139 lines
4.9 KiB
Dart

import 'dart:convert';
import '../../channels/channel_id.dart';
import '../../extensions/helpers_extension.dart';
import '../../retry.dart';
import '../../reverse_engineering/youtube_http_client.dart';
import '../videos.dart';
import 'comment.dart';
/// Queries related to comments of YouTube videos.
class CommentsClient {
final YoutubeHttpClient _httpClient;
/// Initializes an instance of [CommentsClient]
CommentsClient(this._httpClient);
/// Returns the json parsed comments map.
Future<Map<String, dynamic>> _getCommentJson(
String service,
String continuation,
String clickTrackingParams,
String xsfrToken,
String visitorInfoLive,
String ysc) async {
var url = 'https://www.youtube.com/comment_service_ajax?'
'$service=1&'
'pbj=1&'
'ctoken=$continuation&'
'continuation=$continuation&'
'itct=$clickTrackingParams';
return retry(() async {
var raw = await _httpClient.postString(url, headers: {
'cookie': 'YSC=$ysc; GPS=1; VISITOR_INFO1_LIVE=$visitorInfoLive;'
' CONSENT=WP.288163; PREF=f4=4000000',
}, body: {
'session_token': xsfrToken
});
return json.decode(raw);
});
}
/// Returns a stream emitting all the [video]'s comment.
/// A request is page for every comment page,
/// a page contains at most 20 comments, use .take if you want to limit
/// the results.
///
/// The streams doesn't emit any data if [Video.hasWatchPage] is false.
/// Use `videos.get(videoId, forceWatchPage: true)` to assure that the
/// WatchPage is fetched.
Stream<Comment> getComments(Video video) async* {
if (video.watchPage == null) {
return;
}
yield* _getComments(
video.watchPage!.initialData.continuation,
video.watchPage!.initialData.clickTrackingParams,
video.watchPage!.xsfrToken,
video.watchPage!.visitorInfoLive,
video.watchPage!.ysc);
}
Stream<Comment> _getComments(String continuation, String clickTrackingParams,
String xsfrToken, String visitorInfoLive, String ysc) async* {
var data = await _getCommentJson('action_get_comments', continuation,
clickTrackingParams, xsfrToken, visitorInfoLive, ysc);
var contentRoot = data['response']['continuationContents']
['itemSectionContinuation']['contents']
?.map((e) => e['commentThreadRenderer'])
?.toList()
?.cast<Map<String, dynamic>>() as List<Map<String, dynamic>>?;
if (contentRoot == null) {
return;
}
for (final content in contentRoot) {
var commentRaw = content['comment']['commentRenderer'];
String? continuation;
String? clickTrackingParams;
if (content['replies'] != null) {
continuation = content['replies']['commentRepliesRenderer']
['continuations']
.first['nextContinuationData']['continuation'];
clickTrackingParams = content['replies']['commentRepliesRenderer']
['continuations']
.first['nextContinuationData']['clickTrackingParams'];
}
var comment = Comment(
commentRaw['commentId'],
commentRaw['authorText']['simpleText'],
ChannelId(commentRaw['authorEndpoint']['browseEndpoint']['browseId']),
_parseRuns(commentRaw['contentText']),
commentRaw['likeCount'] ?? 0,
_parseRuns(commentRaw['publishedTimeText']),
commentRaw['replyCount'],
continuation,
clickTrackingParams);
yield comment;
}
var continuationRoot = (data
.get('response')
?.get('continuationContents')
?.get('itemSectionContinuation')
?.getValue('continuations')
?.first as Map<String, dynamic>)
.get('nextContinuationData');
if (continuationRoot != null) {
yield* _getComments(
continuationRoot['continuation'],
continuationRoot['clickTrackingParams'],
xsfrToken,
visitorInfoLive,
ysc);
}
}
String _parseRuns(Map<dynamic, dynamic> runs) =>
runs.getValue('runs')?.map((e) => e['text'])?.join() ?? '';
//TODO: Implement replies
/* Stream<Comment> getReplies(Video video, Comment comment) async* {
if (video.watchPage == null || comment.continuation == null
|| comment.clicktrackingParams == null) {
return;
}
yield* _getReplies(
video.watchPage.initialData.continuation,
video.watchPage.initialData.clickTrackingParams,
video.watchPage.xsfrToken,
video.watchPage.visitorInfoLive,
video.watchPage.ysc);
}
Stream<Comment> _getReplies(String continuation, String clickTrackingParams,
String xsfrToken, String visitorInfoLive, String ysc) async* {
var data = await _getCommentJson('action_get_comment_replies', continuation,
clickTrackingParams, xsfrToken, visitorInfoLive, ysc);
print(data);
}*/
}