'use strict'; async function cacheCoversAuto(){ const portable = await localApiAvailable(); const todo = getAllCollections().filter(c => c.status !== 'confirmed_deleted' && !c.cover_local_url); if(!todo.length){ log('syncLog','封面自动缓存:全部已缓存,跳过。'); return; } log('syncLog', `封面自动缓存:开始缓存 ${todo.length} 个未缓存封面${portable ? '到 Docker 文件卷' : '到浏览器 IndexedDB'}...`); let ok=0, fail=0; for(let i=0; i{ const r=new FileReader(); r.onload=()=>resolve(r.result); r.onerror=()=>reject(r.error); r.readAsDataURL(blob); }); } async function imageBlobToDataURLFromCache(c){ const offlineData = c.cover_data_url || ''; const localUrl = c.cover_local_url || ''; const remoteUrl = onlineCoverUrl(c); // v0.20: 导出用的 base64 只在内存中的离线副本里生成,不写回 收藏数据.json。 // Web 运行界面优先使用服务端缓存封面或在线原图。 if(localUrl){ try{ const res = await fetch(localUrl, {cache:'no-store'}); if(res.ok) return await blobToDataURL(await res.blob()); }catch{} } try{ const cached = await idbGet('img:'+c.subject_id); if(cached?.dataUrl) return cached.dataUrl; }catch{} if(offlineData) return offlineData; // 没有本地缓存时,不主动在线拉远端图片进 HTML,避免导出完整 ZIP 时大量跨域下载卡住。 // 需要离线封面的条目,请先点“缓存当前封面到目录”。 return ''; } async function collectCachedImageFiles(arr,onProgress){ const files = {}; let i = 0; for(const c of arr){ i++; if(onProgress) onProgress(i, arr.length, c); if(!c.cover_local_url) { if(i % 40 === 0) await sleep(0); continue; } const file = (c.cover_local_file || '').split(/[\\/]/).pop() || (`${c.subject_id}.jpg`); try{ const res = await fetch(c.cover_local_url, {cache:'no-store'}); if(!res.ok) continue; files['images/' + file] = new Uint8Array(await res.arrayBuffer()); } catch{} if(i % 30 === 0) await sleep(0); } return files; }