summaryrefslogtreecommitdiff
path: root/player-android/lib/providers/api_client_provider.dart
blob: 27bd62f30aa6d9661e3a80acc0a3e02b4c60f23b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import 'package:cookie_jar/cookie_jar.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../api/dio_client.dart';
import '../api/dio_player_api_client.dart';
import '../api/player_api_client.dart';
import '../navigation_key.dart';

/// Base URL for the player-server API, resolved at compile time via
/// the PLAYER_BASE_URL environment variable (or the default below).
///
/// Declared as a package-level identifier (no underscore) so it can be
/// shared by [publicApiClientProvider] in [public_api_client_provider.dart].
/// The value is set once at compile time and never changes at runtime,
/// making it safe to share across providers.
///
/// In production this is injected via `--dart-define=PLAYER_BASE_URL=...`.
/// The default points to the Android emulator host loopback address so the
/// app is runnable out-of-the-box without extra configuration.
const kPlayerBaseUrl = String.fromEnvironment(
  'PLAYER_BASE_URL',
  defaultValue: 'http://10.0.2.2:8080',
);

/// Provides the production [TokenStorage] backed by the OS keychain.
///
/// Riverpod keeps a single instance for the lifetime of [ProviderScope], so
/// there is exactly one [SecureTokenStorage] in the app — consistent with the
/// singleton intent of flutter_secure_storage.
final tokenStorageProvider = Provider<TokenStorage>((ref) {
  return SecureTokenStorage();
});

/// Provides a fully configured [PlayerApiClient] wired with bearer-token
/// injection and global 401 → /login redirect.
///
/// Depends on [tokenStorageProvider] and [navigatorKey] (both singletons) so
/// the same [Dio] instance is reused for every call site — avoiding redundant
/// interceptor stacks.
// Single shared DioClient instance: cached as a Riverpod Provider so both the
// API client and the cookie-jar provider observe the same cookie store.  The
// API client uses Dio; ExoPlayer/video_player/CachedNetworkImage bypass Dio
// and need the cookie jar to attach the session cookie manually.
final _dioClientProvider = Provider<DioClient>((ref) {
  final storage = ref.watch(tokenStorageProvider);
  return DioClient(
    baseUrl: Uri.parse(kPlayerBaseUrl),
    storage: storage,
    // Share the navigator key with go_router so 401 redirects go through the
    // correct router instance rather than the raw Navigator.
    navigatorKey: navigatorKey,
    loginRoute: '/login',
  );
});

final apiClientProvider = Provider<PlayerApiClient>((ref) {
  // Use DioPlayerApiClient — the concrete implementation that maps every
  // PlayerApiClient method to a real HTTP call via Dio.  The base class now
  // acts as the public interface (dependency inversion); callers depend on
  // PlayerApiClient, not on this concrete class.
  return DioPlayerApiClient(dio: ref.watch(_dioClientProvider).dio);
});

/// Provides the same [CookieJar] backing [apiClientProvider]'s Dio stack so
/// non-Dio HTTP clients (ExoPlayer, video_player, CachedNetworkImage) can
/// authenticate streaming/thumbnail requests using the same session cookie.
final cookieJarProvider = Provider<CookieJar>((ref) {
  return ref.watch(_dioClientProvider).cookieJar;
});