fix(playback): play not fetching full playlist if playlist is too long

This commit is contained in:
Kingkor Roy Tirtho 2025-09-08 14:09:16 +06:00
parent 43ddf90c48
commit 7b21eca37b
3 changed files with 92 additions and 34 deletions

View File

@ -13,6 +13,7 @@ import 'package:spotube/provider/audio_player/audio_player.dart';
import 'package:spotube/provider/connect/connect.dart';
import 'package:spotube/provider/history/history.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/logger/logger.dart';
typedef UseActionCallbacks = ({
bool isActive,
@ -82,6 +83,9 @@ UseActionCallbacks useActionCallbacks(WidgetRef ref) {
allTracks.sublist(initialTracks.length),
);
}
} catch (e, stack) {
AppLogger.reportError(e, stack);
rethrow;
} finally {
isLoading.value = false;
}
@ -134,6 +138,9 @@ UseActionCallbacks useActionCallbacks(WidgetRef ref) {
allTracks.sublist(initialTracks.length),
);
}
} catch (e, stack) {
AppLogger.reportError(e, stack);
rethrow;
} finally {
if (context.mounted) {
isLoading.value = false;

View File

@ -1,8 +1,10 @@
import 'dart:async';
import 'dart:math';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/provider/metadata_plugin/utils/common.dart';
import 'package:spotube/services/logger/logger.dart';
abstract class FamilyPaginatedAsyncNotifier<K, A>
extends FamilyAsyncNotifier<SpotubePaginationResponseObject<K>, A>
@ -27,7 +29,8 @@ abstract class FamilyPaginatedAsyncNotifier<K, A>
final items = newState.items.isEmpty ? <K>[] : newState.items.cast<K>();
state = AsyncData(newState.copyWith(items: <K>[...oldItems, ...items]));
} catch (e) {
} catch (e, stack) {
AppLogger.reportError(e, stack);
state = AsyncData(oldState!);
}
}
@ -38,17 +41,32 @@ abstract class FamilyPaginatedAsyncNotifier<K, A>
bool hasMore = true;
while (hasMore) {
await update((state) async {
final newState = await fetch(
state.nextOffset!,
state.limit,
state.value!.nextOffset!,
max(state.value!.limit, 100),
)
.catchError(
(e) => fetch(state.value!.nextOffset!, max(state.value!.limit, 50)),
)
.catchError(
(e) => fetch(state.value!.nextOffset!, state.value!.limit),
)
.catchError(
(e) async {
await Future.delayed(const Duration(milliseconds: 500));
return fetch(state.value!.nextOffset!, state.value!.limit);
},
);
hasMore = newState.hasMore;
final oldItems = state.items.isEmpty ? <K>[] : state.items.cast<K>();
final oldItems =
state.value!.items.isEmpty ? <K>[] : state.value!.items.cast<K>();
final items = newState.items.isEmpty ? <K>[] : newState.items.cast<K>();
return newState.copyWith(items: <K>[...oldItems, ...items]);
});
state = AsyncData(
newState.copyWith(items: [...oldItems, ...items]),
);
}
return state.value!.items.cast<K>();
@ -78,7 +96,8 @@ abstract class AutoDisposeFamilyPaginatedAsyncNotifier<K, A>
...newState.items.cast<K>(),
]),
);
} catch (e) {
} catch (e, stack) {
AppLogger.reportError(e, stack);
state = AsyncData(oldState!);
}
}
@ -89,18 +108,32 @@ abstract class AutoDisposeFamilyPaginatedAsyncNotifier<K, A>
bool hasMore = true;
while (hasMore) {
await update((state) async {
final newState = await fetch(
state.nextOffset!,
state.limit,
state.value!.nextOffset!,
max(state.value!.limit, 100),
)
.catchError(
(e) => fetch(state.value!.nextOffset!, max(state.value!.limit, 50)),
)
.catchError(
(e) => fetch(state.value!.nextOffset!, state.value!.limit),
)
.catchError(
(e) async {
await Future.delayed(const Duration(milliseconds: 500));
return fetch(state.value!.nextOffset!, state.value!.limit);
},
);
hasMore = newState.hasMore;
return newState.copyWith(items: [
...state.items.cast<K>(),
...newState.items.cast<K>(),
]);
});
final oldItems =
state.value!.items.isEmpty ? <K>[] : state.value!.items.cast<K>();
final items = newState.items.isEmpty ? <K>[] : newState.items.cast<K>();
state = AsyncData(
newState.copyWith(items: [...oldItems, ...items]),
);
}
return state.value!.items.cast<K>();

View File

@ -1,10 +1,12 @@
import 'dart:async';
import 'dart:math';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/models/metadata/metadata.dart';
// ignore: implementation_imports
import 'package:riverpod/src/async_notifier.dart';
import 'package:spotube/provider/metadata_plugin/utils/common.dart';
import 'package:spotube/services/logger/logger.dart';
mixin PaginatedAsyncNotifierMixin<K>
// ignore: invalid_use_of_internal_member
@ -28,7 +30,8 @@ mixin PaginatedAsyncNotifierMixin<K>
final items = newState.items.isEmpty ? <K>[] : newState.items.cast<K>();
state = AsyncData(newState.copyWith(items: <K>[...oldItems, ...items]));
} catch (e) {
} catch (e, stack) {
AppLogger.reportError(e, stack);
state = AsyncData(oldState!);
}
}
@ -39,17 +42,32 @@ mixin PaginatedAsyncNotifierMixin<K>
bool hasMore = true;
while (hasMore) {
await update((state) async {
final newState = await fetch(
state.nextOffset!,
state.limit,
state.value!.nextOffset!,
max(state.value!.limit, 100),
)
.catchError(
(e) => fetch(state.value!.nextOffset!, max(state.value!.limit, 50)),
)
.catchError(
(e) => fetch(state.value!.nextOffset!, state.value!.limit),
)
.catchError(
(e) async {
await Future.delayed(const Duration(milliseconds: 500));
return fetch(state.value!.nextOffset!, state.value!.limit);
},
);
hasMore = newState.hasMore;
final oldItems = state.items.isEmpty ? <K>[] : state.items.cast<K>();
final oldItems =
state.value!.items.isEmpty ? <K>[] : state.value!.items.cast<K>();
final items = newState.items.isEmpty ? <K>[] : newState.items.cast<K>();
return newState.copyWith(items: <K>[...oldItems, ...items]);
});
state = AsyncData(
newState.copyWith(items: [...oldItems, ...items]),
);
}
return state.value!.items.cast<K>();