mirror of
https://github.com/MoonTechLab/LunaTV.git
synced 2026-06-20 03:29:18 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
388089d2a1 | ||
|
|
af8caa23be | ||
|
|
31653c1dd7 |
13
CHANGELOG
13
CHANGELOG
@@ -1,3 +1,16 @@
|
||||
## [100.1.3] - 2026-05-28
|
||||
|
||||
### Fixed
|
||||
|
||||
- 修复首页热门电影、热门剧集、热门综艺在番剧接口失败时一并空白的问题
|
||||
- 番剧日历改为通过服务端代理请求,规避 bgm.tv 的 CORS 限制
|
||||
|
||||
## [100.1.2] - 2026-03-15
|
||||
|
||||
### Changed
|
||||
|
||||
- 移除豆瓣图片代理中的「直连」和「豆瓣官方精品 CDN」选项,历史数据自动兼容为服务器代理
|
||||
|
||||
## [100.1.1] - 2026-02-27
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1 +1 @@
|
||||
100.1.1
|
||||
100.1.3
|
||||
|
||||
@@ -195,18 +195,20 @@ function main() {
|
||||
|
||||
fs.writeFileSync(outputPath, tsContent, 'utf-8');
|
||||
|
||||
// 读取 VERSION.txt 并同步到 version.ts
|
||||
const versionTxtPath = path.join(process.cwd(), 'VERSION.txt');
|
||||
const versionFromFile = fs.readFileSync(versionTxtPath, 'utf8').trim();
|
||||
console.log(`📄 VERSION.txt 版本: ${versionFromFile}`);
|
||||
updateVersionTs(versionFromFile);
|
||||
|
||||
// 检查是否在 GitHub Actions 环境中运行
|
||||
const isGitHubActions = process.env.GITHUB_ACTIONS === 'true';
|
||||
|
||||
if (isGitHubActions) {
|
||||
// 在 GitHub Actions 中,更新版本文件
|
||||
console.log('正在更新版本文件...');
|
||||
// 在 GitHub Actions 中,更新 VERSION.txt 为 CHANGELOG 最新版本
|
||||
console.log('正在更新 VERSION.txt...');
|
||||
updateVersionFile(latestVersion);
|
||||
updateVersionTs(latestVersion);
|
||||
} else {
|
||||
// 在本地运行时,只提示但不更新版本文件
|
||||
console.log('🔧 本地运行模式:跳过版本文件更新');
|
||||
console.log('💡 版本文件更新将在 git tag 触发的 release 工作流中完成');
|
||||
}
|
||||
|
||||
console.log(`✅ 成功生成 ${outputPath}`);
|
||||
|
||||
@@ -3416,9 +3416,7 @@ const SiteConfigComponent = ({ config, refreshConfig }: { config: AdminConfig |
|
||||
|
||||
// 豆瓣图片代理选项
|
||||
const doubanImageProxyTypeOptions = [
|
||||
{ value: 'direct', label: '直连(浏览器直接请求豆瓣)' },
|
||||
{ value: 'server', label: '服务器代理(由服务器代理请求豆瓣)' },
|
||||
{ value: 'img3', label: '豆瓣官方精品 CDN(阿里云)' },
|
||||
{
|
||||
value: 'cmliussss-cdn-tencent',
|
||||
label: '豆瓣 CDN By CMLiussss(腾讯云)',
|
||||
@@ -3453,7 +3451,9 @@ const SiteConfigComponent = ({ config, refreshConfig }: { config: AdminConfig |
|
||||
DoubanProxyType: config.SiteConfig.DoubanProxyType || 'cmliussss-cdn-tencent',
|
||||
DoubanProxy: config.SiteConfig.DoubanProxy || '',
|
||||
DoubanImageProxyType:
|
||||
config.SiteConfig.DoubanImageProxyType || 'cmliussss-cdn-tencent',
|
||||
(config.SiteConfig.DoubanImageProxyType === 'direct' || config.SiteConfig.DoubanImageProxyType === 'img3')
|
||||
? 'server'
|
||||
: (config.SiteConfig.DoubanImageProxyType || 'cmliussss-cdn-tencent'),
|
||||
DoubanImageProxy: config.SiteConfig.DoubanImageProxy || '',
|
||||
DisableYellowFilter: config.SiteConfig.DisableYellowFilter || false,
|
||||
FluidSearch: config.SiteConfig.FluidSearch || true,
|
||||
|
||||
43
src/app/api/bangumi/calendar/route.ts
Normal file
43
src/app/api/bangumi/calendar/route.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
import { getCacheTime } from '@/lib/config';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
export async function GET() {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
||||
|
||||
try {
|
||||
const response = await fetch('https://api.bgm.tv/calendar', {
|
||||
signal: controller.signal,
|
||||
headers: {
|
||||
'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: 'application/json',
|
||||
},
|
||||
});
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const cacheTime = await getCacheTime();
|
||||
|
||||
return NextResponse.json(data, {
|
||||
headers: {
|
||||
'Cache-Control': `public, max-age=${cacheTime}, s-maxage=${cacheTime}`,
|
||||
'CDN-Cache-Control': `public, s-maxage=${cacheTime}`,
|
||||
'Vercel-CDN-Cache-Control': `public, s-maxage=${cacheTime}`,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutId);
|
||||
return NextResponse.json(
|
||||
{ error: '获取番剧日历失败', details: (error as Error).message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -72,9 +72,10 @@ function HomeClient() {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// 并行获取热门电影、热门剧集和热门综艺
|
||||
const [moviesData, tvShowsData, varietyShowsData, bangumiCalendarData] =
|
||||
await Promise.all([
|
||||
// 并行获取热门电影、热门剧集、热门综艺和番剧日历
|
||||
// 使用 allSettled 避免单个请求失败导致全部数据为空
|
||||
const [moviesRes, tvShowsRes, varietyShowsRes, bangumiRes] =
|
||||
await Promise.allSettled([
|
||||
getDoubanCategories({
|
||||
kind: 'movie',
|
||||
category: '热门',
|
||||
@@ -85,19 +86,32 @@ function HomeClient() {
|
||||
GetBangumiCalendarData(),
|
||||
]);
|
||||
|
||||
if (moviesData.code === 200) {
|
||||
setHotMovies(moviesData.list);
|
||||
if (moviesRes.status === 'fulfilled' && moviesRes.value.code === 200) {
|
||||
setHotMovies(moviesRes.value.list);
|
||||
} else if (moviesRes.status === 'rejected') {
|
||||
console.error('获取热门电影失败:', moviesRes.reason);
|
||||
}
|
||||
|
||||
if (tvShowsData.code === 200) {
|
||||
setHotTvShows(tvShowsData.list);
|
||||
if (tvShowsRes.status === 'fulfilled' && tvShowsRes.value.code === 200) {
|
||||
setHotTvShows(tvShowsRes.value.list);
|
||||
} else if (tvShowsRes.status === 'rejected') {
|
||||
console.error('获取热门剧集失败:', tvShowsRes.reason);
|
||||
}
|
||||
|
||||
if (varietyShowsData.code === 200) {
|
||||
setHotVarietyShows(varietyShowsData.list);
|
||||
if (
|
||||
varietyShowsRes.status === 'fulfilled' &&
|
||||
varietyShowsRes.value.code === 200
|
||||
) {
|
||||
setHotVarietyShows(varietyShowsRes.value.list);
|
||||
} else if (varietyShowsRes.status === 'rejected') {
|
||||
console.error('获取热门综艺失败:', varietyShowsRes.reason);
|
||||
}
|
||||
|
||||
setBangumiCalendarData(bangumiCalendarData);
|
||||
if (bangumiRes.status === 'fulfilled') {
|
||||
setBangumiCalendarData(bangumiRes.value);
|
||||
} else {
|
||||
console.error('获取番剧日历失败:', bangumiRes.reason);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取推荐数据失败:', error);
|
||||
} finally {
|
||||
|
||||
@@ -88,9 +88,7 @@ export const UserMenu: React.FC = () => {
|
||||
|
||||
// 豆瓣图片代理选项
|
||||
const doubanImageProxyTypeOptions = [
|
||||
{ value: 'direct', label: '直连(浏览器直接请求豆瓣)' },
|
||||
{ value: 'server', label: '服务器代理(由服务器代理请求豆瓣)' },
|
||||
{ value: 'img3', label: '豆瓣官方精品 CDN(阿里云)' },
|
||||
{
|
||||
value: 'cmliussss-cdn-tencent',
|
||||
label: '豆瓣 CDN By CMLiussss(腾讯云)',
|
||||
@@ -159,10 +157,13 @@ export const UserMenu: React.FC = () => {
|
||||
);
|
||||
const defaultDoubanImageProxyType =
|
||||
(window as any).RUNTIME_CONFIG?.DOUBAN_IMAGE_PROXY_TYPE || 'cmliussss-cdn-tencent';
|
||||
// 兼容历史数据:直连和豆瓣官方精品 CDN 统一使用服务器代理
|
||||
const normalizeImageProxyType = (type: string) =>
|
||||
type === 'direct' || type === 'img3' ? 'server' : type;
|
||||
if (savedDoubanImageProxyType !== null) {
|
||||
setDoubanImageProxyType(savedDoubanImageProxyType);
|
||||
setDoubanImageProxyType(normalizeImageProxyType(savedDoubanImageProxyType));
|
||||
} else if (defaultDoubanImageProxyType) {
|
||||
setDoubanImageProxyType(defaultDoubanImageProxyType);
|
||||
setDoubanImageProxyType(normalizeImageProxyType(defaultDoubanImageProxyType));
|
||||
}
|
||||
|
||||
const savedDoubanImageProxyUrl = localStorage.getItem(
|
||||
@@ -422,8 +423,11 @@ export const UserMenu: React.FC = () => {
|
||||
(window as any).RUNTIME_CONFIG?.DOUBAN_PROXY_TYPE || 'cmliussss-cdn-tencent';
|
||||
const defaultDoubanProxy =
|
||||
(window as any).RUNTIME_CONFIG?.DOUBAN_PROXY || '';
|
||||
const defaultDoubanImageProxyType =
|
||||
let defaultDoubanImageProxyType =
|
||||
(window as any).RUNTIME_CONFIG?.DOUBAN_IMAGE_PROXY_TYPE || 'cmliussss-cdn-tencent';
|
||||
if (defaultDoubanImageProxyType === 'direct' || defaultDoubanImageProxyType === 'img3') {
|
||||
defaultDoubanImageProxyType = 'server';
|
||||
}
|
||||
const defaultDoubanImageProxyUrl =
|
||||
(window as any).RUNTIME_CONFIG?.DOUBAN_IMAGE_PROXY || '';
|
||||
const defaultFluidSearch =
|
||||
|
||||
@@ -23,7 +23,10 @@ export interface BangumiCalendarData {
|
||||
}
|
||||
|
||||
export async function GetBangumiCalendarData(): Promise<BangumiCalendarData[]> {
|
||||
const response = await fetch('https://api.bgm.tv/calendar');
|
||||
const response = await fetch('/api/bangumi/calendar');
|
||||
if (!response.ok) {
|
||||
throw new Error(`获取番剧日历失败: HTTP ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
const filteredData = data.map((item: BangumiCalendarData) => ({
|
||||
...item,
|
||||
|
||||
@@ -10,6 +10,33 @@ export interface ChangelogEntry {
|
||||
}
|
||||
|
||||
export const changelog: ChangelogEntry[] = [
|
||||
{
|
||||
version: "100.1.3",
|
||||
date: "2026-05-28",
|
||||
added: [
|
||||
// 无新增内容
|
||||
],
|
||||
changed: [
|
||||
// 无变更内容
|
||||
],
|
||||
fixed: [
|
||||
"修复首页热门电影、热门剧集、热门综艺在番剧接口失败时一并空白的问题",
|
||||
"番剧日历改为通过服务端代理请求,规避 bgm.tv 的 CORS 限制"
|
||||
]
|
||||
},
|
||||
{
|
||||
version: "100.1.2",
|
||||
date: "2026-03-15",
|
||||
added: [
|
||||
// 无新增内容
|
||||
],
|
||||
changed: [
|
||||
"移除豆瓣图片代理中的「直连」和「豆瓣官方精品 CDN」选项,历史数据自动兼容为服务器代理"
|
||||
],
|
||||
fixed: [
|
||||
// 无修复内容
|
||||
]
|
||||
},
|
||||
{
|
||||
version: "100.1.1",
|
||||
date: "2026-02-27",
|
||||
|
||||
@@ -4,18 +4,20 @@ import Hls from 'hls.js';
|
||||
|
||||
function getDoubanImageProxyConfig(): {
|
||||
proxyType:
|
||||
| 'direct'
|
||||
| 'server'
|
||||
| 'img3'
|
||||
| 'cmliussss-cdn-tencent'
|
||||
| 'cmliussss-cdn-ali'
|
||||
| 'custom';
|
||||
proxyUrl: string;
|
||||
} {
|
||||
const doubanImageProxyType =
|
||||
let doubanImageProxyType =
|
||||
localStorage.getItem('doubanImageProxyType') ||
|
||||
(window as any).RUNTIME_CONFIG?.DOUBAN_IMAGE_PROXY_TYPE ||
|
||||
'cmliussss-cdn-tencent';
|
||||
// 兼容历史数据:直连和豆瓣官方精品 CDN 统一使用服务器代理
|
||||
if (doubanImageProxyType === 'direct' || doubanImageProxyType === 'img3') {
|
||||
doubanImageProxyType = 'server';
|
||||
}
|
||||
const doubanImageProxy =
|
||||
localStorage.getItem('doubanImageProxyUrl') ||
|
||||
(window as any).RUNTIME_CONFIG?.DOUBAN_IMAGE_PROXY ||
|
||||
@@ -41,8 +43,6 @@ export function processImageUrl(originalUrl: string): string {
|
||||
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,
|
||||
@@ -55,9 +55,8 @@ export function processImageUrl(originalUrl: string): string {
|
||||
);
|
||||
case 'custom':
|
||||
return `${proxyUrl}${encodeURIComponent(originalUrl)}`;
|
||||
case 'direct':
|
||||
default:
|
||||
return originalUrl;
|
||||
return `/api/image-proxy?url=${encodeURIComponent(originalUrl)}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const CURRENT_VERSION = '100.1.0';
|
||||
const CURRENT_VERSION = '100.1.3';
|
||||
|
||||
// 导出当前版本号供其他地方使用
|
||||
export { CURRENT_VERSION };
|
||||
|
||||
Reference in New Issue
Block a user