@@ -206,15 +206,28 @@ function normalizeSource(item, endpoint, userAgent, request) {
206206 const explicitFormat = cleanText ( item . format ) ?. toLowerCase ( ) ;
207207 const direct = lower . endsWith ( ".m3u8" ) || lower . endsWith ( ".mp4" ) || explicitFormat === "m3u8" || explicitFormat === "mp4" ;
208208 if ( ! direct ) return null ;
209+
209210 const label = cleanText ( item . source_name ?? item . name ?? item . label ) ?? "Purstream" ;
210- const language = parseLanguage ( label ) ;
211- const quality = parseQuality ( label ) ;
211+ const declaredQuality = cleanText ( item . quality ?? item . resolution ) ;
212+ const declaredLanguage = cleanText ( item . language ?? item . lang ?? item . audio_language ) ;
213+ const declaredCodec = cleanText ( item . codec ?? item . video_codec ?? item . videoCodec ) ;
214+ const declaredAudio = cleanText ( item . audio ?? item . audio_codec ?? item . audioCodec ) ;
215+ const declaredDuration = item . duration ?? item . duration_minutes ?? item . durationMinutes ?? item . runtime ?? null ;
216+ const declaredSourceType = cleanText ( item . source_type ?? item . sourceType ?? item . release_type ?? item . releaseType ) ;
217+ const declaredAgeRating = cleanText ( item . age_rating ?? item . ageRating ?? item . certification ) ;
218+
212219 return {
213- title : `Purstream ${ quality } | ${ language } ` ,
220+ // Purstream supplies facts only. The Core owns all user-facing presentation.
221+ title : "Purstream" ,
214222 name : "Purstream" ,
215223 url,
216- quality,
217- language,
224+ quality : declaredQuality ?? parseQuality ( label ) ,
225+ language : declaredLanguage ?? parseLanguage ( label ) ,
226+ codec : declaredCodec ?? parseCodec ( label ) ,
227+ audio : declaredAudio ?? parseAudio ( label ) ,
228+ duration : declaredDuration ,
229+ sourceType : declaredSourceType ?? parseSourceType ( label ) ,
230+ ageRating : declaredAgeRating ,
218231 format : lower . endsWith ( ".mp4" ) || explicitFormat === "mp4" ? "mp4" : "m3u8" ,
219232 headers : {
220233 "User-Agent" : userAgent ,
@@ -231,25 +244,62 @@ function parseLanguage(value) {
231244 if ( text . includes ( "VOSTFR" ) ) return "VOSTFR" ;
232245 if ( text . includes ( "VFQ" ) ) return "VFQ" ;
233246 if ( text . includes ( "VFF" ) ) return "VFF" ;
234- if ( text . includes ( "VF" ) ) return "VF" ;
247+ if ( / \b V F \b / . test ( text ) ) return "VF" ;
235248 if ( text . includes ( "MULTI" ) || text . includes ( "DUAL" ) ) return "MULTI" ;
236- return "MULTI" ;
249+ if ( / \b V O \b / . test ( text ) ) return "VO" ;
250+ return null ;
237251}
238252
239253function parseQuality ( value ) {
240254 const text = String ( value ?? "" ) . toUpperCase ( ) ;
241- if ( text . includes ( "2160" ) || text . includes ( "4K" ) ) return "4K" ;
255+ if ( text . includes ( "2160" ) || / \b 4 K \b / . test ( text ) || text . includes ( "UHD" ) ) return "4K" ;
256+ if ( text . includes ( "1440" ) ) return "1440p" ;
242257 if ( text . includes ( "1080" ) ) return "1080p" ;
243258 if ( text . includes ( "720" ) ) return "720p" ;
259+ if ( text . includes ( "576" ) ) return "576p" ;
244260 if ( text . includes ( "480" ) ) return "480p" ;
245- return "HD" ;
261+ return null ;
262+ }
263+
264+ function parseCodec ( value ) {
265+ const text = String ( value ?? "" ) . toUpperCase ( ) ;
266+ if ( / \b (?: H E V C | H \. ? 2 6 5 | X 2 6 5 ) \b / . test ( text ) ) return "HEVC" ;
267+ if ( / \b (?: A V C | H \. ? 2 6 4 | X 2 6 4 ) \b / . test ( text ) ) return "H.264" ;
268+ if ( / \b A V 1 \b / . test ( text ) ) return "AV1" ;
269+ if ( / \b V P 9 \b / . test ( text ) ) return "VP9" ;
270+ return null ;
271+ }
272+
273+ function parseAudio ( value ) {
274+ const text = String ( value ?? "" ) . toUpperCase ( ) ;
275+ const codec = text . match ( / \b (?: E - ? A C - ? 3 | D D P | D D \+ | A C - ? 3 | A A C | D T S (?: - H D ) ? | T R U E H D | A T M O S | F L A C | O P U S ) \b / ) ;
276+ const channels = text . match ( / \b (?: 7 \. 1 | 5 \. 1 | 2 \. 0 | 2 \. 1 | 1 \. 0 ) \b / ) ;
277+ if ( ! codec && ! channels ) return null ;
278+ let name = codec ?. [ 0 ] ?? "" ;
279+ name = name . replace ( / ^ D D P $ / i, "E-AC3" ) . replace ( / ^ D D \+ $ / i, "E-AC3" ) . replace ( / ^ E - ? A C - ? 3 $ / i, "E-AC3" ) . replace ( / ^ A C - ? 3 $ / i, "AC3" ) ;
280+ return [ name , channels ?. [ 0 ] ] . filter ( Boolean ) . join ( " " ) ;
281+ }
282+
283+ function parseSourceType ( value ) {
284+ const text = String ( value ?? "" ) . toUpperCase ( ) ;
285+ if ( / B L U [ . _ - ] ? R A Y | B D R I P | B R R I P / . test ( text ) ) return "BLU-RAY" ;
286+ if ( / W E B [ . _ - ] ? D L / . test ( text ) ) return "WEB-DL" ;
287+ if ( / W E B [ . _ - ] ? R I P / . test ( text ) ) return "WEBRIP" ;
288+ if ( / \b H D T V \b / . test ( text ) ) return "HDTV" ;
289+ if ( / \b D V D (?: R I P ) ? \b / . test ( text ) ) return "DVD" ;
290+ if ( / \b C A M \b | \b T E L E S Y N C \b / . test ( text ) ) return "CAM" ;
291+ return null ;
246292}
247293
248294async function resolveMetadata ( request , resolver ) {
249295 const base = {
250296 title : cleanText ( request . title ) ,
251297 aliases : [ ] ,
252298 year : asYear ( request . year ) ,
299+ runtime : null ,
300+ durationMinutes : null ,
301+ certification : null ,
302+ ageRating : null ,
253303 } ;
254304 if ( ! resolver ) {
255305 if ( ! base . title ) throw new Error ( "Purstream search requires title or metadataResolver" ) ;
@@ -260,6 +310,11 @@ async function resolveMetadata(request, resolver) {
260310 title : cleanText ( resolved ?. title ?? resolved ?. fr ?? base . title ) ,
261311 aliases : unique ( [ ...( resolved ?. aliases ?? [ ] ) , resolved ?. originalTitle , resolved ?. orig , base . title ] . map ( cleanText ) . filter ( Boolean ) ) ,
262312 year : asYear ( resolved ?. year ?? base . year ) ,
313+ runtime : resolved ?. runtime ?? resolved ?. duration ?? null ,
314+ durationMinutes : resolved ?. durationMinutes ?? resolved ?. duration_minutes ?? null ,
315+ certification : cleanText ( resolved ?. certification ?? resolved ?. contentRating ?? resolved ?. content_rating ) ,
316+ ageRating : cleanText ( resolved ?. ageRating ?? resolved ?. age_rating ) ,
317+ releaseDate : cleanText ( resolved ?. releaseDate ?? resolved ?. release_date ?? resolved ?. firstAirDate ?? resolved ?. first_air_date ) ,
263318 } ;
264319}
265320
0 commit comments