parseSearchResult function
Parses a search result from data
.
Implementation
JsonMap parseSearchResult(JsonMap data, String? resultType, String? category) {
final defaultOffset = ((resultType == null || resultType == 'album') ? 2 : 0);
final searchResult = <String, dynamic>{'category': category};
final videoType = nav(data, [
...PLAY_BUTTON,
'playNavigationEndpoint',
...NAVIGATION_VIDEO_TYPE,
], nullIfAbsent: true);
final String realResultType;
// Determine result type based on browseId
if (resultType == null) {
final browseId =
nav(data, NAVIGATION_BROWSE_ID, nullIfAbsent: true) as String?;
if (browseId != null) {
final mapping = {
'VM': 'playlist',
'RD': 'playlist',
'VL': 'playlist',
'MPLA': 'artist',
'MPRE': 'album',
'MPSP': 'podcast',
'MPED': 'episode',
'UC': 'artist',
};
realResultType =
mapping.entries
.firstWhere(
(e) => browseId.startsWith(e.key),
orElse: () => const MapEntry('', ''),
)
.value;
} else {
realResultType =
{
'MUSIC_VIDEO_TYPE_ATV': 'song',
'MUSIC_VIDEO_TYPE_PODCAST_EPISODE': 'episode',
}[videoType ?? ''] ??
'video';
}
} else {
realResultType = resultType;
}
searchResult['resultType'] = realResultType;
if (realResultType != 'artist') searchResult['title'] = getItemText(data, 0);
if (realResultType == 'artist') {
searchResult['artist'] = getItemText(data, 0);
parseMenuPlaylists(data, searchResult);
} else if (realResultType == 'album') {
searchResult['type'] = getItemText(data, 1);
final playNavigation = nav(data, [
...PLAY_BUTTON,
'playNavigationEndpoint',
], nullIfAbsent: true);
searchResult['playlistId'] = parseAlbumPlaylistIdIfExists(
playNavigation as JsonMap?,
);
} else if (realResultType == 'playlist') {
final flexItem = nav(getFlexColumnItem(data, 1), TEXT_RUNS) as List;
final hasAuthor = flexItem.length == defaultOffset + 3;
searchResult['itemCount'] =
(getItemText(data, 1, runIndex: defaultOffset + (hasAuthor ? 2 : 0)) ??
'')
.split(' ')[0];
if (searchResult['itemCount'] != null &&
(num.tryParse(searchResult['itemCount'] as String) != null)) {
searchResult['itemCount'] = toInt(searchResult['itemCount'] as String);
}
searchResult['author'] =
hasAuthor ? getItemText(data, 1, runIndex: defaultOffset) : null;
} else if (realResultType == 'station') {
searchResult['videoId'] = nav(data, NAVIGATION_VIDEO_ID);
searchResult['playlistId'] = nav(data, NAVIGATION_PLAYLIST_ID);
} else if (realResultType == 'profile') {
searchResult['name'] = getItemText(
data,
1,
runIndex: 2,
noneIfAbsent: true,
);
} else if (realResultType == 'song') {
searchResult['album'] = null;
if (data.containsKey('menu')) {
final toggleMenu = findObjectByKey(
nav(data, MENU_ITEMS) as List,
TOGGLE_MENU,
);
if (toggleMenu != null) {
searchResult['inLibrary'] = parseSongLibraryStatus(toggleMenu);
searchResult['feedbackTokens'] = parseSongMenuTokens(toggleMenu);
}
}
} else if (realResultType == 'upload') {
final browseId = nav(data, NAVIGATION_BROWSE_ID, nullIfAbsent: true);
if (browseId == null) {
final flexItems = [
nav(getFlexColumnItem(data, 0), ['text', 'runs'], nullIfAbsent: true),
nav(getFlexColumnItem(data, 1), ['text', 'runs'], nullIfAbsent: true),
];
if (flexItems[0] != null) {
searchResult['videoId'] = nav(
(flexItems[0] as List)[0],
NAVIGATION_VIDEO_ID,
nullIfAbsent: true,
);
searchResult['playlistId'] = nav(
(flexItems[0] as List)[0],
NAVIGATION_PLAYLIST_ID,
nullIfAbsent: true,
);
}
if (flexItems[1] != null) {
searchResult.addAll(parseSongRuns(flexItems[1] as List));
}
searchResult['resultType'] = 'song';
} else {
searchResult['browseId'] = browseId;
if ((searchResult['browseId'] as String).contains('artist')) {
searchResult['resultType'] = 'artist';
} else {
final flexItem2 = getFlexColumnItem(data, 1);
final runs =
flexItem2 != null
? List.generate(
((flexItem2['text'] as JsonMap)['runs'] as List).length,
(i) {
final run =
((flexItem2['text'] as JsonMap)['runs'] as List)[i];
return i.isEven ? (run as JsonMap)['text'] : null;
},
).whereType<String>().toList()
: <String>[];
if (runs.length > 1) searchResult['artist'] = runs[1];
if (runs.length > 2) searchResult['releaseDate'] = runs[2];
searchResult['resultType'] = 'album';
}
}
}
if (['song', 'video', 'episode'].contains(realResultType)) {
searchResult['videoId'] = nav(data, [
...PLAY_BUTTON,
'playNavigationEndpoint',
'watchEndpoint',
'videoId',
], nullIfAbsent: true);
searchResult['videoType'] = videoType;
}
if (['song', 'video', 'album'].contains(realResultType)) {
searchResult['duration'] = null;
searchResult['year'] = null;
final flexItem = getFlexColumnItem(data, 1);
final runs = (flexItem!['text'] as JsonMap)['runs'] as List;
final flexItem2 = getFlexColumnItem(data, 2);
if (flexItem2 != null) {
runs.addAll([
{'text': ''},
...(flexItem2['text'] as JsonMap)['runs'] as Iterable,
]);
}
final songInfo = parseSongRuns(runs, skipTypeSpec: true);
searchResult.addAll(songInfo);
}
if ([
'artist',
'album',
'playlist',
'profile',
'podcast',
].contains(realResultType)) {
searchResult['browseId'] = nav(
data,
NAVIGATION_BROWSE_ID,
nullIfAbsent: true,
);
}
if (['song', 'album'].contains(realResultType)) {
searchResult['isExplicit'] =
nav(data, BADGE_LABEL, nullIfAbsent: true) != null;
}
if (realResultType == 'episode') {
final flexItem = getFlexColumnItem(data, 1);
final runs = (nav(flexItem, TEXT_RUNS) as List).sublist(defaultOffset);
final hasDate = runs.length > 1 ? 1 : 0;
searchResult['live'] =
nav(data, ['badges', 0, 'liveBadgeRenderer'], nullIfAbsent: true) !=
null;
if (hasDate > 0) searchResult['date'] = (runs[0] as JsonMap)['text'];
searchResult['podcast'] = parseIdName(runs[hasDate * 2] as JsonMap?);
}
searchResult['thumbnails'] = nav(data, THUMBNAILS, nullIfAbsent: true);
return searchResult;
}