import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'shared/settings_persistence.dart'; enum TmdbAuthMode { bearer, apiKey } enum TmdbAccessMode { official, smAccount, } class TmdbSettingsState { static const officialApiBaseUrl = 'https://api.themoviedb.org/3'; static const officialImageBaseUrl = 'https://image.tmdb.org/t/p'; static const smAccountApiBaseUrl = 'https://your-server.example'; final bool enabled; final TmdbAccessMode accessMode; final TmdbAuthMode authMode; final String accessToken; final String apiKey; final String language; const TmdbSettingsState({ this.enabled = false, this.accessMode = TmdbAccessMode.official, this.authMode = TmdbAuthMode.apiKey, this.accessToken = '', this.apiKey = '', this.language = 'zh-CN', }); bool get isConfigured => switch (accessMode) { TmdbAccessMode.smAccount => true, TmdbAccessMode.official => switch (authMode) { TmdbAuthMode.bearer => accessToken.isNotEmpty, TmdbAuthMode.apiKey => apiKey.isNotEmpty, }, }; bool get canRequest => enabled && isConfigured; bool canRequestWithAccount({required bool dataPlaneReady}) => switch (accessMode) { TmdbAccessMode.official => canRequest, TmdbAccessMode.smAccount => enabled && dataPlaneReady, }; String get effectiveApiBaseUrl => switch (accessMode) { TmdbAccessMode.smAccount => smAccountApiBaseUrl, TmdbAccessMode.official => officialApiBaseUrl, }; String get effectiveImageBaseUrl => officialImageBaseUrl; String get effectiveAccessToken => accessMode == TmdbAccessMode.official && authMode == TmdbAuthMode.bearer ? accessToken : ''; String get effectiveApiKey => accessMode == TmdbAccessMode.official && authMode == TmdbAuthMode.apiKey ? apiKey : ''; TmdbSettingsState copyWith({ bool? enabled, TmdbAccessMode? accessMode, TmdbAuthMode? authMode, String? accessToken, String? apiKey, String? language, }) => TmdbSettingsState( enabled: enabled ?? this.enabled, accessMode: accessMode ?? this.accessMode, authMode: authMode ?? this.authMode, accessToken: accessToken ?? this.accessToken, apiKey: apiKey ?? this.apiKey, language: language ?? this.language, ); } class TmdbSettingsNotifier extends AsyncNotifier with SettingsPersistence { static const _keyEnabled = 'smplayer.tmdb_enabled'; static const _keyAccessMode = 'smplayer.tmdb_access_mode'; static const _keyAuthMode = 'smplayer.tmdb_auth_mode'; static const _keyAccessToken = 'smplayer.tmdb_access_token'; static const _keyApiKey = 'smplayer.tmdb_api_key'; static const _keyLanguage = 'smplayer.tmdb_language'; @override Future build() async { final prefs = await getPrefs(); final accessToken = prefs.getString(_keyAccessToken)?.trim() ?? ''; final apiKey = prefs.getString(_keyApiKey)?.trim() ?? ''; return TmdbSettingsState( enabled: prefs.getBool(_keyEnabled) ?? false, accessMode: _parseAccessMode(prefs.getString(_keyAccessMode)), authMode: _parseAuthMode( prefs.getString(_keyAuthMode), hasLegacyAccessToken: accessToken.isNotEmpty, ), accessToken: accessToken, apiKey: apiKey, language: prefs.getString(_keyLanguage) ?? 'zh-CN', ); } Future setEnabled(bool v) async { final current = state.value ?? const TmdbSettingsState(); state = AsyncValue.data(current.copyWith(enabled: v)); await persistField(_keyEnabled, v); } Future setAccessMode(TmdbAccessMode mode) async { final current = state.value ?? const TmdbSettingsState(); state = AsyncValue.data(current.copyWith(accessMode: mode)); await persistField(_keyAccessMode, mode.name); } Future enableSmAccountProxy() async { final current = state.value ?? await future; state = AsyncValue.data( current.copyWith(enabled: true, accessMode: TmdbAccessMode.smAccount), ); await persistFields({ _keyEnabled: true, _keyAccessMode: TmdbAccessMode.smAccount.name, }); } Future setAuthMode(TmdbAuthMode mode) async { final current = state.value ?? const TmdbSettingsState(); state = AsyncValue.data(current.copyWith(authMode: mode)); await persistField(_keyAuthMode, mode.name); } Future setAccessToken(String token) async { final trimmed = token.trim(); final current = state.value ?? const TmdbSettingsState(); state = AsyncValue.data(current.copyWith(accessToken: trimmed)); await persistField(_keyAccessToken, trimmed); } Future setApiKey(String key) async { final trimmed = key.trim(); final current = state.value ?? const TmdbSettingsState(); state = AsyncValue.data(current.copyWith(apiKey: trimmed)); await persistField(_keyApiKey, trimmed); } Future setLanguage(String lang) async { final current = state.value ?? const TmdbSettingsState(); state = AsyncValue.data(current.copyWith(language: lang)); await persistField(_keyLanguage, lang); } } final tmdbSettingsProvider = AsyncNotifierProvider( TmdbSettingsNotifier.new, ); TmdbAccessMode _parseAccessMode(String? value) => TmdbAccessMode.values .firstWhere((m) => m.name == value, orElse: () => TmdbAccessMode.official); TmdbAuthMode _parseAuthMode( String? value, { bool hasLegacyAccessToken = false, }) { if (value == null && hasLegacyAccessToken) { return TmdbAuthMode.bearer; } return TmdbAuthMode.values.firstWhere( (mode) => mode.name == value, orElse: () => TmdbAuthMode.apiKey, ); }