11import { randomUUID } from "node:crypto" ;
22import { type NextRequest , NextResponse } from "next/server" ;
3+ import {
4+ fetchConvertedVideoViaMediaServer ,
5+ isMediaServerConfigured ,
6+ } from "@/lib/media-client" ;
37import { convertRemoteVideoToMp4Buffer } from "@/lib/video-convert" ;
48
59function isHlsUrl ( url : string ) : boolean {
@@ -14,6 +18,24 @@ function isStreamingUrl(url: string): boolean {
1418 return isHlsUrl ( url ) || isMpdUrl ( url ) ;
1519}
1620
21+ function getInputExtension ( url : string ) : string | undefined {
22+ const pathname = new URL ( url ) . pathname . toLowerCase ( ) ;
23+
24+ if ( pathname . endsWith ( ".m3u8" ) ) {
25+ return ".m3u8" ;
26+ }
27+
28+ if ( pathname . endsWith ( ".mpd" ) ) {
29+ return ".mpd" ;
30+ }
31+
32+ if ( pathname . endsWith ( ".mp4" ) ) {
33+ return ".mp4" ;
34+ }
35+
36+ return undefined ;
37+ }
38+
1739async function fetchLoomCdnUrl (
1840 videoId : string ,
1941 endpoint : string ,
@@ -226,6 +248,51 @@ export async function GET(request: NextRequest) {
226248 ) ;
227249 if ( mp4Result ) return mp4Result ;
228250
251+ if ( isMediaServerConfigured ( ) ) {
252+ try {
253+ const convertedResponse = await fetchConvertedVideoViaMediaServer (
254+ cdnUrl ,
255+ getInputExtension ( cdnUrl ) ,
256+ ) ;
257+
258+ if ( convertedResponse . ok && convertedResponse . body ) {
259+ return new NextResponse ( convertedResponse . body , {
260+ headers : {
261+ "Content-Type" : "video/mp4" ,
262+ "Content-Disposition" : `attachment; filename="${ mp4Filename } "` ,
263+ "Cache-Control" : "no-store" ,
264+ } ,
265+ } ) ;
266+ }
267+
268+ let errorMessage = "Failed to convert streaming video" ;
269+ try {
270+ const errorData = ( await convertedResponse . json ( ) ) as {
271+ error ?: string ;
272+ details ?: string ;
273+ } ;
274+ errorMessage =
275+ errorData . details ||
276+ errorData . error ||
277+ "Failed to convert streaming video" ;
278+ } catch { }
279+
280+ throw new Error ( errorMessage ) ;
281+ } catch ( error ) {
282+ if ( ! errorMessageShouldFallback ( error ) ) {
283+ return NextResponse . json (
284+ {
285+ error :
286+ error instanceof Error
287+ ? error . message
288+ : "Failed to convert streaming video" ,
289+ } ,
290+ { status : 502 } ,
291+ ) ;
292+ }
293+ }
294+ }
295+
229296 try {
230297 const mp4Buffer = await convertRemoteVideoToMp4Buffer ( cdnUrl ) ;
231298
@@ -248,3 +315,11 @@ export async function GET(request: NextRequest) {
248315 ) ;
249316 }
250317}
318+
319+ function errorMessageShouldFallback ( error : unknown ) : boolean {
320+ if ( ! ( error instanceof Error ) ) {
321+ return true ;
322+ }
323+
324+ return error . message === "MEDIA_SERVER_URL is not configured" ;
325+ }
0 commit comments