From 546af84dab35de3d2256717a5a00e9b6af7d34b6 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Fri, 15 May 2026 21:42:43 +0800 Subject: [PATCH] =?UTF-8?q?Revert=20"Feat/virtualizarefactor:=20virtualiza?= =?UTF-8?q?tion=20rework=20=E2=80=94=20unify=20Virtual=20components,=20fix?= =?UTF-8?q?=20memory=20leaks,=20migrate=2015+=20consumerstion=20rework=20(?= =?UTF-8?q?#472)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 5953496d84e0a8e847af06893aeb71cbadb5edb7. --- .gitignore | 3 - package.json | 2 - src/@core/utils/image.ts | 92 +- src/api/index.ts | 7 - src/components/cards/MediaCard.vue | 67 +- src/components/cards/PersonCard.vue | 4 +- src/components/cards/PluginAppCard.vue | 18 +- src/components/cards/PluginCard.vue | 17 +- src/components/cards/SubscribeShareCard.vue | 13 +- src/components/dialog/SiteResourceDialog.vue | 15 +- .../dialog/SubscribeHistoryDialog.vue | 168 ++-- src/components/filebrowser/FileList.vue | 12 +- src/components/filebrowser/FileNavigator.vue | 16 +- src/components/misc/ProgressiveCardGrid.vue | 932 ++++++++++++++++++ src/components/virtual/AutoSizer.vue | 60 -- src/components/virtual/VirtualGrid.vue | 199 ---- src/components/virtual/VirtualList.vue | 189 ---- src/components/virtual/VirtualMasonry.vue | 227 ----- src/components/virtual/VirtualTree.vue | 149 --- src/composables/useDynamicHeaderTab.ts | 96 +- src/composables/useInfiniteScroll.ts | 60 ++ src/composables/useScrollRestore.ts | 110 --- src/composables/virtual/useBreakpointCols.ts | 44 - .../virtual/useLoadMoreSentinel.ts | 64 -- src/composables/virtual/useResponsiveCols.ts | 48 - src/composables/virtual/useTreeFlatten.ts | 58 -- .../virtual/useVirtualizerBridge.ts | 75 -- .../virtual/useWindowScrollMargin.ts | 75 -- src/layouts/components/DefaultLayout.vue | 22 +- src/pages/recommend.vue | 28 +- src/pages/resource.vue | 41 +- src/router/index.ts | 4 + src/stores/scrollPosition.ts | 72 -- src/views/dashboard/MediaServerLatest.vue | 24 +- src/views/dashboard/MediaServerLibrary.vue | 34 +- src/views/dashboard/MediaServerPlaying.vue | 34 +- src/views/discover/MediaCardListView.vue | 169 ++-- src/views/discover/MediaCardSlideView.vue | 18 +- src/views/discover/PersonCardListView.vue | 153 ++- src/views/discover/PersonCardSlideView.vue | 4 +- src/views/plugin/PluginCardListView.vue | 96 +- src/views/reorganize/DownloadingListView.vue | 35 +- src/views/site/SiteCardListView.vue | 21 +- src/views/subscribe/SubscribeListView.vue | 36 +- src/views/subscribe/SubscribePopularView.vue | 203 ++-- src/views/subscribe/SubscribeShareView.vue | 217 ++-- src/views/system/MessageView.vue | 212 ++-- src/views/user/UserListView.vue | 20 +- src/views/workflow/WorkflowListView.vue | 33 +- src/views/workflow/WorkflowShareView.vue | 176 ++-- yarn.lock | 62 +- 51 files changed, 2133 insertions(+), 2401 deletions(-) create mode 100644 src/components/misc/ProgressiveCardGrid.vue delete mode 100644 src/components/virtual/AutoSizer.vue delete mode 100644 src/components/virtual/VirtualGrid.vue delete mode 100644 src/components/virtual/VirtualList.vue delete mode 100644 src/components/virtual/VirtualMasonry.vue delete mode 100644 src/components/virtual/VirtualTree.vue create mode 100644 src/composables/useInfiniteScroll.ts delete mode 100644 src/composables/useScrollRestore.ts delete mode 100644 src/composables/virtual/useBreakpointCols.ts delete mode 100644 src/composables/virtual/useLoadMoreSentinel.ts delete mode 100644 src/composables/virtual/useResponsiveCols.ts delete mode 100644 src/composables/virtual/useTreeFlatten.ts delete mode 100644 src/composables/virtual/useVirtualizerBridge.ts delete mode 100644 src/composables/virtual/useWindowScrollMargin.ts delete mode 100644 src/stores/scrollPosition.ts diff --git a/.gitignore b/.gitignore index 0ff483a1..879aa1eb 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,3 @@ package-lock.json # iconify dist files src/@iconify/*.js public/plugin_icon/** - -# AI -.omc/ \ No newline at end of file diff --git a/package.json b/package.json index 42070e4f..e128f639 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,6 @@ { "name": "moviepilot", "version": "2.11.4", - "license": "MIT", "private": true, "type": "module", "bin": "dist/service.js", @@ -29,7 +28,6 @@ "@fullcalendar/timegrid": "^6.1.15", "@fullcalendar/vue3": "^6.1.15", "@iconify/utils": "^2.2.1", - "@tanstack/vue-virtual": "^3.13.24", "@types/crypto-js": "^4.2.2", "@types/js-cookie": "^3.0.6", "@vue-flow/background": "^1.3.2", diff --git a/src/@core/utils/image.ts b/src/@core/utils/image.ts index 9c0e1ace..546c0d92 100644 --- a/src/@core/utils/image.ts +++ b/src/@core/utils/image.ts @@ -14,95 +14,33 @@ function rgbStringToHex(rgbArray: number[]): string { return `#${toHex(r)}${toHex(g)}${toHex(b)}` } -// 主色调缓存:相同 URL 不重复经过 ColorThief 的 canvas 解码 -const DOMINANT_COLOR_CACHE_MAX = 200 -const dominantColorCache = new Map() - -function rememberDominantColor(key: string, value: string) { - if (!key) return - if (dominantColorCache.size >= DOMINANT_COLOR_CACHE_MAX) { - const first = dominantColorCache.keys().next().value - if (first !== undefined) dominantColorCache.delete(first) - } - dominantColorCache.set(key, value) -} - // 提取主要颜色 export async function getDominantColor(image: HTMLImageElement): Promise { - const cacheKey = image?.currentSrc || image?.src || '' - const cached = cacheKey ? dominantColorCache.get(cacheKey) : undefined - if (cached) return cached - try { - const colorThief = new ColorThief() - const dominantColor = colorThief.getColor(image) - const hex = rgbStringToHex(dominantColor) - rememberDominantColor(cacheKey, hex) - return hex - } catch (e) { - console.warn('getDominantColor failed', e) - return '#28A9E1' - } -} - -// 预加载缓存:已成功加载的 URL 不再重复创建 Image 对象 -const PRELOAD_CACHE_MAX = 50 -const preloadedUrls = new Set() - -function rememberPreloaded(url: string) { - if (!url) return - if (preloadedUrls.size >= PRELOAD_CACHE_MAX) { - const first = preloadedUrls.values().next().value - if (first !== undefined) preloadedUrls.delete(first) - } - preloadedUrls.add(url) + const colorThief = new ColorThief() + const dominantColor = colorThief.getColor(image) + return rgbStringToHex(dominantColor) } // 预加载图片 export async function preloadImage(url: string): Promise { - if (!url) return false - if (preloadedUrls.has(url)) return true return new Promise(resolve => { const img = new Image() - img.decoding = 'async' - let settled = false - const finish = (ok: boolean) => { - if (settled) return - settled = true - img.onload = null - img.onerror = null - window.clearTimeout(timeout) - if (ok) rememberPreloaded(url) - else img.src = '' // 释放解码位图 - resolve(ok) - } + img.onload = () => resolve(true) + img.onerror = () => resolve(false) - img.onload = () => finish(true) - img.onerror = () => finish(false) - - const timeout = window.setTimeout(() => finish(false), 5000) + // 设置超时,防止图片长时间加载 + const timeout = setTimeout(() => { + img.src = '' + resolve(false) + }, 5000) // 5秒超时 img.src = url - // 命中浏览器缓存时 onload 可能不会触发 - if (img.complete && img.naturalWidth > 0) finish(true) + // 如果图片已经缓存,onload可能不会触发 + if (img.complete) { + clearTimeout(timeout) + resolve(true) + } }) } - -// TMDB 图片域名地址(仅作为兜底,调用方应优先用 globalSettings.TMDB_IMAGE_DOMAIN) -const TMDB_PATH_RE = /\/t\/p\/(original|w\d+|h\d+|w\d+_and_h\d+_bestv2)\// - -/** - * 把 TMDB 图片 URL 重置到指定渲染尺寸。非 TMDB URL(豆瓣 / Bangumi / 自定义代理)原样返回。 - * 用于在卡片场景下避免下载 / 解码 1MP+ 的原图。 - * - * 常见尺寸:w92 / w154 / w185 / w342 / w500 / w780 / original(poster, backdrop) - * w45 / w185 / h632 / original(profile) - */ -export function tmdbResize( - url: string | undefined | null, - size: 'w92' | 'w154' | 'w185' | 'w342' | 'w500' | 'w780' | 'original', -): string { - if (!url) return '' - return url.replace(TMDB_PATH_RE, `/t/p/${size}/`) -} diff --git a/src/api/index.ts b/src/api/index.ts index 12a18e77..e252dea4 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -45,13 +45,6 @@ api.interceptors.response.use( return response.data }, error => { - // 请求被主动取消(路由切换 / 组件卸载触发 requestOptimizer abort)。 - // 这不是错误:原样透传 cancel error,让调用方用 axios.isCancel() 识别并静默处理。 - // 不能落到下面 new Error(error.message) 分支——那会把 cancel 签名抹掉, - // 调用方只能看到一个 message='canceled' 的普通 Error,被迫当错误打日志。 - if (axios.isCancel(error)) { - return Promise.reject(error) - } if (!error.response) { // 网络错误或请求超时 - 通知离线状态管理系统 const isNetworkError = diff --git a/src/components/cards/MediaCard.vue b/src/components/cards/MediaCard.vue index 1568951a..fcf59935 100644 --- a/src/components/cards/MediaCard.vue +++ b/src/components/cards/MediaCard.vue @@ -1,7 +1,6 @@