diff --git a/src/app/api/source-detail/route.ts b/src/app/api/source-detail/route.ts index 61ad0a9..ab2e239 100644 --- a/src/app/api/source-detail/route.ts +++ b/src/app/api/source-detail/route.ts @@ -22,6 +22,7 @@ export async function GET(request: NextRequest) { const id = searchParams.get('id'); const sourceCode = searchParams.get('source'); const title = searchParams.get('title'); // 用于搜索的标题 + const fileName = searchParams.get('fileName'); // 小雅源:用户点击的文件名 if (!id || !sourceCode || !title) { return NextResponse.json({ error: '缺少必要参数' }, { status: 400 }); @@ -149,41 +150,52 @@ export async function GET(request: NextRequest) { xiaoyaConfig.Token ); - // 对id进行base58解码得到真实路径 - let decodedPath: string; + // 对id进行base58解码得到目录路径 + let decodedDirPath: string; try { - decodedPath = base58Decode(id); - console.log('[xiaoya] 解码路径:', decodedPath); + decodedDirPath = base58Decode(id); + console.log('[xiaoya] 解码目录路径:', decodedDirPath); } catch (decodeError) { console.error('[xiaoya] Base58解码失败:', decodeError); throw new Error('无效的视频ID'); } // 验证解码后的路径 - if (!decodedPath || decodedPath.trim() === '') { + if (!decodedDirPath || decodedDirPath.trim() === '') { throw new Error('解码后的路径为空'); } - // 获取元数据 + // 如果有fileName参数,拼接完整文件路径 + let clickedFilePath: string | undefined; + if (fileName) { + // 拼接目录路径和文件名 + clickedFilePath = `${decodedDirPath}${decodedDirPath.endsWith('/') ? '' : '/'}${fileName}`; + console.log('[xiaoya] 用户点击的文件路径:', clickedFilePath); + } + + // 获取元数据(使用目录路径或点击的文件路径) + const metadataPath = clickedFilePath || decodedDirPath; const metadata = await getXiaoyaMetadata( client, - decodedPath, // 使用解码后的路径 + metadataPath, config.SiteConfig.TMDBApiKey, config.SiteConfig.TMDBProxy ); - // 获取集数列表 - const episodes = await getXiaoyaEpisodes(client, decodedPath); + // 获取集数列表(使用目录路径或点击的文件路径) + const episodes = await getXiaoyaEpisodes(client, metadataPath); - // 找到用户点击的文件在集数列表中的索引 - const clickedFileIndex = episodes.findIndex(ep => ep.path === decodedPath); - console.log('[xiaoya] 用户点击的文件:', decodedPath); - console.log('[xiaoya] 文件在集数列表中的索引:', clickedFileIndex); + // 如果有点击的文件路径,找到对应的集数索引 + let clickedFileIndex = -1; + if (clickedFilePath) { + clickedFileIndex = episodes.findIndex(ep => ep.path === clickedFilePath); + console.log('[xiaoya] 文件在集数列表中的索引:', clickedFileIndex); + } const result = { source: 'xiaoya', source_name: '小雅', - id: id, // 保持编码后的id + id: id, // 保持编码后的目录id title: metadata.title, poster: metadata.poster || '', year: metadata.year || '', diff --git a/src/app/play/page.tsx b/src/app/play/page.tsx index fd542fe..ed06198 100644 --- a/src/app/play/page.tsx +++ b/src/app/play/page.tsx @@ -475,6 +475,7 @@ function PlayPageClient() { // 当前源和ID - source 直接存储完整格式(如 'emby_wumei' 或 'emby') const [currentSource, setCurrentSource] = useState(searchParams.get('source') || ''); const [currentId, setCurrentId] = useState(searchParams.get('id') || ''); + const [fileName] = useState(searchParams.get('fileName') || ''); // 小雅源:用户点击的文件名 // 解析 source 参数以获取 embyKey(仅用于 API 调用) const parseSourceForApi = (source: string): { source: string; embyKey?: string } => { @@ -2353,12 +2354,16 @@ function PlayPageClient() { const fetchSourceDetail = async ( source: string, id: string, - title: string + title: string, + fileNameParam?: string ): Promise => { try { - const detailResponse = await fetch( - `/api/source-detail?source=${source}&id=${id}&title=${encodeURIComponent(title)}` - ); + let url = `/api/source-detail?source=${source}&id=${id}&title=${encodeURIComponent(title)}`; + // 如果有fileName参数(小雅源),添加到URL + if (fileNameParam) { + url += `&fileName=${encodeURIComponent(fileNameParam)}`; + } + const detailResponse = await fetch(url); if (!detailResponse.ok) { throw new Error('获取视频详情失败'); } @@ -2515,7 +2520,13 @@ function PlayPageClient() { // 先快速获取当前源的详情 try { // currentSource 已经是完整格式(如 'emby_wumei') - const currentSourceDetail = await fetchSourceDetail(currentSource, currentId, searchTitle || videoTitle); + // 如果是小雅源且有fileName参数,传递给API + const currentSourceDetail = await fetchSourceDetail( + currentSource, + currentId, + searchTitle || videoTitle, + currentSource === 'xiaoya' ? fileName : undefined + ); if (currentSourceDetail.length > 0) { detailData = currentSourceDetail[0]; sourcesInfo = currentSourceDetail; @@ -2666,6 +2677,8 @@ function PlayPageClient() { newUrl.searchParams.set('year', detailData.year); // 保持原有的 title,不更新 newUrl.searchParams.delete('prefer'); + // 删除fileName参数,避免换集后刷新跳回到最初点击的那一集 + newUrl.searchParams.delete('fileName'); window.history.replaceState({}, '', newUrl.toString()); setLoadingStage('ready'); diff --git a/src/app/private-library/page.tsx b/src/app/private-library/page.tsx index 72561c9..190e24a 100644 --- a/src/app/private-library/page.tsx +++ b/src/app/private-library/page.tsx @@ -693,9 +693,12 @@ export default function PrivateLibraryPage() { key={item.path} onClick={() => { if (isVideoFile) { - // 视频文件:直接播放,对path进行base58编码 - const encodedPath = base58Encode(item.path); - router.push(`/play?source=xiaoya&id=${encodeURIComponent(encodedPath)}&title=${encodeURIComponent(title)}`); + // 视频文件:提取父目录作为ID,传递文件名 + const pathParts = item.path.split('/').filter(Boolean); + const parentDir = '/' + pathParts.slice(0, -1).join('/'); + const fileName = pathParts[pathParts.length - 1]; + const encodedDirPath = base58Encode(parentDir); + router.push(`/play?source=xiaoya&id=${encodeURIComponent(encodedDirPath)}&fileName=${encodeURIComponent(fileName)}&title=${encodeURIComponent(title)}`); } else { // 文件夹:进入浏览 setXiaoyaPath(item.path); @@ -795,9 +798,9 @@ export default function PrivateLibraryPage() {