emby支持多源

This commit is contained in:
mtvpls
2026-01-08 00:31:03 +08:00
parent b61db02478
commit 2736c9e845
22 changed files with 1316 additions and 572 deletions
+42 -24
View File
@@ -456,12 +456,20 @@ function PlayPageClient() {
const [doubanCardSubtitle, setDoubanCardSubtitle] = useState<string>('');
const [doubanAka, setDoubanAka] = useState<string[]>([]);
const [doubanYear, setDoubanYear] = useState<string>(''); // 从 pubdate 提取的年份
// 当前源和ID
const [currentSource, setCurrentSource] = useState(
searchParams.get('source') || ''
);
// 当前源和ID - source 直接存储完整格式(如 'emby_wumei' 或 'emby'
const [currentSource, setCurrentSource] = useState(searchParams.get('source') || '');
const [currentId, setCurrentId] = useState(searchParams.get('id') || '');
// 解析 source 参数以获取 embyKey(仅用于 API 调用)
const parseSourceForApi = (source: string): { source: string; embyKey?: string } => {
if (source.startsWith('emby_')) {
const key = source.substring(5);
return { source: 'emby', embyKey: key };
}
return { source };
};
// 搜索所需信息
const [searchTitle] = useState(searchParams.get('stitle') || '');
const [searchType] = useState(searchParams.get('stype') || '');
@@ -501,8 +509,6 @@ function PlayPageClient() {
// 监听 URL 参数变化,当切换到不同视频时重新加载页面
useEffect(() => {
const urlTitle = searchParams.get('title') || '';
const urlSource = searchParams.get('source') || '';
const urlId = searchParams.get('id') || '';
// 只在切换到不同视频时重新加载页面(title变化)
// 换源(source/id变化)由播放器自己处理,不需要刷新页面
@@ -2366,6 +2372,7 @@ function PlayPageClient() {
if (currentSource && currentId) {
// 先快速获取当前源的详情
try {
// currentSource 已经是完整格式(如 'emby_wumei'
const currentSourceDetail = await fetchSourceDetail(currentSource, currentId, searchTitle || videoTitle);
if (currentSourceDetail.length > 0) {
detailData = currentSourceDetail[0];
@@ -2415,8 +2422,9 @@ function PlayPageClient() {
detailData = target;
// 如果是 openlist 或 emby 源且 episodes 为空,需要调用 detail 接口获取完整信息
if ((detailData.source === 'openlist' || detailData.source === 'emby') && (!detailData.episodes || detailData.episodes.length === 0)) {
if ((detailData.source === 'openlist' || detailData.source === 'emby' || detailData.source.startsWith('emby_')) && (!detailData.episodes || detailData.episodes.length === 0)) {
console.log('[Play] OpenList/Emby source has no episodes, fetching detail...');
// currentSource 已经是完整格式
const detailSources = await fetchSourceDetail(currentSource, currentId, searchTitle || videoTitle);
if (detailSources.length > 0) {
detailData = detailSources[0];
@@ -2437,9 +2445,22 @@ function PlayPageClient() {
setLoadingStage('preferring');
setLoadingMessage('⚡ 正在优选最佳播放源...');
// 过滤掉 openlist 和 emby 源,它们不参与测速
const sourcesToTest = sourcesInfo.filter(s => s.source !== 'openlist' && s.source !== 'emby');
const excludedSources = sourcesInfo.filter(s => s.source === 'openlist' || s.source === 'emby');
// 过滤掉 openlist 和所有 emby 源,它们不参与测速
const sourcesToTest = sourcesInfo.filter(s => {
// 检查是否为 openlist
if (s.source === 'openlist') return false;
// 检查是否为 emby 源(包括 emby 和 emby_xxx 格式)
if (s.source === 'emby' || s.source.startsWith('emby_')) return false;
return true;
});
const excludedSources = sourcesInfo.filter(s =>
s.source === 'openlist' ||
s.source === 'emby' ||
s.source.startsWith('emby_')
);
if (sourcesToTest.length > 0) {
detailData = await preferBestSource(sourcesToTest);
@@ -2463,6 +2484,7 @@ function PlayPageClient() {
}
setNeedPrefer(false);
// 直接使用 detailData.source(已经是完整格式)
setCurrentSource(detailData.source);
setCurrentId(detailData.id);
setVideoYear(detailData.year);
@@ -2475,12 +2497,12 @@ function PlayPageClient() {
setCurrentEpisodeIndex(0);
}
// 规范URL参数
// 规范URL参数(不更新title,避免循环刷新)
const newUrl = new URL(window.location.href);
newUrl.searchParams.set('source', detailData.source);
newUrl.searchParams.set('id', detailData.id);
newUrl.searchParams.set('year', detailData.year);
newUrl.searchParams.set('title', detailData.title);
// 保持原有的 title,不更新
newUrl.searchParams.delete('prefer');
window.history.replaceState({}, '', newUrl.toString());
@@ -2545,21 +2567,17 @@ function PlayPageClient() {
// 只在URL参数存在且与当前状态不同时才处理
if (urlSource && urlId && (urlSource !== currentSource || urlId !== currentId)) {
console.log('[PlayPage] Detected source/id change from URL:', {
urlSource,
urlId,
currentSource,
currentId
});
// 检查新的source和id是否在可用源列表中
// 如果 availableSources 还是空的,说明数据还在加载中,不做处理
if (availableSources.length === 0) {
return;
}
const targetSource = availableSources.find(
(source) => source.source === urlSource && source.id === urlId
);
if (targetSource) {
console.log('[PlayPage] Found matching source in available sources, updating...');
// 记录当前播放进度
const currentPlayTime = artPlayerRef.current?.currentTime || 0;
@@ -2567,7 +2585,7 @@ function PlayPageClient() {
const episodeParam = searchParams.get('episode');
const targetEpisode = episodeParam ? parseInt(episodeParam, 10) - 1 : 0;
// 更新视频源信息
// 更新视频源信息urlSource 已经是完整格式)
setCurrentSource(urlSource);
setCurrentId(urlId);
setVideoTitle(targetSource.title);
@@ -2589,7 +2607,6 @@ function PlayPageClient() {
}
}
} else {
console.log('[PlayPage] Source not found in available sources, reloading page...');
// 如果新源不在可用列表中,强制刷新页面重新加载
window.location.reload();
}
@@ -2712,7 +2729,7 @@ function PlayPageClient() {
}
// 如果是 openlist 或 emby 源且 episodes 为空,需要调用 detail 接口获取完整信息
if ((newDetail.source === 'openlist' || newDetail.source === 'emby') && (!newDetail.episodes || newDetail.episodes.length === 0)) {
if ((newDetail.source === 'openlist' || newDetail.source === 'emby' || newDetail.source.startsWith('emby_')) && (!newDetail.episodes || newDetail.episodes.length === 0)) {
try {
const detailResponse = await fetch(`/api/source-detail?source=${newSource}&id=${newId}&title=${encodeURIComponent(newTitle)}`);
if (detailResponse.ok) {
@@ -2768,6 +2785,7 @@ function PlayPageClient() {
setVideoYear(newDetail.year);
setVideoCover(newDetail.poster);
setVideoDoubanId(newDetail.douban_id || 0);
// newSource 已经是完整格式
setCurrentSource(newSource);
setCurrentId(newId);
setDetail(newDetail);