@@ -24,6 +24,30 @@ const FONGMI_EPISODE_CLEAN_RULES = [
2424 [ / [ _ ~ . - ] + / g, " " ]
2525] ;
2626
27+ const FONGMI_EPISODE_PATTERNS = [
28+ { pattern : / [ S s ] \d { 1 , 2 } \s * [ E e ] ( \d { 1 , 4 } ) / , group : 1 } , // S01E05 / s1e5
29+ { pattern : / 第 \s * ( [ 零 一 二 三 四 五 六 七 八 九 十 百 零 〇 两 \d ] + ) \s * [ 集 期 话 章 回 段 篇 ] / , group : 1 , chinese : true } , // 第12集 / 第十二期
30+ { pattern : / [ E e ] [ P p ] ? \. ? \s * ( \d { 1 , 4 } ) / , group : 1 } , // EP05 / Ep.5 / E5
31+ { pattern : / [ E e ] ( \d { 1 , 4 } ) (?: \s | $ | \. ) / , group : 1 } , // E05 后面有空格/结尾/点
32+ { pattern : / E p i s o d e \s * ( \d { 1 , 4 } ) / i, group : 1 } , // Episode 5
33+ { pattern : / (?: 正 在 ) ? 播 放 [ : : ] \s * ( \d { 1 , 4 } ) / , group : 1 } , // 播放:5 / 正在播放:12
34+ { pattern : / (?: 第 \s * ) ? [ 零 一 二 三 四 五 六 七 八 九 十 百 \d ] + \s * 季 \s * [ | | ] \s * ( \d { 1 , 4 } ) / , group : 1 } , // 第1季|18 / 第一季|05 (Emby格式)
35+ { pattern : / [ \[ 【 \( ( ] ( \d { 1 , 4 } ) [ \] 】 \) ) ] / , group : 1 } , // [05] / 【05】 / (05)
36+ { pattern : / @ @ @ ( \d { 1 , 4 } ) / , group : 1 } , // 剧名@@@5
37+ { pattern : / (?: ^ | [ \s _ \- ] ) ( \d { 1 , 4 } ) x (?: [ \s _ \- ] | $ ) / i, group : 1 } , // 1x05 / 2-3x12-
38+ { pattern : / \s ( \d { 1 , 4 } ) \. (?: m p 4 | m k v | a v i | m o v | m p 3 | 1 0 8 0 | 7 2 0 | 4 k | 2 k ) / i, group : 1 } , // 剧 05.mp4 / 名 12.1080p
39+ { pattern : / [ _ \- ] ( \d { 1 , 4 } ) (?: \s | $ | \. ) / , group : 1 } , // 剧名_05 / 名-12.
40+ { pattern : / \s ( \d { 1 , 4 } ) \s / , group : 1 } , // 剧 05 名 (空格包围)
41+ { pattern : / \s ( \d { 1 , 4 } ) $ / , group : 1 } , // 剧名 05 (末尾数字)
42+ { pattern : / ^ ( \d { 1 , 4 } ) \s / , group : 1 } , // 05 剧名 (开头数字)
43+ { pattern : / ( \d { 1 , 4 } ) (?: \s | $ ) $ / , group : 1 } // 剧名05 (结尾无空格)
44+ ] ;
45+
46+ const FONGMI_DATE_PATTERNS = [
47+ { pattern : / ( \d { 4 } ) [ . \- \/ 年 ] ( \d { 1 , 2 } ) [ . \- \/ 月 ] ( \d { 1 , 2 } ) / , type : "ymd" } , // 2025-05-13 / 2025.05.13 / 2025年5月13日
48+ { pattern : / ( \d { 4 } ) ( \d { 2 } ) ( \d { 2 } ) / , type : "ymd_compact" } // 20250513 (8位紧凑日期)
49+ ] ;
50+
2751/**
2852 * 规范化 FongMi 传入文本,统一大小写、空格和简繁体。
2953 * @param {string } value 原始文本
@@ -74,10 +98,11 @@ function normalizeFongmiTitleByRegex(name) {
7498 */
7599function normalizeFongmiEpisodeByRegex ( episode ) {
76100 let text = String ( episode || "" ) ;
77- FONGMI_EPISODE_CLEAN_RULES . forEach ( ( [ pattern , replacement ] ) => {
101+ FONGMI_EPISODE_CLEAN_RULES . forEach ( ( [ pattern , replacement ] , i ) => {
78102 text = text . replace ( pattern , replacement ) ;
79103 } ) ;
80- return normalizeSpaces ( text ) ;
104+ const result = text . replace ( / \s + / g, ' ' ) . trim ( ) ;
105+ return result ;
81106}
82107
83108/**
@@ -87,29 +112,47 @@ function normalizeFongmiEpisodeByRegex(episode) {
87112 * @returns {number|null } 集数,未提取到则返回 null
88113 */
89114function extractFongmiEpisodeNumber ( episode ) {
90- const normalizedEpisode = normalizeFongmiEpisodeByRegex ( episode ) ;
91- const commonNum = extractEpisodeNumberFromTitle ( normalizedEpisode ) ;
92- if ( commonNum !== null ) return commonNum ;
93-
94- const seasonEpisodeMatch = normalizedEpisode . match ( / S \d { 1 , 2 } \s * E ( \d { 1 , 4 } ) / i) ;
95- if ( seasonEpisodeMatch ) {
96- return parseInt ( seasonEpisodeMatch [ 1 ] , 10 ) ;
97- }
98-
99- const chineseEpisodeMatch = normalizedEpisode . match ( / 第 \s * ( [ 一 二 三 四 五 六 七 八 九 十 百 零 〇 两 \d ] + ) \s * (?: 集 | 话 | 期 | 回 | 章 ) / ) ;
100- if ( chineseEpisodeMatch ) {
101- return convertChineseNumber ( chineseEpisodeMatch [ 1 ] ) ;
115+ if ( ! episode ) return null ;
116+ const text = String ( episode ) ;
117+
118+ for ( const { pattern, group, chinese } of FONGMI_EPISODE_PATTERNS ) {
119+ const match = text . match ( pattern ) ;
120+ if ( match && match [ group ] !== undefined ) {
121+ const value = match [ group ] ;
122+ if ( chinese ) {
123+ const num = convertChineseNumber ( value ) ;
124+ if ( num !== null && ! isNaN ( num ) ) return num ;
125+ } else {
126+ const num = parseInt ( value , 10 ) ;
127+ if ( ! isNaN ( num ) && num > 0 && num < 10000 ) return num ;
128+ }
129+ }
102130 }
103131
104- const xEpisodeMatch = normalizedEpisode . match ( / (?: ^ | \s ) ( \d { 1 , 4 } ) x (?: \s | $ ) / i) ;
105- if ( xEpisodeMatch ) {
106- return parseInt ( xEpisodeMatch [ 1 ] , 10 ) ;
132+ for ( const { pattern, type } of FONGMI_DATE_PATTERNS ) {
133+ const match = text . match ( pattern ) ;
134+ if ( match ) {
135+ if ( type === "ymd" ) {
136+ const y = parseInt ( match [ 1 ] , 10 ) ;
137+ const m = parseInt ( match [ 2 ] , 10 ) ;
138+ const d = parseInt ( match [ 3 ] , 10 ) ;
139+ if ( y >= 2000 && y <= 2099 && m >= 1 && m <= 12 && d >= 1 && d <= 31 ) {
140+ return y * 10000 + m * 100 + d ;
141+ }
142+ } else if ( type === "ymd_compact" ) {
143+ const y = parseInt ( match [ 1 ] , 10 ) ;
144+ const m = parseInt ( match [ 2 ] , 10 ) ;
145+ const d = parseInt ( match [ 3 ] , 10 ) ;
146+ if ( y >= 2000 && y <= 2099 && m >= 1 && m <= 12 && d >= 1 && d <= 31 ) {
147+ return y * 10000 + m * 100 + d ;
148+ }
149+ }
150+ }
107151 }
108152
109- const standaloneEpisodeMatch = normalizedEpisode . match ( / (?: ^ | \s ) ( \d { 1 , 4 } ) (?: \s | $ ) / ) ;
110- if ( standaloneEpisodeMatch ) {
111- return parseInt ( standaloneEpisodeMatch [ 1 ] , 10 ) ;
112- }
153+ const normalizedEpisode = normalizeFongmiEpisodeByRegex ( episode ) ;
154+ const commonNum = extractEpisodeNumberFromTitle ( normalizedEpisode ) ;
155+ if ( commonNum !== null ) return commonNum ;
113156
114157 return null ;
115158}
@@ -362,7 +405,7 @@ export async function getFongmiDanmaku(url, req) {
362405 name : `${ candidate . anime . animeTitle } - ${ candidate . episode . episodeTitle } ` ,
363406 url : candidate . commentUrl
364407 } ) ;
365- if ( items . length >= 12 ) break ;
408+ if ( items . length >= 1000 ) break ;
366409 }
367410
368411 log ( "info" , `[Fongmi] name=${ name } , episode=${ episode } , candidates=${ items . length } ` ) ;
0 commit comments