Skip to content

Title: Fix a shuffle bug on desktop [fix]#9

Merged
UtopiaXC merged 2 commits into
masterfrom
fix_shuffle_on_pc
Feb 10, 2026
Merged

Title: Fix a shuffle bug on desktop [fix]#9
UtopiaXC merged 2 commits into
masterfrom
fix_shuffle_on_pc

Conversation

@UtopiaXC
Copy link
Copy Markdown
Owner

@UtopiaXC UtopiaXC commented Feb 10, 2026

Details: Fix a shuffle bug on desktop
Type: Bugfix.
Test: Local Passed.
Related Issue: #7 #8
Closes #7 #8

Summary by CodeRabbit

发布说明

  • 新功能

    • 移动设备新增网页登录流程(内置浏览器)与手动确认选项
    • 新增网页登录界面和相关提示文案
    • 增加对桌面 macOS/Windows webview 插件支持
  • 多语言支持

    • 新增中英本地化字符串,覆盖网页登录相关提示与按钮
  • 改进

    • 改进播放器在桌面/移动上的循环与随机行为,支持队列重洗与独立队列循环控制
    • 同步 Now Playing 元信息以改善移动端体验

Details: Fix a shuffle bug on desktop
Type: Bugfix.
Test: Local Passed.
Related Issue: N/A.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 10, 2026

📝 Walkthrough

Walkthrough

此PR新增移动端Web登录(应用内浏览器)实现与UI,增加相关本地化字符串、常量与平台插件,并调整播放器队列循环与重排逻辑以支持桌面/移动差异与重新洗牌功能。

Changes

Cohort / File(s) Summary
Web 登录页面
lib/widgets/login/web_login_page.dart
新增 WebLoginPage,实现应用内浏览器登录:加载登录 URL、检测重定向、500ms Cookie 轮询(最多10次)、合并并验证关键 Cookie、保存到多个 CookieJar,完成后触发 AuthProvider 登录并返回结果。
登录对话框与 UI 调整
lib/widgets/login/login_dialog.dart
在移动端增加第三个 tab 用于 Web 登录(TabController 长度动态为2或3),根据平台条件渲染 Web 登录视图并在成功时关闭对话框。
连接常量
lib/connection/utils/api.dart, lib/connection/utils/constants.dart
新增 Web 登录 URL 常量 urlWebLogin 与成功重定向列表 webLoginSuccessUrls;新增 userAgentIOSuserAgentAndroid 两个设备专用 User-Agent 常量。
本地化
lib/l10n/intl_en.arb, lib/l10n/intl_zh.arb
新增8条本地化键(weight_login_*),涵盖 Web 登录按钮、提示、手动确认、Cookie/HttpOnly 警告与不支持提示(英文与中文)。
播放器与音频服务
lib/providers/player_provider.dart, lib/services/audio/audio_player_service.dart, lib/services/database_service.dart
播放模式切换时改进索引管理与预期索引逻辑;引入桌面判定以在桌面禁用 LoopMode.all 并使用新标志 _shouldLoopQueue(并新增 setShouldLoopQueue);新增 DatabaseService.reshufflePlaylist() 在数据库中重排播放列表顺序;在移动端索引更新时同步 iOS Now Playing 信息。
平台插件与依赖
pubspec.yaml, macos/Flutter/GeneratedPluginRegistrant.swift, windows/flutter/generated_plugin_registrant.cc, windows/flutter/generated_plugins.cmake
添加依赖 webview_flutterflutter_inappwebview;在 macOS 与 Windows 的生成注册中加入 InAppWebView / WebView 插件的注册与头文件。

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant LoginDialog as 登录对话框
    participant WebLoginPage as Web登录页面
    participant InAppWebView as 应用内浏览器
    participant AuthServer as 认证服务器
    participant CookiePoller as Cookie轮询器
    participant AuthProvider as 认证提供者
    participant CookieJar as Cookie存储

    User->>LoginDialog: 选择 Web 登录
    LoginDialog->>WebLoginPage: 打开 Web 登录页面
    WebLoginPage->>InAppWebView: 加载 urlWebLogin
    InAppWebView->>AuthServer: 提交认证请求
    AuthServer-->>InAppWebView: 返回/重定向(可能到 success URL)
    InAppWebView-->>WebLoginPage: 检测到重定向
    WebLoginPage->>CookiePoller: 启动轮询(跨域)
    CookiePoller->>AuthServer: 请求各域 Cookie
    AuthServer-->>CookiePoller: 返回 Cookie 集合
    CookiePoller->>AuthProvider: 验证并合并关键 Cookie
    AuthProvider->>CookieJar: 保存 Cookie 到多个 Jar
    CookieJar-->>AuthProvider: 保存完成
    AuthProvider-->>WebLoginPage: 登录成功回调
    WebLoginPage-->>LoginDialog: 关闭并返回成功
    LoginDialog-->>User: 登录完成
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

Poem

🐰 我在树下啃胡萝,浏览器里见新歌;
Cookie 跳舞又唱和,登录成功心欢喜;
播放队列轻轻摇,桌面移动各自好;
小兔挥耳来庆祝, 新功能上线乐陶陶~

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning PR标题声称修复桌面端的shuffle bug,但变更集包含大量与此无关的功能:新增Web登录页面(200+行)、多平台WebView集成、国际化字符串、API常量等,这些都远超shuffle bug修复的范围。 标题应准确反映所有重要变更。建议改为'Add web login support and fix shuffle bug on desktop'或类似表述,或将此PR拆分为多个聚焦的PR。
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix_shuffle_on_pc

No actionable comments were generated in the recent review. 🎉


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @UtopiaXC, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request primarily resolves a shuffle playback issue on desktop platforms, ensuring a more consistent and reliable audio experience. Additionally, it introduces a new web login method specifically for mobile users, offering an alternative and potentially more convenient authentication flow by utilizing an embedded web browser to capture necessary login cookies. These changes enhance both the stability and user experience of the application.

Highlights

  • Desktop Shuffle Bug Fix: Corrected shuffle playback behavior on desktop platforms by ensuring the playlist is properly reshuffled when entering shuffle mode and managing loop behavior through a dedicated flag.
  • Web Login Implementation: Introduced a new web-based login method for mobile platforms, allowing users to log in via an embedded web browser and extract session cookies. This includes new UI components, API endpoints, and localization.
  • Internationalization Updates: Added new localization strings in both English and Chinese to support the newly implemented web login feature, providing clear messages for login steps, warnings, and confirmations.
  • Dependency Additions: Integrated flutter_inappwebview and webview_flutter packages to enable the embedded web browser functionality required for the new web login method.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • lib/connection/utils/api.dart
    • Added new static constants for web login URL and success redirect URLs.
  • lib/connection/utils/constants.dart
    • Added new static constants for iOS and Android user agent strings.
  • lib/generated/intl/messages_en.dart
    • Added new English localization messages related to web login, including confirm login, web login title, button text, hint, and various warning messages.
  • lib/generated/intl/messages_zh.dart
    • Added new Chinese localization messages related to web login, including confirm login, web login title, button text, hint, and various warning messages.
  • lib/generated/l10n.dart
    • Added new getter methods for web login related internationalized messages.
  • lib/l10n/intl_en.arb
    • Added new keys and values for English web login messages.
  • lib/l10n/intl_zh.arb
    • Added new keys and values for Chinese web login messages.
  • lib/providers/player_provider.dart
    • Imported dart:io and flutter/foundation.dart for platform-specific logic.
    • Added logic to reshuffle the playlist via _databaseService.reshufflePlaylist() when switching to shuffle play mode.
    • Removed outdated comments regarding iOS just_audio_media_kit and expected index protection.
    • Modified _setPlayerLoopMode to handle desktop platforms differently for LoopMode.all and introduced _audioPlayerService.setShouldLoopQueue for explicit queue looping control.
  • lib/services/audio/audio_player_service.dart
    • Added a new private boolean field _shouldLoopQueue to control queue looping behavior.
    • Updated iOS Now Playing info when auto-advancing to the next song.
    • Modified the end-of-queue logic to use _shouldLoopQueue instead of _player.loopMode == LoopMode.all.
    • Added a public method setShouldLoopQueue to update the queue looping flag.
  • lib/services/database_service.dart
    • Added a new asynchronous method reshufflePlaylist to randomize the shuffle_order of songs in the database's playlist table.
  • lib/widgets/login/login_dialog.dart
    • Imported dart:io, flutter/foundation.dart, and WebLoginPage.
    • Introduced a _isMobile getter to conditionally enable web login.
    • Adjusted _tabController length based on whether the platform is mobile to include a web login tab.
    • Conditionally added a 'Web Login' tab and its corresponding view (_buildWebLoginView) for mobile platforms.
    • Implemented _buildWebLoginView to display a button that navigates to the WebLoginPage.
  • lib/widgets/login/web_login_page.dart
    • Added a new file implementing WebLoginPage for web-based login.
    • Utilized flutter_inappwebview to display the Bilibili login page.
    • Implemented logic to extract and save cookies upon successful login redirect.
    • Included platform-specific user agent selection for the webview.
    • Provided manual confirmation and refresh options within the web login page.
  • macos/Flutter/GeneratedPluginRegistrant.swift
    • Imported flutter_inappwebview_macos and webview_flutter_wkwebview.
    • Registered InAppWebViewFlutterPlugin and WebViewFlutterPlugin.
  • pubspec.lock
    • Updated package versions and added new entries for flutter_inappwebview and webview_flutter and their transitive dependencies.
  • pubspec.yaml
    • Added webview_flutter and flutter_inappwebview to dependencies.
    • Removed extraneous empty lines in the dev_dependencies section.
    • Added flutter_inappwebview configuration to disable it for Windows and macOS platforms.
  • windows/flutter/generated_plugin_registrant.cc
    • Included header for flutter_inappwebview_windows.
    • Registered FlutterInappwebviewWindowsPluginCApi.
  • windows/flutter/generated_plugins.cmake
    • Added flutter_inappwebview_windows to the list of Flutter plugins.
Activity
  • No specific activity (comments, reviews, progress updates) was provided in the context for this pull request.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request primarily fixes a shuffle bug on desktop by implementing a custom looping mechanism and re-shuffling the playlist when shuffle mode is activated. It also introduces a new web-based login feature for mobile platforms using an InAppWebView. A high-severity security issue has been identified: sensitive session cookies are logged to the application's debug log, which are persisted to disk and can be exported, creating a risk of session hijacking. Additionally, there is a high-severity suggestion to improve cookie handling robustness in the new web login feature and a medium-severity comment on a potential build configuration inconsistency. The shuffle bug fix and other changes appear solid and secure.

Comment thread lib/widgets/login/web_login_page.dart Outdated
Comment on lines +88 to +93
for (var cookie in allCookies) {
final displayValue = cookie.value.length > 10
? '${cookie.value.substring(0, 10)}...'
: cookie.value;
Log.d(_tag, 'Cookie: ${cookie.name}=$displayValue');
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

Sensitive session cookies are being logged to the debug log. Even though the values are truncated to 10 characters, this still leaks partial session data (like SESSDATA) and potentially full User IDs (DedeUserID). Since the application includes an 'Export Logs' feature in the settings, users might accidentally share their session credentials when providing logs for support or bug reporting.

Recommendation: Remove these log statements or ensure that sensitive cookie values are never logged. If logging is necessary for debugging, only log the cookie names.

      // Remove or comment out the following loop to avoid logging sensitive cookies
      /*
      Log.d(_tag, 'Found ${allCookies.length} cookies');
      for (var cookie in allCookies) {
        final displayValue = cookie.value.length > 10
            ? '${cookie.value.substring(0, 10)}...'
            : cookie.value;
        Log.d(_tag, 'Cookie: ${cookie.name}=$displayValue');
      }
      */

Comment on lines +77 to +83
final Map<String, io.Cookie> cookieMap = {};
for (var cookie in siteCookies) {
cookieMap[cookie.name] = io.Cookie(cookie.name, cookie.value);
}
for (var cookie in passportCookies) {
cookieMap[cookie.name] = io.Cookie(cookie.name, cookie.value);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation of merging cookies is lossy. It only preserves the name and value of the cookies, discarding other important attributes like domain, path, expires, and httpOnly. This can lead to incorrect cookie handling and potential authentication issues. It's better to preserve all relevant attributes when converting flutter_inappwebview's Cookie to dart:io's Cookie.

      final Map<String, io.Cookie> cookieMap = {};
      for (final c in [...siteCookies, ...passportCookies]) {
        cookieMap[c.name] = io.Cookie(c.name, c.value)
          ..domain = c.domain
          ..path = c.path
          ..expires = c.expiresDate != null
              ? DateTime.fromMillisecondsSinceEpoch(c.expiresDate!)
              : null
          ..httpOnly = c.isHttpOnly ?? false
          ..secure = c.isSecure ?? false;
      }

Comment thread pubspec.yaml Outdated
Comment on lines +136 to +140
flutter_inappwebview:
windows:
enabled: false
macos:
enabled: false
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's an inconsistency in the project configuration. You've disabled flutter_inappwebview for Windows and macOS here, but the generated plugin registration files (macos/Flutter/GeneratedPluginRegistrant.swift, windows/flutter/generated_plugin_registrant.cc) still include registration code for it. This might cause the plugin to be unnecessarily bundled in desktop builds, increasing their size. You might want to verify your build process, perhaps by running flutter clean followed by flutter pub get, to ensure the generated files reflect this configuration.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/services/audio/audio_player_service.dart (1)

486-509: ⚠️ Potential issue | 🔴 Critical

桌面端手动切歌 (playNext) 在队列末尾不会循环——回归 bug。

_handleDesktopAutoNext(Line 108)已正确使用 _shouldLoopQueue 来处理自动播放下一首时的队列循环。但 playNext() 在桌面端(Line 494)仍然检查 _player.loopMode == LoopMode.all

由于 player_provider.dart 中对桌面端的 loop/shuffle 模式设置了 LoopMode.off(而非 LoopMode.all),当用户在队列最后一首歌时手动点击"下一首",playNext 不会回绕到第一首。而 hasNext(在 player_provider.dart Line 773)对 loop/shuffle 模式返回 true,UI 上"下一首"按钮仍然可点击,但点击后无效果。

建议:在 playNext 中也使用 _shouldLoopQueue
     } else {
-        if (_player.loopMode == LoopMode.all) {
+        if (_shouldLoopQueue) {
           _currentIndex = 0;
           _indexController.add(0);
           await _playSingleDesktop(0);
         }
       }
🤖 Fix all issues with AI agents
In `@lib/widgets/login/web_login_page.dart`:
- Around line 87-93: The code currently logs cookie values (even truncated) in
the loop over allCookies; change the logging in the widget/page
(web_login_page.dart) to only log the cookie name and never output cookie.value
— update the loop that iterates over allCookies (variables: allCookies, cookie)
and the Log.d calls (using _tag) so they emit only something like the
cookie.name and count, removing any reference to cookie.value to avoid leaking
sensitive credentials like SESSDATA or DedeUserID.
- Around line 55-66: The Timer.periodic callback can reenter because it doesn't
await async work; modify _startPollingForCookies to prevent concurrent
_extractAndSaveCookies calls by adding a reentrancy guard (e.g., a private bool
like _isPolling) and check it at the start of the periodic callback, setting
_isPolling = true before awaiting _extractAndSaveCookies and clearing it in a
finally block; also keep the existing checks on _isLoginSuccess and attempts and
ensure _pollTimer is cancelled when _isLoginSuccess becomes true so cookies
aren't saved twice (references: _startPollingForCookies, _pollTimer,
Timer.periodic, _extractAndSaveCookies, _isLoginSuccess).
- Around line 118-128: Move the _isLoginSuccess assignment so it only flips to
true after all persistence and login steps complete: keep cancelling _pollTimer
as needed, but perform the Request()/await request.cookieJar and all await
cookieJar.saveFromResponse(Uri.parse(...), allCookies) calls and the
authProvider.login(...) call first, then set _isLoginSuccess = true; also ensure
any catch/finally resets _isLoginSuccess to false on failure so a retry is
possible. Reference: _isLoginSuccess, _pollTimer, Request,
cookieJar.saveFromResponse, authProvider.login.

In `@pubspec.yaml`:
- Around line 136-141: The pubspec disables flutter_inappwebview for
Windows/macOS but lib/widgets/login/web_login_page.dart still instantiates
InAppWebView (around line 177), which will crash on those platforms; either
re-enable the plugin for those platforms in pubspec.yaml or guard the usage in
WebLoginPage (or the widget/function that creates InAppWebView) with a platform
check (Platform.isWindows/Platform.isMacOS or kIsWeb/Theme.of(context).platform)
and provide an alternative web-login flow or fallback widget for unsupported
platforms; update the constructor/initialization in WebLoginPage to only create
InAppWebView when the platform is supported, and document the chosen approach in
pubspec or the widget code comments.
🧹 Nitpick comments (4)
pubspec.yaml (1)

56-57: PR 描述为修复 shuffle bug,但包含了大量 Web 登录相关的新依赖和功能代码。

webview_flutterflutter_inappwebview 是用于 Web 登录功能的新依赖,与 PR 标题描述的 "Fix a shuffle bug on desktop" 不符。建议将 Web 登录功能拆分到单独的 PR 中,以便于代码审查和问题追踪。

lib/connection/utils/constants.dart (1)

5-8: User-Agent 字符串中的版本号较旧,可能影响 Web 登录兼容性。

userAgentIOS 使用 iOS 16,userAgentAndroid 使用 Android 13 / Chrome 116(2023 年 8 月发布)。部分网站可能会对过旧的 User-Agent 进行风控限制。建议定期更新这些字符串,或考虑动态获取设备的实际 User-Agent。

lib/widgets/login/login_dialog.dart (1)

268-302: _buildWebLoginViewNavigator.push 返回后缺少对 context 挂载状态的二次确认。

Line 284 的 await Navigator.of(context).push<bool>(...) 之后,Line 289 虽然检查了 mounted,但在 awaitmounted 检查之间,context 可能已经失效。当前的实现已经用了 mounted 检查,但建议使用更明确的模式:

建议的改进
             onPressed: () async {
               final result = await Navigator.of(context).push<bool>(
                 MaterialPageRoute(
                   builder: (context) => const WebLoginPage(),
                 ),
               );
-              if (result == true && mounted) {
+              if (!mounted) return;
+              if (result == true) {
                 Navigator.of(context).pop();
               }
             },
lib/providers/player_provider.dart (1)

527-535: 1 秒的保护超时机制可用但略显脆弱。

finally 块中的 1000ms 延迟作为安全网防止 _isTogglingPlayMode 永久卡住是可以接受的。但在极端情况下(如设备性能不足或 just_audio 事件延迟),这个窗口可能不够长或过长。

建议考虑将超时时长提取为常量,方便后续调优:

建议
+  static const Duration _toggleProtectionTimeout = Duration(milliseconds: 1000);
+
   // ...in togglePlayMode:
       } finally {
-        await Future.delayed(const Duration(milliseconds: 1000));
+        await Future.delayed(_toggleProtectionTimeout);

Comment on lines +55 to +66
void _startPollingForCookies() {
_pollTimer?.cancel();
int attempts = 0;
_pollTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) async {
attempts++;
if (attempts > 10 || _isLoginSuccess) {
timer.cancel();
return;
}
await _extractAndSaveCookies(showError: false);
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Timer.periodic 的异步回调可能导致并发重入问题。

Timer.periodic 不会等待异步回调完成。如果 _extractAndSaveCookies 执行时间超过 500ms,多个调用可能会重叠执行。_isLoginSuccess 检查在 Line 69 和赋值在 Line 120 之间存在竞态窗口,可能导致 cookie 被重复保存。

建议的改进:增加并发保护
   void _startPollingForCookies() {
     _pollTimer?.cancel();
     int attempts = 0;
+    bool isPolling = false;
     _pollTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) async {
       attempts++;
       if (attempts > 10 || _isLoginSuccess) {
         timer.cancel();
         return;
       }
+      if (isPolling) return;
+      isPolling = true;
-      await _extractAndSaveCookies(showError: false);
+      try {
+        await _extractAndSaveCookies(showError: false);
+      } finally {
+        isPolling = false;
+      }
     });
   }
🤖 Prompt for AI Agents
In `@lib/widgets/login/web_login_page.dart` around lines 55 - 66, The
Timer.periodic callback can reenter because it doesn't await async work; modify
_startPollingForCookies to prevent concurrent _extractAndSaveCookies calls by
adding a reentrancy guard (e.g., a private bool like _isPolling) and check it at
the start of the periodic callback, setting _isPolling = true before awaiting
_extractAndSaveCookies and clearing it in a finally block; also keep the
existing checks on _isLoginSuccess and attempts and ensure _pollTimer is
cancelled when _isLoginSuccess becomes true so cookies aren't saved twice
(references: _startPollingForCookies, _pollTimer, Timer.periodic,
_extractAndSaveCookies, _isLoginSuccess).

Comment thread lib/widgets/login/web_login_page.dart Outdated
Comment on lines +118 to +128
Log.i(_tag, 'Found essential cookies, saving...');

_isLoginSuccess = true;
_pollTimer?.cancel();

final request = Request();
final cookieJar = await request.cookieJar;

await cookieJar.saveFromResponse(Uri.parse(Api.urlBase), allCookies);
await cookieJar.saveFromResponse(Uri.parse(Api.urlLoginBase), allCookies);
await cookieJar.saveFromResponse(Uri.parse(Api.urlSiteBase), allCookies);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

_isLoginSuccess 在 cookie 持久化完成前就被设为 true,若后续操作失败将无法重试。

Line 120 设置 _isLoginSuccess = true 后,如果 Lines 126-128 的 cookieJar.saveFromResponse 或 Line 134 的 authProvider.login 抛出异常,虽然会被 catch 捕获,但 _isLoginSuccess 仍为 true,用户无法再次触发登录流程。

建议:将成功标志移到所有操作完成之后
       Log.i(_tag, 'Found essential cookies, saving...');

-      _isLoginSuccess = true;
       _pollTimer?.cancel();

       final request = Request();
       final cookieJar = await request.cookieJar;

       await cookieJar.saveFromResponse(Uri.parse(Api.urlBase), allCookies);
       await cookieJar.saveFromResponse(Uri.parse(Api.urlLoginBase), allCookies);
       await cookieJar.saveFromResponse(Uri.parse(Api.urlSiteBase), allCookies);

       Log.i(_tag, 'Cookies saved successfully');

+      _isLoginSuccess = true;
+
       if (mounted) {
🤖 Prompt for AI Agents
In `@lib/widgets/login/web_login_page.dart` around lines 118 - 128, Move the
_isLoginSuccess assignment so it only flips to true after all persistence and
login steps complete: keep cancelling _pollTimer as needed, but perform the
Request()/await request.cookieJar and all await
cookieJar.saveFromResponse(Uri.parse(...), allCookies) calls and the
authProvider.login(...) call first, then set _isLoginSuccess = true; also ensure
any catch/finally resets _isLoginSuccess to false on failure so a retry is
possible. Reference: _isLoginSuccess, _pollTimer, Request,
cookieJar.saveFromResponse, authProvider.login.

Comment thread pubspec.yaml Outdated
Comment on lines +136 to +141
flutter_inappwebview:
windows:
enabled: false
macos:
enabled: false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check pubspec.yaml content around lines 136-141
echo "=== Checking pubspec.yaml at lines 136-141 ==="
sed -n '136,141p' pubspec.yaml

echo ""
echo "=== Full flutter_inappwebview config in pubspec.yaml ==="
rg -A 5 "flutter_inappwebview:" pubspec.yaml

echo ""
echo "=== Check windows/flutter/generated_plugins.cmake ==="
cat -n windows/flutter/generated_plugins.cmake

echo ""
echo "=== Check windows/flutter/generated_plugin_registrant.cc ==="
cat -n windows/flutter/generated_plugin_registrant.cc

echo ""
echo "=== Search for InAppWebView usage in dart files ==="
fd -e dart . lib --exec rg -l "InAppWebView|inappwebview" {} \;

Repository: UtopiaXC/UtopiaMusic

Length of output: 3253


🏁 Script executed:

# Check web_login_page.dart for InAppWebView usage and platform conditions
echo "=== web_login_page.dart content ==="
cat -n lib/widgets/login/web_login_page.dart

echo ""
echo "=== Check for platform-specific code in web_login_page.dart ==="
rg -n "Platform|isWeb|kIsWeb|WebView" lib/widgets/login/web_login_page.dart

Repository: UtopiaXC/UtopiaMusic

Length of output: 8432


InAppWebView 在 Windows/macOS 被禁用的平台上被无条件使用

pubspec.yaml 禁用了 flutter_inappwebview 在 Windows 和 macOS 上的支持,但 lib/widgets/login/web_login_page.dartInAppWebView(第 177 行)的使用没有任何平台检查。这将导致应用在 Windows 或 macOS 上崩溃。

请确认:

  • 是否需要在所有平台启用该插件,还是
  • 需要为 Windows/macOS 添加平台检查,使用替代的 web 登录方法

如果选择启用该插件,则需要在 pubspec.yaml 中移除或修改 Windows/macOS 的禁用配置。

🤖 Prompt for AI Agents
In `@pubspec.yaml` around lines 136 - 141, The pubspec disables
flutter_inappwebview for Windows/macOS but lib/widgets/login/web_login_page.dart
still instantiates InAppWebView (around line 177), which will crash on those
platforms; either re-enable the plugin for those platforms in pubspec.yaml or
guard the usage in WebLoginPage (or the widget/function that creates
InAppWebView) with a platform check (Platform.isWindows/Platform.isMacOS or
kIsWeb/Theme.of(context).platform) and provide an alternative web-login flow or
fallback widget for unsupported platforms; update the constructor/initialization
in WebLoginPage to only create InAppWebView when the platform is supported, and
document the chosen approach in pubspec or the widget code comments.

Details: Addressing the potential risk of cookie leakage
Type: Bugfix.
Test: Local Passed.
Related Issue: N/A.
@UtopiaXC
Copy link
Copy Markdown
Owner Author

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new web-based login method for mobile platforms, leveraging flutter_inappwebview to display the Bilibili login page and extract authentication cookies. Key changes include adding new API constants for web login URLs, platform-specific user agents, and extensive internationalization strings for the new login flow. The LoginDialog was updated to include a 'Web Login' tab for mobile users, which navigates to a new WebLoginPage responsible for handling the in-app browser and cookie extraction. Additionally, the audio player's shuffle functionality was enhanced by implementing a reshufflePlaylist method in the database service, ensuring a new shuffle order when the play mode changes. Loop mode control was also refined, with a new _shouldLoopQueue flag to manage queue looping behavior, especially for desktop platforms. Review comments consistently pointed out the duplication of platform detection logic across player_provider.dart, login_dialog.dart, and web_login_page.dart, recommending consolidation into a centralized PlatformInfo utility class for improved maintainability.

Log.v(_tag, "_setPlayerLoopMode");
await player.setShuffleModeEnabled(false);

final isDesktop = !kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This platform detection logic is duplicated in several places (player_provider.dart, audio_player_service.dart, login_dialog.dart). To improve maintainability and avoid code duplication, consider creating a centralized platform utility class.

For example, you could create a file lib/utils/platform_info.dart:

import 'dart:io';
import 'package:flutter/foundation.dart' show kIsWeb;

class PlatformInfo {
  static bool get isDesktop => !kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS);
  static bool get isMobile => !kIsWeb && (Platform.isIOS || Platform.isAndroid);
  // ... other platform checks
}

Then you can use PlatformInfo.isDesktop here and in other files, which makes the code cleaner and easier to manage.

bool _isSuccess = false;
Timer? _pollTimer;
final LoginApi _loginApi = LoginApi();
bool get _isMobile => !kIsWeb && (Platform.isIOS || Platform.isAndroid);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This platform detection logic is also present in other files. To improve code consistency and maintainability, it would be beneficial to consolidate these checks into a single utility class, as suggested in other comments.

Comment on lines +35 to +42
String _getUserAgent() {
if (io.Platform.isIOS) {
return HttpConstants.userAgentIOS;
} else if (io.Platform.isAndroid) {
return HttpConstants.userAgentAndroid;
}
return HttpConstants.userAgent;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to other parts of the codebase, this platform-specific logic could be centralized into a utility class (e.g., PlatformInfo) to avoid duplication and improve maintainability. This would also make the code more consistent across the app.

Using the suggested PlatformInfo class, this could look like:

String _getUserAgent() {
  if (PlatformInfo.isIOS) {
    return HttpConstants.userAgentIOS;
  } else if (PlatformInfo.isAndroid) {
    return HttpConstants.userAgentAndroid;
  }
  return HttpConstants.userAgent;
}

@UtopiaXC UtopiaXC merged commit eee0a72 into master Feb 10, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 网页登录

1 participant