activate log level in release with env var

Check if the track already exists in the disk while download button is pressed
This commit is contained in:
Kingkor Roy Tirtho 2022-04-04 19:55:17 +06:00
parent 6111e8c291
commit 0036df6e12
22 changed files with 121 additions and 57 deletions

View File

@ -24,7 +24,7 @@ class _ArtistAlbumViewState extends ConsumerState<ArtistAlbumView> {
final PagingController<int, Album> _pagingController = final PagingController<int, Album> _pagingController =
PagingController<int, Album>(firstPageKey: 0); PagingController<int, Album>(firstPageKey: 0);
final logger = createLogger(ArtistAlbumView); final logger = getLogger(ArtistAlbumView);
@override @override
void initState() { void initState() {

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:marquee/marquee.dart'; import 'package:marquee/marquee.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/components/Shared/SpotubeWidgets.dart'; import 'package:spotube/components/Shared/SpotubeMarqueeText.dart';
class ArtistCard extends StatelessWidget { class ArtistCard extends StatelessWidget {
final Artist artist; final Artist artist;

View File

@ -21,7 +21,7 @@ import 'package:spotube/provider/SpotifyDI.dart';
class ArtistProfile extends HookConsumerWidget { class ArtistProfile extends HookConsumerWidget {
final String artistId; final String artistId;
final logger = createLogger(ArtistProfile); final logger = getLogger(ArtistProfile);
ArtistProfile(this.artistId, {Key? key}) : super(key: key); ArtistProfile(this.artistId, {Key? key}) : super(key: key);
@override @override

View File

@ -17,7 +17,7 @@ class CategoryCard extends HookWidget {
this.playlists, this.playlists,
}) : super(key: key); }) : super(key: key);
final logger = createLogger(CategoryCard); final logger = getLogger(CategoryCard);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -42,7 +42,7 @@ List<String> spotifyScopes = [
class Home extends HookConsumerWidget { class Home extends HookConsumerWidget {
Home({Key? key}) : super(key: key); Home({Key? key}) : super(key: key);
final logger = createLogger(Home); final logger = getLogger(Home);
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {

View File

@ -16,7 +16,7 @@ class UserArtists extends ConsumerStatefulWidget {
class _UserArtistsState extends ConsumerState<UserArtists> { class _UserArtistsState extends ConsumerState<UserArtists> {
final PagingController<String, Artist> _pagingController = final PagingController<String, Artist> _pagingController =
PagingController(firstPageKey: ""); PagingController(firstPageKey: "");
final logger = createLogger(UserArtists); final logger = getLogger(UserArtists);
@override @override
void initState() { void initState() {

View File

@ -14,7 +14,7 @@ import 'package:spotube/provider/UserPreferences.dart';
class Login extends HookConsumerWidget { class Login extends HookConsumerWidget {
Login({Key? key}) : super(key: key); Login({Key? key}) : super(key: key);
final log = createLogger(Login); final log = getLogger(Login);
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {

View File

@ -19,7 +19,7 @@ import 'package:flutter/material.dart';
class Player extends HookConsumerWidget { class Player extends HookConsumerWidget {
Player({Key? key}) : super(key: key); Player({Key? key}) : super(key: key);
final logger = createLogger(Player); final logger = getLogger(Player);
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
Playback playback = ref.watch(playbackProvider); Playback playback = ref.watch(playbackProvider);

View File

@ -15,7 +15,7 @@ class PlayerActions extends HookConsumerWidget {
this.mainAxisAlignment = MainAxisAlignment.center, this.mainAxisAlignment = MainAxisAlignment.center,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
final logger = createLogger(PlayerActions); final logger = getLogger(PlayerActions);
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {

View File

@ -14,7 +14,7 @@ class PlayerControls extends HookConsumerWidget {
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
final logger = createLogger(PlayerControls); final logger = getLogger(PlayerControls);
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
@ -47,7 +47,9 @@ class PlayerControls extends HookConsumerWidget {
child: Column( child: Column(
children: [ children: [
StreamBuilder<Duration>( StreamBuilder<Duration>(
stream: player.positionStream, stream: player.positionStream.isBroadcast
? player.positionStream
: player.positionStream.asBroadcastStream(),
builder: (context, snapshot) { builder: (context, snapshot) {
final totalMinutes = final totalMinutes =
zeroPadNumStr(duration.inMinutes.remainder(60)); zeroPadNumStr(duration.inMinutes.remainder(60));

View File

@ -12,7 +12,7 @@ import 'package:spotify/spotify.dart';
import 'package:spotube/provider/SpotifyDI.dart'; import 'package:spotube/provider/SpotifyDI.dart';
class PlaylistView extends HookConsumerWidget { class PlaylistView extends HookConsumerWidget {
final logger = createLogger(PlaylistView); final logger = getLogger(PlaylistView);
final PlaylistSimple playlist; final PlaylistSimple playlist;
PlaylistView(this.playlist, {Key? key}) : super(key: key); PlaylistView(this.playlist, {Key? key}) : super(key: key);

View File

@ -17,10 +17,10 @@ class DownloadTrackButton extends HookWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var status = useState<TrackStatus>(TrackStatus.idle); final status = useState<TrackStatus>(TrackStatus.idle);
YoutubeExplode yt = useMemoized(() => YoutubeExplode()); YoutubeExplode yt = useMemoized(() => YoutubeExplode());
var _downloadTrack = useCallback(() async { final _downloadTrack = useCallback(() async {
if (track == null) return; if (track == null) return;
if ((Platform.isAndroid || Platform.isIOS) && if ((Platform.isAndroid || Platform.isIOS) &&
!await Permission.storage.isGranted && !await Permission.storage.isGranted &&
@ -38,6 +38,43 @@ class DownloadTrackButton extends HookWidget {
StreamManifest manifest = StreamManifest manifest =
await yt.videos.streamsClient.getManifest(track?.href); 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<Artist>(track?.artists ?? [])}.mp3";
File outputFile = File(path.join(downloadFolder, fileName));
if (await outputFile.exists()) {
final shouldReplace = await showDialog<bool>(
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 final audioStream = yt.videos.streamsClient
.get( .get(
manifest.audioOnly manifest.audioOnly
@ -65,16 +102,7 @@ class DownloadTrackButton extends HookWidget {
}, },
); );
String downloadFolder = path.join( if (!outputFile.existsSync()) outputFile.createSync(recursive: true);
Platform.isAndroid
? "/storage/emulated/0/Download"
: (await path_provider.getDownloadsDirectory())!.path,
"Spotube");
String fileName =
"${track?.name} - ${artistsToString<Artist>(track?.artists ?? [])}.mp3";
File outputFile = File(path.join(downloadFolder, fileName));
if (!outputFile.existsSync()) {
outputFile.createSync(recursive: true);
IOSink outputFileStream = outputFile.openWrite(); IOSink outputFileStream = outputFile.openWrite();
await audioStream.pipe(outputFileStream); await audioStream.pipe(outputFileStream);
await outputFileStream.flush(); await outputFileStream.flush();
@ -92,7 +120,6 @@ class DownloadTrackButton extends HookWidget {
} }
return statusCb.cancel(); return statusCb.cancel();
}); });
}
}, [track, status, yt]); }, [track, status, yt]);
useEffect(() { useEffect(() {

View File

@ -1,6 +1,6 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:spotube/components/Shared/SpotubeWidgets.dart'; import 'package:spotube/components/Shared/SpotubeMarqueeText.dart';
class PlaybuttonCard extends StatelessWidget { class PlaybuttonCard extends StatelessWidget {
final void Function()? onTap; final void Function()? onTap;

View File

@ -6,7 +6,7 @@ import 'package:spotube/helpers/get-random-element.dart';
import 'package:spotube/models/Logger.dart'; import 'package:spotube/models/Logger.dart';
import 'package:spotube/models/generated_secrets.dart'; import 'package:spotube/models/generated_secrets.dart';
final logger = createLogger("GetLyrics"); final logger = getLogger("GetLyrics");
String getTitle(String title, String artist) { String getTitle(String title, String artist) {
return "$title $artist" return "$title $artist"

View File

@ -7,7 +7,7 @@ import 'package:spotube/models/Logger.dart';
import 'package:spotube/provider/Auth.dart'; import 'package:spotube/provider/Auth.dart';
const redirectUri = "http://localhost:4304/auth/spotify/callback"; const redirectUri = "http://localhost:4304/auth/spotify/callback";
final logger = createLogger("OAuthLogin"); final logger = getLogger("OAuthLogin");
Future<void> oauthLogin(Auth auth, Future<void> oauthLogin(Auth auth,
{required String clientId, required String clientSecret}) async { {required String clientId, required String clientSecret}) async {

View File

@ -3,7 +3,7 @@ import 'dart:io';
import 'package:spotube/models/Logger.dart'; import 'package:spotube/models/Logger.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
final logger = createLogger("ServerIPC"); final logger = getLogger("ServerIPC");
Future<String?> connectIpc(String authUri, String redirectUri) async { Future<String?> connectIpc(String authUri, String redirectUri) async {
try { try {

View File

@ -1,7 +1,7 @@
import 'package:spotube/models/Logger.dart'; import 'package:spotube/models/Logger.dart';
import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/Playback.dart';
final logger = createLogger("PlaybackHook"); final logger = getLogger("PlaybackHook");
Future<void> Function() useNextTrack(Playback playback) { Future<void> Function() useNextTrack(Playback playback) {
return () async { return () async {

View File

@ -39,7 +39,7 @@ void main() async {
class MyApp extends HookConsumerWidget { class MyApp extends HookConsumerWidget {
final GoRouter _router = createGoRouter(); final GoRouter _router = createGoRouter();
final logger = createLogger(MyApp); final logger = getLogger(MyApp);
MyApp({Key? key}) : super(key: key); MyApp({Key? key}) : super(key: key);
@override @override

View File

@ -4,12 +4,16 @@ import 'package:logger/logger.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
_SpotubeLogger createLogger<T>(T owner) => final _loggerFactory = _SpotubeLogger();
_SpotubeLogger(owner is String ? owner : owner.toString());
_SpotubeLogger getLogger<T>(T owner) {
_loggerFactory.owner = owner is String ? owner : owner.toString();
return _loggerFactory;
}
class _SpotubeLogger extends Logger { class _SpotubeLogger extends Logger {
String owner; String? owner;
_SpotubeLogger(this.owner); _SpotubeLogger([this.owner]) : super(filter: _SpotubeLogFilter());
@override @override
void log(Level level, message, [error, StackTrace? stackTrace]) { void log(Level level, message, [error, StackTrace? stackTrace]) {
@ -23,3 +27,16 @@ class _SpotubeLogger extends Logger {
super.log(level, "[$owner] $message", error, stackTrace); 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);
}
}

View File

@ -48,7 +48,8 @@ class CurrentPlaylist {
} }
class Playback extends ChangeNotifier { class Playback extends ChangeNotifier {
final _logger = createLogger(Playback); AudioSource? _currentAudioSource;
final _logger = getLogger(Playback);
CurrentPlaylist? _currentPlaylist; CurrentPlaylist? _currentPlaylist;
Track? _currentTrack; Track? _currentTrack;
@ -67,6 +68,7 @@ class Playback extends ChangeNotifier {
StreamSubscription<Duration?>? _durationStreamListener; StreamSubscription<Duration?>? _durationStreamListener;
StreamSubscription<ProcessingState>? _processingStateStreamListener; StreamSubscription<ProcessingState>? _processingStateStreamListener;
StreamSubscription<AudioInterruptionEvent>? _audioInterruptionEventListener; StreamSubscription<AudioInterruptionEvent>? _audioInterruptionEventListener;
StreamSubscription<Duration>? _positionStreamListener;
AudioPlayer player; AudioPlayer player;
YoutubeExplode youtube; YoutubeExplode youtube;
@ -97,15 +99,17 @@ class Playback extends ChangeNotifier {
if (player.playing) await player.pause(); if (player.playing) await player.pause();
await player.play(); await player.play();
} }
_duration = duration; _duration = duration;
_callAllDurationListeners(duration);
// for avoiding unnecessary re-renders in other components that // for avoiding unnecessary re-renders in other components that
// doesn't need duration // doesn't need duration
_callAllDurationListeners(duration);
} }
}); });
_processingStateStreamListener = _processingStateStreamListener =
player.processingStateStream.listen((event) async { player.processingStateStream.listen((event) async {
_logger.v("[Processing State Change] $event");
try { try {
if (event != ProcessingState.completed) return; if (event != ProcessingState.completed) return;
if (_currentTrack?.id != null) { 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.instance.then((session) async {
_audioSession = session; _audioSession = session;
await session.configure(const AudioSessionConfiguration.music()); await session.configure(const AudioSessionConfiguration.music());
@ -159,16 +168,19 @@ class Playback extends ChangeNotifier {
} }
set setCurrentTrack(Track track) { set setCurrentTrack(Track track) {
_logger.v("[Setting Current Track] ${track.name} - ${track.id}");
_currentTrack = track; _currentTrack = track;
notifyListeners(); notifyListeners();
} }
set setCurrentPlaylist(CurrentPlaylist playlist) { set setCurrentPlaylist(CurrentPlaylist playlist) {
_logger.v("[Current Playlist Changed] ${playlist.name} - ${playlist.id}");
_currentPlaylist = playlist; _currentPlaylist = playlist;
notifyListeners(); notifyListeners();
} }
void reset() { void reset() {
_logger.v("Playback Reset");
_isPlaying = false; _isPlaying = false;
_duration = null; _duration = null;
_callAllDurationListeners(null); _callAllDurationListeners(null);
@ -200,11 +212,13 @@ class Playback extends ChangeNotifier {
_durationStreamListener?.cancel(); _durationStreamListener?.cancel();
_playingStreamListener?.cancel(); _playingStreamListener?.cancel();
_audioInterruptionEventListener?.cancel(); _audioInterruptionEventListener?.cancel();
_positionStreamListener?.cancel();
_audioSession?.setActive(false); _audioSession?.setActive(false);
super.dispose(); super.dispose();
} }
void movePlaylistPositionBy(int pos) { void movePlaylistPositionBy(int pos) {
_logger.v("[Playlist Position Move] $pos");
if (_currentTrack != null && _currentPlaylist != null) { if (_currentTrack != null && _currentPlaylist != null) {
int index = _currentPlaylist!.trackIds.indexOf(_currentTrack!.id!) + pos; int index = _currentPlaylist!.trackIds.indexOf(_currentTrack!.id!) + pos;
@ -228,6 +242,7 @@ class Playback extends ChangeNotifier {
} }
Future<void> startPlaying([Track? track]) async { Future<void> startPlaying([Track? track]) async {
_logger.v("[Track Playing] ${track?.name} - ${track?.id}");
try { try {
// the track is already playing so no need to change that // the track is already playing so no need to change that
if (track != null && track.id == _currentTrack?.id) return; if (track != null && track.id == _currentTrack?.id) return;
@ -242,9 +257,10 @@ class Playback extends ChangeNotifier {
artUri: Uri.parse(imageToUrlString(track.album?.images)), artUri: Uri.parse(imageToUrlString(track.album?.images)),
); );
if (parsedUri != null && parsedUri.hasAbsolutePath) { if (parsedUri != null && parsedUri.hasAbsolutePath) {
_currentAudioSource = AudioSource.uri(parsedUri, tag: tag);
await player await player
.setAudioSource( .setAudioSource(
AudioSource.uri(parsedUri, tag: tag), _currentAudioSource!,
preload: true, preload: true,
) )
.then((value) async { .then((value) async {
@ -256,9 +272,11 @@ class Playback extends ChangeNotifier {
} }
final ytTrack = await toYoutubeTrack(youtube, track); final ytTrack = await toYoutubeTrack(youtube, track);
if (setTrackUriById(track.id!, ytTrack.uri!)) { if (setTrackUriById(track.id!, ytTrack.uri!)) {
_currentAudioSource =
AudioSource.uri(Uri.parse(ytTrack.uri!), tag: tag);
await player await player
.setAudioSource( .setAudioSource(
AudioSource.uri(Uri.parse(ytTrack.uri!), tag: tag), _currentAudioSource!,
preload: true, preload: true,
) )
.then((value) { .then((value) {

View File

@ -23,7 +23,7 @@ class UserPreferences extends ChangeNotifier {
onInit(); onInit();
} }
final logger = createLogger(UserPreferences); final logger = getLogger(UserPreferences);
Future<HotKey?> _getHotKeyFromLocalStorage( Future<HotKey?> _getHotKeyFromLocalStorage(
SharedPreferences preferences, String key) async { SharedPreferences preferences, String key) async {