mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45:18 +00:00
feat: add metadata api service and models
This commit is contained in:
parent
4f72f3b424
commit
7491175bb6
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -5,6 +5,7 @@
|
||||
"ambiguate",
|
||||
"Amoled",
|
||||
"Buildless",
|
||||
"configurators",
|
||||
"danceability",
|
||||
"fuzzywuzzy",
|
||||
"gapless",
|
||||
|
9
android/app/proguard-rules.pro
vendored
9
android/app/proguard-rules.pro
vendored
@ -1,3 +1,12 @@
|
||||
#Flutter Wrapper
|
||||
-keep class io.flutter.app.** { *; }
|
||||
-keep class io.flutter.plugin.** { *; }
|
||||
-keep class io.flutter.util.** { *; }
|
||||
-keep class io.flutter.view.** { *; }
|
||||
-keep class io.flutter.** { *; }
|
||||
-keep class io.flutter.plugins.** { *; }
|
||||
-keep class de.prosiebensat1digital.** { *; }
|
||||
|
||||
-keep class androidx.lifecycle.DefaultLifecycleObserver
|
||||
|
||||
-keepnames class kotlinx.serialization.** { *; }
|
||||
|
@ -13,6 +13,9 @@ class FontFamily {
|
||||
/// Font family: BootstrapIcons
|
||||
static const String bootstrapIcons = 'BootstrapIcons';
|
||||
|
||||
/// Font family: Cookie
|
||||
static const String cookie = 'Cookie';
|
||||
|
||||
/// Font family: RadixIcons
|
||||
static const String radixIcons = 'RadixIcons';
|
||||
}
|
||||
|
22
lib/models/metadata/album.dart
Normal file
22
lib/models/metadata/album.dart
Normal file
@ -0,0 +1,22 @@
|
||||
part of 'metadata.dart';
|
||||
|
||||
enum SpotubeAlbumType {
|
||||
album,
|
||||
single,
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SpotubeAlbumObject with _$SpotubeAlbumObject {
|
||||
factory SpotubeAlbumObject({
|
||||
required final String uid,
|
||||
required final String title,
|
||||
required final SpotubeArtistObject artist,
|
||||
@Default([]) final List<SpotubeImageObject> images,
|
||||
required final String releaseDate,
|
||||
required final String externalUrl,
|
||||
required final SpotubeAlbumType type,
|
||||
}) = _SpotubeAlbumObject;
|
||||
|
||||
factory SpotubeAlbumObject.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotubeAlbumObjectFromJson(json);
|
||||
}
|
14
lib/models/metadata/artist.dart
Normal file
14
lib/models/metadata/artist.dart
Normal file
@ -0,0 +1,14 @@
|
||||
part of 'metadata.dart';
|
||||
|
||||
@freezed
|
||||
class SpotubeArtistObject with _$SpotubeArtistObject {
|
||||
factory SpotubeArtistObject({
|
||||
required final String uid,
|
||||
required final String name,
|
||||
@Default([]) final List<SpotubeImageObject> images,
|
||||
required final String externalUrl,
|
||||
}) = _SpotubeArtistObject;
|
||||
|
||||
factory SpotubeArtistObject.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotubeArtistObjectFromJson(json);
|
||||
}
|
13
lib/models/metadata/image.dart
Normal file
13
lib/models/metadata/image.dart
Normal file
@ -0,0 +1,13 @@
|
||||
part of 'metadata.dart';
|
||||
|
||||
@freezed
|
||||
class SpotubeImageObject with _$SpotubeImageObject {
|
||||
factory SpotubeImageObject({
|
||||
required final String url,
|
||||
required final int width,
|
||||
required final int height,
|
||||
}) = _SpotubeImageObject;
|
||||
|
||||
factory SpotubeImageObject.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotubeImageObjectFromJson(json);
|
||||
}
|
14
lib/models/metadata/metadata.dart
Normal file
14
lib/models/metadata/metadata.dart
Normal file
@ -0,0 +1,14 @@
|
||||
library metadata_objects;
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'metadata.g.dart';
|
||||
part 'metadata.freezed.dart';
|
||||
|
||||
part 'album.dart';
|
||||
part 'artist.dart';
|
||||
part 'image.dart';
|
||||
part 'playlist.dart';
|
||||
part 'search.dart';
|
||||
part 'track.dart';
|
||||
part 'user.dart';
|
1868
lib/models/metadata/metadata.freezed.dart
Normal file
1868
lib/models/metadata/metadata.freezed.dart
Normal file
File diff suppressed because it is too large
Load Diff
193
lib/models/metadata/metadata.g.dart
Normal file
193
lib/models/metadata/metadata.g.dart
Normal file
@ -0,0 +1,193 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'metadata.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$SpotubeAlbumObjectImpl _$$SpotubeAlbumObjectImplFromJson(Map json) =>
|
||||
_$SpotubeAlbumObjectImpl(
|
||||
uid: json['uid'] as String,
|
||||
title: json['title'] as String,
|
||||
artist: SpotubeArtistObject.fromJson(
|
||||
Map<String, dynamic>.from(json['artist'] as Map)),
|
||||
images: (json['images'] as List<dynamic>?)
|
||||
?.map((e) => SpotubeImageObject.fromJson(
|
||||
Map<String, dynamic>.from(e as Map)))
|
||||
.toList() ??
|
||||
const [],
|
||||
releaseDate: json['releaseDate'] as String,
|
||||
externalUrl: json['externalUrl'] as String,
|
||||
type: $enumDecode(_$SpotubeAlbumTypeEnumMap, json['type']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotubeAlbumObjectImplToJson(
|
||||
_$SpotubeAlbumObjectImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'uid': instance.uid,
|
||||
'title': instance.title,
|
||||
'artist': instance.artist.toJson(),
|
||||
'images': instance.images.map((e) => e.toJson()).toList(),
|
||||
'releaseDate': instance.releaseDate,
|
||||
'externalUrl': instance.externalUrl,
|
||||
'type': _$SpotubeAlbumTypeEnumMap[instance.type]!,
|
||||
};
|
||||
|
||||
const _$SpotubeAlbumTypeEnumMap = {
|
||||
SpotubeAlbumType.album: 'album',
|
||||
SpotubeAlbumType.single: 'single',
|
||||
};
|
||||
|
||||
_$SpotubeArtistObjectImpl _$$SpotubeArtistObjectImplFromJson(Map json) =>
|
||||
_$SpotubeArtistObjectImpl(
|
||||
uid: json['uid'] as String,
|
||||
name: json['name'] as String,
|
||||
images: (json['images'] as List<dynamic>?)
|
||||
?.map((e) => SpotubeImageObject.fromJson(
|
||||
Map<String, dynamic>.from(e as Map)))
|
||||
.toList() ??
|
||||
const [],
|
||||
externalUrl: json['externalUrl'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotubeArtistObjectImplToJson(
|
||||
_$SpotubeArtistObjectImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'uid': instance.uid,
|
||||
'name': instance.name,
|
||||
'images': instance.images.map((e) => e.toJson()).toList(),
|
||||
'externalUrl': instance.externalUrl,
|
||||
};
|
||||
|
||||
_$SpotubeImageObjectImpl _$$SpotubeImageObjectImplFromJson(Map json) =>
|
||||
_$SpotubeImageObjectImpl(
|
||||
url: json['url'] as String,
|
||||
width: (json['width'] as num).toInt(),
|
||||
height: (json['height'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotubeImageObjectImplToJson(
|
||||
_$SpotubeImageObjectImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'url': instance.url,
|
||||
'width': instance.width,
|
||||
'height': instance.height,
|
||||
};
|
||||
|
||||
_$SpotubePlaylistObjectImpl _$$SpotubePlaylistObjectImplFromJson(Map json) =>
|
||||
_$SpotubePlaylistObjectImpl(
|
||||
uid: json['uid'] as String,
|
||||
name: json['name'] as String,
|
||||
images: (json['images'] as List<dynamic>?)
|
||||
?.map((e) => SpotubeImageObject.fromJson(
|
||||
Map<String, dynamic>.from(e as Map)))
|
||||
.toList() ??
|
||||
const [],
|
||||
description: json['description'] as String,
|
||||
externalUrl: json['externalUrl'] as String,
|
||||
owner: SpotubeUserObject.fromJson(
|
||||
Map<String, dynamic>.from(json['owner'] as Map)),
|
||||
collaborators: (json['collaborators'] as List<dynamic>?)
|
||||
?.map((e) => SpotubeUserObject.fromJson(
|
||||
Map<String, dynamic>.from(e as Map)))
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotubePlaylistObjectImplToJson(
|
||||
_$SpotubePlaylistObjectImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'uid': instance.uid,
|
||||
'name': instance.name,
|
||||
'images': instance.images.map((e) => e.toJson()).toList(),
|
||||
'description': instance.description,
|
||||
'externalUrl': instance.externalUrl,
|
||||
'owner': instance.owner.toJson(),
|
||||
'collaborators': instance.collaborators.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
||||
_$SpotubeSearchResponseObjectImpl _$$SpotubeSearchResponseObjectImplFromJson(
|
||||
Map json) =>
|
||||
_$SpotubeSearchResponseObjectImpl(
|
||||
tracks: (json['tracks'] as List<dynamic>?)
|
||||
?.map((e) => SpotubeTrackObject.fromJson(
|
||||
Map<String, dynamic>.from(e as Map)))
|
||||
.toList() ??
|
||||
const [],
|
||||
albums: (json['albums'] as List<dynamic>?)
|
||||
?.map((e) => SpotubeAlbumObject.fromJson(
|
||||
Map<String, dynamic>.from(e as Map)))
|
||||
.toList() ??
|
||||
const [],
|
||||
artists: (json['artists'] as List<dynamic>?)
|
||||
?.map((e) => SpotubeArtistObject.fromJson(
|
||||
Map<String, dynamic>.from(e as Map)))
|
||||
.toList() ??
|
||||
const [],
|
||||
playlists: (json['playlists'] as List<dynamic>?)
|
||||
?.map((e) => SpotubePlaylistObject.fromJson(
|
||||
Map<String, dynamic>.from(e as Map)))
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotubeSearchResponseObjectImplToJson(
|
||||
_$SpotubeSearchResponseObjectImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'tracks': instance.tracks.map((e) => e.toJson()).toList(),
|
||||
'albums': instance.albums.map((e) => e.toJson()).toList(),
|
||||
'artists': instance.artists.map((e) => e.toJson()).toList(),
|
||||
'playlists': instance.playlists.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
||||
_$SpotubeTrackObjectImpl _$$SpotubeTrackObjectImplFromJson(Map json) =>
|
||||
_$SpotubeTrackObjectImpl(
|
||||
uid: json['uid'] as String,
|
||||
title: json['title'] as String,
|
||||
artists: (json['artists'] as List<dynamic>?)
|
||||
?.map((e) => SpotubeArtistObject.fromJson(
|
||||
Map<String, dynamic>.from(e as Map)))
|
||||
.toList() ??
|
||||
const [],
|
||||
album: SpotubeAlbumObject.fromJson(
|
||||
Map<String, dynamic>.from(json['album'] as Map)),
|
||||
durationMs: (json['durationMs'] as num).toInt(),
|
||||
isrc: json['isrc'] as String,
|
||||
externalUrl: json['externalUrl'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotubeTrackObjectImplToJson(
|
||||
_$SpotubeTrackObjectImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'uid': instance.uid,
|
||||
'title': instance.title,
|
||||
'artists': instance.artists.map((e) => e.toJson()).toList(),
|
||||
'album': instance.album.toJson(),
|
||||
'durationMs': instance.durationMs,
|
||||
'isrc': instance.isrc,
|
||||
'externalUrl': instance.externalUrl,
|
||||
};
|
||||
|
||||
_$SpotubeUserObjectImpl _$$SpotubeUserObjectImplFromJson(Map json) =>
|
||||
_$SpotubeUserObjectImpl(
|
||||
uid: json['uid'] as String,
|
||||
name: json['name'] as String,
|
||||
avatars: (json['avatars'] as List<dynamic>?)
|
||||
?.map((e) => SpotubeImageObject.fromJson(
|
||||
Map<String, dynamic>.from(e as Map)))
|
||||
.toList() ??
|
||||
const [],
|
||||
externalUrl: json['externalUrl'] as String,
|
||||
displayName: json['displayName'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SpotubeUserObjectImplToJson(
|
||||
_$SpotubeUserObjectImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'uid': instance.uid,
|
||||
'name': instance.name,
|
||||
'avatars': instance.avatars.map((e) => e.toJson()).toList(),
|
||||
'externalUrl': instance.externalUrl,
|
||||
'displayName': instance.displayName,
|
||||
};
|
17
lib/models/metadata/playlist.dart
Normal file
17
lib/models/metadata/playlist.dart
Normal file
@ -0,0 +1,17 @@
|
||||
part of 'metadata.dart';
|
||||
|
||||
@freezed
|
||||
class SpotubePlaylistObject with _$SpotubePlaylistObject {
|
||||
factory SpotubePlaylistObject({
|
||||
required final String uid,
|
||||
required final String name,
|
||||
@Default([]) final List<SpotubeImageObject> images,
|
||||
required final String description,
|
||||
required final String externalUrl,
|
||||
required final SpotubeUserObject owner,
|
||||
@Default([]) final List<SpotubeUserObject> collaborators,
|
||||
}) = _SpotubePlaylistObject;
|
||||
|
||||
factory SpotubePlaylistObject.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotubePlaylistObjectFromJson(json);
|
||||
}
|
14
lib/models/metadata/search.dart
Normal file
14
lib/models/metadata/search.dart
Normal file
@ -0,0 +1,14 @@
|
||||
part of 'metadata.dart';
|
||||
|
||||
@freezed
|
||||
class SpotubeSearchResponseObject with _$SpotubeSearchResponseObject {
|
||||
factory SpotubeSearchResponseObject({
|
||||
@Default([]) final List<SpotubeTrackObject> tracks,
|
||||
@Default([]) final List<SpotubeAlbumObject> albums,
|
||||
@Default([]) final List<SpotubeArtistObject> artists,
|
||||
@Default([]) final List<SpotubePlaylistObject> playlists,
|
||||
}) = _SpotubeSearchResponseObject;
|
||||
|
||||
factory SpotubeSearchResponseObject.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotubeSearchResponseObjectFromJson(json);
|
||||
}
|
17
lib/models/metadata/track.dart
Normal file
17
lib/models/metadata/track.dart
Normal file
@ -0,0 +1,17 @@
|
||||
part of 'metadata.dart';
|
||||
|
||||
@freezed
|
||||
class SpotubeTrackObject with _$SpotubeTrackObject {
|
||||
factory SpotubeTrackObject({
|
||||
required final String uid,
|
||||
required final String title,
|
||||
@Default([]) final List<SpotubeArtistObject> artists,
|
||||
required final SpotubeAlbumObject album,
|
||||
required final int durationMs,
|
||||
required final String isrc,
|
||||
required final String externalUrl,
|
||||
}) = _SpotubeTrackObject;
|
||||
|
||||
factory SpotubeTrackObject.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotubeTrackObjectFromJson(json);
|
||||
}
|
15
lib/models/metadata/user.dart
Normal file
15
lib/models/metadata/user.dart
Normal file
@ -0,0 +1,15 @@
|
||||
part of 'metadata.dart';
|
||||
|
||||
@freezed
|
||||
class SpotubeUserObject with _$SpotubeUserObject {
|
||||
factory SpotubeUserObject({
|
||||
required final String uid,
|
||||
required final String name,
|
||||
@Default([]) final List<SpotubeImageObject> avatars,
|
||||
required final String externalUrl,
|
||||
required final String displayName,
|
||||
}) = _SpotubeUserObject;
|
||||
|
||||
factory SpotubeUserObject.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpotubeUserObjectFromJson(json);
|
||||
}
|
308
lib/services/metadata/metadata.dart
Normal file
308
lib/services/metadata/metadata.dart
Normal file
@ -0,0 +1,308 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_js/flutter_js.dart';
|
||||
import 'package:spotube/models/metadata/metadata.dart';
|
||||
import 'package:spotube/services/logger/logger.dart';
|
||||
|
||||
const int defaultMetadataLimit = 20;
|
||||
const int defaultMetadataOffset = 0;
|
||||
|
||||
/// Signature for metadata and related methods that will return Spotube native
|
||||
/// objects e.g. SpotubeTrack, SpotubePlaylist, etc.
|
||||
class MetadataApiSignature {
|
||||
late final JavascriptRuntime runtime;
|
||||
|
||||
MetadataApiSignature(String libraryCode) {
|
||||
runtime = getJavascriptRuntime(xhr: true);
|
||||
runtime.enableHandlePromises();
|
||||
|
||||
Timer.periodic(
|
||||
const Duration(milliseconds: 100),
|
||||
(timer) {
|
||||
runtime.executePendingJob();
|
||||
},
|
||||
);
|
||||
|
||||
runtime.evaluate(
|
||||
"""
|
||||
;$libraryCode;
|
||||
const metadataApi = new MetadataApi();
|
||||
""",
|
||||
);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
runtime.dispose();
|
||||
}
|
||||
|
||||
Future invoke(String method, [List? args]) async {
|
||||
final completer = Completer();
|
||||
runtime.onMessage(method, (result) {
|
||||
try {
|
||||
if (result == null) {
|
||||
completer.completeError("Result is null");
|
||||
} else {
|
||||
completer.complete(result is String ? jsonDecode(result) : result);
|
||||
}
|
||||
} catch (e, stack) {
|
||||
AppLogger.reportError(e, stack);
|
||||
}
|
||||
});
|
||||
final code = """
|
||||
$method(...${args != null ? jsonEncode(args) : "[]"})
|
||||
.then((res) => {
|
||||
sendMessage("$method", JSON.stringify(res));
|
||||
}).catch((err) => {
|
||||
sendMessage("$method", null);
|
||||
async}){
|
||||
} final res"metadataApi.=>", [limit, offset] ;= await invoke()
|
||||
|
||||
return res.map(es.fromJson).toList();
|
||||
""";
|
||||
|
||||
final res = await runtime.evaluateAsync(code);
|
||||
|
||||
if (res.isError) {
|
||||
AppLogger.reportError("Error evaluating code: $code\n${res.rawResult}");
|
||||
completer.completeError("Error evaluating code: $code\n${res.rawResult}");
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
// ----- Track ------
|
||||
Future<SpotubeTrackObject> getTrack(String id) async {
|
||||
final result = await invoke("metadataApi.getTrack", [id]);
|
||||
return SpotubeTrackObject.fromJson(result);
|
||||
}
|
||||
|
||||
Future<List<SpotubeTrackObject>> listTracks({
|
||||
List<String>? ids,
|
||||
int limit = defaultMetadataLimit,
|
||||
int offset = defaultMetadataOffset,
|
||||
}) async {
|
||||
final result = await invoke(
|
||||
"metadataApi.listTracks",
|
||||
[
|
||||
ids,
|
||||
limit,
|
||||
offset,
|
||||
],
|
||||
);
|
||||
|
||||
return result.map(SpotubeTrackObject.fromJson).toList();
|
||||
}
|
||||
|
||||
Future<List<SpotubeTrackObject>> listTracksByAlbum(
|
||||
String albumId, {
|
||||
int limit = defaultMetadataLimit,
|
||||
int offset = defaultMetadataOffset,
|
||||
}) async {
|
||||
final res = await invoke(
|
||||
"metadataApi.listTracksByAlbum",
|
||||
[albumId, limit, offset],
|
||||
);
|
||||
|
||||
return res.map(SpotubeTrackObject.fromJson).toList();
|
||||
}
|
||||
|
||||
Future<List<SpotubeTrackObject>> listTopTracksByArtist(
|
||||
String artistId, {
|
||||
int limit = defaultMetadataLimit,
|
||||
int offset = defaultMetadataOffset,
|
||||
}) async {
|
||||
final res = await invoke(
|
||||
"metadataApi.listTopTracksByArtist",
|
||||
[artistId, limit, offset],
|
||||
);
|
||||
|
||||
return res.map(SpotubeTrackObject.fromJson).toList();
|
||||
}
|
||||
|
||||
Future<List<SpotubeTrackObject>> listTracksByPlaylist(
|
||||
String playlistId, {
|
||||
int limit = defaultMetadataLimit,
|
||||
int offset = defaultMetadataOffset,
|
||||
}) async {
|
||||
final res = await invoke(
|
||||
"metadataApi.listTracksByPlaylist",
|
||||
[playlistId, limit, offset],
|
||||
);
|
||||
|
||||
return res.map(SpotubeTrackObject.fromJson).toList();
|
||||
}
|
||||
|
||||
Future<List<SpotubeTrackObject>> listUserSavedTracks(
|
||||
String userId, {
|
||||
int limit = defaultMetadataLimit,
|
||||
int offset = defaultMetadataOffset,
|
||||
}) async {
|
||||
final res = await invoke(
|
||||
"metadataApi.listUserSavedTracks",
|
||||
[userId, limit, offset],
|
||||
);
|
||||
|
||||
return res.map(SpotubeTrackObject.fromJson).toList();
|
||||
}
|
||||
|
||||
// ----- Album ------
|
||||
Future<SpotubeAlbumObject> getAlbum(String id) async {
|
||||
final res = await invoke("metadataApi.getAlbum", [id]);
|
||||
|
||||
return SpotubeAlbumObject.fromJson(res);
|
||||
}
|
||||
|
||||
Future<List<SpotubeAlbumObject>> listAlbums({
|
||||
List<String>? ids,
|
||||
int limit = defaultMetadataLimit,
|
||||
int offset = defaultMetadataOffset,
|
||||
}) async {
|
||||
final res = await invoke(
|
||||
"metadataApi.listAlbums",
|
||||
[ids, limit, offset],
|
||||
);
|
||||
|
||||
return res.map(SpotubeAlbumObject.fromJson).toList();
|
||||
}
|
||||
|
||||
Future<List<SpotubeAlbumObject>> listAlbumsByArtist(
|
||||
String artistId, {
|
||||
int limit = defaultMetadataLimit,
|
||||
int offset = defaultMetadataOffset,
|
||||
}) async {
|
||||
final res = await invoke(
|
||||
"metadataApi.listAlbumsByArtist",
|
||||
[artistId, limit, offset],
|
||||
);
|
||||
|
||||
return res.map(SpotubeAlbumObject.fromJson).toList();
|
||||
}
|
||||
|
||||
Future<List<SpotubeAlbumObject>> listUserSavedAlbums(
|
||||
String userId, {
|
||||
int limit = defaultMetadataLimit,
|
||||
int offset = defaultMetadataOffset,
|
||||
}) async {
|
||||
final res = await invoke(
|
||||
"metadataApi.listUserSavedAlbums",
|
||||
[userId, limit, offset],
|
||||
);
|
||||
|
||||
return res.map(SpotubeAlbumObject.fromJson).toList();
|
||||
}
|
||||
|
||||
// ----- Playlist ------
|
||||
Future<SpotubePlaylistObject> getPlaylist(String id) async {
|
||||
final res = await invoke("metadataApi.getPlaylist", [id]);
|
||||
|
||||
return SpotubePlaylistObject.fromJson(res);
|
||||
}
|
||||
|
||||
Future<List<SpotubePlaylistObject>> listPlaylists({
|
||||
List<String>? ids,
|
||||
int limit = defaultMetadataLimit,
|
||||
int offset = defaultMetadataOffset,
|
||||
}) async {
|
||||
final res = await invoke(
|
||||
"metadataApi.listPlaylists",
|
||||
[ids, limit, offset],
|
||||
);
|
||||
|
||||
return res.map(SpotubePlaylistObject.fromJson).toList();
|
||||
}
|
||||
|
||||
Future<List<SpotubePlaylistObject>> listUserSavedPlaylists(
|
||||
String userId, {
|
||||
int limit = defaultMetadataLimit,
|
||||
int offset = defaultMetadataOffset,
|
||||
}) async {
|
||||
final res = await invoke(
|
||||
"metadataApi.listUserSavedPlaylists",
|
||||
[userId, limit, offset],
|
||||
);
|
||||
|
||||
return res.map(SpotubePlaylistObject.fromJson).toList();
|
||||
}
|
||||
|
||||
// ----- Artist ------
|
||||
Future<SpotubeArtistObject> getArtist(String id) async {
|
||||
final res = await invoke("metadataApi.getArtist", [id]);
|
||||
|
||||
return SpotubeArtistObject.fromJson(res);
|
||||
}
|
||||
|
||||
Future<List<SpotubeArtistObject>> listArtists({
|
||||
List<String>? ids,
|
||||
int limit = defaultMetadataLimit,
|
||||
int offset = defaultMetadataOffset,
|
||||
}) async {
|
||||
final res = await invoke(
|
||||
"metadataApi.listArtists",
|
||||
[ids, limit, offset],
|
||||
);
|
||||
|
||||
return res.map(SpotubeArtistObject.fromJson).toList();
|
||||
}
|
||||
|
||||
Future<List<SpotubeArtistObject>> listUserSavedArtists(
|
||||
String userId, {
|
||||
int limit = defaultMetadataLimit,
|
||||
int offset = defaultMetadataOffset,
|
||||
}) async {
|
||||
final res = await invoke(
|
||||
"metadataApi.listUserSavedArtists",
|
||||
[userId, limit, offset],
|
||||
);
|
||||
|
||||
return res.map(SpotubeArtistObject.fromJson).toList();
|
||||
}
|
||||
|
||||
// ----- Search ------
|
||||
Future<SpotubeSearchResponseObject> search(
|
||||
String query, {
|
||||
int limit = defaultMetadataLimit,
|
||||
int offset = defaultMetadataOffset,
|
||||
}) async {
|
||||
final res = await invoke(
|
||||
"metadataApi.search",
|
||||
[query, limit, offset],
|
||||
);
|
||||
|
||||
return res.map(SpotubeSearchResponseObject.fromJson).toList();
|
||||
}
|
||||
|
||||
// ----- User ------
|
||||
Future<void> followArtist(String userId, String artistId) async {
|
||||
await invoke("metadataApi.followArtist", [userId, artistId]);
|
||||
}
|
||||
|
||||
Future<void> unfollowArtist(String userId, String artistId) async {
|
||||
await invoke("metadataApi.unfollowArtist", [userId, artistId]);
|
||||
}
|
||||
|
||||
Future<void> savePlaylist(String userId, String playlistId) async {
|
||||
await invoke("metadataApi.savePlaylist", [userId, playlistId]);
|
||||
}
|
||||
|
||||
Future<void> unsavePlaylist(String userId, String playlistId) async {
|
||||
await invoke("metadataApi.unsavePlaylist", [userId, playlistId]);
|
||||
}
|
||||
|
||||
Future<void> saveAlbum(String userId, String albumId) async {
|
||||
await invoke("metadataApi.saveAlbum", [userId, albumId]);
|
||||
}
|
||||
|
||||
Future<void> unsaveAlbum(String userId, String albumId) async {
|
||||
await invoke("metadataApi.unsaveAlbum", [userId, albumId]);
|
||||
}
|
||||
|
||||
Future<void> saveTrack(String userId, String trackId) async {
|
||||
await invoke("metadataApi.saveTrack", [userId, trackId]);
|
||||
}
|
||||
|
||||
Future<void> unsaveTrack(String userId, String trackId) async {
|
||||
await invoke("metadataApi.unsaveTrack", [userId, trackId]);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
||||
#include <file_selector_linux/file_selector_plugin.h>
|
||||
#include <flutter_js/flutter_js_plugin.h>
|
||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||
#include <gtk/gtk_plugin.h>
|
||||
#include <local_notifier/local_notifier_plugin.h>
|
||||
@ -27,6 +28,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) flutter_js_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterJsPlugin");
|
||||
flutter_js_plugin_register_with_registrar(flutter_js_registrar);
|
||||
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||
|
@ -5,6 +5,7 @@
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
desktop_webview_window
|
||||
file_selector_linux
|
||||
flutter_js
|
||||
flutter_secure_storage_linux
|
||||
gtk
|
||||
local_notifier
|
||||
|
@ -14,6 +14,7 @@ import desktop_webview_window
|
||||
import device_info_plus
|
||||
import file_selector_macos
|
||||
import flutter_inappwebview_macos
|
||||
import flutter_js
|
||||
import flutter_secure_storage_macos
|
||||
import local_notifier
|
||||
import media_kit_libs_macos_audio
|
||||
@ -39,6 +40,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
||||
FlutterJsPlugin.register(with: registry.registrar(forPlugin: "FlutterJsPlugin"))
|
||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||
LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin"))
|
||||
MediaKitLibsMacosAudioPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosAudioPlugin"))
|
||||
|
@ -909,6 +909,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
flutter_js:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_js
|
||||
sha256: "6b777cd4e468546f046a2f114d078a4596143269f6fa6bad5c29611d5b896369"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.2"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -142,6 +142,7 @@ dependencies:
|
||||
collection: any
|
||||
otp_util: ^1.0.2
|
||||
dio_http2_adapter: ^2.6.0
|
||||
flutter_js: ^0.8.2
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.13
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
||||
#include <file_selector_windows/file_selector_windows.h>
|
||||
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
|
||||
#include <flutter_js/flutter_js_plugin.h>
|
||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||
#include <local_notifier/local_notifier_plugin.h>
|
||||
#include <media_kit_libs_windows_audio/media_kit_libs_windows_audio_plugin_c_api.h>
|
||||
@ -36,6 +37,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
|
||||
FlutterJsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterJsPlugin"));
|
||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||
LocalNotifierPluginRegisterWithRegistrar(
|
||||
|
@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
desktop_webview_window
|
||||
file_selector_windows
|
||||
flutter_inappwebview_windows
|
||||
flutter_js
|
||||
flutter_secure_storage_windows
|
||||
local_notifier
|
||||
media_kit_libs_windows_audio
|
||||
|
Loading…
Reference in New Issue
Block a user