mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
feat: add artist and album providers
This commit is contained in:
parent
326d8212f6
commit
758b0bc9d9
22
lib/provider/metadata_plugin/album/album.dart
Normal file
22
lib/provider/metadata_plugin/album/album.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import 'package:flutter_riverpod/flutter_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/common.dart';
|
||||||
|
import 'package:spotube/services/metadata/endpoints/error.dart';
|
||||||
|
|
||||||
|
final metadataPluginAlbumProvider =
|
||||||
|
FutureProvider.autoDispose.family<SpotubeFullAlbumObject, String>(
|
||||||
|
(ref, id) async {
|
||||||
|
ref.cacheFor();
|
||||||
|
|
||||||
|
final metadataPlugin = await ref.watch(metadataPluginProvider.future);
|
||||||
|
|
||||||
|
if (metadataPlugin == null) {
|
||||||
|
throw MetadataPluginException.noDefaultPlugin(
|
||||||
|
"No metadata plugin is not set",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadataPlugin.album.getAlbum(id);
|
||||||
|
},
|
||||||
|
);
|
32
lib/provider/metadata_plugin/artist/albums.dart
Normal file
32
lib/provider/metadata_plugin/artist/albums.dart
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import 'package:flutter_riverpod/flutter_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/family_paginated.dart';
|
||||||
|
|
||||||
|
class MetadataPluginArtistAlbumNotifier
|
||||||
|
extends FamilyPaginatedAsyncNotifier<SpotubeSimpleAlbumObject, String> {
|
||||||
|
@override
|
||||||
|
Future<SpotubePaginationResponseObject<SpotubeSimpleAlbumObject>> fetch(
|
||||||
|
int offset,
|
||||||
|
int limit,
|
||||||
|
) async {
|
||||||
|
return await (await metadataPlugin).artist.albums(
|
||||||
|
arg,
|
||||||
|
limit: limit,
|
||||||
|
offset: offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
build(arg) async {
|
||||||
|
ref.watch(metadataPluginProvider);
|
||||||
|
return await fetch(0, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final metadataPluginArtistAlbumsProvider = AsyncNotifierFamilyProvider<
|
||||||
|
MetadataPluginArtistAlbumNotifier,
|
||||||
|
SpotubePaginationResponseObject<SpotubeSimpleAlbumObject>,
|
||||||
|
String>(
|
||||||
|
() => MetadataPluginArtistAlbumNotifier(),
|
||||||
|
);
|
22
lib/provider/metadata_plugin/artist/artist.dart
Normal file
22
lib/provider/metadata_plugin/artist/artist.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import 'package:flutter_riverpod/flutter_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/common.dart';
|
||||||
|
import 'package:spotube/services/metadata/endpoints/error.dart';
|
||||||
|
|
||||||
|
final metadataPluginArtistProvider =
|
||||||
|
FutureProvider.autoDispose.family<SpotubeFullArtistObject, String>(
|
||||||
|
(ref, artistId) async {
|
||||||
|
ref.cacheFor();
|
||||||
|
|
||||||
|
final metadataPlugin = await ref.watch(metadataPluginProvider.future);
|
||||||
|
|
||||||
|
if (metadataPlugin == null) {
|
||||||
|
throw MetadataPluginException.noDefaultPlugin(
|
||||||
|
"No metadata plugin is not set",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadataPlugin.artist.getArtist(artistId);
|
||||||
|
},
|
||||||
|
);
|
38
lib/provider/metadata_plugin/artist/top_tracks.dart
Normal file
38
lib/provider/metadata_plugin/artist/top_tracks.dart
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import 'package:hooks_riverpod/hooks_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/family_paginated.dart';
|
||||||
|
import 'package:spotube/provider/metadata_plugin/utils/common.dart';
|
||||||
|
|
||||||
|
class MetadataPluginArtistTopTracksNotifier
|
||||||
|
extends AutoDisposeFamilyPaginatedAsyncNotifier<SpotubeFullTrackObject,
|
||||||
|
String> {
|
||||||
|
MetadataPluginArtistTopTracksNotifier() : super();
|
||||||
|
|
||||||
|
@override
|
||||||
|
fetch(offset, limit) async {
|
||||||
|
final tracks = await (await metadataPlugin).artist.topTracks(
|
||||||
|
arg,
|
||||||
|
offset: offset,
|
||||||
|
limit: limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
return tracks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
build(arg) async {
|
||||||
|
ref.cacheFor();
|
||||||
|
|
||||||
|
ref.watch(metadataPluginProvider);
|
||||||
|
return await fetch(0, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final metadataPluginArtistTopTracksProvider =
|
||||||
|
AutoDisposeAsyncNotifierProviderFamily<
|
||||||
|
MetadataPluginArtistTopTracksNotifier,
|
||||||
|
SpotubePaginationResponseObject<SpotubeFullTrackObject>,
|
||||||
|
String>(
|
||||||
|
() => MetadataPluginArtistTopTracksNotifier(),
|
||||||
|
);
|
@ -0,0 +1,73 @@
|
|||||||
|
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 MetadataPluginSavedArtistNotifier
|
||||||
|
extends PaginatedAsyncNotifier<SpotubeFullArtistObject> {
|
||||||
|
@override
|
||||||
|
Future<SpotubePaginationResponseObject<SpotubeFullArtistObject>> fetch(
|
||||||
|
int offset,
|
||||||
|
int limit,
|
||||||
|
) async {
|
||||||
|
return await (await metadataPlugin).user.savedArtists(
|
||||||
|
limit: limit,
|
||||||
|
offset: offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
build() async {
|
||||||
|
ref.watch(metadataPluginProvider);
|
||||||
|
return await fetch(0, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addFavorite(List<SpotubeFullArtistObject> artists) async {
|
||||||
|
await update((state) async {
|
||||||
|
(await metadataPlugin).artist.save(artists.map((e) => e.id).toList());
|
||||||
|
return state.copyWith(
|
||||||
|
items: [...state.items, artists],
|
||||||
|
) as SpotubePaginationResponseObject<SpotubeFullArtistObject>;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (final artist in artists) {
|
||||||
|
ref.invalidate(metadataPluginIsSavedArtistProvider(artist.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> removeFavorite(List<SpotubeFullArtistObject> artists) async {
|
||||||
|
await update((state) async {
|
||||||
|
final artistIds = artists.map((e) => e.id).toList();
|
||||||
|
(await metadataPlugin).artist.unsave(artistIds);
|
||||||
|
return state.copyWith(
|
||||||
|
items: state.items
|
||||||
|
.where(
|
||||||
|
(e) =>
|
||||||
|
artistIds.contains((e as SpotubeFullArtistObject).id) ==
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.toList() as List<SpotubeFullArtistObject>,
|
||||||
|
) as SpotubePaginationResponseObject<SpotubeFullArtistObject>;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (final artist in artists) {
|
||||||
|
ref.invalidate(metadataPluginIsSavedArtistProvider(artist.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final metadataPluginSavedArtistsProvider = AsyncNotifierProvider<
|
||||||
|
MetadataPluginSavedArtistNotifier,
|
||||||
|
SpotubePaginationResponseObject<SpotubeFullArtistObject>>(
|
||||||
|
() => MetadataPluginSavedArtistNotifier(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final metadataPluginIsSavedArtistProvider =
|
||||||
|
FutureProvider.autoDispose.family<bool, String>(
|
||||||
|
(ref, artistId) async {
|
||||||
|
final metadataPlugin = await ref.watch(metadataPluginProvider.future);
|
||||||
|
|
||||||
|
return metadataPlugin!.user
|
||||||
|
.isSavedArtists([artistId]).then((value) => value.first);
|
||||||
|
},
|
||||||
|
);
|
22
lib/provider/metadata_plugin/playlist/playlist.dart
Normal file
22
lib/provider/metadata_plugin/playlist/playlist.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import 'package:flutter_riverpod/flutter_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/common.dart';
|
||||||
|
import 'package:spotube/services/metadata/endpoints/error.dart';
|
||||||
|
|
||||||
|
final metadataPluginPlaylistProvider =
|
||||||
|
FutureProvider.autoDispose.family<SpotubeFullPlaylistObject, String>(
|
||||||
|
(ref, id) async {
|
||||||
|
ref.cacheFor();
|
||||||
|
|
||||||
|
final metadataPlugin = await ref.watch(metadataPluginProvider.future);
|
||||||
|
|
||||||
|
if (metadataPlugin == null) {
|
||||||
|
throw MetadataPluginException.noDefaultPlugin(
|
||||||
|
"No metadata plugin is not set",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadataPlugin.playlist.getPlaylist(id);
|
||||||
|
},
|
||||||
|
);
|
36
lib/provider/metadata_plugin/tracks/album.dart
Normal file
36
lib/provider/metadata_plugin/tracks/album.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:hooks_riverpod/hooks_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/family_paginated.dart';
|
||||||
|
import 'package:spotube/provider/metadata_plugin/utils/common.dart';
|
||||||
|
|
||||||
|
class MetadataPluginAlbumTracksNotifier
|
||||||
|
extends AutoDisposeFamilyPaginatedAsyncNotifier<SpotubeFullTrackObject,
|
||||||
|
String> {
|
||||||
|
MetadataPluginAlbumTracksNotifier() : super();
|
||||||
|
|
||||||
|
@override
|
||||||
|
fetch(offset, limit) async {
|
||||||
|
final tracks = await (await metadataPlugin).album.tracks(
|
||||||
|
arg,
|
||||||
|
offset: offset,
|
||||||
|
limit: limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
return tracks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
build(arg) async {
|
||||||
|
ref.cacheFor();
|
||||||
|
|
||||||
|
ref.watch(metadataPluginProvider);
|
||||||
|
return await fetch(0, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final metadataPluginAlbumTracksProvider =
|
||||||
|
AutoDisposeAsyncNotifierProviderFamily<MetadataPluginAlbumTracksNotifier,
|
||||||
|
SpotubePaginationResponseObject<SpotubeFullTrackObject>, String>(
|
||||||
|
() => MetadataPluginAlbumTracksNotifier(),
|
||||||
|
);
|
@ -4,9 +4,10 @@ import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
|
|||||||
import 'package:spotube/provider/metadata_plugin/utils/family_paginated.dart';
|
import 'package:spotube/provider/metadata_plugin/utils/family_paginated.dart';
|
||||||
import 'package:spotube/provider/metadata_plugin/utils/common.dart';
|
import 'package:spotube/provider/metadata_plugin/utils/common.dart';
|
||||||
|
|
||||||
class PlaylistTracksNotifier extends AutoDisposeFamilyPaginatedAsyncNotifier<
|
class MetadataPluginPlaylistTracksNotifier
|
||||||
SpotubeFullTrackObject, String> {
|
extends AutoDisposeFamilyPaginatedAsyncNotifier<SpotubeFullTrackObject,
|
||||||
PlaylistTracksNotifier() : super();
|
String> {
|
||||||
|
MetadataPluginPlaylistTracksNotifier() : super();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
fetch(offset, limit) async {
|
fetch(offset, limit) async {
|
||||||
@ -29,7 +30,7 @@ class PlaylistTracksNotifier extends AutoDisposeFamilyPaginatedAsyncNotifier<
|
|||||||
}
|
}
|
||||||
|
|
||||||
final metadataPluginPlaylistTracksProvider =
|
final metadataPluginPlaylistTracksProvider =
|
||||||
AutoDisposeAsyncNotifierProviderFamily<PlaylistTracksNotifier,
|
AutoDisposeAsyncNotifierProviderFamily<MetadataPluginPlaylistTracksNotifier,
|
||||||
SpotubePaginationResponseObject<SpotubeFullTrackObject>, String>(
|
SpotubePaginationResponseObject<SpotubeFullTrackObject>, String>(
|
||||||
() => PlaylistTracksNotifier(),
|
() => MetadataPluginPlaylistTracksNotifier(),
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
import 'package:hetu_script/hetu_script.dart';
|
||||||
|
import 'package:hetu_script/values.dart';
|
||||||
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
|
|
||||||
|
class MetadataPluginArtistEndpoint {
|
||||||
|
final Hetu hetu;
|
||||||
|
MetadataPluginArtistEndpoint(this.hetu);
|
||||||
|
|
||||||
|
HTInstance get hetuMetadataArtist =>
|
||||||
|
(hetu.fetch("metadataPlugin") as HTInstance).memberGet("artist")
|
||||||
|
as HTInstance;
|
||||||
|
|
||||||
|
Future<SpotubeFullArtistObject> getArtist(String id) async {
|
||||||
|
final raw = await hetuMetadataArtist
|
||||||
|
.invoke("getArtist", positionalArgs: [id]) as Map;
|
||||||
|
|
||||||
|
return SpotubeFullArtistObject.fromJson(
|
||||||
|
raw.cast<String, dynamic>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SpotubePaginationResponseObject<SpotubeFullTrackObject>> topTracks(
|
||||||
|
String id, {
|
||||||
|
int? offset,
|
||||||
|
int? limit,
|
||||||
|
}) async {
|
||||||
|
final raw = await hetuMetadataArtist.invoke(
|
||||||
|
"topTracks",
|
||||||
|
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>> albums(
|
||||||
|
String id, {
|
||||||
|
int? offset,
|
||||||
|
int? limit,
|
||||||
|
}) async {
|
||||||
|
final raw = await hetuMetadataArtist.invoke(
|
||||||
|
"albums",
|
||||||
|
positionalArgs: [id],
|
||||||
|
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 hetuMetadataArtist.invoke(
|
||||||
|
"save",
|
||||||
|
positionalArgs: [ids],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> unsave(List<String> ids) async {
|
||||||
|
await hetuMetadataArtist.invoke(
|
||||||
|
"unsave",
|
||||||
|
positionalArgs: [ids],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ 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/album.dart';
|
||||||
|
import 'package:spotube/services/metadata/endpoints/artist.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';
|
||||||
@ -74,12 +75,16 @@ class MetadataPlugin {
|
|||||||
final Hetu hetu;
|
final Hetu hetu;
|
||||||
|
|
||||||
late final MetadataAuthEndpoint auth;
|
late final MetadataAuthEndpoint auth;
|
||||||
|
|
||||||
late final MetadataPluginAlbumEndpoint album;
|
late final MetadataPluginAlbumEndpoint album;
|
||||||
|
late final MetadataPluginArtistEndpoint artist;
|
||||||
late final MetadataPluginPlaylistEndpoint playlist;
|
late final MetadataPluginPlaylistEndpoint playlist;
|
||||||
late final MetadataPluginUserEndpoint user;
|
late final MetadataPluginUserEndpoint user;
|
||||||
|
|
||||||
MetadataPlugin._(this.hetu) {
|
MetadataPlugin._(this.hetu) {
|
||||||
auth = MetadataAuthEndpoint(hetu);
|
auth = MetadataAuthEndpoint(hetu);
|
||||||
|
|
||||||
|
artist = MetadataPluginArtistEndpoint(hetu);
|
||||||
album = MetadataPluginAlbumEndpoint(hetu);
|
album = MetadataPluginAlbumEndpoint(hetu);
|
||||||
playlist = MetadataPluginPlaylistEndpoint(hetu);
|
playlist = MetadataPluginPlaylistEndpoint(hetu);
|
||||||
user = MetadataPluginUserEndpoint(hetu);
|
user = MetadataPluginUserEndpoint(hetu);
|
||||||
|
Loading…
Reference in New Issue
Block a user