✨ Error notifier
This commit is contained in:
parent
ddeda2ce23
commit
ee2633db52
@ -8,6 +8,7 @@ import 'package:rhythm_box/providers/audio_player_stream.dart';
|
|||||||
import 'package:rhythm_box/providers/auth.dart';
|
import 'package:rhythm_box/providers/auth.dart';
|
||||||
import 'package:rhythm_box/providers/database.dart';
|
import 'package:rhythm_box/providers/database.dart';
|
||||||
import 'package:rhythm_box/providers/endless_playback.dart';
|
import 'package:rhythm_box/providers/endless_playback.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/providers/history.dart';
|
import 'package:rhythm_box/providers/history.dart';
|
||||||
import 'package:rhythm_box/providers/palette.dart';
|
import 'package:rhythm_box/providers/palette.dart';
|
||||||
import 'package:rhythm_box/providers/recent_played.dart';
|
import 'package:rhythm_box/providers/recent_played.dart';
|
||||||
@ -84,8 +85,8 @@ class RhythmApp extends StatelessWidget {
|
|||||||
translations: AppTranslations(),
|
translations: AppTranslations(),
|
||||||
onInit: () => _initializeProviders(context),
|
onInit: () => _initializeProviders(context),
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return SystemShell(
|
return ScaffoldMessenger(
|
||||||
child: ScaffoldMessenger(
|
child: SystemShell(
|
||||||
child: child ?? const SizedBox(),
|
child: child ?? const SizedBox(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -97,6 +98,8 @@ class RhythmApp extends StatelessWidget {
|
|||||||
Get.lazyPut(() => SpotifyProvider());
|
Get.lazyPut(() => SpotifyProvider());
|
||||||
Get.lazyPut(() => SyncedLyricsProvider());
|
Get.lazyPut(() => SyncedLyricsProvider());
|
||||||
|
|
||||||
|
Get.put(ErrorNotifier());
|
||||||
|
|
||||||
Get.put(DatabaseProvider());
|
Get.put(DatabaseProvider());
|
||||||
Get.put(AuthenticationProvider());
|
Get.put(AuthenticationProvider());
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import 'dart:developer';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:palette_generator/palette_generator.dart';
|
import 'package:palette_generator/palette_generator.dart';
|
||||||
import 'package:rhythm_box/providers/audio_player.dart';
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/providers/history.dart';
|
import 'package:rhythm_box/providers/history.dart';
|
||||||
import 'package:rhythm_box/providers/palette.dart';
|
import 'package:rhythm_box/providers/palette.dart';
|
||||||
import 'package:rhythm_box/providers/scrobbler.dart';
|
import 'package:rhythm_box/providers/scrobbler.dart';
|
||||||
@ -126,7 +127,8 @@ class AudioPlayerStreamProvider extends GetxController {
|
|||||||
.addTrack(playback.state.value.activeTrack!);
|
.addTrack(playback.state.value.activeTrack!);
|
||||||
lastScrobbled = uid;
|
lastScrobbled = uid;
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
log('[Scrobbler] Error: $e; Trace:\n$stack');
|
Get.find<ErrorNotifier>()
|
||||||
|
.logError('[Scrobbler] Error: $e', trace: stack);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:rhythm_box/providers/audio_player.dart';
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
@ -9,6 +8,8 @@ import 'package:rhythm_box/providers/user_preferences.dart';
|
|||||||
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
|
|
||||||
|
import 'error_notifier.dart';
|
||||||
|
|
||||||
class EndlessPlaybackProvider extends GetxController {
|
class EndlessPlaybackProvider extends GetxController {
|
||||||
late final _auth = Get.find<AuthenticationProvider>();
|
late final _auth = Get.find<AuthenticationProvider>();
|
||||||
late final _playback = Get.find<AudioPlayerProvider>();
|
late final _playback = Get.find<AudioPlayerProvider>();
|
||||||
@ -88,7 +89,8 @@ class EndlessPlaybackProvider extends GetxController {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
log('[EndlessPlayback] Error: $e; Trace:\n$stack');
|
Get.find<ErrorNotifier>()
|
||||||
|
.logError('[EndlessPlayback] Error: $e', trace: stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
37
lib/providers/error_notifier.dart
Normal file
37
lib/providers/error_notifier.dart
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class ErrorNotifier extends GetxController {
|
||||||
|
Rx<MaterialBanner?> showing = Rx(null);
|
||||||
|
|
||||||
|
void logError(String msg, {StackTrace? trace}) {
|
||||||
|
log('$msg${trace != null ? '\nTrace:\ntrace' : ''}');
|
||||||
|
showError(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showError(String msg) {
|
||||||
|
showing.value = MaterialBanner(
|
||||||
|
leading: const Icon(Icons.error),
|
||||||
|
content: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Something went wrong...',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
Text(msg),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
showing.value = null;
|
||||||
|
},
|
||||||
|
child: const Text('Dismiss'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:get/get.dart' hide Value;
|
import 'package:get/get.dart' hide Value;
|
||||||
import 'package:rhythm_box/providers/database.dart';
|
import 'package:rhythm_box/providers/database.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/services/artist.dart';
|
import 'package:rhythm_box/services/artist.dart';
|
||||||
import 'package:rhythm_box/services/database/database.dart';
|
import 'package:rhythm_box/services/database/database.dart';
|
||||||
import 'package:scrobblenaut/scrobblenaut.dart';
|
import 'package:scrobblenaut/scrobblenaut.dart';
|
||||||
@ -44,7 +44,8 @@ class ScrobblerProvider extends GetxController {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
log('[Scrobble] Error: $e; Trace:\n$stack');
|
Get.find<ErrorNotifier>()
|
||||||
|
.logError('[Scrobbler] Error: $e', trace: stack);
|
||||||
scrobbler.value = null;
|
scrobbler.value = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -63,8 +64,9 @@ class ScrobblerProvider extends GetxController {
|
|||||||
timestamp: DateTime.now().toUtc(),
|
timestamp: DateTime.now().toUtc(),
|
||||||
trackNumber: track.trackNumber,
|
trackNumber: track.trackNumber,
|
||||||
);
|
);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stack) {
|
||||||
log('[Scrobble] Error: $e; Trace:\n$stackTrace');
|
Get.find<ErrorNotifier>()
|
||||||
|
.logError('[Scrobbler] Error: $e', trace: stack);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:rhythm_box/providers/database.dart';
|
import 'package:rhythm_box/providers/database.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/providers/user_preferences.dart';
|
import 'package:rhythm_box/providers/user_preferences.dart';
|
||||||
import 'package:rhythm_box/services/database/database.dart';
|
import 'package:rhythm_box/services/database/database.dart';
|
||||||
import 'package:rhythm_box/services/server/active_sourced_track.dart';
|
import 'package:rhythm_box/services/server/active_sourced_track.dart';
|
||||||
@ -72,7 +71,7 @@ Future<List<SkipSegmentTableData>> getAndCacheSkipSegments(String id) async {
|
|||||||
..where((s) => s.trackId.equals(id)))
|
..where((s) => s.trackId.equals(id)))
|
||||||
.get();
|
.get();
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
log('[SkipSegment] Error: $e; Trace:\n$stack');
|
Get.find<ErrorNotifier>().logError('[SkipSegment] Error: $e', trace: stack);
|
||||||
return List.castFrom<dynamic, SkipSegmentTableData>([]);
|
return List.castFrom<dynamic, SkipSegmentTableData>([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,11 +102,11 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
maxWidth: maxAlbumSize,
|
maxWidth: maxAlbumSize,
|
||||||
child: Hero(
|
child: Hero(
|
||||||
tag: const Key('current-active-track-album-art'),
|
tag: const Key('current-active-track-album-art'),
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: 1,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius:
|
borderRadius:
|
||||||
const BorderRadius.all(Radius.circular(16)),
|
const BorderRadius.all(Radius.circular(16)),
|
||||||
child: AspectRatio(
|
|
||||||
aspectRatio: 1,
|
|
||||||
child: _albumArt != null
|
child: _albumArt != null
|
||||||
? AutoCacheImage(
|
? AutoCacheImage(
|
||||||
_albumArt!,
|
_albumArt!,
|
||||||
@ -230,7 +230,8 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
stream: audioPlayer.shuffledStream,
|
stream: audioPlayer.shuffledStream,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
final shuffled = snapshot.data ?? false;
|
final shuffled = snapshot.data ?? false;
|
||||||
return IconButton(
|
return Obx(
|
||||||
|
() => IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
shuffled
|
shuffled
|
||||||
? Icons.shuffle_on_outlined
|
? Icons.shuffle_on_outlined
|
||||||
@ -245,6 +246,7 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
|||||||
audioPlayer.setShuffle(true);
|
audioPlayer.setShuffle(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import 'dart:developer';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:media_kit/media_kit.dart' hide Track;
|
import 'package:media_kit/media_kit.dart' hide Track;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:rhythm_box/platform.dart';
|
import 'package:rhythm_box/platform.dart';
|
||||||
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/services/local_track.dart';
|
import 'package:rhythm_box/services/local_track.dart';
|
||||||
import 'package:rhythm_box/services/server/server.dart';
|
import 'package:rhythm_box/services/server/server.dart';
|
||||||
import 'package:rhythm_box/widgets/tracks/querying_track_info.dart';
|
import 'package:rhythm_box/widgets/tracks/querying_track_info.dart';
|
||||||
@ -93,7 +94,7 @@ abstract class AudioPlayerInterface {
|
|||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
_mkPlayer.stream.error.listen((event) {
|
_mkPlayer.stream.error.listen((event) {
|
||||||
log('[Playback] Error: $event');
|
Get.find<ErrorNotifier>().logError('[Playback] Error: $event');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,16 +90,28 @@ class RhythmAudioPlayer extends AudioPlayerInterface
|
|||||||
|
|
||||||
Future<void> skipToNext() async {
|
Future<void> skipToNext() async {
|
||||||
Get.find<QueryingTrackInfoProvider>().isQueryingTrackInfo.value = true;
|
Get.find<QueryingTrackInfoProvider>().isQueryingTrackInfo.value = true;
|
||||||
|
Get.find<AudioPlayerProvider>().durationBuffered.value =
|
||||||
|
const Duration(seconds: 0);
|
||||||
|
Get.find<AudioPlayerProvider>().durationCurrent.value =
|
||||||
|
const Duration(seconds: 0);
|
||||||
await _mkPlayer.next();
|
await _mkPlayer.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> skipToPrevious() async {
|
Future<void> skipToPrevious() async {
|
||||||
Get.find<QueryingTrackInfoProvider>().isQueryingTrackInfo.value = true;
|
Get.find<QueryingTrackInfoProvider>().isQueryingTrackInfo.value = true;
|
||||||
|
Get.find<AudioPlayerProvider>().durationBuffered.value =
|
||||||
|
const Duration(seconds: 0);
|
||||||
|
Get.find<AudioPlayerProvider>().durationCurrent.value =
|
||||||
|
const Duration(seconds: 0);
|
||||||
await _mkPlayer.previous();
|
await _mkPlayer.previous();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> jumpTo(int index) async {
|
Future<void> jumpTo(int index) async {
|
||||||
Get.find<QueryingTrackInfoProvider>().isQueryingTrackInfo.value = true;
|
Get.find<QueryingTrackInfoProvider>().isQueryingTrackInfo.value = true;
|
||||||
|
Get.find<AudioPlayerProvider>().durationBuffered.value =
|
||||||
|
const Duration(seconds: 0);
|
||||||
|
Get.find<AudioPlayerProvider>().durationCurrent.value =
|
||||||
|
const Duration(seconds: 0);
|
||||||
await _mkPlayer.jump(index);
|
await _mkPlayer.jump(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
import 'package:get/get.dart';
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
import 'package:flutter_broadcasts/flutter_broadcasts.dart';
|
import 'package:flutter_broadcasts/flutter_broadcasts.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:audio_session/audio_session.dart';
|
import 'package:audio_session/audio_session.dart';
|
||||||
import 'package:rhythm_box/platform.dart';
|
import 'package:rhythm_box/platform.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
|
|
||||||
// ignore: implementation_imports
|
// ignore: implementation_imports
|
||||||
import 'package:rhythm_box/services/audio_player/playback_state.dart';
|
import 'package:rhythm_box/services/audio_player/playback_state.dart';
|
||||||
@ -49,7 +50,7 @@ class CustomPlayer extends Player {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
stream.error.listen((event) {
|
stream.error.listen((event) {
|
||||||
log('[MediaKitError] $event');
|
Get.find<ErrorNotifier>().logError('[Playback] Error: $event');
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
PackageInfo.fromPlatform().then((packageInfo) {
|
PackageInfo.fromPlatform().then((packageInfo) {
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:lrc/lrc.dart';
|
import 'package:lrc/lrc.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:rhythm_box/providers/database.dart';
|
import 'package:rhythm_box/providers/database.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/providers/spotify.dart';
|
import 'package:rhythm_box/providers/spotify.dart';
|
||||||
import 'package:rhythm_box/services/database/database.dart';
|
import 'package:rhythm_box/services/database/database.dart';
|
||||||
import 'package:rhythm_box/services/lyrics/model.dart';
|
import 'package:rhythm_box/services/lyrics/model.dart';
|
||||||
@ -164,8 +163,8 @@ class SyncedLyricsProvider extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return lyrics;
|
return lyrics;
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stack) {
|
||||||
log('[Lyrics] Error: $e; Trace:\n$stackTrace');
|
Get.find<ErrorNotifier>().logError('[Lyrics] Error: $e', trace: stack);
|
||||||
return SubtitleSimple(
|
return SubtitleSimple(
|
||||||
uri: Uri.parse('https://example.com/not-found'),
|
uri: Uri.parse('https://example.com/not-found'),
|
||||||
name: 'Lyrics Not Found',
|
name: 'Lyrics Not Found',
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import 'dart:developer';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:media_kit/media_kit.dart' hide Track;
|
import 'package:media_kit/media_kit.dart' hide Track;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:rhythm_box/platform.dart';
|
import 'package:rhythm_box/platform.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/services/audio_player/custom_player.dart';
|
import 'package:rhythm_box/services/audio_player/custom_player.dart';
|
||||||
import 'package:rhythm_box/services/local_track.dart';
|
import 'package:rhythm_box/services/local_track.dart';
|
||||||
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
||||||
@ -85,7 +86,7 @@ abstract class AudioPlayerInterface {
|
|||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
_mkPlayer.stream.error.listen((event) {
|
_mkPlayer.stream.error.listen((event) {
|
||||||
log('[Playback] Error: $event');
|
Get.find<ErrorNotifier>().logError('[Playback] Error: $event');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:rhythm_box/providers/audio_player.dart';
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
import 'package:rhythm_box/services/sourced_track/models/source_info.dart';
|
import 'package:rhythm_box/services/sourced_track/models/source_info.dart';
|
||||||
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
import 'package:rhythm_box/services/sourced_track/sourced_track.dart';
|
||||||
@ -41,7 +40,9 @@ class ActiveSourcedTrackProvider extends GetxController {
|
|||||||
await audioPlayer.removeTrack(oldActiveIndex);
|
await audioPlayer.removeTrack(oldActiveIndex);
|
||||||
await playback.jumpToTrack(newTrack);
|
await playback.jumpToTrack(newTrack);
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
log('[Playback] Failed to swap with siblings. Error: $e; Trace:\n$stack');
|
Get.find<ErrorNotifier>().logError(
|
||||||
|
'[Playback] Failed to swap with siblings. Error: $e',
|
||||||
|
trace: stack);
|
||||||
} finally {
|
} finally {
|
||||||
query.isQueryingTrackInfo.value = false;
|
query.isQueryingTrackInfo.value = false;
|
||||||
await audioPlayer.resume();
|
await audioPlayer.resume();
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart' hide Response;
|
import 'package:dio/dio.dart' hide Response;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:get/get.dart' hide Response;
|
import 'package:get/get.dart' hide Response;
|
||||||
import 'package:rhythm_box/providers/audio_player.dart';
|
import 'package:rhythm_box/providers/audio_player.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
import 'package:rhythm_box/services/audio_player/audio_player.dart';
|
||||||
import 'package:rhythm_box/services/server/active_sourced_track.dart';
|
import 'package:rhythm_box/services/server/active_sourced_track.dart';
|
||||||
import 'package:rhythm_box/services/server/sourced_track.dart';
|
import 'package:rhythm_box/services/server/sourced_track.dart';
|
||||||
@ -57,8 +56,9 @@ class ServerPlaybackRoutesProvider {
|
|||||||
},
|
},
|
||||||
headers: res.headers.map,
|
headers: res.headers.map,
|
||||||
);
|
);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stack) {
|
||||||
log('[PlaybackSever] Error: $e; Trace:\n $stackTrace');
|
Get.find<ErrorNotifier>()
|
||||||
|
.logError('[PlaybackSever] Error: $e', trace: stack);
|
||||||
return Response.internalServerError();
|
return Response.internalServerError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:get/get.dart' hide Value;
|
import 'package:get/get.dart' hide Value;
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:rhythm_box/providers/database.dart';
|
import 'package:rhythm_box/providers/database.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:rhythm_box/services/database/database.dart';
|
import 'package:rhythm_box/services/database/database.dart';
|
||||||
import 'package:rhythm_box/services/utils.dart';
|
import 'package:rhythm_box/services/utils.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
@ -242,7 +241,8 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
];
|
];
|
||||||
} on VideoUnplayableException catch (e) {
|
} on VideoUnplayableException catch (e) {
|
||||||
// Ignore this error and continue with the search
|
// Ignore this error and continue with the search
|
||||||
log('[Source][YoutubeMusic] Unable to search data: $e');
|
Get.find<ErrorNotifier>().logError(
|
||||||
|
'[Source][YoutubeMusic] Unable to play stream on youtube: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +250,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
|
|
||||||
final searchResults = await youtubeClient.search.search(
|
final searchResults = await youtubeClient.search.search(
|
||||||
query,
|
query,
|
||||||
filter: const SearchFilter('CAMSAhAB'),
|
filter: TypeFilters.video,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ServiceUtils.onlyContainsEnglish(query)) {
|
if (ServiceUtils.onlyContainsEnglish(query)) {
|
||||||
|
@ -1,12 +1,43 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:rhythm_box/platform.dart';
|
import 'package:rhythm_box/platform.dart';
|
||||||
|
import 'package:rhythm_box/providers/error_notifier.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
class SystemShell extends StatelessWidget {
|
class SystemShell extends StatefulWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
const SystemShell({super.key, required this.child});
|
const SystemShell({super.key, required this.child});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SystemShell> createState() => _SystemShellState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SystemShellState extends State<SystemShell> {
|
||||||
|
late final ErrorNotifier _errorNotifier = Get.find();
|
||||||
|
|
||||||
|
StreamSubscription? _subscription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_subscription = _errorNotifier.showing.listen((value) {
|
||||||
|
if (value == null) {
|
||||||
|
ScaffoldMessenger.of(context).clearMaterialBanners();
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showMaterialBanner(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_subscription?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (PlatformInfo.isMacOS) {
|
if (PlatformInfo.isMacOS) {
|
||||||
@ -21,12 +52,12 @@ class SystemShell extends StatelessWidget {
|
|||||||
thickness: 0.3,
|
thickness: 0.3,
|
||||||
height: 0.3,
|
height: 0.3,
|
||||||
),
|
),
|
||||||
Expanded(child: child),
|
Expanded(child: widget.child),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return child;
|
return widget.child;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ class _SyncedLyricsState extends State<SyncedLyrics> {
|
|||||||
Theme.of(context).colorScheme.onSurface.withOpacity(0.5);
|
Theme.of(context).colorScheme.onSurface.withOpacity(0.5);
|
||||||
|
|
||||||
void _syncLyricsProgress() {
|
void _syncLyricsProgress() {
|
||||||
|
if (_isLyricSynced) {
|
||||||
for (var idx = 0; idx < _lyric!.lyrics.length; idx++) {
|
for (var idx = 0; idx < _lyric!.lyrics.length; idx++) {
|
||||||
final lyricSlice = _lyric!.lyrics[idx];
|
final lyricSlice = _lyric!.lyrics[idx];
|
||||||
final lyricNextSlice =
|
final lyricNextSlice =
|
||||||
@ -67,8 +68,9 @@ class _SyncedLyricsState extends State<SyncedLyrics> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_lyric!.lyrics.isNotEmpty) {
|
if (_lyric!.lyrics.isNotEmpty || !_isLyricSynced) {
|
||||||
_autoScrollController.scrollToIndex(
|
_autoScrollController.scrollToIndex(
|
||||||
0,
|
0,
|
||||||
preferPosition: AutoScrollPosition.begin,
|
preferPosition: AutoScrollPosition.begin,
|
||||||
@ -120,6 +122,18 @@ class _SyncedLyricsState extends State<SyncedLyrics> {
|
|||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (_lyric != null && _lyric!.lyrics.isNotEmpty && !_isLyricSynced)
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Text(
|
||||||
|
'Lyrics isn\'t synced',
|
||||||
|
textAlign: MediaQuery.of(context).size.width >= 720
|
||||||
|
? TextAlign.center
|
||||||
|
: TextAlign.left,
|
||||||
|
).paddingSymmetric(
|
||||||
|
horizontal: 24,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
),
|
||||||
if (_lyric != null && _lyric!.lyrics.isNotEmpty)
|
if (_lyric != null && _lyric!.lyrics.isNotEmpty)
|
||||||
SliverList.builder(
|
SliverList.builder(
|
||||||
itemCount: _lyric!.lyrics.length,
|
itemCount: _lyric!.lyrics.length,
|
||||||
@ -132,7 +146,8 @@ class _SyncedLyricsState extends State<SyncedLyrics> {
|
|||||||
lyricSlice.time.inSeconds &&
|
lyricSlice.time.inSeconds &&
|
||||||
(lyricNextSlice == null ||
|
(lyricNextSlice == null ||
|
||||||
lyricNextSlice.time.inSeconds >
|
lyricNextSlice.time.inSeconds >
|
||||||
_playback.durationCurrent.value.inSeconds);
|
_playback.durationCurrent.value.inSeconds) &&
|
||||||
|
_isLyricSynced;
|
||||||
|
|
||||||
if (_playback.durationCurrent.value.inSeconds ==
|
if (_playback.durationCurrent.value.inSeconds ==
|
||||||
lyricSlice.time.inSeconds &&
|
lyricSlice.time.inSeconds &&
|
||||||
@ -142,6 +157,7 @@ class _SyncedLyricsState extends State<SyncedLyrics> {
|
|||||||
preferPosition: AutoScrollPosition.middle,
|
preferPosition: AutoScrollPosition.middle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return AutoScrollTag(
|
return AutoScrollTag(
|
||||||
key: ValueKey(idx),
|
key: ValueKey(idx),
|
||||||
index: idx,
|
index: idx,
|
||||||
@ -215,6 +231,7 @@ class _SyncedLyricsState extends State<SyncedLyrics> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Lyrics Not Found',
|
'Lyrics Not Found',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
),
|
),
|
||||||
const Text(
|
const Text(
|
||||||
|
@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.0.0+6
|
version: 1.0.0+7
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.0
|
sdk: ^3.5.0
|
||||||
|
Loading…
Reference in New Issue
Block a user