mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-15 00:33:44 +08:00
Reject unsafe dynamic filter execution, reduce heuristic false positives, add regression coverage, bump the app to 4.9.17, and update Next.js to the latest stable security patch.
101 lines
3.2 KiB
TypeScript
101 lines
3.2 KiB
TypeScript
import {
|
|
isSmallIntegerDurationBlock,
|
|
isSmallNtscLikeBlock,
|
|
} from './m3u8-duration-grid';
|
|
import { extractFilename, extractSegmentLocation } from './m3u8-ad-pattern';
|
|
import type { Block, MainPattern } from './m3u8-ad-types';
|
|
|
|
export const AD_PATH_KEYWORDS = [
|
|
'advert', 'preroll', 'midroll', 'postroll',
|
|
'dai', 'vast', 'ima', 'adjump', 'commercial', 'sponsor',
|
|
];
|
|
|
|
function scoreKeywords(block: Block, extraKeywords: string[]): number {
|
|
const keywords = [
|
|
...AD_PATH_KEYWORDS,
|
|
...extraKeywords.filter(keyword => keyword.length > 2),
|
|
];
|
|
|
|
return block.segments.reduce((score, segment) => {
|
|
const url = segment.url.toLowerCase();
|
|
return keywords.some(keyword => url.includes(keyword.toLowerCase()))
|
|
? score + 2.5
|
|
: score;
|
|
}, 0);
|
|
}
|
|
|
|
function scoreFilenameMismatch(block: Block, mainPattern: MainPattern): number {
|
|
if (!mainPattern.filenameRegex || block.segments.length === 0) return 0;
|
|
|
|
const allMismatch = block.segments.every(segment => (
|
|
!mainPattern.filenameRegex?.test(extractFilename(segment.url))
|
|
));
|
|
return allMismatch ? 1.5 : 0;
|
|
}
|
|
|
|
function scoreLocationMismatch(block: Block, mainPattern: MainPattern): number {
|
|
if ((!mainPattern.pathPrefix && !mainPattern.origin) || block.segments.length === 0) {
|
|
return 0;
|
|
}
|
|
|
|
const locations = block.segments.map(segment => extractSegmentLocation(segment.url));
|
|
const allPathsMismatch = locations.every(location => (
|
|
location.pathPrefix !== mainPattern.pathPrefix
|
|
));
|
|
if (allPathsMismatch) return 5;
|
|
|
|
const allOriginsMismatch = locations.every(location => (
|
|
Boolean(mainPattern.origin && location.origin) && location.origin !== mainPattern.origin
|
|
));
|
|
return allOriginsMismatch ? 3.5 : 0;
|
|
}
|
|
|
|
function scoreDurationMismatch(block: Block, mainPattern: MainPattern): number {
|
|
if (mainPattern.avgDuration <= 0 || block.segments.length === 0 || block.segments.length > 6) {
|
|
return 0;
|
|
}
|
|
|
|
const blockAverage = block.segments.reduce((sum, segment) => (
|
|
sum + segment.duration
|
|
), 0) / block.segments.length;
|
|
const durationRatio = blockAverage / mainPattern.avgDuration;
|
|
return durationRatio < 0.6 || durationRatio > 1.8 ? 1.5 : 0;
|
|
}
|
|
|
|
function scoreDurationGrid(block: Block, mainPattern: MainPattern): number {
|
|
if (!mainPattern.usesNtscLikeGrid && isSmallNtscLikeBlock(block.segments)) {
|
|
return 3.5;
|
|
}
|
|
if (mainPattern.usesFilm24LikeGrid && isSmallIntegerDurationBlock(block.segments)) {
|
|
return 3.5;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
export function scoreBlock(
|
|
block: Block,
|
|
mainPattern: MainPattern,
|
|
extraKeywords: string[] = [],
|
|
isDuplicateSignature: boolean = false,
|
|
): number {
|
|
if (block.hasCueTag || isDuplicateSignature) return 10;
|
|
|
|
return scoreKeywords(block, extraKeywords) +
|
|
scoreFilenameMismatch(block, mainPattern) +
|
|
scoreLocationMismatch(block, mainPattern) +
|
|
scoreDurationMismatch(block, mainPattern) +
|
|
scoreDurationGrid(block, mainPattern);
|
|
}
|
|
|
|
export const THRESHOLDS = {
|
|
HIGH: 5,
|
|
LOW: 3,
|
|
};
|
|
|
|
export function shouldFilterBlock(
|
|
score: number,
|
|
threshold: number = THRESHOLDS.HIGH,
|
|
): boolean {
|
|
return score >= threshold;
|
|
}
|