|
|
@@ -364,8 +364,11 @@ async function extractAccountInfoFromPage(): Promise<{
|
|
|
fansCount: number;
|
|
|
worksCount: number;
|
|
|
} | null> {
|
|
|
- const webview = document.querySelector(`webview[partition="${webviewPartition.value}"]`) as Electron.WebviewTag | null;
|
|
|
- if (!webview) return null;
|
|
|
+ const webview = webviewRef.value;
|
|
|
+ if (!webview) {
|
|
|
+ console.warn('[extractAccountInfoFromPage] webviewRef is null');
|
|
|
+ return null;
|
|
|
+ }
|
|
|
|
|
|
try {
|
|
|
// 根据不同平台使用不同的提取脚本
|
|
|
@@ -374,55 +377,97 @@ async function extractAccountInfoFromPage(): Promise<{
|
|
|
(function() {
|
|
|
const result = { accountId: '', accountName: '', avatarUrl: '', fansCount: 0, worksCount: 0 };
|
|
|
|
|
|
- // 从页面元素提取
|
|
|
- // 头像
|
|
|
- const avatarEl = document.querySelector('img[class*="avatar"], .user-avatar img, [class*="userAvatar"] img');
|
|
|
- if (avatarEl) result.avatarUrl = avatarEl.src || '';
|
|
|
-
|
|
|
- // 昵称 - 多种选择器
|
|
|
- const nameSelectors = [
|
|
|
- '.user-name', '.userName', '[class*="userName"]', '[class*="nickname"]',
|
|
|
- '.user-info .name', '.user-info h3', '.user-info h4',
|
|
|
- '[class*="userInfo"] [class*="name"]', '.creator-name'
|
|
|
- ];
|
|
|
- for (const sel of nameSelectors) {
|
|
|
- const el = document.querySelector(sel);
|
|
|
- if (el && el.textContent?.trim()) {
|
|
|
- result.accountName = el.textContent.trim();
|
|
|
- break;
|
|
|
+ try {
|
|
|
+ // 抖音创作服务平台页面(summon.bytedance.com)
|
|
|
+ // 1. 查找头像 - 通常是较大的圆形图片
|
|
|
+ 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) > 20);
|
|
|
+ const isAvatarSize = img.width >= 40 && img.width <= 150;
|
|
|
+ const isAvatarUrl = src.includes('avatar') || src.includes('aweme') || src.includes('douyinpic') || src.includes('bytedance');
|
|
|
+
|
|
|
+ if ((isRound || isAvatarUrl) && isAvatarSize && src.startsWith('http')) {
|
|
|
+ result.avatarUrl = src;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- // 从 URL 或 Cookie 提取 UID
|
|
|
- const uidMatch = document.cookie.match(/passport_uid=([^;]+)/) ||
|
|
|
- document.cookie.match(/uid=([^;]+)/) ||
|
|
|
- document.cookie.match(/x-uuid=([^;]+)/);
|
|
|
- if (uidMatch) {
|
|
|
- result.accountId = 'douyin_' + uidMatch[1];
|
|
|
- }
|
|
|
-
|
|
|
- // 尝试从 window 对象获取
|
|
|
- if (window.__INITIAL_STATE__) {
|
|
|
- const state = window.__INITIAL_STATE__;
|
|
|
- if (state.user) {
|
|
|
- result.accountName = result.accountName || state.user.nickname || state.user.name || '';
|
|
|
- result.avatarUrl = result.avatarUrl || state.user.avatar || state.user.avatarUrl || '';
|
|
|
- result.accountId = result.accountId || ('douyin_' + (state.user.uid || state.user.userId || ''));
|
|
|
+
|
|
|
+ // 2. 查找抖音号 - 格式通常是 "抖音号:xxx"
|
|
|
+ const bodyText = document.body.innerText;
|
|
|
+ const douyinIdMatch = bodyText.match(/抖音号[::]\s*([a-zA-Z0-9_]+)/);
|
|
|
+ if (douyinIdMatch) {
|
|
|
+ result.accountId = 'douyin_' + douyinIdMatch[1];
|
|
|
}
|
|
|
+
|
|
|
+ // 3. 查找昵称 - 通常在抖音号附近,或者在用户信息区域
|
|
|
+ // 先找到抖音号所在的元素,然后向上找用户名
|
|
|
+ const allElements = document.querySelectorAll('*');
|
|
|
+ let userInfoContainer = null;
|
|
|
+
|
|
|
+ for (const el of allElements) {
|
|
|
+ const text = el.innerText || '';
|
|
|
+ if (text.includes('抖音号') && text.length < 200) {
|
|
|
+ userInfoContainer = el.parentElement || el;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (userInfoContainer) {
|
|
|
+ // 在用户信息容器中查找昵称(通常是第一个非空的短文本)
|
|
|
+ const children = userInfoContainer.querySelectorAll('span, div, p, a');
|
|
|
+ for (const child of children) {
|
|
|
+ const text = child.innerText?.trim() || '';
|
|
|
+ // 昵称特征:2-20字符,不是纯数字,不包含特殊词
|
|
|
+ if (text.length >= 2 && text.length <= 20 &&
|
|
|
+ !/^[0-9]+$/.test(text) &&
|
|
|
+ !text.includes('抖音号') && !text.includes('粉丝') &&
|
|
|
+ !text.includes('关注') && !text.includes('获赞') &&
|
|
|
+ !text.includes('家人') && !text.includes('@')) {
|
|
|
+ result.accountName = text;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 备选:遍历页面查找看起来像用户名的文本
|
|
|
+ if (!result.accountName) {
|
|
|
+ const candidates = [];
|
|
|
+ document.querySelectorAll('span, div').forEach(el => {
|
|
|
+ const text = el.innerText?.trim() || '';
|
|
|
+ const rect = el.getBoundingClientRect();
|
|
|
+ // 筛选条件:可见、长度适中、包含中文或字母
|
|
|
+ if (rect.width > 0 && text.length >= 2 && text.length <= 25 &&
|
|
|
+ /[\u4e00-\u9fa5a-zA-Z]/.test(text) &&
|
|
|
+ !/粉丝|关注|获赞|作品|发布|数据|登录|注册/.test(text)) {
|
|
|
+ candidates.push({ text, top: rect.top, left: rect.left });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // 优先选择靠上方、靠左侧的(通常是用户名位置)
|
|
|
+ candidates.sort((a, b) => (a.top + a.left) - (b.top + b.left));
|
|
|
+ if (candidates.length > 0) {
|
|
|
+ result.accountName = candidates[0].text;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 从 Cookie 获取 UID 作为备选
|
|
|
+ if (!result.accountId) {
|
|
|
+ const uidMatch = document.cookie.match(/passport_uid=([^;]+)/) ||
|
|
|
+ document.cookie.match(/uid_tt=([^;]+)/) ||
|
|
|
+ document.cookie.match(/ttwid=1%7C([^%|]+)/);
|
|
|
+ if (uidMatch) {
|
|
|
+ result.accountId = 'douyin_' + uidMatch[1];
|
|
|
+ } else {
|
|
|
+ result.accountId = 'douyin_' + Date.now();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (e) {
|
|
|
+ console.error('[Douyin Extract] Error:', e);
|
|
|
}
|
|
|
|
|
|
- // 尝试从 SSR 数据获取
|
|
|
- const ssrScript = document.querySelector('script[id*="RENDER_DATA"], script[type="application/json"]');
|
|
|
- if (ssrScript) {
|
|
|
- try {
|
|
|
- const data = JSON.parse(decodeURIComponent(ssrScript.textContent || ''));
|
|
|
- const user = data.user || data.userInfo || data.creator || {};
|
|
|
- result.accountName = result.accountName || user.nickname || user.name || '';
|
|
|
- result.avatarUrl = result.avatarUrl || user.avatar || user.avatarUrl || '';
|
|
|
- result.accountId = result.accountId || ('douyin_' + (user.uid || user.userId || ''));
|
|
|
- } catch {}
|
|
|
- }
|
|
|
-
|
|
|
+ console.log('[Douyin Extract] Result:', result);
|
|
|
return result;
|
|
|
})()
|
|
|
`,
|