@@ -2,6 +2,7 @@ import { type ImageItem, type ImageFormat } from "../types";
22import { UrlResolver } from "./utils/url-resolver" ;
33import { ImageTypeDetector } from "./utils/image-type-detector" ;
44import { runConcurrent } from "./utils/concurrency" ;
5+ import { WeiboResolver } from "./resolvers/weibo" ;
56import {
67 isDomainDisabled ,
78 type SnifferSettings ,
@@ -25,8 +26,8 @@ import {
2526} from "../ui/utils/sniffer-events" ;
2627
2728const METADATA_CONCURRENCY = 12 ;
28- const IMAGE_METADATA_TIMEOUT_MS = 1500 ;
29- const FETCH_METADATA_TIMEOUT_MS = 1000 ;
29+ const IMAGE_METADATA_TIMEOUT_MS = 3000 ;
30+ const FETCH_METADATA_TIMEOUT_MS = 2000 ;
3031
3132/** 为字符串生成稳定的数字哈希(不依赖索引位置) */
3233function stableHash ( str : string ) : string {
@@ -545,7 +546,7 @@ export class Sniffer {
545546 action : "SNIFF_REQUEST" ,
546547 payload : { settings, requestId : options ?. requestId } ,
547548 } ,
548- ( response ) => {
549+ async ( response ) => {
549550 if ( chrome . runtime . lastError || ! response ) {
550551 const errMsg = chrome . runtime . lastError ?. message || "" ;
551552 if ( ! errMsg . includes ( "Receiving end does not exist" ) ) {
@@ -556,7 +557,28 @@ export class Sniffer {
556557 }
557558 resolve ( [ ] ) ;
558559 } else {
559- resolve ( response . results || [ ] ) ;
560+ const results : ImageItem [ ] = response . results || [ ] ;
561+ const updatedResults = [ ...results ] ;
562+
563+ // 在侧边栏环境重新获取防盗链域名的真实宽高(DNR 规则已生效,可正常加载)
564+ await runConcurrent ( updatedResults , METADATA_CONCURRENCY , async ( item , index ) => {
565+ const needUpdate =
566+ item . url . includes ( "sinaimg.cn" ) ||
567+ item . url . includes ( "weibo.com" ) ;
568+ if ( needUpdate ) {
569+ const meta = await this . getImageMetadata ( item . url ) ;
570+ if ( meta ) {
571+ updatedResults [ index ] = {
572+ ...item ,
573+ width : meta . width ,
574+ height : meta . height ,
575+ sizeKB : meta . sizeKB > 0 ? meta . sizeKB : item . sizeKB ,
576+ } ;
577+ }
578+ }
579+ } ) ;
580+
581+ resolve ( updatedResults ) ;
560582 }
561583 } ,
562584 ) ;
@@ -588,13 +610,15 @@ export class Sniffer {
588610 settings ?. interfaceBehavior ?. searchAllFrames ?? true ;
589611 const identifyBackground =
590612 settings ?. interfaceBehavior ?. identifyBackgroundImages ?? true ;
613+ const isTelegramHost = window . location . host . includes ( "telegram" ) ;
591614 const identifyBlob =
592- settings ?. interfaceBehavior ?. identifyBlobImages ?? false ;
615+ ( settings ?. interfaceBehavior ?. identifyBlobImages ?? false ) || isTelegramHost ;
593616
617+ const isPixiv = window . location . href . includes ( "pixiv.net" ) ;
594618 const [ treeUrls , perfUrls , svgUrls ] = await Promise . all ( [
595619 this . sniffNodeTree ( document , searchAllFrames , identifyBackground ) ,
596620 Promise . resolve ( this . sniffPerformance ( ) ) ,
597- Promise . resolve ( this . sniffSVGElements ( ) ) ,
621+ Promise . resolve ( isPixiv ? [ ] : this . sniffSVGElements ( ) ) ,
598622 ] ) ;
599623 [ ...treeUrls , ...perfUrls , ...svgUrls ] . forEach ( ( url ) => {
600624 if (
@@ -603,7 +627,16 @@ export class Sniffer {
603627 url . startsWith ( "data:" ) ||
604628 ( identifyBlob && url . startsWith ( "blob:" ) ) )
605629 ) {
606- urls . add ( url ) ;
630+ const resolved = UrlResolver . transformSiteSpecificUrl ( url ) ;
631+ // 过滤掉 weibo.com 的网页链接(如 /u/false 或 /status/ 等非真实图片)
632+ if ( resolved . includes ( "weibo.com" ) && ! resolved . match ( / \. ( j p g | j p e g | p n g | g i f | w e b p | s v g ) / i) ) {
633+ return ;
634+ }
635+ // 如果在 Pixiv 网站,过滤掉所有的 SVG 资源,防止 UI 背景渐变/图标等乱入
636+ if ( isPixiv && ( resolved . includes ( "image/svg+xml" ) || resolved . toLowerCase ( ) . includes ( ".svg" ) ) ) {
637+ return ;
638+ }
639+ urls . add ( resolved ) ;
607640 }
608641 } ) ;
609642
@@ -624,17 +657,32 @@ export class Sniffer {
624657
625658 const items : ImageItem [ ] = [ ] ;
626659 metadataResults . forEach ( ( metadata , index ) => {
660+ const url = urlArray [ index ] ;
661+ // 优先复用已有 ID,否则生成新的稳定哈希
662+ const id = existingIdMap . get ( url ) ?? stableHash ( url ) ;
663+
627664 if ( metadata ) {
628- const url = urlArray [ index ] ;
629- // 优先复用已有 ID,否则生成新的稳定哈希
630- const id = existingIdMap . get ( url ) ?? stableHash ( url ) ;
631665 items . push ( {
632666 ...metadata ,
633667 id,
634668 isSelected : false ,
635669 pageTitle : document . title ,
636670 pageUrl : window . location . href ,
637671 } ) ;
672+ } else {
673+ // 兜底保留:如果测量超时或失败,不直接丢弃图片,而是生成兜底的 ImageItem!
674+ items . push ( {
675+ url,
676+ width : 0 ,
677+ height : 0 ,
678+ sizeKB : 0 ,
679+ format : ImageTypeDetector . getFormatFromUrl ( url ) ,
680+ filename : url . split ( "/" ) . pop ( ) ?. split ( / [ ? # ] / ) [ 0 ] || "image" ,
681+ id,
682+ isSelected : false ,
683+ pageTitle : document . title ,
684+ pageUrl : window . location . href ,
685+ } ) ;
638686 }
639687 } ) ;
640688
@@ -658,6 +706,18 @@ export class Sniffer {
658706 private async getImageMetadata (
659707 url : string ,
660708 ) : Promise < Omit < ImageItem , "id" | "isSelected" > | null > {
709+ const weiboSize = WeiboResolver . parseDimensions ( url ) ;
710+ if ( weiboSize ) {
711+ return {
712+ url,
713+ width : weiboSize . width ,
714+ height : weiboSize . height ,
715+ sizeKB : 0 ,
716+ format : ImageTypeDetector . getFormatFromUrl ( url ) ,
717+ filename : url . split ( "/" ) . pop ( ) ?. split ( / [ ? # ] / ) [ 0 ] || "image" ,
718+ } ;
719+ }
720+
661721 try {
662722 const dimensions = await new Promise < { width : number ; height : number } > (
663723 ( resolve , reject ) => {
@@ -695,7 +755,42 @@ export class Sniffer {
695755 new Error ( `Failed to load image: ${ String ( url ) . slice ( 0 , 100 ) } ` ) ,
696756 ) ;
697757 } ;
698- img . src = url ;
758+ // 仅侧边栏页面(chrome-extension:// 协议)才需要走后台 Blob 代理。
759+ // Content script 运行在目标页上下文,可直接加载且受 DNR 规则保护,无需代理。
760+ // 对于本地内存中的 blob: 图片,后台 Service Worker 无法跨越沙盒拉取,直接不走代理。
761+ const isSidePanelPage =
762+ typeof window !== "undefined" &&
763+ window . location . protocol === "chrome-extension:" &&
764+ typeof chrome !== "undefined" &&
765+ ! ! chrome . runtime ?. sendMessage ;
766+
767+ const isBlobUrl = url . startsWith ( "blob:" ) ;
768+
769+ if ( isSidePanelPage && ! isBlobUrl ) {
770+ // 根据域名传入对应的防盗链 Referer,Service Worker fetch 不受 DNR 规则覆盖
771+ let referer : string | undefined ;
772+ if ( url . includes ( "sinaimg.cn" ) || url . includes ( "weibo.com" ) ) {
773+ referer = "https://weibo.com/" ;
774+ } else if ( url . includes ( "pximg.net" ) ) {
775+ referer = "https://www.pixiv.net/" ;
776+ }
777+ chrome . runtime . sendMessage (
778+ {
779+ type : "FETCH_BLOB" ,
780+ payload : { url, referer } ,
781+ } ,
782+ ( response ) => {
783+ if ( response ?. success && response . arrayBuffer ) {
784+ const mimeType = response . mimeType || "image/jpeg" ;
785+ img . src = `data:${ mimeType } ;base64,${ response . arrayBuffer } ` ;
786+ } else {
787+ img . src = url ;
788+ }
789+ } ,
790+ ) ;
791+ } else {
792+ img . src = url ;
793+ }
699794 } ,
700795 ) ;
701796
0 commit comments