From ea0ebcd8ce9a4b01475556fefcc0a7031e20c434 Mon Sep 17 00:00:00 2001 From: mtvpls Date: Fri, 23 Jan 2026 00:54:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8E=BB=E5=B9=BF=E5=91=8A=E5=A2=9E=E5=BC=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/admin/page.tsx | 43 ++++++++++++++++++--- src/app/api/ad-filter/route.ts | 1 + src/app/api/proxy-m3u8/route.ts | 54 +++++++++++++++++---------- src/app/layout.tsx | 4 ++ src/app/play/page.tsx | 66 ++++++++++++++++++--------------- 5 files changed, 114 insertions(+), 54 deletions(-) diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 9d09b16..d1302da 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -8429,18 +8429,51 @@ const CustomAdFilterConfig = ({ const defaultAdFilterCode = `function filterAdsFromM3U8(type: string, m3u8Content: string): string { if (!m3u8Content) return ''; + // 广告关键字列表 + const adKeywords = [ + 'sponsor', + '/ad/', + '/ads/', + 'advert', + 'advertisement', + '/adjump', + 'redtraffic' + ]; + // 按行分割M3U8内容 const lines = m3u8Content.split('\\n'); const filteredLines = []; - let nextdelete = false; - for (let i = 0; i < lines.length; i++) { + let i = 0; + while (i < lines.length) { const line = lines[i]; - // 只过滤#EXT-X-DISCONTINUITY标识 - if (!line.includes('#EXT-X-DISCONTINUITY')) { - filteredLines.push(line); + // 跳过 #EXT-X-DISCONTINUITY 标识 + if (line.includes('#EXT-X-DISCONTINUITY')) { + i++; + continue; } + + // 如果是 EXTINF 行,检查下一行 URL 是否包含广告关键字 + if (line.includes('#EXTINF:')) { + // 检查下一行 URL 是否包含广告关键字 + if (i + 1 < lines.length) { + const nextLine = lines[i + 1]; + const containsAdKeyword = adKeywords.some(keyword => + nextLine.toLowerCase().includes(keyword.toLowerCase()) + ); + + if (containsAdKeyword) { + // 跳过 EXTINF 行和 URL 行 + i += 2; + continue; + } + } + } + + // 保留当前行 + filteredLines.push(line); + i++; } return filteredLines.join('\\n'); diff --git a/src/app/api/ad-filter/route.ts b/src/app/api/ad-filter/route.ts index 7de486e..e8f793e 100644 --- a/src/app/api/ad-filter/route.ts +++ b/src/app/api/ad-filter/route.ts @@ -3,6 +3,7 @@ import { NextResponse } from 'next/server'; import { getConfig } from '@/lib/config'; export const runtime = 'nodejs'; +export const dynamic = 'force-dynamic'; // 禁用缓存 /** * GET /api/ad-filter diff --git a/src/app/api/proxy-m3u8/route.ts b/src/app/api/proxy-m3u8/route.ts index de806b6..1c86565 100644 --- a/src/app/api/proxy-m3u8/route.ts +++ b/src/app/api/proxy-m3u8/route.ts @@ -107,37 +107,51 @@ export async function GET(request: Request) { function filterAdsFromM3U8Default(type: string, m3u8Content: string): string { if (!m3u8Content) return ''; + // 广告关键字列表 + const adKeywords = [ + 'sponsor', + '/ad/', + '/ads/', + 'advert', + 'advertisement', + '/adjump', + 'redtraffic' + ]; + // 按行分割M3U8内容 const lines = m3u8Content.split('\n'); const filteredLines = []; - let nextdelete = false; - for (let i = 0; i < lines.length; i++) { + let i = 0; + while (i < lines.length) { const line = lines[i]; - if (nextdelete) { - nextdelete = false; + // 跳过 #EXT-X-DISCONTINUITY 标识 + if (line.includes('#EXT-X-DISCONTINUITY')) { + i++; continue; } - // 只过滤#EXT-X-DISCONTINUITY标识 - if (!line.includes('#EXT-X-DISCONTINUITY')) { - if ( - type === 'ruyi' && - (line.includes('EXTINF:5.640000') || - line.includes('EXTINF:2.960000') || - line.includes('EXTINF:3.480000') || - line.includes('EXTINF:4.000000') || - line.includes('EXTINF:0.960000') || - line.includes('EXTINF:10.000000') || - line.includes('EXTINF:1.266667')) - ) { - nextdelete = true; - continue; - } + // 如果是 EXTINF 行,检查下一行 URL 是否包含广告关键字 + if (line.includes('#EXTINF:')) { + // 检查下一行 URL 是否包含广告关键字 + if (i + 1 < lines.length) { + const nextLine = lines[i + 1]; + const containsAdKeyword = adKeywords.some(keyword => + nextLine.toLowerCase().includes(keyword.toLowerCase()) + ); - filteredLines.push(line); + if (containsAdKeyword) { + // 跳过 EXTINF 行和 URL 行 + i += 2; + continue; + } + } } + + // 保留当前行 + filteredLines.push(line); + i++; } return filteredLines.join('\n'); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 766a5e3..27861cd 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -84,6 +84,7 @@ export default async function RootLayout({ let aiDefaultMessageWithVideo = ''; let enableMovieRequest = true; let webLiveEnabled = false; + let customAdFilterVersion = 0; let customCategories = [] as { name: string; type: 'movie' | 'tv'; @@ -130,6 +131,8 @@ export default async function RootLayout({ enableMovieRequest = config.SiteConfig.EnableMovieRequest ?? true; // 网络直播功能配置 webLiveEnabled = config.WebLiveEnabled ?? false; + // 自定义去广告代码版本号 + customAdFilterVersion = config.SiteConfig?.CustomAdFilterVersion || 0; // 检查是否启用了 OpenList 功能 openListEnabled = !!( config.OpenListConfig?.Enabled && @@ -186,6 +189,7 @@ export default async function RootLayout({ AI_DEFAULT_MESSAGE_WITH_VIDEO: aiDefaultMessageWithVideo, ENABLE_MOVIE_REQUEST: enableMovieRequest, WEB_LIVE_ENABLED: webLiveEnabled, + CUSTOM_AD_FILTER_VERSION: customAdFilterVersion, }; return ( diff --git a/src/app/play/page.tsx b/src/app/play/page.tsx index 22d00de..1f06ca1 100644 --- a/src/app/play/page.tsx +++ b/src/app/play/page.tsx @@ -245,14 +245,8 @@ function PlayPageClient() { console.log('使用缓存的去广告代码'); } - // 第一步:先只获取版本号,检查是否需要更新 - const versionResponse = await fetch('/api/ad-filter'); - if (!versionResponse.ok) { - console.warn('获取去广告代码版本失败,使用缓存'); - return; - } - - const { version } = await versionResponse.json(); + // 从 window.RUNTIME_CONFIG 获取版本号 + const version = (window as any).RUNTIME_CONFIG?.CUSTOM_AD_FILTER_VERSION || 0; // 如果版本号为 0,说明去广告未设置,清空缓存并跳过 if (version === 0) { @@ -267,7 +261,7 @@ function PlayPageClient() { if (!cachedVersion || parseInt(cachedVersion) !== version) { console.log('检测到去广告代码更新(版本 ' + version + '),获取最新代码'); - // 第二步:获取完整代码 + // 获取完整代码 const fullResponse = await fetch('/api/ad-filter?full=true'); if (!fullResponse.ok) { console.warn('获取完整去广告代码失败,使用缓存'); @@ -2499,37 +2493,51 @@ function PlayPageClient() { // 默认去广告规则 if (!m3u8Content) return ''; + // 广告关键字列表 + const adKeywords = [ + 'sponsor', + '/ad/', + '/ads/', + 'advert', + 'advertisement', + '/adjump', + 'redtraffic' + ]; + // 按行分割M3U8内容 const lines = m3u8Content.split('\n'); const filteredLines = []; - let nextdelete = false; - for (let i = 0; i < lines.length; i++) { + let i = 0; + while (i < lines.length) { const line = lines[i]; - if (nextdelete) { - nextdelete = false; + // 跳过 #EXT-X-DISCONTINUITY 标识 + if (line.includes('#EXT-X-DISCONTINUITY')) { + i++; continue; } - // 只过滤#EXT-X-DISCONTINUITY标识 - if (!line.includes('#EXT-X-DISCONTINUITY')) { - if ( - type == 'ruyi' && - (line.includes('EXTINF:5.640000') || - line.includes('EXTINF:2.960000') || - line.includes('EXTINF:3.480000') || - line.includes('EXTINF:4.000000') || - line.includes('EXTINF:0.960000') || - line.includes('EXTINF:10.000000') || - line.includes('EXTINF:1.266667')) - ) { - nextdelete = true; - continue; - } + // 如果是 EXTINF 行,检查下一行 URL 是否包含广告关键字 + if (line.includes('#EXTINF:')) { + // 检查下一行 URL 是否包含广告关键字 + if (i + 1 < lines.length) { + const nextLine = lines[i + 1]; + const containsAdKeyword = adKeywords.some(keyword => + nextLine.toLowerCase().includes(keyword.toLowerCase()) + ); - filteredLines.push(line); + if (containsAdKeyword) { + // 跳过 EXTINF 行和 URL 行 + i += 2; + continue; + } + } } + + // 保留当前行 + filteredLines.push(line); + i++; } return filteredLines.join('\n');