diff --git a/README.md b/README.md index fdbc8908..ad6f6b59 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![Spotube](assets/spotube_banner.png) -Spotube is a [qt](https://qt.io) based lightweight spotify client which uses [nodegui/react-nodegui](https://github.com/nodegui/react-nodegui) as frontend & nodejs as backend. It utilizes the power of Spotify & Youtube's public API & creates a hazardless, performant & resource friendly User Experience +Spotube is a [Flutter](https://flutter.dev) based lightweight spotify client. It utilizes the power of Spotify & Youtube's public API & creates a hazardless, performant & resource friendly User Experience ![Application Screenshot](assets/spotube-screenshot.png) ## Features @@ -10,11 +10,11 @@ Following are the features that currently spotube offers: - Open Source - No telementry, diagnostics or user data collection - Lightweight & resource friendly -- Near native performance & seemless with default desktop themes (Win10, Win7, OSX, QT-default) +- Near native performance (Thanks to Flutter+Skia) - Playback control is on user's machine instead of server based - Small size & less data hungry - No spotify or youtube ads since it uses all public & free APIs (But it's recommended to support the creators by watching/liking/subscribing to the artists youtube channel or add as favourite track in spotify. Mostly buying spotify premium is the best way to support their valuable creations) -- Lyrics +- Lyrics (WIP) - Downloadable track ## Requirements (Linux🐧 only) diff --git a/assets/spotube-screenshot.png b/assets/spotube-screenshot.png index a8dd920c..1a19e7df 100644 Binary files a/assets/spotube-screenshot.png and b/assets/spotube-screenshot.png differ diff --git a/lib/components/Home.dart b/lib/components/Home.dart index a50e4dad..e2258f17 100644 --- a/lib/components/Home.dart +++ b/lib/components/Home.dart @@ -8,6 +8,7 @@ import 'package:spotube/components/CategoryCard.dart'; import 'package:spotube/components/Login.dart'; import 'package:spotube/components/Player.dart' as player; import 'package:spotube/components/Settings.dart'; +import 'package:spotube/components/UserLibrary.dart'; import 'package:spotube/models/LocalStorageKeys.dart'; import 'package:spotube/models/sideBarTiles.dart'; import 'package:spotube/provider/Auth.dart'; @@ -205,6 +206,7 @@ class _HomeState extends State { ), ), ), + if (_selectedIndex == 2) const UserLibrary(), // player itself ], ), diff --git a/lib/components/PlaylistCard.dart b/lib/components/PlaylistCard.dart index fbd1ea20..fdb50f4c 100644 --- a/lib/components/PlaylistCard.dart +++ b/lib/components/PlaylistCard.dart @@ -64,10 +64,17 @@ class _PlaylistCardState extends State { onPressed: () async { if (isPlaylistPlaying) return; - List tracks = (await data.spotifyApi.playlists - .getTracksByPlaylistId(widget.playlist.id!) - .all()) - .toList(); + List tracks = + (widget.playlist.id != "user-liked-tracks" + ? await data.spotifyApi.playlists + .getTracksByPlaylistId( + widget.playlist.id!) + .all() + : await data.spotifyApi.tracks.me.saved + .all() + .then((tracks) => + tracks.map((e) => e.track!))) + .toList(); playback.setCurrentPlaylist = CurrentPlaylist( tracks: tracks, diff --git a/lib/components/PlaylistView.dart b/lib/components/PlaylistView.dart index f09ca7b3..a500872d 100644 --- a/lib/components/PlaylistView.dart +++ b/lib/components/PlaylistView.dart @@ -25,7 +25,7 @@ class _PlaylistViewState extends State { children: [ TableCell( child: Text( - track.key.toString(), + (track.key + 1).toString(), textAlign: TextAlign.center, )), TableCell( @@ -85,9 +85,13 @@ class _PlaylistViewState extends State { return Consumer(builder: (_, data, __) { return Scaffold( body: FutureBuilder>( - future: data.spotifyApi.playlists - .getTracksByPlaylistId(widget.playlist.id) - .all(), + future: widget.playlist.id != "user-liked-tracks" + ? data.spotifyApi.playlists + .getTracksByPlaylistId(widget.playlist.id) + .all() + : data.spotifyApi.tracks.me.saved + .all() + .then((tracks) => tracks.map((e) => e.track!)), builder: (context, snapshot) { List tracks = snapshot.data?.toList() ?? []; TextStyle tableHeadStyle = diff --git a/lib/components/UserLibrary.dart b/lib/components/UserLibrary.dart new file mode 100644 index 00000000..bb90aa77 --- /dev/null +++ b/lib/components/UserLibrary.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart' hide Image; +import 'package:provider/provider.dart'; +import 'package:spotify/spotify.dart'; +import 'package:spotube/components/PlaylistCard.dart'; +import 'package:spotube/provider/SpotifyDI.dart'; + +class UserLibrary extends StatefulWidget { + const UserLibrary({Key? key}) : super(key: key); + + @override + _UserLibraryState createState() => _UserLibraryState(); +} + +class _UserLibraryState extends State { + @override + Widget build(BuildContext context) { + SpotifyDI data = context.watch(); + + return FutureBuilder>( + future: data.spotifyApi.playlists.me.all(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const CircularProgressIndicator.adaptive(); + } + Image image = Image(); + image.height = 300; + image.width = 300; + PlaylistSimple likedTracksPlaylist = PlaylistSimple(); + likedTracksPlaylist.name = "Liked Tracks"; + likedTracksPlaylist.type = "playlist"; + likedTracksPlaylist.collaborative = false; + likedTracksPlaylist.public = false; + likedTracksPlaylist.id = "user-liked-tracks"; + image.url = + "https://t.scdn.co/images/3099b3803ad9496896c43f22fe9be8c4.png"; + likedTracksPlaylist.images = [image]; + return Expanded( + child: Scrollbar( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Wrap( + spacing: 8.0, // gap between adjacent chips + runSpacing: 8.0, // gap between lines + alignment: WrapAlignment.center, + children: [ + PlaylistCard(likedTracksPlaylist), + ...snapshot.data! + .map((playlist) => PlaylistCard(playlist)) + .toList(), + ], + ), + ), + ), + ), + ); + }, + ); + } +}