mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2025-12-06 11:09:28 +00:00
feat: api token login
This commit is contained in:
parent
880960c745
commit
682631fb8e
17 changed files with 993 additions and 254 deletions
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:shelfsdk/audiobookshelf_api.dart';
|
||||
|
|
@ -10,6 +11,12 @@ import 'package:vaani/settings/api_settings_provider.dart';
|
|||
|
||||
part 'api_provider.g.dart';
|
||||
|
||||
// TODO: workaround for https://github.com/rrousselGit/riverpod/issues/3718
|
||||
typedef ResponseErrorHandler = void Function(
|
||||
Response response, [
|
||||
Object? error,
|
||||
]);
|
||||
|
||||
final _logger = Logger('api_provider');
|
||||
|
||||
Uri makeBaseUrl(String address) {
|
||||
|
|
@ -52,25 +59,33 @@ AudiobookshelfApi authenticatedApi(AuthenticatedApiRef ref) {
|
|||
/// ping the server to check if it is reachable
|
||||
@riverpod
|
||||
FutureOr<bool> isServerAlive(IsServerAliveRef ref, String address) async {
|
||||
// return (await ref.watch(audiobookshelfApiProvider).server.ping()) ?? false;
|
||||
// if address not starts with http or https, add https
|
||||
|
||||
// !remove this line
|
||||
// return true;
|
||||
|
||||
if (address.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
if (!address.startsWith('http') && !address.startsWith('https')) {
|
||||
address = 'https://$address';
|
||||
}
|
||||
|
||||
// check url is valid
|
||||
if (!Uri.parse(address).isAbsolute) {
|
||||
try {
|
||||
return await AudiobookshelfApi(baseUrl: makeBaseUrl(address))
|
||||
.server
|
||||
.ping() ??
|
||||
false;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return await AudiobookshelfApi(baseUrl: Uri.parse(address)).server.ping() ??
|
||||
false;
|
||||
}
|
||||
|
||||
/// fetch status of server
|
||||
@riverpod
|
||||
FutureOr<ServerStatusResponse?> serverStatus(
|
||||
ServerStatusRef ref,
|
||||
Uri baseUrl, [
|
||||
ResponseErrorHandler? responseErrorHandler,
|
||||
]) async {
|
||||
_logger.fine('fetching server status: $baseUrl');
|
||||
final api = ref.watch(audiobookshelfApiProvider(baseUrl));
|
||||
final res =
|
||||
await api.server.status(responseErrorHandler: responseErrorHandler);
|
||||
_logger.fine('server status: $res');
|
||||
return res;
|
||||
}
|
||||
|
||||
/// fetch the personalized view
|
||||
|
|
@ -97,13 +112,18 @@ class PersonalizedView extends _$PersonalizedView {
|
|||
) ??
|
||||
await apiResponseCacheManager.getFileFromCache(key);
|
||||
if (cachedRes != null) {
|
||||
final resJson = jsonDecode(await cachedRes.file.readAsString()) as List;
|
||||
final res = [
|
||||
for (final item in resJson)
|
||||
Shelf.fromJson(item as Map<String, dynamic>),
|
||||
];
|
||||
_logger.fine('reading from cache: $cachedRes');
|
||||
yield res;
|
||||
_logger.fine('reading from cache: $cachedRes for key: $key');
|
||||
try {
|
||||
final resJson = jsonDecode(await cachedRes.file.readAsString()) as List;
|
||||
final res = [
|
||||
for (final item in resJson)
|
||||
Shelf.fromJson(item as Map<String, dynamic>),
|
||||
];
|
||||
_logger.fine('successfully read from cache key: $key');
|
||||
yield res;
|
||||
} catch (e) {
|
||||
_logger.warning('error reading from cache: $e\n$cachedRes');
|
||||
}
|
||||
}
|
||||
|
||||
// ! exagerated delay
|
||||
|
|
@ -112,14 +132,20 @@ class PersonalizedView extends _$PersonalizedView {
|
|||
.getPersonalized(libraryId: apiSettings.activeLibraryId!);
|
||||
// debugPrint('personalizedView: ${res!.map((e) => e).toSet()}');
|
||||
// save to cache
|
||||
final newFile = await apiResponseCacheManager.putFile(
|
||||
key,
|
||||
utf8.encode(jsonEncode(res)),
|
||||
fileExtension: 'json',
|
||||
key: key,
|
||||
);
|
||||
_logger.fine('writing to cache: $newFile');
|
||||
yield res!;
|
||||
if (res != null) {
|
||||
final newFile = await apiResponseCacheManager.putFile(
|
||||
key,
|
||||
utf8.encode(jsonEncode(res)),
|
||||
fileExtension: 'json',
|
||||
key: key,
|
||||
);
|
||||
_logger.fine('writing to cache: $newFile');
|
||||
yield res;
|
||||
} else {
|
||||
_logger.warning('failed to fetch personalized view');
|
||||
yield [];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// method to force refresh the view and ignore the cache
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ final authenticatedApiProvider = Provider<AudiobookshelfApi>.internal(
|
|||
);
|
||||
|
||||
typedef AuthenticatedApiRef = ProviderRef<AudiobookshelfApi>;
|
||||
String _$isServerAliveHash() => r'f839350795fbdeb0ca1d5f0c84a9065cac4dd40a';
|
||||
String _$isServerAliveHash() => r'6ff90b6e0febd2cd4a4d3a5209a59afc778cd3b6';
|
||||
|
||||
/// ping the server to check if it is reachable
|
||||
///
|
||||
|
|
@ -327,6 +327,166 @@ class _IsServerAliveProviderElement
|
|||
String get address => (origin as IsServerAliveProvider).address;
|
||||
}
|
||||
|
||||
String _$serverStatusHash() => r'2739906a1862d09b098588ebd16749a09032ee99';
|
||||
|
||||
/// fetch status of server
|
||||
///
|
||||
/// Copied from [serverStatus].
|
||||
@ProviderFor(serverStatus)
|
||||
const serverStatusProvider = ServerStatusFamily();
|
||||
|
||||
/// fetch status of server
|
||||
///
|
||||
/// Copied from [serverStatus].
|
||||
class ServerStatusFamily extends Family<AsyncValue<ServerStatusResponse?>> {
|
||||
/// fetch status of server
|
||||
///
|
||||
/// Copied from [serverStatus].
|
||||
const ServerStatusFamily();
|
||||
|
||||
/// fetch status of server
|
||||
///
|
||||
/// Copied from [serverStatus].
|
||||
ServerStatusProvider call(
|
||||
Uri baseUrl, [
|
||||
void Function(Response, [Object?])? responseErrorHandler,
|
||||
]) {
|
||||
return ServerStatusProvider(
|
||||
baseUrl,
|
||||
responseErrorHandler,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
ServerStatusProvider getProviderOverride(
|
||||
covariant ServerStatusProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.baseUrl,
|
||||
provider.responseErrorHandler,
|
||||
);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'serverStatusProvider';
|
||||
}
|
||||
|
||||
/// fetch status of server
|
||||
///
|
||||
/// Copied from [serverStatus].
|
||||
class ServerStatusProvider
|
||||
extends AutoDisposeFutureProvider<ServerStatusResponse?> {
|
||||
/// fetch status of server
|
||||
///
|
||||
/// Copied from [serverStatus].
|
||||
ServerStatusProvider(
|
||||
Uri baseUrl, [
|
||||
void Function(Response, [Object?])? responseErrorHandler,
|
||||
]) : this._internal(
|
||||
(ref) => serverStatus(
|
||||
ref as ServerStatusRef,
|
||||
baseUrl,
|
||||
responseErrorHandler,
|
||||
),
|
||||
from: serverStatusProvider,
|
||||
name: r'serverStatusProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$serverStatusHash,
|
||||
dependencies: ServerStatusFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
ServerStatusFamily._allTransitiveDependencies,
|
||||
baseUrl: baseUrl,
|
||||
responseErrorHandler: responseErrorHandler,
|
||||
);
|
||||
|
||||
ServerStatusProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.baseUrl,
|
||||
required this.responseErrorHandler,
|
||||
}) : super.internal();
|
||||
|
||||
final Uri baseUrl;
|
||||
final void Function(Response, [Object?])? responseErrorHandler;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
FutureOr<ServerStatusResponse?> Function(ServerStatusRef provider) create,
|
||||
) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: ServerStatusProvider._internal(
|
||||
(ref) => create(ref as ServerStatusRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
baseUrl: baseUrl,
|
||||
responseErrorHandler: responseErrorHandler,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeFutureProviderElement<ServerStatusResponse?> createElement() {
|
||||
return _ServerStatusProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is ServerStatusProvider &&
|
||||
other.baseUrl == baseUrl &&
|
||||
other.responseErrorHandler == responseErrorHandler;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, baseUrl.hashCode);
|
||||
hash = _SystemHash.combine(hash, responseErrorHandler.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
mixin ServerStatusRef on AutoDisposeFutureProviderRef<ServerStatusResponse?> {
|
||||
/// The parameter `baseUrl` of this provider.
|
||||
Uri get baseUrl;
|
||||
|
||||
/// The parameter `responseErrorHandler` of this provider.
|
||||
void Function(Response, [Object?])? get responseErrorHandler;
|
||||
}
|
||||
|
||||
class _ServerStatusProviderElement
|
||||
extends AutoDisposeFutureProviderElement<ServerStatusResponse?>
|
||||
with ServerStatusRef {
|
||||
_ServerStatusProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
Uri get baseUrl => (origin as ServerStatusProvider).baseUrl;
|
||||
@override
|
||||
void Function(Response, [Object?])? get responseErrorHandler =>
|
||||
(origin as ServerStatusProvider).responseErrorHandler;
|
||||
}
|
||||
|
||||
String _$fetchContinueListeningHash() =>
|
||||
r'f65fe3ac3a31b8ac074330525c5d2cc4b526802d';
|
||||
|
||||
|
|
@ -361,7 +521,7 @@ final meProvider = AutoDisposeFutureProvider<User>.internal(
|
|||
);
|
||||
|
||||
typedef MeRef = AutoDisposeFutureProviderRef<User>;
|
||||
String _$personalizedViewHash() => r'dada8d72845ffd516f731f88193941f7ebdd47ed';
|
||||
String _$personalizedViewHash() => r'4c392ece4650bdc36d7195a0ddb8810e8fe4caa9';
|
||||
|
||||
/// fetch the personalized view
|
||||
///
|
||||
|
|
|
|||
|
|
@ -52,8 +52,16 @@ class AuthenticatedUser extends _$AuthenticatedUser {
|
|||
_logger.fine('writing state to box: $state');
|
||||
}
|
||||
|
||||
void addUser(model.AuthenticatedUser user) {
|
||||
void addUser(model.AuthenticatedUser user, {bool setActive = false}) {
|
||||
state = state..add(user);
|
||||
if (setActive) {
|
||||
final apiSettings = ref.read(apiSettingsProvider);
|
||||
ref.read(apiSettingsProvider.notifier).updateState(
|
||||
apiSettings.copyWith(
|
||||
activeUser: user,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void removeUsersOfServer(AudiobookShelfServer registeredServer) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'authenticated_user_provider.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$authenticatedUserHash() => r'8578d7fda1755ecacce6853076da4149e4ebe3e7';
|
||||
String _$authenticatedUserHash() => r'308f19b33ae04af6340fb83167fa64aa23400a09';
|
||||
|
||||
/// provides with a set of authenticated users
|
||||
///
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue