145 lines
4.2 KiB
Go
145 lines
4.2 KiB
Go
// 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)
|
|
}
|