1- import { Body , Controller , Get , Param , Post , Req , Res } from '@nestjs/common' ;
1+ import {
2+ Body ,
3+ Controller ,
4+ Get ,
5+ Param ,
6+ Post ,
7+ Query ,
8+ Req ,
9+ Res ,
10+ StreamableFile ,
11+ } from '@nestjs/common' ;
212import { ApiTags } from '@nestjs/swagger' ;
313import { AgenciesService } from '@gitroom/nestjs-libraries/database/prisma/agencies/agencies.service' ;
414import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service' ;
@@ -11,6 +21,10 @@ import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
1121import { getCookieUrlFromDomain } from '@gitroom/helpers/subdomain/subdomain.management' ;
1222import { AgentGraphInsertService } from '@gitroom/nestjs-libraries/agent/agent.graph.insert.service' ;
1323import { Nowpayments } from '@gitroom/nestjs-libraries/crypto/nowpayments' ;
24+ import { Readable , pipeline } from 'stream' ;
25+ import { promisify } from 'util' ;
26+
27+ const pump = promisify ( pipeline ) ;
1428
1529@ApiTags ( 'Public' )
1630@Controller ( '/public' )
@@ -136,4 +150,46 @@ export class PublicController {
136150 console . log ( 'cryptoPost' , body , path ) ;
137151 return this . _nowpayments . processPayment ( path , body ) ;
138152 }
153+
154+ @Get ( '/stream' )
155+ async streamFile (
156+ @Query ( 'url' ) url : string ,
157+ @Res ( ) res : Response ,
158+ @Req ( ) req : Request
159+ ) {
160+ if ( ! url . endsWith ( 'mp4' ) ) {
161+ return res . status ( 400 ) . send ( 'Invalid video URL' ) ;
162+ }
163+
164+ const ac = new AbortController ( ) ;
165+ const onClose = ( ) => ac . abort ( ) ;
166+ req . on ( 'aborted' , onClose ) ;
167+ res . on ( 'close' , onClose ) ;
168+
169+ const r = await fetch ( url , { signal : ac . signal } ) ;
170+
171+ if ( ! r . ok && r . status !== 206 ) {
172+ res . status ( r . status ) ;
173+ throw new Error ( `Upstream error: ${ r . statusText } ` ) ;
174+ }
175+
176+ const type = r . headers . get ( 'content-type' ) ?? 'application/octet-stream' ;
177+ res . setHeader ( 'Content-Type' , type ) ;
178+
179+ const contentRange = r . headers . get ( 'content-range' ) ;
180+ if ( contentRange ) res . setHeader ( 'Content-Range' , contentRange ) ;
181+
182+ const len = r . headers . get ( 'content-length' ) ;
183+ if ( len ) res . setHeader ( 'Content-Length' , len ) ;
184+
185+ const acceptRanges = r . headers . get ( 'accept-ranges' ) ?? 'bytes' ;
186+ res . setHeader ( 'Accept-Ranges' , acceptRanges ) ;
187+
188+ if ( r . status === 206 ) res . status ( 206 ) ; // Partial Content for range responses
189+
190+ try {
191+ await pump ( Readable . fromWeb ( r . body as any ) , res ) ;
192+ } catch ( err ) {
193+ }
194+ }
139195}
0 commit comments