Skip to content

Commit

Permalink
feat(v1.1.0): ability to keep original .heic files
Browse files Browse the repository at this point in the history
  • Loading branch information
zpg6 committed Nov 28, 2024
1 parent e347fb3 commit 24578e2
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 15 deletions.
6 changes: 4 additions & 2 deletions DropMeJPEG.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = DropMeJPEG/DropMeJPEG.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
Expand All @@ -283,7 +284,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 1.0.0;
MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.ZachGrimaldi.DropMeJPEG;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand All @@ -297,6 +298,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = DropMeJPEG/DropMeJPEG.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
Expand All @@ -314,7 +316,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 1.0.0;
MARKETING_VERSION = 1.1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.ZachGrimaldi.DropMeJPEG;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand Down
36 changes: 28 additions & 8 deletions DropMeJPEG/DropMeJPEGApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,19 @@ struct DropMeJPEGApp: App {

var body: some Scene {
MenuBarExtra("DropMeJPEG", systemImage: "photo") {
Text("DropMeJPEG").bold()
Text("DropMeJPEG v\(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0")")
Divider()

Toggle(isOn: $folderMonitor.isEnabled) {
Text(folderMonitor.isEnabled ? "Enabled" : "Disabled")
}

Toggle(isOn: $folderMonitor.shouldDeleteOriginal) {
Text("Delete original HEIC files")
}
.disabled(!folderMonitor.isEnabled)

Divider()
Button("Quit") {
NSApplication.shared.terminate(nil)
}.keyboardShortcut("q")
Expand All @@ -36,7 +42,8 @@ class FolderMonitor: ObservableObject {
isEnabled ? startMonitoring() : stopMonitoring()
}
}
@Published var shouldDeleteOriginalInputFile = false
@Published var delayAfterWrite: TimeInterval = 4
@Published var shouldDeleteOriginal: Bool = true
private var folderURL = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first!
private var monitor: DispatchSourceFileSystemObject?
private var fileDescriptor: CInt = -1
Expand Down Expand Up @@ -83,23 +90,33 @@ class FolderMonitor: ObservableObject {

/// There are some folder changes, this function will handle detecting new HEIC files and converting them to JPEG.
private func handleFolderChanges() {
guard let currentFiles = try? FileManager.default.contentsOfDirectory(atPath: folderURL.path) else { return }
print("\n⚡️Folder changed... waiting for \(delayAfterWrite) seconds for AirDrop to finish writing any files.")
sleep(UInt32(delayAfterWrite)) // Wait for the file to be fully written

guard let currentFiles = try? FileManager.default.contentsOfDirectory(atPath: folderURL.path) else {
print("❌ Failed to list files in folder \(folderURL.path)")
return
}
let currentSet = Set(currentFiles)

// Detect newly added files
let newFiles = currentSet.subtracting(knownFiles)
knownFiles = currentSet

if newFiles.isEmpty {
print("Nothing new to process.\(shouldDeleteOriginal ? " This may have been our own cleanup operation of original HEIC file." : "")")
return
}

// Process new HEIC files
newFiles
.filter { $0.uppercased().hasSuffix(".HEIC") }
.map { folderURL.appendingPathComponent($0) }
.forEach {

convertHEICtoJPEG($0)
}
}

/// Conversion from HEIC to JPEG uses the `sips` command line tool that is built-into macOS.
private func convertHEICtoJPEG(_ fileURL: URL) {
print("PROCESSING INPUT URL: \(fileURL)")
Expand All @@ -125,10 +142,13 @@ class FolderMonitor: ObservableObject {
if !outputData.isEmpty { print("convertHEICtoJPEG Output: \(String(data: outputData, encoding: .utf8) ?? "")") }
if !errorData.isEmpty { print("convertHEICtoJPEG Error: \(String(data: errorData, encoding: .utf8) ?? "")") }

if process.terminationStatus == 0 {
try FileManager.default.removeItem(at: fileURL)
if process.terminationStatus == 0 && !outputData.isEmpty {
if shouldDeleteOriginal {
try FileManager.default.removeItem(at: fileURL)
}
print("✅ SIPS command succeeded")
} else {
print("SIPS command failed (status: \(process.terminationStatus))")
print("SIPS command failed (status: \(process.terminationStatus))")
}
} catch {
print("convertHEICtoJPEG failed: \(error)")
Expand Down
28 changes: 23 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,30 @@

# DropMeJPEG

MacOS Menu Bar App to automatically convert AirDrop'd `.heic` images to `.jpeg`. It's as simple as opening the app and sending yourself some images via AirDrop. This was a feature I've been wanting, so I built it myself.
MacOS Menu Bar app to automatically convert AirDrop'd `.heic` images to `.jpeg`. It's as simple as opening the app and sending yourself some images via AirDrop. This was a feature I've been wanting, so I built it myself.

The alternative for me has been to open the `.heic` image in **Preview app** and then export it as a `.jpeg`. As of a recent MacOS update, you can open the image in Finder and right-click to convert it to JPEG.
> [!NOTE]
> The app is working well on my machine, but I am still finalizing a few more things before I release it to the App Store. It will remain free and open-source.
## Why build this?

Until now, the alternative for me has been to open the `.heic` image in **Preview app** and then export it as a `.jpeg` (10+ clicks). As of a recent MacOS update, you can open the image in Finder and right-click to convert it to JPEG (4-5 clicks).

Yes, you can also disable the High-Effeiciency Image Format (HEIF) in the Camera settings. But with our option to keep or delete the original `.heic` files, you can have the best of both worlds.

## Powered by `sips`

To convert images, this app utilizes the Scriptable Image Processing System (sips) which is built-into MacOS. Specifically the `format` command.

```bash
sips --setProperty format jpeg --out filename.jpeg filename.HEIC
```

View the [`sips` MAN page](https://ss64.com/mac/sips.html).

## Changelog

| Date | Version | Description |
| ---------- | ------- | ---------------------------------------- |
| 2024-11-28 | 1.0.0 | Initial Release (minimum viable product) |
| Date | Version | Description |
| ---------- | ------- | ------------------------------------------ |
| 2024-11-28 | 1.1.0 | Add ability to keep original `.heic` files |
| 2024-11-28 | 1.0.0 | Initial Release (minimum viable product) |
Binary file modified docs/Banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 24578e2

Please sign in to comment.