@@ -20,8 +20,78 @@ import { stringOrNumberOptional } from "@/utils/zod";
2020import { withAuth } from "../../utils" ;
2121import { parseVideoIdOrFileKey } from "../utils" ;
2222
23+ function contentTypeForSubpath ( subpath : string ) : string {
24+ if ( subpath . endsWith ( ".json" ) ) return "application/json" ;
25+ if ( subpath . endsWith ( ".mp4" ) || subpath . endsWith ( ".m4s" ) ) return "video/mp4" ;
26+ if ( subpath . endsWith ( ".jpg" ) || subpath . endsWith ( ".jpeg" ) )
27+ return "image/jpeg" ;
28+ if ( subpath . endsWith ( ".aac" ) ) return "audio/aac" ;
29+ if ( subpath . endsWith ( ".webm" ) ) return "audio/webm" ;
30+ if ( subpath . endsWith ( ".m3u8" ) ) return "application/x-mpegURL" ;
31+ return "application/octet-stream" ;
32+ }
33+
2334export const app = new Hono ( ) . use ( withAuth ) ;
2435
36+ app . post (
37+ "/batch" ,
38+ zValidator (
39+ "json" ,
40+ z . object ( {
41+ videoId : z . string ( ) ,
42+ subpaths : z
43+ . array (
44+ z
45+ . string ( )
46+ . refine (
47+ ( s ) => ! s . includes ( ".." ) && ! s . startsWith ( "/" ) ,
48+ "Invalid subpath" ,
49+ ) ,
50+ )
51+ . min ( 1 )
52+ . max ( 50 ) ,
53+ } ) ,
54+ ) ,
55+ async ( c ) => {
56+ const user = c . get ( "user" ) ;
57+ const { videoId, subpaths } = c . req . valid ( "json" ) ;
58+
59+ try {
60+ const [ customBucket ] = await db ( )
61+ . select ( )
62+ . from ( Db . s3Buckets )
63+ . where ( eq ( Db . s3Buckets . ownerId , user . id ) ) ;
64+
65+ const urls = await Effect . gen ( function * ( ) {
66+ const [ bucket ] = yield * S3Buckets . getBucketAccess (
67+ Option . fromNullable ( customBucket ?. id ) ,
68+ ) ;
69+
70+ const entries = yield * Effect . all (
71+ subpaths . map ( ( subpath ) => {
72+ const fileKey = `${ user . id } /${ videoId } /${ subpath } ` ;
73+ return bucket
74+ . getPresignedPutUrl (
75+ fileKey ,
76+ { ContentType : contentTypeForSubpath ( subpath ) } ,
77+ { expiresIn : 1800 } ,
78+ )
79+ . pipe ( Effect . map ( ( url ) => [ subpath , url ] as const ) ) ;
80+ } ) ,
81+ { concurrency : "unbounded" } ,
82+ ) ;
83+
84+ return Object . fromEntries ( entries ) ;
85+ } ) . pipe ( runPromise ) ;
86+
87+ return c . json ( { urls } ) ;
88+ } catch ( error ) {
89+ console . error ( "Batch signed URL generation failed:" , error ) ;
90+ return c . json ( { error : "Internal server error" } , 500 ) ;
91+ }
92+ } ,
93+ ) ;
94+
2595app . post (
2696 "/" ,
2797 zValidator (
0 commit comments