diff --git a/ishare/App.swift b/ishare/App.swift index a8d0954..760393a 100644 --- a/ishare/App.swift +++ b/ishare/App.swift @@ -31,7 +31,7 @@ struct ishare: App { class AppDelegate: NSObject, NSApplicationDelegate { static private(set) var shared: AppDelegate! = nil - + func application(_ application: NSApplication, open urls: [URL]) { if urls.count == 1 { importIscu(urls.first!) @@ -43,6 +43,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { }() var isIconShown = false + var recordingTask: Process? func applicationDidFinishLaunching(_ notification: Notification) { AppDelegate.shared = self @@ -59,5 +60,14 @@ class AppDelegate: NSObject, NSApplicationDelegate { } else { statusBarItem.button?.image = nil } + + if !isIconShown { + stopRecording() + } + } + + func stopRecording() { + recordingTask?.interrupt() + recordingTask = nil } } diff --git a/ishare/Http/Custom.swift b/ishare/Http/Custom.swift index 09c6cb0..2f3ead9 100644 --- a/ishare/Http/Custom.swift +++ b/ishare/Http/Custom.swift @@ -17,7 +17,6 @@ enum CustomUploadError: Error { } func customUpload(fileURL: URL, specification: CustomUploader, callback: ((Error?, URL?) -> Void)? = nil, completion: @escaping () -> Void) { - @Default(.imageFileFormName) var imageFileFormName @Default(.captureFileType) var fileType guard specification.isValid() else { @@ -32,6 +31,9 @@ func customUpload(fileURL: URL, specification: CustomUploader, callback: ((Error if let requestHeaders = specification.headers { headers = HTTPHeaders(requestHeaders) } + + let fileFormName = fileURL.pathExtension == "mov" ? "video" : "image" + let mimeType = fileURL.pathExtension == "mov" ? "video/mov" : "image/\(fileType)" AF.upload(multipartFormData: { multipartFormData in if let formData = specification.formData { @@ -41,7 +43,7 @@ func customUpload(fileURL: URL, specification: CustomUploader, callback: ((Error } let fileData = try? Data(contentsOf: fileURL) - multipartFormData.append(fileData!, withName: specification.fileFormName ?? imageFileFormName, fileName: fileURL.lastPathComponent, mimeType: "image/\(fileType)") + multipartFormData.append(fileData!, withName: specification.fileFormName ?? fileFormName, fileName: fileURL.lastPathComponent, mimeType: mimeType) }, to: url, method: .post, headers: headers).response { response in if let data = response.data { let json = JSON(data) diff --git a/ishare/Http/Imgur.swift b/ishare/Http/Imgur.swift index ccc60fe..8568a49 100644 --- a/ishare/Http/Imgur.swift +++ b/ishare/Http/Imgur.swift @@ -17,8 +17,12 @@ func imgurUpload(_ fileURL: URL, completion: @escaping () -> Void) { let url = "https://api.imgur.com/3/upload" + let fileFormName = fileURL.pathExtension == "mov" ? "video" : "image" + let fileName = fileURL.pathExtension == "mov" ? "ishare.mov" : "ishare.\(fileType)" + let mimeType = fileURL.pathExtension == "mov" ? "video/mov" : "image/\(fileType)" + AF.upload(multipartFormData: { multipartFormData in - multipartFormData.append(fileURL, withName: "image", fileName: "ishare.\(fileType)", mimeType: "image/\(fileType)") + multipartFormData.append(fileURL, withName: fileFormName, fileName: fileName, mimeType: mimeType) }, to: url, method: .post, headers: ["Authorization": "Client-ID " + imgurClientId]).response { response in if let data = response.data { let json = JSON(data) diff --git a/ishare/Util/Constants.swift b/ishare/Util/Constants.swift index e3c5a0d..369ad9b 100644 --- a/ishare/Util/Constants.swift +++ b/ishare/Util/Constants.swift @@ -28,14 +28,15 @@ extension Defaults.Keys { static let openInFinder = Key("openInFinder", default: false) static let uploadMedia = Key("uploadMedia", default: false) static let capturePath = Key("capturePath", default: "~/Pictures/") + static let recordingPath = Key("recordingPath", default: "~/Pictures/") static let captureFileType = Key("captureFileType", default: .PNG) static let captureFileName = Key("captureFileName", default: "ishare") + static let recordingFileName = Key("recordingFileName", default: "ishare") static let imgurClientId = Key("imgurClientId", default: "867afe9433c0a53") static let captureBinary = Key("captureBinary", default: "/usr/sbin/screencapture") static let activeCustomUploader = Key("activeCustomUploader", default: nil) static let savedCustomUploaders = Key?>("savedCustomUploaders") static let uploadType = Key("uploadType", default: .IMGUR) - static let imageFileFormName = Key("imageFileFormName", default: "image") static let menuBarAppIcon = Key("menuBarAppIcon", default: true) static let uploadDestination = Key("uploadDestination", default: .builtIn(.IMGUR)) } diff --git a/ishare/Util/ScreenRecording.swift b/ishare/Util/ScreenRecording.swift index f390e54..cc01f97 100644 --- a/ishare/Util/ScreenRecording.swift +++ b/ishare/Util/ScreenRecording.swift @@ -6,16 +6,90 @@ // import Foundation +import Defaults import AppKit import Cocoa enum RecordingType: String { - case SCREEN = "-t" - case WINDOW = "-wt" - case REGION = "-st" + case SCREEN = "-v" + // case WINDOW = "-wt" + // case REGION = "-st" } -func recordScreen(type: RecordingType, display: Int = 1) -> Void { - print("recording") +func recordScreen(type: RecordingType, display: Int = 1) { + @Default(.copyToClipboard) var copyToClipboard + @Default(.openInFinder) var openInFinder + @Default(.recordingPath) var recordingPath + @Default(.recordingFileName) var fileName + @Default(.uploadType) var uploadType + @Default(.uploadMedia) var uploadMedia + + let timestamp = Int(Date().timeIntervalSince1970) + let uniqueFilename = "\(fileName)-\(timestamp)" + + var path = "\(recordingPath)\(uniqueFilename).mov" + path = NSString(string: path).expandingTildeInPath + + recordingTask(path: path, type: type, display: display) { + let fileURL = URL(fileURLWithPath: path) + + if !FileManager.default.fileExists(atPath: fileURL.path) { + return + } + + if copyToClipboard { + let pasteboard = NSPasteboard.general + pasteboard.clearContents() + + pasteboard.setString(fileURL.absoluteString, forType: .fileURL) + } + + if openInFinder { + NSWorkspace.shared.activateFileViewerSelecting([fileURL]) + } + + if uploadMedia { + uploadFile(fileURL: fileURL, uploadType: uploadType) { + showToast(fileURL: fileURL) + NSSound.beep() + } + } else { + showToast(fileURL: fileURL) + NSSound.beep() + } + + deleteScreenRecordings() + } +} + +func recordingTask(path: String, type: RecordingType, display: Int = 1, completion: @escaping () -> Void) { AppDelegate.shared.toggleIcon(AppDelegate.shared as AnyObject) + + @Default(.captureBinary) var captureBinary + + let task = Process() + task.launchPath = captureBinary + task.arguments = type == RecordingType.SCREEN ? [type.rawValue, "-D", "\(display)", path] : [type.rawValue, path] + + AppDelegate.shared.recordingTask = task + + DispatchQueue.global(qos: .background).async { + task.launch() + task.waitUntilExit() + + DispatchQueue.main.async { + AppDelegate.shared.recordingTask = nil + completion() + } + } +} + +func deleteScreenRecordings() { + let screenRecordingsPath = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("Library/ScreenRecordings") + + do { + try FileManager.default.removeItem(at: screenRecordingsPath) + } catch { + print("Error deleting the ScreenRecordings folder: \(error)") + } } diff --git a/ishare/Util/ToastPopover.swift b/ishare/Util/ToastPopover.swift index 5f4cd88..178c589 100644 --- a/ishare/Util/ToastPopover.swift +++ b/ishare/Util/ToastPopover.swift @@ -1,4 +1,5 @@ import SwiftUI +import AVFoundation struct ToastPopoverView: View { let thumbnailImage: NSImage @@ -21,7 +22,24 @@ struct ToastPopoverView: View { } func showToast(fileURL: URL) { - guard let thumbnailImage = NSImage(contentsOf: fileURL) else { + var thumbnailImage: NSImage? + + if fileURL.pathExtension == "mov" { + let asset = AVURLAsset(url: fileURL) + let imageGenerator = AVAssetImageGenerator(asset: asset) + imageGenerator.appliesPreferredTrackTransform = true + let time = CMTime(seconds: 2, preferredTimescale: 60) + do { + let cgImage = try imageGenerator.copyCGImage(at: time, actualTime: nil) + thumbnailImage = NSImage(cgImage: cgImage, size: CGSize(width: cgImage.width, height: cgImage.height)) + } catch { + print("Error generating thumbnail: \(error)") + } + } else { + thumbnailImage = NSImage(contentsOf: fileURL) + } + + guard let thumbnail = thumbnailImage else { return } @@ -37,7 +55,7 @@ func showToast(fileURL: URL) { toastWindow.backgroundColor = .clear toastWindow.isMovableByWindowBackground = false toastWindow.contentView = NSHostingView( - rootView: ToastPopoverView(thumbnailImage: thumbnailImage, fileURL: fileURL) + rootView: ToastPopoverView(thumbnailImage: thumbnail, fileURL: fileURL) ) toastWindow.makeKeyAndOrderFront(nil) diff --git a/ishare/Views/SettingsMenuView.swift b/ishare/Views/SettingsMenuView.swift index 444bfcd..010d1aa 100644 --- a/ishare/Views/SettingsMenuView.swift +++ b/ishare/Views/SettingsMenuView.swift @@ -127,10 +127,30 @@ struct CaptureSettingsView: View { } struct RecordingSettingsView: View { + @Default(.recordingPath) var recordingPath + @Default(.recordingFileName) var fileName + var body: some View { VStack { HStack { - } + Text("Capture path:") + TextField(text: $recordingPath) {} + Button("Select directory") { + selectFolder { folderURL in + if let url = folderURL { + recordingPath = url.path() + } + } + } + }.padding(10) + + HStack { + Text("File name:") + TextField(String(), text: $fileName) + Button("Default") { + fileName = Defaults.Keys.recordingFileName.defaultValue + } + }.padding(20) } } }