|
|
@@ -431,10 +431,25 @@ async function handleAILoginSuccess(currentScreenshot: string) {
|
|
|
fetchingAccountInfo.value = true;
|
|
|
accountInfo.value = null;
|
|
|
|
|
|
- console.log('[AI] Extracting account info from screenshot...');
|
|
|
+ console.log('[AI] Extracting account info...');
|
|
|
+
|
|
|
+ // 多次尝试关闭可能的活动弹框(有些弹框可能需要多次点击)
|
|
|
+ console.log('[AI] Trying to close popups...');
|
|
|
+ for (let i = 0; i < 3; i++) {
|
|
|
+ const closed = await closeActivityPopup();
|
|
|
+ if (closed) {
|
|
|
+ console.log(`[AI] Closed popup on attempt ${i + 1}`);
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 500));
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // 尝试关闭可能的活动弹框
|
|
|
- await closeActivityPopup();
|
|
|
+ // 对于视频号平台,需要导航到首页才能获取完整的账号信息(视频号ID、作品数、粉丝数)
|
|
|
+ if (platform.value === 'weixin_video') {
|
|
|
+ console.log('[AI] Weixin Video: navigating to home page for complete account info...');
|
|
|
+ await navigateToWeixinVideoHomePage();
|
|
|
+ }
|
|
|
|
|
|
// 最多尝试 3 次提取账号信息
|
|
|
const MAX_EXTRACT_RETRIES = 3;
|
|
|
@@ -446,69 +461,121 @@ async function handleAILoginSuccess(currentScreenshot: string) {
|
|
|
worksCount: number;
|
|
|
} | null = null;
|
|
|
|
|
|
- for (let attempt = 1; attempt <= MAX_EXTRACT_RETRIES; attempt++) {
|
|
|
- console.log(`[AI] Extract attempt ${attempt}/${MAX_EXTRACT_RETRIES}...`);
|
|
|
+ // 优先从 HTML 中提取账号信息(不受遮罩/弹窗影响)
|
|
|
+ console.log('[AI] Trying to extract account info from HTML first...');
|
|
|
+ try {
|
|
|
+ const htmlExtractResult = await extractAccountInfoFromPage();
|
|
|
+ console.log('[AI] HTML extract result:', htmlExtractResult);
|
|
|
|
|
|
- try {
|
|
|
- // 每次重试都重新截图
|
|
|
- const screenshot = attempt === 1 ? currentScreenshot : await captureWebviewScreenshot();
|
|
|
- if (!screenshot) {
|
|
|
- console.warn('[AI] No screenshot for extraction');
|
|
|
- continue;
|
|
|
- }
|
|
|
+ if (htmlExtractResult && htmlExtractResult.accountName) {
|
|
|
+ // 检查 accountId 是否有效(不是时间戳生成的,也不是空)
|
|
|
+ const accountId = htmlExtractResult.accountId || '';
|
|
|
+ const isValidAccountId = accountId &&
|
|
|
+ !accountId.match(/_\d{13}$/) && // 不是时间戳结尾
|
|
|
+ !accountId.match(/_\d{10}$/); // 也不是 Unix 时间戳
|
|
|
|
|
|
- // 调用 AI 提取账号信息
|
|
|
- const extractResult = await aiApi.extractAccountInfo(screenshot, platform.value);
|
|
|
+ // 即使没有有效的 accountId,只要有用户名就可以先保存
|
|
|
+ // 后台刷新会获取正确的 accountId
|
|
|
+ extractedAccountInfo = {
|
|
|
+ accountId: isValidAccountId ? accountId : `${platform.value}_${Date.now()}`,
|
|
|
+ accountName: htmlExtractResult.accountName,
|
|
|
+ avatarUrl: htmlExtractResult.avatarUrl || '',
|
|
|
+ fansCount: htmlExtractResult.fansCount || 0,
|
|
|
+ worksCount: htmlExtractResult.worksCount || 0,
|
|
|
+ };
|
|
|
|
|
|
- console.log('[AI] Extract result:', extractResult);
|
|
|
+ if (isValidAccountId) {
|
|
|
+ console.log('[AI] Successfully extracted complete account info from HTML:', extractedAccountInfo);
|
|
|
+ } else {
|
|
|
+ console.log('[AI] Extracted account name but ID is not valid, will refresh later:', extractedAccountInfo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (htmlError) {
|
|
|
+ console.warn('[AI] HTML extraction failed:', htmlError);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果 HTML 提取失败,再用 AI 截图分析
|
|
|
+ if (!extractedAccountInfo) {
|
|
|
+ console.log('[AI] HTML extraction failed, falling back to screenshot AI...');
|
|
|
+
|
|
|
+ for (let attempt = 1; attempt <= MAX_EXTRACT_RETRIES; attempt++) {
|
|
|
+ console.log(`[AI] Extract attempt ${attempt}/${MAX_EXTRACT_RETRIES}...`);
|
|
|
|
|
|
- if (extractResult.found && extractResult.accountName) {
|
|
|
- // AI 成功提取到账号信息
|
|
|
- extractedAccountInfo = {
|
|
|
- accountId: extractResult.accountId || `${platform.value}_${Date.now()}`,
|
|
|
- accountName: extractResult.accountName,
|
|
|
- avatarUrl: '', // AI 返回的是描述,不是 URL,头像需要从页面 JS 获取
|
|
|
- fansCount: parseInt(String(extractResult.fansCount || 0)) || 0,
|
|
|
- worksCount: parseInt(String(extractResult.worksCount || 0)) || 0,
|
|
|
- };
|
|
|
-
|
|
|
- // 尝试从页面获取头像 URL(补充 AI 无法获取的信息)
|
|
|
- try {
|
|
|
- const avatarUrl = await getAvatarUrlFromPage();
|
|
|
- if (avatarUrl) {
|
|
|
- extractedAccountInfo.avatarUrl = avatarUrl;
|
|
|
- }
|
|
|
- } catch (e) {
|
|
|
- console.warn('[AI] Failed to get avatar URL from page:', e);
|
|
|
+ try {
|
|
|
+ // 每次重试都重新截图
|
|
|
+ const screenshot = attempt === 1 ? currentScreenshot : await captureWebviewScreenshot();
|
|
|
+ if (!screenshot) {
|
|
|
+ console.warn('[AI] No screenshot for extraction');
|
|
|
+ continue;
|
|
|
}
|
|
|
|
|
|
- console.log('[AI] Successfully extracted account info:', extractedAccountInfo);
|
|
|
- break;
|
|
|
- } else if (extractResult.navigationGuide) {
|
|
|
- // AI 提供了导航建议
|
|
|
- console.log('[AI] Navigation suggestion:', extractResult.navigationGuide);
|
|
|
- if (aiAnalysis.value) {
|
|
|
- aiAnalysis.value.suggestedAction = extractResult.navigationGuide || undefined;
|
|
|
+ // 调用 AI 提取账号信息
|
|
|
+ const extractResult = await aiApi.extractAccountInfo(screenshot, platform.value);
|
|
|
+
|
|
|
+ console.log('[AI] Extract result:', extractResult);
|
|
|
+
|
|
|
+ if (extractResult.found && extractResult.accountName) {
|
|
|
+ // AI 成功提取到账号信息
|
|
|
+ // 注意:worksCount 设为 0,让后台刷新来获取准确数据(AI 提取的作品数常常不准确)
|
|
|
+ extractedAccountInfo = {
|
|
|
+ accountId: extractResult.accountId || `${platform.value}_${Date.now()}`,
|
|
|
+ accountName: extractResult.accountName,
|
|
|
+ avatarUrl: '', // AI 返回的是描述,不是 URL,头像需要从页面 JS 获取
|
|
|
+ fansCount: parseInt(String(extractResult.fansCount || 0)) || 0,
|
|
|
+ worksCount: 0, // 由后台刷新获取准确数据
|
|
|
+ };
|
|
|
+
|
|
|
+ // 尝试从页面获取头像 URL(补充 AI 无法获取的信息)
|
|
|
+ try {
|
|
|
+ const avatarUrl = await getAvatarUrlFromPage();
|
|
|
+ if (avatarUrl) {
|
|
|
+ extractedAccountInfo.avatarUrl = avatarUrl;
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.warn('[AI] Failed to get avatar URL from page:', e);
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('[AI] Successfully extracted account info:', extractedAccountInfo);
|
|
|
+ break;
|
|
|
+ } else if (extractResult.navigationGuide) {
|
|
|
+ // AI 提供了导航建议
|
|
|
+ console.log('[AI] Navigation suggestion:', extractResult.navigationGuide);
|
|
|
+ if (aiAnalysis.value) {
|
|
|
+ aiAnalysis.value.suggestedAction = extractResult.navigationGuide || undefined;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果启用了自动操作,尝试执行导航操作
|
|
|
+ if (autoOperationEnabled.value && !isExecutingOperation.value) {
|
|
|
+ console.log('[AI] Attempting auto navigation...');
|
|
|
+ await performAutoOperation(`获取账号信息:${extractResult.navigationGuide}`);
|
|
|
+ // 导航后等待页面加载
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 2000));
|
|
|
+ } else {
|
|
|
+ showAIPanel.value = true;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // 如果启用了自动操作,尝试执行导航操作
|
|
|
- if (autoOperationEnabled.value && !isExecutingOperation.value) {
|
|
|
- console.log('[AI] Attempting auto navigation...');
|
|
|
- await performAutoOperation(`获取账号信息:${extractResult.navigationGuide}`);
|
|
|
- // 导航后等待页面加载
|
|
|
+ // 等待后重试
|
|
|
+ if (attempt < MAX_EXTRACT_RETRIES) {
|
|
|
+ // 如果 AI 提到弹窗遮挡,多次尝试关闭
|
|
|
+ const needClosePopup = extractResult?.navigationGuide?.includes('弹窗') ||
|
|
|
+ extractResult?.navigationGuide?.includes('遮挡') ||
|
|
|
+ extractResult?.navigationGuide?.includes('关闭');
|
|
|
+ if (needClosePopup) {
|
|
|
+ console.log('[AI] AI detected popup, trying to close...');
|
|
|
+ for (let i = 0; i < 3; i++) {
|
|
|
+ const closed = await closeActivityPopup();
|
|
|
+ if (!closed) break;
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 500));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ await closeActivityPopup();
|
|
|
+ }
|
|
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
|
- } else {
|
|
|
- showAIPanel.value = true;
|
|
|
}
|
|
|
+ } catch (error) {
|
|
|
+ console.error(`[AI] Extract attempt ${attempt} failed:`, error);
|
|
|
}
|
|
|
-
|
|
|
- // 等待后重试
|
|
|
- if (attempt < MAX_EXTRACT_RETRIES) {
|
|
|
- await closeActivityPopup();
|
|
|
- await new Promise(resolve => setTimeout(resolve, 2000));
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error(`[AI] Extract attempt ${attempt} failed:`, error);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -562,12 +629,13 @@ async function handleAILoginSuccess(currentScreenshot: string) {
|
|
|
!defaultNames.includes(retryResult.accountName);
|
|
|
|
|
|
if (retryIsValid) {
|
|
|
+ // 注意:worksCount 设为 0,让后台刷新来获取准确数据
|
|
|
extractedAccountInfo = {
|
|
|
accountId: retryResult.accountId || `${platform.value}_${Date.now()}`,
|
|
|
accountName: retryResult.accountName,
|
|
|
avatarUrl: '',
|
|
|
fansCount: parseInt(String(retryResult.fansCount || 0)) || 0,
|
|
|
- worksCount: parseInt(String(retryResult.worksCount || 0)) || 0,
|
|
|
+ worksCount: 0, // 由后台刷新获取准确数据
|
|
|
};
|
|
|
|
|
|
try {
|
|
|
@@ -851,24 +919,59 @@ async function getAvatarUrlFromPage(): Promise<string | null> {
|
|
|
(function() {
|
|
|
// 查找可能的头像图片
|
|
|
const avatarSelectors = [
|
|
|
+ // 通用头像选择器
|
|
|
'[class*="avatar"] img',
|
|
|
'[class*="Avatar"] img',
|
|
|
'img[class*="avatar"]',
|
|
|
'.user-avatar img',
|
|
|
'.user-info img',
|
|
|
'.ant-avatar img',
|
|
|
- '.header-user img'
|
|
|
+ '.header-user img',
|
|
|
+ // 微信视频号特定选择器
|
|
|
+ 'img[src*="wx.qlogo"]',
|
|
|
+ 'img[src*="mmbiz.qpic"]',
|
|
|
+ 'img[src*="thirdwx"]',
|
|
|
+ '.finder-avatar img',
|
|
|
+ '.account-avatar img',
|
|
|
+ // 顶部导航栏头像
|
|
|
+ '.header img',
|
|
|
+ 'nav img',
|
|
|
+ '.navbar img'
|
|
|
];
|
|
|
|
|
|
for (const selector of avatarSelectors) {
|
|
|
const imgs = document.querySelectorAll(selector);
|
|
|
for (const img of imgs) {
|
|
|
const src = img.src || '';
|
|
|
- if (src && src.startsWith('http') && !src.includes('logo') && !src.includes('icon')) {
|
|
|
+ if (src && src.startsWith('http') && !src.includes('logo') && !src.includes('icon') && !src.includes('banner')) {
|
|
|
+ console.log('[getAvatarUrlFromPage] Found avatar:', src);
|
|
|
return src;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ // 备选:查找所有圆形或适当大小的图片
|
|
|
+ const allImgs = document.querySelectorAll('img');
|
|
|
+ for (const img of allImgs) {
|
|
|
+ const src = img.src || '';
|
|
|
+ if (!src || !src.startsWith('http')) continue;
|
|
|
+ if (src.includes('logo') || src.includes('icon') || src.includes('banner')) continue;
|
|
|
+
|
|
|
+ const style = window.getComputedStyle(img);
|
|
|
+ const rect = img.getBoundingClientRect();
|
|
|
+ const isRound = style.borderRadius && (style.borderRadius.includes('50%') || parseInt(style.borderRadius) > 10);
|
|
|
+ const isAvatarSize = rect.width >= 24 && rect.width <= 100;
|
|
|
+ const isInHeader = rect.top < 100;
|
|
|
+
|
|
|
+ // 微信头像 URL 特征
|
|
|
+ const isWeixinAvatar = src.includes('wx.qlogo') || src.includes('mmbiz') || src.includes('thirdwx');
|
|
|
+
|
|
|
+ if ((isWeixinAvatar || (isRound && isAvatarSize) || (isInHeader && isAvatarSize))) {
|
|
|
+ console.log('[getAvatarUrlFromPage] Found avatar via fallback:', src);
|
|
|
+ return src;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
return null;
|
|
|
})()
|
|
|
`;
|
|
|
@@ -1658,16 +1761,202 @@ async function extractAccountInfoFromPage(): Promise<{
|
|
|
(function() {
|
|
|
const result = { accountId: '', accountName: '', avatarUrl: '', fansCount: 0, worksCount: 0 };
|
|
|
|
|
|
- const avatarEl = document.querySelector('.user-avatar img, [class*="avatar"] img');
|
|
|
- if (avatarEl) result.avatarUrl = avatarEl.src || '';
|
|
|
-
|
|
|
- const nameEl = document.querySelector('.user-name, [class*="userName"], [class*="nickname"]');
|
|
|
- if (nameEl) result.accountName = nameEl.textContent?.trim() || '';
|
|
|
-
|
|
|
- const uidMatch = document.cookie.match(/customerClientId=([^;]+)/) ||
|
|
|
- document.cookie.match(/web_session=([^;]+)/);
|
|
|
- if (uidMatch) result.accountId = 'xiaohongshu_' + uidMatch[1].slice(0, 16);
|
|
|
+ try {
|
|
|
+ console.log('[Xiaohongshu Extract] Starting extraction...');
|
|
|
+ console.log('[Xiaohongshu Extract] Current URL:', window.location.href);
|
|
|
+
|
|
|
+ // 1. 查找用户名 - 小红书创作平台用户名通常在右上角或侧边栏
|
|
|
+ const nameSelectors = [
|
|
|
+ // 创作平台顶部用户名(常见位置)
|
|
|
+ '.creator-header [class*="name"]',
|
|
|
+ '.user-info [class*="name"]',
|
|
|
+ // 右上角下拉菜单中的用户名
|
|
|
+ '[class*="dropdown"] [class*="name"]',
|
|
|
+ '[class*="user"] [class*="name"]',
|
|
|
+ // 通用选择器
|
|
|
+ '[class*="userName"]',
|
|
|
+ '[class*="UserName"]',
|
|
|
+ '[class*="user-name"]',
|
|
|
+ '[class*="nickname"]',
|
|
|
+ '[class*="NickName"]',
|
|
|
+ '[class*="nick-name"]',
|
|
|
+ // Ant Design 或其他UI库的组件
|
|
|
+ '.ant-dropdown-trigger [class*="name"]'
|
|
|
+ ];
|
|
|
+
|
|
|
+ for (const selector of nameSelectors) {
|
|
|
+ const els = document.querySelectorAll(selector);
|
|
|
+ for (const el of els) {
|
|
|
+ const text = el.textContent?.trim() || '';
|
|
|
+ console.log('[Xiaohongshu Extract] Checking selector:', selector, 'text:', text);
|
|
|
+ if (text.length >= 2 && text.length <= 30 &&
|
|
|
+ !/登录|注册|小红书|创作|发布|数据|内容|消息|设置|帮助|退出|首页|笔记|灵感|学院|活动/.test(text) &&
|
|
|
+ /[\u4e00-\u9fa5a-zA-Z0-9]/.test(text)) {
|
|
|
+ result.accountName = text;
|
|
|
+ console.log('[Xiaohongshu Extract] Found name via selector:', text);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (result.accountName) break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 备选方案:在整个页面中查找可能是用户名的文本
|
|
|
+ if (!result.accountName) {
|
|
|
+ console.log('[Xiaohongshu Extract] Trying fallback method for name...');
|
|
|
+ // 查找页面右上角区域的文本(通常是用户名所在位置)
|
|
|
+ const allElements = document.querySelectorAll('span, div, p, a');
|
|
|
+ const candidates = [];
|
|
|
+
|
|
|
+ for (const el of allElements) {
|
|
|
+ const rect = el.getBoundingClientRect();
|
|
|
+ // 只查找右上角区域(通常用户信息在这里)
|
|
|
+ if (rect.right < window.innerWidth * 0.5 || rect.top > 200) continue;
|
|
|
+
|
|
|
+ // 获取直接文本内容
|
|
|
+ const directText = Array.from(el.childNodes)
|
|
|
+ .filter(node => node.nodeType === Node.TEXT_NODE)
|
|
|
+ .map(node => node.textContent?.trim())
|
|
|
+ .join('')
|
|
|
+ .trim();
|
|
|
+
|
|
|
+ if (!directText) continue;
|
|
|
+
|
|
|
+ if (directText.length >= 2 && directText.length <= 20 &&
|
|
|
+ /[\u4e00-\u9fa5]/.test(directText) &&
|
|
|
+ !/登录|注册|小红书|创作|发布|数据|内容|消息|设置|帮助|退出|首页|笔记|灵感|学院|活动|图文|视频|直播|百科/.test(directText)) {
|
|
|
+ candidates.push({ text: directText, right: rect.right, top: rect.top });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按右上角位置排序
|
|
|
+ candidates.sort((a, b) => (b.right - a.right) + (a.top - b.top));
|
|
|
+ console.log('[Xiaohongshu Extract] Candidates:', candidates.slice(0, 5));
|
|
|
+
|
|
|
+ if (candidates.length > 0) {
|
|
|
+ result.accountName = candidates[0].text;
|
|
|
+ console.log('[Xiaohongshu Extract] Using fallback name:', result.accountName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 查找头像
|
|
|
+ const avatarSelectors = [
|
|
|
+ // 创作平台头像
|
|
|
+ '.creator-header img',
|
|
|
+ '.user-info img',
|
|
|
+ '[class*="avatar"] img',
|
|
|
+ '[class*="Avatar"] img',
|
|
|
+ 'img[class*="avatar"]',
|
|
|
+ 'img[class*="Avatar"]',
|
|
|
+ // 右上角用户头像
|
|
|
+ '.ant-avatar img',
|
|
|
+ '.ant-avatar-image'
|
|
|
+ ];
|
|
|
+
|
|
|
+ for (const selector of avatarSelectors) {
|
|
|
+ const els = document.querySelectorAll(selector);
|
|
|
+ for (const el of els) {
|
|
|
+ const src = el.src || el.style?.backgroundImage?.match(/url\\(["']?([^"')]+)["']?\\)/)?.[1];
|
|
|
+ if (src && src.startsWith('http') && !src.includes('logo') && !src.includes('icon')) {
|
|
|
+ result.avatarUrl = src;
|
|
|
+ console.log('[Xiaohongshu Extract] Found avatar:', src);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (result.avatarUrl) break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 备选:查找所有图片,找到可能是头像的(圆形、适当大小)
|
|
|
+ if (!result.avatarUrl) {
|
|
|
+ const imgs = Array.from(document.querySelectorAll('img'));
|
|
|
+ for (const img of imgs) {
|
|
|
+ const src = img.src || '';
|
|
|
+ const style = window.getComputedStyle(img);
|
|
|
+ const isRound = style.borderRadius && (style.borderRadius.includes('50%') || parseInt(style.borderRadius) > 15);
|
|
|
+ const isAvatarSize = img.width >= 24 && img.width <= 100;
|
|
|
+ const rect = img.getBoundingClientRect();
|
|
|
+ const isInTopArea = rect.top < 150;
|
|
|
+
|
|
|
+ if ((isRound || isInTopArea) && isAvatarSize && src.startsWith('http') &&
|
|
|
+ !src.includes('logo') && !src.includes('icon') && !src.includes('banner')) {
|
|
|
+ result.avatarUrl = src;
|
|
|
+ console.log('[Xiaohongshu Extract] Found avatar via fallback:', src);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 查找粉丝数
|
|
|
+ const bodyText = document.body.innerText;
|
|
|
+ const fansMatch = bodyText.match(/粉丝[\\s::]*([0-9,\\.]+[万亿]?)/);
|
|
|
+ if (fansMatch) {
|
|
|
+ let fansStr = fansMatch[1].replace(/,/g, '');
|
|
|
+ if (fansStr.includes('万')) {
|
|
|
+ result.fansCount = Math.round(parseFloat(fansStr) * 10000);
|
|
|
+ } else if (fansStr.includes('亿')) {
|
|
|
+ result.fansCount = Math.round(parseFloat(fansStr) * 100000000);
|
|
|
+ } else {
|
|
|
+ result.fansCount = parseInt(fansStr) || 0;
|
|
|
+ }
|
|
|
+ console.log('[Xiaohongshu Extract] Found fans:', result.fansCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 优先从页面获取小红书号作为 account_id
|
|
|
+ // 小红书号格式:通常是 "小红书号:xxxxxxx" 或 "小红书号:xxxxxxx"
|
|
|
+ const xhsIdMatch = bodyText.match(/小红书号[::]\s*([a-zA-Z0-9_]+)/) ||
|
|
|
+ bodyText.match(/红书号[::]\s*([a-zA-Z0-9_]+)/);
|
|
|
+ if (xhsIdMatch) {
|
|
|
+ result.accountId = 'xiaohongshu_' + xhsIdMatch[1];
|
|
|
+ console.log('[Xiaohongshu Extract] Found 小红书号 from page:', result.accountId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. 备选:尝试从页面元素中查找小红书号
|
|
|
+ if (!result.accountId) {
|
|
|
+ // 查找可能包含小红书号的元素
|
|
|
+ const idSelectors = [
|
|
|
+ '[class*="redId"]',
|
|
|
+ '[class*="red-id"]',
|
|
|
+ '[class*="userId"]',
|
|
|
+ '[class*="user-id"]'
|
|
|
+ ];
|
|
|
+ for (const selector of idSelectors) {
|
|
|
+ const el = document.querySelector(selector);
|
|
|
+ const text = el?.textContent?.trim();
|
|
|
+ if (text && /^[a-zA-Z0-9_]+$/.test(text) && text.length >= 6 && text.length <= 30) {
|
|
|
+ result.accountId = 'xiaohongshu_' + text;
|
|
|
+ console.log('[Xiaohongshu Extract] Found ID from selector:', result.accountId);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 8. 备选:从 URL 中查找用户 ID
|
|
|
+ if (!result.accountId) {
|
|
|
+ const urlIdMatch = window.location.href.match(/user\\/([a-f0-9]+)/);
|
|
|
+ if (urlIdMatch) {
|
|
|
+ result.accountId = 'xiaohongshu_' + urlIdMatch[1];
|
|
|
+ console.log('[Xiaohongshu Extract] Found ID from URL:', result.accountId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 9. 最后备选:从 Cookie 获取
|
|
|
+ if (!result.accountId) {
|
|
|
+ const uidMatch = document.cookie.match(/customerClientId=([^;]+)/) ||
|
|
|
+ document.cookie.match(/customer-client-id=([^;]+)/) ||
|
|
|
+ document.cookie.match(/web_session=([^;]+)/);
|
|
|
+ if (uidMatch) {
|
|
|
+ result.accountId = 'xiaohongshu_' + uidMatch[1].slice(0, 20);
|
|
|
+ console.log('[Xiaohongshu Extract] Found ID from cookie:', result.accountId);
|
|
|
+ } else {
|
|
|
+ result.accountId = 'xiaohongshu_' + Date.now();
|
|
|
+ console.log('[Xiaohongshu Extract] Using timestamp as fallback ID');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (e) {
|
|
|
+ console.error('[Xiaohongshu Extract] Error:', e);
|
|
|
+ }
|
|
|
|
|
|
+ console.log('[Xiaohongshu Extract] Final result:', result);
|
|
|
return result;
|
|
|
})()
|
|
|
`,
|
|
|
@@ -1847,74 +2136,188 @@ async function extractAccountInfoFromPage(): Promise<{
|
|
|
const result = { accountId: '', accountName: '', avatarUrl: '', fansCount: 0, worksCount: 0 };
|
|
|
|
|
|
try {
|
|
|
- // 1. 查找头像 - 视频号创作者平台头像
|
|
|
- const imgs = Array.from(document.querySelectorAll('img'));
|
|
|
- for (const img of imgs) {
|
|
|
+ console.log('[Weixin Extract] Starting extraction...');
|
|
|
+ console.log('[Weixin Extract] Current URL:', window.location.href);
|
|
|
+
|
|
|
+ // 1. 查找头像 - 视频号创作者平台头像(优先微信特有域名)
|
|
|
+ // 先查找微信头像 URL 特征的图片
|
|
|
+ const allImgs = Array.from(document.querySelectorAll('img'));
|
|
|
+ for (const img of allImgs) {
|
|
|
const src = img.src || '';
|
|
|
- const className = img.className || '';
|
|
|
- const parentClass = img.parentElement?.className || '';
|
|
|
-
|
|
|
- // 判断是否是头像图片
|
|
|
- const isAvatar = className.includes('avatar') ||
|
|
|
- parentClass.includes('avatar') ||
|
|
|
- src.includes('mmbiz') ||
|
|
|
- src.includes('wx.qlogo');
|
|
|
-
|
|
|
- if (isAvatar && src.startsWith('http')) {
|
|
|
+ // 微信头像通常包含这些域名
|
|
|
+ if (src.includes('wx.qlogo') || src.includes('mmbiz.qpic') || src.includes('thirdwx.qlogo')) {
|
|
|
result.avatarUrl = src;
|
|
|
+ console.log('[Weixin Extract] Found avatar by URL pattern:', src);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 2. 查找视频号ID - 格式通常是 "视频号ID:xxx" 或页面元素
|
|
|
+ // 2. 如果没找到,尝试通过选择器查找
|
|
|
+ if (!result.avatarUrl) {
|
|
|
+ const avatarSelectors = [
|
|
|
+ '.finder-avatar img',
|
|
|
+ '.account-avatar img',
|
|
|
+ '.user-avatar img',
|
|
|
+ '[class*="avatar"] img',
|
|
|
+ '[class*="Avatar"] img',
|
|
|
+ 'img[class*="avatar"]',
|
|
|
+ '.header-user img',
|
|
|
+ '.header img',
|
|
|
+ 'nav img',
|
|
|
+ '.navbar img'
|
|
|
+ ];
|
|
|
+
|
|
|
+ for (const selector of avatarSelectors) {
|
|
|
+ const el = document.querySelector(selector);
|
|
|
+ if (el && el.src && el.src.startsWith('http')) {
|
|
|
+ const src = el.src;
|
|
|
+ if (!src.includes('logo') && !src.includes('icon') && !src.includes('banner')) {
|
|
|
+ result.avatarUrl = src;
|
|
|
+ console.log('[Weixin Extract] Found avatar by selector:', selector, src);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 备选:查找所有圆形或适当大小且在页面顶部的图片
|
|
|
+ if (!result.avatarUrl) {
|
|
|
+ for (const img of allImgs) {
|
|
|
+ const src = img.src || '';
|
|
|
+ if (!src || !src.startsWith('http')) continue;
|
|
|
+ if (src.includes('logo') || src.includes('icon') || src.includes('banner')) continue;
|
|
|
+
|
|
|
+ const style = window.getComputedStyle(img);
|
|
|
+ const rect = img.getBoundingClientRect();
|
|
|
+ const isRound = style.borderRadius && (style.borderRadius.includes('50%') || parseInt(style.borderRadius) > 10);
|
|
|
+ const isAvatarSize = rect.width >= 24 && rect.width <= 80;
|
|
|
+ const isInHeader = rect.top < 120;
|
|
|
+
|
|
|
+ if ((isRound && isAvatarSize) || (isInHeader && isAvatarSize)) {
|
|
|
+ result.avatarUrl = src;
|
|
|
+ console.log('[Weixin Extract] Found avatar by fallback:', src);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 优先查找视频号ID - 格式通常是 "视频号ID:xxx" 或 "视频号ID:xxx"
|
|
|
const bodyText = document.body.innerText;
|
|
|
- const finderIdMatch = bodyText.match(/视频号ID[::]\s*([a-zA-Z0-9_]+)/);
|
|
|
+ // 匹配多种格式:视频号ID:xxx、视频号ID:xxx、视频号ID xxx
|
|
|
+ const finderIdMatch = bodyText.match(/视频号ID[::\s]*([a-zA-Z0-9_]+)/) ||
|
|
|
+ bodyText.match(/视频号[::\s]*ID[::\s]*([a-zA-Z0-9_]+)/);
|
|
|
if (finderIdMatch) {
|
|
|
- result.accountId = 'weixin_' + finderIdMatch[1];
|
|
|
+ result.accountId = 'weixin_video_' + finderIdMatch[1];
|
|
|
+ console.log('[Weixin Extract] Found 视频号ID from page text:', result.accountId);
|
|
|
}
|
|
|
|
|
|
- // 3. 查找昵称 - 在用户信息区域
|
|
|
- const nicknameEl = document.querySelector('.finder-nickname, [class*="nickname"], [class*="user-name"], h2.name');
|
|
|
- if (nicknameEl) {
|
|
|
- result.accountName = nicknameEl.textContent?.trim() || '';
|
|
|
+ // 4.1 备选:从页面元素中查找视频号ID
|
|
|
+ if (!result.accountId) {
|
|
|
+ // 查找可能包含视频号ID的元素
|
|
|
+ const idSelectors = [
|
|
|
+ '[class*="finder-id"]',
|
|
|
+ '[class*="finderId"]',
|
|
|
+ '[class*="channel-id"]',
|
|
|
+ '[class*="channelId"]'
|
|
|
+ ];
|
|
|
+ for (const selector of idSelectors) {
|
|
|
+ const el = document.querySelector(selector);
|
|
|
+ const text = el?.textContent?.trim();
|
|
|
+ if (text && /^[a-zA-Z0-9_]+$/.test(text) && text.length >= 10 && text.length <= 30) {
|
|
|
+ result.accountId = 'weixin_video_' + text;
|
|
|
+ console.log('[Weixin Extract] Found ID from selector:', result.accountId);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // 4. 备选:遍历页面查找看起来像用户名的文本
|
|
|
+ // 5. 查找昵称 - 在用户信息区域
|
|
|
+ const nameSelectors = [
|
|
|
+ '.finder-nickname',
|
|
|
+ '.account-name',
|
|
|
+ '.user-name',
|
|
|
+ '[class*="nickname"]',
|
|
|
+ '[class*="userName"]',
|
|
|
+ '[class*="user-name"]',
|
|
|
+ '.header-user-name',
|
|
|
+ 'h2.name',
|
|
|
+ '.name-text'
|
|
|
+ ];
|
|
|
+
|
|
|
+ for (const selector of nameSelectors) {
|
|
|
+ const el = document.querySelector(selector);
|
|
|
+ const text = el?.textContent?.trim();
|
|
|
+ if (text && text.length >= 2 && text.length <= 30) {
|
|
|
+ result.accountName = text;
|
|
|
+ console.log('[Weixin Extract] Found name by selector:', selector, text);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 备选:遍历页面查找看起来像用户名的文本
|
|
|
if (!result.accountName) {
|
|
|
const candidates = [];
|
|
|
- document.querySelectorAll('span, div, h2').forEach(el => {
|
|
|
- const text = el.innerText?.trim() || '';
|
|
|
+ document.querySelectorAll('span, div, h2, h3').forEach(el => {
|
|
|
const rect = el.getBoundingClientRect();
|
|
|
- // 筛选条件:可见、长度适中、包含中文或字母
|
|
|
- if (rect.width > 0 && text.length >= 2 && text.length <= 20 &&
|
|
|
- /[\u4e00-\u9fa5a-zA-Z]/.test(text) &&
|
|
|
- !/粉丝|关注|获赞|作品|发布|数据|登录|注册|视频号ID/.test(text)) {
|
|
|
- candidates.push({ text, top: rect.top, left: rect.left });
|
|
|
+ // 只在页面顶部区域查找
|
|
|
+ if (rect.top > 200 || rect.width === 0) return;
|
|
|
+
|
|
|
+ // 获取直接文本内容
|
|
|
+ const directText = Array.from(el.childNodes)
|
|
|
+ .filter(node => node.nodeType === Node.TEXT_NODE)
|
|
|
+ .map(node => node.textContent?.trim())
|
|
|
+ .join('')
|
|
|
+ .trim();
|
|
|
+
|
|
|
+ if (directText && directText.length >= 2 && directText.length <= 20 &&
|
|
|
+ /[\u4e00-\u9fa5a-zA-Z]/.test(directText) &&
|
|
|
+ !/粉丝|关注|获赞|作品|发布|数据|登录|注册|视频号|首页|创作|评论|消息|设置/.test(directText)) {
|
|
|
+ candidates.push({ text: directText, top: rect.top, left: rect.left });
|
|
|
}
|
|
|
});
|
|
|
- // 优先选择靠上方、靠左侧的(通常是用户名位置)
|
|
|
- candidates.sort((a, b) => (a.top + a.left) - (b.top + b.left));
|
|
|
+ // 优先选择靠上方的
|
|
|
+ candidates.sort((a, b) => a.top - b.top);
|
|
|
+ console.log('[Weixin Extract] Name candidates:', candidates.slice(0, 5));
|
|
|
if (candidates.length > 0) {
|
|
|
result.accountName = candidates[0].text;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 5. 从 Cookie 或 URL 获取唯一标识作为备选
|
|
|
- if (!result.accountId) {
|
|
|
- const uidMatch = document.cookie.match(/wxuin=([^;]+)/) ||
|
|
|
- document.cookie.match(/pass_ticket=([^;]+)/);
|
|
|
- if (uidMatch) {
|
|
|
- result.accountId = 'weixin_' + uidMatch[1].slice(0, 16);
|
|
|
+ // 7. 查找粉丝数(视频号使用"关注者"表示粉丝)
|
|
|
+ const fansMatch = bodyText.match(/关注者[\\s::]*([0-9,\\.]+[万亿]?)/) ||
|
|
|
+ bodyText.match(/粉丝[\\s::]*([0-9,\\.]+[万亿]?)/);
|
|
|
+ if (fansMatch) {
|
|
|
+ let fansStr = fansMatch[1].replace(/,/g, '');
|
|
|
+ if (fansStr.includes('万')) {
|
|
|
+ result.fansCount = Math.round(parseFloat(fansStr) * 10000);
|
|
|
+ } else if (fansStr.includes('亿')) {
|
|
|
+ result.fansCount = Math.round(parseFloat(fansStr) * 100000000);
|
|
|
} else {
|
|
|
- result.accountId = 'weixin_' + Date.now();
|
|
|
+ result.fansCount = parseInt(fansStr) || 0;
|
|
|
}
|
|
|
+ console.log('[Weixin Extract] Found fans (关注者):', result.fansCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7.1 查找作品数(视频号显示为"视频 X")
|
|
|
+ const worksMatch = bodyText.match(/视频[\\s::]*([0-9]+)/) ||
|
|
|
+ bodyText.match(/作品[\\s::]*([0-9]+)/);
|
|
|
+ if (worksMatch) {
|
|
|
+ result.worksCount = parseInt(worksMatch[1]) || 0;
|
|
|
+ console.log('[Weixin Extract] Found works (视频):', result.worksCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 8. 不再从 Cookie 获取 ID,只有真正的视频号ID才有效
|
|
|
+ // 如果没有找到视频号ID,返回空字符串,让后续流程处理
|
|
|
+ if (!result.accountId) {
|
|
|
+ console.log('[Weixin Extract] No valid 视频号ID found, will use fallback');
|
|
|
+ // 不设置默认值,让后续流程判断是否需要重新获取
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
console.error('[Weixin Extract] Error:', e);
|
|
|
}
|
|
|
|
|
|
- console.log('[Weixin Extract] Result:', result);
|
|
|
+ console.log('[Weixin Extract] Final result:', result);
|
|
|
return result;
|
|
|
})()
|
|
|
`,
|
|
|
@@ -1954,44 +2357,88 @@ async function closeActivityPopup(): Promise<boolean> {
|
|
|
(function() {
|
|
|
let closed = false;
|
|
|
|
|
|
- // 百家号活动弹框关闭
|
|
|
- const baijiahaoClose = document.querySelectorAll(
|
|
|
- '.dialog-close, .modal-close, .popup-close, ' +
|
|
|
- '[class*="close-btn"], [class*="closeBtn"], ' +
|
|
|
- '.ant-modal-close, .el-dialog__close, ' +
|
|
|
- '[class*="dialog"] [class*="close"], ' +
|
|
|
- '[class*="modal"] [class*="close"], ' +
|
|
|
- '[class*="popup"] [class*="close"], ' +
|
|
|
- 'button[aria-label="Close"], ' +
|
|
|
- '.mask-close, [class*="mask"] [class*="close"]'
|
|
|
- );
|
|
|
+ // 通用关闭按钮选择器
|
|
|
+ const closeSelectors = [
|
|
|
+ // 通用关闭按钮
|
|
|
+ '.dialog-close, .modal-close, .popup-close',
|
|
|
+ '[class*="close-btn"], [class*="closeBtn"], [class*="close-icon"], [class*="closeIcon"]',
|
|
|
+ '.ant-modal-close, .el-dialog__close, .el-message-box__close',
|
|
|
+ '[class*="dialog"] [class*="close"], [class*="modal"] [class*="close"]',
|
|
|
+ '[class*="popup"] [class*="close"], [class*="toast"] [class*="close"]',
|
|
|
+ 'button[aria-label="Close"], button[aria-label="关闭"]',
|
|
|
+ '.mask-close, [class*="mask"] [class*="close"]',
|
|
|
+ // 小红书特定选择器
|
|
|
+ '[class*="guide"] [class*="close"]',
|
|
|
+ '[class*="tip"] [class*="close"]',
|
|
|
+ '[class*="notice"] [class*="close"]',
|
|
|
+ '[class*="banner"] [class*="close"]',
|
|
|
+ '[class*="float"] [class*="close"]',
|
|
|
+ '.close, .icon-close, .btn-close',
|
|
|
+ // X 按钮(通常是 SVG 或特殊字符)
|
|
|
+ 'button svg[class*="close"]',
|
|
|
+ '[role="button"][class*="close"]',
|
|
|
+ // 确认/知道了/我知道了 按钮(用于关闭提示)
|
|
|
+ 'button:contains("知道了")', 'button:contains("我知道了")',
|
|
|
+ 'button:contains("确定")', 'button:contains("关闭")',
|
|
|
+ '[class*="confirm"], [class*="ok-btn"]'
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 尝试所有关闭按钮
|
|
|
+ for (const selector of closeSelectors) {
|
|
|
+ try {
|
|
|
+ const elements = document.querySelectorAll(selector);
|
|
|
+ for (const btn of elements) {
|
|
|
+ if (btn && btn.offsetParent !== null) {
|
|
|
+ const rect = btn.getBoundingClientRect();
|
|
|
+ // 确保按钮可见且在视口内
|
|
|
+ if (rect.width > 0 && rect.height > 0 && rect.top >= 0 && rect.left >= 0) {
|
|
|
+ btn.click();
|
|
|
+ closed = true;
|
|
|
+ console.log('[closeActivityPopup] Clicked:', selector);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ // 忽略无效选择器
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- for (const btn of baijiahaoClose) {
|
|
|
- if (btn && btn.offsetParent !== null) {
|
|
|
- btn.click();
|
|
|
- closed = true;
|
|
|
- console.log('[closeActivityPopup] Clicked close button');
|
|
|
+ // 特殊处理:查找包含"关闭"、"知道了"、"确定"文字的按钮
|
|
|
+ const allButtons = document.querySelectorAll('button, [role="button"], a[class*="btn"]');
|
|
|
+ for (const btn of allButtons) {
|
|
|
+ const text = btn.textContent?.trim() || '';
|
|
|
+ if (['关闭', '知道了', '我知道了', '确定', '取消', '跳过', '暂不', '稍后再说'].some(t => text.includes(t))) {
|
|
|
+ if (btn.offsetParent !== null) {
|
|
|
+ const rect = btn.getBoundingClientRect();
|
|
|
+ if (rect.width > 0 && rect.height > 0) {
|
|
|
+ btn.click();
|
|
|
+ closed = true;
|
|
|
+ console.log('[closeActivityPopup] Clicked button with text:', text);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 尝试点击遮罩层外部关闭
|
|
|
const masks = document.querySelectorAll(
|
|
|
- '.ant-modal-mask, .el-overlay, [class*="mask"], [class*="overlay"]'
|
|
|
+ '.ant-modal-mask, .el-overlay, [class*="mask"], [class*="overlay"], [class*="backdrop"]'
|
|
|
);
|
|
|
for (const mask of masks) {
|
|
|
const style = window.getComputedStyle(mask);
|
|
|
- if (style.display !== 'none' && style.visibility !== 'hidden') {
|
|
|
+ if (style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0') {
|
|
|
// 查找是否有可点击关闭的按钮
|
|
|
- const closeInMask = mask.querySelector('[class*="close"]');
|
|
|
- if (closeInMask) {
|
|
|
+ const closeInMask = mask.querySelector('[class*="close"], .close, button');
|
|
|
+ if (closeInMask && closeInMask.offsetParent !== null) {
|
|
|
closeInMask.click();
|
|
|
closed = true;
|
|
|
+ console.log('[closeActivityPopup] Clicked close in mask');
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 按 ESC 键尝试关闭
|
|
|
- document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', keyCode: 27 }));
|
|
|
+ document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', keyCode: 27, bubbles: true }));
|
|
|
+ document.body.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', keyCode: 27, bubbles: true }));
|
|
|
|
|
|
return closed;
|
|
|
})()
|
|
|
@@ -2001,7 +2448,7 @@ async function closeActivityPopup(): Promise<boolean> {
|
|
|
if (result) {
|
|
|
console.log('[closeActivityPopup] Successfully closed popup');
|
|
|
// 等待弹框关闭动画
|
|
|
- await new Promise(resolve => setTimeout(resolve, 500));
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 800));
|
|
|
}
|
|
|
return result;
|
|
|
} catch (error) {
|
|
|
@@ -2012,6 +2459,70 @@ async function closeActivityPopup(): Promise<boolean> {
|
|
|
|
|
|
// 旧的 quickLoginSuccess 函数已被 handleAILoginSuccess 替代
|
|
|
|
|
|
+// 视频号:导航到首页获取完整账号信息
|
|
|
+async function navigateToWeixinVideoHomePage(): Promise<boolean> {
|
|
|
+ const webview = webviewRef.value;
|
|
|
+ if (!webview) return false;
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 获取当前 URL
|
|
|
+ const currentUrl = webview.getURL ? webview.getURL() : (webview.src || '');
|
|
|
+ console.log('[WeixinVideo] Current URL:', currentUrl);
|
|
|
+
|
|
|
+ // 如果已经在首页,不需要导航
|
|
|
+ if (currentUrl.includes('/platform/home') || currentUrl.includes('/platform/post/list')) {
|
|
|
+ console.log('[WeixinVideo] Already on home page');
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用 webview 的 src 属性直接导航(避免 iframe 跨域问题)
|
|
|
+ console.log('[WeixinVideo] Navigating to home page via webview.src...');
|
|
|
+ const targetUrl = 'https://channels.weixin.qq.com/platform/home';
|
|
|
+
|
|
|
+ // 设置 src 并等待加载完成
|
|
|
+ return new Promise((resolve) => {
|
|
|
+ const onDidFinishLoad = () => {
|
|
|
+ console.log('[WeixinVideo] Page loaded');
|
|
|
+ webview.removeEventListener('did-finish-load', onDidFinishLoad);
|
|
|
+ // 页面加载完成后,等待一段时间让内容渲染
|
|
|
+ setTimeout(async () => {
|
|
|
+ // 再次尝试关闭可能出现的弹窗
|
|
|
+ for (let i = 0; i < 3; i++) {
|
|
|
+ const closed = await closeActivityPopup();
|
|
|
+ if (!closed) break;
|
|
|
+ await new Promise(r => setTimeout(r, 500));
|
|
|
+ }
|
|
|
+ console.log('[WeixinVideo] Navigation complete');
|
|
|
+ resolve(true);
|
|
|
+ }, 2000);
|
|
|
+ };
|
|
|
+
|
|
|
+ const onDidFailLoad = () => {
|
|
|
+ console.error('[WeixinVideo] Page load failed');
|
|
|
+ webview.removeEventListener('did-fail-load', onDidFailLoad);
|
|
|
+ resolve(false);
|
|
|
+ };
|
|
|
+
|
|
|
+ webview.addEventListener('did-finish-load', onDidFinishLoad);
|
|
|
+ webview.addEventListener('did-fail-load', onDidFailLoad);
|
|
|
+
|
|
|
+ // 设置超时
|
|
|
+ setTimeout(() => {
|
|
|
+ webview.removeEventListener('did-finish-load', onDidFinishLoad);
|
|
|
+ webview.removeEventListener('did-fail-load', onDidFailLoad);
|
|
|
+ console.log('[WeixinVideo] Navigation timeout, continuing anyway...');
|
|
|
+ resolve(true);
|
|
|
+ }, 10000);
|
|
|
+
|
|
|
+ // 执行导航
|
|
|
+ webview.src = targetUrl;
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ console.error('[WeixinVideo] Navigation failed:', error);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// 检查是否有登录 cookie
|
|
|
function checkHasLoginCookie(cookies: Electron.Cookie[]): boolean {
|
|
|
// 根据不同平台检查特定的 cookie
|