From 8d5bafe62ec6c9b2502f3952d1aaf6fc927e4769 Mon Sep 17 00:00:00 2001 From: Julian Dice <19397727+windoze95@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:03:55 -0500 Subject: [PATCH] feat(settings): show the actual reason cookies aren't working MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Surface the backend's last_error in the YouTube Account panel: when cookies are present but extraction keeps failing (bad format / expired / age gate), show the real yt-dlp message ("…does not look like a Netscape format cookies file", etc.) under a "Cookies aren't working — re-export and paste fresh ones" warning, instead of a vague "expired". analyze clean, 156 tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/screens/settings_screen.dart | 43 ++++++++++++++++++++++++-------- lib/services/api_service.dart | 13 +++++----- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index c3ccb59..e5d067e 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -343,7 +343,8 @@ class _YoutubeCookiesSection extends ConsumerStatefulWidget { class _YoutubeCookiesSectionState extends ConsumerState<_YoutubeCookiesSection> { final _controller = TextEditingController(); - ({bool configured, bool stale, String? updatedAt})? _status; + ({bool configured, bool stale, String? updatedAt, String? lastError})? + _status; bool _loading = true; bool _busy = false; String? _error; @@ -521,7 +522,9 @@ class _YoutubeCookiesSectionState ); } - Widget _statusRow(({bool configured, bool stale, String? updatedAt})? s) { + Widget _statusRow( + ({bool configured, bool stale, String? updatedAt, String? lastError})? s, + ) { if (s == null || !s.configured) { return const Row( children: [ @@ -532,17 +535,35 @@ class _YoutubeCookiesSectionState ); } if (s.stale) { - return const Row( + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Icon( - Icons.warning_amber_rounded, - color: NullFeedTheme.errorColor, - size: 18, - ), - SizedBox(width: 8), - Expanded( - child: Text('Cookies expired — paste fresh ones to keep playing'), + const Row( + children: [ + Icon( + Icons.warning_amber_rounded, + color: NullFeedTheme.errorColor, + size: 18, + ), + SizedBox(width: 8), + Expanded( + child: Text( + "Cookies aren't working — re-export and paste fresh ones", + ), + ), + ], ), + if (s.lastError != null) ...[ + const SizedBox(height: 6), + Text( + s.lastError!, + style: const TextStyle( + color: NullFeedTheme.textMuted, + fontSize: 11, + fontFamily: 'monospace', + ), + ), + ], ], ); } diff --git a/lib/services/api_service.dart b/lib/services/api_service.dart index 1e5709b..604a068 100644 --- a/lib/services/api_service.dart +++ b/lib/services/api_service.dart @@ -453,23 +453,22 @@ class ApiService { }); // YouTube cookies (admin only) — enables age-restricted / members-only videos. - ({bool configured, bool stale, String? updatedAt}) _parseCookieStatus( - Map data, - ) => ( + ({bool configured, bool stale, String? updatedAt, String? lastError}) + _parseCookieStatus(Map data) => ( configured: data['configured'] == true, stale: data['stale'] == true, updatedAt: data['updated_at'] as String?, + lastError: data['last_error'] as String?, ); - Future<({bool configured, bool stale, String? updatedAt})> + Future<({bool configured, bool stale, String? updatedAt, String? lastError})> getYoutubeCookiesStatus() => _guard(() async { final r = await _dio.get('$_baseUrl${AppConstants.settingsYoutubeCookies}'); return _parseCookieStatus(r.data as Map); }); - Future<({bool configured, bool stale, String? updatedAt})> saveYoutubeCookies( - String cookies, - ) => _guard(() async { + Future<({bool configured, bool stale, String? updatedAt, String? lastError})> + saveYoutubeCookies(String cookies) => _guard(() async { final r = await _dio.put( '$_baseUrl${AppConstants.settingsYoutubeCookies}', data: {'cookies': cookies},