转存播放补充元信息
This commit is contained in:
+142
-39
@@ -572,6 +572,13 @@ function PlayPageClient() {
|
|||||||
|
|
||||||
// 纠错后的描述信息(用于显示,不触发 detail 更新)
|
// 纠错后的描述信息(用于显示,不触发 detail 更新)
|
||||||
const [correctedDesc, setCorrectedDesc] = useState<string>('');
|
const [correctedDesc, setCorrectedDesc] = useState<string>('');
|
||||||
|
const [quarkTempTMDBMeta, setQuarkTempTMDBMeta] = useState<{
|
||||||
|
desc?: string;
|
||||||
|
poster?: string;
|
||||||
|
year?: string;
|
||||||
|
tmdbId?: number;
|
||||||
|
} | null>(null);
|
||||||
|
const [pendingQuarkTempTMDBData, setPendingQuarkTempTMDBData] = useState<any | null>(null);
|
||||||
|
|
||||||
// 当前源和ID - source 直接存储完整格式(如 'emby_wumei' 或 'emby')
|
// 当前源和ID - source 直接存储完整格式(如 'emby_wumei' 或 'emby')
|
||||||
const [currentSource, setCurrentSource] = useState(searchParams.get('source') || '');
|
const [currentSource, setCurrentSource] = useState(searchParams.get('source') || '');
|
||||||
@@ -579,6 +586,11 @@ function PlayPageClient() {
|
|||||||
const [fileName] = useState(searchParams.get('fileName') || ''); // 小雅源:用户点击的文件名
|
const [fileName] = useState(searchParams.get('fileName') || ''); // 小雅源:用户点击的文件名
|
||||||
const isDirectPlay = currentSource === 'directplay';
|
const isDirectPlay = currentSource === 'directplay';
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setQuarkTempTMDBMeta(null);
|
||||||
|
setPendingQuarkTempTMDBData(null);
|
||||||
|
}, [currentSource, currentId]);
|
||||||
|
|
||||||
// 解析 source 参数以获取 embyKey(仅用于 API 调用)
|
// 解析 source 参数以获取 embyKey(仅用于 API 调用)
|
||||||
const parseSourceForApi = (source: string): { source: string; embyKey?: string } => {
|
const parseSourceForApi = (source: string): { source: string; embyKey?: string } => {
|
||||||
if (source.startsWith('emby_')) {
|
if (source.startsWith('emby_')) {
|
||||||
@@ -1187,14 +1199,18 @@ function PlayPageClient() {
|
|||||||
const detCacheAge = Date.now() - detTimestamp;
|
const detCacheAge = Date.now() - detTimestamp;
|
||||||
const detCacheMaxAge = 24 * 60 * 60 * 1000; // 1天
|
const detCacheMaxAge = 24 * 60 * 60 * 1000; // 1天
|
||||||
|
|
||||||
if (detCacheAge < detCacheMaxAge && data && data.backdrop) {
|
if (detCacheAge < detCacheMaxAge && data) {
|
||||||
console.log('使用缓存的TMDB详情数据');
|
if (data.backdrop) {
|
||||||
setTmdbBackdrop(processImageUrl(data.backdrop));
|
setTmdbBackdrop(processImageUrl(data.backdrop));
|
||||||
|
} else {
|
||||||
|
setTmdbBackdrop(null);
|
||||||
|
}
|
||||||
|
|
||||||
// 如果没有豆瓣ID,使用TMDb数据补充
|
// 如果没有豆瓣ID,使用TMDb数据补充
|
||||||
if (!videoDoubanId || videoDoubanId === 0) {
|
if (!videoDoubanId || videoDoubanId === 0) {
|
||||||
populateDoubanFieldsFromTMDB(data);
|
populateDoubanFieldsFromTMDB(data);
|
||||||
}
|
}
|
||||||
|
populatePlayMetadataFromTMDB(data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -1215,7 +1231,6 @@ function PlayPageClient() {
|
|||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.log('获取TMDB详情失败');
|
|
||||||
setTmdbBackdrop(null);
|
setTmdbBackdrop(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1224,45 +1239,94 @@ function PlayPageClient() {
|
|||||||
|
|
||||||
if (result.backdrop) {
|
if (result.backdrop) {
|
||||||
setTmdbBackdrop(processImageUrl(result.backdrop));
|
setTmdbBackdrop(processImageUrl(result.backdrop));
|
||||||
|
|
||||||
// 如果没有豆瓣ID,使用TMDb数据补充
|
|
||||||
if (!videoDoubanId || videoDoubanId === 0) {
|
|
||||||
populateDoubanFieldsFromTMDB(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存title到tmdbId的映射到localStorage(1个月)
|
|
||||||
if (result.tmdbId) {
|
|
||||||
try {
|
|
||||||
localStorage.setItem(
|
|
||||||
mappingCacheKey,
|
|
||||||
JSON.stringify({
|
|
||||||
tmdbId: result.tmdbId,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// 保存TMDB详情数据到localStorage(1天)
|
|
||||||
const detailsCacheKey = `tmdb_details_${result.tmdbId}`;
|
|
||||||
localStorage.setItem(
|
|
||||||
detailsCacheKey,
|
|
||||||
JSON.stringify({
|
|
||||||
data: result,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('保存缓存失败:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
setTmdbBackdrop(null);
|
setTmdbBackdrop(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果没有豆瓣ID,使用TMDb数据补充
|
||||||
|
if (!videoDoubanId || videoDoubanId === 0) {
|
||||||
|
populateDoubanFieldsFromTMDB(result);
|
||||||
|
}
|
||||||
|
populatePlayMetadataFromTMDB(result);
|
||||||
|
|
||||||
|
// 保存title到tmdbId的映射到localStorage(1个月)
|
||||||
|
if (result.tmdbId) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(
|
||||||
|
mappingCacheKey,
|
||||||
|
JSON.stringify({
|
||||||
|
tmdbId: result.tmdbId,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// 保存TMDB详情数据到localStorage(1天)
|
||||||
|
const detailsCacheKey = `tmdb_details_${result.tmdbId}`;
|
||||||
|
localStorage.setItem(
|
||||||
|
detailsCacheKey,
|
||||||
|
JSON.stringify({
|
||||||
|
data: result,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('保存缓存失败:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取TMDB背景图失败:', error);
|
console.error('获取TMDB背景图失败:', error);
|
||||||
setTmdbBackdrop(null);
|
setTmdbBackdrop(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const populatePlayMetadataFromTMDB = (tmdbData: any) => {
|
||||||
|
const currentDetail = detailRef.current;
|
||||||
|
if (!currentDetail || currentDetail.source !== 'quark-temp') {
|
||||||
|
setPendingQuarkTempTMDBData(tmdbData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tmdbYear = tmdbData.releaseDate?.split('-')[0] || '';
|
||||||
|
const shouldReplaceDesc = !currentDetail.desc || currentDetail.desc.startsWith('临时播放目录:');
|
||||||
|
|
||||||
|
const resolvedTmdbId = typeof tmdbData.tmdbId === 'string'
|
||||||
|
? Number(String(tmdbData.tmdbId).split(':')[1] || 0)
|
||||||
|
: tmdbData.tmdbId;
|
||||||
|
|
||||||
|
setQuarkTempTMDBMeta({
|
||||||
|
desc: shouldReplaceDesc ? (tmdbData.overview || currentDetail.desc) : currentDetail.desc,
|
||||||
|
poster: currentDetail.poster || tmdbData.poster || '',
|
||||||
|
year: currentDetail.year || tmdbYear,
|
||||||
|
tmdbId: currentDetail.tmdb_id || resolvedTmdbId,
|
||||||
|
});
|
||||||
|
|
||||||
|
setDetail((prev) => {
|
||||||
|
if (!prev || prev.source !== 'quark-temp') {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
poster: prev.poster || tmdbData.poster || '',
|
||||||
|
year: prev.year || tmdbYear,
|
||||||
|
desc: shouldReplaceDesc ? (tmdbData.overview || prev.desc) : prev.desc,
|
||||||
|
tmdb_id: prev.tmdb_id || resolvedTmdbId,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tmdbData.overview && (!correctedDesc || currentDetail.desc?.startsWith('临时播放目录:'))) {
|
||||||
|
setCorrectedDesc(tmdbData.overview);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmdbData.poster && !currentDetail.poster) {
|
||||||
|
setVideoCover(processImageUrl(tmdbData.poster));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmdbYear && !currentDetail.year) {
|
||||||
|
setVideoYear(tmdbYear);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 辅助函数:使用TMDb数据填充豆瓣字段
|
// 辅助函数:使用TMDb数据填充豆瓣字段
|
||||||
const populateDoubanFieldsFromTMDB = (tmdbData: any) => {
|
const populateDoubanFieldsFromTMDB = (tmdbData: any) => {
|
||||||
// 设置评分
|
// 设置评分
|
||||||
@@ -1296,6 +1360,45 @@ function PlayPageClient() {
|
|||||||
fetchTMDBBackdrop();
|
fetchTMDBBackdrop();
|
||||||
}, [videoTitle, videoDoubanId, isDirectPlay]);
|
}, [videoTitle, videoDoubanId, isDirectPlay]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
pendingQuarkTempTMDBData &&
|
||||||
|
detail?.source === 'quark-temp'
|
||||||
|
) {
|
||||||
|
const pending = pendingQuarkTempTMDBData;
|
||||||
|
setPendingQuarkTempTMDBData(null);
|
||||||
|
const tmdbYear = pending.releaseDate?.split('-')[0] || '';
|
||||||
|
const shouldReplaceDesc = !detail.desc || detail.desc.startsWith('临时播放目录:');
|
||||||
|
const resolvedTmdbId = typeof pending.tmdbId === 'string'
|
||||||
|
? Number(String(pending.tmdbId).split(':')[1] || 0)
|
||||||
|
: pending.tmdbId;
|
||||||
|
|
||||||
|
setQuarkTempTMDBMeta({
|
||||||
|
desc: shouldReplaceDesc ? (pending.overview || detail.desc) : detail.desc,
|
||||||
|
poster: detail.poster || pending.poster || '',
|
||||||
|
year: detail.year || tmdbYear,
|
||||||
|
tmdbId: detail.tmdb_id || resolvedTmdbId,
|
||||||
|
});
|
||||||
|
|
||||||
|
setDetail((prev) => prev && prev.source === 'quark-temp' ? {
|
||||||
|
...prev,
|
||||||
|
poster: prev.poster || pending.poster || '',
|
||||||
|
year: prev.year || tmdbYear,
|
||||||
|
desc: shouldReplaceDesc ? (pending.overview || prev.desc) : prev.desc,
|
||||||
|
tmdb_id: prev.tmdb_id || resolvedTmdbId,
|
||||||
|
} : prev);
|
||||||
|
|
||||||
|
if (pending.poster && !detail.poster) {
|
||||||
|
setVideoCover(processImageUrl(pending.poster));
|
||||||
|
}
|
||||||
|
if (tmdbYear && !detail.year) {
|
||||||
|
setVideoYear(tmdbYear);
|
||||||
|
}
|
||||||
|
if (pending.overview) {
|
||||||
|
setCorrectedDesc(pending.overview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [pendingQuarkTempTMDBData, detail]);
|
||||||
|
|
||||||
// 视频播放地址
|
// 视频播放地址
|
||||||
const [videoUrl, setVideoUrl] = useState('');
|
const [videoUrl, setVideoUrl] = useState('');
|
||||||
@@ -8872,12 +8975,12 @@ function PlayPageClient() {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{/* 优先使用 doubanYear,如果没有则使用 detail.year 或 videoYear */}
|
{/* 优先使用 doubanYear,如果没有则使用 detail.year 或 videoYear */}
|
||||||
{(doubanYear || detail?.year || videoYear) && (
|
{(doubanYear || quarkTempTMDBMeta?.year || detail?.year || videoYear) && (
|
||||||
<span>{doubanYear || detail?.year || videoYear}</span>
|
<span>{doubanYear || quarkTempTMDBMeta?.year || detail?.year || videoYear}</span>
|
||||||
)}
|
)}
|
||||||
{detail?.source_name && (
|
{detail?.source_name && (
|
||||||
<span
|
<span
|
||||||
className={`relative group cursor-pointer border px-2 py-[1px] rounded ${detail.source === 'xiaoya' ? 'border-blue-500' : detail.source === 'openlist' || detail.source === 'emby' || detail.source?.startsWith('emby_') ? 'border-yellow-500' : 'border-gray-500/60'
|
className={`relative group cursor-pointer border px-2 py-[1px] rounded ${detail.source === 'xiaoya' ? 'border-blue-500' : detail.source === 'quark-temp' ? 'border-purple-500' : detail.source === 'openlist' || detail.source === 'emby' || detail.source?.startsWith('emby_') ? 'border-yellow-500' : 'border-gray-500/60'
|
||||||
}`}
|
}`}
|
||||||
onClick={fetchCurrentSourceVideoInfo}
|
onClick={fetchCurrentSourceVideoInfo}
|
||||||
>
|
>
|
||||||
@@ -8897,7 +9000,7 @@ function PlayPageClient() {
|
|||||||
{detail?.type_name && <span>{detail.type_name}</span>}
|
{detail?.type_name && <span>{detail.type_name}</span>}
|
||||||
</div>
|
</div>
|
||||||
{/* 剧情简介 */}
|
{/* 剧情简介 */}
|
||||||
{(doubanCardSubtitle || correctedDesc || detail?.desc) && (
|
{(doubanCardSubtitle || quarkTempTMDBMeta?.desc || correctedDesc || detail?.desc) && (
|
||||||
<div
|
<div
|
||||||
className={`mt-0 text-base leading-relaxed opacity-90 overflow-y-auto pr-2 flex-1 min-h-0 scrollbar-hide ${tmdbBackdrop ? 'text-white' : ''}`}
|
className={`mt-0 text-base leading-relaxed opacity-90 overflow-y-auto pr-2 flex-1 min-h-0 scrollbar-hide ${tmdbBackdrop ? 'text-white' : ''}`}
|
||||||
style={{ whiteSpace: 'pre-line' }}
|
style={{ whiteSpace: 'pre-line' }}
|
||||||
@@ -8908,7 +9011,7 @@ function PlayPageClient() {
|
|||||||
{doubanCardSubtitle}
|
{doubanCardSubtitle}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{correctedDesc || detail?.desc}
|
{quarkTempTMDBMeta?.desc || correctedDesc || detail?.desc}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -940,7 +940,7 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
|
|||||||
{/* 源名称和集数信息 - 垂直居中 */}
|
{/* 源名称和集数信息 - 垂直居中 */}
|
||||||
<div className='flex items-center justify-between'>
|
<div className='flex items-center justify-between'>
|
||||||
<span className={`text-xs px-2 py-1 border rounded text-gray-700 dark:text-gray-300 ${
|
<span className={`text-xs px-2 py-1 border rounded text-gray-700 dark:text-gray-300 ${
|
||||||
source.source === 'xiaoya' ? 'border-blue-500' : source.source === 'openlist' || source.source === 'emby' || source.source?.startsWith('emby_')
|
source.source === 'xiaoya' ? 'border-blue-500' : source.source === 'quark-temp' ? 'border-purple-500' : source.source === 'openlist' || source.source === 'emby' || source.source?.startsWith('emby_')
|
||||||
? 'border-yellow-500'
|
? 'border-yellow-500'
|
||||||
: 'border-gray-500/60'
|
: 'border-gray-500/60'
|
||||||
}`}>
|
}`}>
|
||||||
|
|||||||
@@ -1043,7 +1043,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={`inline-block border rounded px-1 py-0.5 text-[8px] text-white/90 bg-black/60 ${
|
className={`inline-block border rounded px-1 py-0.5 text-[8px] text-white/90 bg-black/60 ${
|
||||||
actualSource === 'xiaoya' ? 'border-blue-500' : actualSource === 'openlist' || actualSource === 'emby' || actualSource?.startsWith('emby_') ? 'border-yellow-500' : origin === 'live' ? 'border-red-500' : 'border-white/60'
|
actualSource === 'xiaoya' ? 'border-blue-500' : actualSource === 'quark-temp' ? 'border-purple-500' : actualSource === 'openlist' || actualSource === 'emby' || actualSource?.startsWith('emby_') ? 'border-yellow-500' : origin === 'live' ? 'border-red-500' : 'border-white/60'
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
WebkitUserSelect: 'none',
|
WebkitUserSelect: 'none',
|
||||||
@@ -1367,7 +1367,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
|||||||
{config.showSourceName && source_name && !cmsData && (
|
{config.showSourceName && source_name && !cmsData && (
|
||||||
<span
|
<span
|
||||||
className={`inline-block border rounded px-1 py-0.5 text-[8px] text-white/90 bg-black/30 backdrop-blur-sm ${
|
className={`inline-block border rounded px-1 py-0.5 text-[8px] text-white/90 bg-black/30 backdrop-blur-sm ${
|
||||||
actualSource === 'xiaoya' ? 'border-blue-500' : actualSource === 'openlist' || actualSource === 'emby' || actualSource?.startsWith('emby_') ? 'border-yellow-500' : 'border-white/60'
|
actualSource === 'xiaoya' ? 'border-blue-500' : actualSource === 'quark-temp' ? 'border-purple-500' : actualSource === 'openlist' || actualSource === 'emby' || actualSource?.startsWith('emby_') ? 'border-yellow-500' : 'border-white/60'
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
WebkitUserSelect: 'none',
|
WebkitUserSelect: 'none',
|
||||||
|
|||||||
Reference in New Issue
Block a user