From fa958d09874bce9a918a9653bf29534d0dbe5a6a Mon Sep 17 00:00:00 2001 From: katelya Date: Tue, 2 Sep 2025 17:08:23 +0800 Subject: [PATCH] Refactor service worker and remove test page - Updated service worker (sw.js) to improve caching strategies and update asset revisions. - Deleted the test page (page.tsx) as it is no longer needed. - Refactored EpisodeSelector component to simplify logic and improve performance. - Added a .dockerignore file to exclude unnecessary files from Docker builds. --- .dockerignore | 16 ++ public/sw.js | 2 +- src/app/test/page.tsx | 43 ---- src/components/EpisodeSelector.tsx | 305 ++++++++--------------------- 4 files changed, 101 insertions(+), 265 deletions(-) create mode 100644 .dockerignore delete mode 100644 src/app/test/page.tsx diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..7e7a9d6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,16 @@ +node_modules +npm-debug.log +Dockerfile +.dockerignore +.git +.gitignore +README.md +.env +.env.local +.env.development.local +.env.test.local +.env.production.local +.next +.vercel +.vscode +**/*.backup.tsx diff --git a/public/sw.js b/public/sw.js index a1dd0a1..ddfe6d0 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1 +1 @@ -if(!self.define){let e,s={};const c=(c,i)=>(c=new URL(c+".js",i).href,s[c]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=c,e.onload=s,document.head.appendChild(e)}else e=c,importScripts(c),s()}).then(()=>{let e=s[c];if(!e)throw new Error(`Module ${c} didn’t register its module`);return e}));self.define=(i,n)=>{const a=e||("document"in self?document.currentScript.src:"")||location.href;if(s[a])return;let t={};const r=e=>c(e,a),o={module:{uri:a},exports:t,require:r};s[a]=Promise.all(i.map(e=>o[e]||r(e))).then(e=>(n(...e),t))}}define(["./workbox-e9849328"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/app-build-manifest.json",revision:"2357a9c84856ed003a11ad65ffbbfdda"},{url:"/_next/static/chunks/110-cb68faf0f47f94e5.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/154-de4a84fd5b2e0100.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/29-0844689411ca7d55.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/459-6bec40a8423cc309.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/51b697cb-f464f3017ac1ea30.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/682-d1dca8d17a3a8e6f.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/900-fb094d8873768e88.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/967-217cdcb80ae3beeb.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/97-d1910366547d7828.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/998-568996670b543597.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/app/_not-found/page-ac328df06cf68f14.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/app/admin/page-d0def26e413c060d.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/app/douban/page-2d0023184aa37aff.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/app/layout-bd0bfbfdb401e15f.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/app/login/page-6d62f8fe1814a4fb.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/app/page-6a58e37ab3250691.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/app/play/page-b61faca5d1a625e7.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/app/search/page-63fe30b91e0539a7.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/app/test/page-3710ab42821bc0c1.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/app/warning/page-11cba4cf9332a238.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/c72274ce-06682d6fc8197e6d.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/da9543df-bf6da1a431d8604f.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/framework-6e06c675866dc992.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/main-2b5384ed203ccb88.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/main-app-dbd320e104e1a5dc.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/pages/_app-792b631a362c29e1.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/pages/_error-9fde6601392a2a99.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-17170f1d90853b2d.js",revision:"lh6cAymZbESh2W8gRq0ki"},{url:"/_next/static/css/23100062f5d4aac0.css",revision:"23100062f5d4aac0"},{url:"/_next/static/css/275ed64cc4367444.css",revision:"275ed64cc4367444"},{url:"/_next/static/css/f98ccb5975c7aa46.css",revision:"f98ccb5975c7aa46"},{url:"/_next/static/lh6cAymZbESh2W8gRq0ki/_buildManifest.js",revision:"046380ae5bc74b46b6d5eac3eed65355"},{url:"/_next/static/lh6cAymZbESh2W8gRq0ki/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/media/26a46d62cd723877-s.woff2",revision:"befd9c0fdfa3d8a645d5f95717ed6420"},{url:"/_next/static/media/55c55f0601d81cf3-s.woff2",revision:"43828e14271c77b87e3ed582dbff9f74"},{url:"/_next/static/media/581909926a08bbc8-s.woff2",revision:"f0b86e7c24f455280b8df606b89af891"},{url:"/_next/static/media/8e9860b6e62d6359-s.woff2",revision:"01ba6c2a184b8cba08b0d57167664d75"},{url:"/_next/static/media/97e0cb1ae144a2a9-s.woff2",revision:"e360c61c5bd8d90639fd4503c829c2dc"},{url:"/_next/static/media/df0a9ae256c0569c-s.woff2",revision:"d54db44de5ccb18886ece2fda72bdfe0"},{url:"/_next/static/media/e4af272ccee01ff0-s.p.woff2",revision:"65850a373e258f1c897a2b3d75eb74de"},{url:"/favicon.ico",revision:"c5de6e56c5664adda146825f75ea6ecf"},{url:"/icons/icon-192x192.png",revision:"4a56c090828a1ad254c903c7aec0389d"},{url:"/icons/icon-256x256.png",revision:"f6409eb1a001f754121e3a8281c0319c"},{url:"/icons/icon-384x384.png",revision:"f6efc3e357b9ffdf4e0d8c14b2ed0ac1"},{url:"/icons/icon-512x512.png",revision:"9c008cbbeb6a576fe07bb1284a83f4d2"},{url:"/logo.png",revision:"40de611b143c47c6291c7bdad2c959ca"},{url:"/manifest.json",revision:"7bd3dabc1cfbfe40f09577efca223d31"},{url:"/robots.txt",revision:"e2b2cd8514443456bc6fb9d77b3b1f3e"},{url:"/screenshot1.png",revision:"10572bfcea54dc93ac4c5f7c9057fc98"},{url:"/screenshot2.png",revision:"f815a8990973a221899976867365c239"},{url:"/screenshot3.png",revision:"49709e96345dfeeab1d8083821d4b44e"},{url:"/screenshot4.png",revision:"a76c751e41e37556048a487e4f8b8b1c"},{url:"/wechat.jpg",revision:"d0f601311802667cd6ca5a37dc69bfa7"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:s,event:c,state:i})=>s&&"opaqueredirect"===s.type?new Response(s.body,{status:200,statusText:"OK",headers:s.headers}):s}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;const s=e.pathname;return!s.startsWith("/api/auth/")&&!!s.startsWith("/api/")},new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")},new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>!(self.origin===e.origin),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")}); +if(!self.define){let e,n={};const s=(s,i)=>(s=new URL(s+".js",i).href,n[s]||new Promise(n=>{if("document"in self){const e=document.createElement("script");e.src=s,e.onload=n,document.head.appendChild(e)}else e=s,importScripts(s),n()}).then(()=>{let e=n[s];if(!e)throw new Error(`Module ${s} didn’t register its module`);return e}));self.define=(i,c)=>{const a=e||("document"in self?document.currentScript.src:"")||location.href;if(n[a])return;let d={};const t=e=>s(e,a),r={module:{uri:a},exports:d,require:t};n[a]=Promise.all(i.map(e=>r[e]||t(e))).then(e=>(c(...e),d))}}define(["./workbox-e9849328"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/app-build-manifest.json",revision:"80b388c46e04a40635a9fe2b8cfc2a01"},{url:"/_next/static/chunks/110-cb68faf0f47f94e5.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/154-de4a84fd5b2e0100.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/29-0844689411ca7d55.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/459-6bec40a8423cc309.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/51b697cb-f464f3017ac1ea30.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/682-d1dca8d17a3a8e6f.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/900-fb094d8873768e88.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/967-217cdcb80ae3beeb.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/998-568996670b543597.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/app/_not-found/page-ac328df06cf68f14.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/app/admin/page-d0def26e413c060d.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/app/douban/page-2d0023184aa37aff.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/app/layout-bd0bfbfdb401e15f.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/app/login/page-6d62f8fe1814a4fb.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/app/page-6a58e37ab3250691.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/app/play/page-cbcfbf4a92cde119.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/app/search/page-63fe30b91e0539a7.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/app/warning/page-11cba4cf9332a238.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/c72274ce-06682d6fc8197e6d.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/da9543df-bf6da1a431d8604f.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/framework-6e06c675866dc992.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/main-95de9e33689c098a.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/main-app-dbd320e104e1a5dc.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/pages/_app-792b631a362c29e1.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/pages/_error-9fde6601392a2a99.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-17170f1d90853b2d.js",revision:"nlHzYUnQridQuWdwd-dlI"},{url:"/_next/static/css/23100062f5d4aac0.css",revision:"23100062f5d4aac0"},{url:"/_next/static/css/275ed64cc4367444.css",revision:"275ed64cc4367444"},{url:"/_next/static/css/3ec63c712b6e4432.css",revision:"3ec63c712b6e4432"},{url:"/_next/static/media/26a46d62cd723877-s.woff2",revision:"befd9c0fdfa3d8a645d5f95717ed6420"},{url:"/_next/static/media/55c55f0601d81cf3-s.woff2",revision:"43828e14271c77b87e3ed582dbff9f74"},{url:"/_next/static/media/581909926a08bbc8-s.woff2",revision:"f0b86e7c24f455280b8df606b89af891"},{url:"/_next/static/media/8e9860b6e62d6359-s.woff2",revision:"01ba6c2a184b8cba08b0d57167664d75"},{url:"/_next/static/media/97e0cb1ae144a2a9-s.woff2",revision:"e360c61c5bd8d90639fd4503c829c2dc"},{url:"/_next/static/media/df0a9ae256c0569c-s.woff2",revision:"d54db44de5ccb18886ece2fda72bdfe0"},{url:"/_next/static/media/e4af272ccee01ff0-s.p.woff2",revision:"65850a373e258f1c897a2b3d75eb74de"},{url:"/_next/static/nlHzYUnQridQuWdwd-dlI/_buildManifest.js",revision:"046380ae5bc74b46b6d5eac3eed65355"},{url:"/_next/static/nlHzYUnQridQuWdwd-dlI/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/favicon.ico",revision:"c5de6e56c5664adda146825f75ea6ecf"},{url:"/icons/icon-192x192.png",revision:"4a56c090828a1ad254c903c7aec0389d"},{url:"/icons/icon-256x256.png",revision:"f6409eb1a001f754121e3a8281c0319c"},{url:"/icons/icon-384x384.png",revision:"f6efc3e357b9ffdf4e0d8c14b2ed0ac1"},{url:"/icons/icon-512x512.png",revision:"9c008cbbeb6a576fe07bb1284a83f4d2"},{url:"/logo.png",revision:"40de611b143c47c6291c7bdad2c959ca"},{url:"/manifest.json",revision:"7bd3dabc1cfbfe40f09577efca223d31"},{url:"/robots.txt",revision:"e2b2cd8514443456bc6fb9d77b3b1f3e"},{url:"/screenshot1.png",revision:"10572bfcea54dc93ac4c5f7c9057fc98"},{url:"/screenshot2.png",revision:"f815a8990973a221899976867365c239"},{url:"/screenshot3.png",revision:"49709e96345dfeeab1d8083821d4b44e"},{url:"/screenshot4.png",revision:"a76c751e41e37556048a487e4f8b8b1c"},{url:"/wechat.jpg",revision:"d0f601311802667cd6ca5a37dc69bfa7"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:n,event:s,state:i})=>n&&"opaqueredirect"===n.type?new Response(n.body,{status:200,statusText:"OK",headers:n.headers}):n}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;const n=e.pathname;return!n.startsWith("/api/auth/")&&!!n.startsWith("/api/")},new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")},new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>!(self.origin===e.origin),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")}); diff --git a/src/app/test/page.tsx b/src/app/test/page.tsx deleted file mode 100644 index 1fab705..0000000 --- a/src/app/test/page.tsx +++ /dev/null @@ -1,43 +0,0 @@ -'use client'; - -import React from 'react'; - -import EpisodeSelector from '@/components/EpisodeSelector'; - -export default function TestPage() { - return ( -
-

- 🧪 剧集选择器测试页面 -

- -
-
- { - // 选择了第 {_episode + 1} 集 - }} - availableSources={[]} - sourceSearchLoading={false} - sourceSearchError={null} - /> -
-
- -
-

测试说明:

-
    -
  • • 应该看到红色的调试信息条
  • -
  • • 应该看到彩色的测试网格
  • -
  • • 左右导航按钮应该可见
  • -
  • • 剧集按钮应该是彩色的(黄橙红渐变)
  • -
  • • 激活的按钮应该是红粉紫渐变
  • -
  • • 改变浏览器窗口大小,网格应该响应式变化
  • -
-
-
- ); -} diff --git a/src/components/EpisodeSelector.tsx b/src/components/EpisodeSelector.tsx index ab3691b..2a4122a 100644 --- a/src/components/EpisodeSelector.tsx +++ b/src/components/EpisodeSelector.tsx @@ -6,7 +6,6 @@ import { useRouter } from 'next/navigation'; import React, { useCallback, useEffect, - useMemo, useRef, useState, } from 'react'; @@ -19,7 +18,7 @@ interface VideoInfo { quality: string; loadSpeed: string; pingTime: number; - hasError?: boolean; // 添加错误状态标识 + hasError?: boolean; } interface EpisodeSelectorProps { @@ -98,7 +97,7 @@ const EpisodeSelector: React.FC = ({ // 是否倒序显示 const [descending, setDescending] = useState(false); - // 获取视频信息的函数 - 移除 attemptedSources 依赖避免不必要的重新创建 + // 获取视频信息的函数 const getVideoInfo = useCallback(async (source: SearchResult) => { const sourceKey = `${source.source}-${source.id}`; @@ -136,7 +135,6 @@ const EpisodeSelector: React.FC = ({ // 当有预计算结果时,先合并到videoInfoMap中 useEffect(() => { if (precomputedVideoInfo && precomputedVideoInfo.size > 0) { - // 原子性地更新两个状态,避免时序问题 setVideoInfoMap((prev) => { const newMap = new Map(prev); precomputedVideoInfo.forEach((value, key) => { @@ -148,107 +146,61 @@ const EpisodeSelector: React.FC = ({ setAttemptedSources((prev) => { const newSet = new Set(prev); precomputedVideoInfo.forEach((info, key) => { - if (!info.hasError) { - newSet.add(key); - } + newSet.add(key); }); return newSet; }); - - // 同步更新 ref,确保 getVideoInfo 能立即看到更新 - precomputedVideoInfo.forEach((info, key) => { - if (!info.hasError) { - attemptedSourcesRef.current.add(key); - } - }); } }, [precomputedVideoInfo]); - // 读取本地“优选和测速”开关,默认开启 - const [optimizationEnabled] = useState(() => { - if (typeof window !== 'undefined') { - const saved = localStorage.getItem('enableOptimization'); - if (saved !== null) { - try { - return JSON.parse(saved); - } catch { - /* ignore */ - } - } - } - return true; - }); - - // 当切换到换源tab并且有源数据时,异步获取视频信息 - 移除 attemptedSources 依赖避免循环触发 + // 当换源Tab激活且没有测速过时,开始测速 useEffect(() => { - const fetchVideoInfosInBatches = async () => { - if ( - !optimizationEnabled || // 若关闭测速则直接退出 - activeTab !== 'sources' || - availableSources.length === 0 - ) - return; - - // 筛选出尚未测速的播放源 - const pendingSources = availableSources.filter((source) => { + if (activeTab === 'sources') { + availableSources.forEach((source) => { const sourceKey = `${source.source}-${source.id}`; - return !attemptedSourcesRef.current.has(sourceKey); + if (!attemptedSourcesRef.current.has(sourceKey)) { + getVideoInfo(source); + } }); + } + }, [activeTab, availableSources, getVideoInfo]); - if (pendingSources.length === 0) return; - - const batchSize = Math.ceil(pendingSources.length / 2); - - for (let start = 0; start < pendingSources.length; start += batchSize) { - const batch = pendingSources.slice(start, start + batchSize); - await Promise.all(batch.map(getVideoInfo)); - } - }; - - fetchVideoInfosInBatches(); - // 依赖项保持与之前一致 - }, [activeTab, availableSources, getVideoInfo, optimizationEnabled]); - - // 升序分页标签 - const categoriesAsc = useMemo(() => { - return Array.from({ length: pageCount }, (_, i) => { - const start = i * episodesPerPage + 1; - const end = Math.min(start + episodesPerPage - 1, totalEpisodes); - return `${start}-${end}`; - }); - }, [pageCount, episodesPerPage, totalEpisodes]); - - // 分页标签始终保持升序 - const categories = categoriesAsc; - + // 分类标签容器和按钮的引用 const categoryContainerRef = useRef(null); const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]); - // 当分页切换时,将激活的分页标签滚动到视口中间 + // 自动滚动到当前分页标签 useEffect(() => { - const btn = buttonRefs.current[currentPage]; - const container = categoryContainerRef.current; - if (btn && container) { - // 手动计算滚动位置,只滚动分页标签容器 - const containerRect = container.getBoundingClientRect(); - const btnRect = btn.getBoundingClientRect(); - const scrollLeft = container.scrollLeft; + if (categoryContainerRef.current && buttonRefs.current[currentPage]) { + const container = categoryContainerRef.current; + const button = buttonRefs.current[currentPage]; + + if (button) { + const containerRect = container.getBoundingClientRect(); + const buttonRect = button.getBoundingClientRect(); + const scrollLeft = container.scrollLeft; - // 计算按钮相对于容器的位置 - const btnLeft = btnRect.left - containerRect.left + scrollLeft; - const btnWidth = btnRect.width; - const containerWidth = containerRect.width; - - // 计算目标滚动位置,使按钮居中 - const targetScrollLeft = btnLeft - (containerWidth - btnWidth) / 2; - - // 平滑滚动到目标位置 - container.scrollTo({ - left: targetScrollLeft, - behavior: 'smooth', - }); + if (buttonRect.left < containerRect.left) { + container.scrollTo({ + left: scrollLeft - (containerRect.left - buttonRect.left) - 20, + behavior: 'smooth', + }); + } else if (buttonRect.right > containerRect.right) { + container.scrollTo({ + left: scrollLeft + (buttonRect.right - containerRect.right) + 20, + behavior: 'smooth', + }); + } + } } - }, [currentPage, pageCount]); + }, [currentPage]); + + // 生成分页标签 + const categories = Array.from({ length: pageCount }, (_, i) => { + const start = i * episodesPerPage + 1; + const end = Math.min(start + episodesPerPage - 1, totalEpisodes); + return start === end ? `${start}` : `${start}-${end}`; + }); // 处理换源tab点击,只在点击时才搜索 const handleSourceTabClick = () => { @@ -287,11 +239,11 @@ const EpisodeSelector: React.FC = ({
setActiveTab('episodes')} className={`flex-1 py-3 px-6 text-center cursor-pointer transition-all duration-200 font-medium - ${ - activeTab === 'episodes' - ? 'text-green-600 dark:text-green-400' - : 'text-gray-700 hover:text-green-600 bg-black/5 dark:bg-white/5 dark:text-gray-300 dark:hover:text-green-400 hover:bg-black/3 dark:hover:bg-white/3' - } + ${ + activeTab === 'episodes' + ? 'text-green-600 dark:text-green-400' + : 'text-gray-700 hover:text-green-600 bg-black/5 dark:bg-white/5 dark:text-gray-300 dark:hover:text-green-400 hover:bg-black/3 dark:hover:bg-white/3' + } `.trim()} > 选集 @@ -300,12 +252,12 @@ const EpisodeSelector: React.FC = ({
换源
@@ -316,22 +268,6 @@ const EpisodeSelector: React.FC = ({ <> {/* 分类标签 */}
- {/* 左导航按钮 - 强制显示用于测试 */} - -
{categories.map((label, idx) => { @@ -343,7 +279,7 @@ const EpisodeSelector: React.FC = ({ buttonRefs.current[idx] = el; }} onClick={() => handleCategoryClick(idx)} - className={`w-20 relative py-2 text-sm font-medium transition-colors whitespace-nowrap flex-shrink-0 text-center + className={`w-20 relative py-2 text-sm font-medium transition-colors whitespace-nowrap flex-shrink-0 text-center ${ isActive ? 'text-green-500 dark:text-green-400' @@ -360,23 +296,6 @@ const EpisodeSelector: React.FC = ({ })}
- - {/* 右导航按钮 - 强制显示用于测试 */} - - {/* 向上/向下按钮 */}
- {/* 测试区域 - 验证响应式是否工作 */} -
-

🔧 响应式测试区域

-
- {[1,2,3,4,5,6,7,8,9,10].map(n => ( -
- {n} -
- ))} -
-

如果这个网格随着浏览器缩放改变,说明响应式正常工作

-
- - {/* 集数网格 - 超响应式布局 */} -
- {/* 调试信息 */} -
- 📊 总共 {totalEpisodes} 集 | 每页 {episodesPerPage} 集 | 当前第 {currentPage + 1} 页 | 共 {pageCount} 页 -
- - {/* 响应式网格 - 简化但有效的方案 */} -
- {(() => { - const len = currentEnd - currentStart + 1; - const episodes = Array.from({ length: len }, (_, i) => - descending ? currentEnd - i : currentStart + i - ); - return episodes; - })().map((episodeNumber) => { - const isActive = episodeNumber === value; - return ( - - ); - })} -
+ {/* 集数网格 */} +
+ {(() => { + const len = currentEnd - currentStart + 1; + const episodes = Array.from({ length: len }, (_, i) => + descending ? currentEnd - i : currentStart + i + ); + return episodes; + })().map((episodeNumber) => { + const isActive = episodeNumber === value; + return ( + + ); + })}
)} @@ -548,11 +412,11 @@ const EpisodeSelector: React.FC = ({ !isCurrentSource && handleSourceClick(source) } className={`flex items-start gap-3 px-2 py-3 rounded-lg transition-all select-none duration-200 relative - ${ - isCurrentSource - ? 'bg-green-500/10 dark:bg-green-500/20 border-green-500/30 border' - : 'hover:bg-gray-200/50 dark:hover:bg-white/10 hover:scale-[1.02] cursor-pointer' - }`.trim()} + ${ + isCurrentSource + ? 'bg-green-500/10 dark:bg-green-500/20 border-green-500/30 border' + : 'hover:bg-gray-200/50 dark:hover:bg-white/10 hover:scale-[1.02] cursor-pointer' + }`.trim()} > {/* 封面 */}
@@ -588,7 +452,6 @@ const EpisodeSelector: React.FC = ({ {(() => { const sourceKey = `${source.source}-${source.id}`; const videoInfo = videoInfoMap.get(sourceKey); - if (videoInfo && videoInfo.quality !== '未知') { if (videoInfo.hasError) { return ( @@ -658,7 +521,7 @@ const EpisodeSelector: React.FC = ({
无测速数据
- ); // 占位div + ); } } })()}