mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55: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",
|
"ambiguate",
|
||||||
"Amoled",
|
"Amoled",
|
||||||
"Buildless",
|
"Buildless",
|
||||||
|
"configurators",
|
||||||
"danceability",
|
"danceability",
|
||||||
"fuzzywuzzy",
|
"fuzzywuzzy",
|
||||||
"gapless",
|
"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
|
-keep class androidx.lifecycle.DefaultLifecycleObserver
|
||||||
|
|
||||||
-keepnames class kotlinx.serialization.** { *; }
|
-keepnames class kotlinx.serialization.** { *; }
|
||||||
|
@ -13,6 +13,9 @@ class FontFamily {
|
|||||||
/// Font family: BootstrapIcons
|
/// Font family: BootstrapIcons
|
||||||
static const String bootstrapIcons = 'BootstrapIcons';
|
static const String bootstrapIcons = 'BootstrapIcons';
|
||||||
|
|
||||||
|
/// Font family: Cookie
|
||||||
|
static const String cookie = 'Cookie';
|
||||||
|
|
||||||
/// Font family: RadixIcons
|
/// Font family: RadixIcons
|
||||||
static const String radixIcons = '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 <desktop_webview_window/desktop_webview_window_plugin.h>
|
||||||
#include <file_selector_linux/file_selector_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 <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
#include <gtk/gtk_plugin.h>
|
#include <gtk/gtk_plugin.h>
|
||||||
#include <local_notifier/local_notifier_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 =
|
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
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 =
|
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||||
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
desktop_webview_window
|
desktop_webview_window
|
||||||
file_selector_linux
|
file_selector_linux
|
||||||
|
flutter_js
|
||||||
flutter_secure_storage_linux
|
flutter_secure_storage_linux
|
||||||
gtk
|
gtk
|
||||||
local_notifier
|
local_notifier
|
||||||
|
@ -14,6 +14,7 @@ import desktop_webview_window
|
|||||||
import device_info_plus
|
import device_info_plus
|
||||||
import file_selector_macos
|
import file_selector_macos
|
||||||
import flutter_inappwebview_macos
|
import flutter_inappwebview_macos
|
||||||
|
import flutter_js
|
||||||
import flutter_secure_storage_macos
|
import flutter_secure_storage_macos
|
||||||
import local_notifier
|
import local_notifier
|
||||||
import media_kit_libs_macos_audio
|
import media_kit_libs_macos_audio
|
||||||
@ -39,6 +40,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||||
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
||||||
|
FlutterJsPlugin.register(with: registry.registrar(forPlugin: "FlutterJsPlugin"))
|
||||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin"))
|
LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin"))
|
||||||
MediaKitLibsMacosAudioPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosAudioPlugin"))
|
MediaKitLibsMacosAudioPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosAudioPlugin"))
|
||||||
|
@ -909,6 +909,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
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:
|
flutter_launcher_icons:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -142,6 +142,7 @@ dependencies:
|
|||||||
collection: any
|
collection: any
|
||||||
otp_util: ^1.0.2
|
otp_util: ^1.0.2
|
||||||
dio_http2_adapter: ^2.6.0
|
dio_http2_adapter: ^2.6.0
|
||||||
|
flutter_js: ^0.8.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.4.13
|
build_runner: ^2.4.13
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.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 <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
#include <local_notifier/local_notifier_plugin.h>
|
#include <local_notifier/local_notifier_plugin.h>
|
||||||
#include <media_kit_libs_windows_audio/media_kit_libs_windows_audio_plugin_c_api.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"));
|
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||||
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
|
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
|
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
|
||||||
|
FlutterJsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("FlutterJsPlugin"));
|
||||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
LocalNotifierPluginRegisterWithRegistrar(
|
LocalNotifierPluginRegisterWithRegistrar(
|
||||||
|
@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||||||
desktop_webview_window
|
desktop_webview_window
|
||||||
file_selector_windows
|
file_selector_windows
|
||||||
flutter_inappwebview_windows
|
flutter_inappwebview_windows
|
||||||
|
flutter_js
|
||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
local_notifier
|
local_notifier
|
||||||
media_kit_libs_windows_audio
|
media_kit_libs_windows_audio
|
||||||
|
Loading…
Reference in New Issue
Block a user