Skip to content

Commit 3115245

Browse files
authored
74 feat weibo video (#77)
* feat: add Weibo video platform sync support - 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 * feat: improve Weibo video upload with content script and enhanced sync functionality - Add Weibo-specific content script for video upload handling - Modify Weibo sync function to support title and content input - Update platform configuration to target specific Weibo upload channel - Implement message-based video upload mechanism - Enhance element selection and interaction for Weibo platform
1 parent 0a6164d commit 3115245

File tree

3 files changed

+103
-53
lines changed

3 files changed

+103
-53
lines changed

src/contents/weibo-helper.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/* eslint-disable prefer-const */
2+
3+
export {};
4+
import type { PlasmoCSConfig } from 'plasmo';
5+
6+
export const config: PlasmoCSConfig = {
7+
matches: ['https://weibo.com/upload/channel*'],
8+
world: 'MAIN',
9+
run_at: 'document_start',
10+
};
11+
12+
let originalCreateElement = document.createElement.bind(document);
13+
let createdInputs: HTMLInputElement[] = [];
14+
15+
document.createElement = function (tagName, options) {
16+
let element = originalCreateElement(tagName, options);
17+
if (tagName.toLowerCase() === 'input') {
18+
createdInputs.push(element);
19+
console.log('createdInputs', element);
20+
}
21+
return element;
22+
};
23+
24+
interface UploadMessage {
25+
type: string;
26+
video: File;
27+
}
28+
29+
async function findElementByText(
30+
selector: string,
31+
text: string,
32+
maxRetries = 5,
33+
retryInterval = 1000,
34+
): Promise<Element | null> {
35+
for (let i = 0; i < maxRetries; i++) {
36+
const elements = document.querySelectorAll(selector);
37+
const element = Array.from(elements).find((element) => element.textContent?.includes(text));
38+
39+
if (element) {
40+
return element;
41+
}
42+
43+
console.log(`未找到包含文本 "${text}" 的元素,尝试次数:${i + 1}`);
44+
await new Promise((resolve) => setTimeout(resolve, retryInterval));
45+
}
46+
47+
console.error(`在 ${maxRetries} 次尝试后未找到包含文本 "${text}" 的元素`);
48+
return null;
49+
}
50+
51+
async function handleVideoUpload(event: MessageEvent) {
52+
const { data } = event as { data: UploadMessage };
53+
if (data.type !== 'WEIBO_VIDEO_UPLOAD') return;
54+
55+
window.removeEventListener('message', handleVideoUpload);
56+
57+
const video = data.video;
58+
59+
const uploadVideoButton = await findElementByText('span', '上传视频');
60+
if (!uploadVideoButton) {
61+
return;
62+
}
63+
64+
(uploadVideoButton as HTMLElement).click();
65+
await new Promise((resolve) => setTimeout(resolve, 500));
66+
67+
const uploadInput = createdInputs.find((input) => input.type === 'file' && input.id === '_ef');
68+
if (!uploadInput) {
69+
console.error('未找到上传输入框');
70+
return;
71+
}
72+
73+
const dataTransfer = new DataTransfer();
74+
dataTransfer.items.add(video);
75+
uploadInput.files = dataTransfer.files;
76+
77+
uploadInput.disabled = true;
78+
79+
await new Promise((resolve) => setTimeout(resolve, 1000));
80+
81+
uploadInput.disabled = false;
82+
uploadInput.dispatchEvent(new Event('change', { bubbles: true }));
83+
}
84+
85+
window.addEventListener('message', handleVideoUpload);

src/sync/common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ export const infoMap: Record<string, PlatformInfo> = {
390390
homeUrl: 'https://weibo.com/',
391391
faviconUrl: 'https://weibo.com/favicon.ico',
392392
platformName: chrome.i18n.getMessage('platformWeibo'),
393-
injectUrl: 'https://weibo.com',
393+
injectUrl: 'https://weibo.com/upload/channel',
394394
injectFunction: VideoWeibo,
395395
},
396396
};

src/sync/video/weibo.ts

Lines changed: 17 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -31,59 +31,21 @@ export async function VideoWeibo(data: SyncData) {
3131
});
3232
}
3333

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/')) {
34+
try {
35+
// 处理视频上传
36+
if (video) {
6937
const response = await fetch(video.url);
7038
const blob = await response.blob();
7139
const videoFile = new File([blob], video.name, { type: video.type });
7240
console.log(`文件: ${videoFile.name} ${videoFile.type} ${videoFile.size}`);
73-
dataTransfer.items.add(videoFile);
74-
}
7541

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('没有成功添加任何文件');
42+
window.postMessage({ type: 'WEIBO_VIDEO_UPLOAD', video: videoFile }, '*');
8343
}
84-
}
8544

86-
try {
45+
console.log('成功填入微博内容和视频');
46+
47+
await new Promise((resolve) => setTimeout(resolve, 1000));
48+
8749
// 使用 findElementByText 函数查找输入元素
8850
const inputElement = (await waitForElement(
8951
'textarea[placeholder="有什么新鲜事想分享给大家?"]',
@@ -94,21 +56,24 @@ export async function VideoWeibo(data: SyncData) {
9456
}
9557

9658
// 组合标题和内容
97-
const fullContent = `${title}\n${content}`;
59+
const fullContent = `${content}`;
9860

9961
// 填写内容
10062
inputElement.value = fullContent;
10163
inputElement.dispatchEvent(new Event('input', { bubbles: true }));
10264

10365
console.log('成功填入微博内容');
10466

105-
// 处理视频上传
106-
if (video) {
107-
await uploadFiles();
108-
await waitForElements('i[title="删除"]', 1);
67+
const titleInputElement = (await waitForElement('input[placeholder="填写标题(0~30个字)"]')) as HTMLInputElement;
68+
69+
if (!titleInputElement) {
70+
throw new Error('未找到微博标题输入框');
10971
}
11072

111-
console.log('成功填入微博内容和视频');
73+
titleInputElement.value = title;
74+
titleInputElement.dispatchEvent(new Event('input', { bubbles: true }));
75+
76+
console.log('成功填入微博标题');
11277

11378
// 处理自动发布
11479
if (data.auto_publish) {

0 commit comments

Comments
 (0)