mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
feat: add albums metadata endpoint and provider
This commit is contained in:
parent
a9ba2582fb
commit
326d8212f6
@ -1,8 +1,73 @@
|
|||||||
import 'package:riverpod/riverpod.dart';
|
import 'package:riverpod/riverpod.dart';
|
||||||
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
|
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
|
||||||
|
import 'package:spotube/provider/metadata_plugin/utils/paginated.dart';
|
||||||
|
|
||||||
class LibraryAlbumsNotifier extends AsyncNotifier<void> {
|
class MetadataPluginSavedAlbumNotifier
|
||||||
|
extends PaginatedAsyncNotifier<SpotubeSimpleAlbumObject> {
|
||||||
@override
|
@override
|
||||||
build() {
|
Future<SpotubePaginationResponseObject<SpotubeSimpleAlbumObject>> fetch(
|
||||||
return null;
|
int offset,
|
||||||
|
int limit,
|
||||||
|
) async {
|
||||||
|
return await (await metadataPlugin).user.savedAlbums(
|
||||||
|
limit: limit,
|
||||||
|
offset: offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
build() async {
|
||||||
|
ref.watch(metadataPluginProvider);
|
||||||
|
return await fetch(0, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addFavorite(List<SpotubeSimpleAlbumObject> albums) async {
|
||||||
|
await update((state) async {
|
||||||
|
(await metadataPlugin).album.save(albums.map((e) => e.id).toList());
|
||||||
|
return state.copyWith(
|
||||||
|
items: [...state.items, albums],
|
||||||
|
) as SpotubePaginationResponseObject<SpotubeSimpleAlbumObject>;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (final album in albums) {
|
||||||
|
ref.invalidate(metadataPluginIsSavedAlbumProvider(album.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> removeFavorite(List<SpotubeSimpleAlbumObject> albums) async {
|
||||||
|
await update((state) async {
|
||||||
|
final albumIds = albums.map((e) => e.id).toList();
|
||||||
|
(await metadataPlugin).album.unsave(albumIds);
|
||||||
|
return state.copyWith(
|
||||||
|
items: state.items
|
||||||
|
.where(
|
||||||
|
(e) =>
|
||||||
|
albumIds.contains((e as SpotubeSimpleAlbumObject).id) ==
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.toList() as List<SpotubeSimpleAlbumObject>,
|
||||||
|
) as SpotubePaginationResponseObject<SpotubeSimpleAlbumObject>;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (final album in albums) {
|
||||||
|
ref.invalidate(metadataPluginIsSavedAlbumProvider(album.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final metadataPluginSavedAlbumsProvider = AsyncNotifierProvider<
|
||||||
|
MetadataPluginSavedAlbumNotifier,
|
||||||
|
SpotubePaginationResponseObject<SpotubeSimpleAlbumObject>>(
|
||||||
|
() => MetadataPluginSavedAlbumNotifier(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final metadataPluginIsSavedAlbumProvider =
|
||||||
|
FutureProvider.autoDispose.family<bool, String>(
|
||||||
|
(ref, albumId) async {
|
||||||
|
final metadataPlugin = await ref.watch(metadataPluginProvider.future);
|
||||||
|
|
||||||
|
return metadataPlugin!.user
|
||||||
|
.isSavedAlbums([albumId]).then((value) => value.first);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
@ -6,17 +6,17 @@ import 'package:spotube/provider/metadata_plugin/tracks/playlist.dart';
|
|||||||
import 'package:spotube/provider/metadata_plugin/utils/paginated.dart';
|
import 'package:spotube/provider/metadata_plugin/utils/paginated.dart';
|
||||||
import 'package:spotube/services/metadata/endpoints/error.dart';
|
import 'package:spotube/services/metadata/endpoints/error.dart';
|
||||||
|
|
||||||
class FavoritePlaylistsNotifier
|
class MetadataPluginSavedPlaylistsNotifier
|
||||||
extends PaginatedAsyncNotifier<SpotubeSimplePlaylistObject> {
|
extends PaginatedAsyncNotifier<SpotubeSimplePlaylistObject> {
|
||||||
FavoritePlaylistsNotifier() : super();
|
MetadataPluginSavedPlaylistsNotifier() : super();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
fetch(int offset, int limit) async {
|
fetch(int offset, int limit) async {
|
||||||
final playlists = await (await metadataPlugin)
|
final playlists = await (await metadataPlugin)
|
||||||
?.user
|
.user
|
||||||
.savedPlaylists(limit: limit, offset: offset);
|
.savedPlaylists(limit: limit, offset: offset);
|
||||||
|
|
||||||
return playlists!;
|
return playlists;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -43,7 +43,7 @@ class FavoritePlaylistsNotifier
|
|||||||
|
|
||||||
Future<void> addFavorite(SpotubeSimplePlaylistObject playlist) async {
|
Future<void> addFavorite(SpotubeSimplePlaylistObject playlist) async {
|
||||||
await update((state) async {
|
await update((state) async {
|
||||||
(await metadataPlugin)!.playlist.save(playlist.id);
|
(await metadataPlugin).playlist.save(playlist.id);
|
||||||
return state.copyWith(
|
return state.copyWith(
|
||||||
items: [...state.items, playlist],
|
items: [...state.items, playlist],
|
||||||
) as SpotubePaginationResponseObject<SpotubeSimplePlaylistObject>;
|
) as SpotubePaginationResponseObject<SpotubeSimplePlaylistObject>;
|
||||||
@ -54,7 +54,7 @@ class FavoritePlaylistsNotifier
|
|||||||
|
|
||||||
Future<void> removeFavorite(SpotubeSimplePlaylistObject playlist) async {
|
Future<void> removeFavorite(SpotubeSimplePlaylistObject playlist) async {
|
||||||
await update((state) async {
|
await update((state) async {
|
||||||
(await metadataPlugin)!.playlist.unsave(playlist.id);
|
(await metadataPlugin).playlist.unsave(playlist.id);
|
||||||
return state.copyWith(
|
return state.copyWith(
|
||||||
items: state.items
|
items: state.items
|
||||||
.where((e) => (e as SpotubeSimplePlaylistObject).id != playlist.id)
|
.where((e) => (e as SpotubeSimplePlaylistObject).id != playlist.id)
|
||||||
@ -67,7 +67,7 @@ class FavoritePlaylistsNotifier
|
|||||||
|
|
||||||
Future<void> delete(SpotubeSimplePlaylistObject playlist) async {
|
Future<void> delete(SpotubeSimplePlaylistObject playlist) async {
|
||||||
await update((state) async {
|
await update((state) async {
|
||||||
(await metadataPlugin)!.playlist.deletePlaylist(playlist.id);
|
(await metadataPlugin).playlist.deletePlaylist(playlist.id);
|
||||||
return state.copyWith(
|
return state.copyWith(
|
||||||
items: state.items
|
items: state.items
|
||||||
.where((e) => (e as SpotubeSimplePlaylistObject).id != playlist.id)
|
.where((e) => (e as SpotubeSimplePlaylistObject).id != playlist.id)
|
||||||
@ -82,7 +82,7 @@ class FavoritePlaylistsNotifier
|
|||||||
Future<void> addTracks(String playlistId, List<String> trackIds) async {
|
Future<void> addTracks(String playlistId, List<String> trackIds) async {
|
||||||
if (state.value == null) return;
|
if (state.value == null) return;
|
||||||
|
|
||||||
await (await metadataPlugin)!
|
await (await metadataPlugin)
|
||||||
.playlist
|
.playlist
|
||||||
.addTracks(playlistId, trackIds: trackIds);
|
.addTracks(playlistId, trackIds: trackIds);
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ class FavoritePlaylistsNotifier
|
|||||||
Future<void> removeTracks(String playlistId, List<String> trackIds) async {
|
Future<void> removeTracks(String playlistId, List<String> trackIds) async {
|
||||||
if (state.value == null) return;
|
if (state.value == null) return;
|
||||||
|
|
||||||
await (await metadataPlugin)!
|
await (await metadataPlugin)
|
||||||
.playlist
|
.playlist
|
||||||
.removeTracks(playlistId, trackIds: trackIds);
|
.removeTracks(playlistId, trackIds: trackIds);
|
||||||
|
|
||||||
@ -101,9 +101,9 @@ class FavoritePlaylistsNotifier
|
|||||||
}
|
}
|
||||||
|
|
||||||
final metadataPluginSavedPlaylistsProvider = AsyncNotifierProvider<
|
final metadataPluginSavedPlaylistsProvider = AsyncNotifierProvider<
|
||||||
FavoritePlaylistsNotifier,
|
MetadataPluginSavedPlaylistsNotifier,
|
||||||
SpotubePaginationResponseObject<SpotubeSimplePlaylistObject>>(
|
SpotubePaginationResponseObject<SpotubeSimplePlaylistObject>>(
|
||||||
() => FavoritePlaylistsNotifier(),
|
() => MetadataPluginSavedPlaylistsNotifier(),
|
||||||
);
|
);
|
||||||
|
|
||||||
final metadataPluginIsSavedPlaylistProvider =
|
final metadataPluginIsSavedPlaylistProvider =
|
||||||
|
@ -10,7 +10,7 @@ class PlaylistTracksNotifier extends AutoDisposeFamilyPaginatedAsyncNotifier<
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
fetch(offset, limit) async {
|
fetch(offset, limit) async {
|
||||||
final tracks = await (await metadataPlugin)!.playlist.tracks(
|
final tracks = await (await metadataPlugin).playlist.tracks(
|
||||||
arg,
|
arg,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
@ -28,9 +28,8 @@ class PlaylistTracksNotifier extends AutoDisposeFamilyPaginatedAsyncNotifier<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final metadataPluginPlaylistTracksProvider = AutoDisposeAsyncNotifierProviderFamily<
|
final metadataPluginPlaylistTracksProvider =
|
||||||
PlaylistTracksNotifier,
|
AutoDisposeAsyncNotifierProviderFamily<PlaylistTracksNotifier,
|
||||||
SpotubePaginationResponseObject<SpotubeFullTrackObject>,
|
SpotubePaginationResponseObject<SpotubeFullTrackObject>, String>(
|
||||||
String>(
|
|
||||||
() => PlaylistTracksNotifier(),
|
() => PlaylistTracksNotifier(),
|
||||||
);
|
);
|
||||||
|
@ -6,13 +6,22 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:spotube/models/metadata/metadata.dart';
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
|
|
||||||
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
|
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
|
||||||
|
import 'package:spotube/services/metadata/endpoints/error.dart';
|
||||||
import 'package:spotube/services/metadata/metadata.dart';
|
import 'package:spotube/services/metadata/metadata.dart';
|
||||||
|
|
||||||
mixin MetadataPluginMixin<K>
|
mixin MetadataPluginMixin<K>
|
||||||
// ignore: invalid_use_of_internal_member
|
// ignore: invalid_use_of_internal_member
|
||||||
on AsyncNotifierBase<SpotubePaginationResponseObject<K>> {
|
on AsyncNotifierBase<SpotubePaginationResponseObject<K>> {
|
||||||
Future<MetadataPlugin?> get metadataPlugin async =>
|
Future<MetadataPlugin> get metadataPlugin async {
|
||||||
await ref.read(metadataPluginProvider.future);
|
final plugin = await ref.read(metadataPluginProvider.future);
|
||||||
|
|
||||||
|
if (plugin == null) {
|
||||||
|
throw MetadataPluginException.noDefaultPlugin(
|
||||||
|
"Metadata plugin is not set");
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AutoDisposeAsyncNotifierCacheFor
|
extension AutoDisposeAsyncNotifierCacheFor
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
import 'package:hetu_script/hetu_script.dart';
|
||||||
|
import 'package:hetu_script/values.dart';
|
||||||
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
|
|
||||||
|
class MetadataPluginAlbumEndpoint {
|
||||||
|
final Hetu hetu;
|
||||||
|
MetadataPluginAlbumEndpoint(this.hetu);
|
||||||
|
|
||||||
|
HTInstance get hetuMetadataAlbum =>
|
||||||
|
(hetu.fetch("metadataPlugin") as HTInstance).memberGet("album")
|
||||||
|
as HTInstance;
|
||||||
|
|
||||||
|
Future<SpotubeFullAlbumObject> getAlbum(String id) async {
|
||||||
|
final raw =
|
||||||
|
await hetuMetadataAlbum.invoke("getAlbum", positionalArgs: [id]) as Map;
|
||||||
|
|
||||||
|
return SpotubeFullAlbumObject.fromJson(
|
||||||
|
raw.cast<String, dynamic>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SpotubePaginationResponseObject<SpotubeFullTrackObject>> tracks(
|
||||||
|
String id, {
|
||||||
|
int? offset,
|
||||||
|
int? limit,
|
||||||
|
}) async {
|
||||||
|
final raw = await hetuMetadataAlbum.invoke(
|
||||||
|
"tracks",
|
||||||
|
positionalArgs: [id],
|
||||||
|
namedArgs: {
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit,
|
||||||
|
}..removeWhere((key, value) => value == null),
|
||||||
|
) as Map;
|
||||||
|
|
||||||
|
return SpotubePaginationResponseObject.fromJson(
|
||||||
|
raw.cast<String, dynamic>(),
|
||||||
|
(Map json) =>
|
||||||
|
SpotubeFullTrackObject.fromJson(json.cast<String, dynamic>()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SpotubePaginationResponseObject<SpotubeSimpleAlbumObject>> releases({
|
||||||
|
int? offset,
|
||||||
|
int? limit,
|
||||||
|
}) async {
|
||||||
|
final raw = await hetuMetadataAlbum.invoke(
|
||||||
|
"releases",
|
||||||
|
namedArgs: {
|
||||||
|
"offset": offset,
|
||||||
|
"limit": limit,
|
||||||
|
}..removeWhere((key, value) => value == null),
|
||||||
|
) as Map;
|
||||||
|
|
||||||
|
return SpotubePaginationResponseObject.fromJson(
|
||||||
|
raw.cast<String, dynamic>(),
|
||||||
|
(Map json) =>
|
||||||
|
SpotubeSimpleAlbumObject.fromJson(json.cast<String, dynamic>()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> save(List<String> ids) async {
|
||||||
|
await hetuMetadataAlbum.invoke(
|
||||||
|
"save",
|
||||||
|
positionalArgs: [ids],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> unsave(List<String> ids) async {
|
||||||
|
await hetuMetadataAlbum.invoke(
|
||||||
|
"unsave",
|
||||||
|
positionalArgs: [ids],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ import 'package:spotube/collections/routes.dart';
|
|||||||
import 'package:spotube/components/titlebar/titlebar.dart';
|
import 'package:spotube/components/titlebar/titlebar.dart';
|
||||||
import 'package:spotube/models/metadata/metadata.dart';
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
import 'package:spotube/services/metadata/apis/localstorage.dart';
|
import 'package:spotube/services/metadata/apis/localstorage.dart';
|
||||||
|
import 'package:spotube/services/metadata/endpoints/album.dart';
|
||||||
import 'package:spotube/services/metadata/endpoints/auth.dart';
|
import 'package:spotube/services/metadata/endpoints/auth.dart';
|
||||||
import 'package:spotube/services/metadata/endpoints/playlist.dart';
|
import 'package:spotube/services/metadata/endpoints/playlist.dart';
|
||||||
import 'package:spotube/services/metadata/endpoints/user.dart';
|
import 'package:spotube/services/metadata/endpoints/user.dart';
|
||||||
@ -73,11 +74,13 @@ class MetadataPlugin {
|
|||||||
final Hetu hetu;
|
final Hetu hetu;
|
||||||
|
|
||||||
late final MetadataAuthEndpoint auth;
|
late final MetadataAuthEndpoint auth;
|
||||||
late final MetadataPluginUserEndpoint user;
|
late final MetadataPluginAlbumEndpoint album;
|
||||||
late final MetadataPluginPlaylistEndpoint playlist;
|
late final MetadataPluginPlaylistEndpoint playlist;
|
||||||
|
late final MetadataPluginUserEndpoint user;
|
||||||
|
|
||||||
MetadataPlugin._(this.hetu) {
|
MetadataPlugin._(this.hetu) {
|
||||||
auth = MetadataAuthEndpoint(hetu);
|
auth = MetadataAuthEndpoint(hetu);
|
||||||
|
album = MetadataPluginAlbumEndpoint(hetu);
|
||||||
playlist = MetadataPluginPlaylistEndpoint(hetu);
|
playlist = MetadataPluginPlaylistEndpoint(hetu);
|
||||||
user = MetadataPluginUserEndpoint(hetu);
|
user = MetadataPluginUserEndpoint(hetu);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user