✨ Better explore ever
This commit is contained in:
214
lib/services/spotify/spotify_endpoints.dart
Normal file
214
lib/services/spotify/spotify_endpoints.dart
Normal file
@ -0,0 +1,214 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:rhythm_box/services/spotify/spotify_feed.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
||||
class CustomSpotifyEndpoints {
|
||||
static const _baseUrl = 'https://api.spotify.com/v1';
|
||||
final String accessToken;
|
||||
final Dio _client;
|
||||
|
||||
CustomSpotifyEndpoints(this.accessToken)
|
||||
: _client = Dio(
|
||||
BaseOptions(
|
||||
baseUrl: _baseUrl,
|
||||
responseType: ResponseType.json,
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
if (accessToken.isNotEmpty)
|
||||
'authorization': 'Bearer $accessToken',
|
||||
'accept': 'application/json',
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// views API
|
||||
|
||||
/// Get a single view of given genre
|
||||
///
|
||||
/// Currently known genres are:
|
||||
/// - new-releases-page
|
||||
/// - made-for-x-hub (it requires authentication)
|
||||
/// - my-mix-genres (it requires authentication)
|
||||
/// - artist-seed-mixes (it requires authentication)
|
||||
/// - my-mix-decades (it requires authentication)
|
||||
/// - my-mix-moods (it requires authentication)
|
||||
/// - podcasts-and-more (it requires authentication)
|
||||
/// - uniquely-yours-in-hub (it requires authentication)
|
||||
/// - made-for-x-dailymix (it requires authentication)
|
||||
/// - made-for-x-discovery (it requires authentication)
|
||||
Future<Map<String, dynamic>> getView(
|
||||
String view, {
|
||||
int limit = 20,
|
||||
int contentLimit = 10,
|
||||
List<String> types = const [
|
||||
'album',
|
||||
'playlist',
|
||||
'artist',
|
||||
'show',
|
||||
'station',
|
||||
'episode',
|
||||
'merch',
|
||||
'artist_concerts',
|
||||
'uri_link'
|
||||
],
|
||||
String imageStyle = 'gradient_overlay',
|
||||
String includeExternal = 'audio',
|
||||
String? locale,
|
||||
Market? market,
|
||||
Market? country,
|
||||
}) async {
|
||||
if (accessToken.isEmpty) {
|
||||
throw Exception('[CustomSpotifyEndpoints.getView]: accessToken is empty');
|
||||
}
|
||||
|
||||
final queryParams = {
|
||||
'limit': limit.toString(),
|
||||
'content_limit': contentLimit.toString(),
|
||||
'types': types.join(','),
|
||||
'image_style': imageStyle,
|
||||
'include_external': includeExternal,
|
||||
'timestamp': DateTime.now().toUtc().toIso8601String(),
|
||||
if (locale != null) 'locale': locale,
|
||||
if (market != null) 'market': market.name,
|
||||
if (country != null) 'country': country.name,
|
||||
}.entries.map((e) => '${e.key}=${e.value}').join('&');
|
||||
|
||||
final res = await _client.getUri(
|
||||
Uri.parse('$_baseUrl/views/$view?$queryParams'),
|
||||
);
|
||||
|
||||
if (res.statusCode == 200) {
|
||||
return res.data;
|
||||
} else {
|
||||
throw Exception(
|
||||
'[CustomSpotifyEndpoints.getView]: Failed to get view'
|
||||
'\nStatus code: ${res.statusCode}'
|
||||
'\nBody: ${res.data}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<String>> listGenreSeeds() async {
|
||||
final res = await _client.getUri(
|
||||
Uri.parse('$_baseUrl/recommendations/available-genre-seeds'),
|
||||
);
|
||||
|
||||
if (res.statusCode == 200) {
|
||||
final body = res.data;
|
||||
return List<String>.from(body['genres'] ?? []);
|
||||
} else {
|
||||
throw Exception(
|
||||
'[CustomSpotifyEndpoints.listGenreSeeds]: Failed to get genre seeds'
|
||||
'\nStatus code: ${res.statusCode}'
|
||||
'\nBody: ${res.data}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<SpotifyHomeFeed> getHomeFeed({
|
||||
required String spTCookie,
|
||||
required Market country,
|
||||
}) async {
|
||||
final headers = {
|
||||
'app-platform': 'WebPlayer',
|
||||
'authorization': 'Bearer $accessToken',
|
||||
'content-type': 'application/json;charset=UTF-8',
|
||||
'dnt': '1',
|
||||
'origin': 'https://open.spotify.com',
|
||||
'referer': 'https://open.spotify.com/'
|
||||
};
|
||||
final response = await _client.getUri(
|
||||
Uri(
|
||||
scheme: 'https',
|
||||
host: 'api-partner.spotify.com',
|
||||
path: '/pathfinder/v1/query',
|
||||
queryParameters: {
|
||||
'operationName': 'home',
|
||||
'variables': jsonEncode({
|
||||
'timeZone': tz.local.name,
|
||||
'sp_t': spTCookie,
|
||||
'country': country.name,
|
||||
'facet': null,
|
||||
'sectionItemsLimit': 10
|
||||
}),
|
||||
'extensions': jsonEncode(
|
||||
{
|
||||
'persistedQuery': {
|
||||
'version': 1,
|
||||
|
||||
/// GraphQL persisted Query hash
|
||||
/// This can change overtime. We've to lookout for it
|
||||
/// Docs: https://www.apollographql.com/docs/graphos/operations/persisted-queries/
|
||||
'sha256Hash':
|
||||
'eb3fba2d388cf4fc4d696b1757a58584e9538a3b515ea742e9cc9465807340be',
|
||||
}
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
options: Options(headers: headers),
|
||||
);
|
||||
|
||||
final data = SpotifyHomeFeed.fromJson(
|
||||
transformHomeFeedJsonMap(response.data),
|
||||
);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
Future<SpotifyHomeFeedSection> getHomeFeedSection(
|
||||
String sectionUri, {
|
||||
required String spTCookie,
|
||||
required Market country,
|
||||
}) async {
|
||||
final headers = {
|
||||
'app-platform': 'WebPlayer',
|
||||
'authorization': 'Bearer $accessToken',
|
||||
'content-type': 'application/json;charset=UTF-8',
|
||||
'dnt': '1',
|
||||
'origin': 'https://open.spotify.com',
|
||||
'referer': 'https://open.spotify.com/'
|
||||
};
|
||||
final response = await _client.getUri(
|
||||
Uri(
|
||||
scheme: 'https',
|
||||
host: 'api-partner.spotify.com',
|
||||
path: '/pathfinder/v1/query',
|
||||
queryParameters: {
|
||||
'operationName': 'homeSection',
|
||||
'variables': jsonEncode({
|
||||
'timeZone': tz.local.name,
|
||||
'sp_t': spTCookie,
|
||||
'country': country.name,
|
||||
'uri': sectionUri
|
||||
}),
|
||||
'extensions': jsonEncode(
|
||||
{
|
||||
'persistedQuery': {
|
||||
'version': 1,
|
||||
|
||||
/// GraphQL persisted Query hash
|
||||
/// This can change overtime. We've to lookout for it
|
||||
/// Docs: https://www.apollographql.com/docs/graphos/operations/persisted-queries/
|
||||
'sha256Hash':
|
||||
'eb3fba2d388cf4fc4d696b1757a58584e9538a3b515ea742e9cc9465807340be',
|
||||
}
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
options: Options(headers: headers),
|
||||
);
|
||||
|
||||
final data = SpotifyHomeFeedSection.fromJson(
|
||||
transformSectionItemJsonMap(
|
||||
response.data['data']['homeSections']['sections'][0],
|
||||
),
|
||||
);
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
247
lib/services/spotify/spotify_feed.dart
Normal file
247
lib/services/spotify/spotify_feed.dart
Normal file
@ -0,0 +1,247 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
|
||||
part 'spotify_feed.freezed.dart';
|
||||
part 'spotify_feed.g.dart';
|
||||
|
||||
@freezed
|
||||
class SpotifySectionPlaylist with _$SpotifySectionPlaylist {
|
||||
const SpotifySectionPlaylist._();
|
||||
|
||||
const factory SpotifySectionPlaylist({
|
||||
required String description,
|
||||
required String format,
|
||||
required List<SpotifySectionItemImage> images,
|
||||
required String name,
|
||||
required String owner,
|
||||
required String uri,
|
||||
}) = _SpotifySectionPlaylist;
|
||||
|
||||
factory SpotifySectionPlaylist.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotifySectionPlaylistFromJson(json);
|
||||
|
||||
String get id => uri.split(':').last;
|
||||
|
||||
Playlist get asPlaylist {
|
||||
return Playlist()
|
||||
..id = id
|
||||
..name = name
|
||||
..description = description
|
||||
..collaborative = false
|
||||
..images = images.map((e) => e.asImage).toList()
|
||||
..owner = (User()..displayName = 'Spotify')
|
||||
..uri = uri
|
||||
..type = 'playlist';
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SpotifySectionArtist with _$SpotifySectionArtist {
|
||||
const SpotifySectionArtist._();
|
||||
|
||||
const factory SpotifySectionArtist({
|
||||
required String name,
|
||||
required String uri,
|
||||
required List<SpotifySectionItemImage> images,
|
||||
}) = _SpotifySectionArtist;
|
||||
|
||||
factory SpotifySectionArtist.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotifySectionArtistFromJson(json);
|
||||
|
||||
String get id => uri.split(':').last;
|
||||
|
||||
Artist get asArtist {
|
||||
return Artist()
|
||||
..id = id
|
||||
..name = name
|
||||
..images = images.map((e) => e.asImage).toList()
|
||||
..type = 'artist'
|
||||
..uri = uri;
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SpotifySectionAlbum with _$SpotifySectionAlbum {
|
||||
const SpotifySectionAlbum._();
|
||||
|
||||
const factory SpotifySectionAlbum({
|
||||
required List<SpotifySectionAlbumArtist> artists,
|
||||
required List<SpotifySectionItemImage> images,
|
||||
required String name,
|
||||
required String uri,
|
||||
}) = _SpotifySectionAlbum;
|
||||
|
||||
factory SpotifySectionAlbum.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotifySectionAlbumFromJson(json);
|
||||
|
||||
String get id => uri.split(':').last;
|
||||
|
||||
Album get asAlbum {
|
||||
return Album()
|
||||
..id = id
|
||||
..name = name
|
||||
..artists = artists.map((a) => a.asArtist).toList()
|
||||
..albumType = AlbumType.album
|
||||
..images = images.map((e) => e.asImage).toList()
|
||||
..uri = uri;
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SpotifySectionAlbumArtist with _$SpotifySectionAlbumArtist {
|
||||
const SpotifySectionAlbumArtist._();
|
||||
|
||||
const factory SpotifySectionAlbumArtist({
|
||||
required String name,
|
||||
required String uri,
|
||||
}) = _SpotifySectionAlbumArtist;
|
||||
|
||||
factory SpotifySectionAlbumArtist.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotifySectionAlbumArtistFromJson(json);
|
||||
|
||||
String get id => uri.split(':').last;
|
||||
|
||||
Artist get asArtist {
|
||||
return Artist()
|
||||
..id = id
|
||||
..name = name
|
||||
..type = 'artist'
|
||||
..uri = uri;
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SpotifySectionItemImage with _$SpotifySectionItemImage {
|
||||
const SpotifySectionItemImage._();
|
||||
|
||||
const factory SpotifySectionItemImage({
|
||||
required num? height,
|
||||
required String url,
|
||||
required num? width,
|
||||
}) = _SpotifySectionItemImage;
|
||||
|
||||
factory SpotifySectionItemImage.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotifySectionItemImageFromJson(json);
|
||||
|
||||
Image get asImage {
|
||||
return Image()
|
||||
..height = height?.toInt()
|
||||
..width = width?.toInt()
|
||||
..url = url;
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SpotifyHomeFeedSectionItem with _$SpotifyHomeFeedSectionItem {
|
||||
factory SpotifyHomeFeedSectionItem({
|
||||
required String typename,
|
||||
SpotifySectionPlaylist? playlist,
|
||||
SpotifySectionArtist? artist,
|
||||
SpotifySectionAlbum? album,
|
||||
}) = _SpotifyHomeFeedSectionItem;
|
||||
|
||||
factory SpotifyHomeFeedSectionItem.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotifyHomeFeedSectionItemFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SpotifyHomeFeedSection with _$SpotifyHomeFeedSection {
|
||||
factory SpotifyHomeFeedSection({
|
||||
required String typename,
|
||||
String? title,
|
||||
required String uri,
|
||||
required List<SpotifyHomeFeedSectionItem> items,
|
||||
}) = _SpotifyHomeFeedSection;
|
||||
|
||||
factory SpotifyHomeFeedSection.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotifyHomeFeedSectionFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SpotifyHomeFeed with _$SpotifyHomeFeed {
|
||||
factory SpotifyHomeFeed({
|
||||
required String greeting,
|
||||
required List<SpotifyHomeFeedSection> sections,
|
||||
}) = _SpotifyHomeFeed;
|
||||
|
||||
factory SpotifyHomeFeed.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotifyHomeFeedFromJson(json);
|
||||
}
|
||||
|
||||
Map<String, dynamic> transformSectionItemTypeJsonMap(
|
||||
Map<String, dynamic> json) {
|
||||
final data = json['content']['data'];
|
||||
final objType = json['content']['data']['__typename'];
|
||||
return {
|
||||
'typename': json['content']['__typename'],
|
||||
if (objType == 'Playlist')
|
||||
'playlist': {
|
||||
'name': data['name'],
|
||||
'description': data['description'],
|
||||
'format': data['format'],
|
||||
'images': (data['images']['items'] as List)
|
||||
.expand((j) => j['sources'] as dynamic)
|
||||
.toList()
|
||||
.cast<Map<String, dynamic>>(),
|
||||
'owner': data['ownerV2']['data']['name'],
|
||||
'uri': data['uri']
|
||||
},
|
||||
if (objType == 'Artist')
|
||||
'artist': {
|
||||
'name': data['profile']['name'],
|
||||
'uri': data['uri'],
|
||||
'images': data['visuals']['avatarImage']['sources'],
|
||||
},
|
||||
if (objType == 'Album')
|
||||
'album': {
|
||||
'name': data['name'],
|
||||
'uri': data['uri'],
|
||||
'images': data['coverArt']['sources'],
|
||||
'artists': data['artists']['items']
|
||||
.map(
|
||||
(artist) => {
|
||||
'name': artist['profile']['name'],
|
||||
'uri': artist['uri'],
|
||||
},
|
||||
)
|
||||
.toList()
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> transformSectionItemJsonMap(Map<String, dynamic> json) {
|
||||
return {
|
||||
'typename': json['data']['__typename'],
|
||||
'title': json['data']?['title']?['text'],
|
||||
'uri': json['uri'],
|
||||
'items': (json['sectionItems']['items'] as List)
|
||||
.map(
|
||||
(data) =>
|
||||
transformSectionItemTypeJsonMap(data as Map<String, dynamic>)
|
||||
as dynamic,
|
||||
)
|
||||
.where(
|
||||
(w) =>
|
||||
w['playlist'] != null ||
|
||||
w['artist'] != null ||
|
||||
w['album'] != null,
|
||||
)
|
||||
.toList()
|
||||
.cast<Map<String, dynamic>>()
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> transformHomeFeedJsonMap(Map<String, dynamic> json) {
|
||||
return {
|
||||
'greeting': json['data']['home']['greeting']['text'],
|
||||
'sections':
|
||||
(json['data']['home']['sectionContainer']['sections']['items'] as List)
|
||||
.map(
|
||||
(item) =>
|
||||
transformSectionItemJsonMap(item as Map<String, dynamic>)
|
||||
as dynamic,
|
||||
)
|
||||
.toList()
|
||||
.cast<Map<String, dynamic>>()
|
||||
};
|
||||
}
|
1776
lib/services/spotify/spotify_feed.freezed.dart
Normal file
1776
lib/services/spotify/spotify_feed.freezed.dart
Normal file
File diff suppressed because it is too large
Load Diff
169
lib/services/spotify/spotify_feed.g.dart
Normal file
169
lib/services/spotify/spotify_feed.g.dart
Normal file
@ -0,0 +1,169 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'spotify_feed.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$SpotifySectionPlaylistImpl _$$SpotifySectionPlaylistImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$SpotifySectionPlaylistImpl(
|
||||
description: json['description'] as String,
|
||||
format: json['format'] as String,
|
||||
images: (json['images'] as List<dynamic>)
|
||||
.map((e) =>
|
||||
SpotifySectionItemImage.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
name: json['name'] as String,
|
||||
owner: json['owner'] as String,
|
||||
uri: json['uri'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotifySectionPlaylistImplToJson(
|
||||
_$SpotifySectionPlaylistImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'description': instance.description,
|
||||
'format': instance.format,
|
||||
'images': instance.images,
|
||||
'name': instance.name,
|
||||
'owner': instance.owner,
|
||||
'uri': instance.uri,
|
||||
};
|
||||
|
||||
_$SpotifySectionArtistImpl _$$SpotifySectionArtistImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$SpotifySectionArtistImpl(
|
||||
name: json['name'] as String,
|
||||
uri: json['uri'] as String,
|
||||
images: (json['images'] as List<dynamic>)
|
||||
.map((e) =>
|
||||
SpotifySectionItemImage.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotifySectionArtistImplToJson(
|
||||
_$SpotifySectionArtistImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'uri': instance.uri,
|
||||
'images': instance.images,
|
||||
};
|
||||
|
||||
_$SpotifySectionAlbumImpl _$$SpotifySectionAlbumImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$SpotifySectionAlbumImpl(
|
||||
artists: (json['artists'] as List<dynamic>)
|
||||
.map((e) =>
|
||||
SpotifySectionAlbumArtist.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
images: (json['images'] as List<dynamic>)
|
||||
.map((e) =>
|
||||
SpotifySectionItemImage.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
name: json['name'] as String,
|
||||
uri: json['uri'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotifySectionAlbumImplToJson(
|
||||
_$SpotifySectionAlbumImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'artists': instance.artists,
|
||||
'images': instance.images,
|
||||
'name': instance.name,
|
||||
'uri': instance.uri,
|
||||
};
|
||||
|
||||
_$SpotifySectionAlbumArtistImpl _$$SpotifySectionAlbumArtistImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$SpotifySectionAlbumArtistImpl(
|
||||
name: json['name'] as String,
|
||||
uri: json['uri'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotifySectionAlbumArtistImplToJson(
|
||||
_$SpotifySectionAlbumArtistImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'uri': instance.uri,
|
||||
};
|
||||
|
||||
_$SpotifySectionItemImageImpl _$$SpotifySectionItemImageImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$SpotifySectionItemImageImpl(
|
||||
height: json['height'] as num?,
|
||||
url: json['url'] as String,
|
||||
width: json['width'] as num?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotifySectionItemImageImplToJson(
|
||||
_$SpotifySectionItemImageImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'height': instance.height,
|
||||
'url': instance.url,
|
||||
'width': instance.width,
|
||||
};
|
||||
|
||||
_$SpotifyHomeFeedSectionItemImpl _$$SpotifyHomeFeedSectionItemImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$SpotifyHomeFeedSectionItemImpl(
|
||||
typename: json['typename'] as String,
|
||||
playlist: json['playlist'] == null
|
||||
? null
|
||||
: SpotifySectionPlaylist.fromJson(
|
||||
json['playlist'] as Map<String, dynamic>),
|
||||
artist: json['artist'] == null
|
||||
? null
|
||||
: SpotifySectionArtist.fromJson(
|
||||
json['artist'] as Map<String, dynamic>),
|
||||
album: json['album'] == null
|
||||
? null
|
||||
: SpotifySectionAlbum.fromJson(json['album'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotifyHomeFeedSectionItemImplToJson(
|
||||
_$SpotifyHomeFeedSectionItemImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'typename': instance.typename,
|
||||
'playlist': instance.playlist,
|
||||
'artist': instance.artist,
|
||||
'album': instance.album,
|
||||
};
|
||||
|
||||
_$SpotifyHomeFeedSectionImpl _$$SpotifyHomeFeedSectionImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$SpotifyHomeFeedSectionImpl(
|
||||
typename: json['typename'] as String,
|
||||
title: json['title'] as String?,
|
||||
uri: json['uri'] as String,
|
||||
items: (json['items'] as List<dynamic>)
|
||||
.map((e) =>
|
||||
SpotifyHomeFeedSectionItem.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotifyHomeFeedSectionImplToJson(
|
||||
_$SpotifyHomeFeedSectionImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'typename': instance.typename,
|
||||
'title': instance.title,
|
||||
'uri': instance.uri,
|
||||
'items': instance.items,
|
||||
};
|
||||
|
||||
_$SpotifyHomeFeedImpl _$$SpotifyHomeFeedImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$SpotifyHomeFeedImpl(
|
||||
greeting: json['greeting'] as String,
|
||||
sections: (json['sections'] as List<dynamic>)
|
||||
.map(
|
||||
(e) => SpotifyHomeFeedSection.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotifyHomeFeedImplToJson(
|
||||
_$SpotifyHomeFeedImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'greeting': instance.greeting,
|
||||
'sections': instance.sections,
|
||||
};
|
Reference in New Issue
Block a user