getArtist method
- String channelId
Get information about an artist and their top releases (songs, albums, singles, videos, and related artists).
The top lists contain pointers for getting the full list of releases.
Possible content types for getArtist are:
- songs
- albums
- singles
- shows
- videos
- episodes
- podcasts
- related
Each of these content keys in the response contains
results and possibly browseId and params:
- For songs/videos, pass the
browseIdto PlaylistsMixin.getPlaylist. - For albums/singles/shows, pass
browseIdandparamsto getArtistAlbums.
Arguments:
channelIdchannel id of the artist.
Returns Map with requested information.
WARNING:
- The returned channelId is not the same as the one passed to the function.
It should be used only with
subscribeArtist. // TODO subscribeArtist is currently missing
Example:
{
"description": "Oasis were ...",
"descriptionRuns": [
{
"text":"Oasis were ..."
}
],
"views": "3,693,390,359 views",
"name": "Oasis",
"channelId": "UCUDVBtnOQi4c7E8jebpjc9Q",
"shuffleId": "RDAOkjHYJjL1a3xspEyVkhHAsg",
"radioId": "RDEMkjHYJjL1a3xspEyVkhHAsg",
"subscribers": "3.86M",
"monthlyListeners": "29.1M",
"subscribed": false,
"thumbnails": [...],
"songs": {
"browseId": "VLPLMpM3Z0118S42R1npOhcjoakLIv1aqnS1",
"results": [
{
"videoId": "ZrOKjDZOtkA",
"title": "Wonderwall (Remastered)",
"thumbnails": [...],
"artist": "Oasis",
"album": "(What's The Story) Morning Glory? (Remastered)"
}
]
},
"albums": {
"results": [
{
"title": "Familiar To Millions",
"thumbnails": [...],
"year": "2018",
"browseId": "MPREb_AYetWMZunqA"
}
],
"browseId": "UCmMUZbaYdNH0bEd1PAlAqsA",
"params": "6gPTAUNwc0JDbndLYlFBQV..."
},
"singles": {
"results": [
{
"title": "Stand By Me (Mustique Demo)",
"thumbnails": [...],
"type": "Single",
"year": "2016",
"browseId": "MPREb_7MPKLhibN5G"
}
],
"browseId": "UCmMUZbaYdNH0bEd1PAlAqsA",
"params": "6gPTAUNwc0JDbndLYlFBQV..."
},
"videos": {
"results": [
{
"title": "Wonderwall",
"thumbnails": [...],
"views": "358M",
"videoId": "bx1Bh8ZvH84",
"playlistId": "PLMpM3Z0118S5xuNckw1HUcj1D021AnMEB"
}
],
"browseId": "VLPLMpM3Z0118S5xuNckw1HUcj1D021AnMEB"
},
"related": {
"results": [
{
"browseId": "UCt2KxZpY5D__kapeQ8cauQw",
"subscribers": "450K",
"title": "The Verve"
},
{
"browseId": "UCwK2Grm574W1u-sBzLikldQ",
"subscribers": "341K",
"title": "Liam Gallagher"
},
...
]
}
}
The difference between description and descriptionRuns is that description is just the raw text of the description and does not have any knowledge of the hyperlinks in the description.
Whereas descriptionRuns is a list of dictionaries which contains the text runs of the description that is aware of the hyperlinks in the description. There are two types of text runs.
1. PlainText: which has format {"text": string}
2. HyperLink: which has format {"text": string, "url": string}
Implementation
Future<JsonMap> getArtist(String channelId) async {
final String cleanChannelId;
if (channelId.startsWith('MPLA')) {
cleanChannelId = channelId.substring(4);
} else {
cleanChannelId = channelId;
}
const endpoint = 'browse';
final body = JsonMap.from({'browseId': cleanChannelId});
final response = await sendRequest(endpoint, body);
final results = List<JsonMap>.from(
nav(response, SINGLE_COLUMN_TAB + SECTION_LIST) as List,
);
final header =
JsonMap.from(
response['header']! as JsonMap,
)['musicImmersiveHeaderRenderer']
as JsonMap? ??
JsonMap.from(
response['header']! as JsonMap,
)['musicVisualHeaderRenderer']
as JsonMap;
final artist = <String, dynamic>{
'name': nav(header, TITLE_TEXT),
'description': null,
'descriptionRuns': [],
'views': null,
};
final descriptionShelf = findObjectByKey(
results,
DESCRIPTION_SHELF[0] as String,
isKey: true,
);
if (descriptionShelf != null) {
final parsedDescriptionRuns = parseDescriptionRuns(
nav(descriptionShelf, DESCRIPTION_RUN_LIST),
);
final description = parsedDescriptionRuns.$1;
final descriptionRuns = parsedDescriptionRuns.$2;
artist['description'] = description;
artist['descriptionRuns'] = descriptionRuns;
artist['views'] =
descriptionShelf.containsKey('subheader')
? (((descriptionShelf['subheader']! as JsonMap)['runs']!
as List)[0]
as JsonMap)['text']
: null;
}
final subscriptionButton =
(header['subscriptionButton']! as JsonMap)['subscribeButtonRenderer']
as JsonMap;
artist['channelId'] = subscriptionButton['channelId'];
artist['shuffleId'] = nav(header, [
'playButton',
'buttonRenderer',
...NAVIGATION_PLAYLIST_ID,
], nullIfAbsent: true);
artist['radioId'] = nav(header, [
'startRadioButton',
'buttonRenderer',
...NAVIGATION_PLAYLIST_ID,
], nullIfAbsent: true);
artist['subscribers'] = nav(subscriptionButton, [
'subscriberCountText',
'runs',
0,
'text',
], nullIfAbsent: true);
artist['monthlyListeners'] = nav(header, [
'monthlyListenerCount',
'runs',
0,
'text',
], nullIfAbsent: true);
artist['monthlyListeners'] = (artist['monthlyListeners'] as String?)
?.replaceAll(' monthly audience', '');
artist['subscribed'] = subscriptionButton['subscribed'];
artist['thumbnails'] = nav(header, THUMBNAILS, nullIfAbsent: true);
artist['songs'] = JsonMap.from({'browseId': null});
if (results.isNotEmpty && results[0].containsKey('musicShelfRenderer')) {
// API sometimes does not return songs
final musicShelf = nav(results[0], MUSIC_SHELF) as JsonMap;
if ((nav(musicShelf, TITLE) as JsonMap).containsKey(
'navigationEndpoint',
)) {
(artist['songs'] as JsonMap)['browseId'] = nav(
musicShelf,
TITLE + NAVIGATION_BROWSE_ID,
);
}
(artist['songs'] as JsonMap)['results'] = parsePlaylistItems(
List<JsonMap>.from(musicShelf['contents'] as List),
);
}
artist.addAll(parser.parseChannelContents(results));
return artist;
}