From 97488c3beec4307a2db48278886f7b3565ccf980 Mon Sep 17 00:00:00 2001 From: mtvpls Date: Sat, 3 Jan 2026 01:41:23 +0800 Subject: [PATCH] =?UTF-8?q?emby=E6=BB=91=E5=8A=A8=E5=88=86=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/emby/list/route.ts | 2 - src/app/private-library/page.tsx | 181 ++++++++++++++++++++----------- 2 files changed, 120 insertions(+), 63 deletions(-) diff --git a/src/app/api/emby/list/route.ts b/src/app/api/emby/list/route.ts index 9c26a47..c0df1ba 100644 --- a/src/app/api/emby/list/route.ts +++ b/src/app/api/emby/list/route.ts @@ -16,8 +16,6 @@ export async function GET(request: NextRequest) { const config = await getConfig(); const embyConfig = config.EmbyConfig; - console.log('[Emby List] EmbyConfig:', JSON.stringify(embyConfig, null, 2)); - if (!embyConfig?.Enabled || !embyConfig.ServerURL) { return NextResponse.json({ error: 'Emby 未配置或未启用', diff --git a/src/app/private-library/page.tsx b/src/app/private-library/page.tsx index 746a966..a89a629 100644 --- a/src/app/private-library/page.tsx +++ b/src/app/private-library/page.tsx @@ -3,7 +3,7 @@ 'use client'; import { useRouter } from 'next/navigation'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useRef } from 'react'; import CapsuleSwitch from '@/components/CapsuleSwitch'; import PageLayout from '@/components/PageLayout'; @@ -30,58 +30,127 @@ export default function PrivateLibraryPage() { const [source, setSource] = useState('openlist'); const [videos, setVideos] = useState([]); const [loading, setLoading] = useState(true); + const [loadingMore, setLoadingMore] = useState(false); const [error, setError] = useState(''); const [page, setPage] = useState(1); - const [totalPages, setTotalPages] = useState(1); + const [hasMore, setHasMore] = useState(true); const pageSize = 20; + const observerTarget = useRef(null); + const isFetchingRef = useRef(false); - // 切换源时重置页码 + // 切换源时重置所有状态 useEffect(() => { setPage(1); + setVideos([]); + setHasMore(true); + setError(''); + isFetchingRef.current = false; }, [source]); + // 加载数据的函数 useEffect(() => { + const fetchVideos = async () => { + const isInitial = page === 1; + + // 防止重复请求 + if (isFetchingRef.current) { + return; + } + + isFetchingRef.current = true; + + try { + if (isInitial) { + setLoading(true); + } else { + setLoadingMore(true); + } + setError(''); + + const endpoint = source === 'openlist' + ? `/api/openlist/list?page=${page}&pageSize=${pageSize}` + : `/api/emby/list?page=${page}&pageSize=${pageSize}`; + + const response = await fetch(endpoint); + + if (!response.ok) { + throw new Error('获取视频列表失败'); + } + + const data = await response.json(); + + if (data.error) { + setError(data.error); + if (isInitial) { + setVideos([]); + } + } else { + const newVideos = data.list || []; + + if (isInitial) { + setVideos(newVideos); + } else { + setVideos((prev) => [...prev, ...newVideos]); + } + + // 检查是否还有更多数据 + const currentPage = data.page || page; + const totalPages = data.totalPages || 1; + const hasMoreData = currentPage < totalPages; + setHasMore(hasMoreData); + } + } catch (err) { + console.error('获取视频列表失败:', err); + setError('获取视频列表失败'); + if (isInitial) { + setVideos([]); + } + } finally { + if (isInitial) { + setLoading(false); + } else { + setLoadingMore(false); + } + isFetchingRef.current = false; + } + }; + fetchVideos(); - }, [page, source]); - - const fetchVideos = async () => { - try { - setLoading(true); - setError(''); - - const endpoint = source === 'openlist' - ? `/api/openlist/list?page=${page}&pageSize=${pageSize}` - : `/api/emby/list?page=${page}&pageSize=${pageSize}`; - - const response = await fetch(endpoint); - - if (!response.ok) { - throw new Error('获取视频列表失败'); - } - - const data = await response.json(); - - if (data.error) { - setError(data.error); - setVideos([]); - } else { - setVideos(data.list || []); - setTotalPages(data.totalPages || 1); - } - } catch (err) { - console.error('获取视频列表失败:', err); - setError('获取视频列表失败'); - setVideos([]); - } finally { - setLoading(false); - } - }; + }, [source, page]); const handleVideoClick = (video: Video) => { // 跳转到播放页面 router.push(`/play?source=${source}&id=${encodeURIComponent(video.id)}`); }; + // 使用 Intersection Observer 监听滚动 + useEffect(() => { + if (!observerTarget.current) { + return; + } + + const observer = new IntersectionObserver( + (entries) => { + const entry = entries[0]; + + // 当目标元素可见且还有更多数据且没有正在加载时,加载下一页 + if (entry.isIntersecting && hasMore && !loadingMore && !loading && !isFetchingRef.current) { + setPage((prev) => prev + 1); + } + }, + { threshold: 0.1, rootMargin: '100px' } + ); + + const currentTarget = observerTarget.current; + observer.observe(currentTarget); + + return () => { + if (currentTarget) { + observer.unobserve(currentTarget); + } + }; + }, [hasMore, loadingMore, loading, page]); + return (
@@ -152,30 +221,20 @@ export default function PrivateLibraryPage() { ))}
- {/* 分页 */} - {totalPages > 1 && ( -
- - - - 第 {page} / {totalPages} 页 - - - -
- )} + {/* 滚动加载指示器 - 始终渲染以便 observer 可以监听 */} +
+ {loadingMore && ( +
+
+ 加载中... +
+ )} + {!hasMore && videos.length > 0 && !loadingMore && ( +
+ 已加载全部内容 +
+ )} +
)}