2021-03-11 14:20:10 +01:00
|
|
|
import 'package:collection/collection.dart';
|
2021-03-04 10:46:37 +01:00
|
|
|
import 'package:html/parser.dart' as parser;
|
|
|
|
|
|
|
|
import '../../../youtube_explode_dart.dart';
|
|
|
|
import '../../extensions/helpers_extension.dart';
|
|
|
|
import '../../retry.dart';
|
2021-07-21 02:06:02 +02:00
|
|
|
import '../models/initial_data.dart';
|
2021-07-23 12:54:29 +02:00
|
|
|
import '../models/youtube_page.dart';
|
|
|
|
import '../youtube_http_client.dart';
|
2021-03-04 10:46:37 +01:00
|
|
|
|
|
|
|
///
|
2021-07-21 02:06:02 +02:00
|
|
|
class PlaylistPage extends YoutubePage<_InitialData> {
|
2021-03-04 10:46:37 +01:00
|
|
|
///
|
|
|
|
final String playlistId;
|
|
|
|
|
2021-07-21 02:06:02 +02:00
|
|
|
late final List<_Video> videos = initialData.playlistVideos;
|
2021-03-04 10:46:37 +01:00
|
|
|
|
2021-07-21 02:06:02 +02:00
|
|
|
late final String? title = initialData.title;
|
2021-03-04 10:46:37 +01:00
|
|
|
|
2021-07-21 02:06:02 +02:00
|
|
|
late final String? description = initialData.description;
|
2021-03-04 10:46:37 +01:00
|
|
|
|
2021-07-21 02:06:02 +02:00
|
|
|
late final String? author = initialData.author;
|
2021-03-04 10:46:37 +01:00
|
|
|
|
2021-07-21 02:06:02 +02:00
|
|
|
late final int? viewCount = initialData.viewCount;
|
|
|
|
|
2021-08-29 13:26:21 +02:00
|
|
|
late final int? videoCount = initialData.videoCount;
|
|
|
|
|
2021-07-21 02:06:02 +02:00
|
|
|
/// InitialData
|
|
|
|
PlaylistPage.id(this.playlistId, _InitialData initialData)
|
|
|
|
: super(null, null, initialData);
|
2021-03-04 10:46:37 +01:00
|
|
|
|
|
|
|
///
|
2021-03-11 14:20:10 +01:00
|
|
|
Future<PlaylistPage?> nextPage(YoutubeHttpClient httpClient) async {
|
2021-07-21 02:06:02 +02:00
|
|
|
if (initialData.continuationToken?.isEmpty == null) {
|
2021-03-04 10:46:37 +01:00
|
|
|
return null;
|
|
|
|
}
|
2021-07-21 02:06:02 +02:00
|
|
|
|
|
|
|
final data =
|
|
|
|
await httpClient.sendPost('browse', initialData.continuationToken!);
|
|
|
|
|
|
|
|
return PlaylistPage.id(playlistId, _InitialData(data));
|
2021-03-04 10:46:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
///
|
2021-07-21 02:06:02 +02:00
|
|
|
static Future<PlaylistPage> get(
|
|
|
|
YoutubeHttpClient httpClient,
|
|
|
|
String id,
|
|
|
|
) async {
|
2021-03-04 10:46:37 +01:00
|
|
|
var url = 'https://www.youtube.com/playlist?list=$id&hl=en&persist_hl=1';
|
2021-09-10 11:44:47 +02:00
|
|
|
return retry(httpClient, () async {
|
2021-03-04 10:46:37 +01:00
|
|
|
var raw = await httpClient.getString(url);
|
|
|
|
return PlaylistPage.parse(raw, id);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
2021-07-21 02:06:02 +02:00
|
|
|
PlaylistPage.parse(String raw, this.playlistId)
|
|
|
|
: super(parser.parse(raw), (root) => _InitialData(root));
|
2021-03-04 10:46:37 +01:00
|
|
|
}
|
|
|
|
|
2021-07-21 02:06:02 +02:00
|
|
|
class _InitialData extends InitialData {
|
|
|
|
_InitialData(JsonMap root) : super(root);
|
2021-03-04 10:46:37 +01:00
|
|
|
|
2021-03-18 22:22:55 +01:00
|
|
|
late final String? title = root
|
|
|
|
.get('metadata')
|
|
|
|
?.get('playlistMetadataRenderer')
|
|
|
|
?.getT<String>('title');
|
2021-03-04 10:46:37 +01:00
|
|
|
|
2021-03-11 14:20:10 +01:00
|
|
|
late final String? author = root
|
2021-03-04 10:46:37 +01:00
|
|
|
.get('sidebar')
|
|
|
|
?.get('playlistSidebarRenderer')
|
|
|
|
?.getList('items')
|
|
|
|
?.elementAtSafe(1)
|
|
|
|
?.get('playlistSidebarSecondaryInfoRenderer')
|
|
|
|
?.get('videoOwner')
|
|
|
|
?.get('videoOwnerRenderer')
|
|
|
|
?.get('title')
|
|
|
|
?.getT<List<dynamic>>('runs')
|
|
|
|
?.parseRuns();
|
|
|
|
|
2021-03-18 22:22:55 +01:00
|
|
|
late final String? description = root
|
|
|
|
.get('metadata')
|
|
|
|
?.get('playlistMetadataRenderer')
|
|
|
|
?.getT<String>('description');
|
2021-03-04 10:46:37 +01:00
|
|
|
|
2021-03-11 14:20:10 +01:00
|
|
|
late final int? viewCount = root
|
|
|
|
.get('sidebar')
|
2021-03-04 10:46:37 +01:00
|
|
|
?.get('playlistSidebarRenderer')
|
|
|
|
?.getList('items')
|
|
|
|
?.firstOrNull
|
|
|
|
?.get('playlistSidebarPrimaryInfoRenderer')
|
|
|
|
?.getList('stats')
|
|
|
|
?.elementAtSafe(1)
|
|
|
|
?.getT<String>('simpleText')
|
2021-08-29 13:26:21 +02:00
|
|
|
?.parseInt();
|
|
|
|
|
|
|
|
// sidebar.playlistSidebarRenderer.items[0].playlistSidebarPrimaryInfoRenderer.stats
|
|
|
|
late final int? videoCount = root
|
|
|
|
.get('sidebar')
|
|
|
|
?.get('playlistSidebarRenderer')
|
|
|
|
?.getList('items')
|
|
|
|
?.firstOrNull
|
|
|
|
?.get('playlistSidebarPrimaryInfoRenderer')
|
|
|
|
?.getList('stats')
|
|
|
|
?.elementAtSafe(0)
|
|
|
|
?.getList('runs')
|
|
|
|
?.firstOrNull
|
|
|
|
?.getT<String>('text')
|
2021-03-04 10:46:37 +01:00
|
|
|
?.parseInt();
|
|
|
|
|
2021-03-18 22:22:55 +01:00
|
|
|
late final String? continuationToken =
|
|
|
|
(videosContent ?? playlistVideosContent)
|
|
|
|
?.firstWhereOrNull((e) => e['continuationItemRenderer'] != null)
|
|
|
|
?.get('continuationItemRenderer')
|
|
|
|
?.get('continuationEndpoint')
|
|
|
|
?.get('continuationCommand')
|
|
|
|
?.getT<String>('token');
|
2021-03-04 10:46:37 +01:00
|
|
|
|
2021-07-21 02:06:02 +02:00
|
|
|
List<JsonMap>? get playlistVideosContent =>
|
2021-03-04 10:46:37 +01:00
|
|
|
root
|
|
|
|
.get('contents')
|
|
|
|
?.get('twoColumnBrowseResultsRenderer')
|
|
|
|
?.getList('tabs')
|
|
|
|
?.firstOrNull
|
|
|
|
?.get('tabRenderer')
|
|
|
|
?.get('content')
|
|
|
|
?.get('sectionListRenderer')
|
|
|
|
?.getList('contents')
|
|
|
|
?.firstOrNull
|
|
|
|
?.get('itemSectionRenderer')
|
|
|
|
?.getList('contents')
|
|
|
|
?.firstOrNull
|
|
|
|
?.get('playlistVideoListRenderer')
|
|
|
|
?.getList('contents') ??
|
2021-03-18 22:22:55 +01:00
|
|
|
root
|
|
|
|
.getList('onResponseReceivedActions')
|
|
|
|
?.firstOrNull
|
|
|
|
?.get('appendContinuationItemsAction')
|
|
|
|
?.getList('continuationItems');
|
2021-03-04 10:46:37 +01:00
|
|
|
|
2021-07-21 02:06:02 +02:00
|
|
|
late final List<JsonMap>? videosContent = root
|
2021-03-04 10:46:37 +01:00
|
|
|
.get('contents')
|
|
|
|
?.get('twoColumnSearchResultsRenderer')
|
|
|
|
?.get('primaryContents')
|
|
|
|
?.get('sectionListRenderer')
|
|
|
|
?.getList('contents') ??
|
2021-03-18 22:22:55 +01:00
|
|
|
root
|
|
|
|
.getList('onResponseReceivedCommands')
|
|
|
|
?.firstOrNull
|
|
|
|
?.get('appendContinuationItemsAction')
|
|
|
|
?.getList('continuationItems');
|
2021-03-04 10:46:37 +01:00
|
|
|
|
|
|
|
List<_Video> get playlistVideos =>
|
2021-03-18 22:22:55 +01:00
|
|
|
playlistVideosContent
|
|
|
|
?.where((e) => e['playlistVideoRenderer'] != null)
|
|
|
|
.map((e) => _Video(e['playlistVideoRenderer']))
|
|
|
|
.toList() ??
|
2021-03-04 10:46:37 +01:00
|
|
|
const [];
|
|
|
|
|
2021-07-21 02:06:02 +02:00
|
|
|
/* List<_Video> get videos =>
|
2021-03-04 10:46:37 +01:00
|
|
|
videosContent?.firstOrNull
|
|
|
|
?.get('itemSectionRenderer')
|
|
|
|
?.getList('contents')
|
|
|
|
?.where((e) => e['videoRenderer'] != null)
|
2021-03-11 14:20:10 +01:00
|
|
|
.map((e) => _Video(e))
|
|
|
|
.toList() ??
|
2021-07-21 02:06:02 +02:00
|
|
|
const [];*/
|
2021-03-04 10:46:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
class _Video {
|
|
|
|
// Json parsed map
|
2021-07-21 02:06:02 +02:00
|
|
|
final JsonMap root;
|
2021-03-04 10:46:37 +01:00
|
|
|
|
|
|
|
_Video(this.root);
|
|
|
|
|
2021-03-11 14:20:10 +01:00
|
|
|
String get id => root.getT<String>('videoId')!;
|
2021-03-04 10:46:37 +01:00
|
|
|
|
|
|
|
String get author =>
|
2021-03-11 14:20:10 +01:00
|
|
|
root.get('ownerText')?.getT<List<dynamic>>('runs')?.parseRuns() ??
|
|
|
|
root.get('shortBylineText')?.getT<List<dynamic>>('runs')?.parseRuns() ??
|
2021-03-04 10:46:37 +01:00
|
|
|
'';
|
|
|
|
|
|
|
|
String get channelId =>
|
2021-03-18 22:22:55 +01:00
|
|
|
root
|
|
|
|
.get('ownerText')
|
|
|
|
?.getList('runs')
|
|
|
|
?.firstOrNull
|
|
|
|
?.get('navigationEndpoint')
|
|
|
|
?.get('browseEndpoint')
|
|
|
|
?.getT<String>('browseId') ??
|
2021-03-04 10:46:37 +01:00
|
|
|
root
|
|
|
|
.get('shortBylineText')
|
|
|
|
?.getList('runs')
|
|
|
|
?.firstOrNull
|
|
|
|
?.get('navigationEndpoint')
|
|
|
|
?.get('browseEndpoint')
|
|
|
|
?.getT<String>('browseId') ??
|
|
|
|
'';
|
|
|
|
|
|
|
|
String get title => root.get('title')?.getList('runs')?.parseRuns() ?? '';
|
|
|
|
|
2021-03-18 22:22:55 +01:00
|
|
|
String get description =>
|
|
|
|
root.getList('descriptionSnippet')?.parseRuns() ?? '';
|
2021-03-04 10:46:37 +01:00
|
|
|
|
2021-03-18 22:22:55 +01:00
|
|
|
Duration? get duration =>
|
2021-04-26 15:33:47 +02:00
|
|
|
root.get('lengthText')?.getT<String>('simpleText')?.toDuration();
|
2021-03-04 10:46:37 +01:00
|
|
|
|
2021-03-18 22:22:55 +01:00
|
|
|
int get viewCount =>
|
|
|
|
root.get('viewCountText')?.getT<String>('simpleText')?.parseInt() ?? 0;
|
2021-03-04 10:46:37 +01:00
|
|
|
}
|