Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3563d80
fix(player):修复直链播放m3u8时跨域错误及分片加载失败的问题
Troray Mar 10, 2026
b4320b7
修复直链播放时,测速组件预加载CORS报错的问题
Troray Mar 10, 2026
802bf80
修复SSRF/Host Header注入与Null报错的问题
Troray Mar 10, 2026
5591170
修改为仅m3u8/m3u等使用代理播放,mp4/flv/mkv等常规视频文件直接播放
Troray Mar 11, 2026
f387781
修正search页面僵尸历史记录tag
mtvpls Mar 11, 2026
11dd787
feat(play): 直链播放默认不走代理及无后缀流透传兼容
Troray Mar 11, 2026
1a79d49
搜索页面海量数据下使用虚拟滚动提高性能
mtvpls Mar 11, 2026
df48b9d
移除无用依赖
mtvpls Mar 11, 2026
2892819
Merge branch 'dev' of https://github.com/mtvpls/moontvplus-dev
mtvpls Mar 11, 2026
c2a46e1
Merge branch 'dev' of https://github.com/mtvpls/moontvplus-dev into dev
mtvpls Mar 11, 2026
3b5a6c9
修正弹幕搜索框挤压
mtvpls Mar 11, 2026
3e9fdbb
fix: 强化 SSRF 防护、修复代理链路安全漏洞与代码质量问题
Troray Mar 11, 2026
52166a4
Merge branch 'pr-213' into dev
mtvpls Mar 12, 2026
11b3c0c
增加主动恢复进度按钮
mtvpls Mar 13, 2026
1659804
弹幕搜索面板标题增加tooltip
mtvpls Mar 13, 2026
8a4bb4a
修复搜索页面未找到相关结果优先显示加载条
mtvpls Mar 13, 2026
976bc7a
pansou增加重试按钮
mtvpls Mar 13, 2026
ea16959
尝试修复emby代理内存泄漏
mtvpls Mar 13, 2026
11317ab
ai评论生成
mtvpls Mar 14, 2026
cdba909
fix缓存
mtvpls Mar 14, 2026
e8681fd
一键render部署
mtvpls Mar 15, 2026
3bec268
修改render必要变量
mtvpls Mar 15, 2026
fe210c2
电视直播代理控制权从客户端改为服务端
mtvpls Mar 16, 2026
826acf4
电视直播三种代理模式
mtvpls Mar 17, 2026
02b5c87
获取视频源详情不再依赖title
mtvpls Mar 18, 2026
5ac63ef
新增播放记录面板
mtvpls Mar 18, 2026
986ab4b
新增影视搜索列表视图
mtvpls Mar 19, 2026
88d453c
修正年份筛选和卡片视图来源数量显示
mtvpls Mar 19, 2026
0322b26
修复live全量代理not source
mtvpls Mar 19, 2026
dbc32a2
修复继续观看渐进式加载
mtvpls Mar 19, 2026
5a1e578
v215
mtvpls Mar 20, 2026
1c904c0
Merge branch 'dev'
mtvpls Mar 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
## [215.0.0] - 2026-03-20
### Added
- 增加主动恢复进度按钮
- 新增播放记录面板
- 弹幕搜索面板标题增加tooltip
- 影视搜索新增列表视图
- pansou增加重试按钮
- 新增ai评论生成
- 增加一键render部署
- 电视直播增加三种代理模式

### Changed
- 优化直链播放m3u8体验
- 搜索页面海量数据下使用虚拟滚动提高性能
- 优化emby代理内存泄漏问题
- 电视直播代理控制权从用户端改为管理端
- 获取视频源详情不再依赖title

### Fixed
- 修复search页面僵尸历史记录tag
- 修复弹幕搜索框挤压
- 修复搜索页面加载条显示顺序错误
- 修复继续观看渐进式加载的一些问题

## [214.1.0] - 2026-03-10
### Changed
- emby兼容jellyfin
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,14 @@

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/mtvpls/MoonTVPlus)

**一键部署到zeabur**
**一键部署到 Zeabur**

[![Deploy on Zeabur](https://zeabur.com/button.svg)](https://zeabur.com/templates/SCHCAY/deploy)

**一键部署到 Render**

[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/mtvpls/MoonTVPlus)



### Cloudflare Workers 部署(通过 GitHub Actions)
Expand Down
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
214.1.0
215.0.0

2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
"react-dom": "^18.3.1",
"react-icons": "^5.4.0",
"react-markdown": "^10.1.0",
"react-window": "^2.2.3",
"redis": "^4.6.7",
"remark-gfm": "^3.0.1",
"server-only": "^0.0.1",
Expand Down Expand Up @@ -96,7 +95,6 @@
"@types/pg": "^8.16.0",
"@types/react": "^18.3.18",
"@types/react-dom": "^19.1.6",
"@types/react-window": "^2.0.0",
"@types/testing-library__jest-dom": "^5.14.9",
"@types/xml2js": "^0.4.14",
"@typescript-eslint/eslint-plugin": "^5.62.0",
Expand Down
28 changes: 0 additions & 28 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions render.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
services:
- type: web
name: moontvplus
runtime: image
plan: free
image:
url: ghcr.io/mtvpls/moontvplus:latest
envVars:
- key: USERNAME
sync: false
- key: PASSWORD
sync: false
- key: NEXT_PUBLIC_SITE_NAME
value: MoonTVPlus
- key: CRON_PASSWORD
value: mtvpls
73 changes: 73 additions & 0 deletions src/app/admin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ interface LiveDataSource {
channelNumber?: number;
disabled?: boolean;
from: 'config' | 'custom';
proxyMode?: 'full' | 'm3u8-only' | 'direct'; // 代理模式
}

// 自定义分类数据类型
Expand Down Expand Up @@ -10327,6 +10328,7 @@ const AIConfigComponent = ({
const [enableHomepageEntry, setEnableHomepageEntry] = useState(true);
const [enableVideoCardEntry, setEnableVideoCardEntry] = useState(true);
const [enablePlayPageEntry, setEnablePlayPageEntry] = useState(true);
const [enableAIComments, setEnableAIComments] = useState(false);

// 权限控制
const [allowRegularUsers, setAllowRegularUsers] = useState(true);
Expand Down Expand Up @@ -10357,6 +10359,7 @@ const AIConfigComponent = ({
setEnableHomepageEntry(config.AIConfig.EnableHomepageEntry !== false);
setEnableVideoCardEntry(config.AIConfig.EnableVideoCardEntry !== false);
setEnablePlayPageEntry(config.AIConfig.EnablePlayPageEntry !== false);
setEnableAIComments(config.AIConfig.EnableAIComments || false);
setAllowRegularUsers(config.AIConfig.AllowRegularUsers !== false);
setTemperature(config.AIConfig.Temperature ?? 0.7);
setMaxTokens(config.AIConfig.MaxTokens ?? 1000);
Expand Down Expand Up @@ -10390,6 +10393,7 @@ const AIConfigComponent = ({
EnableHomepageEntry: enableHomepageEntry,
EnableVideoCardEntry: enableVideoCardEntry,
EnablePlayPageEntry: enablePlayPageEntry,
EnableAIComments: enableAIComments,
AllowRegularUsers: allowRegularUsers,
Temperature: temperature,
MaxTokens: maxTokens,
Expand Down Expand Up @@ -10659,6 +10663,7 @@ const AIConfigComponent = ({
{ key: 'homepage', label: '首页入口', desc: '在首页显示AI问片入口', state: enableHomepageEntry, setState: setEnableHomepageEntry },
{ key: 'videocard', label: '视频卡片入口', desc: '在视频卡片菜单中显示AI问片选项', state: enableVideoCardEntry, setState: setEnableVideoCardEntry },
{ key: 'playpage', label: '播放页入口', desc: '在视频播放页显示AI问片功能', state: enablePlayPageEntry, setState: setEnablePlayPageEntry },
{ key: 'aicomments', label: 'AI评论功能', desc: '在播放页生成AI评论(独立于豆瓣评论)', state: enableAIComments, setState: setEnableAIComments },
].map((item) => (
<div key={item.key} className='flex items-center justify-between py-2'>
<div>
Expand Down Expand Up @@ -10932,6 +10937,53 @@ const LiveSourceConfig = ({
});
};

const handleSetProxyMode = (key: string, mode: 'full' | 'm3u8-only' | 'direct') => {
withLoading(`setLiveProxyMode_${key}`, async () => {
// 保存旧值用于回滚
const oldMode = liveSources.find((s) => s.key === key)?.proxyMode;

// 乐观更新本地状态
setLiveSources((prev) =>
prev.map((s) =>
s.key === key ? { ...s, proxyMode: mode } : s
)
);

try {
const response = await fetch('/api/admin/live', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'set_proxy_mode',
key,
proxyMode: mode,
}),
});

if (!response.ok) {
throw new Error('设置代理模式失败');
}

// 成功后刷新配置
await refreshConfig();
} catch (error) {
// 失败时回滚本地状态
setLiveSources((prev) =>
prev.map((s) =>
s.key === key ? { ...s, proxyMode: oldMode } : s
)
);
showError(
error instanceof Error ? error.message : '设置代理模式失败',
showAlert
);
throw error;
}
}).catch(() => {
console.error('操作失败', 'set_proxy_mode', key);
});
};

const handleDelete = (key: string) => {
withLoading(`deleteLiveSource_${key}`, () =>
callLiveSourceApi({ action: 'delete', key })
Expand Down Expand Up @@ -11108,6 +11160,24 @@ const LiveSourceConfig = ({
{!liveSource.disabled ? '启用中' : '已禁用'}
</span>
</td>
<td className='px-6 py-4 whitespace-nowrap'>
<select
value={liveSource.proxyMode || 'full'}
onChange={(e) => {
handleSetProxyMode(liveSource.key, e.target.value as 'full' | 'm3u8-only' | 'direct');
}}
disabled={isLoading(`setLiveProxyMode_${liveSource.key}`)}
className={`px-2 py-1 text-xs rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 ${
isLoading(`setLiveProxyMode_${liveSource.key}`)
? 'opacity-50 cursor-not-allowed'
: 'cursor-pointer'
}`}
>
<option value='full'>全量代理</option>
<option value='m3u8-only'>仅代理m3u8</option>
<option value='direct'>直连</option>
</select>
</td>
<td className='px-6 py-4 whitespace-nowrap text-right text-sm font-medium space-x-2'>
<button
onClick={() => handleToggleEnable(liveSource.key)}
Expand Down Expand Up @@ -11415,6 +11485,9 @@ const LiveSourceConfig = ({
<th className='px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider'>
状态
</th>
<th className='px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider'>
代理模式
</th>
<th className='px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider'>
操作
</th>
Expand Down
4 changes: 4 additions & 0 deletions src/app/api/admin/ai/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export async function POST(request: NextRequest) {
EnableHomepageEntry,
EnableVideoCardEntry,
EnablePlayPageEntry,
EnableAIComments,
AllowRegularUsers,
Temperature,
MaxTokens,
Expand Down Expand Up @@ -93,6 +94,7 @@ export async function POST(request: NextRequest) {
EnableHomepageEntry: boolean;
EnableVideoCardEntry: boolean;
EnablePlayPageEntry: boolean;
EnableAIComments: boolean;
AllowRegularUsers: boolean;
Temperature?: number;
MaxTokens?: number;
Expand Down Expand Up @@ -132,6 +134,7 @@ export async function POST(request: NextRequest) {
typeof EnableHomepageEntry !== 'boolean' ||
typeof EnableVideoCardEntry !== 'boolean' ||
typeof EnablePlayPageEntry !== 'boolean' ||
typeof EnableAIComments !== 'boolean' ||
typeof AllowRegularUsers !== 'boolean' ||
(Temperature !== undefined && typeof Temperature !== 'number') ||
(MaxTokens !== undefined && typeof MaxTokens !== 'number') ||
Expand Down Expand Up @@ -181,6 +184,7 @@ export async function POST(request: NextRequest) {
EnableHomepageEntry,
EnableVideoCardEntry,
EnablePlayPageEntry,
EnableAIComments,
AllowRegularUsers,
Temperature,
MaxTokens,
Expand Down
14 changes: 13 additions & 1 deletion src/app/api/admin/live/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export async function POST(request: NextRequest) {
}

const body = await request.json();
const { action, key, name, url, ua, epg } = body;
const { action, key, name, url, ua, epg, proxyMode } = body;

if (!config) {
return NextResponse.json({ error: '配置不存在' }, { status: 404 });
Expand Down Expand Up @@ -153,6 +153,18 @@ export async function POST(request: NextRequest) {
config.LiveConfig = sortedLiveConfig;
break;

case 'set_proxy_mode':
// 设置代理模式
const setProxySource = config.LiveConfig.find((l) => l.key === key);
if (!setProxySource) {
return NextResponse.json({ error: '直播源不存在' }, { status: 404 });
}
if (!proxyMode || !['full', 'm3u8-only', 'direct'].includes(proxyMode)) {
return NextResponse.json({ error: '无效的代理模式' }, { status: 400 });
}
setProxySource.proxyMode = proxyMode as 'full' | 'm3u8-only' | 'direct';
break;

default:
return NextResponse.json({ error: '未知操作' }, { status: 400 });
}
Expand Down
Loading