pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add granite subtitle source

Pas b875bc93 d527446e

+77
+1
src/components/player/atoms/settings/CaptionsView.tsx
··· 134 134 "bg-blue-500": props.subtitleSource.includes("wyzie"), 135 135 "bg-orange-500": props.subtitleSource === "opensubs", 136 136 "bg-purple-500": props.subtitleSource === "febbox", 137 + "bg-green-500": props.subtitleSource === "granite", 137 138 }, 138 139 )} 139 140 >
+76
src/utils/externalSubtitles.ts
··· 284 284 } 285 285 } 286 286 287 + export async function scrapeVdrkCaptions( 288 + tmdbId: string | number, 289 + season?: number, 290 + episode?: number, 291 + ): Promise<CaptionListItem[]> { 292 + try { 293 + const tmdbIdNum = 294 + typeof tmdbId === "string" ? parseInt(tmdbId, 10) : tmdbId; 295 + 296 + let url: string; 297 + if (season && episode) { 298 + // For TV shows: https://sub.vdrk.site/v1/tv/{tmdb_id}/{season}/{episode} 299 + url = `https://sub.vdrk.site/v1/tv/${tmdbIdNum}/${season}/${episode}`; 300 + } else { 301 + // For movies: https://sub.vdrk.site/v1/movie/{tmdb_id} 302 + url = `https://sub.vdrk.site/v1/movie/${tmdbIdNum}`; 303 + } 304 + 305 + console.log("Searching VDRK subtitles with URL:", url); 306 + 307 + const response = await fetch(url); 308 + 309 + if (!response.ok) { 310 + throw new Error(`VDRK API returned ${response.status}`); 311 + } 312 + 313 + const data = await response.json(); 314 + 315 + // Check if response is an array 316 + if (!Array.isArray(data)) { 317 + console.log("Invalid VDRK response format"); 318 + return []; 319 + } 320 + 321 + const vdrkCaptions: CaptionListItem[] = []; 322 + 323 + for (const subtitle of data) { 324 + if (subtitle.file && subtitle.label) { 325 + // Parse label to extract language and hearing impaired info 326 + const label = subtitle.label; 327 + const isHearingImpaired = label.includes(" Hi") || label.includes("Hi"); 328 + const languageName = label 329 + .replace(/\s*Hi\d*$/, "") 330 + .replace(/\s*Hi$/, "") 331 + .replace(/\d+$/, ""); 332 + const language = labelToLanguageCode(languageName); 333 + 334 + if (!language) continue; 335 + 336 + vdrkCaptions.push({ 337 + id: subtitle.file, 338 + language, 339 + url: subtitle.file, 340 + type: "vtt", // VDRK provides VTT files 341 + needsProxy: false, 342 + opensubtitles: true, 343 + display: subtitle.label, 344 + isHearingImpaired, 345 + source: "granite", 346 + }); 347 + } 348 + } 349 + 350 + console.log(`Found ${vdrkCaptions.length} VDRK subtitles`); 351 + return vdrkCaptions; 352 + } catch (error) { 353 + console.error("Error fetching VDRK subtitles:", error); 354 + return []; 355 + } 356 + } 357 + 287 358 export async function scrapeExternalSubtitles( 288 359 meta: PlayerMeta, 289 360 ): Promise<CaptionListItem[]> { ··· 310 381 episode, 311 382 ); 312 383 // const febboxPromise = scrapeFebboxCaptions(imdbId, season, episode); 384 + const vdrkPromise = scrapeVdrkCaptions(tmdbId, season, episode); 313 385 314 386 // Create timeout promises 315 387 const timeoutPromise = new Promise<CaptionListItem[]>((resolve) => { ··· 347 419 // handleSourceCompletion("Febbox", captions); 348 420 // return captions; 349 421 // }), 422 + Promise.race([vdrkPromise, timeoutPromise]).then((captions) => { 423 + handleSourceCompletion("Granite", captions); 424 + return captions; 425 + }), 350 426 ]; 351 427 352 428 // Wait for all sources to complete (with timeouts)