-
-
-
商品链接
-
-
-
-
定时发布
@@ -509,6 +497,7 @@ import { ElMessage } from 'element-plus'
import { useAccountStore } from '@/stores/account'
import { useAppStore } from '@/stores/app'
import { materialApi } from '@/api/material'
+import { http } from '@/utils/request'
// API base URL
const apiBaseUrl = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5409'
@@ -565,7 +554,8 @@ const defaultTabInit = {
startDays: 0, // 从今天开始计算的发布天数,0表示明天,1表示后天
publishStatus: null, // 发布状态,包含message和type
publishing: false, // 发布状态,用于控制按钮loading效果
- isDraft: false // 是否保存为草稿,仅视频号平台可见
+ isDraft: false, // 是否保存为草稿,仅视频号平台可见
+ isOriginal: false // 是否标记为原创
}
// helper to create a fresh deep-copied tab from defaultTabInit
@@ -663,7 +653,6 @@ const handleUploadSuccess = (response, file, tab) => {
}))]
ElMessage.success('文件上传成功')
- console.log('上传成功:', fileInfo)
} else {
ElMessage.error(response.msg || '上传失败')
}
@@ -672,7 +661,6 @@ const handleUploadSuccess = (response, file, tab) => {
// 处理文件上传失败
const handleUploadError = (error) => {
ElMessage.error('文件上传失败')
- console.error('上传错误:', error)
}
// 删除已上传文件
@@ -774,102 +762,77 @@ const cancelPublish = (tab) => {
const confirmPublish = async (tab) => {
// 防止重复点击
if (tab.publishing) {
- return Promise.reject(new Error('正在发布中,请稍候...'))
+ throw new Error('正在发布中,请稍候...')
}
tab.publishing = true // 设置发布状态为进行中
- return new Promise((resolve, reject) => {
- // 数据验证
- if (tab.fileList.length === 0) {
- ElMessage.error('请先上传视频文件')
- tab.publishing = false // 重置发布状态
- reject(new Error('请先上传视频文件'))
- return
- }
- if (!tab.title.trim()) {
- ElMessage.error('请输入标题')
- tab.publishing = false // 重置发布状态
- reject(new Error('请输入标题'))
- return
- }
- if (!tab.selectedPlatform) {
- ElMessage.error('请选择发布平台')
- tab.publishing = false // 重置发布状态
- reject(new Error('请选择发布平台'))
- return
- }
- if (tab.selectedAccounts.length === 0) {
- ElMessage.error('请选择发布账号')
- tab.publishing = false // 重置发布状态
- reject(new Error('请选择发布账号'))
- return
- }
+ // 数据验证
+ if (tab.fileList.length === 0) {
+ ElMessage.error('请先上传视频文件')
+ tab.publishing = false
+ throw new Error('请先上传视频文件')
+ }
+ if (!tab.title.trim()) {
+ ElMessage.error('请输入标题')
+ tab.publishing = false
+ throw new Error('请输入标题')
+ }
+ if (!tab.selectedPlatform) {
+ ElMessage.error('请选择发布平台')
+ tab.publishing = false
+ throw new Error('请选择发布平台')
+ }
+ if (tab.selectedAccounts.length === 0) {
+ ElMessage.error('请选择发布账号')
+ tab.publishing = false
+ throw new Error('请选择发布账号')
+ }
- // 构造发布数据,符合后端API格式
- const publishData = {
- type: tab.selectedPlatform,
- title: tab.title,
- tags: tab.selectedTopics, // 不带#号的话题列表
- fileList: tab.fileList.map(file => file.path), // 只发送文件路径
- accountList: tab.selectedAccounts.map(accountId => {
- const account = accountStore.accounts.find(acc => acc.id === accountId)
- return account ? account.filePath : accountId
- }), // 发送账号的文件路径
- enableTimer: tab.scheduleEnabled ? 1 : 0, // 是否启用定时发布,开启传1,不开启传0
- videosPerDay: tab.scheduleEnabled ? tab.videosPerDay || 1 : 1, // 每天发布视频数量,1-55
- dailyTimes: tab.scheduleEnabled ? tab.dailyTimes || ['10:00'] : ['10:00'], // 每天发布时间点
- startDays: tab.scheduleEnabled ? tab.startDays || 0 : 0, // 从今天开始计算的发布天数,0表示明天,1表示后天
- category: 0, //表示非原创
- productLink: tab.productLink.trim() || '', // 商品链接
- productTitle: tab.productTitle.trim() || '', // 商品名称
- isDraft: tab.isDraft // 是否保存为草稿,仅视频号平台使用
- }
+ // 构造发布数据,符合后端API格式
+ const publishData = {
+ type: tab.selectedPlatform,
+ title: tab.title,
+ tags: tab.selectedTopics, // 不带#号的话题列表
+ fileList: tab.fileList.map(file => file.path), // 只发送文件路径
+ accountList: tab.selectedAccounts.map(accountId => {
+ const account = accountStore.accounts.find(acc => acc.id === accountId)
+ return account ? account.filePath : accountId
+ }), // 发送账号的文件路径
+ enableTimer: tab.scheduleEnabled ? 1 : 0,
+ videosPerDay: tab.scheduleEnabled ? tab.videosPerDay || 1 : 1,
+ dailyTimes: tab.scheduleEnabled ? tab.dailyTimes || ['10:00'] : ['10:00'],
+ startDays: tab.scheduleEnabled ? tab.startDays || 0 : 0,
+ category: tab.isOriginal ? 1 : 0, // 1表示原创,0表示非原创
+ productLink: tab.productLink.trim() || '',
+ productTitle: tab.productTitle.trim() || '',
+ isDraft: tab.isDraft
+ }
- // 调用后端发布API
- fetch(`${apiBaseUrl}/postVideo`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- ...authHeaders.value
- },
- body: JSON.stringify(publishData)
- })
- .then(response => response.json())
- .then(data => {
- if (data.code === 200) {
- tab.publishStatus = {
- message: '发布成功',
- type: 'success'
- }
- // 清空当前tab的数据
- tab.fileList = []
- tab.displayFileList = []
- tab.title = ''
- tab.selectedTopics = []
- tab.selectedAccounts = []
- tab.scheduleEnabled = false
- resolve()
- } else {
- tab.publishStatus = {
- message: `发布失败:${data.msg || '发布失败'}`,
- type: 'error'
- }
- reject(new Error(data.msg || '发布失败'))
- }
- })
- .catch(error => {
- console.error('发布错误:', error)
- tab.publishStatus = {
- message: '发布失败,请检查网络连接',
- type: 'error'
- }
- reject(error)
- })
- .finally(() => {
- tab.publishing = false // 重置发布状态
- })
- })
+ // 调用后端发布API(使用统一的http封装)
+ try {
+ const data = await http.post('/postVideo', publishData)
+ tab.publishStatus = {
+ message: '发布成功',
+ type: 'success'
+ }
+ // 清空当前tab的数据
+ tab.fileList = []
+ tab.displayFileList = []
+ tab.title = ''
+ tab.selectedTopics = []
+ tab.selectedAccounts = []
+ tab.scheduleEnabled = false
+ } catch (error) {
+ console.error('发布错误:', error)
+ tab.publishStatus = {
+ message: `发布失败:${error.message || '请检查网络连接'}`,
+ type: 'error'
+ }
+ throw error
+ } finally {
+ tab.publishing = false
+ }
}
// 显示上传选项
@@ -1324,6 +1287,15 @@ const batchPublish = async () => {
margin: 10px 0;
}
}
+
+ .original-section {
+ margin: 10px 0 20px;
+
+ .original-checkbox {
+ display: block;
+ margin: 10px 0;
+ }
+ }
}
}
}
From 27bb20b206a3d5d4b418c94733393af16ad62b24 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=92=9F=E5=81=9C=E4=B8=BA=E6=AD=A2?= <13330202540@163.com>
Date: Thu, 5 Mar 2026 10:37:05 +0800
Subject: [PATCH 2/2] =?UTF-8?q?=20fix:=20=E8=A7=A3=E5=86=B3=E6=9C=AC?=
=?UTF-8?q?=E5=9C=B0=20Chrome=20=E9=85=8D=E7=BD=AE(LOCAL=5FCHROME=5FPATH)?=
=?UTF-8?q?=E6=9C=AA=E7=94=9F=E6=95=88=E4=B8=8E=E5=B9=B3=E5=8F=B0=E6=89=AB?=
=?UTF-8?q?=E7=A0=81=E9=99=90=E6=B5=81=E5=81=87=E6=AD=BB=E9=97=AE=E9=A2=98?=
=?UTF-8?q?=20##=20=E8=83=8C=E6=99=AF=E4=B8=8E=E5=A4=8D=E7=8E=B0=E9=97=AE?=
=?UTF-8?q?=E9=A2=98=20=20=20=E5=BD=93=E5=89=8D=E6=9E=B6=E6=9E=84=E4=B8=AD?=
=?UTF-8?q?=EF=BC=8C=E8=AE=B8=E5=A4=9A=E7=94=A8=E6=88=B7=E5=8D=B3=E4=BD=BF?=
=?UTF-8?q?=E5=9C=A8=20`conf.py`=20=E4=B8=AD=E9=85=8D=E7=BD=AE=E4=BA=86=20?=
=?UTF-8?q?`LOCAL=5FCHROME=5FHEADLESS=3DFalse`=20=E4=BB=A5=E5=8F=8A?=
=?UTF-8?q?=E8=87=AA=E5=B7=B1=E7=9A=84=20=20=20`LOCAL=5FCHROME=5FPATH`?=
=?UTF-8?q?=EF=BC=8C=E6=89=AB=E7=A0=81=E7=99=BB=E5=BD=95=E6=97=B6=E4=BE=9D?=
=?UTF-8?q?=E7=84=B6=E4=BC=9A=E8=B0=83=E7=94=A8=E8=87=AA=E5=B8=A6=E7=9A=84?=
=?UTF-8?q?=E4=B8=8D=E9=9A=90=E8=BA=AB=E5=86=85=E6=A0=B8=EF=BC=8C=E8=BF=99?=
=?UTF-8?q?=E8=AF=B1=E5=8F=91=E4=BA=86=E4=B8=A4=E4=B8=AA=E4=B8=A5=E9=87=8D?=
=?UTF-8?q?=E9=98=BB=E6=96=AD=EF=BC=9A=20=20=201.=20=E7=8E=AF=E5=A2=83?=
=?UTF-8?q?=E5=8F=98=E9=87=8F=E9=80=83=E9=80=B8=EF=BC=9A=E5=9B=A0=E4=B8=BA?=
=?UTF-8?q?=E6=95=A3=E8=90=BD=E5=9C=A8=20`login.py`=20=E7=AD=89=E6=96=87?=
=?UTF-8?q?=E4=BB=B6=E4=B8=AD=E5=A4=A7=E5=A4=9A=E5=8F=AA=E4=BC=A0=E4=BA=86?=
=?UTF-8?q?=20`headless`=20=E5=8F=82=E6=95=B0=EF=BC=8C=E5=B9=B6=E6=9C=AA?=
=?UTF-8?q?=E4=BC=A0=E9=80=92=20`executable=5Fpath`=EF=BC=8C=E5=AF=BC?=
=?UTF-8?q?=E8=87=B4=E5=A4=96=E9=83=A8=E6=B5=8F=E8=A7=88=E5=99=A8=E9=85=8D?=
=?UTF-8?q?=E7=BD=AE=E5=BD=BB=E5=BA=95=E5=A4=B1=E6=95=88=E3=80=82=20=20=20?=
=?UTF-8?q?2.=20=E5=8F=8D=E7=88=AC=E9=A3=8E=E6=8E=A7=E5=81=87=E6=AD=BB?=
=?UTF-8?q?=EF=BC=9A=E7=B3=BB=E7=BB=9F=E7=9B=B4=E6=8E=A5=E6=8A=9B=E5=87=BA?=
=?UTF-8?q?=E7=9A=84=20`webdriver=3Dtrue`=20=E4=BC=9A=E9=81=AD=E6=9D=A5?=
=?UTF-8?q?=E6=8A=96=E9=9F=B3/=E5=B0=8F=E7=BA=A2=E4=B9=A6=E7=BA=A7?=
=?UTF-8?q?=E5=88=AB=E6=9E=81=E5=85=B6=E4=B8=A5=E5=8E=89=E7=9A=84=E6=BB=91?=
=?UTF-8?q?=E5=9D=97=E7=94=9A=E8=87=B3=E4=BA=8C=E7=BB=B4=E7=A0=81=E9=98=BB?=
=?UTF-8?q?=E6=96=AD=EF=BC=8C=E6=9C=80=E7=BB=88=E5=8D=A1=E6=AD=BB=EF=BC=8C?=
=?UTF-8?q?=E5=90=8E=E5=8F=B0=E6=8A=A5=E9=94=99=20`=E7=9B=91=E5=90=AC?=
=?UTF-8?q?=E9=A1=B5=E9=9D=A2=E8=B7=B3=E8=BD=AC=E8=B6=85=E6=97=B6`?=
=?UTF-8?q?=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## 本次修复
1. **全局接管配置**:构建统一的浏览器环境变量拦截钩子 `utils/browser_hook.py`。
2. **防风控注入**:加入 `--disable-blink-features=AutomationControlled` 隐藏驱动器指纹,免受大数据识别拦截,极大提升扫码登入与发布的通过率。
3. **修复登录扫码挂载**:改写 `login.py` 对接,已验证能够丝滑拉起本地指纹浏览器。
---
myUtils/login.py | 23 +++++++++++++++++++----
utils/browser_hook.py | 17 +++++++++++++++++
2 files changed, 36 insertions(+), 4 deletions(-)
create mode 100644 utils/browser_hook.py
diff --git a/myUtils/login.py b/myUtils/login.py
index 4437f99..90333a2 100644
--- a/myUtils/login.py
+++ b/myUtils/login.py
@@ -7,7 +7,24 @@ from myUtils.auth import check_cookie
from utils.base_social_media import set_init_script
import uuid
from pathlib import Path
-from conf import BASE_DIR, LOCAL_CHROME_HEADLESS
+from conf import BASE_DIR, LOCAL_CHROME_HEADLESS, LOCAL_CHROME_PATH
+
+# 统一获取浏览器启动配置(防风控+引入本地浏览器)
+def get_browser_options():
+ options = {
+ 'headless': LOCAL_CHROME_HEADLESS,
+ 'args': [
+ '--disable-blink-features=AutomationControlled', # 核心防爬屏蔽:去掉 window.navigator.webdriver 标签
+ '--lang=zh-CN',
+ '--disable-infobars',
+ '--start-maximized'
+ ]
+ }
+ # 如果用户在 conf.py 里配置了本地 Chrome,就用本地的,这样成功率极高
+ if LOCAL_CHROME_PATH:
+ options['executable_path'] = LOCAL_CHROME_PATH
+
+ return options
# 抖音登录
async def douyin_cookie_gen(id,status_queue):
@@ -17,9 +34,7 @@ async def douyin_cookie_gen(id,status_queue):
if page.url != original_url:
url_changed_event.set()
async with async_playwright() as playwright:
- options = {
- 'headless': LOCAL_CHROME_HEADLESS
- }
+ options = get_browser_options()
# Make sure to run headed.
browser = await playwright.chromium.launch(**options)
# Setup context however you like.
diff --git a/utils/browser_hook.py b/utils/browser_hook.py
new file mode 100644
index 0000000..7f82ce7
--- /dev/null
+++ b/utils/browser_hook.py
@@ -0,0 +1,17 @@
+from conf import LOCAL_CHROME_HEADLESS, LOCAL_CHROME_PATH
+
+def get_browser_options():
+ options = {
+ 'headless': LOCAL_CHROME_HEADLESS,
+ 'args': [
+ '--disable-blink-features=AutomationControlled',
+ '--lang=zh-CN',
+ '--disable-infobars',
+ '--start-maximized',
+ '--no-sandbox',
+ '--disable-web-security'
+ ]
+ }
+ if LOCAL_CHROME_PATH:
+ options['executable_path'] = LOCAL_CHROME_PATH
+ return options