@@ -72,17 +72,59 @@ class MonochromeSource {
7272 }
7373 /**
7474 * Performs provider-specific resource initialization.
75+ * Runs an initial latency check to score instances.
7576 * @returns A promise resolving to true if initialized.
7677 */
7778 async setup ( ) {
7879 const apiCount = this . apiInstances . length ;
7980 const streamCount = this . streamingInstances . length ;
80- if ( apiCount > 0 ) {
81- logger ( 'info' , 'Monochrome' , `Source is ready with ${ apiCount } API and ${ streamCount } streaming instances.` ) ;
82- return true ;
81+ if ( apiCount === 0 ) {
82+ logger ( 'warn' , 'Monochrome' , 'Source failed to initialize: No instances available.' ) ;
83+ return false ;
84+ }
85+ logger ( 'info' , 'Monochrome' , `Initializing latency check for ${ apiCount } instances...` ) ;
86+ const checkInstance = async ( instance ) => {
87+ const start = Date . now ( ) ;
88+ try {
89+ const { statusCode } = await makeRequest ( instance . url + '/' , {
90+ method : 'GET' ,
91+ timeout : 3000
92+ } ) ;
93+ const latency = Date . now ( ) - start ;
94+ if ( statusCode === 200 || statusCode === 404 || statusCode === 302 ) {
95+ // Success (or at least reachable)
96+ if ( latency < 500 )
97+ instance . score = 100 ;
98+ else if ( latency < 1500 )
99+ instance . score = 80 ;
100+ else if ( latency < 3000 )
101+ instance . score = 50 ;
102+ else
103+ instance . score = 20 ;
104+ logger ( 'debug' , 'Monochrome' , `Instance ${ instance . url } - Latency: ${ latency } ms, Initial Score: ${ instance . score } ` ) ;
105+ }
106+ else {
107+ instance . score = 0 ;
108+ instance . lastFailure = Date . now ( ) ;
109+ logger ( 'debug' , 'Monochrome' , `Instance ${ instance . url } - Error: Status ${ statusCode } , Score: 0` ) ;
110+ }
111+ }
112+ catch ( e ) {
113+ instance . score = 0 ;
114+ instance . lastFailure = Date . now ( ) ;
115+ logger ( 'debug' , 'Monochrome' , `Instance ${ instance . url } - Connection Failed, Score: 0` ) ;
116+ }
117+ } ;
118+ await Promise . allSettled ( this . apiInstances . map ( checkInstance ) ) ;
119+ // Sync streaming scores if they use the same URLs
120+ for ( const s of this . streamingInstances ) {
121+ const api = this . apiInstances . find ( a => a . url === s . url ) ;
122+ if ( api )
123+ s . score = api . score ;
83124 }
84- logger ( 'warn' , 'Monochrome' , 'Source failed to initialize: No instances available.' ) ;
85- return false ;
125+ const reachable = this . apiInstances . filter ( i => i . score > 0 ) . length ;
126+ logger ( 'info' , 'Monochrome' , `Source is ready with ${ apiCount } API (${ reachable } reachable) and ${ streamCount } streaming instances.` ) ;
127+ return true ;
86128 }
87129 /**
88130 * Selects the healthiest instance from the pool using a scored random strategy.
@@ -94,14 +136,15 @@ class MonochromeSource {
94136 getBestInstance ( type = 'api' , minVersion ) {
95137 const pool = type === 'streaming' ? this . streamingInstances : this . apiInstances ;
96138 const now = Date . now ( ) ;
97- let candidates = pool . filter ( ( i ) => ( i . score > 0 || now - i . lastFailure > 30_000 ) &&
139+ let candidates = pool . filter ( ( i ) => ( i . score > 0 || now - i . lastFailure > 60_000 ) &&
98140 ( ! minVersion || ( i . version && i . version >= minVersion ) ) ) ;
99141 if ( candidates . length === 0 && minVersion ) {
100- candidates = pool . filter ( ( i ) => i . score > 0 || now - i . lastFailure > 30_000 ) ;
142+ candidates = pool . filter ( ( i ) => i . score > 0 || now - i . lastFailure > 60_000 ) ;
101143 }
102144 const activePool = candidates . length > 0 ? candidates : pool ;
103145 const sorted = activePool . sort ( ( a , b ) => b . score - a . score || a . activeRequests - b . activeRequests ) ;
104- const instance = sorted [ Math . floor ( Math . random ( ) * Math . min ( sorted . length , 3 ) ) ] || pool [ 0 ] ;
146+ // Increase randomization factor to 5 to avoid overloading the very best one
147+ const instance = sorted [ Math . floor ( Math . random ( ) * Math . min ( sorted . length , 5 ) ) ] || pool [ 0 ] ;
105148 if ( ! instance ) {
106149 throw new Error ( 'No instances available in pool' ) ;
107150 }
@@ -125,6 +168,7 @@ class MonochromeSource {
125168 instance . activeRequests ++ ;
126169 try {
127170 const { body, error, statusCode } = await makeRequest ( url , {
171+ timeout : 5000 ,
128172 headers : {
129173 'User-Agent' : 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36' ,
130174 Accept : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8' ,
0 commit comments