轮播图数据源增加豆瓣
This commit is contained in:
@@ -6006,7 +6006,7 @@ const SiteConfigComponent = ({
|
||||
DanmakuApiToken: '87654321',
|
||||
TMDBApiKey: '',
|
||||
TMDBProxy: '',
|
||||
BannerDataSource: 'TMDB',
|
||||
BannerDataSource: 'Douban',
|
||||
RecommendationDataSource: 'Mixed',
|
||||
PansouApiUrl: '',
|
||||
PansouUsername: '',
|
||||
@@ -6095,7 +6095,7 @@ const SiteConfigComponent = ({
|
||||
DanmakuApiToken: config.SiteConfig.DanmakuApiToken || '87654321',
|
||||
TMDBApiKey: config.SiteConfig.TMDBApiKey || '',
|
||||
TMDBProxy: config.SiteConfig.TMDBProxy || '',
|
||||
BannerDataSource: config.SiteConfig.BannerDataSource || 'TMDB',
|
||||
BannerDataSource: config.SiteConfig.BannerDataSource || 'Douban',
|
||||
PansouApiUrl: config.SiteConfig.PansouApiUrl || '',
|
||||
PansouUsername: config.SiteConfig.PansouUsername || '',
|
||||
PansouPassword: config.SiteConfig.PansouPassword || '',
|
||||
@@ -6577,7 +6577,7 @@ const SiteConfigComponent = ({
|
||||
轮播图数据源
|
||||
</label>
|
||||
<select
|
||||
value={siteSettings.BannerDataSource || 'TMDB'}
|
||||
value={siteSettings.BannerDataSource || 'Douban'}
|
||||
onChange={(e) =>
|
||||
setSiteSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -6586,6 +6586,7 @@ const SiteConfigComponent = ({
|
||||
}
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-green-500 focus:border-transparent'
|
||||
>
|
||||
<option value='Douban'>豆瓣</option>
|
||||
<option value='TMDB'>TMDB</option>
|
||||
<option value='TX'>TX</option>
|
||||
</select>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getTMDBTrendingContent, getTMDBVideos } from '@/lib/tmdb.client';
|
||||
import { getConfig } from '@/lib/config';
|
||||
import { fetchDoubanData } from '@/lib/douban';
|
||||
|
||||
// 缓存配置 - 服务器内存缓存3小时
|
||||
const CACHE_DURATION = 3 * 60 * 60 * 1000; // 3小时
|
||||
@@ -10,6 +11,7 @@ const CACHE_DURATION = 3 * 60 * 60 * 1000; // 3小时
|
||||
// 为不同数据源分别维护缓存
|
||||
let tmdbCache: { data: any; timestamp: number } | null = null;
|
||||
let txCache: { data: any; timestamp: number } | null = null;
|
||||
let doubanCache: { data: any; timestamp: number } | null = null;
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
@@ -17,10 +19,10 @@ export async function GET() {
|
||||
try {
|
||||
// 获取配置
|
||||
const config = await getConfig();
|
||||
const bannerDataSource = config.SiteConfig?.BannerDataSource || 'TMDB';
|
||||
const bannerDataSource = config.SiteConfig?.BannerDataSource || 'Douban';
|
||||
|
||||
// 根据数据源选择对应的缓存
|
||||
const cache = bannerDataSource === 'TX' ? txCache : tmdbCache;
|
||||
const cache = bannerDataSource === 'TX' ? txCache : bannerDataSource === 'Douban' ? doubanCache : tmdbCache;
|
||||
|
||||
// 检查缓存
|
||||
if (cache && Date.now() - cache.timestamp < CACHE_DURATION) {
|
||||
@@ -30,7 +32,17 @@ export async function GET() {
|
||||
let result: any;
|
||||
|
||||
// 根据配置的数据源获取数据
|
||||
if (bannerDataSource === 'TX') {
|
||||
if (bannerDataSource === 'Douban') {
|
||||
// 使用豆瓣数据源
|
||||
result = await getDoubanBannerContent();
|
||||
// 添加数据源标识
|
||||
result.source = 'Douban';
|
||||
// 更新豆瓣缓存
|
||||
doubanCache = {
|
||||
data: result,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
} else if (bannerDataSource === 'TX') {
|
||||
// 使用TX数据源
|
||||
result = await getTXBannerContent();
|
||||
// 添加数据源标识
|
||||
@@ -234,3 +246,151 @@ function parseTXBannerData(data: any): any[] {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取豆瓣轮播图内容
|
||||
*/
|
||||
async function getDoubanBannerContent(): Promise<{ code: number; list: any[] }> {
|
||||
try {
|
||||
// 获取豆瓣热门电影
|
||||
const hotMoviesUrl = 'https://m.douban.com/rexxar/api/v2/subject/recent_hot/movie?start=0&limit=10&category=热门&type=全部';
|
||||
|
||||
interface DoubanHotMovie {
|
||||
id: string;
|
||||
title: string;
|
||||
card_subtitle?: string;
|
||||
pic?: {
|
||||
large: string;
|
||||
normal: string;
|
||||
};
|
||||
rating?: {
|
||||
value: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface DoubanHotMoviesResponse {
|
||||
items: DoubanHotMovie[];
|
||||
}
|
||||
|
||||
const hotMoviesData = await fetchDoubanData<DoubanHotMoviesResponse>(hotMoviesUrl);
|
||||
|
||||
if (!hotMoviesData.items || hotMoviesData.items.length === 0) {
|
||||
return { code: 200, list: [] };
|
||||
}
|
||||
|
||||
// 取前5个电影
|
||||
const topMovies = hotMoviesData.items.slice(0, 5);
|
||||
|
||||
// 为每个电影获取详情信息
|
||||
const bannerItems = await Promise.all(
|
||||
topMovies.map(async (movie) => {
|
||||
try {
|
||||
const detailUrl = `https://m.douban.com/rexxar/api/v2/subject/${movie.id}`;
|
||||
|
||||
interface DoubanDetailResponse {
|
||||
id: string;
|
||||
title: string;
|
||||
original_title?: string;
|
||||
year: string;
|
||||
rating?: {
|
||||
value: number;
|
||||
};
|
||||
intro?: string;
|
||||
genres?: string[];
|
||||
cover_url?: string;
|
||||
trailers?: Array<{
|
||||
video_url?: string;
|
||||
[key: string]: any;
|
||||
}>;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const detail = await fetchDoubanData<DoubanDetailResponse>(detailUrl);
|
||||
|
||||
// 获取预告片链接(取第一个)- 豆瓣是直链视频URL
|
||||
const trailerUrl = detail.trailers && detail.trailers.length > 0
|
||||
? detail.trailers[0].video_url
|
||||
: null;
|
||||
|
||||
// 获取横屏图片
|
||||
const backdropPath = detail.cover_url || movie.pic?.large || movie.pic?.normal || '';
|
||||
|
||||
// 提取年份
|
||||
const year = detail.year || movie.card_subtitle?.match(/(\d{4})/)?.[1] || '';
|
||||
|
||||
// 从card_subtitle提取标签(只读取第二个部分,通过空格分割)
|
||||
let tags: string[] = [];
|
||||
if (movie.card_subtitle) {
|
||||
const parts = movie.card_subtitle.split('/').map(s => s.trim());
|
||||
// 过滤掉年份(纯数字)和空字符串
|
||||
const filteredParts = parts.filter(part =>
|
||||
part && !/^\d{4}$/.test(part)
|
||||
);
|
||||
// 取第二个部分(类型),通过空格分割
|
||||
if (filteredParts.length >= 2) {
|
||||
tags = filteredParts[1].split(/\s+/).filter(t => t);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: movie.id,
|
||||
title: detail.title,
|
||||
backdrop_path: backdropPath,
|
||||
poster_path: backdropPath,
|
||||
release_date: year,
|
||||
overview: detail.intro || '',
|
||||
vote_average: detail.rating?.value || movie.rating?.value || 0,
|
||||
media_type: 'movie',
|
||||
genre_ids: [],
|
||||
genres: tags, // 使用从card_subtitle提取的标签
|
||||
trailer_url: trailerUrl, // 豆瓣预告片直链
|
||||
video_key: null, // 豆瓣不使用YouTube key
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`获取豆瓣电影 ${movie.id} 详情失败:`, error);
|
||||
|
||||
// 从card_subtitle提取标签(只读取第二个部分,通过空格分割)
|
||||
let tags: string[] = [];
|
||||
if (movie.card_subtitle) {
|
||||
const parts = movie.card_subtitle.split('/').map(s => s.trim());
|
||||
// 过滤掉年份(纯数字)和空字符串
|
||||
const filteredParts = parts.filter(part =>
|
||||
part && !/^\d{4}$/.test(part)
|
||||
);
|
||||
// 取第二个部分(类型),通过空格分割
|
||||
if (filteredParts.length >= 2) {
|
||||
tags = filteredParts[1].split(/\s+/).filter(t => t);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果获取详情失败,使用基本信息
|
||||
return {
|
||||
id: movie.id,
|
||||
title: movie.title,
|
||||
backdrop_path: movie.pic?.large || movie.pic?.normal || '',
|
||||
poster_path: movie.pic?.large || movie.pic?.normal || '',
|
||||
release_date: movie.card_subtitle?.match(/(\d{4})/)?.[1] || '',
|
||||
overview: '',
|
||||
vote_average: movie.rating?.value || 0,
|
||||
media_type: 'movie',
|
||||
genre_ids: [],
|
||||
genres: tags, // 使用从card_subtitle提取的标签
|
||||
trailer_url: null,
|
||||
video_key: null,
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// 过滤掉没有图片的项目
|
||||
const validBannerItems = bannerItems.filter(item => item.backdrop_path);
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
list: validBannerItems,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取豆瓣轮播图数据失败:', error);
|
||||
return { code: 500, list: [] };
|
||||
}
|
||||
}
|
||||
|
||||
91
src/app/api/video-proxy/route.ts
Normal file
91
src/app/api/video-proxy/route.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
// 视频代理接口,支持Range请求
|
||||
export async function GET(request: Request) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const videoUrl = searchParams.get('url');
|
||||
|
||||
if (!videoUrl) {
|
||||
return NextResponse.json({ error: 'Missing video URL' }, { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取客户端的Range请求头
|
||||
const range = request.headers.get('range');
|
||||
|
||||
const fetchHeaders: HeadersInit = {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
|
||||
Accept: 'video/mp4,video/*;q=0.9,*/*;q=0.8',
|
||||
Referer: 'https://movie.douban.com/',
|
||||
};
|
||||
|
||||
// 如果客户端发送了Range请求,转发给源服务器
|
||||
if (range) {
|
||||
fetchHeaders['Range'] = range;
|
||||
}
|
||||
|
||||
const videoResponse = await fetch(videoUrl, {
|
||||
headers: fetchHeaders,
|
||||
});
|
||||
|
||||
if (!videoResponse.ok) {
|
||||
return NextResponse.json(
|
||||
{ error: videoResponse.statusText },
|
||||
{ status: videoResponse.status }
|
||||
);
|
||||
}
|
||||
|
||||
if (!videoResponse.body) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Video response has no body' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
// 创建响应头
|
||||
const headers = new Headers();
|
||||
|
||||
// 复制重要的响应头
|
||||
const contentType = videoResponse.headers.get('content-type');
|
||||
if (contentType) {
|
||||
headers.set('Content-Type', contentType);
|
||||
}
|
||||
|
||||
const contentLength = videoResponse.headers.get('content-length');
|
||||
if (contentLength) {
|
||||
headers.set('Content-Length', contentLength);
|
||||
}
|
||||
|
||||
const contentRange = videoResponse.headers.get('content-range');
|
||||
if (contentRange) {
|
||||
headers.set('Content-Range', contentRange);
|
||||
}
|
||||
|
||||
const acceptRanges = videoResponse.headers.get('accept-ranges');
|
||||
if (acceptRanges) {
|
||||
headers.set('Accept-Ranges', acceptRanges);
|
||||
}
|
||||
|
||||
// 设置缓存头
|
||||
headers.set('Cache-Control', 'public, max-age=31536000, s-maxage=31536000'); // 缓存1年
|
||||
headers.set('CDN-Cache-Control', 'public, s-maxage=31536000');
|
||||
headers.set('Vercel-CDN-Cache-Control', 'public, s-maxage=31536000');
|
||||
|
||||
// 返回视频流,状态码根据是否有Range请求决定
|
||||
const status = range && contentRange ? 206 : 200;
|
||||
|
||||
return new Response(videoResponse.body, {
|
||||
status,
|
||||
headers,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error proxying video:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Error fetching video' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import Image from 'next/image';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { getTMDBImageUrl, getGenreNames, type TMDBItem } from '@/lib/tmdb.client';
|
||||
import { ChevronLeft, ChevronRight, Play } from 'lucide-react';
|
||||
import { processImageUrl, processVideoUrl } from '@/lib/utils';
|
||||
|
||||
interface BannerCarouselProps {
|
||||
autoPlayInterval?: number; // 自动播放间隔(毫秒)
|
||||
@@ -14,6 +15,8 @@ interface BannerCarouselProps {
|
||||
interface BannerItem extends TMDBItem {
|
||||
subtitle?: string; // TX数据源的子标题
|
||||
tags?: string[]; // TX数据源的标签
|
||||
trailer_url?: string | null; // 豆瓣预告片直链
|
||||
genres?: string[]; // 豆瓣数据源的类型标签
|
||||
}
|
||||
|
||||
export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarouselProps) {
|
||||
@@ -25,6 +28,7 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
|
||||
const [skipNextAutoPlay, setSkipNextAutoPlay] = useState(false); // 跳过下一次自动播放
|
||||
const [isYouTubeAccessible, setIsYouTubeAccessible] = useState(false); // YouTube连通性(默认false,检查后再决定)
|
||||
const [enableTrailers, setEnableTrailers] = useState(false); // 是否启用预告片(默认关闭)
|
||||
const [dataSource, setDataSource] = useState<string>(''); // 当前数据源
|
||||
const touchStartX = useRef(0);
|
||||
const touchEndX = useRef(0);
|
||||
const isManualChange = useRef(false); // 标记是否为手动切换
|
||||
@@ -45,14 +49,29 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
|
||||
// 获取图片URL(处理TX完整URL和TMDB路径)
|
||||
const getImageUrl = (path: string | null) => {
|
||||
if (!path) return '';
|
||||
// 如果是完整URL(TX数据源),直接返回
|
||||
// 如果是完整URL(TX数据源或豆瓣),需要判断是否需要代理
|
||||
if (path.startsWith('http://') || path.startsWith('https://')) {
|
||||
// 豆瓣图片需要通过代理
|
||||
if (path.includes('doubanio.com')) {
|
||||
return processImageUrl(path);
|
||||
}
|
||||
// TX等其他完整URL直接返回
|
||||
return path;
|
||||
}
|
||||
// 否则使用TMDB的URL拼接
|
||||
return getTMDBImageUrl(path, 'original');
|
||||
};
|
||||
|
||||
// 获取视频URL(处理豆瓣视频代理)
|
||||
const getVideoUrl = (url: string | null) => {
|
||||
if (!url) return null;
|
||||
// 豆瓣视频需要通过域名替换代理
|
||||
if (url.includes('doubanio.com')) {
|
||||
return processVideoUrl(url);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
// 读取本地设置
|
||||
useEffect(() => {
|
||||
const setting = localStorage.getItem('enableTrailers');
|
||||
@@ -61,10 +80,10 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 检测YouTube连通性 - 仅在启用预告片时检测
|
||||
// 检测YouTube连通性 - 仅在启用预告片且数据源为TMDB时检测
|
||||
useEffect(() => {
|
||||
// 如果未启用预告片,不进行检测
|
||||
if (!enableTrailers) {
|
||||
// 如果未启用预告片或数据源不是TMDB,不进行检测
|
||||
if (!enableTrailers || dataSource !== 'TMDB') {
|
||||
setIsYouTubeAccessible(false);
|
||||
return;
|
||||
}
|
||||
@@ -91,14 +110,14 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
|
||||
};
|
||||
|
||||
checkYouTubeAccess();
|
||||
}, [enableTrailers]);
|
||||
}, [enableTrailers, dataSource]);
|
||||
|
||||
// 获取热门内容
|
||||
useEffect(() => {
|
||||
const fetchTrending = async () => {
|
||||
try {
|
||||
// 先尝试从所有可能的数据源缓存中读取
|
||||
const sources = ['TMDB', 'TX'];
|
||||
const sources = ['TMDB', 'TX', 'Douban'];
|
||||
let cachedData = null;
|
||||
let validSource = null;
|
||||
|
||||
@@ -126,6 +145,7 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
|
||||
// 如果有有效的缓存,直接使用,不请求API
|
||||
if (cachedData) {
|
||||
setItems(cachedData);
|
||||
setDataSource(validSource || ''); // 设置数据源
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
@@ -139,6 +159,7 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
|
||||
const cacheKey = getLocalStorageKey(dataSource);
|
||||
|
||||
setItems(result.list);
|
||||
setDataSource(dataSource); // 设置数据源
|
||||
|
||||
// 保存到 localStorage(使用数据源特定的key)
|
||||
try {
|
||||
@@ -285,7 +306,20 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
|
||||
index === currentIndex ? 'opacity-100' : 'opacity-0'
|
||||
}`}
|
||||
>
|
||||
{item.video_key && isYouTubeAccessible && enableTrailers ? (
|
||||
{item.trailer_url && enableTrailers ? (
|
||||
/* 显示豆瓣直链视频 */
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
<video
|
||||
src={getVideoUrl(item.trailer_url) || undefined}
|
||||
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 min-w-full min-h-full w-auto h-auto object-cover"
|
||||
autoPlay
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
preload="metadata"
|
||||
/>
|
||||
</div>
|
||||
) : item.video_key && isYouTubeAccessible && enableTrailers ? (
|
||||
/* 显示YouTube视频 */
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
<iframe
|
||||
@@ -333,13 +367,20 @@ export default function BannerCarousel({ autoPlayInterval = 5000 }: BannerCarous
|
||||
{currentItem.vote_average.toFixed(1)}
|
||||
</span>
|
||||
)}
|
||||
{/* 显示TX数据源的标签 */}
|
||||
{/* 显示标签:优先TX的tags,其次豆瓣的genres,最后TMDB的genre_ids */}
|
||||
{currentItem.tags && currentItem.tags.length > 0 ? (
|
||||
currentItem.tags.slice(0, 3).map((tag, index) => (
|
||||
<span key={index} className="px-2 py-1 bg-white/20 backdrop-blur-sm rounded text-sm">
|
||||
{tag}
|
||||
</span>
|
||||
))
|
||||
) : currentItem.genres && Array.isArray(currentItem.genres) && currentItem.genres.length > 0 ? (
|
||||
/* 显示豆瓣数据源的标签 */
|
||||
currentItem.genres.slice(0, 3).map((genre, index) => (
|
||||
<span key={index} className="px-2 py-1 bg-white/20 backdrop-blur-sm rounded text-sm">
|
||||
{genre}
|
||||
</span>
|
||||
))
|
||||
) : (
|
||||
/* 显示TMDB数据源的类型标签 */
|
||||
getGenreNames(currentItem.genre_ids, 3).map(genre => (
|
||||
|
||||
@@ -22,7 +22,7 @@ export interface AdminConfig {
|
||||
// TMDB配置
|
||||
TMDBApiKey?: string;
|
||||
TMDBProxy?: string;
|
||||
BannerDataSource?: string; // 轮播图数据源:TMDB 或 TX
|
||||
BannerDataSource?: string; // 轮播图数据源:TMDB、TX 或 Douban
|
||||
RecommendationDataSource?: string; // 更多推荐数据源:Douban、TMDB、Mixed、MixedSmart
|
||||
// Pansou配置
|
||||
PansouApiUrl?: string;
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface ChangelogEntry {
|
||||
export const changelog: ChangelogEntry[] = [
|
||||
{
|
||||
version: '206.1.0',
|
||||
date: '2026-01-04',
|
||||
date: '2026-01-05',
|
||||
added: [
|
||||
"新增手动上传弹幕功能"
|
||||
],
|
||||
|
||||
@@ -27,7 +27,7 @@ function getDoubanImageProxyConfig(): {
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理图片 URL,如果设置了图片代理则使用代理
|
||||
* 处理图片 URL,统一使用服务器代理
|
||||
*/
|
||||
export function processImageUrl(originalUrl: string): string {
|
||||
if (!originalUrl) return originalUrl;
|
||||
@@ -42,28 +42,23 @@ export function processImageUrl(originalUrl: string): string {
|
||||
return originalUrl;
|
||||
}
|
||||
|
||||
const { proxyType, proxyUrl } = getDoubanImageProxyConfig();
|
||||
switch (proxyType) {
|
||||
case 'server':
|
||||
return `/api/image-proxy?url=${encodeURIComponent(originalUrl)}`;
|
||||
case 'img3':
|
||||
return originalUrl.replace(/img\d+\.doubanio\.com/g, 'img3.doubanio.com');
|
||||
case 'cmliussss-cdn-tencent':
|
||||
return originalUrl.replace(
|
||||
/img\d+\.doubanio\.com/g,
|
||||
'img.doubanio.cmliussss.net'
|
||||
);
|
||||
case 'cmliussss-cdn-ali':
|
||||
return originalUrl.replace(
|
||||
/img\d+\.doubanio\.com/g,
|
||||
'img.doubanio.cmliussss.com'
|
||||
);
|
||||
case 'custom':
|
||||
return `${proxyUrl}${encodeURIComponent(originalUrl)}`;
|
||||
case 'direct':
|
||||
default:
|
||||
return originalUrl;
|
||||
// 统一使用服务器代理
|
||||
return `/api/image-proxy?url=${encodeURIComponent(originalUrl)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理视频 URL,如果<E5A682><E69E9C><EFBFBD>置了代理则使用代理(与图片使用相同的代理配置)
|
||||
*/
|
||||
export function processVideoUrl(originalUrl: string): string {
|
||||
if (!originalUrl) return originalUrl;
|
||||
|
||||
// 仅处理豆瓣视频代理
|
||||
if (!originalUrl.includes('doubanio.com')) {
|
||||
return originalUrl;
|
||||
}
|
||||
|
||||
// 统一使用服务器代理
|
||||
return `/api/video-proxy?url=${encodeURIComponent(originalUrl)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user