mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-12-06 07:29:42 +00:00
feat: Integrate DAB Music, add quality display, and optimize performance
This commit introduces several new features and improvements to Spotube: - **DAB Music Integration:** Adds DAB Music as a new high-quality audio source, with support for searching, streaming, and downloading tracks. - **Playback Quality Display:** Adds a UI element to the player to display the actual audio quality of the currently playing stream. - **Performance Optimization:** Improves the startup and shutdown performance of the desktop application. - **Dependency Fix:** Resolves a dependency conflict with `dio_retry` by implementing a custom retry interceptor.
This commit is contained in:
parent
e7523c70d0
commit
f3e3159ca9
@ -156,21 +156,9 @@ class Spotube extends HookConsumerWidget {
|
|||||||
useEffect(() {
|
useEffect(() {
|
||||||
(() async {
|
(() async {
|
||||||
await EncryptedKvStoreService.initialize();
|
await EncryptedKvStoreService.initialize();
|
||||||
if (kIsAndroid || kIsDesktop) {
|
|
||||||
await NewPipeExtractor.init();
|
|
||||||
}
|
|
||||||
if (!kIsWeb) {
|
if (!kIsWeb) {
|
||||||
MetadataGod.initialize();
|
MetadataGod.initialize();
|
||||||
}
|
}
|
||||||
if (kIsDesktop) {
|
|
||||||
await YtDlp.instance
|
|
||||||
.setBinaryLocation(
|
|
||||||
KVStoreService.getYoutubeEnginePath(YoutubeClientEngine.ytDlp) ??
|
|
||||||
"yt-dlp${kIsWindows ? '.exe' : ''}",
|
|
||||||
)
|
|
||||||
.catchError((e, stack) => null);
|
|
||||||
await FlutterDiscordRPC.initialize(Env.discordAppId);
|
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
FlutterNativeSplash.remove();
|
FlutterNativeSplash.remove();
|
||||||
|
|
||||||
@ -178,6 +166,21 @@ class Spotube extends HookConsumerWidget {
|
|||||||
HomeWidget.registerInteractivityCallback(glanceBackgroundCallback);
|
HomeWidget.registerInteractivityCallback(glanceBackgroundCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future.delayed(const Duration(seconds: 5), () {
|
||||||
|
if (kIsAndroid || kIsDesktop) {
|
||||||
|
NewPipeExtractor.init();
|
||||||
|
}
|
||||||
|
if (kIsDesktop) {
|
||||||
|
YtDlp.instance
|
||||||
|
.setBinaryLocation(
|
||||||
|
KVStoreService.getYoutubeEnginePath(YoutubeClientEngine.ytDlp) ??
|
||||||
|
"yt-dlp${kIsWindows ? '.exe' : ''}",
|
||||||
|
)
|
||||||
|
.catchError((e, stack) => null);
|
||||||
|
FlutterDiscordRPC.initialize(Env.discordAppId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return () {
|
return () {
|
||||||
/// For enabling hot reload for audio player
|
/// For enabling hot reload for audio player
|
||||||
if (!kDebugMode) return;
|
if (!kDebugMode) return;
|
||||||
|
|||||||
27
lib/models/audio_quality.dart
Normal file
27
lib/models/audio_quality.dart
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
enum AudioQuality {
|
||||||
|
low,
|
||||||
|
high,
|
||||||
|
lossless;
|
||||||
|
|
||||||
|
String toDabMusicQuality() {
|
||||||
|
switch (this) {
|
||||||
|
case AudioQuality.low:
|
||||||
|
return '12';
|
||||||
|
case AudioQuality.high:
|
||||||
|
return '27';
|
||||||
|
case AudioQuality.lossless:
|
||||||
|
return '28';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String toShortString() {
|
||||||
|
switch (this) {
|
||||||
|
case AudioQuality.low:
|
||||||
|
return 'Low';
|
||||||
|
case AudioQuality.high:
|
||||||
|
return 'High';
|
||||||
|
case AudioQuality.lossless:
|
||||||
|
return 'Lossless';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -66,7 +66,7 @@ class AppDatabase extends _$AppDatabase {
|
|||||||
AppDatabase() : super(_openConnection());
|
AppDatabase() : super(_openConnection());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 10;
|
int get schemaVersion => 11;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration {
|
MigrationStrategy get migration {
|
||||||
@ -237,6 +237,12 @@ class AppDatabase extends _$AppDatabase {
|
|||||||
.dropColumn(schema.sourceMatchTable, "source_id")
|
.dropColumn(schema.sourceMatchTable, "source_id")
|
||||||
.catchError((e, stack) => AppLogger.reportError(e, stack));
|
.catchError((e, stack) => AppLogger.reportError(e, stack));
|
||||||
},
|
},
|
||||||
|
from10To11: (m, schema) async {
|
||||||
|
await m.addColumn(
|
||||||
|
schema.preferencesTable,
|
||||||
|
schema.preferencesTable.audioQuality,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
part of '../database.dart';
|
part of '../database.dart';
|
||||||
|
|
||||||
|
import 'package:spotube/models/audio_quality.dart';
|
||||||
|
|
||||||
enum LayoutMode {
|
enum LayoutMode {
|
||||||
compact,
|
compact,
|
||||||
extended,
|
extended,
|
||||||
@ -79,6 +81,8 @@ class PreferencesTable extends Table {
|
|||||||
TextColumn get themeMode =>
|
TextColumn get themeMode =>
|
||||||
textEnum<ThemeMode>().withDefault(Constant(ThemeMode.system.name))();
|
textEnum<ThemeMode>().withDefault(Constant(ThemeMode.system.name))();
|
||||||
TextColumn get audioSourceId => text().nullable()();
|
TextColumn get audioSourceId => text().nullable()();
|
||||||
|
TextColumn get audioQuality =>
|
||||||
|
textEnum<AudioQuality>().withDefault(Constant(AudioQuality.high.name))();
|
||||||
TextColumn get youtubeClientEngine => textEnum<YoutubeClientEngine>()
|
TextColumn get youtubeClientEngine => textEnum<YoutubeClientEngine>()
|
||||||
.withDefault(Constant(YoutubeClientEngine.youtubeExplode.name))();
|
.withDefault(Constant(YoutubeClientEngine.youtubeExplode.name))();
|
||||||
BoolColumn get discordPresence =>
|
BoolColumn get discordPresence =>
|
||||||
@ -119,6 +123,7 @@ class PreferencesTable extends Table {
|
|||||||
enableConnect: false,
|
enableConnect: false,
|
||||||
cacheMusic: true,
|
cacheMusic: true,
|
||||||
connectPort: -1,
|
connectPort: -1,
|
||||||
|
audioQuality: AudioQuality.high,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import 'package:spotube/extensions/constrains.dart';
|
|||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/modules/root/spotube_navigation_bar.dart';
|
import 'package:spotube/modules/root/spotube_navigation_bar.dart';
|
||||||
import 'package:spotube/provider/audio_player/audio_player.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/metadata_plugin/audio_source/quality_label.dart';
|
import 'package:spotube/provider/player/playback_quality_provider.dart';
|
||||||
import 'package:spotube/provider/server/active_track_sources.dart';
|
import 'package:spotube/provider/server/active_track_sources.dart';
|
||||||
import 'package:spotube/provider/volume_provider.dart';
|
import 'package:spotube/provider/volume_provider.dart';
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
final currentActiveTrackSource = sourcedCurrentTrack.asData?.value?.source;
|
final currentActiveTrackSource = sourcedCurrentTrack.asData?.value?.source;
|
||||||
final isLocalTrack = currentActiveTrack is SpotubeLocalTrackObject;
|
final isLocalTrack = currentActiveTrack is SpotubeLocalTrackObject;
|
||||||
final mediaQuery = MediaQuery.sizeOf(context);
|
final mediaQuery = MediaQuery.sizeOf(context);
|
||||||
final qualityLabel = ref.watch(audioSourceQualityLabelProvider);
|
final quality = ref.watch(playbackQualityProvider);
|
||||||
|
|
||||||
final shouldHide = useState(true);
|
final shouldHide = useState(true);
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
leading: const Icon(SpotubeIcons.lightningOutlined),
|
leading: const Icon(SpotubeIcons.lightningOutlined),
|
||||||
child: Text(qualityLabel),
|
child: Text(quality.toShortString()),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@ -11,8 +11,9 @@ import 'package:spotube/components/links/link_text.dart';
|
|||||||
import 'package:spotube/extensions/constrains.dart';
|
import 'package:spotube/extensions/constrains.dart';
|
||||||
import 'package:spotube/models/metadata/metadata.dart';
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
import 'package:spotube/provider/audio_player/audio_player.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
|
import 'package.spotube/provider/player/playback_quality_provider.dart';
|
||||||
import 'package:spotube/provider/server/sourced_track_provider.dart';
|
import 'package:spotube/provider/server/sourced_track_provider.dart';
|
||||||
import 'package.spotube/services/sourced_track/sourced_track.dart';
|
import 'package:spotube/services/sourced_track/sourced_track.dart';
|
||||||
|
|
||||||
class PlayerTrackDetails extends HookConsumerWidget {
|
class PlayerTrackDetails extends HookConsumerWidget {
|
||||||
final Color? color;
|
final Color? color;
|
||||||
@ -24,9 +25,7 @@ class PlayerTrackDetails extends HookConsumerWidget {
|
|||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final mediaQuery = MediaQuery.of(context);
|
final mediaQuery = MediaQuery.of(context);
|
||||||
final playback = ref.watch(audioPlayerProvider);
|
final playback = ref.watch(audioPlayerProvider);
|
||||||
final sourcedTrack = playback.activeTrack != null
|
final quality = ref.watch(playbackQualityProvider);
|
||||||
? ref.watch(sourcedTrackProvider(playback.activeTrack!))
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
@ -64,12 +63,11 @@ class PlayerTrackDetails extends HookConsumerWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: theme.typography.small.copyWith(color: color),
|
style: theme.typography.small.copyWith(color: color),
|
||||||
),
|
),
|
||||||
if (sourcedTrack?.asData?.value != null)
|
Text(
|
||||||
Text(
|
quality.toShortString(),
|
||||||
sourcedTrack!.asData!.value.qualityPreset?.name ?? "",
|
overflow: TextOverflow.ellipsis,
|
||||||
overflow: TextOverflow.ellipsis,
|
style: theme.typography.small.copyWith(color: color),
|
||||||
style: theme.typography.small.copyWith(color: color),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -93,12 +91,11 @@ class PlayerTrackDetails extends HookConsumerWidget {
|
|||||||
onOverflowArtistClick: () =>
|
onOverflowArtistClick: () =>
|
||||||
context.navigateTo(TrackRoute(trackId: track!.id)),
|
context.navigateTo(TrackRoute(trackId: track!.id)),
|
||||||
),
|
),
|
||||||
if (sourcedTrack?.asData?.value != null)
|
Text(
|
||||||
Text(
|
quality.toShortString(),
|
||||||
sourcedTrack!.asData!.value.qualityPreset?.name ?? "",
|
overflow: TextOverflow.ellipsis,
|
||||||
overflow: TextOverflow.ellipsis,
|
style: theme.typography.small.copyWith(color: color),
|
||||||
style: theme.typography.small.copyWith(color: color),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -193,7 +193,7 @@ class DownloadManagerNotifier extends Notifier<List<DownloadTask>> {
|
|||||||
String? url;
|
String? url;
|
||||||
if (track.source == 'DAB Music') {
|
if (track.source == 'DAB Music') {
|
||||||
final dabMusicApi = DabMusicApi();
|
final dabMusicApi = DabMusicApi();
|
||||||
url = await dabMusicApi.getDownloadUrl(track.query.album.id);
|
url = await dabMusicApi.getDownloadUrl(track.query.id);
|
||||||
} else {
|
} else {
|
||||||
url = track.getUrlOfQuality(
|
url = track.getUrlOfQuality(
|
||||||
container,
|
container,
|
||||||
|
|||||||
@ -1,9 +1,15 @@
|
|||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:spotube/models/audio_quality.dart';
|
||||||
import 'package:spotube/models/metadata/metadata.dart';
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
import 'package:spotube/provider/metadata_plugin/audio_source.dart';
|
import 'package:spotube/provider/metadata_plugin/audio_source.dart';
|
||||||
|
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||||
import 'package:spotube/services/dab_music/dab_music_api.dart';
|
import 'package:spotube/services/dab_music/dab_music_api.dart';
|
||||||
|
|
||||||
class DabMusicAudioSource extends AudioSource {
|
class DabMusicAudioSource extends AudioSource {
|
||||||
final DabMusicApi _api = DabMusicApi();
|
final DabMusicApi _api = DabMusicApi();
|
||||||
|
final Ref? ref;
|
||||||
|
|
||||||
|
DabMusicAudioSource([this.ref]);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get name => 'DAB Music';
|
String get name => 'DAB Music';
|
||||||
@ -15,6 +21,7 @@ class DabMusicAudioSource extends AudioSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> getStreamUrl(SpotubeTrackObject track) {
|
Future<String> getStreamUrl(SpotubeTrackObject track) {
|
||||||
return _api.getStreamUrl(track.id);
|
final quality = ref?.read(userPreferencesProvider).audioQuality ?? AudioQuality.high;
|
||||||
|
return _api.getStreamUrl(track.id, quality: quality);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
58
lib/provider/player/playback_quality_provider.dart
Normal file
58
lib/provider/player/playback_quality_provider.dart
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:spotube/models/audio_quality.dart';
|
||||||
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
|
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||||
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
|
|
||||||
|
class PlaybackQualityState {
|
||||||
|
final AudioQuality selectedQuality;
|
||||||
|
final double? bitrate;
|
||||||
|
|
||||||
|
PlaybackQualityState({
|
||||||
|
required this.selectedQuality,
|
||||||
|
this.bitrate,
|
||||||
|
});
|
||||||
|
|
||||||
|
PlaybackQualityState copyWith({
|
||||||
|
AudioQuality? selectedQuality,
|
||||||
|
double? bitrate,
|
||||||
|
}) {
|
||||||
|
return PlaybackQualityState(
|
||||||
|
selectedQuality: selectedQuality ?? this.selectedQuality,
|
||||||
|
bitrate: bitrate ?? this.bitrate,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toShortString() {
|
||||||
|
if (bitrate != null) {
|
||||||
|
return '${(bitrate! / 1000).round()} kbps';
|
||||||
|
}
|
||||||
|
return selectedQuality.toShortString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PlaybackQualityNotifier extends StateNotifier<PlaybackQualityState> {
|
||||||
|
final Ref ref;
|
||||||
|
|
||||||
|
PlaybackQualityNotifier(this.ref)
|
||||||
|
: super(
|
||||||
|
PlaybackQualityState(
|
||||||
|
selectedQuality:
|
||||||
|
ref.read(userPreferencesProvider).audioQuality,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
ref.listen(userPreferencesProvider.select((s) => s.audioQuality),
|
||||||
|
(previous, next) {
|
||||||
|
state = state.copyWith(selectedQuality: next);
|
||||||
|
});
|
||||||
|
|
||||||
|
audioPlayer.audioBitrateStream.listen((bitrate) {
|
||||||
|
state = state.copyWith(bitrate: bitrate);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final playbackQualityProvider =
|
||||||
|
StateNotifierProvider<PlaybackQualityNotifier, PlaybackQualityState>(
|
||||||
|
(ref) => PlaybackQualityNotifier(ref),
|
||||||
|
);
|
||||||
@ -10,6 +10,7 @@ import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart';
|
|||||||
import 'package:spotube/provider/database/database.dart';
|
import 'package:spotube/provider/database/database.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/logger/logger.dart';
|
import 'package:spotube/services/logger/logger.dart';
|
||||||
|
import 'package:spotube/models/audio_quality.dart';
|
||||||
import 'package:spotube/utils/platform.dart';
|
import 'package:spotube/utils/platform.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
import 'package:open_file/open_file.dart';
|
import 'package:open_file/open_file.dart';
|
||||||
@ -226,6 +227,10 @@ class UserPreferencesNotifier extends Notifier<PreferencesTableData> {
|
|||||||
void setCacheMusic(bool cache) {
|
void setCacheMusic(bool cache) {
|
||||||
setData(PreferencesTableCompanion(cacheMusic: Value(cache)));
|
setData(PreferencesTableCompanion(cacheMusic: Value(cache)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAudioQuality(AudioQuality quality) {
|
||||||
|
setData(PreferencesTableCompanion(audioQuality: Value(quality)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final userPreferencesProvider =
|
final userPreferencesProvider =
|
||||||
|
|||||||
@ -1,18 +1,23 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:dio_retry/dio_retry.dart';
|
import 'package:spotube/models/audio_quality.dart';
|
||||||
import 'package:spotube/models/metadata/metadata.dart';
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
|
import 'package:spotube/services/dio/retry_interceptor.dart';
|
||||||
|
|
||||||
class DabMusicApi {
|
class DabMusicApi {
|
||||||
final Dio _dio = Dio(BaseOptions(baseUrl: 'https://dabmusic.xyz/api'));
|
final Dio _dio = Dio(
|
||||||
|
BaseOptions(
|
||||||
|
baseUrl: 'https://dabmusic.xyz/api',
|
||||||
|
followRedirects: false,
|
||||||
|
validateStatus: (status) {
|
||||||
|
return status != null && status < 500;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
DabMusicApi() {
|
DabMusicApi() {
|
||||||
_dio.interceptors.add(
|
_dio.interceptors.add(
|
||||||
RetryInterceptor(
|
RetryInterceptor(
|
||||||
dio: _dio,
|
dio: _dio,
|
||||||
options: const RetryOptions(
|
|
||||||
retries: 3,
|
|
||||||
retryInterval: Duration(seconds: 1),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -44,18 +49,21 @@ class DabMusicApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getStreamUrl(String trackId, {String quality = '27'}) async {
|
Future<String> getStreamUrl(
|
||||||
|
String trackId, {
|
||||||
|
AudioQuality quality = AudioQuality.high,
|
||||||
|
}) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get(
|
final response = await _dio.get(
|
||||||
'/stream',
|
'/stream',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'trackId': trackId,
|
'trackId': trackId,
|
||||||
'quality': quality,
|
'quality': quality.toDabMusicQuality(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 302) {
|
||||||
return response.data['streamUrl'];
|
return response.headers.value('location')!;
|
||||||
} else {
|
} else {
|
||||||
throw Exception('Failed to get stream URL');
|
throw Exception('Failed to get stream URL');
|
||||||
}
|
}
|
||||||
@ -64,19 +72,21 @@ class DabMusicApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getDownloadUrl(String albumId, {String quality = '27'}) async {
|
Future<String> getDownloadUrl(
|
||||||
|
String trackId, {
|
||||||
|
AudioQuality quality = AudioQuality.high,
|
||||||
|
}) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get(
|
final response = await _dio.get(
|
||||||
'/download',
|
'/download',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'albumId': albumId,
|
'trackId': trackId,
|
||||||
'quality': quality,
|
'quality': quality.toDabMusicQuality(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 302) {
|
||||||
// Assuming the API returns a direct download link or a JSON with the link
|
return response.headers.value('location')!;
|
||||||
return response.data['downloadUrl'];
|
|
||||||
} else {
|
} else {
|
||||||
throw Exception('Failed to get download URL');
|
throw Exception('Failed to get download URL');
|
||||||
}
|
}
|
||||||
|
|||||||
51
lib/services/dio/retry_interceptor.dart
Normal file
51
lib/services/dio/retry_interceptor.dart
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
|
class RetryInterceptor extends Interceptor {
|
||||||
|
final Dio dio;
|
||||||
|
final int retries;
|
||||||
|
final Duration retryInterval;
|
||||||
|
|
||||||
|
RetryInterceptor({
|
||||||
|
required this.dio,
|
||||||
|
this.retries = 3,
|
||||||
|
this.retryInterval = const Duration(seconds: 1),
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future onError(DioError err, ErrorInterceptorHandler handler) async {
|
||||||
|
int retryCount = err.requestOptions.extra['retry_count'] ?? 0;
|
||||||
|
if (retryCount < retries && _shouldRetry(err)) {
|
||||||
|
retryCount++;
|
||||||
|
err.requestOptions.extra['retry_count'] = retryCount;
|
||||||
|
try {
|
||||||
|
await Future.delayed(retryInterval);
|
||||||
|
final response = await dio.request(
|
||||||
|
err.requestOptions.path,
|
||||||
|
cancelToken: err.requestOptions.cancelToken,
|
||||||
|
data: err.requestOptions.data,
|
||||||
|
onReceiveProgress: err.requestOptions.onReceiveProgress,
|
||||||
|
onSendProgress: err.requestOptions.onSendProgress,
|
||||||
|
queryParameters: err.requestOptions.queryParameters,
|
||||||
|
options: Options(
|
||||||
|
method: err.requestOptions.method,
|
||||||
|
headers: err.requestOptions.headers,
|
||||||
|
responseType: err.requestOptions.responseType,
|
||||||
|
extra: err.requestOptions.extra,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return handler.resolve(response);
|
||||||
|
} catch (e) {
|
||||||
|
return super.onError(err, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.onError(err, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _shouldRetry(DioError err) {
|
||||||
|
return err.type == DioErrorType.other ||
|
||||||
|
err.type == DioErrorType.connectTimeout ||
|
||||||
|
err.response?.statusCode == 429;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,7 +30,6 @@ dependencies:
|
|||||||
url: https://github.com/KRTirtho/flutter-plugins.git
|
url: https://github.com/KRTirtho/flutter-plugins.git
|
||||||
device_info_plus: ^11.1.1
|
device_info_plus: ^11.1.1
|
||||||
dio: ^5.4.3+1
|
dio: ^5.4.3+1
|
||||||
dio_retry: ^4.0.0
|
|
||||||
disable_battery_optimization:
|
disable_battery_optimization:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/KRTirtho/Disable-Battery-Optimizations.git
|
url: https://github.com/KRTirtho/Disable-Battery-Optimizations.git
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user