diff --git a/README.md b/README.md index 98d7923..b2d4354 100644 --- a/README.md +++ b/README.md @@ -337,7 +337,7 @@ shows its version and has "Update now"), so there is nothing to install. Sites that refuse guests (Bilibili answers "HTTP Error 412", YouTube may ask to sign in) need cookies: Settings → Player → yt-dlp → Cookies reads them from a browser, or from a cookies.txt or a cookie extension's JSON -export (picked or dropped; tick the sites to keep). They stay on this +export (picked or dropped; every site in it is kept). They stay on this computer and are used by its yt-dlp only (your own playback, "through me", and a server you host). Cookies log yt-dlp in as you on those sites: keep the file private, and for YouTube consider a throwaway diff --git a/apps/desktop/lib/src/shell/app_shell.dart b/apps/desktop/lib/src/shell/app_shell.dart index 0482dc0..60faff9 100644 --- a/apps/desktop/lib/src/shell/app_shell.dart +++ b/apps/desktop/lib/src/shell/app_shell.dart @@ -501,7 +501,9 @@ class AppShellState extends State with SingleTickerProviderStateMixin _selectTab(ShellTab.library); } + // Off on Settings: its own drop targets (cookies) take the files there. Widget _dropTarget(Widget child) => DropTarget( + enable: _tab != ShellTab.settings, onDragEntered: (_) => setState(() => _dragging = _onTop), onDragExited: (_) => setState(() => _dragging = false), onDragDone: (d) { diff --git a/apps/desktop/lib/src/shell/settings_tab.dart b/apps/desktop/lib/src/shell/settings_tab.dart index 77bd812..339ba1f 100644 --- a/apps/desktop/lib/src/shell/settings_tab.dart +++ b/apps/desktop/lib/src/shell/settings_tab.dart @@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; import 'package:fox_utils/fox_utils.dart' show DesktopDropX, DropTarget; import 'package:signals/signals_flutter.dart'; import 'package:sync_relay_core/core.dart' - show CookieBrowser, CookieFile, NetscapeCookie, YtDlp, YtDlpCookies, cookieBrowsers, cookiesBySite, parseCookies, suggestedCookieSites; + show CookieBrowser, CookieFile, NetscapeCookie, YtDlp, YtDlpCookies, cookieBrowsers, parseCookies; import '../controls/player_settings.dart'; import '../jellyfin/accounts.dart'; @@ -461,7 +461,8 @@ class _YtDlpCookieSettingsState extends State { } String get _summary => switch (_source) { - CookieFile f => '${f.count} cookie${f.count == 1 ? '' : 's'} for ${f.sites.join(', ')}', + CookieFile f => '${f.count} cookie${f.count == 1 ? '' : 's'} for ' + '${f.sites.length <= 3 ? f.sites.join(', ') : '${f.sites.length} sites'}', CookieBrowser b => 'From ${b.browser}${b.profile.isEmpty ? '' : ' (profile ${b.profile})'}', _ => 'No cookies: yt-dlp visits sites as a guest', }; @@ -494,14 +495,9 @@ class _YtDlpCookieSettingsState extends State { return; } if (!mounted) return; - final bySite = cookiesBySite(all); - final sites = await showDialog>( - context: context, - builder: (_) => _CookieSitesDialog(bySite: {for (final e in bySite.entries) e.key: e.value.length}), - ); - if (sites == null || sites.isEmpty) return; + // Every site in the file: an export is "my logins", not a pick list. try { - await widget.cookies.saveFile([for (final s in sites) ...?bySite[s]]); + await widget.cookies.saveFile(all); _reload(); } catch (e) { _reload('Could not keep the cookies: $e'); @@ -586,7 +582,7 @@ class _YtDlpCookieSettingsState extends State { const SizedBox(height: 8), Text( 'For sites that refuse guests (Bilibili, YouTube asking to sign in). Drop a cookies.txt or a ' - 'cookie-extension JSON export here, or pick one; only the sites you tick are kept, on this computer only. ' + 'cookie-extension JSON export here, or pick one; all of it is kept, on this computer only. ' 'Cookies log yt-dlp in as you on those sites. Keep the file private; for YouTube consider a ' 'throwaway account (Google may flag accounts used with yt-dlp).', style: TextStyle(color: scheme.onSurfaceVariant, fontSize: 12), @@ -598,51 +594,6 @@ class _YtDlpCookieSettingsState extends State { } } -/// Which sites of an imported cookie file to keep; pops the ticked ones. -class _CookieSitesDialog extends StatefulWidget { - /// Site -> number of cookies. - final Map bySite; - const _CookieSitesDialog({required this.bySite}); - - @override - State<_CookieSitesDialog> createState() => _CookieSitesDialogState(); -} - -class _CookieSitesDialogState extends State<_CookieSitesDialog> { - late final _on = {for (final s in widget.bySite.keys) if (suggestedCookieSites.contains(s)) s}; - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: const Text('Keep cookies for'), - content: SizedBox( - width: 380, - child: ListView( - shrinkWrap: true, - children: [ - for (final e in widget.bySite.entries) - CheckboxListTile( - dense: true, - value: _on.contains(e.key), - title: Text(e.key), - subtitle: Text('${e.value} cookie${e.value == 1 ? '' : 's'}'), - onChanged: (v) => setState(() => v == true ? _on.add(e.key) : _on.remove(e.key)), - ), - ], - ), - ), - actions: [ - TextButton(onPressed: () => Navigator.pop(context), child: const Text('Cancel')), - FilledButton( - onPressed: _on.isEmpty ? null : () => Navigator.pop(context, {..._on}), - child: const Text('Keep'), - ), - ], - ); - } -} - -/// Which browser (and profile) yt-dlp reads cookies from. class _CookieBrowserDialog extends StatefulWidget { final CookieBrowser? initial; const _CookieBrowserDialog({this.initial}); diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index e369b7f..7561f76 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -560,8 +560,8 @@ all three link modes. Nothing is bundled or installed by hand. registrable domain by a short rule, with google.com filed under youtube.com and bilibili's CDNs under bilibili.com). The app: Settings → Player → yt-dlp → Cookies (a browser and profile, or a file picked or - dropped, then the sites to keep, bilibili.com and youtube.com ticked - when present; "Remove cookies"); its hosted server shares the folder, + dropped, kept whole: every site in it; "Remove cookies"; the shell's + own drop target is off on Settings so only this box takes the file); its hosted server shares the folder, so it uses them too. mpv's hook gets the same as `ytdl-raw-options` (`cookies=%N%` / `cookies-from-browser=%N%`, length-quoted), re-set before each external link when it changed. The dedicated