mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-12-08 16:27:31 +00:00
feat: add playback history provider
This commit is contained in:
parent
9e25c742d4
commit
2d9cd58fce
@ -10,6 +10,7 @@ import 'package:spotube/extensions/image.dart';
|
||||
import 'package:spotube/extensions/track.dart';
|
||||
import 'package:spotube/models/connect/connect.dart';
|
||||
import 'package:spotube/provider/connect/connect.dart';
|
||||
import 'package:spotube/provider/history/history.dart';
|
||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||
import 'package:spotube/provider/spotify/spotify.dart';
|
||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||
@ -32,6 +33,7 @@ class AlbumCard extends HookConsumerWidget {
|
||||
final playing =
|
||||
useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying;
|
||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
||||
final historyNotifier = ref.read(playbackHistoryProvider.notifier);
|
||||
|
||||
bool isPlaylistPlaying = useMemoized(
|
||||
() => playlist.containsCollection(album.id!),
|
||||
@ -87,6 +89,7 @@ class AlbumCard extends HookConsumerWidget {
|
||||
} else {
|
||||
await playlistNotifier.load(fetchedTracks, autoPlay: true);
|
||||
playlistNotifier.addCollection(album.id!);
|
||||
historyNotifier.addAlbums([album]);
|
||||
}
|
||||
} finally {
|
||||
updating.value = false;
|
||||
@ -104,6 +107,7 @@ class AlbumCard extends HookConsumerWidget {
|
||||
if (fetchedTracks.isEmpty) return;
|
||||
playlistNotifier.addTracks(fetchedTracks);
|
||||
playlistNotifier.addCollection(album.id!);
|
||||
historyNotifier.addAlbums([album]);
|
||||
if (context.mounted) {
|
||||
final snackbar = SnackBar(
|
||||
content: Text(
|
||||
|
||||
@ -7,6 +7,7 @@ import 'package:spotube/components/shared/playbutton_card.dart';
|
||||
import 'package:spotube/extensions/image.dart';
|
||||
import 'package:spotube/models/connect/connect.dart';
|
||||
import 'package:spotube/provider/connect/connect.dart';
|
||||
import 'package:spotube/provider/history/history.dart';
|
||||
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
|
||||
import 'package:spotube/provider/spotify/spotify.dart';
|
||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||
@ -22,6 +23,8 @@ class PlaylistCard extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, ref) {
|
||||
final playlistQueue = ref.watch(proxyPlaylistProvider);
|
||||
final playlistNotifier = ref.watch(proxyPlaylistProvider.notifier);
|
||||
final historyNotifier = ref.read(playbackHistoryProvider.notifier);
|
||||
|
||||
final playing =
|
||||
useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying;
|
||||
bool isPlaylistPlaying = useMemoized(
|
||||
@ -86,6 +89,7 @@ class PlaylistCard extends HookConsumerWidget {
|
||||
} else {
|
||||
await playlistNotifier.load(fetchedTracks, autoPlay: true);
|
||||
playlistNotifier.addCollection(playlist.id!);
|
||||
historyNotifier.addPlaylists([playlist]);
|
||||
}
|
||||
} finally {
|
||||
if (context.mounted) {
|
||||
@ -104,6 +108,7 @@ class PlaylistCard extends HookConsumerWidget {
|
||||
|
||||
playlistNotifier.addTracks(fetchedTracks);
|
||||
playlistNotifier.addCollection(playlist.id!);
|
||||
historyNotifier.addPlaylists([playlist]);
|
||||
if (context.mounted) {
|
||||
final snackbar = SnackBar(
|
||||
content: Text("Added ${fetchedTracks.length} tracks to queue"),
|
||||
|
||||
@ -1,21 +1,6 @@
|
||||
import 'package:spotify/spotify.dart';
|
||||
|
||||
extension AlbumExtensions on AlbumSimple {
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
"albumType": albumType?.name,
|
||||
"id": id,
|
||||
"name": name,
|
||||
"images": images
|
||||
?.map((image) => {
|
||||
"height": image.height,
|
||||
"url": image.url,
|
||||
"width": image.width,
|
||||
})
|
||||
.toList(),
|
||||
};
|
||||
}
|
||||
|
||||
Album toAlbum() {
|
||||
Album album = Album();
|
||||
album.albumType = albumType;
|
||||
|
||||
@ -1,17 +1,5 @@
|
||||
import 'package:spotify/spotify.dart';
|
||||
|
||||
extension ArtistJson on ArtistSimple {
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
"href": href,
|
||||
"id": id,
|
||||
"name": name,
|
||||
"type": type,
|
||||
"uri": uri,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
extension ArtistExtension on List<ArtistSimple> {
|
||||
String asString() {
|
||||
return map((e) => e.name?.replaceAll(",", " ")).join(", ");
|
||||
|
||||
@ -3,8 +3,6 @@ import 'dart:io';
|
||||
import 'package:metadata_god/metadata_god.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/extensions/album_simple.dart';
|
||||
import 'package:spotube/extensions/artist_simple.dart';
|
||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||
|
||||
extension TrackExtensions on Track {
|
||||
@ -39,33 +37,6 @@ extension TrackExtensions on Track {
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return TrackExtensions.trackToJson(this);
|
||||
}
|
||||
|
||||
static Map<String, dynamic> trackToJson(Track track) {
|
||||
return {
|
||||
"album": track.album?.toJson(),
|
||||
"artists": track.artists?.map((artist) => artist.toJson()).toList(),
|
||||
"available_markets": track.availableMarkets?.map((e) => e.name).toList(),
|
||||
"disc_number": track.discNumber,
|
||||
"duration_ms": track.durationMs,
|
||||
"explicit": track.explicit,
|
||||
// "external_ids"track.: externalIds,
|
||||
// "external_urls"track.: externalUrls,
|
||||
"href": track.href,
|
||||
"id": track.id,
|
||||
"is_playable": track.isPlayable,
|
||||
// "linked_from"track.: linkedFrom,
|
||||
"name": track.name,
|
||||
"popularity": track.popularity,
|
||||
"preview_rrl": track.previewUrl,
|
||||
"track_number": track.trackNumber,
|
||||
"type": track.type,
|
||||
"uri": track.uri,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
extension TrackSimpleExtensions on TrackSimple {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/extensions/track.dart';
|
||||
|
||||
class LocalTrack extends Track {
|
||||
final String path;
|
||||
@ -35,9 +34,10 @@ class LocalTrack extends Track {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
...TrackExtensions.trackToJson(this),
|
||||
...super.toJson(),
|
||||
'path': path,
|
||||
};
|
||||
}
|
||||
|
||||
83
lib/provider/history/history.dart
Normal file
83
lib/provider/history/history.dart
Normal file
@ -0,0 +1,83 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/provider/history/state.dart';
|
||||
import 'package:spotube/utils/persisted_state_notifier.dart';
|
||||
|
||||
class PlaybackHistoryState {
|
||||
final List<PlaybackHistoryBase> items;
|
||||
const PlaybackHistoryState({this.items = const []});
|
||||
|
||||
factory PlaybackHistoryState.fromJson(Map<String, dynamic> json) {
|
||||
return PlaybackHistoryState(
|
||||
items:
|
||||
json["items"]?.map((json) => PlaybackHistoryBase.fromJson(json)));
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
"items": items.map((s) => s.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
|
||||
PlaybackHistoryState copyWith({
|
||||
List<PlaybackHistoryBase>? items,
|
||||
}) {
|
||||
return PlaybackHistoryState(items: items ?? this.items);
|
||||
}
|
||||
}
|
||||
|
||||
class PlaybackHistoryNotifier
|
||||
extends PersistedStateNotifier<PlaybackHistoryState> {
|
||||
PlaybackHistoryNotifier()
|
||||
: super(const PlaybackHistoryState(), "playback_history");
|
||||
|
||||
@override
|
||||
FutureOr<PlaybackHistoryState> fromJson(Map<String, dynamic> json) =>
|
||||
PlaybackHistoryState.fromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return state.toJson();
|
||||
}
|
||||
|
||||
void addPlaylists(List<PlaylistSimple> playlists) {
|
||||
state = state.copyWith(
|
||||
items: [
|
||||
...state.items,
|
||||
for (final playlist in playlists)
|
||||
PlaybackHistoryPlaylist(date: DateTime.now(), playlist: playlist),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void addAlbums(List<AlbumSimple> albums) {
|
||||
state = state.copyWith(
|
||||
items: [
|
||||
...state.items,
|
||||
for (final album in albums)
|
||||
PlaybackHistoryAlbum(date: DateTime.now(), album: album),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void addTracks(List<TrackSimple> tracks) {
|
||||
state = state.copyWith(
|
||||
items: [
|
||||
...state.items,
|
||||
for (final track in tracks)
|
||||
PlaybackHistoryTrack(date: DateTime.now(), track: track),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
state = state.copyWith(items: []);
|
||||
}
|
||||
}
|
||||
|
||||
final playbackHistoryProvider =
|
||||
StateNotifierProvider<PlaybackHistoryNotifier, PlaybackHistoryState>(
|
||||
(ref) => PlaybackHistoryNotifier(),
|
||||
);
|
||||
70
lib/provider/history/state.dart
Normal file
70
lib/provider/history/state.dart
Normal file
@ -0,0 +1,70 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
|
||||
part 'state.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class PlaybackHistoryBase {
|
||||
final DateTime date;
|
||||
|
||||
const PlaybackHistoryBase({required this.date});
|
||||
|
||||
factory PlaybackHistoryBase.fromJson(Map<String, dynamic> json) {
|
||||
if (json.containsKey("playlist")) {
|
||||
return PlaybackHistoryPlaylist.fromJson(json);
|
||||
} else if (json.containsKey("album")) {
|
||||
return PlaybackHistoryAlbum.fromJson(json);
|
||||
} else if (json.containsKey("track")) {
|
||||
return PlaybackHistoryTrack.fromJson(json);
|
||||
}
|
||||
|
||||
return _$PlaybackHistoryBaseFromJson(json);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => _$PlaybackHistoryBaseToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class PlaybackHistoryPlaylist extends PlaybackHistoryBase {
|
||||
final PlaylistSimple playlist;
|
||||
PlaybackHistoryPlaylist({
|
||||
required super.date,
|
||||
required this.playlist,
|
||||
});
|
||||
|
||||
factory PlaybackHistoryPlaylist.fromJson(Map<String, dynamic> json) =>
|
||||
_$PlaybackHistoryPlaylistFromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() => _$PlaybackHistoryPlaylistToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class PlaybackHistoryAlbum extends PlaybackHistoryBase {
|
||||
final AlbumSimple album;
|
||||
PlaybackHistoryAlbum({
|
||||
required super.date,
|
||||
required this.album,
|
||||
});
|
||||
|
||||
factory PlaybackHistoryAlbum.fromJson(Map<String, dynamic> json) =>
|
||||
_$PlaybackHistoryAlbumFromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() => _$PlaybackHistoryAlbumToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class PlaybackHistoryTrack extends PlaybackHistoryBase {
|
||||
final TrackSimple track;
|
||||
PlaybackHistoryTrack({
|
||||
required super.date,
|
||||
required this.track,
|
||||
});
|
||||
|
||||
factory PlaybackHistoryTrack.fromJson(Map<String, dynamic> json) =>
|
||||
_$PlaybackHistoryTrackFromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() => _$PlaybackHistoryTrackToJson(this);
|
||||
}
|
||||
61
lib/provider/history/state.g.dart
Normal file
61
lib/provider/history/state.g.dart
Normal file
@ -0,0 +1,61 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
PlaybackHistoryBase _$PlaybackHistoryBaseFromJson(Map<String, dynamic> json) =>
|
||||
PlaybackHistoryBase(
|
||||
date: DateTime.parse(json['date'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$PlaybackHistoryBaseToJson(
|
||||
PlaybackHistoryBase instance) =>
|
||||
<String, dynamic>{
|
||||
'date': instance.date.toIso8601String(),
|
||||
};
|
||||
|
||||
PlaybackHistoryPlaylist _$PlaybackHistoryPlaylistFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
PlaybackHistoryPlaylist(
|
||||
date: DateTime.parse(json['date'] as String),
|
||||
playlist:
|
||||
PlaylistSimple.fromJson(json['playlist'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$PlaybackHistoryPlaylistToJson(
|
||||
PlaybackHistoryPlaylist instance) =>
|
||||
<String, dynamic>{
|
||||
'date': instance.date.toIso8601String(),
|
||||
'playlist': instance.playlist,
|
||||
};
|
||||
|
||||
PlaybackHistoryAlbum _$PlaybackHistoryAlbumFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
PlaybackHistoryAlbum(
|
||||
date: DateTime.parse(json['date'] as String),
|
||||
album: AlbumSimple.fromJson(json['album'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$PlaybackHistoryAlbumToJson(
|
||||
PlaybackHistoryAlbum instance) =>
|
||||
<String, dynamic>{
|
||||
'date': instance.date.toIso8601String(),
|
||||
'album': instance.album,
|
||||
};
|
||||
|
||||
PlaybackHistoryTrack _$PlaybackHistoryTrackFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
PlaybackHistoryTrack(
|
||||
date: DateTime.parse(json['date'] as String),
|
||||
track: TrackSimple.fromJson(json['track'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$PlaybackHistoryTrackToJson(
|
||||
PlaybackHistoryTrack instance) =>
|
||||
<String, dynamic>{
|
||||
'date': instance.date.toIso8601String(),
|
||||
'track': instance.track,
|
||||
};
|
||||
11
pubspec.lock
11
pubspec.lock
@ -2105,11 +2105,12 @@ packages:
|
||||
spotify:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: spotify
|
||||
sha256: "2308a84511c18ec1e72515a57e28abb1467389549d571c460732b4538c2e34de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.3"
|
||||
path: "."
|
||||
ref: "feat/to-json"
|
||||
resolved-ref: "05ace91cdfe64db23d8c62077069e7c25b3645cb"
|
||||
url: "https://github.com/KRTirtho/spotify-dart.git"
|
||||
source: git
|
||||
version: "0.13.5"
|
||||
sqflite:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@ -122,7 +122,10 @@ dependencies:
|
||||
flutter_sharing_intent: ^1.1.0
|
||||
flutter_broadcasts: ^0.4.0
|
||||
freezed_annotation: ^2.4.1
|
||||
spotify: ^0.13.3
|
||||
spotify:
|
||||
git:
|
||||
url: https://github.com/KRTirtho/spotify-dart.git
|
||||
ref: feat/to-json
|
||||
bonsoir: ^5.1.9
|
||||
shelf: ^1.4.1
|
||||
shelf_router: ^1.1.4
|
||||
|
||||
Loading…
Reference in New Issue
Block a user