Skip to content

Commit 0a6164d

Browse files
authored
feat: add Weibo video platform sync support (#75)
- Implement video upload functionality for Weibo platform - Add platform configuration for Weibo video sync in common configuration - Create dedicated sync function for Weibo video content upload - Support file upload, description input, and optional auto-publishing - Implement robust element selection and interaction with Weibo's web interface
1 parent 8013f1a commit 0a6164d

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed

src/sync/common.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { VideoBaijiahao } from './video/baijiahao';
2828
import { ArticleBaijiahao } from './article/baijiahao';
2929
import { DynamicToutiao } from './dynamic/toutiao';
3030
import { ArticleToutiao } from './article/toutiao';
31+
import { VideoWeibo } from './video/weibo';
3132

3233
export interface SyncData {
3334
platforms: string[];
@@ -383,6 +384,15 @@ export const infoMap: Record<string, PlatformInfo> = {
383384
injectUrl: 'https://baijiahao.baidu.com/builder/rc/edit?type=videoV2',
384385
injectFunction: VideoBaijiahao,
385386
},
387+
VIDEO_WEIBO: {
388+
type: 'VIDEO',
389+
name: 'VIDEO_WEIBO',
390+
homeUrl: 'https://weibo.com/',
391+
faviconUrl: 'https://weibo.com/favicon.ico',
392+
platformName: chrome.i18n.getMessage('platformWeibo'),
393+
injectUrl: 'https://weibo.com',
394+
injectFunction: VideoWeibo,
395+
},
386396
};
387397

388398
export function getDefaultPlatformInfo(platform: string): PlatformInfo | null {

src/sync/video/weibo.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import type { VideoData, SyncData } from '../common';
2+
3+
export async function VideoWeibo(data: SyncData) {
4+
const { content, video, title } = data.data as VideoData;
5+
6+
function waitForElement(selector: string, timeout = 10000): Promise<Element> {
7+
return new Promise((resolve, reject) => {
8+
const element = document.querySelector(selector);
9+
if (element) {
10+
resolve(element);
11+
return;
12+
}
13+
14+
const observer = new MutationObserver(() => {
15+
const element = document.querySelector(selector);
16+
if (element) {
17+
resolve(element);
18+
observer.disconnect();
19+
}
20+
});
21+
22+
observer.observe(document.body, {
23+
childList: true,
24+
subtree: true,
25+
});
26+
27+
setTimeout(() => {
28+
observer.disconnect();
29+
reject(new Error(`Element with selector "${selector}" not found within ${timeout}ms`));
30+
}, timeout);
31+
});
32+
}
33+
34+
// 辅助函数:等待多个元素出现
35+
function waitForElements(selector: string, count: number, timeout = 30000): Promise<Element[]> {
36+
return new Promise((resolve, reject) => {
37+
const checkElements = () => {
38+
const elements = document.querySelectorAll(selector);
39+
if (elements.length >= count) {
40+
resolve(Array.from(elements));
41+
return;
42+
}
43+
44+
if (Date.now() - startTime > timeout) {
45+
reject(new Error(`未能在 ${timeout}ms 内找到 ${count} 个 "${selector}" 元素`));
46+
return;
47+
}
48+
49+
setTimeout(checkElements, 100);
50+
};
51+
52+
const startTime = Date.now();
53+
checkElements();
54+
});
55+
}
56+
57+
// 辅助函数:上传文件
58+
async function uploadFiles() {
59+
const fileInput = (await waitForElement('input[type="file"]')) as HTMLInputElement;
60+
if (!fileInput) {
61+
console.error('未找到文件输入元素');
62+
return;
63+
}
64+
65+
const dataTransfer = new DataTransfer();
66+
67+
// 只处理视频文件
68+
if (video.type.startsWith('video/')) {
69+
const response = await fetch(video.url);
70+
const blob = await response.blob();
71+
const videoFile = new File([blob], video.name, { type: video.type });
72+
console.log(`文件: ${videoFile.name} ${videoFile.type} ${videoFile.size}`);
73+
dataTransfer.items.add(videoFile);
74+
}
75+
76+
if (dataTransfer.files.length > 0) {
77+
fileInput.files = dataTransfer.files;
78+
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
79+
await new Promise((resolve) => setTimeout(resolve, 2000)); // 等待文件处理
80+
console.log('文件上传操作完成');
81+
} else {
82+
console.error('没有成功添加任何文件');
83+
}
84+
}
85+
86+
try {
87+
// 使用 findElementByText 函数查找输入元素
88+
const inputElement = (await waitForElement(
89+
'textarea[placeholder="有什么新鲜事想分享给大家?"]',
90+
)) as HTMLTextAreaElement;
91+
92+
if (!inputElement) {
93+
throw new Error('未找到微博输入框');
94+
}
95+
96+
// 组合标题和内容
97+
const fullContent = `${title}\n${content}`;
98+
99+
// 填写内容
100+
inputElement.value = fullContent;
101+
inputElement.dispatchEvent(new Event('input', { bubbles: true }));
102+
103+
console.log('成功填入微博内容');
104+
105+
// 处理视频上传
106+
if (video) {
107+
await uploadFiles();
108+
await waitForElements('i[title="删除"]', 1);
109+
}
110+
111+
console.log('成功填入微博内容和视频');
112+
113+
// 处理自动发布
114+
if (data.auto_publish) {
115+
const sendButtons = document.querySelectorAll('span.woo-button-content');
116+
const sendButton = Array.from(sendButtons).find((button) => button.textContent?.includes('发送'));
117+
118+
if (sendButton) {
119+
console.log('点击发送按钮');
120+
(sendButton as HTMLElement).click();
121+
await new Promise((resolve) => setTimeout(resolve, 3000));
122+
window.location.reload();
123+
} else {
124+
console.log("未找到'发送'按钮");
125+
}
126+
}
127+
} catch (error) {
128+
console.error('填入微博内容或上传视频时出错:', error);
129+
}
130+
}

0 commit comments

Comments
 (0)