@@ -63,24 +63,16 @@ const FPS_OPTIONS = [
63
63
{ label : "60 FPS" , value : 60 } ,
64
64
] satisfies Array < { label : string ; value : number } > ;
65
65
66
+ export interface ExportEstimates {
67
+ duration_seconds : number ;
68
+ estimated_time_seconds : number ;
69
+ estimated_size_mb : number ;
70
+ }
71
+
66
72
export function Header ( ) {
67
73
const currentWindow = getCurrentWindow ( ) ;
68
74
const { videoId, project, prettyName } = useEditorContext ( ) ;
69
75
70
- const [ metadata ] = createResource ( async ( ) => {
71
- const result = await commands . getVideoMetadata ( videoId , null ) . catch ( ( e ) => {
72
- console . error ( `Failed to get metadata: ${ e } ` ) ;
73
- } ) ;
74
- if ( ! result ) return ;
75
-
76
- const { duration, size } = result ;
77
- const estimatedExportTime = Math . ceil ( duration * 1.5 ) ;
78
- console . log (
79
- `Metadata for video: duration=${ duration } , size=${ size } , estimatedExport=${ estimatedExportTime } `
80
- ) ;
81
- return { duration, size, estimatedExportTime } ;
82
- } ) ;
83
-
84
76
const [ showExportOptions , setShowExportOptions ] = createSignal ( false ) ;
85
77
const [ selectedFps , setSelectedFps ] = createSignal (
86
78
Number ( localStorage . getItem ( "cap-export-fps" ) ) || 30
@@ -92,6 +84,24 @@ export function Header() {
92
84
) || RESOLUTION_OPTIONS [ 0 ]
93
85
) ;
94
86
87
+ const [ exportEstimates ] = createResource (
88
+ ( ) => ( {
89
+ videoId,
90
+ resolution : {
91
+ x : selectedResolution ( ) ?. width || RESOLUTION_OPTIONS [ 0 ] . width ,
92
+ y : selectedResolution ( ) ?. height || RESOLUTION_OPTIONS [ 0 ] . height ,
93
+ } ,
94
+ fps : selectedFps ( ) ,
95
+ } ) ,
96
+ async ( params ) => {
97
+ return commands . getExportEstimates (
98
+ params . videoId ,
99
+ params . resolution ,
100
+ params . fps
101
+ ) ;
102
+ }
103
+ ) ;
104
+
95
105
let unlistenTitlebar : UnlistenFn | undefined ;
96
106
onMount ( async ( ) => {
97
107
unlistenTitlebar = await initializeTitlebar ( ) ;
@@ -189,8 +199,8 @@ export function Header() {
189
199
true ,
190
200
selectedFps ( ) ,
191
201
{
192
- x : selectedResolution ( ) . width ,
193
- y : selectedResolution ( ) . height ,
202
+ x : selectedResolution ( ) ?. width || RESOLUTION_OPTIONS [ 0 ] . width ,
203
+ y : selectedResolution ( ) ?. height || RESOLUTION_OPTIONS [ 0 ] . height ,
194
204
}
195
205
) ;
196
206
await commands . copyFileToPath ( videoPath , path ) ;
@@ -287,9 +297,9 @@ export function Header() {
287
297
</ div >
288
298
< div >
289
299
< label class = "block text-sm font-medium mb-1 text-gray-700 dark:text-gray-300" >
290
- Frame Rate
300
+ FPS
291
301
</ label >
292
- < KSelect < ( typeof FPS_OPTIONS ) [ number ] >
302
+ < KSelect
293
303
options = { FPS_OPTIONS }
294
304
optionValue = "value"
295
305
optionTextValue = "label"
@@ -341,8 +351,8 @@ export function Header() {
341
351
>
342
352
Export Video
343
353
</ Button >
344
- < Show when = { metadata ( ) } >
345
- { ( metadata ) => (
354
+ < Show when = { exportEstimates ( ) } >
355
+ { ( est ) => (
346
356
< div
347
357
class = { cx (
348
358
"font-medium z-40 flex justify-between items-center pointer-events-none transition-all max-w-full overflow-hidden text-xs"
@@ -351,21 +361,55 @@ export function Header() {
351
361
< p class = "flex items-center gap-4" >
352
362
< span class = "flex items-center text-[--gray-500]" >
353
363
< IconCapCamera class = "w-[14px] h-[14px] mr-1.5 text-[--gray-500]" />
354
- { Math . floor ( metadata ( ) . duration / 60 ) } :
355
- { Math . floor ( metadata ( ) . duration % 60 )
356
- . toString ( )
357
- . padStart ( 2 , "0" ) }
364
+ { ( ( ) => {
365
+ const totalSeconds = Math . round (
366
+ est ( ) . duration_seconds
367
+ ) ;
368
+ const hours = Math . floor ( totalSeconds / 3600 ) ;
369
+ const minutes = Math . floor (
370
+ ( totalSeconds % 3600 ) / 60
371
+ ) ;
372
+ const seconds = totalSeconds % 60 ;
373
+
374
+ if ( hours > 0 ) {
375
+ return `${ hours } :${ minutes
376
+ . toString ( )
377
+ . padStart ( 2 , "0" ) } :${ seconds
378
+ . toString ( )
379
+ . padStart ( 2 , "0" ) } `;
380
+ }
381
+ return `${ minutes } :${ seconds
382
+ . toString ( )
383
+ . padStart ( 2 , "0" ) } `;
384
+ } ) ( ) }
358
385
</ span >
359
386
< span class = "flex items-center text-[--gray-500]" >
360
387
< IconLucideHardDrive class = "w-[14px] h-[14px] mr-1.5 text-[--gray-500]" />
361
- { metadata ( ) . size . toFixed ( 2 ) } MB
388
+ { est ( ) . estimated_size_mb . toFixed ( 2 ) } MB
362
389
</ span >
363
390
< span class = "flex items-center text-[--gray-500]" >
364
391
< IconLucideClock class = "w-[14px] h-[14px] mr-1.5 text-[--gray-500]" />
365
- ~{ Math . floor ( metadata ( ) . estimatedExportTime / 60 ) } :
366
- { Math . floor ( metadata ( ) . estimatedExportTime % 60 )
367
- . toString ( )
368
- . padStart ( 2 , "0" ) }
392
+ { ( ( ) => {
393
+ const totalSeconds = Math . round (
394
+ est ( ) . estimated_time_seconds
395
+ ) ;
396
+ const hours = Math . floor ( totalSeconds / 3600 ) ;
397
+ const minutes = Math . floor (
398
+ ( totalSeconds % 3600 ) / 60
399
+ ) ;
400
+ const seconds = totalSeconds % 60 ;
401
+
402
+ if ( hours > 0 ) {
403
+ return `~${ hours } :${ minutes
404
+ . toString ( )
405
+ . padStart ( 2 , "0" ) } :${ seconds
406
+ . toString ( )
407
+ . padStart ( 2 , "0" ) } `;
408
+ }
409
+ return `~${ minutes } :${ seconds
410
+ . toString ( )
411
+ . padStart ( 2 , "0" ) } `;
412
+ } ) ( ) }
369
413
</ span >
370
414
</ p >
371
415
</ div >
@@ -652,8 +696,10 @@ function ShareButton(props: ShareButtonProps) {
652
696
true ,
653
697
props . selectedFps ( ) ,
654
698
{
655
- x : props . selectedResolution ( ) . width ,
656
- y : props . selectedResolution ( ) . height ,
699
+ x : props . selectedResolution ( ) ?. width || RESOLUTION_OPTIONS [ 0 ] . width ,
700
+ y :
701
+ props . selectedResolution ( ) ?. height ||
702
+ RESOLUTION_OPTIONS [ 0 ] . height ,
657
703
}
658
704
) ;
659
705
0 commit comments