Skip to content

Feat/cookie - #11

Open
ztygod wants to merge 2 commits into
mainfrom
feat/cookie
Open

Feat/cookie#11
ztygod wants to merge 2 commits into
mainfrom
feat/cookie

Conversation

@ztygod

@ztygod ztygod commented Sep 3, 2026

Copy link
Copy Markdown
Owner

No description provided.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Confirmed runtime bugs exist in the new auth flow (browser enum string handling and RuntimeError escaping the AuthError boundary), plus a Playwright provider cleanup issue that can leak browser processes on exceptions.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR introduces a new noteforge.auth layer to centrally manage cookie import/validation/encrypted storage and integrates it into the media pipeline and CLI, while tightening test-time safety boundaries to prevent reading real browser credentials.

Changes:

  • Add AuthManager + providers (browser/JSON/raw/Playwright) + validator + encrypted store, and wire media execution to use authenticated cookie leases with a single refresh retry.
  • Introduce SubtitleAccessStatus so “no subtitles” can be distinguished from “anonymous request lacked access”.
  • Add noteforge auth CLI commands and expand tests to enforce “no real browser cookies in unit tests”.
File summaries
File Description
uv.lock Adds Playwright and transitive deps to the lockfile.
pyproject.toml Adds Playwright as a runtime dependency.
src/noteforge/auth/init.py Exposes the new auth public API surface.
src/noteforge/auth/errors.py Defines auth-specific exception types.
src/noteforge/auth/models.py Adds auth platform/status/result models.
src/noteforge/auth/manager.py Implements the auth lifecycle orchestration (load/validate/refresh/login/logout).
src/noteforge/auth/store.py Adds AES-GCM + keyring backed encrypted cookie store (with legacy read support).
src/noteforge/auth/validator.py Implements remote cookie validation for Bilibili/YouTube.
src/noteforge/auth/signing.py Adds a CSRF-token helper extension point.
src/noteforge/auth/providers/init.py Exports built-in cookie providers.
src/noteforge/auth/providers/base.py Defines provider protocol + domain filtering helpers.
src/noteforge/auth/providers/browser.py Imports cookies from local browsers via yt-dlp and filters by platform.
src/noteforge/auth/providers/json_file.py Imports cookies from JSON exports and filters by platform.
src/noteforge/auth/providers/raw.py Imports cookies from raw Cookie header text.
src/noteforge/auth/providers/playwright.py Implements interactive login via Playwright and cookie extraction.
src/noteforge/media/cookies/service.py Simplifies cookie service into a short-lived lease factory (no persistence/import).
src/noteforge/media/cookies/init.py Removes CredentialInfo exports after cookie-service refactor.
src/noteforge/media/models.py Adds SubtitleAccessStatus and simplifies AuthRequest.
src/noteforge/media/config.py Updates docs to reflect auth is managed by AuthManager.
src/noteforge/media/init.py Updates exports to reflect refactored cookie/auth surface.
src/noteforge/media/service.py Integrates AuthManager and adds unified authenticated execution with one refresh retry.
src/noteforge/media/ytdlp/errors.py Sanitizes user-facing yt-dlp error messages to avoid leaking context.
src/noteforge/cli/app.py Registers the new noteforge auth Typer sub-app.
src/noteforge/cli/commands/auth.py Adds auth login/status/logout commands.
src/noteforge/cli/commands/generate.py Uses SubtitleAccessStatus to provide targeted preflight failures.
tests/conftest.py Adds an autouse fixture preventing tests from reading real browser cookies/keyring.
tests/auth/test_auth.py Adds unit tests for auth store/provider/validator/manager and media retry behavior.
tests/media/test_media.py Injects an anonymous auth manager to keep media tests isolated from real cookies.
README.md Updates docs to describe AuthManager behavior and noteforge auth usage.
README.zh-CN.md Updates Chinese docs with auth lifecycle and CLI examples.
Review details

Suppressed comments (3)

src/noteforge/auth/store.py:154

  • _load_legacy() 解密旧版凭据失败时同样抛出 RuntimeError,会导致调用 AuthManager/CLI 时无法按 AuthError 统一处理。建议改为抛出 AuthError(或其子类)。
        except Exception as error:
            raise RuntimeError("无法读取旧版加密 Cookie 存储。") from error

src/noteforge/auth/store.py:76

  • save() 依赖缺失时抛出 RuntimeError 会让 noteforge auth login/status 等命令无法被 except AuthError 捕获并转成友好错误输出。建议将此类依赖/环境问题也统一包装为 AuthError(或其子类)。
        except ImportError as error:
            raise RuntimeError("加密保存 Cookie 需要 cryptography。") from error

src/noteforge/auth/store.py:216

  • _vault_key() 对环境变量解析失败或缺少 keyring 时抛出 RuntimeError,上层 CLI 当前只捕获 AuthError,会导致直接异常退出。建议在该方法内统一引入 AuthError 并用它替代这些 RuntimeError
            except ValueError as error:
                raise RuntimeError("Cookie Vault 密钥必须是十六进制。") from error
            if len(key) != 32:
                raise RuntimeError("Cookie Vault 密钥必须为 256 位。")
            return key
  • Files reviewed: 29/30 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +33 to +49
with sync_playwright() as playwright:
browser = playwright.chromium.launch(headless=False)
context = browser.new_context()
page = context.new_page()
page.goto(self.LOGIN_URLS[platform])
deadline = monotonic() + self.timeout
while monotonic() < deadline:
jar = self._from_playwright(context.cookies())
filtered = filter_cookies(jar, platform)
if self._has_session_marker(platform, filtered):
context.close()
browser.close()
return filtered
sleep(1)
context.close()
browser.close()
except InteractiveLoginError:
Comment on lines +60 to +61
except Exception as error:
raise RuntimeError("无法读取加密 Cookie 存储。") from error
"""统一注入认证,并在明确认证失败时最多刷新重试一次。"""

platform = AuthPlatform(adapter.platform.value)
browser = str(auth.browser) if auth is not None else None
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.

2 participants