From 0036df6e122100f97bb14cdeca311ff1f0a3e0f8 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Mon, 4 Apr 2022 19:55:17 +0600 Subject: [PATCH] activate log level in release with env var Check if the track already exists in the disk while download button is pressed --- lib/components/Artist/ArtistAlbumView.dart | 2 +- lib/components/Artist/ArtistCard.dart | 2 +- lib/components/Artist/ArtistProfile.dart | 2 +- lib/components/Category/CategoryCard.dart | 2 +- lib/components/Home/Home.dart | 2 +- lib/components/Library/UserArtists.dart | 2 +- lib/components/Login.dart | 2 +- lib/components/Player/Player.dart | 2 +- lib/components/Player/PlayerActions.dart | 2 +- lib/components/Player/PlayerControls.dart | 6 +- lib/components/Playlist/PlaylistView.dart | 2 +- .../Shared/DownloadTrackButton.dart | 87 ++++++++++++------- lib/components/Shared/PlaybuttonCard.dart | 2 +- ...beWidgets.dart => SpotubeMarqueeText.dart} | 0 lib/helpers/getLyrics.dart | 2 +- lib/helpers/oauth-login.dart | 2 +- lib/helpers/server_ipc.dart | 2 +- lib/hooks/playback.dart | 2 +- lib/main.dart | 2 +- lib/models/Logger.dart | 25 +++++- lib/provider/Playback.dart | 26 +++++- lib/provider/UserPreferences.dart | 2 +- 22 files changed, 121 insertions(+), 57 deletions(-) rename lib/components/Shared/{SpotubeWidgets.dart => SpotubeMarqueeText.dart} (100%) diff --git a/lib/components/Artist/ArtistAlbumView.dart b/lib/components/Artist/ArtistAlbumView.dart index b65c67eb..488575bb 100644 --- a/lib/components/Artist/ArtistAlbumView.dart +++ b/lib/components/Artist/ArtistAlbumView.dart @@ -24,7 +24,7 @@ class _ArtistAlbumViewState extends ConsumerState { final PagingController _pagingController = PagingController(firstPageKey: 0); - final logger = createLogger(ArtistAlbumView); + final logger = getLogger(ArtistAlbumView); @override void initState() { diff --git a/lib/components/Artist/ArtistCard.dart b/lib/components/Artist/ArtistCard.dart index c36e899f..7f43d3cb 100644 --- a/lib/components/Artist/ArtistCard.dart +++ b/lib/components/Artist/ArtistCard.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:marquee/marquee.dart'; import 'package:spotify/spotify.dart'; -import 'package:spotube/components/Shared/SpotubeWidgets.dart'; +import 'package:spotube/components/Shared/SpotubeMarqueeText.dart'; class ArtistCard extends StatelessWidget { final Artist artist; diff --git a/lib/components/Artist/ArtistProfile.dart b/lib/components/Artist/ArtistProfile.dart index 61681c14..f799cec5 100644 --- a/lib/components/Artist/ArtistProfile.dart +++ b/lib/components/Artist/ArtistProfile.dart @@ -21,7 +21,7 @@ import 'package:spotube/provider/SpotifyDI.dart'; class ArtistProfile extends HookConsumerWidget { final String artistId; - final logger = createLogger(ArtistProfile); + final logger = getLogger(ArtistProfile); ArtistProfile(this.artistId, {Key? key}) : super(key: key); @override diff --git a/lib/components/Category/CategoryCard.dart b/lib/components/Category/CategoryCard.dart index 19571664..f2963041 100644 --- a/lib/components/Category/CategoryCard.dart +++ b/lib/components/Category/CategoryCard.dart @@ -17,7 +17,7 @@ class CategoryCard extends HookWidget { this.playlists, }) : super(key: key); - final logger = createLogger(CategoryCard); + final logger = getLogger(CategoryCard); @override Widget build(BuildContext context) { diff --git a/lib/components/Home/Home.dart b/lib/components/Home/Home.dart index d3bc8c80..734fe4b0 100644 --- a/lib/components/Home/Home.dart +++ b/lib/components/Home/Home.dart @@ -42,7 +42,7 @@ List spotifyScopes = [ class Home extends HookConsumerWidget { Home({Key? key}) : super(key: key); - final logger = createLogger(Home); + final logger = getLogger(Home); @override Widget build(BuildContext context, ref) { diff --git a/lib/components/Library/UserArtists.dart b/lib/components/Library/UserArtists.dart index dc6a55c7..46524930 100644 --- a/lib/components/Library/UserArtists.dart +++ b/lib/components/Library/UserArtists.dart @@ -16,7 +16,7 @@ class UserArtists extends ConsumerStatefulWidget { class _UserArtistsState extends ConsumerState { final PagingController _pagingController = PagingController(firstPageKey: ""); - final logger = createLogger(UserArtists); + final logger = getLogger(UserArtists); @override void initState() { diff --git a/lib/components/Login.dart b/lib/components/Login.dart index 918c2e5a..a5b1fa88 100644 --- a/lib/components/Login.dart +++ b/lib/components/Login.dart @@ -14,7 +14,7 @@ import 'package:spotube/provider/UserPreferences.dart'; class Login extends HookConsumerWidget { Login({Key? key}) : super(key: key); - final log = createLogger(Login); + final log = getLogger(Login); @override Widget build(BuildContext context, ref) { diff --git a/lib/components/Player/Player.dart b/lib/components/Player/Player.dart index 53f4bee7..957a1dab 100644 --- a/lib/components/Player/Player.dart +++ b/lib/components/Player/Player.dart @@ -19,7 +19,7 @@ import 'package:flutter/material.dart'; class Player extends HookConsumerWidget { Player({Key? key}) : super(key: key); - final logger = createLogger(Player); + final logger = getLogger(Player); @override Widget build(BuildContext context, ref) { Playback playback = ref.watch(playbackProvider); diff --git a/lib/components/Player/PlayerActions.dart b/lib/components/Player/PlayerActions.dart index 28d64319..5e793f1f 100644 --- a/lib/components/Player/PlayerActions.dart +++ b/lib/components/Player/PlayerActions.dart @@ -15,7 +15,7 @@ class PlayerActions extends HookConsumerWidget { this.mainAxisAlignment = MainAxisAlignment.center, Key? key, }) : super(key: key); - final logger = createLogger(PlayerActions); + final logger = getLogger(PlayerActions); @override Widget build(BuildContext context, ref) { diff --git a/lib/components/Player/PlayerControls.dart b/lib/components/Player/PlayerControls.dart index 8b1c91b3..cb9a8d2f 100644 --- a/lib/components/Player/PlayerControls.dart +++ b/lib/components/Player/PlayerControls.dart @@ -14,7 +14,7 @@ class PlayerControls extends HookConsumerWidget { Key? key, }) : super(key: key); - final logger = createLogger(PlayerControls); + final logger = getLogger(PlayerControls); @override Widget build(BuildContext context, ref) { @@ -47,7 +47,9 @@ class PlayerControls extends HookConsumerWidget { child: Column( children: [ StreamBuilder( - stream: player.positionStream, + stream: player.positionStream.isBroadcast + ? player.positionStream + : player.positionStream.asBroadcastStream(), builder: (context, snapshot) { final totalMinutes = zeroPadNumStr(duration.inMinutes.remainder(60)); diff --git a/lib/components/Playlist/PlaylistView.dart b/lib/components/Playlist/PlaylistView.dart index bb08e3c1..9f7ab012 100644 --- a/lib/components/Playlist/PlaylistView.dart +++ b/lib/components/Playlist/PlaylistView.dart @@ -12,7 +12,7 @@ import 'package:spotify/spotify.dart'; import 'package:spotube/provider/SpotifyDI.dart'; class PlaylistView extends HookConsumerWidget { - final logger = createLogger(PlaylistView); + final logger = getLogger(PlaylistView); final PlaylistSimple playlist; PlaylistView(this.playlist, {Key? key}) : super(key: key); diff --git a/lib/components/Shared/DownloadTrackButton.dart b/lib/components/Shared/DownloadTrackButton.dart index 4fa7c3e1..baaf45a5 100644 --- a/lib/components/Shared/DownloadTrackButton.dart +++ b/lib/components/Shared/DownloadTrackButton.dart @@ -17,10 +17,10 @@ class DownloadTrackButton extends HookWidget { @override Widget build(BuildContext context) { - var status = useState(TrackStatus.idle); + final status = useState(TrackStatus.idle); YoutubeExplode yt = useMemoized(() => YoutubeExplode()); - var _downloadTrack = useCallback(() async { + final _downloadTrack = useCallback(() async { if (track == null) return; if ((Platform.isAndroid || Platform.isIOS) && !await Permission.storage.isGranted && @@ -38,6 +38,43 @@ class DownloadTrackButton extends HookWidget { StreamManifest manifest = await yt.videos.streamsClient.getManifest(track?.href); + String downloadFolder = path.join( + Platform.isAndroid + ? "/storage/emulated/0/Download" + : (await path_provider.getDownloadsDirectory())!.path, + "Spotube"); + String fileName = + "${track?.name} - ${artistsToString(track?.artists ?? [])}.mp3"; + File outputFile = File(path.join(downloadFolder, fileName)); + + if (await outputFile.exists()) { + final shouldReplace = await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text("Track Already Exists"), + content: const Text( + "Do you want to replace the already downloaded track?"), + actions: [ + TextButton( + child: const Text("No"), + onPressed: () { + Navigator.pop(context, false); + }, + ), + TextButton( + child: const Text("Yes"), + onPressed: () { + Navigator.pop(context, true); + }, + ) + ], + ); + }, + ); + if (shouldReplace != true) return; + } + final audioStream = yt.videos.streamsClient .get( manifest.audioOnly @@ -65,34 +102,24 @@ class DownloadTrackButton extends HookWidget { }, ); - String downloadFolder = path.join( - Platform.isAndroid - ? "/storage/emulated/0/Download" - : (await path_provider.getDownloadsDirectory())!.path, - "Spotube"); - String fileName = - "${track?.name} - ${artistsToString(track?.artists ?? [])}.mp3"; - File outputFile = File(path.join(downloadFolder, fileName)); - if (!outputFile.existsSync()) { - outputFile.createSync(recursive: true); - IOSink outputFileStream = outputFile.openWrite(); - await audioStream.pipe(outputFileStream); - await outputFileStream.flush(); - await outputFileStream.close().then((value) async { - if (status.value == TrackStatus.downloading) { - status.value = TrackStatus.done; - await Future.delayed( - const Duration(seconds: 3), - () { - if (status.value == TrackStatus.done) { - status.value = TrackStatus.idle; - } - }, - ); - } - return statusCb.cancel(); - }); - } + if (!outputFile.existsSync()) outputFile.createSync(recursive: true); + IOSink outputFileStream = outputFile.openWrite(); + await audioStream.pipe(outputFileStream); + await outputFileStream.flush(); + await outputFileStream.close().then((value) async { + if (status.value == TrackStatus.downloading) { + status.value = TrackStatus.done; + await Future.delayed( + const Duration(seconds: 3), + () { + if (status.value == TrackStatus.done) { + status.value = TrackStatus.idle; + } + }, + ); + } + return statusCb.cancel(); + }); }, [track, status, yt]); useEffect(() { diff --git a/lib/components/Shared/PlaybuttonCard.dart b/lib/components/Shared/PlaybuttonCard.dart index 169dd778..120f8e02 100644 --- a/lib/components/Shared/PlaybuttonCard.dart +++ b/lib/components/Shared/PlaybuttonCard.dart @@ -1,6 +1,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; -import 'package:spotube/components/Shared/SpotubeWidgets.dart'; +import 'package:spotube/components/Shared/SpotubeMarqueeText.dart'; class PlaybuttonCard extends StatelessWidget { final void Function()? onTap; diff --git a/lib/components/Shared/SpotubeWidgets.dart b/lib/components/Shared/SpotubeMarqueeText.dart similarity index 100% rename from lib/components/Shared/SpotubeWidgets.dart rename to lib/components/Shared/SpotubeMarqueeText.dart diff --git a/lib/helpers/getLyrics.dart b/lib/helpers/getLyrics.dart index d757ad34..197cc862 100644 --- a/lib/helpers/getLyrics.dart +++ b/lib/helpers/getLyrics.dart @@ -6,7 +6,7 @@ import 'package:spotube/helpers/get-random-element.dart'; import 'package:spotube/models/Logger.dart'; import 'package:spotube/models/generated_secrets.dart'; -final logger = createLogger("GetLyrics"); +final logger = getLogger("GetLyrics"); String getTitle(String title, String artist) { return "$title $artist" diff --git a/lib/helpers/oauth-login.dart b/lib/helpers/oauth-login.dart index ea78ab89..c89197d8 100644 --- a/lib/helpers/oauth-login.dart +++ b/lib/helpers/oauth-login.dart @@ -7,7 +7,7 @@ import 'package:spotube/models/Logger.dart'; import 'package:spotube/provider/Auth.dart'; const redirectUri = "http://localhost:4304/auth/spotify/callback"; -final logger = createLogger("OAuthLogin"); +final logger = getLogger("OAuthLogin"); Future oauthLogin(Auth auth, {required String clientId, required String clientSecret}) async { diff --git a/lib/helpers/server_ipc.dart b/lib/helpers/server_ipc.dart index f455c28e..f44d1dc5 100644 --- a/lib/helpers/server_ipc.dart +++ b/lib/helpers/server_ipc.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:spotube/models/Logger.dart'; import 'package:url_launcher/url_launcher.dart'; -final logger = createLogger("ServerIPC"); +final logger = getLogger("ServerIPC"); Future connectIpc(String authUri, String redirectUri) async { try { diff --git a/lib/hooks/playback.dart b/lib/hooks/playback.dart index a37bb245..2dc4f035 100644 --- a/lib/hooks/playback.dart +++ b/lib/hooks/playback.dart @@ -1,7 +1,7 @@ import 'package:spotube/models/Logger.dart'; import 'package:spotube/provider/Playback.dart'; -final logger = createLogger("PlaybackHook"); +final logger = getLogger("PlaybackHook"); Future Function() useNextTrack(Playback playback) { return () async { diff --git a/lib/main.dart b/lib/main.dart index 35aa3bdc..87293f8c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -39,7 +39,7 @@ void main() async { class MyApp extends HookConsumerWidget { final GoRouter _router = createGoRouter(); - final logger = createLogger(MyApp); + final logger = getLogger(MyApp); MyApp({Key? key}) : super(key: key); @override diff --git a/lib/models/Logger.dart b/lib/models/Logger.dart index cc9a5fab..d9299468 100644 --- a/lib/models/Logger.dart +++ b/lib/models/Logger.dart @@ -4,12 +4,16 @@ import 'package:logger/logger.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart' as path; -_SpotubeLogger createLogger(T owner) => - _SpotubeLogger(owner is String ? owner : owner.toString()); +final _loggerFactory = _SpotubeLogger(); + +_SpotubeLogger getLogger(T owner) { + _loggerFactory.owner = owner is String ? owner : owner.toString(); + return _loggerFactory; +} class _SpotubeLogger extends Logger { - String owner; - _SpotubeLogger(this.owner); + String? owner; + _SpotubeLogger([this.owner]) : super(filter: _SpotubeLogFilter()); @override void log(Level level, message, [error, StackTrace? stackTrace]) { @@ -23,3 +27,16 @@ class _SpotubeLogger extends Logger { super.log(level, "[$owner] $message", error, stackTrace); } } + +class _SpotubeLogFilter extends DevelopmentFilter { + @override + bool shouldLog(LogEvent event) { + final env = Platform.environment; + if ((env["DEBUG"] == "true" && event.level == Level.debug) || + (env["VERBOSE"] == "true" && event.level == Level.verbose) || + (env["ERROR"] == "true" && event.level == Level.error)) { + return true; + } + return super.shouldLog(event); + } +} diff --git a/lib/provider/Playback.dart b/lib/provider/Playback.dart index d7e0e874..cfcbe0fd 100644 --- a/lib/provider/Playback.dart +++ b/lib/provider/Playback.dart @@ -48,7 +48,8 @@ class CurrentPlaylist { } class Playback extends ChangeNotifier { - final _logger = createLogger(Playback); + AudioSource? _currentAudioSource; + final _logger = getLogger(Playback); CurrentPlaylist? _currentPlaylist; Track? _currentTrack; @@ -67,6 +68,7 @@ class Playback extends ChangeNotifier { StreamSubscription? _durationStreamListener; StreamSubscription? _processingStateStreamListener; StreamSubscription? _audioInterruptionEventListener; + StreamSubscription? _positionStreamListener; AudioPlayer player; YoutubeExplode youtube; @@ -97,15 +99,17 @@ class Playback extends ChangeNotifier { if (player.playing) await player.pause(); await player.play(); } + _duration = duration; - _callAllDurationListeners(duration); // for avoiding unnecessary re-renders in other components that // doesn't need duration + _callAllDurationListeners(duration); } }); _processingStateStreamListener = player.processingStateStream.listen((event) async { + _logger.v("[Processing State Change] $event"); try { if (event != ProcessingState.completed) return; if (_currentTrack?.id != null) { @@ -122,6 +126,11 @@ class Playback extends ChangeNotifier { } }); + _positionStreamListener = (player.positionStream.isBroadcast + ? player.positionStream + : player.positionStream.asBroadcastStream()) + .listen((position) async {}); + AudioSession.instance.then((session) async { _audioSession = session; await session.configure(const AudioSessionConfiguration.music()); @@ -159,16 +168,19 @@ class Playback extends ChangeNotifier { } set setCurrentTrack(Track track) { + _logger.v("[Setting Current Track] ${track.name} - ${track.id}"); _currentTrack = track; notifyListeners(); } set setCurrentPlaylist(CurrentPlaylist playlist) { + _logger.v("[Current Playlist Changed] ${playlist.name} - ${playlist.id}"); _currentPlaylist = playlist; notifyListeners(); } void reset() { + _logger.v("Playback Reset"); _isPlaying = false; _duration = null; _callAllDurationListeners(null); @@ -200,11 +212,13 @@ class Playback extends ChangeNotifier { _durationStreamListener?.cancel(); _playingStreamListener?.cancel(); _audioInterruptionEventListener?.cancel(); + _positionStreamListener?.cancel(); _audioSession?.setActive(false); super.dispose(); } void movePlaylistPositionBy(int pos) { + _logger.v("[Playlist Position Move] $pos"); if (_currentTrack != null && _currentPlaylist != null) { int index = _currentPlaylist!.trackIds.indexOf(_currentTrack!.id!) + pos; @@ -228,6 +242,7 @@ class Playback extends ChangeNotifier { } Future startPlaying([Track? track]) async { + _logger.v("[Track Playing] ${track?.name} - ${track?.id}"); try { // the track is already playing so no need to change that if (track != null && track.id == _currentTrack?.id) return; @@ -242,9 +257,10 @@ class Playback extends ChangeNotifier { artUri: Uri.parse(imageToUrlString(track.album?.images)), ); if (parsedUri != null && parsedUri.hasAbsolutePath) { + _currentAudioSource = AudioSource.uri(parsedUri, tag: tag); await player .setAudioSource( - AudioSource.uri(parsedUri, tag: tag), + _currentAudioSource!, preload: true, ) .then((value) async { @@ -256,9 +272,11 @@ class Playback extends ChangeNotifier { } final ytTrack = await toYoutubeTrack(youtube, track); if (setTrackUriById(track.id!, ytTrack.uri!)) { + _currentAudioSource = + AudioSource.uri(Uri.parse(ytTrack.uri!), tag: tag); await player .setAudioSource( - AudioSource.uri(Uri.parse(ytTrack.uri!), tag: tag), + _currentAudioSource!, preload: true, ) .then((value) { diff --git a/lib/provider/UserPreferences.dart b/lib/provider/UserPreferences.dart index 09185a06..49ba2c8e 100644 --- a/lib/provider/UserPreferences.dart +++ b/lib/provider/UserPreferences.dart @@ -23,7 +23,7 @@ class UserPreferences extends ChangeNotifier { onInit(); } - final logger = createLogger(UserPreferences); + final logger = getLogger(UserPreferences); Future _getHotKeyFromLocalStorage( SharedPreferences preferences, String key) async {