diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 8d91667..b6952df 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -11224,7 +11224,7 @@ function AdminPageClient() { const userLimit = 10; // 获取新版本用户列表 - const fetchUsersV2 = useCallback(async (page: number = 1) => { + const fetchUsersV2 = useCallback(async (page = 1) => { try { setUserListLoading(true); const response = await fetch(`/api/admin/users?page=${page}&limit=${userLimit}`); diff --git a/src/app/api/admin/email/route.ts b/src/app/api/admin/email/route.ts index dce0b6b..bdd4e1f 100644 --- a/src/app/api/admin/email/route.ts +++ b/src/app/api/admin/email/route.ts @@ -1,10 +1,10 @@ import { NextRequest, NextResponse } from 'next/server'; -import { getAuthInfoFromCookie } from '@/lib/auth'; -import { getStorage } from '@/lib/db'; -import { getConfig } from '@/lib/config'; -import { EmailService } from '@/lib/email.service'; import type { AdminConfig } from '@/lib/admin.types'; +import { getAuthInfoFromCookie } from '@/lib/auth'; +import { getConfig } from '@/lib/config'; +import { getStorage } from '@/lib/db'; +import { EmailService } from '@/lib/email.service'; export const runtime = 'nodejs'; diff --git a/src/app/api/admin/emby/export/route.ts b/src/app/api/admin/emby/export/route.ts index e631ef5..14160b2 100644 --- a/src/app/api/admin/emby/export/route.ts +++ b/src/app/api/admin/emby/export/route.ts @@ -1,4 +1,5 @@ import { NextRequest, NextResponse } from 'next/server'; + import { getAuthInfoFromCookie } from '@/lib/auth'; import { getConfig } from '@/lib/config'; diff --git a/src/app/api/admin/emby/import/route.ts b/src/app/api/admin/emby/import/route.ts index ecd20c1..99c828a 100644 --- a/src/app/api/admin/emby/import/route.ts +++ b/src/app/api/admin/emby/import/route.ts @@ -1,4 +1,5 @@ import { NextRequest, NextResponse } from 'next/server'; + import { getAuthInfoFromCookie } from '@/lib/auth'; import { getConfig } from '@/lib/config'; import { db } from '@/lib/db'; @@ -37,7 +38,6 @@ export async function POST(request: NextRequest) { // 追加和覆盖:合并Sources数组 if (data.Sources && Array.isArray(data.Sources)) { const existingSources = adminConfig.EmbyConfig?.Sources || []; - const existingKeys = new Set(existingSources.map(s => s.key)); // 覆盖已存在的,追加新的 const mergedSources = [...existingSources]; diff --git a/src/app/api/admin/openlist/route.ts b/src/app/api/admin/openlist/route.ts index e81fc36..5914b4e 100644 --- a/src/app/api/admin/openlist/route.ts +++ b/src/app/api/admin/openlist/route.ts @@ -87,7 +87,7 @@ export async function POST(request: NextRequest) { } // 验证扫描间隔 - let scanInterval = parseInt(ScanInterval) || 0; + const scanInterval = parseInt(ScanInterval) || 0; if (scanInterval > 0 && scanInterval < 60) { return NextResponse.json( { error: '定时扫描间隔最低为 60 分钟' }, diff --git a/src/app/api/admin/users/route.ts b/src/app/api/admin/users/route.ts index 0964e46..84a5811 100644 --- a/src/app/api/admin/users/route.ts +++ b/src/app/api/admin/users/route.ts @@ -2,7 +2,6 @@ import { NextRequest, NextResponse } from 'next/server'; import { getAuthInfoFromCookie } from '@/lib/auth'; -import { getConfig } from '@/lib/config'; import { db } from '@/lib/db'; export const runtime = 'nodejs'; @@ -24,9 +23,6 @@ export async function GET(request: NextRequest) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } - // 获取配置 - const adminConfig = await getConfig(); - // 判定操作者角色 let operatorRole: 'owner' | 'admin' | 'user' = 'user'; if (authInfo.username === process.env.USERNAME) { diff --git a/src/app/api/admin/web-live/route.ts b/src/app/api/admin/web-live/route.ts index 7efbbfe..7a8da57 100644 --- a/src/app/api/admin/web-live/route.ts +++ b/src/app/api/admin/web-live/route.ts @@ -1,4 +1,5 @@ import { NextRequest, NextResponse } from 'next/server'; + import { getAuthInfoFromCookie } from '@/lib/auth'; import { getConfig } from '@/lib/config'; import { db } from '@/lib/db'; diff --git a/src/app/api/ai/chat/route.ts b/src/app/api/ai/chat/route.ts index 9430bde..e2d107f 100644 --- a/src/app/api/ai/chat/route.ts +++ b/src/app/api/ai/chat/route.ts @@ -2,11 +2,11 @@ import { NextRequest, NextResponse } from 'next/server'; -import { getAuthInfoFromCookie } from '@/lib/auth'; import { orchestrateDataSources, VideoContext, } from '@/lib/ai-orchestrator'; +import { getAuthInfoFromCookie } from '@/lib/auth'; import { getConfig } from '@/lib/config'; import { db } from '@/lib/db'; @@ -35,7 +35,7 @@ async function streamOpenAIChat( temperature: number; maxTokens: number; }, - enableStreaming: boolean = true + enableStreaming = true ): Promise { const response = await fetch(`${config.baseURL}/chat/completions`, { method: 'POST', @@ -61,45 +61,6 @@ async function streamOpenAIChat( return enableStreaming ? response.body! : response; } -/** - * Claude API流式聊天请求 - */ -async function streamClaudeChat( - messages: ChatMessage[], - systemPrompt: string, - config: { - apiKey: string; - model: string; - temperature: number; - maxTokens: number; - } -): Promise { - const response = await fetch('https://api.anthropic.com/v1/messages', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-api-key': config.apiKey, - 'anthropic-version': '2023-06-01', - }, - body: JSON.stringify({ - model: config.model, - max_tokens: config.maxTokens, - temperature: config.temperature, - system: systemPrompt, - messages: messages, - stream: true, - }), - }); - - if (!response.ok) { - throw new Error( - `Claude API error: ${response.status} ${response.statusText}` - ); - } - - return response.body!; -} - /** * 转换流为SSE格式 */ diff --git a/src/app/api/auth/oidc/complete-register/route.ts b/src/app/api/auth/oidc/complete-register/route.ts index 098986e..692d8c7 100644 --- a/src/app/api/auth/oidc/complete-register/route.ts +++ b/src/app/api/auth/oidc/complete-register/route.ts @@ -123,7 +123,7 @@ export async function POST(request: NextRequest) { } // 检查用户名是否已存在(优先使用新版本) - let userExists = await db.checkUserExistV2(username); + const userExists = await db.checkUserExistV2(username); if (userExists) { return NextResponse.json( { error: '用户名已存在' }, diff --git a/src/app/api/cms-proxy/route.ts b/src/app/api/cms-proxy/route.ts index 9618dfd..224df4d 100644 --- a/src/app/api/cms-proxy/route.ts +++ b/src/app/api/cms-proxy/route.ts @@ -4,7 +4,6 @@ import { NextRequest, NextResponse } from 'next/server'; import { getConfig } from '@/lib/config'; import { db } from '@/lib/db'; -import { OpenListClient } from '@/lib/openlist.client'; import { getCachedMetaInfo, MetaInfo, @@ -260,13 +259,6 @@ async function handleOpenListProxy(request: NextRequest) { ); } - const rootPath = openListConfig.RootPath || '/'; - const client = new OpenListClient( - openListConfig.URL, - openListConfig.Username, - openListConfig.Password - ); - // 读取 metainfo (从数据库或缓存) let metaInfo: MetaInfo | null = getCachedMetaInfo(); @@ -296,7 +288,7 @@ async function handleOpenListProxy(request: NextRequest) { if (wd) { const results = Object.entries(metaInfo.folders) .filter( - ([key, info]) => + ([_key, info]) => info.folderName.toLowerCase().includes(wd.toLowerCase()) || info.title.toLowerCase().includes(wd.toLowerCase()) ) diff --git a/src/app/api/cron/[password]/route.ts b/src/app/api/cron/[password]/route.ts index d5c79bc..3ea93d7 100644 --- a/src/app/api/cron/[password]/route.ts +++ b/src/app/api/cron/[password]/route.ts @@ -4,12 +4,12 @@ import { NextRequest, NextResponse } from 'next/server'; import { getConfig, refineConfig } from '@/lib/config'; import { db, getStorage } from '@/lib/db'; +import { EmailService } from '@/lib/email.service'; +import { FavoriteUpdate,getBatchFavoriteUpdateEmailTemplate } from '@/lib/email.templates'; import { fetchVideoDetail } from '@/lib/fetchVideoDetail'; import { refreshLiveChannels } from '@/lib/live'; import { startOpenListRefresh } from '@/lib/openlist-refresh'; import { SearchResult } from '@/lib/types'; -import { EmailService } from '@/lib/email.service'; -import { getBatchFavoriteUpdateEmailTemplate, FavoriteUpdate } from '@/lib/email.templates'; export const runtime = 'nodejs'; diff --git a/src/app/api/danmaku-filter/route.ts b/src/app/api/danmaku-filter/route.ts index abe1282..21a6e8e 100644 --- a/src/app/api/danmaku-filter/route.ts +++ b/src/app/api/danmaku-filter/route.ts @@ -3,7 +3,6 @@ import { NextRequest, NextResponse } from 'next/server'; import { getAuthInfoFromCookie } from '@/lib/auth'; -import { getConfig } from '@/lib/config'; import { db } from '@/lib/db'; import { DanmakuFilterConfig } from '@/lib/types'; diff --git a/src/app/api/douban-comments/route.ts b/src/app/api/douban-comments/route.ts index e761727..7648d93 100644 --- a/src/app/api/douban-comments/route.ts +++ b/src/app/api/douban-comments/route.ts @@ -1,5 +1,6 @@ -import { NextRequest, NextResponse } from 'next/server'; import * as cheerio from 'cheerio'; +import { NextRequest, NextResponse } from 'next/server'; + import { fetchDoubanWithVerification } from '@/lib/douban-anti-crawler'; export const runtime = 'nodejs'; diff --git a/src/app/api/douban-recommendations/route.ts b/src/app/api/douban-recommendations/route.ts index 23d4e0a..d4cf231 100644 --- a/src/app/api/douban-recommendations/route.ts +++ b/src/app/api/douban-recommendations/route.ts @@ -1,5 +1,6 @@ -import { NextRequest, NextResponse } from 'next/server'; import * as cheerio from 'cheerio'; +import { NextRequest, NextResponse } from 'next/server'; + import { fetchDoubanData } from '@/lib/douban'; import { fetchDoubanWithVerification } from '@/lib/douban-anti-crawler'; diff --git a/src/app/api/emby/list/route.ts b/src/app/api/emby/list/route.ts index a8a41fa..6b259ba 100644 --- a/src/app/api/emby/list/route.ts +++ b/src/app/api/emby/list/route.ts @@ -2,8 +2,8 @@ import { NextRequest, NextResponse } from 'next/server'; -import { embyManager } from '@/lib/emby-manager'; import { getCachedEmbyList, setCachedEmbyList } from '@/lib/emby-cache'; +import { embyManager } from '@/lib/emby-manager'; export const runtime = 'nodejs'; diff --git a/src/app/api/emby/views/route.ts b/src/app/api/emby/views/route.ts index f3bca93..6110d6a 100644 --- a/src/app/api/emby/views/route.ts +++ b/src/app/api/emby/views/route.ts @@ -2,8 +2,8 @@ import { NextRequest, NextResponse } from 'next/server'; -import { embyManager } from '@/lib/emby-manager'; import { getCachedEmbyViews, setCachedEmbyViews } from '@/lib/emby-cache'; +import { embyManager } from '@/lib/emby-manager'; export const runtime = 'nodejs'; diff --git a/src/app/api/favorites/route.ts b/src/app/api/favorites/route.ts index d69633b..9a74276 100644 --- a/src/app/api/favorites/route.ts +++ b/src/app/api/favorites/route.ts @@ -3,7 +3,6 @@ import { NextRequest, NextResponse } from 'next/server'; import { getAuthInfoFromCookie } from '@/lib/auth'; -import { getConfig } from '@/lib/config'; import { db } from '@/lib/db'; import { Favorite } from '@/lib/types'; diff --git a/src/app/api/movie-requests/route.ts b/src/app/api/movie-requests/route.ts index 2946636..ed0bb74 100644 --- a/src/app/api/movie-requests/route.ts +++ b/src/app/api/movie-requests/route.ts @@ -1,5 +1,5 @@ -import { NextRequest, NextResponse } from 'next/server'; import { nanoid } from 'nanoid'; +import { NextRequest, NextResponse } from 'next/server'; import { getAuthInfoFromCookie } from '@/lib/auth'; import { getStorage } from '@/lib/db'; diff --git a/src/app/api/offline-download/local/[source]/[videoId]/[episodeIndex]/[...file]/route.ts b/src/app/api/offline-download/local/[source]/[videoId]/[episodeIndex]/[...file]/route.ts index 010ba2c..30a5e43 100644 --- a/src/app/api/offline-download/local/[source]/[videoId]/[episodeIndex]/[...file]/route.ts +++ b/src/app/api/offline-download/local/[source]/[videoId]/[episodeIndex]/[...file]/route.ts @@ -3,11 +3,12 @@ * 路径格式: /api/offline-download/local/[source]/[videoId]/[episodeIndex]/[file] */ -import { NextRequest, NextResponse } from 'next/server'; -import { getAuthInfoFromCookie } from '@/lib/auth'; import * as fs from 'fs'; +import { NextRequest, NextResponse } from 'next/server'; import * as path from 'path'; +import { getAuthInfoFromCookie } from '@/lib/auth'; + // 检查是否启用离线下载功能 const OFFLINE_DOWNLOAD_ENABLED = process.env.NEXT_PUBLIC_ENABLE_OFFLINE_DOWNLOAD === 'true'; const OFFLINE_DOWNLOAD_DIR = process.env.OFFLINE_DOWNLOAD_DIR || '/data'; diff --git a/src/app/api/offline-download/local/route.ts b/src/app/api/offline-download/local/route.ts index 14a1d55..de414c1 100644 --- a/src/app/api/offline-download/local/route.ts +++ b/src/app/api/offline-download/local/route.ts @@ -2,11 +2,12 @@ * 本地下载视频播放代理 API */ -import { NextRequest, NextResponse } from 'next/server'; -import { getAuthInfoFromCookie } from '@/lib/auth'; import * as fs from 'fs'; +import { NextRequest, NextResponse } from 'next/server'; import * as path from 'path'; +import { getAuthInfoFromCookie } from '@/lib/auth'; + // 检查是否启用离线下载功能 const OFFLINE_DOWNLOAD_ENABLED = process.env.NEXT_PUBLIC_ENABLE_OFFLINE_DOWNLOAD === 'true'; const OFFLINE_DOWNLOAD_DIR = process.env.OFFLINE_DOWNLOAD_DIR || '/data'; diff --git a/src/app/api/offline-download/route.ts b/src/app/api/offline-download/route.ts index 875b10b..6609951 100644 --- a/src/app/api/offline-download/route.ts +++ b/src/app/api/offline-download/route.ts @@ -2,11 +2,12 @@ * 离线下载任务管理 API */ +import * as fs from 'fs'; import { NextRequest, NextResponse } from 'next/server'; +import * as path from 'path'; + import { getAuthInfoFromCookie } from '@/lib/auth'; import { OfflineDownloader, OfflineDownloadTask } from '@/lib/offline-downloader'; -import * as fs from 'fs'; -import * as path from 'path'; // 检查是否启用离线下载功能 const OFFLINE_DOWNLOAD_ENABLED = process.env.NEXT_PUBLIC_ENABLE_OFFLINE_DOWNLOAD === 'true'; diff --git a/src/app/api/openlist/delete/route.ts b/src/app/api/openlist/delete/route.ts index 77501ce..0cfce85 100644 --- a/src/app/api/openlist/delete/route.ts +++ b/src/app/api/openlist/delete/route.ts @@ -6,7 +6,6 @@ import { getAuthInfoFromCookie } from '@/lib/auth'; import { getConfig } from '@/lib/config'; import { db } from '@/lib/db'; import { - getCachedMetaInfo, invalidateMetaInfoCache, MetaInfo, setCachedMetaInfo, diff --git a/src/app/api/playrecords/route.ts b/src/app/api/playrecords/route.ts index e44ed53..dd5eeca 100644 --- a/src/app/api/playrecords/route.ts +++ b/src/app/api/playrecords/route.ts @@ -3,7 +3,6 @@ import { NextRequest, NextResponse } from 'next/server'; import { getAuthInfoFromCookie } from '@/lib/auth'; -import { getConfig } from '@/lib/config'; import { db } from '@/lib/db'; import { PlayRecord } from '@/lib/types'; diff --git a/src/app/api/searchhistory/route.ts b/src/app/api/searchhistory/route.ts index 9333e53..b5b0ba6 100644 --- a/src/app/api/searchhistory/route.ts +++ b/src/app/api/searchhistory/route.ts @@ -3,7 +3,6 @@ import { NextRequest, NextResponse } from 'next/server'; import { getAuthInfoFromCookie } from '@/lib/auth'; -import { getConfig } from '@/lib/config'; import { db } from '@/lib/db'; export const runtime = 'nodejs'; diff --git a/src/app/api/skipconfigs/route.ts b/src/app/api/skipconfigs/route.ts index ee032a6..712697c 100644 --- a/src/app/api/skipconfigs/route.ts +++ b/src/app/api/skipconfigs/route.ts @@ -3,7 +3,6 @@ import { NextRequest, NextResponse } from 'next/server'; import { getAuthInfoFromCookie } from '@/lib/auth'; -import { getConfig } from '@/lib/config'; import { db } from '@/lib/db'; import { SkipConfig } from '@/lib/types'; diff --git a/src/app/api/theme/css/route.ts b/src/app/api/theme/css/route.ts index 3a13fc4..aa20607 100644 --- a/src/app/api/theme/css/route.ts +++ b/src/app/api/theme/css/route.ts @@ -3,6 +3,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { getConfig } from '@/lib/config'; + import { getThemeCSS } from '@/styles/themes'; export const runtime = 'nodejs'; diff --git a/src/app/api/tmdb-details/route.ts b/src/app/api/tmdb-details/route.ts index 7f48dcc..82e2976 100644 --- a/src/app/api/tmdb-details/route.ts +++ b/src/app/api/tmdb-details/route.ts @@ -1,11 +1,12 @@ import { NextRequest, NextResponse } from 'next/server'; + +import { getConfig } from '@/lib/config'; import { - searchTMDBMulti, + getTMDBImageUrl, getTMDBMovieDetails, getTMDBTVDetails, - getTMDBImageUrl, + searchTMDBMulti, } from '@/lib/tmdb.client'; -import { getConfig } from '@/lib/config'; // 服务器端缓存(内存) const searchCache = new Map< diff --git a/src/app/api/tmdb-recommendations/route.ts b/src/app/api/tmdb-recommendations/route.ts index 08e8690..5d8bfab 100644 --- a/src/app/api/tmdb-recommendations/route.ts +++ b/src/app/api/tmdb-recommendations/route.ts @@ -1,11 +1,12 @@ import { NextRequest, NextResponse } from 'next/server'; + +import { getConfig } from '@/lib/config'; import { - searchTMDBMulti, + getTMDBImageUrl, getTMDBMovieRecommendations, getTMDBTVRecommendations, - getTMDBImageUrl, + searchTMDBMulti, } from '@/lib/tmdb.client'; -import { getConfig } from '@/lib/config'; // 服务器端缓存(1天) const searchCache = new Map(); diff --git a/src/app/api/tmdb/detail/route.ts b/src/app/api/tmdb/detail/route.ts index 8ff93eb..a12fc40 100644 --- a/src/app/api/tmdb/detail/route.ts +++ b/src/app/api/tmdb/detail/route.ts @@ -1,12 +1,12 @@ /* eslint-disable @typescript-eslint/no-explicit-any, no-console */ +import { HttpsProxyAgent } from 'https-proxy-agent'; import { NextRequest, NextResponse } from 'next/server'; +import nodeFetch from 'node-fetch'; import { getAuthInfoFromCookie } from '@/lib/auth'; import { getConfig } from '@/lib/config'; import { getNextApiKey } from '@/lib/tmdb.client'; -import { HttpsProxyAgent } from 'https-proxy-agent'; -import nodeFetch from 'node-fetch'; export const runtime = 'nodejs'; diff --git a/src/app/api/tmdb/episodes/route.ts b/src/app/api/tmdb/episodes/route.ts index c00282f..baab637 100644 --- a/src/app/api/tmdb/episodes/route.ts +++ b/src/app/api/tmdb/episodes/route.ts @@ -1,11 +1,11 @@ /* eslint-disable @typescript-eslint/no-explicit-any, no-console */ +import { HttpsProxyAgent } from 'https-proxy-agent'; import { NextRequest, NextResponse } from 'next/server'; +import nodeFetch from 'node-fetch'; import { getAuthInfoFromCookie } from '@/lib/auth'; import { getConfig } from '@/lib/config'; -import { HttpsProxyAgent } from 'https-proxy-agent'; -import nodeFetch from 'node-fetch'; import { getNextApiKey } from '@/lib/tmdb.client'; export const runtime = 'nodejs'; diff --git a/src/app/api/tmdb/search/route.ts b/src/app/api/tmdb/search/route.ts index 50d12cc..297b3e3 100644 --- a/src/app/api/tmdb/search/route.ts +++ b/src/app/api/tmdb/search/route.ts @@ -1,12 +1,12 @@ /* eslint-disable @typescript-eslint/no-explicit-any, no-console */ +import { HttpsProxyAgent } from 'https-proxy-agent'; import { NextRequest, NextResponse } from 'next/server'; +import nodeFetch from 'node-fetch'; import { getAuthInfoFromCookie } from '@/lib/auth'; import { getConfig } from '@/lib/config'; import { getNextApiKey } from '@/lib/tmdb.client'; -import { HttpsProxyAgent } from 'https-proxy-agent'; -import nodeFetch from 'node-fetch'; export const runtime = 'nodejs'; diff --git a/src/app/api/tmdb/trending/route.ts b/src/app/api/tmdb/trending/route.ts index e23f49c..3fd835f 100644 --- a/src/app/api/tmdb/trending/route.ts +++ b/src/app/api/tmdb/trending/route.ts @@ -1,9 +1,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { NextResponse } from 'next/server'; -import { getTMDBTrendingContent, getTMDBVideos } from '@/lib/tmdb.client'; + import { getConfig } from '@/lib/config'; import { fetchDoubanData } from '@/lib/douban'; +import { getTMDBTrendingContent, getTMDBVideos } from '@/lib/tmdb.client'; // 缓存配置 - 服务器内存缓存3小时 const CACHE_DURATION = 3 * 60 * 60 * 1000; // 3小时 diff --git a/src/app/api/tmdb/upcoming/route.ts b/src/app/api/tmdb/upcoming/route.ts index e78b6ea..2607aab 100644 --- a/src/app/api/tmdb/upcoming/route.ts +++ b/src/app/api/tmdb/upcoming/route.ts @@ -1,6 +1,7 @@ import { NextRequest, NextResponse } from 'next/server'; -import { getTMDBUpcomingContent } from '@/lib/tmdb.client'; + import { getConfig } from '@/lib/config'; +import { getTMDBUpcomingContent } from '@/lib/tmdb.client'; // 内存缓存对象 interface CacheItem { diff --git a/src/app/api/web-live/sources/route.ts b/src/app/api/web-live/sources/route.ts index 5f1c138..502e53d 100644 --- a/src/app/api/web-live/sources/route.ts +++ b/src/app/api/web-live/sources/route.ts @@ -1,4 +1,5 @@ import { NextRequest, NextResponse } from 'next/server'; + import { getConfig } from '@/lib/config'; export const dynamic = 'force-dynamic'; // 禁用缓存 diff --git a/src/app/api/web-live/stream/route.ts b/src/app/api/web-live/stream/route.ts index 4ae97ff..2eb522a 100644 --- a/src/app/api/web-live/stream/route.ts +++ b/src/app/api/web-live/stream/route.ts @@ -1,5 +1,5 @@ -import { NextRequest, NextResponse } from 'next/server'; import crypto from 'crypto'; +import { NextRequest, NextResponse } from 'next/server'; function getAntiCode(oldAntiCode: string, streamName: string): string { const paramsT = 100; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 2e697f5..beb03d7 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -7,16 +7,16 @@ import './globals.css'; import { getConfig } from '@/lib/config'; +import { DanmakuCacheCleanup } from '../components/DanmakuCacheCleanup'; +import { DownloadBubble } from '../components/DownloadBubble'; +import { DownloadPanel } from '../components/DownloadPanel'; import { GlobalErrorIndicator } from '../components/GlobalErrorIndicator'; import { SiteProvider } from '../components/SiteProvider'; import { ThemeProvider } from '../components/ThemeProvider'; -import { WatchRoomProvider } from '../components/WatchRoomProvider'; -import ChatFloatingWindow from '../components/watch-room/ChatFloatingWindow'; -import { DownloadProvider } from '../contexts/DownloadContext'; -import { DownloadBubble } from '../components/DownloadBubble'; -import { DownloadPanel } from '../components/DownloadPanel'; -import { DanmakuCacheCleanup } from '../components/DanmakuCacheCleanup'; import TopProgressBar from '../components/TopProgressBar'; +import ChatFloatingWindow from '../components/watch-room/ChatFloatingWindow'; +import { WatchRoomProvider } from '../components/WatchRoomProvider'; +import { DownloadProvider } from '../contexts/DownloadContext'; const inter = Inter({ subsets: ['latin'] }); export const dynamic = 'force-dynamic'; diff --git a/src/app/live/page.tsx b/src/app/live/page.tsx index d891610..6623fd7 100644 --- a/src/app/live/page.tsx +++ b/src/app/live/page.tsx @@ -6,8 +6,6 @@ import { Heart, Radio, Tv } from 'lucide-react'; import { useRouter, useSearchParams } from 'next/navigation'; import { useEffect, useRef, useState } from 'react'; -import { useLiveSync } from '@/hooks/useLiveSync'; - import { deleteFavorite, generateStorageKey, @@ -17,6 +15,7 @@ import { subscribeToDataUpdates, } from '@/lib/db.client'; import { parseCustomTimeFormat } from '@/lib/time'; +import { useLiveSync } from '@/hooks/useLiveSync'; import EpgScrollableRow from '@/components/EpgScrollableRow'; import PageLayout from '@/components/PageLayout'; @@ -1150,7 +1149,7 @@ function LivePageClient() { if (!selectedGroup) return; // 先在当前分组搜索 - let filtered = filterChannels(selectedGroup, keyword); + const filtered = filterChannels(selectedGroup, keyword); // 如果当前分组没有匹配的频道,且有搜索关键词,轮询所有分组 if (filtered.length === 0 && keyword.trim() && groupedChannels) { diff --git a/src/app/movie-request/page.tsx b/src/app/movie-request/page.tsx index 2126290..a9a6818 100644 --- a/src/app/movie-request/page.tsx +++ b/src/app/movie-request/page.tsx @@ -1,14 +1,15 @@ 'use client'; -import { useState, useEffect } from 'react'; +import { AlertCircle, CheckCircle } from 'lucide-react'; import { useRouter } from 'next/navigation'; +import { useEffect,useState } from 'react'; import { createPortal } from 'react-dom'; -import { CheckCircle, AlertCircle, Plus } from 'lucide-react'; -import PageLayout from '@/components/PageLayout'; import { getTMDBImageUrl } from '@/lib/tmdb.client'; import { processImageUrl } from '@/lib/utils'; +import PageLayout from '@/components/PageLayout'; + interface TMDBResult { id: number; title?: string; diff --git a/src/app/page.tsx b/src/app/page.tsx index 142e4c3..f04e6a2 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,9 +2,9 @@ 'use client'; -import { ChevronRight, Bot, ListVideo } from 'lucide-react'; +import { Bot, ChevronRight, ListVideo } from 'lucide-react'; import Link from 'next/link'; -import { Suspense, useCallback, useEffect, useRef, useState } from 'react'; +import { Suspense, useEffect, useState } from 'react'; import { BangumiCalendarData, @@ -15,15 +15,15 @@ import { getTMDBImageUrl, TMDBItem } from '@/lib/tmdb.client'; import { DoubanItem } from '@/lib/types'; import { processImageUrl } from '@/lib/utils'; +import AIChatPanel from '@/components/AIChatPanel'; +import BannerCarousel from '@/components/BannerCarousel'; import ContinueWatching from '@/components/ContinueWatching'; +import FireworksCanvas from '@/components/FireworksCanvas'; +import HttpWarningDialog from '@/components/HttpWarningDialog'; import PageLayout from '@/components/PageLayout'; import ScrollableRow from '@/components/ScrollableRow'; import { useSite } from '@/components/SiteProvider'; import VideoCard from '@/components/VideoCard'; -import HttpWarningDialog from '@/components/HttpWarningDialog'; -import BannerCarousel from '@/components/BannerCarousel'; -import AIChatPanel from '@/components/AIChatPanel'; -import FireworksCanvas from '@/components/FireworksCanvas'; // 首页模块配置接口 interface HomeModule { @@ -151,7 +151,9 @@ function HomeClient() { const setCache = (key: string, data: any) => { try { localStorage.setItem(key, JSON.stringify({ data, timestamp: Date.now() })); - } catch {} + } catch { + // Ignore localStorage errors + } }; const moviesCache = getCache('homepage_movies'); diff --git a/src/app/play/page.tsx b/src/app/play/page.tsx index 1f06ca1..f2e1055 100644 --- a/src/app/play/page.tsx +++ b/src/app/play/page.tsx @@ -2,70 +2,69 @@ 'use client'; -import { Heart, Search, X, Cloud, Sparkles, AlertCircle } from 'lucide-react'; +import { AlertCircle,Cloud, Heart, Sparkles, X } from 'lucide-react'; import { useRouter, useSearchParams } from 'next/navigation'; import { Suspense, useEffect, useRef, useState } from 'react'; -import { usePlaySync } from '@/hooks/usePlaySync'; -import { getDoubanDetail } from '@/lib/douban.client'; -import { useDownload } from '@/contexts/DownloadContext'; import { getAuthInfoFromBrowserCookie } from '@/lib/auth'; -import { useSite } from '@/components/SiteProvider'; - +import { + convertDanmakuFormat, + getDanmakuById, + getDanmakuFromCache, + getEpisodes, + initDanmakuModule, + loadDanmakuDisplayState, + loadDanmakuSettings, + saveDanmakuDisplayState, + saveDanmakuSettings, + searchAnime, +} from '@/lib/danmaku/api'; +import { + getDanmakuAnimeId, + getDanmakuSearchKeyword, + getDanmakuSourceIndex, + getManualDanmakuSelection, + saveDanmakuAnimeId, + saveDanmakuSearchKeyword, + saveDanmakuSourceIndex, + saveManualDanmakuSelection, +} from '@/lib/danmaku/selection-memory'; +import type { DanmakuAnime, DanmakuComment,DanmakuSelection, DanmakuSettings } from '@/lib/danmaku/types'; import { deleteFavorite, deletePlayRecord, deleteSkipConfig, generateStorageKey, getAllPlayRecords, + getDanmakuFilterConfig, + getEpisodeFilterConfig, getSkipConfig, isFavorited, saveFavorite, savePlayRecord, saveSkipConfig, subscribeToDataUpdates, - getDanmakuFilterConfig, - getEpisodeFilterConfig, } from '@/lib/db.client'; -import { - convertDanmakuFormat, - getDanmakuById, - getEpisodes, - loadDanmakuSettings, - saveDanmakuSettings, - searchAnime, - initDanmakuModule, - getDanmakuFromCache, - saveDanmakuDisplayState, - loadDanmakuDisplayState, -} from '@/lib/danmaku/api'; -import { - getDanmakuSourceIndex, - saveDanmakuSourceIndex, - getManualDanmakuSelection, - saveManualDanmakuSelection, - saveDanmakuSearchKeyword, - getDanmakuSearchKeyword, - saveDanmakuAnimeId, - getDanmakuAnimeId, -} from '@/lib/danmaku/selection-memory'; -import type { DanmakuAnime, DanmakuSelection, DanmakuSettings, DanmakuComment } from '@/lib/danmaku/types'; -import { SearchResult, DanmakuFilterConfig, EpisodeFilterConfig } from '@/lib/types'; -import { getVideoResolutionFromM3u8, processImageUrl } from '@/lib/utils'; +import { getDoubanDetail } from '@/lib/douban.client'; import { getTMDBImageUrl } from '@/lib/tmdb.search'; - -import EpisodeSelector from '@/components/EpisodeSelector'; -import DownloadEpisodeSelector from '@/components/DownloadEpisodeSelector'; -import PageLayout from '@/components/PageLayout'; -import DoubanComments from '@/components/DoubanComments'; -import SmartRecommendations from '@/components/SmartRecommendations'; -import DanmakuFilterSettings from '@/components/DanmakuFilterSettings'; -import Toast, { ToastProps } from '@/components/Toast'; -import AIChatPanel from '@/components/AIChatPanel'; +import { DanmakuFilterConfig, EpisodeFilterConfig,SearchResult } from '@/lib/types'; +import { getVideoResolutionFromM3u8, processImageUrl } from '@/lib/utils'; import { useEnableComments } from '@/hooks/useEnableComments'; -import PansouSearch from '@/components/PansouSearch'; -import CustomHeatmap from '@/components/CustomHeatmap'; +import { usePlaySync } from '@/hooks/usePlaySync'; + +import AIChatPanel from '@/components/AIChatPanel'; import CorrectDialog from '@/components/CorrectDialog'; +import DanmakuFilterSettings from '@/components/DanmakuFilterSettings'; +import DoubanComments from '@/components/DoubanComments'; +import DownloadEpisodeSelector from '@/components/DownloadEpisodeSelector'; +import EpisodeSelector from '@/components/EpisodeSelector'; +import PageLayout from '@/components/PageLayout'; +import PansouSearch from '@/components/PansouSearch'; +import { useSite } from '@/components/SiteProvider'; +import SmartRecommendations from '@/components/SmartRecommendations'; +import Toast, { ToastProps } from '@/components/Toast'; + +import { useDownload } from '@/contexts/DownloadContext'; // 扩展 HTMLVideoElement 类型以支持 hls 属性 declare global { @@ -545,7 +544,7 @@ function PlayPageClient() { // 只有用户主动点击推荐时才会添加 _reload 参数 if (reloadParam && urlTitle && urlTitle !== videoTitle && !isSourceChangingRef.current) { console.log('[PlayPage] User clicked recommendation, reloading page'); - window.location.href = window.location.href; + window.location.reload(); } // 重置换源标记 @@ -1526,7 +1525,7 @@ function PlayPageClient() { const refreshXiaoyaUrl = async ( hls: any, video: HTMLVideoElement, - isScheduled: boolean = false + isScheduled = false ) => { // 防抖:距离上次刷新不足3秒则不刷新 const now = Date.now(); @@ -3911,7 +3910,7 @@ function PlayPageClient() { }; // 处理弹幕选择 - const handleDanmakuSelect = async (selection: DanmakuSelection, isManual: boolean = false) => { + const handleDanmakuSelect = async (selection: DanmakuSelection, isManual = false) => { console.log(`[弹幕选择] isManual=${isManual}, selection:`, selection); setCurrentDanmakuSelection(selection); @@ -3944,7 +3943,7 @@ function PlayPageClient() { }; // 处理用户选择弹幕源 - const handleDanmakuSourceSelect = async (selectedAnime: DanmakuAnime, selectedIndex?: number, isManualSearch: boolean = false) => { + const handleDanmakuSourceSelect = async (selectedAnime: DanmakuAnime, selectedIndex?: number, isManualSearch = false) => { setShowDanmakuSourceSelector(false); try { @@ -4863,12 +4862,14 @@ function PlayPageClient() { return; } // 检查其他 HTTP 错误状态码 - const statusCode = data.response?.code || data.response?.status; - if (statusCode && statusCode >= 400) { - console.log(`HTTP ${statusCode} 错误`); - hls.destroy(); - setVideoError(`HTTP ${statusCode} 错误`); - return; + { + const statusCode = data.response?.code || data.response?.status; + if (statusCode && statusCode >= 400) { + console.log(`HTTP ${statusCode} 错误`); + hls.destroy(); + setVideoError(`HTTP ${statusCode} 错误`); + return; + } } console.log('网络错误,尝试恢复...'); hls.startLoad(); diff --git a/src/app/private-library/page.tsx b/src/app/private-library/page.tsx index 3854e5d..730b2a5 100644 --- a/src/app/private-library/page.tsx +++ b/src/app/private-library/page.tsx @@ -2,15 +2,16 @@ 'use client'; +import { ArrowDownWideNarrow, ArrowUpNarrowWide,Film } from 'lucide-react'; import { useRouter, useSearchParams } from 'next/navigation'; -import { useEffect, useState, useRef, useMemo } from 'react'; -import { Film, ArrowUpDown, ArrowDownWideNarrow, ArrowUpNarrowWide } from 'lucide-react'; +import { useEffect, useMemo,useRef, useState } from 'react'; import { createPortal } from 'react-dom'; +import { base58Encode } from '@/lib/utils'; + import CapsuleSwitch from '@/components/CapsuleSwitch'; import PageLayout from '@/components/PageLayout'; import VideoCard from '@/components/VideoCard'; -import { base58Encode } from '@/lib/utils'; type LibrarySourceType = 'openlist' | 'emby' | 'xiaoya' | `emby:${string}` | `emby_${string}`; diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx index c686929..375cf99 100644 --- a/src/app/search/page.tsx +++ b/src/app/search/page.tsx @@ -1,10 +1,11 @@ /* eslint-disable react-hooks/exhaustive-deps, @typescript-eslint/no-explicit-any,@typescript-eslint/no-non-null-assertion,no-empty */ 'use client'; -import { ChevronUp, RefreshCw, Search, X, Film, HardDrive, Magnet } from 'lucide-react'; +import { ChevronUp, Film, HardDrive, Magnet,RefreshCw, Search, X } from 'lucide-react'; import { useRouter, useSearchParams } from 'next/navigation'; import React, { startTransition, Suspense, useEffect, useMemo, useRef, useState } from 'react'; +import { getAuthInfoFromBrowserCookie } from '@/lib/auth'; import { addSearchHistory, clearSearchHistory, @@ -13,15 +14,14 @@ import { subscribeToDataUpdates, } from '@/lib/db.client'; import { SearchResult } from '@/lib/types'; -import { getAuthInfoFromBrowserCookie } from '@/lib/auth'; +import AcgSearch from '@/components/AcgSearch'; +import CapsuleSwitch from '@/components/CapsuleSwitch'; import PageLayout from '@/components/PageLayout'; +import PansouSearch from '@/components/PansouSearch'; import SearchResultFilter, { SearchFilterCategory } from '@/components/SearchResultFilter'; import SearchSuggestions from '@/components/SearchSuggestions'; import VideoCard, { VideoCardHandle } from '@/components/VideoCard'; -import PansouSearch from '@/components/PansouSearch'; -import AcgSearch from '@/components/AcgSearch'; -import CapsuleSwitch from '@/components/CapsuleSwitch'; function SearchPageClient() { // 搜索历史 diff --git a/src/app/watch-room/page.tsx b/src/app/watch-room/page.tsx index d30fbf9..cad5a12 100644 --- a/src/app/watch-room/page.tsx +++ b/src/app/watch-room/page.tsx @@ -1,12 +1,15 @@ // 观影室首页 - 选项卡式界面 'use client'; -import { useState, useEffect } from 'react'; -import { Users, UserPlus, List as ListIcon, Lock, RefreshCw } from 'lucide-react'; +import { List as ListIcon, Lock, RefreshCw,UserPlus, Users } from 'lucide-react'; import { useRouter } from 'next/navigation'; -import { useWatchRoomContext } from '@/components/WatchRoomProvider'; -import PageLayout from '@/components/PageLayout'; +import { useEffect,useState } from 'react'; + import { getAuthInfoFromBrowserCookie } from '@/lib/auth'; + +import PageLayout from '@/components/PageLayout'; +import { useWatchRoomContext } from '@/components/WatchRoomProvider'; + import type { Room } from '@/types/watch-room'; type TabType = 'create' | 'join' | 'list'; diff --git a/src/app/web-live/page.tsx b/src/app/web-live/page.tsx index 908fb27..2ae6552 100644 --- a/src/app/web-live/page.tsx +++ b/src/app/web-live/page.tsx @@ -1,9 +1,9 @@ 'use client'; +import { AlertTriangle,Radio } from 'lucide-react'; import { useEffect, useRef, useState } from 'react'; + import PageLayout from '@/components/PageLayout'; -import { Radio, AlertTriangle } from 'lucide-react'; -import Head from 'next/head'; let Artplayer: any = null; let Hls: any = null; diff --git a/src/components/AIChatPanel.tsx b/src/components/AIChatPanel.tsx index c278e54..387186b 100644 --- a/src/components/AIChatPanel.tsx +++ b/src/components/AIChatPanel.tsx @@ -1,11 +1,12 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ 'use client'; -import React, { useState, useRef, useEffect } from 'react'; +import { Bot, Loader2, Send, Sparkles, Trash2,X } from 'lucide-react'; +import React, { useEffect,useRef, useState } from 'react'; import { createPortal } from 'react-dom'; -import { X, Send, Bot, Loader2, Sparkles, Trash2 } from 'lucide-react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; + import { VideoContext } from '@/lib/ai-orchestrator'; interface ChatMessage { diff --git a/src/components/AcgSearch.tsx b/src/components/AcgSearch.tsx index 9a180c7..3d3c4ed 100644 --- a/src/components/AcgSearch.tsx +++ b/src/components/AcgSearch.tsx @@ -2,10 +2,10 @@ 'use client'; import { AlertCircle, Download, ExternalLink, Loader2 } from 'lucide-react'; -import { useEffect, useState, useRef, useCallback } from 'react'; +import { useCallback,useEffect, useRef, useState } from 'react'; -import Toast, { ToastProps } from '@/components/Toast'; import CapsuleSwitch from '@/components/CapsuleSwitch'; +import Toast, { ToastProps } from '@/components/Toast'; interface AcgSearchItem { title: string; diff --git a/src/components/BannerCarousel.tsx b/src/components/BannerCarousel.tsx index c12bac1..91c3a4e 100644 --- a/src/components/BannerCarousel.tsx +++ b/src/components/BannerCarousel.tsx @@ -1,11 +1,12 @@ 'use client'; -import { useEffect, useState, useCallback, useRef } from 'react'; +import { ChevronLeft, ChevronRight, Play } from 'lucide-react'; import Image from 'next/image'; import { useRouter } from 'next/navigation'; -import { getTMDBImageUrl, getGenreNames, type TMDBItem } from '@/lib/tmdb.client'; +import { useCallback, useEffect, useRef,useState } from 'react'; + +import { type TMDBItem,getGenreNames, getTMDBImageUrl } from '@/lib/tmdb.client'; import { processImageUrl } from '@/lib/utils'; -import { ChevronLeft, ChevronRight, Play } from 'lucide-react'; interface BannerCarouselProps { autoPlayInterval?: number; // 自动播放间隔(毫秒) diff --git a/src/components/ContinueWatching.tsx b/src/components/ContinueWatching.tsx index ddb01cd..57813aa 100644 --- a/src/components/ContinueWatching.tsx +++ b/src/components/ContinueWatching.tsx @@ -1,9 +1,9 @@ /* eslint-disable no-console */ 'use client'; +import { AlertTriangle } from 'lucide-react'; import { useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; -import { AlertTriangle } from 'lucide-react'; import type { PlayRecord } from '@/lib/db.client'; import { diff --git a/src/components/CorrectDialog.tsx b/src/components/CorrectDialog.tsx index 6e2a85a..472cd51 100644 --- a/src/components/CorrectDialog.tsx +++ b/src/components/CorrectDialog.tsx @@ -225,7 +225,7 @@ export default function CorrectDialog({ try { // 构建标题和ID:如果是第二季及以后,在标题后加上季度名称,并使用季度ID let finalTitle = result.title || result.name; - let finalTmdbId = result.id; + const finalTmdbId = result.id; if (season && season.season_number > 1) { finalTitle = `${finalTitle} ${season.name}`; diff --git a/src/components/CustomHeatmap.tsx b/src/components/CustomHeatmap.tsx index b24ec0b..9c7501a 100644 --- a/src/components/CustomHeatmap.tsx +++ b/src/components/CustomHeatmap.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useEffect, useRef, useState, useCallback } from 'react'; +import React, { useCallback,useEffect, useRef, useState } from 'react'; interface DanmakuData { time: number; diff --git a/src/components/DanmakuCacheCleanup.tsx b/src/components/DanmakuCacheCleanup.tsx index 5bb5859..1a58cf9 100644 --- a/src/components/DanmakuCacheCleanup.tsx +++ b/src/components/DanmakuCacheCleanup.tsx @@ -1,6 +1,7 @@ 'use client'; import { useEffect } from 'react'; + import { initDanmakuModule } from '@/lib/danmaku/api'; /** diff --git a/src/components/DanmakuFilterSettings.tsx b/src/components/DanmakuFilterSettings.tsx index 95ec81f..1ab378c 100644 --- a/src/components/DanmakuFilterSettings.tsx +++ b/src/components/DanmakuFilterSettings.tsx @@ -1,10 +1,11 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ 'use client'; -import { useState, useEffect, useRef } from 'react'; -import { X, Plus, Trash2, ToggleLeft, ToggleRight } from 'lucide-react'; -import { DanmakuFilterConfig, DanmakuFilterRule } from '@/lib/types'; +import { Plus, ToggleLeft, ToggleRight,Trash2, X } from 'lucide-react'; +import { useEffect, useRef,useState } from 'react'; + import { getDanmakuFilterConfig, saveDanmakuFilterConfig } from '@/lib/db.client'; +import { DanmakuFilterConfig, DanmakuFilterRule } from '@/lib/types'; interface DanmakuFilterSettingsProps { isOpen: boolean; diff --git a/src/components/DanmakuPanel.tsx b/src/components/DanmakuPanel.tsx index b2c332b..703dfa7 100644 --- a/src/components/DanmakuPanel.tsx +++ b/src/components/DanmakuPanel.tsx @@ -1,7 +1,7 @@ 'use client'; import { MagnifyingGlassIcon } from '@heroicons/react/24/outline'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { getEpisodes, searchAnime } from '@/lib/danmaku/api'; import type { diff --git a/src/components/DetailPanel.tsx b/src/components/DetailPanel.tsx index 47f4f29..f752413 100644 --- a/src/components/DetailPanel.tsx +++ b/src/components/DetailPanel.tsx @@ -1,9 +1,10 @@ 'use client'; -import { X, Calendar, Star, Clock, Tag, Users, Globe, Film } from 'lucide-react'; +import { Calendar, Clock, Film,Globe, Star, Tag, Users, X } from 'lucide-react'; import Image from 'next/image'; import React, { useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; + import { getTMDBImageUrl } from '@/lib/tmdb.client'; import { processImageUrl } from '@/lib/utils'; diff --git a/src/components/DoubanComments.tsx b/src/components/DoubanComments.tsx index e631ebd..e574107 100644 --- a/src/components/DoubanComments.tsx +++ b/src/components/DoubanComments.tsx @@ -1,6 +1,7 @@ 'use client'; -import { useEffect, useState, useCallback } from 'react'; +import { useCallback,useEffect, useState } from 'react'; + import { useEnableComments } from '@/hooks/useEnableComments'; interface DoubanComment { diff --git a/src/components/DoubanRecommendations.tsx b/src/components/DoubanRecommendations.tsx index 7f4c564..aa13709 100644 --- a/src/components/DoubanRecommendations.tsx +++ b/src/components/DoubanRecommendations.tsx @@ -1,9 +1,11 @@ 'use client'; -import { useEffect, useState, useCallback } from 'react'; +import { useCallback,useEffect, useState } from 'react'; + import { useEnableComments } from '@/hooks/useEnableComments'; -import VideoCard from '@/components/VideoCard'; + import ScrollableRow from '@/components/ScrollableRow'; +import VideoCard from '@/components/VideoCard'; interface DoubanRecommendation { doubanId: string; diff --git a/src/components/DownloadBubble.tsx b/src/components/DownloadBubble.tsx index 895337f..628d72f 100644 --- a/src/components/DownloadBubble.tsx +++ b/src/components/DownloadBubble.tsx @@ -1,6 +1,7 @@ 'use client'; import React from 'react'; + import { useDownload } from '@/contexts/DownloadContext'; export function DownloadBubble() { diff --git a/src/components/DownloadEpisodeSelector.tsx b/src/components/DownloadEpisodeSelector.tsx index 46f6de8..9719c8e 100644 --- a/src/components/DownloadEpisodeSelector.tsx +++ b/src/components/DownloadEpisodeSelector.tsx @@ -1,6 +1,6 @@ /* eslint-disable @next/next/no-img-element */ -import React, { useState, useMemo } from 'react'; +import React, { useMemo,useState } from 'react'; interface DownloadEpisodeSelectorProps { /** 是否显示弹窗 */ diff --git a/src/components/DownloadPanel.tsx b/src/components/DownloadPanel.tsx index adad9f0..5073b32 100644 --- a/src/components/DownloadPanel.tsx +++ b/src/components/DownloadPanel.tsx @@ -1,9 +1,11 @@ 'use client'; import React from 'react'; -import { useDownload } from '@/contexts/DownloadContext'; + import { M3U8DownloadTask } from '@/lib/m3u8-downloader'; +import { useDownload } from '@/contexts/DownloadContext'; + export function DownloadPanel() { const { tasks, showDownloadPanel, setShowDownloadPanel, startTask, pauseTask, cancelTask, retryFailedSegments, getProgress } = useDownload(); diff --git a/src/components/EpgScrollableRow.tsx b/src/components/EpgScrollableRow.tsx index bc96dd6..5e40a1d 100644 --- a/src/components/EpgScrollableRow.tsx +++ b/src/components/EpgScrollableRow.tsx @@ -1,6 +1,6 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { Clock, Target, Tv, List, BarChart3 } from 'lucide-react'; +import { BarChart3,Clock, List, Target, Tv } from 'lucide-react'; import { useEffect, useRef, useState } from 'react'; import { formatTimeToHHMM, parseCustomTimeFormat } from '@/lib/time'; diff --git a/src/components/EpisodeFilterSettings.tsx b/src/components/EpisodeFilterSettings.tsx index 0f7efb8..e3b4dc5 100644 --- a/src/components/EpisodeFilterSettings.tsx +++ b/src/components/EpisodeFilterSettings.tsx @@ -1,10 +1,11 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ 'use client'; -import { useState, useEffect, useRef } from 'react'; -import { X, Plus, Trash2, ToggleLeft, ToggleRight } from 'lucide-react'; -import { EpisodeFilterConfig, EpisodeFilterRule } from '@/lib/types'; +import { Plus, ToggleLeft, ToggleRight,Trash2, X } from 'lucide-react'; +import { useEffect, useRef,useState } from 'react'; + import { getEpisodeFilterConfig, saveEpisodeFilterConfig } from '@/lib/db.client'; +import { EpisodeFilterConfig, EpisodeFilterRule } from '@/lib/types'; interface EpisodeFilterSettingsProps { isOpen: boolean; diff --git a/src/components/EpisodeSelector.tsx b/src/components/EpisodeSelector.tsx index e536f10..ee82b8b 100644 --- a/src/components/EpisodeSelector.tsx +++ b/src/components/EpisodeSelector.tsx @@ -1,5 +1,6 @@ /* eslint-disable @next/next/no-img-element */ +import { Settings } from 'lucide-react'; import { useRouter } from 'next/navigation'; import React, { useCallback, @@ -8,13 +9,13 @@ import React, { useRef, useState, } from 'react'; -import { Settings } from 'lucide-react'; + +import type { DanmakuComment,DanmakuSelection } from '@/lib/danmaku/types'; +import { EpisodeFilterConfig,SearchResult } from '@/lib/types'; +import { getVideoResolutionFromM3u8, processImageUrl } from '@/lib/utils'; import DanmakuPanel from '@/components/DanmakuPanel'; import EpisodeFilterSettings from '@/components/EpisodeFilterSettings'; -import type { DanmakuSelection, DanmakuComment } from '@/lib/danmaku/types'; -import { SearchResult, EpisodeFilterConfig } from '@/lib/types'; -import { getVideoResolutionFromM3u8, processImageUrl } from '@/lib/utils'; // 定义视频信息类型 interface VideoInfo { diff --git a/src/components/FavoritesPanel.tsx b/src/components/FavoritesPanel.tsx index 7276523..3b54413 100644 --- a/src/components/FavoritesPanel.tsx +++ b/src/components/FavoritesPanel.tsx @@ -2,7 +2,7 @@ 'use client'; -import { Star, X, AlertTriangle } from 'lucide-react'; +import { AlertTriangle,Star, X } from 'lucide-react'; import { useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; @@ -12,6 +12,7 @@ import { getAllPlayRecords, subscribeToDataUpdates, } from '@/lib/db.client'; + import VideoCard from '@/components/VideoCard'; interface FavoriteItem { diff --git a/src/components/HttpWarningDialog.tsx b/src/components/HttpWarningDialog.tsx index af26c8d..ced8f3b 100644 --- a/src/components/HttpWarningDialog.tsx +++ b/src/components/HttpWarningDialog.tsx @@ -3,6 +3,7 @@ import { AlertTriangle } from 'lucide-react'; import { useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; + import { getAuthInfoFromBrowserCookie } from '@/lib/auth'; interface HttpWarningDialogProps { diff --git a/src/components/MobileBottomNav.tsx b/src/components/MobileBottomNav.tsx index a2f082d..f28b892 100644 --- a/src/components/MobileBottomNav.tsx +++ b/src/components/MobileBottomNav.tsx @@ -66,7 +66,7 @@ const MobileBottomNav = ({ activePath }: MobileBottomNavProps) => { const runtimeConfig = (window as any).RUNTIME_CONFIG; // 基础导航项(不包括观影室) - let items = [ + const items = [ { icon: Home, label: '首页', href: '/' }, { icon: Film, diff --git a/src/components/NotificationPanel.tsx b/src/components/NotificationPanel.tsx index 996cdba..d6b3300 100644 --- a/src/components/NotificationPanel.tsx +++ b/src/components/NotificationPanel.tsx @@ -3,8 +3,8 @@ 'use client'; import { Bell, Check, Trash2, X } from 'lucide-react'; -import { useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; import { Notification } from '@/lib/types'; diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index d27e83b..3b8cf4e 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -158,7 +158,7 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => { const runtimeConfig = (window as any).RUNTIME_CONFIG; // 基础菜单项(不包括观影室) - let items = [ + const items = [ { icon: Film, label: '电影', diff --git a/src/components/SmartRecommendations.tsx b/src/components/SmartRecommendations.tsx index 97e42a3..e8b2db4 100644 --- a/src/components/SmartRecommendations.tsx +++ b/src/components/SmartRecommendations.tsx @@ -1,10 +1,12 @@ 'use client'; -import { useEffect, useState, useCallback } from 'react'; +import { useCallback,useEffect, useState } from 'react'; + import { useEnableComments } from '@/hooks/useEnableComments'; import { useRecommendationDataSource } from '@/hooks/useRecommendationDataSource'; -import VideoCard from '@/components/VideoCard'; + import ScrollableRow from '@/components/ScrollableRow'; +import VideoCard from '@/components/VideoCard'; interface Recommendation { doubanId?: string; diff --git a/src/components/Toast.tsx b/src/components/Toast.tsx index c890c6f..9a80ca8 100644 --- a/src/components/Toast.tsx +++ b/src/components/Toast.tsx @@ -1,7 +1,7 @@ 'use client'; +import { CheckCircle, Info, X,XCircle } from 'lucide-react'; import { useEffect, useState } from 'react'; -import { CheckCircle, XCircle, Info, X } from 'lucide-react'; export interface ToastProps { message: string; diff --git a/src/components/TopProgressBar.tsx b/src/components/TopProgressBar.tsx index c351ab1..6afd5c5 100644 --- a/src/components/TopProgressBar.tsx +++ b/src/components/TopProgressBar.tsx @@ -1,8 +1,8 @@ 'use client'; -import { useEffect, useRef } from 'react'; -import { usePathname, useSearchParams, useRouter } from 'next/navigation'; +import { usePathname, useRouter,useSearchParams } from 'next/navigation'; import NProgress from 'nprogress'; +import { useEffect, useRef } from 'react'; // 创建全局钩子来拦截 router let globalRouterRef: any = null; diff --git a/src/components/UserMenu.tsx b/src/components/UserMenu.tsx index 82dcb3c..bfcf3c4 100644 --- a/src/components/UserMenu.tsx +++ b/src/components/UserMenu.tsx @@ -30,7 +30,7 @@ import { X, } from 'lucide-react'; import { useRouter } from 'next/navigation'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; import { getAuthInfoFromBrowserCookie } from '@/lib/auth'; @@ -38,11 +38,11 @@ import { clearAllDanmakuCache } from '@/lib/danmaku/api'; import { CURRENT_VERSION } from '@/lib/version'; import { UpdateStatus } from '@/lib/version_check'; +import { FavoritesPanel } from './FavoritesPanel'; +import { NotificationPanel } from './NotificationPanel'; +import { OfflineDownloadPanel } from './OfflineDownloadPanel'; import { useVersionCheck } from './VersionCheckProvider'; import { VersionPanel } from './VersionPanel'; -import { OfflineDownloadPanel } from './OfflineDownloadPanel'; -import { NotificationPanel } from './NotificationPanel'; -import { FavoritesPanel } from './FavoritesPanel'; interface AuthInfo { username?: string; diff --git a/src/components/VideoCard.tsx b/src/components/VideoCard.tsx index 08ac4ec..5badba6 100644 --- a/src/components/VideoCard.tsx +++ b/src/components/VideoCard.tsx @@ -24,10 +24,10 @@ import { import { processImageUrl } from '@/lib/utils'; import { useLongPress } from '@/hooks/useLongPress'; -import { ImagePlaceholder } from '@/components/ImagePlaceholder'; -import MobileActionSheet from '@/components/MobileActionSheet'; import AIChatPanel from '@/components/AIChatPanel'; import DetailPanel from '@/components/DetailPanel'; +import { ImagePlaceholder } from '@/components/ImagePlaceholder'; +import MobileActionSheet from '@/components/MobileActionSheet'; export interface VideoCardProps { id?: string; diff --git a/src/components/VirtualScrollableRow.tsx b/src/components/VirtualScrollableRow.tsx index d54417d..451911b 100644 --- a/src/components/VirtualScrollableRow.tsx +++ b/src/components/VirtualScrollableRow.tsx @@ -1,7 +1,7 @@ 'use client'; -import { useRef, useEffect, useState } from 'react'; import { ChevronLeft, ChevronRight } from 'lucide-react'; +import { useEffect, useRef, useState } from 'react'; interface VirtualScrollableRowProps { children: React.ReactNode[]; diff --git a/src/components/WatchRoomProvider.tsx b/src/components/WatchRoomProvider.tsx index 175c2ac..ee1e048 100644 --- a/src/components/WatchRoomProvider.tsx +++ b/src/components/WatchRoomProvider.tsx @@ -1,11 +1,14 @@ // WatchRoom 全局状态管理 Provider 'use client'; -import React, { createContext, useContext, useEffect, useState, useCallback } from 'react'; +import React, { createContext, useCallback,useContext, useEffect, useState } from 'react'; + import { useWatchRoom } from '@/hooks/useWatchRoom'; -import type { Room, Member, ChatMessage, WatchRoomConfig } from '@/types/watch-room'; + import Toast, { ToastProps } from '@/components/Toast'; +import type { ChatMessage, Member, Room, WatchRoomConfig } from '@/types/watch-room'; + // Import type from watch-room-socket type WatchRoomSocket = import('@/lib/watch-room-socket').WatchRoomSocket; diff --git a/src/components/watch-room/ChatFloatingWindow.tsx b/src/components/watch-room/ChatFloatingWindow.tsx index 09089b3..3a4f32e 100644 --- a/src/components/watch-room/ChatFloatingWindow.tsx +++ b/src/components/watch-room/ChatFloatingWindow.tsx @@ -1,11 +1,13 @@ // 全局聊天悬浮窗和房间信息按钮 'use client'; -import { useState, useEffect, useRef } from 'react'; -import { MessageCircle, X, Send, Smile, Minimize2, Maximize2, Info, Users, LogOut, XCircle, Mic, MicOff, Volume2, VolumeX, AlertCircle } from 'lucide-react'; -import { useWatchRoomContextSafe } from '@/components/WatchRoomProvider'; +import { AlertCircle,Info, LogOut, Maximize2, MessageCircle, Mic, MicOff, Minimize2, Send, Smile, Users, Volume2, VolumeX, X, XCircle } from 'lucide-react'; +import { useEffect, useRef,useState } from 'react'; + import { useVoiceChat } from '@/hooks/useVoiceChat'; +import { useWatchRoomContextSafe } from '@/components/WatchRoomProvider'; + const EMOJI_LIST = ['😀', '😂', '😍', '🥰', '😎', '🤔', '👍', '👏', '🎉', '❤️', '🔥', '⭐']; export default function ChatFloatingWindow() { diff --git a/src/contexts/DownloadContext.tsx b/src/contexts/DownloadContext.tsx index a1e9bf8..b0bb1ab 100644 --- a/src/contexts/DownloadContext.tsx +++ b/src/contexts/DownloadContext.tsx @@ -1,6 +1,7 @@ 'use client'; -import React, { createContext, useContext, useState, useCallback, useEffect } from 'react'; +import React, { createContext, useCallback, useContext,useState } from 'react'; + import { M3U8Downloader, M3U8DownloadTask } from '@/lib/m3u8-downloader'; interface DownloadContextType { diff --git a/src/hooks/useEnableComments.ts b/src/hooks/useEnableComments.ts index 612443c..dd8b5c8 100644 --- a/src/hooks/useEnableComments.ts +++ b/src/hooks/useEnableComments.ts @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useEffect,useState } from 'react'; interface RuntimeConfig { EnableComments: boolean; diff --git a/src/hooks/useLiveSync.ts b/src/hooks/useLiveSync.ts index 3885628..0df3db5 100644 --- a/src/hooks/useLiveSync.ts +++ b/src/hooks/useLiveSync.ts @@ -1,8 +1,10 @@ // React Hook for Live Page Synchronization 'use client'; -import { useEffect, useRef, useCallback } from 'react'; +import { useCallback,useEffect, useRef } from 'react'; + import { useWatchRoomContextSafe } from '@/components/WatchRoomProvider'; + import type { LiveState } from '@/types/watch-room'; interface UseLiveSyncOptions { diff --git a/src/hooks/usePlaySync.ts b/src/hooks/usePlaySync.ts index df3f0ad..c1844ee 100644 --- a/src/hooks/usePlaySync.ts +++ b/src/hooks/usePlaySync.ts @@ -1,9 +1,11 @@ // React Hook for Play Page Synchronization 'use client'; -import { useEffect, useRef, useCallback } from 'react'; import { useRouter } from 'next/navigation'; +import { useCallback,useEffect, useRef } from 'react'; + import { useWatchRoomContextSafe } from '@/components/WatchRoomProvider'; + import type { PlayState } from '@/types/watch-room'; interface UsePlaySyncOptions { diff --git a/src/hooks/useVoiceChat.ts b/src/hooks/useVoiceChat.ts index b1c5f41..14ab0b9 100644 --- a/src/hooks/useVoiceChat.ts +++ b/src/hooks/useVoiceChat.ts @@ -1,8 +1,10 @@ // React Hook for Voice Chat in Watch Room 'use client'; -import { useEffect, useRef, useCallback, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; + import type { WatchRoomSocket } from '@/lib/watch-room-socket'; + import type { Member } from '@/types/watch-room'; interface UseVoiceChatOptions { @@ -520,7 +522,7 @@ export function useVoiceChat({ }, [switchToServerRelay]); // 播放服务器中转的音频 - 使用Web Audio API播放PCM数据 - const playServerRelayAudio = useCallback(async (userId: string, audioData: number[], sampleRate: number = 16000) => { + const playServerRelayAudio = useCallback(async (userId: string, audioData: number[], sampleRate = 16000) => { if (!isSpeakerEnabled) return; try { diff --git a/src/hooks/useWatchRoom.ts b/src/hooks/useWatchRoom.ts index 34e7b6f..0f19c75 100644 --- a/src/hooks/useWatchRoom.ts +++ b/src/hooks/useWatchRoom.ts @@ -1,16 +1,18 @@ // React Hook for Watch Room 'use client'; -import { useEffect, useState, useCallback, useRef } from 'react'; -import { watchRoomSocketManager, type WatchRoomSocket } from '@/lib/watch-room-socket'; +import { useCallback, useEffect, useRef,useState } from 'react'; + +import { type WatchRoomSocket,watchRoomSocketManager } from '@/lib/watch-room-socket'; + import type { - Room, + ChatMessage, + LiveState, Member, PlayState, - LiveState, - ChatMessage, - WatchRoomConfig, + Room, StoredRoomInfo, + WatchRoomConfig, } from '@/types/watch-room'; const STORAGE_KEY = 'watch_room_info'; diff --git a/src/lib/aes-decryptor.ts b/src/lib/aes-decryptor.ts index c885fcd..44e4e8f 100644 --- a/src/lib/aes-decryptor.ts +++ b/src/lib/aes-decryptor.ts @@ -139,7 +139,7 @@ export class AESDecryptor { const invSubMix2 = invSubMix[2]; const invSubMix3 = invSubMix[3]; - let prev: number = 0; + let prev = 0; let t: number; for (ksRow = 0; ksRow < ksRows; ksRow++) { diff --git a/src/lib/ai-orchestrator.ts b/src/lib/ai-orchestrator.ts index af77070..9238c9c 100644 --- a/src/lib/ai-orchestrator.ts +++ b/src/lib/ai-orchestrator.ts @@ -5,7 +5,6 @@ */ import { fetchDoubanData as fetchDoubanAPI } from '@/lib/douban'; -import { searchTMDB, getTVSeasons } from '@/lib/tmdb.search'; import { getNextApiKey } from '@/lib/tmdb.client'; export interface VideoContext { diff --git a/src/lib/danmaku/api.ts b/src/lib/danmaku/api.ts index 6d1a8ee..51cc001 100644 --- a/src/lib/danmaku/api.ts +++ b/src/lib/danmaku/api.ts @@ -1,6 +1,14 @@ // 弹幕 API 服务封装(通过本地代理转发) +import { + clearAllDanmakuCache, + clearDanmakuCache, + clearExpiredDanmakuCache, + generateCacheKey, + getDanmakuCacheStats, + getDanmakuFromCache, + saveDanmakuToCache, +} from './cache'; import type { - DanmakuAnime, DanmakuComment, DanmakuCommentsResponse, DanmakuEpisodesResponse, @@ -10,16 +18,6 @@ import type { DanmakuSettings, } from './types'; -import { - getDanmakuFromCache, - saveDanmakuToCache, - clearExpiredDanmakuCache, - clearAllDanmakuCache, - clearDanmakuCache, - getDanmakuCacheStats, - generateCacheKey, -} from './cache'; - // 初始化弹幕模块(清理过期缓存) let _cacheCleanupInitialized = false; @@ -46,8 +44,8 @@ export { clearAllDanmakuCache, clearDanmakuCache, clearExpiredDanmakuCache, - getDanmakuCacheStats, generateCacheKey, + getDanmakuCacheStats, getDanmakuFromCache, }; diff --git a/src/lib/db.client.ts b/src/lib/db.client.ts index cf78f10..d6cdc13 100644 --- a/src/lib/db.client.ts +++ b/src/lib/db.client.ts @@ -15,7 +15,7 @@ */ import { getAuthInfoFromBrowserCookie } from './auth'; -import { SkipConfig, DanmakuFilterConfig, EpisodeFilterConfig } from './types'; +import { DanmakuFilterConfig, EpisodeFilterConfig,SkipConfig } from './types'; // 全局错误触发函数 function triggerGlobalError(message: string) { diff --git a/src/lib/db.ts b/src/lib/db.ts index ba1a38b..b735e29 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -3,7 +3,7 @@ import { AdminConfig } from './admin.types'; import { KvrocksStorage } from './kvrocks.db'; import { RedisStorage } from './redis.db'; -import { Favorite, IStorage, PlayRecord, SkipConfig, DanmakuFilterConfig } from './types'; +import { DanmakuFilterConfig,Favorite, IStorage, PlayRecord, SkipConfig } from './types'; import { UpstashRedisStorage } from './upstash.db'; // storage type 常量: 'localstorage' | 'redis' | 'upstash',默认 'localstorage' @@ -224,8 +224,8 @@ export class DbManager { } async getUserListV2( - offset: number = 0, - limit: number = 20, + offset = 0, + limit = 20, ownerUsername?: string ): Promise<{ users: Array<{ diff --git a/src/lib/email.service.ts b/src/lib/email.service.ts index ec1919e..7ca6ef2 100644 --- a/src/lib/email.service.ts +++ b/src/lib/email.service.ts @@ -1,4 +1,5 @@ import nodemailer from 'nodemailer'; + import type { AdminConfig } from './admin.types'; export interface EmailOptions { diff --git a/src/lib/emby-cache.ts b/src/lib/emby-cache.ts index 2ce56ba..baf6161 100644 --- a/src/lib/emby-cache.ts +++ b/src/lib/emby-cache.ts @@ -72,7 +72,7 @@ export function clearEmbyCache(): { cleared: number } { /** * 获取缓存的 Emby 媒体库列表 */ -export function getCachedEmbyViews(embyKey: string = 'default'): any | null { +export function getCachedEmbyViews(embyKey = 'default'): any | null { const cacheKey = `${EMBY_VIEWS_CACHE_KEY}:${embyKey}`; const entry = EMBY_CACHE.get(cacheKey); if (!entry) return null; @@ -89,7 +89,7 @@ export function getCachedEmbyViews(embyKey: string = 'default'): any | null { /** * 设置缓存的 Emby 媒体库列表 */ -export function setCachedEmbyViews(embyKey: string = 'default', data: any): void { +export function setCachedEmbyViews(embyKey = 'default', data: any): void { const now = Date.now(); const cacheKey = `${EMBY_VIEWS_CACHE_KEY}:${embyKey}`; EMBY_CACHE.set(cacheKey, { diff --git a/src/lib/emby-manager.ts b/src/lib/emby-manager.ts index 548a140..311efea 100644 --- a/src/lib/emby-manager.ts +++ b/src/lib/emby-manager.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { EmbyClient } from './emby.client'; -import { getConfig } from './config'; import { AdminConfig } from './admin.types'; +import { getConfig } from './config'; +import { EmbyClient } from './emby.client'; interface EmbySourceConfig { key: string; diff --git a/src/lib/emby.client.ts b/src/lib/emby.client.ts index dbde767..9adc31a 100644 --- a/src/lib/emby.client.ts +++ b/src/lib/emby.client.ts @@ -165,19 +165,15 @@ export class EmbyClient { const url = `${this.serverUrl}/Users/Me`; const headers = this.getHeaders(); - try { - const response = await fetch(url, { headers }); + const response = await fetch(url, { headers }); - if (!response.ok) { - const errorText = await response.text(); - throw new Error(`获取当前用户信息失败 (${response.status}): ${errorText}`); - } - - const data = await response.json(); - return data; - } catch (error) { - throw error; + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`获取当前用户信息失败 (${response.status}): ${errorText}`); } + + const data = await response.json(); + return data; } async getUserViews(): Promise { @@ -467,7 +463,7 @@ export class EmbyClient { } } - async getStreamUrl(itemId: string, direct: boolean = true, forceDirectUrl: boolean = false): Promise { + async getStreamUrl(itemId: string, direct = true, forceDirectUrl = false): Promise { const token = this.apiKey || this.authToken; // 如果启用了代理播放且不是强制获取直接URL,返回代理URL diff --git a/src/lib/kvrocks.db.ts b/src/lib/kvrocks.db.ts index 8c07b9c..29acd4e 100644 --- a/src/lib/kvrocks.db.ts +++ b/src/lib/kvrocks.db.ts @@ -1,7 +1,7 @@ /* eslint-disable no-console, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ -import { BaseRedisStorage, createRedisClient, createRetryWrapper } from './redis-base.db'; import { StandardRedisAdapter } from './redis-adapter'; +import { BaseRedisStorage, createRedisClient, createRetryWrapper } from './redis-base.db'; export class KvrocksStorage extends BaseRedisStorage { constructor() { diff --git a/src/lib/m3u8-downloader.ts b/src/lib/m3u8-downloader.ts index e5d8f37..3e792aa 100644 --- a/src/lib/m3u8-downloader.ts +++ b/src/lib/m3u8-downloader.ts @@ -3,10 +3,11 @@ * 基于 M3U8Download 项目改造为 TypeScript 版本 */ -import { AESDecryptor } from './aes-decryptor'; // @ts-ignore - mux.js 没有类型定义 import * as muxjs from 'mux.js'; +import { AESDecryptor } from './aes-decryptor'; + export interface M3U8DownloadTask { id: string; url: string; diff --git a/src/lib/offline-downloader.ts b/src/lib/offline-downloader.ts index c77d3f8..dd3b3e0 100644 --- a/src/lib/offline-downloader.ts +++ b/src/lib/offline-downloader.ts @@ -4,11 +4,11 @@ */ import * as fs from 'fs'; -import * as path from 'path'; -import * as https from 'https'; import * as http from 'http'; -import * as zlib from 'zlib'; +import * as https from 'https'; +import * as path from 'path'; import { URL } from 'url'; +import * as zlib from 'zlib'; export interface OfflineDownloadTask { id: string; @@ -467,12 +467,16 @@ export class OfflineDownloader { }); fileStream.on('error', (err) => { - fs.unlink(savePath, () => {}); + fs.unlink(savePath, () => { + // Ignore unlink errors + }); reject(err); }); stream.on('error', (err) => { - fs.unlink(savePath, () => {}); + fs.unlink(savePath, () => { + // Ignore unlink errors + }); reject(err); }); }); diff --git a/src/lib/openlist-refresh.ts b/src/lib/openlist-refresh.ts index 065fde5..9409b39 100644 --- a/src/lib/openlist-refresh.ts +++ b/src/lib/openlist-refresh.ts @@ -1,11 +1,13 @@ /* eslint-disable @typescript-eslint/no-explicit-any, no-console */ +import parseTorrentName from 'parse-torrent-name'; + +import type { AdminConfig } from '@/lib/admin.types'; import { getConfig } from '@/lib/config'; import { generateFolderKey } from '@/lib/crypto'; import { db } from '@/lib/db'; import { OpenListClient } from '@/lib/openlist.client'; import { - getCachedMetaInfo, invalidateMetaInfoCache, MetaInfo, setCachedMetaInfo, @@ -18,9 +20,7 @@ import { updateScanTaskProgress, } from '@/lib/scan-task'; import { parseSeasonFromTitle } from '@/lib/season-parser'; -import { searchTMDB, getTVSeasonDetails } from '@/lib/tmdb.search'; -import parseTorrentName from 'parse-torrent-name'; -import type { AdminConfig } from '@/lib/admin.types'; +import { getTVSeasonDetails,searchTMDB } from '@/lib/tmdb.search'; /** * 获取根目录列表(兼容新旧配置) @@ -58,7 +58,7 @@ async function migrateToMultiRoot(openListConfig: NonNullable { +export async function startOpenListRefresh(clearMetaInfo = false): Promise<{ taskId: string }> { const config = await getConfig(); const openListConfig = config.OpenListConfig; diff --git a/src/lib/openlist.client.ts b/src/lib/openlist.client.ts index 612c9f8..1b0f448 100644 --- a/src/lib/openlist.client.ts +++ b/src/lib/openlist.client.ts @@ -33,7 +33,7 @@ export interface OpenListGetResponse { } export class OpenListClient { - private token: string = ''; + private token = ''; constructor( private baseURL: string, diff --git a/src/lib/redis-adapter.ts b/src/lib/redis-adapter.ts index 96fbdc5..50ccdb8 100644 --- a/src/lib/redis-adapter.ts +++ b/src/lib/redis-adapter.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { RedisClientType } from 'redis'; import { Redis } from '@upstash/redis'; +import { RedisClientType } from 'redis'; /** * 统一的 Redis 适配器接口 diff --git a/src/lib/redis-base.db.ts b/src/lib/redis-base.db.ts index e03b14c..d702c2b 100644 --- a/src/lib/redis-base.db.ts +++ b/src/lib/redis-base.db.ts @@ -796,8 +796,8 @@ export abstract class BaseRedisStorage implements IStorage { // 获取用户列表(分页,新版本) async getUserListV2( - offset: number = 0, - limit: number = 20, + offset = 0, + limit = 20, ownerUsername?: string ): Promise<{ users: Array<{ diff --git a/src/lib/redis.db.ts b/src/lib/redis.db.ts index 1876743..83fab7c 100644 --- a/src/lib/redis.db.ts +++ b/src/lib/redis.db.ts @@ -1,7 +1,7 @@ /* eslint-disable no-console, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ -import { BaseRedisStorage, createRedisClient, createRetryWrapper } from './redis-base.db'; import { StandardRedisAdapter } from './redis-adapter'; +import { BaseRedisStorage, createRedisClient, createRetryWrapper } from './redis-base.db'; export class RedisStorage extends BaseRedisStorage { constructor() { diff --git a/src/lib/tmdb.client.ts b/src/lib/tmdb.client.ts index e49cb10..d000c4d 100644 --- a/src/lib/tmdb.client.ts +++ b/src/lib/tmdb.client.ts @@ -85,8 +85,8 @@ interface TMDBTVAiringTodayResponse { */ export async function getTMDBUpcomingMovies( apiKey: string, - page: number = 1, - region: string = 'CN', + page = 1, + region = 'CN', proxy?: string, reverseProxyBaseUrl?: string ): Promise<{ code: number; list: TMDBMovie[] }> { @@ -140,7 +140,7 @@ export async function getTMDBUpcomingMovies( */ export async function getTMDBUpcomingTVShows( apiKey: string, - page: number = 1, + page = 1, proxy?: string, reverseProxyBaseUrl?: string ): Promise<{ code: number; list: TMDBTVShow[] }> { @@ -400,7 +400,7 @@ export async function getTMDBTrendingContent( */ export function getTMDBImageUrl( path: string | null, - size: string = 'w500' + size = 'w500' ): string { if (!path) return ''; const baseUrl = typeof window !== 'undefined' @@ -450,7 +450,7 @@ export const TMDB_GENRES: Record = { * @param limit - 最多返回几个类型,默认2个 * @returns 类型名称数组 */ -export function getGenreNames(genreIds: number[] = [], limit: number = 2): string[] { +export function getGenreNames(genreIds: number[] = [], limit = 2): string[] { return genreIds .map(id => TMDB_GENRES[id]) .filter(Boolean) diff --git a/src/lib/tmdb.search.ts b/src/lib/tmdb.search.ts index bcfd40f..63e0b6f 100644 --- a/src/lib/tmdb.search.ts +++ b/src/lib/tmdb.search.ts @@ -2,6 +2,7 @@ import { HttpsProxyAgent } from 'https-proxy-agent'; import nodeFetch from 'node-fetch'; + import { getNextApiKey } from './tmdb.client'; // TMDB API 默认 Base URL(不包含 /3/,由程序拼接) @@ -226,7 +227,7 @@ export async function getTVSeasonDetails( */ export function getTMDBImageUrl( path: string | null, - size: string = 'w500' + size = 'w500' ): string { if (!path) return ''; diff --git a/src/lib/upstash.db.ts b/src/lib/upstash.db.ts index 163b312..8ff6920 100644 --- a/src/lib/upstash.db.ts +++ b/src/lib/upstash.db.ts @@ -2,8 +2,8 @@ import { Redis } from '@upstash/redis'; -import { BaseRedisStorage } from './redis-base.db'; import { UpstashRedisAdapter } from './redis-adapter'; +import { BaseRedisStorage } from './redis-base.db'; // 添加Upstash Redis操作重试包装器 async function withRetry( diff --git a/src/lib/utils.ts b/src/lib/utils.ts index b43c9cc..ccda257 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any,no-console */ +import bs58 from 'bs58'; import he from 'he'; import Hls from 'hls.js'; -import bs58 from 'bs58'; function getDoubanImageProxyConfig(): { proxyType: @@ -155,7 +155,7 @@ export function processVideoUrl(originalUrl: string): string { */ export async function getVideoResolutionFromM3u8( m3u8Url: string, - timeoutMs: number = 4000 + timeoutMs = 4000 ): Promise<{ quality: string; // 如720p、1080p等 loadSpeed: string; // 自动转换为KB/s或MB/s diff --git a/src/lib/video-parser.ts b/src/lib/video-parser.ts index 025de36..daf72dc 100644 --- a/src/lib/video-parser.ts +++ b/src/lib/video-parser.ts @@ -34,13 +34,13 @@ export function parseVideoFileName(fileName: string): ParsedVideoInfo { // S01E01, s01e01, S01E1234, S01E01.5 (支持1-4位数字和小数) - 最具体 { pattern: /[Ss](\d+)[Ee](\d{1,4}(?:\.\d+)?)/, extractSeason: true }, // [01], (01), [01.5], (01.5) (支持小数,但要排除中文括号内容) - 很具体 - { pattern: /[\[\(](\d+(?:\.\d+)?)[\]\)]/ }, + { pattern: /[[(](\d+(?:\.\d+)?)[)\]]/ }, // E01, E1, e01, e1, E01.5 (支持小数) { pattern: /[Ee](\d+(?:\.\d+)?)/ }, // 第01集, 第1集, 第01话, 第1话, 第1.5集 (支持小数) { pattern: /第(\d+(?:\.\d+)?)[集话]/ }, // _01_, -01-, _01.5_, -01.5- (支持小数) - { pattern: /[_\-](\d+(?:\.\d+)?)[_\-]/ }, + { pattern: /[_-](\d+(?:\.\d+)?)[_-]/ }, // 01.mp4, 001.mp4, 01.5.mp4 (纯数字开头,支持小数) - 最不具体 { pattern: /^(\d+(?:\.\d+)?)[^\d.]/ }, ]; diff --git a/src/lib/watch-room-server.ts b/src/lib/watch-room-server.ts index 4a3d728..2d4afad 100644 --- a/src/lib/watch-room-server.ts +++ b/src/lib/watch-room-server.ts @@ -1,14 +1,13 @@ // Socket.IO 观影室服务器逻辑(共享代码) import { Server as SocketIOServer, Socket } from 'socket.io'; + import type { - Room, - Member, - PlayState, - LiveState, ChatMessage, - ServerToClientEvents, ClientToServerEvents, + Member, + Room, RoomMemberInfo, + ServerToClientEvents, } from '@/types/watch-room'; type TypedSocket = Socket; diff --git a/src/lib/watch-room-socket.ts b/src/lib/watch-room-socket.ts index 377dfb3..1949d98 100644 --- a/src/lib/watch-room-socket.ts +++ b/src/lib/watch-room-socket.ts @@ -153,7 +153,7 @@ class WatchRoomSocketManager { }); // 监听心跳响应 - this.socket.on('heartbeat:pong', (data: { timestamp: number }) => { + this.socket.on('heartbeat:pong', (_data: { timestamp: number }) => { this.lastHeartbeatResponse = Date.now(); }); diff --git a/src/lib/xiaoya-metadata.ts b/src/lib/xiaoya-metadata.ts index bab9397..bc3e5f7 100644 --- a/src/lib/xiaoya-metadata.ts +++ b/src/lib/xiaoya-metadata.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { XiaoyaClient } from './xiaoya.client'; -import { parseNFO, NFOMetadata } from './nfo-parser'; +import { NFOMetadata,parseNFO } from './nfo-parser'; import { parseVideoFileName } from './video-parser'; +import { XiaoyaClient } from './xiaoya.client'; export interface XiaoyaMetadata { tmdbId?: number; @@ -208,7 +208,7 @@ export async function getXiaoyaMetadata( const fileName = pathParts[pathParts.length - 1]; const searchQuery = fileName .replace(/\.(mp4|mkv|avi|m3u8|flv|ts)$/i, '') - .replace(/[\[\]()]/g, ' ') + .replace(/[[\]()]/g, ' ') .trim(); // 如果文件名是纯数字(可能带小数点)或者是 SxxExx 格式,跳过文件名搜索,直接使用文件夹名 @@ -240,7 +240,7 @@ export async function getXiaoyaMetadata( // 优先级 4: 实时搜索 TMDb(使用文件夹名) if (tmdbApiKey) { const searchQuery = folderName - .replace(/[\[\](){}]/g, ' ') + .replace(/[[\](){}]/g, ' ') .replace(/\d{4}/g, '') .trim(); diff --git a/src/lib/xiaoya.client.ts b/src/lib/xiaoya.client.ts index 079339c..c32cad7 100644 --- a/src/lib/xiaoya.client.ts +++ b/src/lib/xiaoya.client.ts @@ -16,7 +16,7 @@ export interface XiaoyaListResponse { } export class XiaoyaClient { - private token: string = ''; + private token = ''; constructor( private baseURL: string,