getPlaylist method
Returns a list of playlist items.
playlistId
Playlist id.limit
How many songs to return.null
retrieves them all. (Default:100
).related
Whether to fetch 10 related playlists or not. (Default:false
).suggestionsLimit
How many suggestions to return. The result is a list of suggested playlist items (videos) contained in asuggestions
key. 7 items are retrieved in each internal request. (Default:0
).
Returns Map with information about the playlist.
The key tracks
contains a List of playlistItem
Maps
The result is in the following format:
{
"id": "PLQwVIlKxHM6qv-o99iX9R85og7IzF9YS_",
"privacy": "PUBLIC",
"title": "New EDM This Week 03/13/2020",
"thumbnails": [...],
"description": "Weekly r/EDM new release roundup. Created with github.com/sigma67/spotifyplaylist_to_gmusic",
"author": {
"name": "sigmatics",
"id": "..."
},
"year": "2020",
"duration": "6+ hours",
"duration_seconds": 52651,
"trackCount": 237,
"suggestions": [
{
"videoId": "HLCsfOykA94",
"title": "Mambo (GATTÜSO Remix)",
"artists": [
{
"name": "Nikki Vianna",
"id": "UCMW5eSIO1moVlIBLQzq4PnQ"
}
],
"album": {
"name": "Mambo (GATTÜSO Remix)",
"id": "MPREb_jLeQJsd7U9w"
},
"likeStatus": "LIKE",
"thumbnails": [...],
"isAvailable": true,
"isExplicit": false,
"duration": "3:32",
"duration_seconds": 212,
"setVideoId": "to_be_updated_by_client"
}
],
"related": [
{
"title": "Presenting MYRNE",
"playlistId": "RDCLAK5uy_mbdO3_xdD4NtU1rWI0OmvRSRZ8NH4uJCM",
"thumbnails": [...],
"description": "Playlist • YouTube Music"
}
],
"tracks": [
{
"videoId": "bjGppZKiuFE",
"title": "Lost",
"artists": [
{
"name": "Guest Who",
"id": "UCkgCRdnnqWnUeIH7EIc3dBg"
},
{
"name": "Kate Wild",
"id": "UCwR2l3JfJbvB6aq0RnnJfWg"
}
],
"album": {
"name": "Lost",
"id": "MPREb_PxmzvDuqOnC"
},
"duration": "2:58",
"duration_seconds": 178,
"setVideoId": "748EE8...",
"likeStatus": "INDIFFERENT",
"thumbnails": [...],
"isAvailable": true,
"isExplicit": false,
"videoType": "MUSIC_VIDEO_TYPE_OMV",
"feedbackTokens": {
"add": "AB9zfpJxtvrU...",
"remove": "AB9zfpKTyZ..."
}
}
]
}
The setVideoId
is the unique id of this playlist item and
needed for moving/removing playlist items.
Implementation
Future<JsonMap> getPlaylist(
String playlistId, {
int? limit = 100,
bool related = false,
int suggestionsLimit = 0,
}) async {
checkAuth();
final browseId = playlistId.startsWith('VL') ? playlistId : 'VL$playlistId';
final body = {'browseId': browseId};
const endpoint = 'browse';
Future<JsonMap> requestFunc(String additionalParams) =>
sendRequest(endpoint, body, additionalParams: additionalParams);
final response = await requestFunc('');
Future<JsonMap> requestFuncContinuations(JsonMap body) =>
sendRequest(endpoint, body);
if (playlistId.startsWith('OLA') || playlistId.startsWith('VLOLA')) {
return parseAudioPlaylist(response, limit, requestFuncContinuations);
}
final headerData =
nav(response, [
...TWO_COLUMN_RENDERER,
...TAB_CONTENT,
...SECTION_LIST_ITEM,
])
as JsonMap;
final sectionList =
nav(response, [...TWO_COLUMN_RENDERER, 'secondaryContents', ...SECTION])
as JsonMap;
final playlist = <String, dynamic>{};
playlist['owned'] = headerData.containsKey(
EDITABLE_PLAYLIST_DETAIL_HEADER[0],
);
late JsonMap header;
if (!(playlist['owned'] as bool)) {
header = nav(headerData, RESPONSIVE_HEADER) as JsonMap;
playlist['id'] = nav(header, [
'buttons',
1,
'musicPlayButtonRenderer',
'playNavigationEndpoint',
...WATCH_PLAYLIST_ID,
], nullIfAbsent: true);
playlist['privacy'] = 'PUBLIC';
} else {
playlist['id'] = nav(headerData, [
...EDITABLE_PLAYLIST_DETAIL_HEADER,
...PLAYLIST_ID,
]);
header =
nav(headerData, [
...EDITABLE_PLAYLIST_DETAIL_HEADER,
...HEADER,
...RESPONSIVE_HEADER,
])
as JsonMap;
playlist['privacy'] =
(((headerData[EDITABLE_PLAYLIST_DETAIL_HEADER[0]]
as JsonMap)['editHeader']
as JsonMap)['musicPlaylistEditHeaderRenderer']
as JsonMap)['privacy'];
}
final descriptionShelf =
nav(header, ['description', ...DESCRIPTION_SHELF], nullIfAbsent: true)
as JsonMap?;
playlist['description'] =
descriptionShelf != null
? ((descriptionShelf['description'] as JsonMap)['runs']
as List<JsonMap>)
.map((run) => run['text'])
.join()
: null;
playlist.addAll(parsePlaylistHeaderMeta(header));
playlist.addAll(
parseSongRuns(
(nav(header, SUBTITLE_RUNS) as List).sublist(
2 + ((playlist['owned'] as bool) ? 2 : 0),
),
),
);
// suggestions and related are missing e.g. on liked songs
playlist['related'] = [];
if (sectionList.containsKey('continuations')) {
var additionalParams = getContinuationParams(sectionList);
if ((playlist['owned'] as bool) && (suggestionsLimit > 0 || related)) {
List parseFunc(results) => parsePlaylistItems(results as List<JsonMap>);
final suggested = await requestFunc(additionalParams);
final continuation = nav(suggested, SECTION_LIST_CONTINUATION);
additionalParams = getContinuationParams(continuation as JsonMap);
final suggestionsShelf = nav(continuation, [
...CONTENT,
...MUSIC_SHELF,
]);
playlist['suggestions'] = getContinuationContents(
suggestionsShelf as JsonMap,
parseFunc,
);
(playlist['suggestions'] as List).addAll(
await getReloadableContinuations(
suggestionsShelf,
'musicShelfContinuation',
suggestionsLimit -
((playlist['suggestions'] as List).length), // TODO
requestFunc,
parseFunc,
),
);
}
if (related) {
final relatedResponse = await requestFunc(additionalParams);
final continuation = nav(
relatedResponse,
SECTION_LIST_CONTINUATION,
nullIfAbsent: true,
);
if (continuation != null) {
Future<List> parseFunc(results) =>
parseContentList(results as List<JsonMap>, parsePlaylist);
playlist['related'] = getContinuationContents(
nav(continuation, [...CONTENT, ...CAROUSEL]) as JsonMap,
parseFunc,
);
}
}
}
playlist['tracks'] = [];
final contentData =
nav(sectionList, [...CONTENT, 'musicPlaylistShelfRenderer']) as JsonMap;
if (contentData.containsKey('contents')) {
playlist['tracks'] = parsePlaylistItems(
contentData['contents'] as List<JsonMap>,
);
List parseFunc(contents) => parsePlaylistItems(contents as List<JsonMap>);
(playlist['tracks'] as List).addAll(
await getContinuations2025(
contentData,
limit,
requestFuncContinuations,
parseFunc,
),
);
}
playlist['duration_seconds'] = sumTotalDuration(playlist);
return playlist;
}