Skip to content

Commit 39a5e2e

Browse files
committed
update readme with file handling workarounds
Closes #37
1 parent 0b976f4 commit 39a5e2e

1 file changed

Lines changed: 98 additions & 0 deletions

File tree

README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,26 @@ defer { await UIApplication.shared.endBackgroundTask(taskID) }
374374
try await exporter.export()
375375
```
376376

377+
### Working with Photos Library
378+
379+
When exporting videos from the user's photo library, copy the file to your app's directory first to avoid permission issues:
380+
381+
```swift
382+
// ⚠️ NOT RECOMMENDED: Direct PHAsset access may cause cancelled errors
383+
let phAsset = // ... from photo library
384+
let avAsset = AVAsset(url: phAsset.url) // May fail!
385+
386+
// ✅ RECOMMENDED: Copy to app directory first
387+
let tempURL = FileManager.default.temporaryDirectory
388+
.appendingPathComponent("video.mov")
389+
390+
// Export PHAsset to temp file, then create AVAsset
391+
let avAsset = AVAsset(url: tempURL)
392+
let exporter = NextLevelSessionExporter(withAsset: avAsset)
393+
```
394+
395+
See the [Troubleshooting section](#export-fails-with-cancelled-error-issue-37) for complete implementation.
396+
377397
## Troubleshooting
378398

379399
### Error -11819 "Cannot Complete Action" (iOS 14.5+)
@@ -417,6 +437,83 @@ func exportWithRetry(maxAttempts: Int = 3) async throws -> URL {
417437
- [Apple Forums Thread](https://developer.apple.com/forums/thread/679862)
418438
- [Radar: FB8815719](https://openradar.appspot.com/FB8815719)
419439

440+
### Export Fails with "Cancelled" Error (Issue #37)
441+
442+
**Problem:** Some videos fail to compress with a cancelled/canceled error message, especially when selecting videos directly from the photo library.
443+
444+
**Cause:** File access permissions or buffering issues when reading from certain storage locations.
445+
446+
**Solution:** Copy the video to your app's writable directory before exporting:
447+
448+
```swift
449+
func exportVideoFromLibrary(asset: PHAsset) async throws -> URL {
450+
// 1. Export to temporary file first
451+
let tempURL = FileManager.default.temporaryDirectory
452+
.appendingPathComponent(UUID().uuidString)
453+
.appendingPathExtension("mov")
454+
455+
// 2. Request video resource from Photos library
456+
let options = PHVideoRequestOptions()
457+
options.version = .current
458+
options.deliveryMode = .highQualityFormat
459+
460+
try await withCheckedThrowingContinuation { continuation in
461+
PHImageManager.default().requestExportSession(
462+
forVideo: asset,
463+
options: options,
464+
exportPreset: AVAssetExportPresetPassthrough
465+
) { exportSession, _ in
466+
guard let session = exportSession else {
467+
continuation.resume(throwing: NSError(domain: "Export", code: -1))
468+
return
469+
}
470+
471+
session.outputURL = tempURL
472+
session.outputFileType = .mov
473+
session.exportAsynchronously {
474+
if session.status == .completed {
475+
continuation.resume(returning: ())
476+
} else {
477+
continuation.resume(throwing: session.error ?? NSError(domain: "Export", code: -1))
478+
}
479+
}
480+
}
481+
}
482+
483+
// 3. Now export with NextLevelSessionExporter
484+
let avAsset = AVAsset(url: tempURL)
485+
let exporter = NextLevelSessionExporter(withAsset: avAsset)
486+
487+
let outputURL = FileManager.default.temporaryDirectory
488+
.appendingPathComponent(UUID().uuidString)
489+
.appendingPathExtension("mp4")
490+
491+
exporter.outputURL = outputURL
492+
exporter.videoOutputConfiguration = [/* your config */]
493+
exporter.audioOutputConfiguration = [/* your config */]
494+
495+
let result = try await exporter.export()
496+
497+
// 4. Clean up temp file
498+
try? FileManager.default.removeItem(at: tempURL)
499+
500+
return result
501+
}
502+
```
503+
504+
**Alternative (simpler):** Use `AVAsset(url:)` with a file URL rather than `PHAsset` directly:
505+
506+
```swift
507+
// Copy to caches directory first
508+
let cacheURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
509+
.appendingPathComponent("video.mov")
510+
511+
// ... copy file to cacheURL ...
512+
513+
let asset = AVAsset(url: cacheURL)
514+
let exporter = NextLevelSessionExporter(withAsset: asset)
515+
```
516+
420517
### Export Fails with "Reading Failure"
421518

422519
**Problem:** Export fails when reading the source asset.
@@ -425,6 +522,7 @@ func exportWithRetry(maxAttempts: Int = 3) async throws -> URL {
425522
- Verify the source asset is not corrupted
426523
- Check that the asset is a supported format (MP4, MOV, M4V, etc.)
427524
- Ensure the asset is accessible and not protected by DRM
525+
- If reading from Photos library, see "Cancelled Error" above
428526

429527
### Memory Issues on Long Videos
430528

0 commit comments

Comments
 (0)