Skip to content

Commit 8c2eb44

Browse files
committed
feat(kuaishou): enhance dynamic content publishing with image validation and improved upload process
- Added validation to ensure at least one image is provided before publishing. - Refactored image upload process to utilize DataTransfer for better handling. - Improved error handling and logging for video and cover uploads. - Updated content description handling to support dynamic input and formatting.
1 parent 8878663 commit 8c2eb44

File tree

2 files changed

+163
-141
lines changed

2 files changed

+163
-141
lines changed

src/sync/dynamic/kuaishou.ts

Lines changed: 81 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
import type { DynamicData, SyncData } from '../common';
22

3-
// 优先发布图文
3+
/**
4+
* 快手图文动态发布函数
5+
* @description 优先发布图文内容到快手平台
6+
* @param {SyncData} data - 同步数据,包含标题、内容、图片等信息
7+
*/
48
export async function DynamicKuaishou(data: SyncData) {
59
const { title, content, images } = data.data as DynamicData;
10+
11+
// 检查图片数量
12+
if (!images || images.length === 0) {
13+
alert('发布图文,请至少提供一张图片');
14+
return;
15+
}
16+
617
// 辅助函数:等待元素出现
718
function waitForElement(selector: string, timeout = 10000): Promise<Element> {
819
return new Promise((resolve, reject) => {
@@ -32,71 +43,8 @@ export async function DynamicKuaishou(data: SyncData) {
3243
});
3344
}
3445

35-
// 辅助函数:通过文本内容查找元素
36-
async function findElementByText(
37-
selector: string,
38-
text: string,
39-
maxRetries = 5,
40-
retryInterval = 1000,
41-
): Promise<Element | null> {
42-
for (let i = 0; i < maxRetries; i++) {
43-
const elements = document.querySelectorAll(selector);
44-
const element = Array.from(elements).find((element) => element.textContent?.includes(text));
45-
46-
if (element) {
47-
return element;
48-
}
49-
50-
console.log(`未找到包含文本 "${text}" 的元素,尝试次数:${i + 1}`);
51-
await new Promise((resolve) => setTimeout(resolve, retryInterval));
52-
}
53-
54-
console.error(`在 ${maxRetries} 次尝试后未找到包含文本 "${text}" 的元素`);
55-
return null;
56-
}
57-
58-
// 辅助函数:上传文件
59-
async function uploadImages() {
60-
const fileInput = (await waitForElement(
61-
'input[type="file"][accept="image/png, image/jpg, image/jpeg, image/webp"]',
62-
)) as HTMLInputElement;
63-
if (!fileInput) {
64-
console.error('未找到文件输入元素');
65-
return;
66-
}
67-
68-
const dataTransfer = new DataTransfer();
69-
70-
console.log('开始上传图片');
71-
for (const fileInfo of images) {
72-
console.log(`准备上传图片: ${fileInfo.url}`);
73-
try {
74-
const response = await fetch(fileInfo.url);
75-
if (!response.ok) {
76-
throw new Error(`HTTP 错误! 状态: ${response.status}`);
77-
}
78-
const blob = await response.blob();
79-
const file = new File([blob], fileInfo.name, { type: fileInfo.type });
80-
dataTransfer.items.add(file);
81-
} catch (error) {
82-
console.error(`上传图片 ${fileInfo.url} 失败:`, error);
83-
}
84-
}
85-
86-
if (dataTransfer.files.length > 0) {
87-
const uploadButton = (await findElementByText('button', '上传图片')) as HTMLElement;
88-
89-
// 使用 simulateDragAndDrop 函数模拟拖拽事件
90-
simulateDragAndDrop(uploadButton.parentElement.parentElement, dataTransfer);
91-
console.log('文件上传操作完成');
92-
} else {
93-
console.error('没有成功添加任何文件');
94-
}
95-
}
96-
9746
// 模拟拖拽事件的函数
9847
function simulateDragAndDrop(element: HTMLElement, dataTransfer: DataTransfer) {
99-
console.log('simulateDragAndDrop', dataTransfer);
10048
const events = [
10149
new DragEvent('dragenter', { bubbles: true }),
10250
new DragEvent('dragover', { bubbles: true }),
@@ -106,50 +54,82 @@ export async function DynamicKuaishou(data: SyncData) {
10654
Object.defineProperty(event, 'preventDefault', { value: () => {} });
10755
});
10856
events.forEach((event) => {
109-
console.log('event', event);
11057
element.dispatchEvent(event);
11158
});
11259
}
11360

114-
if (images && images.length > 0) {
115-
console.log('检测到图片,开始上传');
61+
// 等待文件输入元素
62+
await waitForElement('input[type="file"]');
63+
await new Promise((resolve) => setTimeout(resolve, 1000));
11664

117-
const imageTab = (await waitForElement('div#rc-tabs-0-tab-2')) as HTMLElement;
118-
imageTab.click();
119-
await new Promise((resolve) => setTimeout(resolve, 2000));
120-
await uploadImages();
65+
// 查找并点击上传图片的tab
66+
const uploadTab = document.querySelector('div#rc-tabs-0-tab-2') as HTMLElement;
67+
if (!uploadTab) {
68+
console.error('未找到 uploadTab');
69+
return;
70+
}
71+
uploadTab.click();
72+
await new Promise((resolve) => setTimeout(resolve, 2000));
73+
74+
// 创建 DataTransfer 对象并添加文件
75+
const dataTransfer = new DataTransfer();
76+
for (const fileInfo of images) {
77+
console.log('try upload file', fileInfo);
78+
try {
79+
const response = await fetch(fileInfo.url);
80+
if (!response.ok) {
81+
throw new Error(`HTTP 错误! 状态: ${response.status}`);
82+
}
83+
const arrayBuffer = await response.arrayBuffer();
84+
const file = new File([arrayBuffer], fileInfo.name, { type: fileInfo.type });
85+
dataTransfer.items.add(file);
86+
} catch (error) {
87+
console.error(`上传图片 ${fileInfo.url} 失败:`, error);
88+
}
89+
}
12190

122-
await new Promise((resolve) => setTimeout(resolve, 5000));
91+
// 查找上传图片按钮
92+
const buttons = document.querySelectorAll('button');
93+
const uploadButton = Array.from(buttons).find((button) => button.textContent === '上传图片') as HTMLElement;
12394

124-
// 处理作品描述
125-
const contentEditor = (await waitForElement('div#work-description-edit[contenteditable="true"]')) as HTMLDivElement;
126-
if (contentEditor) {
127-
contentEditor.innerText = `${title || ''}\n\n${content}`;
128-
contentEditor.dispatchEvent(new Event('input', { bubbles: true }));
129-
}
95+
if (!uploadButton) {
96+
console.error("未找到'上传图片'按钮");
97+
return;
98+
}
13099

131-
// 等待内容更新
132-
await new Promise((resolve) => setTimeout(resolve, 3000));
133-
134-
if (data.isAutoPublish) {
135-
console.log('开始自动发布');
136-
const maxAttempts = 3;
137-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
138-
try {
139-
const publishButton = (await findElementByText('div', '发布')) as HTMLElement;
140-
publishButton.click();
141-
console.log('发布按钮已点击');
142-
await new Promise((resolve) => setTimeout(resolve, 3000));
143-
window.location.href = 'https://cp.kuaishou.com/article/manage/video';
144-
break; // 成功点击后退出循环
145-
} catch (error) {
146-
console.warn(`第 ${attempt + 1} 次尝试查找发布按钮失败:`, error);
147-
if (attempt === maxAttempts - 1) {
148-
console.error('达到最大尝试次数,无法找到发布按钮');
149-
}
150-
await new Promise((resolve) => setTimeout(resolve, 2000)); // 等待2秒后重试
151-
}
152-
}
153-
}
100+
// 执行拖拽上传
101+
const dropTarget = uploadButton.parentElement?.parentElement as HTMLElement;
102+
simulateDragAndDrop(dropTarget, dataTransfer);
103+
console.log('文件上传操作完成');
104+
105+
// 等待描述输入框出现
106+
await waitForElement('div[placeholder="添加合适的话题和描述,作品能获得更多推荐~"][contenteditable="true"]');
107+
108+
// 查找描述输入框并粘贴内容
109+
const descriptionInput = document.querySelector(
110+
'div[placeholder="添加合适的话题和描述,作品能获得更多推荐~"][contenteditable="true"]',
111+
) as HTMLDivElement;
112+
113+
if (descriptionInput) {
114+
descriptionInput.focus();
115+
116+
// 拼接标题和内容,如果有标题则用 \n 分隔
117+
const textContent = title ? `${title}\n${content}` : content;
118+
119+
// 使用 ClipboardEvent 粘贴内容
120+
const pasteEvent = new ClipboardEvent('paste', {
121+
bubbles: true,
122+
cancelable: true,
123+
clipboardData: new DataTransfer(),
124+
});
125+
pasteEvent.clipboardData?.setData('text/plain', textContent);
126+
descriptionInput.dispatchEvent(pasteEvent);
127+
}
128+
129+
await new Promise((resolve) => setTimeout(resolve, 3000));
130+
131+
// 如果是自动发布,提示需要手动确认
132+
if (data.isAutoPublish) {
133+
alert('为确保内容符合预期,请手动确认发布');
154134
}
155135
}

0 commit comments

Comments
 (0)