diff --git a/lib/components/album/album_card.dart b/lib/components/album/album_card.dart index a71fbf03..4931b162 100644 --- a/lib/components/album/album_card.dart +++ b/lib/components/album/album_card.dart @@ -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( diff --git a/lib/components/playlist/playlist_card.dart b/lib/components/playlist/playlist_card.dart index ae6f20e5..98309aef 100644 --- a/lib/components/playlist/playlist_card.dart +++ b/lib/components/playlist/playlist_card.dart @@ -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"), diff --git a/lib/extensions/album_simple.dart b/lib/extensions/album_simple.dart index 7c8ae09e..5678390c 100644 --- a/lib/extensions/album_simple.dart +++ b/lib/extensions/album_simple.dart @@ -1,21 +1,6 @@ import 'package:spotify/spotify.dart'; extension AlbumExtensions on AlbumSimple { - Map 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; diff --git a/lib/extensions/artist_simple.dart b/lib/extensions/artist_simple.dart index 6a80300e..7997355d 100644 --- a/lib/extensions/artist_simple.dart +++ b/lib/extensions/artist_simple.dart @@ -1,17 +1,5 @@ import 'package:spotify/spotify.dart'; -extension ArtistJson on ArtistSimple { - Map toJson() { - return { - "href": href, - "id": id, - "name": name, - "type": type, - "uri": uri, - }; - } -} - extension ArtistExtension on List { String asString() { return map((e) => e.name?.replaceAll(",", " ")).join(", "); diff --git a/lib/extensions/track.dart b/lib/extensions/track.dart index 9755179d..02c0c492 100644 --- a/lib/extensions/track.dart +++ b/lib/extensions/track.dart @@ -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 toJson() { - return TrackExtensions.trackToJson(this); - } - - static Map 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 { diff --git a/lib/models/local_track.dart b/lib/models/local_track.dart index 923f5f26..def3b64f 100644 --- a/lib/models/local_track.dart +++ b/lib/models/local_track.dart @@ -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 toJson() { return { - ...TrackExtensions.trackToJson(this), + ...super.toJson(), 'path': path, }; } diff --git a/lib/provider/history/history.dart b/lib/provider/history/history.dart new file mode 100644 index 00000000..d2ff89a2 --- /dev/null +++ b/lib/provider/history/history.dart @@ -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 items; + const PlaybackHistoryState({this.items = const []}); + + factory PlaybackHistoryState.fromJson(Map json) { + return PlaybackHistoryState( + items: + json["items"]?.map((json) => PlaybackHistoryBase.fromJson(json))); + } + + Map toJson() { + return { + "items": items.map((s) => s.toJson()).toList(), + }; + } + + PlaybackHistoryState copyWith({ + List? items, + }) { + return PlaybackHistoryState(items: items ?? this.items); + } +} + +class PlaybackHistoryNotifier + extends PersistedStateNotifier { + PlaybackHistoryNotifier() + : super(const PlaybackHistoryState(), "playback_history"); + + @override + FutureOr fromJson(Map json) => + PlaybackHistoryState.fromJson(json); + + @override + Map toJson() { + return state.toJson(); + } + + void addPlaylists(List playlists) { + state = state.copyWith( + items: [ + ...state.items, + for (final playlist in playlists) + PlaybackHistoryPlaylist(date: DateTime.now(), playlist: playlist), + ], + ); + } + + void addAlbums(List albums) { + state = state.copyWith( + items: [ + ...state.items, + for (final album in albums) + PlaybackHistoryAlbum(date: DateTime.now(), album: album), + ], + ); + } + + void addTracks(List 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( + (ref) => PlaybackHistoryNotifier(), +); diff --git a/lib/provider/history/state.dart b/lib/provider/history/state.dart new file mode 100644 index 00000000..a011c2b0 --- /dev/null +++ b/lib/provider/history/state.dart @@ -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 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 toJson() => _$PlaybackHistoryBaseToJson(this); +} + +@JsonSerializable() +class PlaybackHistoryPlaylist extends PlaybackHistoryBase { + final PlaylistSimple playlist; + PlaybackHistoryPlaylist({ + required super.date, + required this.playlist, + }); + + factory PlaybackHistoryPlaylist.fromJson(Map json) => + _$PlaybackHistoryPlaylistFromJson(json); + + @override + Map toJson() => _$PlaybackHistoryPlaylistToJson(this); +} + +@JsonSerializable() +class PlaybackHistoryAlbum extends PlaybackHistoryBase { + final AlbumSimple album; + PlaybackHistoryAlbum({ + required super.date, + required this.album, + }); + + factory PlaybackHistoryAlbum.fromJson(Map json) => + _$PlaybackHistoryAlbumFromJson(json); + + @override + Map toJson() => _$PlaybackHistoryAlbumToJson(this); +} + +@JsonSerializable() +class PlaybackHistoryTrack extends PlaybackHistoryBase { + final TrackSimple track; + PlaybackHistoryTrack({ + required super.date, + required this.track, + }); + + factory PlaybackHistoryTrack.fromJson(Map json) => + _$PlaybackHistoryTrackFromJson(json); + + @override + Map toJson() => _$PlaybackHistoryTrackToJson(this); +} diff --git a/lib/provider/history/state.g.dart b/lib/provider/history/state.g.dart new file mode 100644 index 00000000..f8aa9885 --- /dev/null +++ b/lib/provider/history/state.g.dart @@ -0,0 +1,61 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'state.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +PlaybackHistoryBase _$PlaybackHistoryBaseFromJson(Map json) => + PlaybackHistoryBase( + date: DateTime.parse(json['date'] as String), + ); + +Map _$PlaybackHistoryBaseToJson( + PlaybackHistoryBase instance) => + { + 'date': instance.date.toIso8601String(), + }; + +PlaybackHistoryPlaylist _$PlaybackHistoryPlaylistFromJson( + Map json) => + PlaybackHistoryPlaylist( + date: DateTime.parse(json['date'] as String), + playlist: + PlaylistSimple.fromJson(json['playlist'] as Map), + ); + +Map _$PlaybackHistoryPlaylistToJson( + PlaybackHistoryPlaylist instance) => + { + 'date': instance.date.toIso8601String(), + 'playlist': instance.playlist, + }; + +PlaybackHistoryAlbum _$PlaybackHistoryAlbumFromJson( + Map json) => + PlaybackHistoryAlbum( + date: DateTime.parse(json['date'] as String), + album: AlbumSimple.fromJson(json['album'] as Map), + ); + +Map _$PlaybackHistoryAlbumToJson( + PlaybackHistoryAlbum instance) => + { + 'date': instance.date.toIso8601String(), + 'album': instance.album, + }; + +PlaybackHistoryTrack _$PlaybackHistoryTrackFromJson( + Map json) => + PlaybackHistoryTrack( + date: DateTime.parse(json['date'] as String), + track: TrackSimple.fromJson(json['track'] as Map), + ); + +Map _$PlaybackHistoryTrackToJson( + PlaybackHistoryTrack instance) => + { + 'date': instance.date.toIso8601String(), + 'track': instance.track, + }; diff --git a/pubspec.lock b/pubspec.lock index 8d19f604..68bad095 100644 --- a/pubspec.lock +++ b/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: diff --git a/pubspec.yaml b/pubspec.yaml index 16f51981..ee86c67c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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