commit 5e0ff1a6a02540c4d7b1453e01c7975b071ae4c4
parent 6517dcba3f6114f86e25886db335887663f49559
Author: Swift <scoder1747@gmail.com>
Date: Fri, 24 Mar 2023 18:39:22 -0400
Video Uploads
Changelog-Added: Add support for video uploads
Diffstat:
5 files changed, 65 insertions(+), 43 deletions(-)
diff --git a/damus/Models/ImageUploadModel.swift b/damus/Models/ImageUploadModel.swift
@@ -12,8 +12,8 @@ import UIKit
class ImageUploadModel: NSObject, URLSessionTaskDelegate, ObservableObject {
@Published var progress: Double? = nil
- func start(img: UIImage, uploader: ImageUploader) async -> ImageUploadResult {
- let res = await create_image_upload_request(imageToUpload: img, imageUploader: uploader, progress: self)
+ func start(media: Any, uploader: MediaUploader) async -> ImageUploadResult {
+ let res = await create_upload_request(mediaToUpload: media, mediaUploader: uploader, progress: self)
DispatchQueue.main.async {
self.progress = nil
}
diff --git a/damus/Models/UserSettingsStore.swift b/damus/Models/UserSettingsStore.swift
@@ -50,10 +50,10 @@ func get_default_wallet(_ pubkey: String) -> Wallet {
}
}
-func get_image_uploader(_ pubkey: String) -> ImageUploader {
- if let defaultImageUploader = UserDefaults.standard.string(forKey: "default_image_uploader"),
- let defaultImageUploader = ImageUploader(rawValue: defaultImageUploader) {
- return defaultImageUploader
+func get_media_uploader(_ pubkey: String) -> MediaUploader {
+ if let defaultMediaUploader = UserDefaults.standard.string(forKey: "default_media_uploader"),
+ let defaultMediaUploader = MediaUploader(rawValue: defaultMediaUploader) {
+ return defaultMediaUploader
} else {
return .nostrBuild
}
@@ -98,9 +98,9 @@ class UserSettingsStore: ObservableObject {
}
}
- @Published var default_image_uploader: ImageUploader {
+ @Published var default_media_uploader: MediaUploader {
didSet {
- UserDefaults.standard.set(default_image_uploader.rawValue, forKey: "default_image_uploader")
+ UserDefaults.standard.set(default_media_uploader.rawValue, forKey: "default_media_uploader")
}
}
@@ -217,7 +217,7 @@ class UserSettingsStore: ObservableObject {
show_wallet_selector = should_show_wallet_selector(pubkey)
always_show_images = UserDefaults.standard.object(forKey: "always_show_images") as? Bool ?? false
- default_image_uploader = get_image_uploader(pubkey)
+ default_media_uploader = get_media_uploader(pubkey)
left_handed = UserDefaults.standard.object(forKey: "left_handed") as? Bool ?? false
zap_vibration = UserDefaults.standard.object(forKey: "zap_vibration") as? Bool ?? false
diff --git a/damus/Views/AttachMediaUtility.swift b/damus/Views/AttachMediaUtility.swift
@@ -15,23 +15,24 @@ enum ImageUploadResult {
case failed(Error?)
}
-fileprivate func create_upload_body(imageDataKey: Data, boundary: String, imageUploader: ImageUploader) -> Data {
+fileprivate func create_upload_body(mediaData: Data, boundary: String, mediaUploader: MediaUploader, mediaIsImage: Bool) -> Data {
let body = NSMutableData();
- let contentType = "image/jpg"
+ let contentType = mediaIsImage ? "image/jpg" : "video/mp4"
+ let genericFileName = mediaIsImage ? "damus_generic_filename.jpg" : "damus_generic_filename.mp4"
body.appendString(string: "Content-Type: multipart/form-data; boundary=\(boundary)\r\n\r\n")
body.appendString(string: "--\(boundary)\r\n")
- body.appendString(string: "Content-Disposition: form-data; name=\(imageUploader.nameParam); filename=\"damus_generic_filename.jpg\"\r\n")
+ body.appendString(string: "Content-Disposition: form-data; name=\(mediaUploader.nameParam); filename=\(genericFileName)\r\n")
body.appendString(string: "Content-Type: \(contentType)\r\n\r\n")
- body.append(imageDataKey as Data)
+ body.append(mediaData as Data)
body.appendString(string: "\r\n")
body.appendString(string: "--\(boundary)--\r\n")
return body as Data
}
-
-func create_image_upload_request(imageToUpload: UIImage, imageUploader: ImageUploader, progress: URLSessionTaskDelegate) async -> ImageUploadResult {
-
- guard let url = URL(string: imageUploader.postAPI) else {
+func create_upload_request(mediaToUpload: Any, mediaUploader: MediaUploader, progress: URLSessionTaskDelegate) async -> ImageUploadResult {
+ var mediaIsImage: Bool = false
+ var mediaData: Data?
+ guard let url = URL(string: mediaUploader.postAPI) else {
return .failed(nil)
}
@@ -39,14 +40,19 @@ func create_image_upload_request(imageToUpload: UIImage, imageUploader: ImageUpl
request.httpMethod = "POST";
let boundary = "Boundary-\(UUID().description)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
-
- // otherwise convert to jpg
- guard let jpegData = imageToUpload.jpegData(compressionQuality: 0.8) else {
- // somehow failed, just return original
+
+ if let imageToUpload = mediaToUpload as? UIImage {
+ mediaData = imageToUpload.jpegData(compressionQuality: 0.8)
+ mediaIsImage = true
+ } else if let videoToUpload = mediaToUpload as? URL {
+ mediaData = try? Data(contentsOf: videoToUpload)
+ }
+
+ guard let mediaData = mediaData else {
return .failed(nil)
}
-
- request.httpBody = create_upload_body(imageDataKey: jpegData, boundary: boundary, imageUploader: imageUploader)
+
+ request.httpBody = create_upload_body(mediaData: mediaData, boundary: boundary, mediaUploader: mediaUploader, mediaIsImage: mediaIsImage)
do {
let (data, _) = try await URLSession.shared.data(for: request, delegate: progress)
@@ -56,8 +62,8 @@ func create_image_upload_request(imageToUpload: UIImage, imageUploader: ImageUpl
return .failed(nil)
}
- guard let url = imageUploader.getImageURL(from: responseString) else {
- print("Upload failed getting image url")
+ guard let url = mediaUploader.getMediaURL(from: responseString, mediaIsImage: mediaIsImage) else {
+ print("Upload failed getting media url")
return .failed(nil)
}
@@ -66,7 +72,6 @@ func create_image_upload_request(imageToUpload: UIImage, imageUploader: ImageUpl
} catch {
return .failed(error)
}
-
}
extension PostView {
@@ -76,7 +81,9 @@ extension PostView {
private var presentationMode
let sourceType: UIImagePickerController.SourceType
+ let damusState: DamusState
let onImagePicked: (UIImage) -> Void
+ let onVideoPicked: (URL) -> Void
final class Coordinator: NSObject,
UINavigationControllerDelegate,
@@ -86,19 +93,27 @@ extension PostView {
private var presentationMode: PresentationMode
private let sourceType: UIImagePickerController.SourceType
private let onImagePicked: (UIImage) -> Void
+ private let onVideoPicked: (URL) -> Void
init(presentationMode: Binding<PresentationMode>,
sourceType: UIImagePickerController.SourceType,
- onImagePicked: @escaping (UIImage) -> Void) {
+ onImagePicked: @escaping (UIImage) -> Void,
+ onVideoPicked: @escaping (URL) -> Void) {
_presentationMode = presentationMode
self.sourceType = sourceType
self.onImagePicked = onImagePicked
+ self.onVideoPicked = onVideoPicked
}
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
- let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
- onImagePicked(uiImage)
+ if let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL {
+ // Handle the selected video
+ onVideoPicked(videoURL)
+ } else if let uiImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
+ // Handle the selected image
+ onImagePicked(uiImage)
+ }
presentationMode.dismiss()
}
@@ -112,12 +127,17 @@ extension PostView {
func makeCoordinator() -> Coordinator {
return Coordinator(presentationMode: presentationMode,
sourceType: sourceType,
- onImagePicked: onImagePicked)
+ onImagePicked: onImagePicked, onVideoPicked: onVideoPicked)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.sourceType = sourceType
+ let mediaUploader = get_media_uploader(damusState.keypair.pubkey)
+ picker.mediaTypes = ["public.image"]
+ if mediaUploader.supportsVideo {
+ picker.mediaTypes.append("public.movie")
+ }
picker.delegate = context.coordinator
return picker
}
@@ -138,7 +158,7 @@ extension NSMutableData {
}
}
-enum ImageUploader: String, CaseIterable, Identifiable {
+enum MediaUploader: String, CaseIterable, Identifiable {
var id: String { self.rawValue }
case nostrBuild
case nostrImg
@@ -152,12 +172,12 @@ enum ImageUploader: String, CaseIterable, Identifiable {
}
}
- var displayImageUploaderName: String {
+ var supportsVideo: Bool {
switch self {
case .nostrBuild:
- return "NostrBuild"
+ return true
case .nostrImg:
- return "NostrImg"
+ return false
}
}
@@ -187,7 +207,7 @@ enum ImageUploader: String, CaseIterable, Identifiable {
}
}
- func getImageURL(from responseString: String) -> String? {
+ func getMediaURL(from responseString: String, mediaIsImage: Bool) -> String? {
switch self {
case .nostrBuild:
guard let startIndex = responseString.range(of: "nostr.build_")?.lowerBound else {
@@ -199,7 +219,7 @@ enum ImageUploader: String, CaseIterable, Identifiable {
return nil
}
let nostrBuildImageName = responseString[startIndex..<endIndex]
- let nostrBuildURL = "https://nostr.build/i/\(nostrBuildImageName)"
+ let nostrBuildURL = mediaIsImage ? "https://nostr.build/i/\(nostrBuildImageName)" : "https://nostr.build/av/\(nostrBuildImageName)"
return nostrBuildURL
case .nostrImg:
diff --git a/damus/Views/ConfigView.swift b/damus/Views/ConfigView.swift
@@ -228,8 +228,8 @@ struct ConfigView: View {
}
Picker(NSLocalizedString("Select image uploader", comment: "Prompt selection of user's image uploader"),
- selection: $settings.default_image_uploader) {
- ForEach(ImageUploader.allCases, id: \.self) { uploader in
+ selection: $settings.default_media_uploader) {
+ ForEach(MediaUploader.allCases, id: \.self) { uploader in
Text(uploader.model.displayName)
.tag(uploader.model.tag)
}
diff --git a/damus/Views/PostView.swift b/damus/Views/PostView.swift
@@ -168,11 +168,11 @@ struct PostView: View {
post = combinedAttributedString
}
- func handle_upload(image: UIImage) {
- let uploader = get_image_uploader(damus_state.pubkey)
+ func handle_upload(media: Any) {
+ let uploader = get_media_uploader(damus_state.pubkey)
Task.init {
- let res = await image_upload.start(img: image, uploader: uploader)
+ let res = await image_upload.start(media: media, uploader: uploader)
switch res {
case .success(let url):
@@ -215,8 +215,10 @@ struct PostView: View {
}
.padding()
.sheet(isPresented: $attach_media) {
- ImagePicker(sourceType: .photoLibrary) { img in
- handle_upload(image: img)
+ ImagePicker(sourceType: .photoLibrary, damusState: damus_state) { img in
+ handle_upload(media: img)
+ } onVideoPicked: { url in
+ handle_upload(media: url)
}
}
.onAppear() {