转存播放补充元信息
This commit is contained in:
+142
-39
@@ -572,6 +572,13 @@ function PlayPageClient() {
|
||||
|
||||
// 纠错后的描述信息(用于显示,不触发 detail 更新)
|
||||
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')
|
||||
const [currentSource, setCurrentSource] = useState(searchParams.get('source') || '');
|
||||
@@ -579,6 +586,11 @@ function PlayPageClient() {
|
||||
const [fileName] = useState(searchParams.get('fileName') || ''); // 小雅源:用户点击的文件名
|
||||
const isDirectPlay = currentSource === 'directplay';
|
||||
|
||||
useEffect(() => {
|
||||
setQuarkTempTMDBMeta(null);
|
||||
setPendingQuarkTempTMDBData(null);
|
||||
}, [currentSource, currentId]);
|
||||
|
||||
// 解析 source 参数以获取 embyKey(仅用于 API 调用)
|
||||
const parseSourceForApi = (source: string): { source: string; embyKey?: string } => {
|
||||
if (source.startsWith('emby_')) {
|
||||
@@ -1187,14 +1199,18 @@ function PlayPageClient() {
|
||||
const detCacheAge = Date.now() - detTimestamp;
|
||||
const detCacheMaxAge = 24 * 60 * 60 * 1000; // 1天
|
||||
|
||||
if (detCacheAge < detCacheMaxAge && data && data.backdrop) {
|
||||
console.log('使用缓存的TMDB详情数据');
|
||||
setTmdbBackdrop(processImageUrl(data.backdrop));
|
||||
if (detCacheAge < detCacheMaxAge && data) {
|
||||
if (data.backdrop) {
|
||||
setTmdbBackdrop(processImageUrl(data.backdrop));
|
||||
} else {
|
||||
setTmdbBackdrop(null);
|
||||
}
|
||||
|
||||
// 如果没有豆瓣ID,使用TMDb数据补充
|
||||
if (!videoDoubanId || videoDoubanId === 0) {
|
||||
populateDoubanFieldsFromTMDB(data);
|
||||
}
|
||||
populatePlayMetadataFromTMDB(data);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -1215,7 +1231,6 @@ function PlayPageClient() {
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
console.log('获取TMDB详情失败');
|
||||
setTmdbBackdrop(null);
|
||||
return;
|
||||
}
|
||||
@@ -1224,45 +1239,94 @@ function PlayPageClient() {
|
||||
|
||||
if (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 {
|
||||
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) {
|
||||
console.error('获取TMDB背景图失败:', error);
|
||||
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数据填充豆瓣字段
|
||||
const populateDoubanFieldsFromTMDB = (tmdbData: any) => {
|
||||
// 设置评分
|
||||
@@ -1296,6 +1360,45 @@ function PlayPageClient() {
|
||||
fetchTMDBBackdrop();
|
||||
}, [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('');
|
||||
@@ -8872,12 +8975,12 @@ function PlayPageClient() {
|
||||
</span>
|
||||
)}
|
||||
{/* 优先使用 doubanYear,如果没有则使用 detail.year 或 videoYear */}
|
||||
{(doubanYear || detail?.year || videoYear) && (
|
||||
<span>{doubanYear || detail?.year || videoYear}</span>
|
||||
{(doubanYear || quarkTempTMDBMeta?.year || detail?.year || videoYear) && (
|
||||
<span>{doubanYear || quarkTempTMDBMeta?.year || detail?.year || videoYear}</span>
|
||||
)}
|
||||
{detail?.source_name && (
|
||||
<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}
|
||||
>
|
||||
@@ -8897,7 +9000,7 @@ function PlayPageClient() {
|
||||
{detail?.type_name && <span>{detail.type_name}</span>}
|
||||
</div>
|
||||
{/* 剧情简介 */}
|
||||
{(doubanCardSubtitle || correctedDesc || detail?.desc) && (
|
||||
{(doubanCardSubtitle || quarkTempTMDBMeta?.desc || correctedDesc || detail?.desc) && (
|
||||
<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' : ''}`}
|
||||
style={{ whiteSpace: 'pre-line' }}
|
||||
@@ -8908,7 +9011,7 @@ function PlayPageClient() {
|
||||
{doubanCardSubtitle}
|
||||
</div>
|
||||
)}
|
||||
{correctedDesc || detail?.desc}
|
||||
{quarkTempTMDBMeta?.desc || correctedDesc || detail?.desc}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -940,7 +940,7 @@ const EpisodeSelector: React.FC<EpisodeSelectorProps> = ({
|
||||
{/* 源名称和集数信息 - 垂直居中 */}
|
||||
<div className='flex items-center justify-between'>
|
||||
<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-gray-500/60'
|
||||
}`}>
|
||||
|
||||
@@ -1043,7 +1043,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
||||
>
|
||||
<span
|
||||
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={{
|
||||
WebkitUserSelect: 'none',
|
||||
@@ -1367,7 +1367,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(function VideoCard
|
||||
{config.showSourceName && source_name && !cmsData && (
|
||||
<span
|
||||
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={{
|
||||
WebkitUserSelect: 'none',
|
||||
|
||||
Reference in New Issue
Block a user