4 commits squashed into 1 due to force-push via API. See PR #229 body for full description.
Key changes:
- cookie_auth: polling loop (10s) instead of single count() snapshot (race fix)
- cookie_auth: env var DOUYIN_COOKIE_AUTH_HEADLESS for linux server headless override
- _extract_douyin_qrcode_src: 4-tier selector fallback (animate_qrcode_container)
- _wait_for_douyin_login: 2FA detection with warning
- export_douyin_cookie: USERNAME from os.environ
- ks/xiaohongshu uploader: chromium default + cdp_url support for ks
Same reason as the ks uploader: bundled patchright cannot reliably resolve the system 'chrome' channel on every platform, so use the bundled 'chromium' channel by default and rely on conf.py:LOCAL_CHROME_PATH for users who want real Chrome.
- Add tag count validation (max 10) in both CLI and uploader layers
- CLI: exit with error if tags > 10, prompt user to reduce
- Uploader: truncate to first 10 with warning as fallback
- Prevents infinite loop when XHS UI rejects excess tags
- Add check_original_declaration() to auto-check original content
declaration before publishing (video and note modes)
- Gracefully skips if the option is not present on the page
Move newline normalization out of the f-string expression so the CLI can import and run under Python 3.11+ without syntax errors.
Co-authored-by: Cursor <cursoragent@cursor.com>