Skip to content

Commit 5a32bfe

Browse files
authored
Merge pull request #43 from jessielw/copilot/create-app-package-macos
Add macOS .app bundle packaging and Intel (x86_64) architecture support
2 parents db567db + 510ef77 commit 5a32bfe

File tree

4 files changed

+307
-16
lines changed

4 files changed

+307
-16
lines changed

.github/workflows/build.yml

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
path: pyinstaller_build/bundled_mode/Mp4Forge/
3838
retention-days: 90
3939

40-
build-macos:
40+
build-macos-arm64:
4141
runs-on: macos-latest
4242

4343
steps:
@@ -62,8 +62,37 @@ jobs:
6262
- name: Upload artifact
6363
uses: actions/upload-artifact@v4
6464
with:
65-
name: Mp4Forge-macOS
66-
path: pyinstaller_build/bundled_mode/Mp4Forge/
65+
name: Mp4Forge-macOS-ARM64
66+
path: pyinstaller_build/bundled_mode/Mp4Forge.app/
67+
retention-days: 90
68+
69+
build-macos-intel:
70+
runs-on: macos-15-intel
71+
72+
steps:
73+
- uses: actions/checkout@v4
74+
75+
- name: Set up Python
76+
uses: actions/setup-python@v5
77+
with:
78+
python-version: "3.11"
79+
80+
- name: Install uv
81+
uses: astral-sh/setup-uv@v4
82+
with:
83+
enable-cache: true
84+
85+
- name: Install dependencies
86+
run: uv sync --all-extras
87+
88+
- name: Build desktop app
89+
run: uv run python build_desktop.py
90+
91+
- name: Upload artifact
92+
uses: actions/upload-artifact@v4
93+
with:
94+
name: Mp4Forge-macOS-Intel
95+
path: pyinstaller_build/bundled_mode/Mp4Forge.app/
6796
retention-days: 90
6897

6998
build-linux:
@@ -96,7 +125,7 @@ jobs:
96125
retention-days: 90
97126

98127
create-release:
99-
needs: [build-windows, build-macos, build-linux]
128+
needs: [build-windows, build-macos-arm64, build-macos-intel, build-linux]
100129
runs-on: ubuntu-latest
101130
if: startsWith(github.ref, 'refs/tags/')
102131

@@ -109,11 +138,17 @@ jobs:
109138
name: Mp4Forge-Windows
110139
path: ./dist/windows
111140

112-
- name: Download macOS artifact
141+
- name: Download macOS ARM64 artifact
113142
uses: actions/download-artifact@v4
114143
with:
115-
name: Mp4Forge-macOS
116-
path: ./dist/macos
144+
name: Mp4Forge-macOS-ARM64
145+
path: ./dist/macos-arm64
146+
147+
- name: Download macOS Intel artifact
148+
uses: actions/download-artifact@v4
149+
with:
150+
name: Mp4Forge-macOS-Intel
151+
path: ./dist/macos-intel
117152

118153
- name: Download Linux artifact
119154
uses: actions/download-artifact@v4
@@ -128,9 +163,15 @@ jobs:
128163
zip -r archives/Mp4Forge-Windows.zip Mp4Forge
129164
mv Mp4Forge dist/windows
130165
131-
mv dist/macos Mp4Forge
132-
zip -r archives/Mp4Forge-macOS.zip Mp4Forge
133-
mv Mp4Forge dist/macos
166+
# macOS ARM64 .app bundle
167+
cd dist/macos-arm64
168+
zip -r ../../archives/Mp4Forge-macOS-ARM64.zip Mp4Forge.app
169+
cd ../..
170+
171+
# macOS Intel .app bundle
172+
cd dist/macos-intel
173+
zip -r ../../archives/Mp4Forge-macOS-Intel.zip Mp4Forge.app
174+
cd ../..
134175
135176
mv dist/linux Mp4Forge
136177
tar czf archives/Mp4Forge-Linux.tar.gz Mp4Forge
@@ -141,7 +182,8 @@ jobs:
141182
with:
142183
files: |
143184
archives/Mp4Forge-Windows.zip
144-
archives/Mp4Forge-macOS.zip
185+
archives/Mp4Forge-macOS-ARM64.zip
186+
archives/Mp4Forge-macOS-Intel.zip
145187
archives/Mp4Forge-Linux.tar.gz
146188
draft: true
147189
prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') || contains(github.ref, 'alpha') }}

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,14 @@ A modern MP4 muxing tool with a desktop GUI interface, powered by MP4Box.
4848
1. Go to the [Releases](https://github.com/jessielw/MP4-Mux-Tool/releases) page
4949
2. Download the archive for your platform:
5050
- **Windows**: `Mp4Forge-Windows.zip`
51-
- **macOS**: `Mp4Forge-macOS.zip`
51+
- **macOS (Apple Silicon)**: `Mp4Forge-macOS-ARM64.zip`
52+
- **macOS (Intel)**: `Mp4Forge-macOS-Intel.zip`
5253
- **Linux**: `Mp4Forge-Linux.tar.gz`
5354
3. Extract the archive
54-
4. Run the executable:
55-
- Windows: `Mp4Forge.exe`
56-
- macOS/Linux: `./Mp4Forge`
55+
4. Run the application:
56+
- **Windows**: Run `Mp4Forge.exe`
57+
- **macOS**: Extract the .zip and drag `Mp4Forge.app` to your Applications folder, then launch from Launchpad or Finder
58+
- **Linux**: Run `./Mp4Forge`
5759

5860
### Option 2: Run from Source
5961

build_desktop.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,26 @@
44
from pathlib import Path
55
from subprocess import run
66

7+
# Import macOS app builder if on macOS
8+
if platform.system() == "Darwin":
9+
from build_macos_app import create_app_bundle
10+
711

812
def get_executable_extension() -> str:
913
return ".exe" if platform.system() == "Windows" else ""
1014

1115

16+
def load_toml(file_path: Path) -> dict:
17+
"""Load TOML file, handling different Python versions."""
18+
try:
19+
import tomllib
20+
except ImportError:
21+
import tomli as tomllib
22+
23+
with open(file_path, "rb") as f:
24+
return tomllib.load(f)
25+
26+
1227
# def get_site_packages() -> Path:
1328
# output = run(
1429
# ("uv", "pip", "show", "PySide6"),
@@ -109,14 +124,41 @@ def build_app():
109124

110125
# Check onedir (bundle) build
111126
onedir_path = Path("bundled_mode") / "Mp4Forge" / f"Mp4Forge{exe_str}"
112-
if onedir_path.is_file() and str(build_job_onedir.returncode) == "0":
127+
build_succeeded = onedir_path.is_file() and str(build_job_onedir.returncode) == "0"
128+
129+
if build_succeeded:
113130
success_msgs.append(f"Bundle build success! Path: {Path.cwd() / onedir_path}")
114131
else:
115132
success_msgs.append("Bundle build did not complete successfully")
116133

134+
# Store absolute path before changing directory
135+
pyinstaller_output = pyinstaller_folder / "bundled_mode"
136+
117137
# change directory back to original directory
118138
os.chdir(desktop_script.parent)
119139

140+
# On macOS, create a proper .app bundle
141+
if platform.system() == "Darwin" and build_succeeded:
142+
try:
143+
# Get version from pyproject.toml
144+
pyproject_path = project_root / "pyproject.toml"
145+
pyproject = load_toml(pyproject_path)
146+
version = pyproject["project"]["version"]
147+
148+
# Create .app bundle (pyinstaller_output already defined above)
149+
icon_png = project_root / "runtime" / "images" / "mp4.png"
150+
151+
app_bundle = create_app_bundle(
152+
pyinstaller_output_dir=pyinstaller_output,
153+
app_name="Mp4Forge",
154+
version=version,
155+
bundle_identifier="io.github.jessielw.mp4forge",
156+
icon_path=icon_png if icon_png.exists() else None,
157+
)
158+
success_msgs.append(f"macOS app bundle created: {app_bundle}")
159+
except Exception as e:
160+
success_msgs.append(f"Warning: Failed to create .app bundle: {e}")
161+
120162
return "\n".join(success_msgs)
121163

122164

0 commit comments

Comments
 (0)