A professional media converter built for macOS Apple Silicon.
Remux-first, privacy-minded, and tuned for post-production workflows.
- Table of Contents
- Overview
- Key Features
- System Requirements
- Quick Start
- How It Works
- Extract comprehensive metadata from input files
- Determine optimal conversion strategy (copy vs. transcode)
- Spawn FFmpeg process with real-time progress tracking
- Architecture
- Usage
- Development
- Legal & Licensing
- Troubleshooting
- Contributing
- Acknowledgements
- License
Honeymelon is a native macOS desktop application that provides an intelligent interface for FFmpeg-powered media conversion. Built exclusively for Apple Silicon, it emphasizes lossless stream copying (remuxing) over re-encoding whenever possible, delivering faster conversions with zero quality loss.
Core Philosophy:
- Remux-first: Automatically detects when lossless stream copying is possible
- Privacy by design: All processing happens locallyβno network access, no telemetry
- Performance-optimized: Leverages Apple VideoToolbox hardware acceleration
- Production-ready: Manages concurrent jobs, preserves color metadata, handles subtitle formats
Technology Stack:
- Frontend: Vue 3 (Composition API) + TypeScript + Pinia + Tailwind CSS + shadcn-vue
- Backend: Tauri 2.x + Rust + FFmpeg (out-of-process, LGPL-compliant)
- Platform: macOS 13+ on Apple Silicon (M1/M2/M3/M4)
- Remux-first strategy: Automatically detects codec compatibility and uses lossless stream copying when possible
- Smart planning: Three-phase pipeline (Probe > Plan > Execute) ensures optimal conversion strategy
- Container-aware: Validates codec compatibility with target containers (MP4, MKV, WebM, MOV, AVI, FLV, TS, OGV, MPEG)
- Quality tiers: Fast (remux-priority), Balanced (quality/size), High (maximum quality)
- Color metadata preservation: Copies color primaries, transfer characteristics, and colorspace during transcoding
- Subtitle support: Text subtitle conversion (mov_text for MP4), image subtitle detection
- Hardware acceleration: Leverages Apple VideoToolbox for H.264, HEVC, and ProRes encoding
- Multi-format support: Video (MP4, MKV, MOV, WebM, GIF, AVI, FLV, M4V, TS, OGV, MPEG), Audio (M4A, MP3, FLAC, WAV, OGG, AAC, AIFF, Opus), Image (PNG, JPEG, WebP, BMP, TIFF)
- Concurrent job management: Configurable parallel processing with intelligent concurrency limits
- Exclusive codec handling: Heavy codecs (AV1, ProRes) run exclusively to prevent resource exhaustion
- Real-time progress: Live FPS, encoding speed, ETA calculations with circular log buffers
- Atomic operations: Temp file strategy ensures safe output with automatic cleanup on failure
- macOS notifications: Desktop alerts on job completion or failure
- Apple Silicon native: Bundle metadata blocks Rosetta fallback, keeps Retina UI crisp, and predeclares camera/mic usage for notarization readiness
- 100% local processing: All conversions happen on-device with no network access
- No telemetry: Zero data collection or external communication
- Command injection protection: Security-first validation of all FFmpeg arguments
- Full Disk Access support: Optional permission for protected directory access
| Requirement | Minimum | Recommended |
|---|---|---|
| Operating system | macOS 13 (Ventura) | macOS 14 (Sonoma) or newer |
| Hardware | Apple Silicon (M1/M2/M3/M4) | M2 Pro/Max for heavy 4K+ workloads |
| Memory | 4β―GB | 16β―GB for concurrent 4K conversions |
| Disk space | 50β―MB app + headroom for outputs | β₯β―500β―MB free for temporary files |
| Network | Not required for day-to-day use | Optional for future update channels |
Note: Honeymelon is compiled exclusively for Apple Silicon (ARM64 architecture). Intel-based Macs are not supported.
- Download the latest signed DMG from the Releases page
- Drag
Honeymelon.appinto/Applications - On first launch, macOS Gatekeeper may prompt you to confirm the developerβchoose Open
- (Optional) Grant Full Disk Access in System Settings for access to protected directories
Note: For air-gapped deployments, distribute the DMG and bundled FFmpeg binaries internallyβno activation or online checks are required.
# 1. Clone and install dependencies
git clone https://github.com/honeymelon-app/honeymelon.git
cd honeymelon
npm install
# 2. Download FFmpeg binaries (Apple Silicon arm64)
npm run download-ffmpeg
# 3. Launch development app with hot reload
npm run tauri dev
# 4. Or run frontend only (no FFmpeg access)
npm run dev
Honeymelon uses a three-stage conversion pipeline that intelligently decides between lossless stream copying and transcoding.
- Backend: Executes
ffprobewith JSON output (-print_format json -show_format -show_streams) - Parsing: Normalizes codec names, handles multiple frame rate formats, categorizes subtitle types
- Output:
ProbeSummarywith duration, dimensions, codecs, color metadata (bt709, bt2020, etc.), subtitle flags
Implementation: ffmpeg-probe.ts (frontend) + ffmpeg_probe.rs (backend, 933 lines)
Binary Resolution (4-tier fallback):
$HONEYMELON_FFPROBE_PATHenvironment variable- Development bundle:
src-tauri/bin/ffprobe - Application bundle resource directory
- System PATH
- Decision Logic: Matches source codec against target preset codecβif identical, uses stream copy; otherwise transcodes
- Container Rules: Validates codec compatibility (e.g., MP4 only supports H.264/HEVC/AV1 video, AAC/ALAC audio)
- Quality Tiers: Fast (remux-priority), Balanced (moderate bitrate), High (low CRF/high bitrate)
- Special Handling: GIF palette generation, color metadata copying, subtitle format conversion
Implementation: ffmpeg-plan.ts + container-rules.ts + presets.ts (420 lines)
Decision Matrix:
- Full Remux: All streams copied (
remuxOnly = true) > fastest, lossless - Partial Transcode: Some streams copied, others re-encoded
- Full Transcode: All streams require encoding > applies tier-specific settings
Output: PlannerDecision with complete FFmpeg arguments array, remux flag, human-readable notes, warnings
- Concurrency Control: Atomic validation with configurable limits (default: 2 concurrent jobs)
- Exclusive Mode: Heavy codecs (AV1, ProRes) block other jobs to prevent resource exhaustion
- Temp File Strategy: Writes to
<output>.tmp, atomically renames on success, auto-cleanup on failure - Progress Parsing: Background thread parses stderr for time/fps/speed metrics, emits Tauri events
- Security: Command injection prevention validates all arguments before spawning
Implementation: runner modules under src-tauri/src/runner/ (e.g. mod.rs, process_spawner.rs, progress_monitor.rs, output_manager.rs, validator.rs, concurrency.rs) + the frontend orchestration stack (src/composables/use-job-orchestrator.ts, src/composables/orchestrator/planner-client.ts, src/composables/orchestrator/runner-client.ts, src/composables/orchestrator/event-subscriber.ts, src/composables/use-capability-gate.ts, src/composables/use-desktop-bridge.ts)
Event System:
ffmpeg://progress> Real-time metrics (processed seconds, FPS, encoding speed)ffmpeg://stderr> Raw FFmpeg output for debuggingffmpeg://completion> Final status with success/failure/cancellation flag
Circular Log Buffer: Last 500 lines retained per job, prevents unbounded memory growth
| Layer | Technology | Purpose |
|---|---|---|
| Frontend | Vue 3 (Composition API) + TypeScript | UI components, business logic |
| Pinia | State management (jobs, preferences) | |
| shadcn-vue + Tailwind CSS | UI components and styling | |
| Vite 6.x | Build tooling and dev server | |
| Backend | Tauri 2.x + Rust (2021 edition) | Native integration, FFmpeg orchestration |
| Tokio async runtime | Process management and IPC | |
| Serde + JSON | Type-safe serialization | |
| Media | FFmpeg/FFprobe (out-of-process) | Media probing and conversion |
| Apple VideoToolbox | Hardware acceleration (H.264/HEVC/ProRes) |
Key Directories:
src/
βββ lib/ # Core business logic
β βββ ffmpeg-plan.ts # Planning engine
β βββ ffmpeg-probe.ts # Probe wrapper
β βββ container-rules.ts # Codec compatibility rules
β βββ presets.ts # Dynamic preset generation (420 lines)
β βββ media-formats.ts # Video/audio/image container definitions
β βββ job-lifecycle.ts # Shared lifecycle chart + DEV assertions
β βββ types.ts # TypeScript definitions
βββ stores/ # Pinia state
β βββ job-queue.ts # Job queue operations (enqueue/start/peek)
β βββ job-state.ts # Job state transitions (probing/planning/running)
β βββ job-progress.ts # Progress tracking utilities
β βββ job-logs.ts # Circular job log buffer
β βββ prefs.ts # User preferences
βββ services/ # Shared service layer
β βββ job-service.ts # Repository proxy with duplicate detection
βββ composables/ # Vue composables
β βββ use-app-orchestration.ts # Queue/start/cancel coordination
β βββ use-capability-gate.ts # Capability gating + preset readiness
β βββ use-desktop-bridge.ts # Drag/drop and menu wiring
β βββ use-job-orchestrator.ts # Coordinates planner/runner clients
β βββ orchestrator/
β βββ event-subscriber.ts # Centralized event lifecycle management
β βββ planner-client.ts # Frontend bridge to planner commands
β βββ runner-client.ts # Frontend bridge to runner commands
βββ components/ # Vue UI components
src-tauri/
βββ Info.plist # Bundle overrides (arm64-only, Retina, privacy usage descriptions)
βββ bin/ # Bundled FFmpeg sidecars (arm64 builds)
βββ src/
βββ runner/ # FFmpeg process orchestration split into focused modules
β βββ mod.rs
β βββ process_spawner.rs
β βββ progress_monitor.rs
β βββ output_manager.rs
β βββ validator.rs
β βββ concurrency.rs
βββ ffmpeg_probe.rs # Media probing
βββ ffmpeg_capabilities.rs # Capability detection
βββ fs_utils.rs # File discovery
βββ error.rs # Unified error handling
Documentation:
- CLAUDE.md β Comprehensive codebase guide for AI assistants
- AGENTS.md β Commit conventions and PR workflow
- CONTRIBUTING.md β Contribution guidelines
- LGPL Compliance: FFmpeg runs as separate processβno static/dynamic linking, communication via stdin/stdout/files only
- Type-Safe State Machine: Discriminated union types for job states prevent invalid transitions
- Event-Driven Progress: Tauri events stream FFmpeg output asynchronously, non-blocking UI updates
- Atomic Operations: Temp file writes + atomic renames ensure data integrity
- Security First: Command injection validation, path sanitization, permission checks
- Add Files: Drag-and-drop files/folders or click "Choose Files"
- Select Preset: Auto-selected based on file type, or choose from dropdown
- Choose Quality Tier:
- Fast: Remux-priority (lossless copy when possible)
- Balanced: Moderate bitrate for good quality/size ratio
- High: Maximum quality (low CRF, high bitrate)
- Monitor Progress: Real-time progress bar, FPS, encoding speed, ETA
- Access Output: Default location same as source, or configure custom output directory
| Shortcut | Action |
|---|---|
Cmd+, |
Open Preferences |
Cmd+O |
Open file picker |
Cmd+Q |
Quit Application |
Cmd+W |
Close Window |
Cmd+M |
Minimize Window |
Access: Cmd+, or menu: Honeymelon > Preferences
Settings:
- Concurrent Jobs: 1-β (default: 2, recommended 2-4 based on Mac model)
- Output Directory: Choose custom location or use source directory
- FFmpeg Path: Auto-detected or specify custom binary
- Filename Options: Include preset/tier in output filenames, configure separator
Environment Variables:
HONEYMELON_FFMPEG_PATHβ Override FFmpeg binary pathRUST_LOG=debugβ Enable Rust logging for development
| Command | Purpose |
|---|---|
npm install |
Install dependencies |
npm run tauri dev |
Run full app with hot reload (recommended) |
npm run dev |
Run frontend only (no FFmpeg) |
npm run build |
Build frontend assets |
npm run tauri build |
Build production DMG |
npm run lint |
Lint TypeScript + Rust |
npm run format |
Format all code |
npm run type-check |
Validate TypeScript |
npm run test:unit |
Run Vitest suites for planners/runners/jobs |
npm test |
Run all tests |
cd src-tauri && cargo test |
Run Rust tests |
cd src-tauri && cargo clippy |
Lint Rust code |
TypeScript/Vue:
- Strict mode, explicit types, no
any - Use
<script setup>(Composition API) - 2-space indentation, single quotes
- File naming: kebab-case for libs, PascalCase for components
Rust:
- Idiomatic Rust with
Result<T, E>error handling - Async/await for I/O operations
- 4-space indentation, snake_case modules
- Document public APIs with
///
General:
- Discriminated unions for state machines
- Immutability by default (
constoverlet) - Extract reusable logic into composables/helper functions
- Keep functions focused (<100 lines ideal)
Stack
- Frontend: Vitest powers unit suites for ffmpeg-plan, planner/runner clients, repositories, and composables
- Backend:
cargo testexercises probe parsing, runner validator/concurrency, filesystem helpers, and the mirrored RustJobLifecycle - Integration: Playwright drives drag/drop, preset selection, licensing, and exclusive-job workflows
- Coverage:
npm run test:unit:coverageemits V8 coverage for the TypeScript surface area
Coverage highlights
src/services/job-service.tsand repository helpers guard duplicate detection, timestamp patching, and transactional writessrc/lib/job-lifecycle.tsenforces legal transitions in Pinia stores while matching the Rust backend state chartsrc/composables/orchestrator/*.tssuites rely on a shared teardown helper, ensuring mocked Tauri listeners are cleaned after each run- Planner tests assert tier fallbacks, warning propagation, and capability filtering for video-optional inputs; runner tests cover exclusive job fan-out and cleanup paths
use-file-handlerspecs verify browser vs. Tauri file acquisition, preset readiness fallbacks, and mixed-media filtering
Running tests locally
npm run download-ffmpegβ fetch Apple Silicon FFmpeg sidecars used by planner/orchestrator specs (alternatively setHONEYMELON_FFMPEG_PATHto your own build)npm run test:unitβ execute Vitest suites headlessly; usenpm run test:unit:watchfor rapid cyclescd src-tauri && cargo testβ run backend unit and integration testsnpm run test:e2eβ launch Playwright against the Tauri shell (requiresnpm run tauri devin another terminal)- Optional:
npm run test:unit:coveragefor per-file coverage gating
ci.ymlinstalls FFmpeg vianpm run download-ffmpeg, runs ESLint/Clippy, enforces Markdown lint + format checks, executesnpm run type-check,npm run test:unit,npm run build, and finishes withcargo testrelease.ymlmirrors those gates (lint > type-check > unit tests > coverage > build) before packaging artifacts, preventing regressions from skipping CI- Both workflows cache npm/cargo artifacts for speed and fail fast when Vitest coverage or Markdown formatting drifts from the expected state
- Bump versions in
package.json,src-tauri/Cargo.toml,src-tauri/tauri.conf.json - Update
CHANGELOG.mdwith release notes - Run full QA:
npm run lint && npm test && npm run build && cd src-tauri && cargo test - Confirm bundled binaries are Apple Silicon only:
file src-tauri/bin/ffmpeg src-tauri/bin/ffprobe(expectarm64) - Configure code signing environment variables (APPLE_ID, APPLE_PASSWORD, APPLE_TEAM_ID)
- Build signed bundle:
npm run tauri build - Verify signature:
codesign -vvv --deep --strict <app>andspctl -a -vvv -t install <app> - Generate SHA256 checksum for DMG
- Create GitHub Release with DMG, changelog, and checksum
- Smoke test on clean machine
Output: src-tauri/target/aarch64-apple-darwin/release/bundle/dmg/Honeymelon_*.dmg
Honeymelon is free and open-source software licensed under the GNU General Public License version 3.0 or later (GPL-3.0-or-later).
This means you are free to:
- Use Honeymelon for any purpose
- Study how it works and modify it
- Share copies with others
- Distribute modified versions
Under the conditions that:
- You provide the source code when distributing
- You license your modifications under GPL-3.0-or-later
- You preserve copyright and license notices
For the complete license text, see LICENSE.
License: LGPL v2.1 or later
Compliance Method: Process Separation
- Honeymelon executes FFmpeg as a completely separate process (no shared memory, no library calls)
- No static linking to FFmpeg libraries (
.afiles) - No dynamic linking to FFmpeg libraries (
.so/.dylibfiles) - Communication exclusively via:
- Command-line arguments
- Standard input/output/error streams
- File system (input files, output files)
- This approach satisfies LGPL requirements and is compatible with GPL-3.0-or-later
Implications:
- FFmpeg's LGPL license is compatible with Honeymelon's GPL-3.0-or-later license
- Process separation ensures compliance without restrictions
- Must include FFmpeg license file with distribution
- Must provide information on obtaining FFmpeg source code
- Honeymelon does not modify FFmpeg
See LICENSES/FFMPEG-LGPL.txt for complete license text.
FFmpeg Installation & Sources:
For trusted FFmpeg installation methods and binary verification:
- Homebrew (recommended):
brew install ffmpeg - Official builds: https://evermeet.cx/ffmpeg/
- Build from source: https://github.com/FFmpeg/FFmpeg
See docs/FFMPEG_SOURCES.md for detailed information about:
- Recommended installation methods
- Binary verification and checksums
- Building from source
- Security considerations
- Distribution requirements
Certain audio and video codecs may be subject to patent claims in various jurisdictions:
H.264/HEVC (H.265):
- Patent pools managed by MPEG LA and HEVC Advance
- When using hardware encoders (VideoToolbox), Apple handles patent licensing
- Software encoders (libx264, libx265) may require separate licensing in some jurisdictions
- Recommendation: Use system-provided hardware encoders when possible
AAC:
- Patent pool managed by Via Licensing
- Provided by system codecs when using hardware acceleration
- Covered by Apple's licensing for system-provided encoders
VP9/AV1/Opus:
- Royalty-free codecs without patent licensing requirements
- Open-source encoder libraries (libvpx, libaom, libopus)
- No additional licensing needed
User Responsibility: Users who compile custom FFmpeg builds with software encoders (x264, x265, fdk-aac) are responsible for ensuring proper patent licensing in their jurisdiction.
All third-party dependencies are documented with proper attribution:
License Documentation:
- THIRD_PARTY_NOTICES.md: Complete list of dependencies and licenses
- LICENSES/: Individual license files for major dependencies
Key Dependencies (all used in compliance with their respective licenses):
- Tauri: MIT/Apache-2.0 dual license
- Vue.js: MIT license
- Rust ecosystem: Primarily MIT/Apache-2.0 dual licensed crates
- shadcn-vue: MIT license
- Tailwind CSS: MIT license
- FFmpeg: LGPL v2.1+ (process-separated, not linked)
When distributing Honeymelon under GPL-3.0-or-later, the following must be included:
- LICENSE file (GNU GPLv3 license)
- LICENSES/FFMPEG-LGPL.txt (FFmpeg LGPL license)
- THIRD_PARTY_NOTICES.md (all dependency attributions)
- Source code or clear instructions for obtaining it
- Link to FFmpeg source code: https://ffmpeg.org/download.html
Source code is available at: https://github.com/honeymelon-app/honeymelon
| Issue | Solution |
|---|---|
| Conversion fails immediately | Check FFmpeg: which ffmpeg && ffmpeg -version. Verify encoder support: ffmpeg -encoders | grep <name>. Check Console.app logs (filter: "Honeymelon") |
| Progress stuck at 0% | Input lacks duration metadata. Conversion continues normally, but ETA unavailable. Common with streaming formats. |
| Image subtitles in MP4 | MP4 doesn't support PGS/VOBSUB. Use burn-in (hardcoded) or output to MKV. Text subs auto-convert to mov_text. |
| Output file missing | Temp rename failed. Check for .tmp file. Ensure sufficient disk space and write permissions. |
| Choppy HEVC playback | Use H.264 preset for broader compatibility. Reduce quality tier from High to Balanced. |
| Large GIF files | Keep clips <10s. Resolution auto-limited to 640px width. Consider WebM for better compression. |
| FFmpeg not found | Install: brew install ffmpeg, or specify custom path in Preferences, or bundle with app. |
| Permission denied | Grant Full Disk Access: System Settings > Privacy & Security > Full Disk Access > Add Honeymelon.app |
| Slow AV1/ProRes encoding | Expected: AV1 (0.05-0.5x realtime), ProRes (0.5-2x). Exclusive mode prevents concurrent slow jobs. |
Speed:
- Use Fast tier (remux-priority)
- Match source/target codecs (enables stream copy)
- Increase concurrent jobs if CPU/RAM available
- Use MKV (accepts any codec > more remux opportunities)
Memory:
- Decrease concurrent jobs to 1-2
- Process files sequentially
- Clear completed jobs regularly
Before Reporting:
- Check troubleshooting above
- Review GitHub Discussions
- Verify:
ffmpeg -versionandffmpeg -encoders - Check Console.app (filter: "Honeymelon")
Bug Reports: GitHub Issues with macOS version, chip type, Honeymelon version, file details, FFmpeg version, logs
Feature Requests: GitHub Issues with use case, expected behavior, examples
Contributions welcome! See CONTRIBUTING.md for:
- Code of conduct
- Development setup
- Coding standards (TypeScript strict mode, Vue
<script setup>, Rust idioms) - Commit format (Conventional Commits)
- PR workflow
Priorities:
- High: Concurrency management, error handling, performance optimizations
- Medium: New presets, UI/UX refinements, testing infrastructure
- Future: Subtitle burn-in, multi-track selection, video trimming
See AGENTS.md for commit conventions.
Built with gratitude for:
- FFmpeg Team β Comprehensive media processing
- Tauri Team β Modern desktop framework
- Vue.js Team β Reactive UI framework
- Rust Community β Safe systems programming
- shadcn Community β Accessible UI components
- All Contributors β Code, docs, bug reports, suggestions
Honeymelon: Licensed under GNU General Public License v3.0 or later (GPL-3.0-or-later). Copyright Β© 2025 Jerome Thayananthajothy. See LICENSE.
FFmpeg: LGPL v2.1+. Process-separated (no linking). See LICENSES/FFMPEG-LGPL.txt.
Dependencies: See THIRD_PARTY_NOTICES.md for full attributions.
Licensing inquiries: tjthavarshan@gmail.com