@@ -11,6 +11,14 @@ private enum CompressionStorageDecision {
1111 case compressWithBackup
1212}
1313
14+ private struct CompressionStorageAssessment {
15+ let decision : CompressionStorageDecision
16+ let videoSizeBytes : Int64 ?
17+ let requiredCapacityBytes : Int64 ?
18+ let requiredCapacityWithBackupBytes : Int64 ?
19+ let availableCapacityBytes : Int64 ?
20+ }
21+
1422struct VideoInfoRow : View {
1523 @ObservedObject var videoAttachment : Attachment
1624 @ObservedObject var viewModel : WriterViewModel
@@ -24,6 +32,7 @@ struct VideoInfoRow: View {
2432 @State private var compressionStartedAt : Date ?
2533 @State private var compressionFramesPerSecond : Double ?
2634 @State private var lastLoggedCompressionProgressStep : Int = - 1
35+ @State private var presentableCompressionOptions : [ VideoCompressionJob . Option ] = [ ]
2736 @State private var isCompressing : Bool = false
2837 @State private var isShowingCompressionOptions : Bool = false
2938
@@ -161,19 +170,30 @@ struct VideoInfoRow: View {
161170
162171 @MainActor
163172 private func openCompressionOptions( ) {
164- let assessment = compressionStorageDecision ( for: videoAttachment. path)
165- logStorageAssessment ( assessment, context: " openCompressionOptions " )
166- guard assessment. decision != . blocked else {
173+ let assessments = availableCompressionOptions. map {
174+ ( $0, compressionStorageAssessment ( for: videoAttachment. path, option: $0) )
175+ }
176+ let feasibleOptions = assessments. compactMap { option, assessment in
177+ assessment. decision == . blocked ? nil : option
178+ }
179+ presentableCompressionOptions = feasibleOptions
180+ guard !feasibleOptions. isEmpty else {
181+ let blockingAssessment = assessments
182+ . map ( \. 1 )
183+ . min { lhs, rhs in
184+ ( lhs. requiredCapacityBytes ?? . max) < ( rhs. requiredCapacityBytes ?? . max)
185+ } ?? compressionStorageAssessment ( for: videoAttachment. path, option: nil )
186+ logStorageAssessment ( blockingAssessment, context: " openCompressionOptions " )
167187 log ( " compression options blocked because temporary disk space is insufficient " )
168188 showInsufficientDiskSpaceAlert (
169- videoSizeBytes : assessment . videoSizeBytes ,
170- availableCapacityBytes: assessment . availableCapacityBytes
189+ requiredCapacityBytes : blockingAssessment . requiredCapacityBytes ,
190+ availableCapacityBytes: blockingAssessment . availableCapacityBytes
171191 )
172192 return
173193 }
174194
175195 log (
176- " showing compression options availableOptions= \( availableCompressionOptions . map ( \. id) . joined ( separator: " , " ) ) videoInfoLoaded= \( videoInfo != nil ) "
196+ " showing compression options availableOptions= \( feasibleOptions . map ( \. id) . joined ( separator: " , " ) ) videoInfoLoaded= \( videoInfo != nil ) "
177197 )
178198 isShowingCompressionOptions = true
179199 }
@@ -185,13 +205,13 @@ struct VideoInfoRow: View {
185205 return
186206 }
187207
188- let assessment = compressionStorageDecision ( for: videoAttachment. path)
208+ let assessment = compressionStorageAssessment ( for: videoAttachment. path, option : option )
189209 logStorageAssessment ( assessment, context: " startCompression " )
190210 guard assessment. decision != . blocked else {
191211 isShowingCompressionOptions = false
192212 log ( " compression blocked before starting export " )
193213 showInsufficientDiskSpaceAlert (
194- videoSizeBytes : assessment. videoSizeBytes ,
214+ requiredCapacityBytes : assessment. requiredCapacityBytes ,
195215 availableCapacityBytes: assessment. availableCapacityBytes
196216 )
197217 return
@@ -455,7 +475,7 @@ struct VideoInfoRow: View {
455475 }
456476
457477 VStack ( spacing: 10 ) {
458- ForEach ( availableCompressionOptions ) { option in
478+ ForEach ( presentableCompressionOptions ) { option in
459479 Button {
460480 startCompression ( using: option)
461481 } label: {
@@ -479,27 +499,63 @@ struct VideoInfoRow: View {
479499 . frame ( width: 360 )
480500 }
481501
482- private func compressionStorageDecision(
483- for sourceURL: URL
484- ) -> ( decision: CompressionStorageDecision , videoSizeBytes: Int64 ? , availableCapacityBytes: Int64 ? ) {
485- guard let videoSizeBytes = fileSizeBytes ( for: sourceURL) , videoSizeBytes > 0 else {
486- return ( . compressWithoutBackup, nil , temporaryDirectoryAvailableCapacityBytes ( ) )
502+ private func compressionStorageAssessment(
503+ for sourceURL: URL ,
504+ option: VideoCompressionJob . Option ?
505+ ) -> CompressionStorageAssessment {
506+ let videoSizeBytes = fileSizeBytes ( for: sourceURL)
507+ let requiredCapacityBytes =
508+ option? . estimatedMultipassTemporaryCapacityBytes (
509+ durationSeconds: videoInfo? . durationSecondsValue,
510+ sourceFileSizeBytes: videoSizeBytes
511+ )
512+ ?? videoSizeBytes? . multipliedReportingOverflow ( by: 2 ) . partialValue
513+ let requiredCapacityWithBackupBytes : Int64 ?
514+ if let requiredCapacityBytes, let videoSizeBytes {
515+ let sum = requiredCapacityBytes. addingReportingOverflow ( videoSizeBytes)
516+ requiredCapacityWithBackupBytes = sum. overflow ? Int64 . max : sum. partialValue
517+ } else {
518+ requiredCapacityWithBackupBytes = nil
487519 }
488520
489521 guard let availableCapacityBytes = temporaryDirectoryAvailableCapacityBytes ( ) else {
490- return ( . compressWithoutBackup, videoSizeBytes, nil )
522+ return CompressionStorageAssessment (
523+ decision: . compressWithoutBackup,
524+ videoSizeBytes: videoSizeBytes,
525+ requiredCapacityBytes: requiredCapacityBytes,
526+ requiredCapacityWithBackupBytes: requiredCapacityWithBackupBytes,
527+ availableCapacityBytes: nil
528+ )
491529 }
492530
493- if availableCapacityBytes < videoSizeBytes {
494- return ( . blocked, videoSizeBytes, availableCapacityBytes)
531+ if let requiredCapacityBytes, availableCapacityBytes < requiredCapacityBytes {
532+ return CompressionStorageAssessment (
533+ decision: . blocked,
534+ videoSizeBytes: videoSizeBytes,
535+ requiredCapacityBytes: requiredCapacityBytes,
536+ requiredCapacityWithBackupBytes: requiredCapacityWithBackupBytes,
537+ availableCapacityBytes: availableCapacityBytes
538+ )
495539 }
496540
497- let tenTimesVideoSize = videoSizeBytes. multipliedReportingOverflow ( by: 10 )
498- if !tenTimesVideoSize. overflow, availableCapacityBytes >= tenTimesVideoSize. partialValue {
499- return ( . compressWithBackup, videoSizeBytes, availableCapacityBytes)
541+ if let requiredCapacityWithBackupBytes,
542+ availableCapacityBytes >= requiredCapacityWithBackupBytes {
543+ return CompressionStorageAssessment (
544+ decision: . compressWithBackup,
545+ videoSizeBytes: videoSizeBytes,
546+ requiredCapacityBytes: requiredCapacityBytes,
547+ requiredCapacityWithBackupBytes: requiredCapacityWithBackupBytes,
548+ availableCapacityBytes: availableCapacityBytes
549+ )
500550 }
501551
502- return ( . compressWithoutBackup, videoSizeBytes, availableCapacityBytes)
552+ return CompressionStorageAssessment (
553+ decision: . compressWithoutBackup,
554+ videoSizeBytes: videoSizeBytes,
555+ requiredCapacityBytes: requiredCapacityBytes,
556+ requiredCapacityWithBackupBytes: requiredCapacityWithBackupBytes,
557+ availableCapacityBytes: availableCapacityBytes
558+ )
503559 }
504560
505561 private func fileSizeBytes( for url: URL ) -> Int64 ? {
@@ -567,14 +623,14 @@ struct VideoInfoRow: View {
567623 }
568624
569625 private func showInsufficientDiskSpaceAlert(
570- videoSizeBytes : Int64 ? ,
626+ requiredCapacityBytes : Int64 ? ,
571627 availableCapacityBytes: Int64 ?
572628 ) {
573629 let message : String
574- if let videoSizeBytes , let availableCapacityBytes {
575- message = " Planet needs at least \( formattedByteCount ( videoSizeBytes ) ) of free temporary disk space to compress this video. Only \( formattedByteCount ( availableCapacityBytes) ) is currently available. "
630+ if let requiredCapacityBytes , let availableCapacityBytes {
631+ message = " Planet needs at least \( formattedByteCount ( requiredCapacityBytes ) ) of free temporary disk space to compress this video. Only \( formattedByteCount ( availableCapacityBytes) ) is currently available. "
576632 } else {
577- message = " Planet needs at least as much free temporary disk space as the source video size to compress this video. "
633+ message = " Planet needs more free temporary disk space to compress this video. "
578634 }
579635
580636 PlanetStore . shared. alert (
@@ -674,11 +730,11 @@ struct VideoInfoRow: View {
674730 }
675731
676732 private func logStorageAssessment(
677- _ assessment: ( decision : CompressionStorageDecision , videoSizeBytes : Int64 ? , availableCapacityBytes : Int64 ? ) ,
733+ _ assessment: CompressionStorageAssessment ,
678734 context: String
679735 ) {
680736 log (
681- " \( context) decision= \( describeStorageDecision ( assessment. decision) ) videoSizeBytes= \( formatBytes ( assessment. videoSizeBytes) ) availableCapacityBytes= \( formatBytes ( assessment. availableCapacityBytes) ) "
737+ " \( context) decision= \( describeStorageDecision ( assessment. decision) ) videoSizeBytes= \( formatBytes ( assessment. videoSizeBytes) ) requiredCapacityBytes= \( formatBytes ( assessment . requiredCapacityBytes ) ) requiredCapacityWithBackupBytes= \( formatBytes ( assessment . requiredCapacityWithBackupBytes ) ) availableCapacityBytes=\( formatBytes ( assessment. availableCapacityBytes) ) "
682738 )
683739 }
684740
0 commit comments