Ethanfly 10 uur geleden
bovenliggende
commit
ab28ccca02
1 gewijzigde bestanden met toevoegingen van 92 en 47 verwijderingen
  1. 92 47
      client/src/components/BrowserTab.vue

+ 92 - 47
client/src/components/BrowserTab.vue

@@ -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;
         })()
       `,