client: Windows 客户端(WinUI3 壳 + agent-core;调试用 Electron 壳存档);localserver token 改为常数时间比较
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
// Publish 通用发布流程:进上传页 → 注入 cookie → 传视频 → 填表单 → 点发布 → 等成功跳转 → 截图回执。
|
||||
// 各平台选择器差异经 PublishConfig 配置;平台改版时只需调整配置。
|
||||
package cdp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-rod/rod"
|
||||
"github.com/go-rod/rod/lib/proto"
|
||||
)
|
||||
|
||||
// PublishConfig 发布流程配置(选择器按平台填写)
|
||||
type PublishConfig struct {
|
||||
Platform string
|
||||
UploadURL string
|
||||
SuccessMarker string // 发布成功后的 URL 特征
|
||||
FileInputSel string
|
||||
TitleSel string
|
||||
DescSel string
|
||||
PublishBtnSel string
|
||||
FormWait time.Duration // 等视频处理出表单的最长时间
|
||||
SuccessWait time.Duration
|
||||
ReceiptDir string
|
||||
}
|
||||
|
||||
// PublishResult 发布结果
|
||||
type PublishResult struct {
|
||||
URL string
|
||||
Receipts []string
|
||||
}
|
||||
|
||||
// Publish 执行通用发布流程
|
||||
func (b *Browser) Publish(ctx context.Context, cfg PublishConfig, videoPath, title, content string, tags []string, cookieStr, cookieDomain string) (*PublishResult, error) {
|
||||
if err := b.Page.Navigate(cfg.UploadURL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = b.Page.WaitLoad()
|
||||
// 注入登录 cookie(有则免扫码)
|
||||
if cookieStr != "" {
|
||||
_ = b.InjectCookies(cookieStr, cookieDomain)
|
||||
if err := b.Page.Navigate(cfg.UploadURL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = b.Page.WaitLoad()
|
||||
}
|
||||
|
||||
// 1) 传视频
|
||||
fileInput, err := b.Page.Element(cfg.FileInputSel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("找不到上传入口(%s): %w", cfg.FileInputSel, err)
|
||||
}
|
||||
if err = fileInput.SetFiles([]string{videoPath}); err != nil {
|
||||
return nil, fmt.Errorf("选择文件失败: %w", err)
|
||||
}
|
||||
Log("%s: 视频已选择,等待平台处理...", cfg.Platform)
|
||||
|
||||
// 2) 等表单渲染(视频转码后出现标题输入框)
|
||||
titleEl, err := b.Page.ElementR(cfg.TitleSel, "")
|
||||
if err != nil {
|
||||
titleEl, err = b.waitElement(ctx, cfg.TitleSel, cfg.FormWait)
|
||||
}
|
||||
if err != nil {
|
||||
b.Screenshot(cfg.ReceiptDir, cfg.Platform+"-form-fail.png")
|
||||
return nil, fmt.Errorf("等表单渲染超时(%s): %w", cfg.TitleSel, err)
|
||||
}
|
||||
|
||||
// 3) 填标题/描述(话题并入描述)
|
||||
if err = titleEl.Input(title); err != nil {
|
||||
return nil, fmt.Errorf("填标题失败: %w", err)
|
||||
}
|
||||
desc := content
|
||||
if len(tags) > 0 {
|
||||
for _, t := range tags {
|
||||
t = strings.TrimSpace(t)
|
||||
if t != "" && !strings.HasPrefix(t, "#") {
|
||||
t = "#" + t
|
||||
}
|
||||
desc += " " + t
|
||||
}
|
||||
}
|
||||
if descEl, derr := b.Page.Element(cfg.DescSel); derr == nil {
|
||||
_ = descEl.Input(desc)
|
||||
}
|
||||
|
||||
// 4) 点发布
|
||||
btn, err := b.waitElement(ctx, cfg.PublishBtnSel, 15*time.Second)
|
||||
if err != nil {
|
||||
b.Screenshot(cfg.ReceiptDir, cfg.Platform+"-btn-fail.png")
|
||||
return nil, fmt.Errorf("找不到发布按钮(%s): %w", cfg.PublishBtnSel, err)
|
||||
}
|
||||
if err = btn.Click(proto.InputMouseButtonLeft, 1); err != nil {
|
||||
return nil, fmt.Errorf("点发布失败: %w", err)
|
||||
}
|
||||
Log("%s: 已点发布,等待结果...", cfg.Platform)
|
||||
|
||||
// 5) 等成功跳转
|
||||
if err = b.waitURL(ctx, cfg.SuccessMarker, cfg.SuccessWait); err != nil {
|
||||
b.Screenshot(cfg.ReceiptDir, cfg.Platform+"-publish-fail.png")
|
||||
return nil, err
|
||||
}
|
||||
info, _ := b.Page.Info()
|
||||
receipt := b.Screenshot(cfg.ReceiptDir, cfg.Platform+"-success.png")
|
||||
Log("%s: 发布成功 %s", cfg.Platform, info.URL)
|
||||
return &PublishResult{URL: info.URL, Receipts: []string{receipt}}, nil
|
||||
}
|
||||
|
||||
// waitElement 轮询等待元素出现
|
||||
func (b *Browser) waitElement(ctx context.Context, selector string, timeout time.Duration) (*rod.Element, error) {
|
||||
deadline := time.Now().Add(timeout)
|
||||
for time.Now().Before(deadline) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, fmt.Errorf("cancelled")
|
||||
default:
|
||||
}
|
||||
el, err := b.Page.Element(selector)
|
||||
if err == nil {
|
||||
return el, nil
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
return nil, fmt.Errorf("timeout waiting %s", selector)
|
||||
}
|
||||
|
||||
// waitURL 轮询等待 URL 命中特征
|
||||
func (b *Browser) waitURL(ctx context.Context, marker string, timeout time.Duration) error {
|
||||
deadline := time.Now().Add(timeout)
|
||||
for time.Now().Before(deadline) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("cancelled")
|
||||
default:
|
||||
}
|
||||
info, err := b.Page.Info()
|
||||
if err == nil && strings.Contains(info.URL, marker) {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
return fmt.Errorf("timeout waiting url %s", marker)
|
||||
}
|
||||
Reference in New Issue
Block a user