mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-24 01:01:00 +00:00
Save media
This commit is contained in:
parent
493ea98f13
commit
2eddf8c558
6 changed files with 112 additions and 4 deletions
54
Extensions/AVURLAsset+Extensions.swift
Normal file
54
Extensions/AVURLAsset+Extensions.swift
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright © 2021 Metabolist. All rights reserved.
|
||||
|
||||
import AVFoundation
|
||||
|
||||
enum AssetExportError: Error {
|
||||
case exportSetup
|
||||
case export
|
||||
}
|
||||
|
||||
extension AVURLAsset {
|
||||
func exportWithoutAudioTrack(completion: @escaping ((Result<URL, AssetExportError>) -> Void)) {
|
||||
let composition = AVMutableComposition()
|
||||
let exportDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
||||
|
||||
guard let sourceVideoTrack = tracks(withMediaType: .video).first,
|
||||
let compositionVideoTrack = composition.addMutableTrack(
|
||||
withMediaType: .video,
|
||||
preferredTrackID: kCMPersistentTrackID_Invalid),
|
||||
case .success = Result(catching: {
|
||||
try compositionVideoTrack.insertTimeRange(
|
||||
CMTimeRange(start: .zero, duration: duration),
|
||||
of: sourceVideoTrack, at: .zero)
|
||||
}),
|
||||
let exportSession = AVAssetExportSession(
|
||||
asset: composition,
|
||||
presetName: AVAssetExportPresetHighestQuality),
|
||||
exportSession.supportedFileTypes.contains(.mp4),
|
||||
case .success = Result(catching: {
|
||||
try FileManager.default.createDirectory(
|
||||
at: exportDirectory,
|
||||
withIntermediateDirectories: false)
|
||||
})
|
||||
else {
|
||||
completion(.failure(.exportSetup))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let exportURL = exportDirectory.appendingPathComponent(url.lastPathComponent)
|
||||
|
||||
exportSession.outputFileType = AVFileType.mp4
|
||||
exportSession.outputURL = exportURL
|
||||
exportSession.timeRange = CMTimeRange(start: .zero, duration: duration)
|
||||
exportSession.exportAsynchronously {
|
||||
guard exportSession.status == .completed else {
|
||||
completion(.failure(.export))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
completion(.success(exportURL))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,6 +39,7 @@
|
|||
"attachment.edit.thumbnail.prompt" = "Drag the circle on the preview to choose the focal point which will always be in view on all thumbnails";
|
||||
"attachment.sensitive-content" = "Sensitive content";
|
||||
"attachment.media-hidden" = "Media hidden";
|
||||
"attachment.unable-to-export-media" = "Unable to export media";
|
||||
"bookmarks" = "Bookmarks";
|
||||
"camera-access.title" = "Camera access needed";
|
||||
"camera-access.description" = "Open system settings to allow camera access";
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
D059373E25AB8D5200754FDF /* CompositionPollOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D059373D25AB8D5200754FDF /* CompositionPollOptionView.swift */; };
|
||||
D059373F25AB8D5200754FDF /* CompositionPollOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D059373D25AB8D5200754FDF /* CompositionPollOptionView.swift */; };
|
||||
D059376125ABE2E800754FDF /* XMLUnescaper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D059376025ABE2E800754FDF /* XMLUnescaper.swift */; };
|
||||
D05E688525B55AE8001FB2C6 /* AVURLAsset+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05E688425B55AE8001FB2C6 /* AVURLAsset+Extensions.swift */; };
|
||||
D0625E59250F092900502611 /* StatusListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0625E58250F092900502611 /* StatusListCell.swift */; };
|
||||
D0625E5D250F0B5C00502611 /* StatusContentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0625E5C250F0B5C00502611 /* StatusContentConfiguration.swift */; };
|
||||
D06BC5E625202AD90079541D /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06BC5E525202AD90079541D /* ProfileViewController.swift */; };
|
||||
|
@ -218,6 +219,7 @@
|
|||
D059373225AAEA7000754FDF /* CompositionPollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionPollView.swift; sourceTree = "<group>"; };
|
||||
D059373D25AB8D5200754FDF /* CompositionPollOptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionPollOptionView.swift; sourceTree = "<group>"; };
|
||||
D059376025ABE2E800754FDF /* XMLUnescaper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLUnescaper.swift; sourceTree = "<group>"; };
|
||||
D05E688425B55AE8001FB2C6 /* AVURLAsset+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVURLAsset+Extensions.swift"; sourceTree = "<group>"; };
|
||||
D0625E58250F092900502611 /* StatusListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusListCell.swift; sourceTree = "<group>"; };
|
||||
D0625E5C250F0B5C00502611 /* StatusContentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentConfiguration.swift; sourceTree = "<group>"; };
|
||||
D0666A2124C677B400F3F04B /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -569,6 +571,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
D01C6FAB252024BD003D0300 /* Array+Extensions.swift */,
|
||||
D05E688425B55AE8001FB2C6 /* AVURLAsset+Extensions.swift */,
|
||||
D0F0B135251AA12700942152 /* CollectionItem+Extensions.swift */,
|
||||
D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */,
|
||||
D0C7D46B24F76169001EBDBB /* NSMutableAttributedString+Extensions.swift */,
|
||||
|
@ -834,6 +837,7 @@
|
|||
D0FE1C8F253686F9003EF1EB /* PlayerView.swift in Sources */,
|
||||
D0CE9F87258B076900E3A6B6 /* AttachmentUploadView.swift in Sources */,
|
||||
D0F0B113251A86A000942152 /* AccountContentConfiguration.swift in Sources */,
|
||||
D05E688525B55AE8001FB2C6 /* AVURLAsset+Extensions.swift in Sources */,
|
||||
D036AA02254B6101009094DF /* NotificationListCell.swift in Sources */,
|
||||
D08B8D42253F92B600B1EBEF /* ImagePageViewController.swift in Sources */,
|
||||
D01F41D924F880C400D55A2D /* TouchFallthroughTextView.swift in Sources */,
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Enables Metatext to take videos and add them to your posts.</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Enables Metatext to take photos and videos and add them to your posts.</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
|
@ -24,6 +20,14 @@
|
|||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Enables Metatext to take photos and videos and add them to your posts</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Enables Metatext to take videos and add them to your posts</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Enables Metatext to add items to your Photo Library</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Enables Metatext to access photos from your library and add them to your posts</string>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
|
|
|
@ -48,6 +48,12 @@ final class ImagePageViewController: UIPageViewController {
|
|||
systemItem: .close,
|
||||
primaryAction: UIAction { [weak self] _ in self?.presentingViewController?.dismiss(animated: true) })
|
||||
|
||||
navigationItem.rightBarButtonItem = .init(
|
||||
systemItem: .action,
|
||||
primaryAction: UIAction { [weak self] _ in
|
||||
(self?.viewControllers?.first as? ImageViewController)?.presentActivityViewController()
|
||||
})
|
||||
|
||||
navigationController?.barHideOnTapGestureRecognizer.addTarget(
|
||||
self,
|
||||
action: #selector(toggleDescriptionVisibility))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import AVFoundation
|
||||
import Kingfisher
|
||||
import UIKit
|
||||
import ViewModels
|
||||
|
@ -146,6 +147,44 @@ extension ImageViewController {
|
|||
self.descriptionBackgroundView.alpha = self.descriptionBackgroundView.alpha > 0 ? 0 : 1
|
||||
}
|
||||
}
|
||||
|
||||
func presentActivityViewController() {
|
||||
if let image = imageView.image {
|
||||
let activityViewController = UIActivityViewController(activityItems: [image], applicationActivities: [])
|
||||
|
||||
present(activityViewController, animated: true)
|
||||
} else if let asset = playerView.player?.currentItem?.asset as? AVURLAsset {
|
||||
asset.exportWithoutAudioTrack { result in
|
||||
DispatchQueue.main.async {
|
||||
switch result {
|
||||
case let .success(url):
|
||||
let activityViewController = UIActivityViewController(
|
||||
activityItems: [url],
|
||||
applicationActivities: [])
|
||||
|
||||
activityViewController.completionWithItemsHandler = { _, _, _, _ in
|
||||
try? FileManager.default.removeItem(at: url.deletingLastPathComponent())
|
||||
}
|
||||
|
||||
self.present(activityViewController, animated: true)
|
||||
case .failure:
|
||||
let alertController = UIAlertController(
|
||||
title: nil,
|
||||
message: NSLocalizedString("attachment.unable-to-export-media", comment: ""),
|
||||
preferredStyle: .alert)
|
||||
|
||||
let okAction = UIAlertAction(
|
||||
title: NSLocalizedString("ok", comment: ""),
|
||||
style: .default) { _ in }
|
||||
|
||||
alertController.addAction(okAction)
|
||||
|
||||
self.present(alertController, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ImageViewController: UIScrollViewDelegate {
|
||||
|
|
Loading…
Reference in a new issue