- _focus_desc_editor: locate the description editor with multiple fallback
strategies instead of a single brittle selector; dump page screenshot/HTML
on failure for easier selector updates
- cookie_auth: positive verification via the upload button presence, with
login-page / intro-page / legacy checks as fallbacks
- close_guide_overlay: also remove the Joyride portal and spotlight layers
that intercept clicks
- add collection_name support to KSVideo
- Open homepage first, then click the visible 'publish video' entry
button instead of loading /platform/post/create directly, which gets
bounced back to the homepage and leaves the form iframe un-mounted
- Detect expired cookies up front by watching for the /login.html
redirect and the qrconnect iframe
- Drop the redundant confirm_thumbnail_crop call that caused a 5s
timeout on cover upload
- Select collection by exact name after upload completes (form resets
selections made while the video is still uploading); keep it
unselected when no matching name is found instead of picking a
random one
- Scroll the target option into view and fall back to forced click /
native dispatch for headless visibility issues
- Tighten short-title length to 7~15 chars to pass platform validation
- Dismiss the occasional 'switch account' dialog that blocks the
description box and the publish button
- Set cover via native click with retries; skip gracefully instead of failing the whole order
- Select the collection via its dedicated Semi select with overlay cleanup
- Rewrite self-declaration dialog handling (entry/dialog/option/confirm) and reuse it in upload
- Type description before tags and fill via set_input_files with robust upload input detection
- Keep SMS verification code prompt as a publish-loop guard
- check_original_declaration: replace the original checkbox with the
'来源转载' (repost) declaration flow: open the declaration picker, select
'来源转载', fill the media name, confirm. Falls back to JS click when the
element text is wrapped in non-interactive spans
- set_thumbnail: switch to the '上传封面' tab and target the upload-wrapper
file input instead of the cover-plugin-preview selector; skip gracefully on
failure and let the video first frame be used
The WeChat Channels login page now embeds the QR code in an
open.weixin.qq.com/connect/qrconnect iframe whose img.qrcode src is a
relative path (e.g. /connect/qrcode/xxxx) instead of an inline
data:image/ URL. The previous logic looked for a login-for-iframe
frame and only accepted data:image/ sources, so login always failed
with '未获取到视频号登录二维码地址'.
Changes:
- Detect the qrconnect iframe and resolve the relative img src,
download the QR image via the browser context and convert it to a
data URL so downstream save/decode logic stays unchanged
- Keep the legacy login-for-iframe and selector fallbacks
- Tolerate QR extraction failure: the login flow now continues and
lets the user scan directly in the headed browser instead of
aborting the whole login
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
Title and description were typed character-by-character, so a '#'
(e.g. '#Shorts') triggered YouTube's topic-autocomplete dropdown that
follows the caret and covers the Next/Publish buttons, stalling the
upload. Switch to fill() (one-shot insert, no per-keystroke autocomplete)
with a type() fallback, and add _dismiss_autocomplete (blur, then Escape
only when a dropdown is actually visible so the upload dialog is never
closed by mistake).
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.
Two related changes for kuaishou uploader:
- Default browser channel is switched from 'chrome' to 'chromium'. The launcher binary that ships with patchright no longer reliably resolves the system Chrome channel on every platform, so falling back to bundled chromium avoids launch-time channel-resolution errors. The user can still force a real Chrome via conf.py:LOCAL_CHROME_PATH.
- New cdp_url parameter on ks_setup / get_ks_cookie. When set, the login flow uses connect_over_cdp() to drive the user's already-running real Chrome instead of spawning a separate browser. This is the supported way around strict anti-bot detection: real Chrome has a fingerprint that passes verification, while the bundled chromium does not. should_close_context tracks ownership so we don't close a context we didn't create.
The Douyin creator center UI (https://creator.douyin.com/creator-micro/) has been refactored and breaks the existing login flow in three independent ways. This commit fixes all of them.
1. QR code selector no longer matches
The previous selector chain assumed the QR sat in a sibling div with an aria-label of 二维码. After the refactor, the QR is rendered as a base64 PNG inside div#animate_qrcode_container and has no aria-label. The new chain is a 4-tier fallback (id -> class partial -> container partial -> aria-label) preceded by a networkidle wait so the SPA is fully hydrated before we look.
2. cookie_auth() race condition
page.goto + wait_for_url(5000) + immediate get_by_text count() was racy: the SPA had not finished hydrating, so count() could return 0 even when the page actually shows the login card, leading to false-positive 'cookie valid' results. Replaced with a 10s polling loop that returns invalid if neither the login-card text nor the upload-page text is observed.
3. _wait_for_douyin_login() only waited for sessionid cookie (120s)
During 2FA the cookie is not yet set; the page is still on the verification screen and the URL has already changed. The new loop detects 2FA input fields, logs a warning so the user knows to type the SMS code, and keeps polling. Timeout raised to 200s to accommodate the slower human-in-the-loop flow.
Verified the selector chain against the live creator.douyin.com page: returns a 2.9 KB base64 PNG that decodes into a valid douyin scan_login URL.
- Wait until the upload progress reaches 100% before clicking publish. The
browser-based upload only progresses while the page is open, so publishing
(and closing the browser) mid-upload cut it off and left the video stuck
partway (e.g. 76%).
- Add optional YT_PROXY (conf.py): where youtube.com is blocked, direct
connections time out and the chromium does not use the system proxy, so
allow pointing it at a local proxy explicitly.
Add a YouTube uploader that follows the existing cookie-based browser
automation pattern (uploader/youtube_uploader + sau youtube CLI:
login/check/upload-video). Supports title/description/tags, thumbnail,
adding to a playlist (for series), and visibility (public/unlisted/private).
Browser automation is used instead of the official Data API on purpose:
videos uploaded by an unaudited API project are force-locked to private
and cannot be made public without Google's compliance audit, which is
impractical for personal/single-channel use. Browser automation has no
such restriction and matches how every other platform here works.