parsePlaylistItem function
Parses item from playlist.
Implementation
JsonMap? parsePlaylistItem(
JsonMap data, {
bool isAlbum = false,
bool isCollaborative = false,
}) {
String? videoId;
String? setVideoId;
String? creditsBrowseId;
String? like;
// If the item has a menu, find its setVideoId
if (data.containsKey('menu')) {
for (final item in List<JsonMap>.from(nav(data, MENU_ITEMS) as List)) {
if (item.containsKey('menuServiceItemRenderer')) {
final menuService = nav(item, MENU_SERVICE) as JsonMap;
if (menuService.containsKey('playlistEditEndpoint')) {
setVideoId =
nav(menuService, [
'playlistEditEndpoint',
'actions',
0,
'setVideoId',
], nullIfAbsent: true)
as String?;
videoId =
nav(menuService, [
'playlistEditEndpoint',
'actions',
0,
'removedVideoId',
], nullIfAbsent: true)
as String?;
}
} else if (item.containsKey(MNIR)) {
final maybeCreditsBrowseId =
nav(item, [MNIR, ...NAVIGATION_BROWSE_ID], nullIfAbsent: true)
as String?;
if (maybeCreditsBrowseId != null &&
maybeCreditsBrowseId.startsWith('MPTC')) {
creditsBrowseId = maybeCreditsBrowseId;
}
}
}
}
final songMenuData = <String, dynamic>{
'inLibrary': null,
'pinnedToListenAgain': null,
...parseSongMenuData(data),
};
final playButton = nav(data, PLAY_BUTTON, nullIfAbsent: true) as JsonMap?;
if (playButton != null && playButton.containsKey('playNavigationEndpoint')) {
videoId =
nav(data, [
...PLAY_BUTTON,
'playNavigationEndpoint',
'watchEndpoint',
'videoId',
])
as String;
if (data.containsKey('menu')) {
like = nav(data, MENU_LIKE_STATUS, nullIfAbsent: true) as String?;
}
}
var isAvailable = true;
if (data.containsKey('musicItemRendererDisplayPolicy')) {
isAvailable =
data['musicItemRendererDisplayPolicy'] !=
'MUSIC_ITEM_RENDERER_DISPLAY_POLICY_GREY_OUT';
}
final usePresetColumns = (!isAvailable || isAlbum) ? true : null;
int? titleIndex = usePresetColumns == true ? 0 : null;
int? artistIndex = usePresetColumns == true ? 1 : null;
int? durationIndex;
int? albumIndex = isCollaborative ? 3 : (usePresetColumns == true ? 2 : null);
final userChannelIndexes = <int>[];
int? unrecognizedIndex;
final flexColumns = data['flexColumns'] as List;
for (var index = 0; index < flexColumns.length; index++) {
final flexColumnItem = getFlexColumnItem(data, index);
final navigationEndpoint =
nav(flexColumnItem, [
...TEXT_RUN,
'navigationEndpoint',
], nullIfAbsent: true)
as JsonMap?;
if (navigationEndpoint == null) {
final run = nav(flexColumnItem, TEXT_RUN, nullIfAbsent: true) as JsonMap?;
if (run != null && run.containsKey('text')) {
final parsed = parseSongRun(run);
if (parsed['type'] == 'duration') {
durationIndex = index;
} else {
unrecognizedIndex ??= index;
}
}
continue;
}
if (navigationEndpoint.containsKey('watchEndpoint')) {
titleIndex = index;
} else if (navigationEndpoint.containsKey('browseEndpoint')) {
final pageType = nav(navigationEndpoint, [
'browseEndpoint',
'browseEndpointContextSupportedConfigs',
'browseEndpointContextMusicConfig',
'pageType',
]);
if (pageType == 'MUSIC_PAGE_TYPE_ARTIST' ||
pageType == 'MUSIC_PAGE_TYPE_UNKNOWN') {
artistIndex = index;
} else if (pageType == 'MUSIC_PAGE_TYPE_ALBUM' ||
pageType == 'MUSIC_PAGE_TYPE_AUDIOBOOK') {
albumIndex = index;
} else if (pageType == 'MUSIC_PAGE_TYPE_USER_CHANNEL') {
userChannelIndexes.add(index);
} else if (pageType == 'MUSIC_PAGE_TYPE_NON_MUSIC_AUDIO_TRACK_PAGE') {
titleIndex = index;
}
}
}
artistIndex ??= unrecognizedIndex;
if (artistIndex == null && userChannelIndexes.isNotEmpty) {
artistIndex = userChannelIndexes.last;
}
final title = titleIndex != null ? getItemText(data, titleIndex) : null;
if (title == 'Song deleted') {
return null;
}
final artists =
artistIndex != null ? parseSongArtists(data, artistIndex) : null;
final album = albumIndex != null ? parseSongAlbum(data, albumIndex) : null;
final views = isAlbum ? getItemText(data, 2) : null;
String? duration =
durationIndex != null ? getItemText(data, durationIndex) : null;
if (data.containsKey('fixedColumns')) {
final fixedColumn = getFixedColumnItem(data, 0);
if (nav(fixedColumn, ['text', 'simpleText'], nullIfAbsent: true) != null) {
duration = nav(fixedColumn, ['text', 'simpleText']) as String;
} else {
duration = nav(fixedColumn, TEXT_RUN_TEXT) as String;
}
}
final thumbnails = nav(data, THUMBNAILS, nullIfAbsent: true);
final isExplicit = nav(data, BADGE_LABEL, nullIfAbsent: true) != null;
final videoType = nav(data, [
...MENU_ITEMS,
0,
MNIR,
'navigationEndpoint',
...NAVIGATION_VIDEO_TYPE,
], nullIfAbsent: true);
final votingStatus =
nav(data, ENGAGEMENT_BAR, nullIfAbsent: true) as JsonMap?;
final communityVoteStatus =
votingStatus == null
? null
: {
'netVoteValue': votingStatus['votes'],
'status': VoteStatus.fromValue(votingStatus['status'] as String),
};
final song = <String, dynamic>{
'videoId': videoId,
'title': title,
'artists': artists,
'album': album,
'likeStatus': like,
...songMenuData,
'thumbnails': thumbnails,
'isAvailable': isAvailable,
'isExplicit': isExplicit,
'videoType': videoType,
'views': views,
'communityVoteStatus': communityVoteStatus,
};
if (isAlbum) {
song['trackNumber'] =
isAvailable
? int.parse(nav(data, ['index', 'runs', 0, 'text']) as String)
: null;
}
if (duration != null && duration.isNotEmpty) {
song['duration'] = duration;
song['duration_seconds'] = parseDuration(duration);
}
if (setVideoId != null) {
song['setVideoId'] = setVideoId;
}
if (creditsBrowseId != null) {
song['creditsBrowseId'] = creditsBrowseId;
}
return song;
}