2 Commits

4 changed files with 103 additions and 12 deletions
+38
View File
@@ -0,0 +1,38 @@
# macOS test build — 0.1.1-mac.1
This is an unsigned, unnotarized test prerelease. It has been packaged on Linux; it has NOT been launched or playback-tested on macOS. Do not treat it as an approved customer release.
## Build
Based on main commit `2c93670aafd04f05e4e14b803f4bf0eeab6dd991`. Player code is unchanged. Version metadata and three vulnerable build-time transitive dependencies were updated (xmldom, fast-uri, js-yaml).
```sh
npm ci
npm run check
npm audit
CSC_IDENTITY_AUTO_DISCOVERY=false npx electron-builder --mac zip --arm64 --x64 --publish never
```
Linux Electron launch smoke: `npm run smoke:headless`. This checks the shared application, not the macOS executable.
ZIP packaging works on Linux with this electron-builder version, but its default ZIP output dereferences framework symlinks. The first archives failed macOS Archive Utility with Error 94. Repackage the unpacked apps with Unix symlinks preserved and standard ZIP metadata:
```sh
python scripts/package-mac-zip.py 'dist/mac-arm64/Alta Video Player.app' dist/Alta-Video-Player-0.1.1-mac.1-mac-arm64-repacked.zip
python scripts/package-mac-zip.py 'dist/mac/Alta Video Player.app' dist/Alta-Video-Player-0.1.1-mac.1-mac-x64-repacked.zip
```
The replacement archives pass CRC, file-byte/mode and symlink-target checks on Linux; macOS Archive Utility compatibility still requires a user test. Signing/notarization and a native macOS launch test remain separate gates. No claim is made that an unsigned app will pass Gatekeeper or Apple Silicon signature enforcement.
## Test on a Mac
1. Apple menu > About This Mac: Apple M-series uses arm64; Intel uses x64.
2. Download the matching ZIP from this prerelease and extract it.
3. Move Alta Video Player.app to Applications and try opening it.
4. If macOS blocks it, report the exact message. Do not disable Gatekeeper globally. A scoped Open Anyway option may be available under System Settings > Privacy & Security for this trusted test build; signature failures may instead require a Mac-built/ad-hoc-signed replacement.
5. Import a known video, then a representative export ZIP (including an encrypted one if available).
6. Check playback, seeking, speed, multiple camera synchronization and zoom.
7. Check integrity/certificate verification with a known export.
8. Quit and reopen. Report macOS version, processor, and any errors. Keep customer footage and export contents out of GitPeji issues.
Production remains on its existing stable release. This prerelease does not deliberately change the production download button; publish validated Mac assets to the stable release only after acceptance, retaining Windows downloads.
+11 -11
View File
@@ -1,12 +1,12 @@
{ {
"name": "webavp-desktop", "name": "webavp-desktop",
"version": "0.1.0", "version": "0.1.1-mac.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "webavp-desktop", "name": "webavp-desktop",
"version": "0.1.0", "version": "0.1.1-mac.1",
"devDependencies": { "devDependencies": {
"electron": "^43.4.1", "electron": "^43.4.1",
"electron-builder": "^26.15.3" "electron-builder": "^26.15.3"
@@ -734,9 +734,9 @@
} }
}, },
"node_modules/@xmldom/xmldom": { "node_modules/@xmldom/xmldom": {
"version": "0.8.14", "version": "0.8.15",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.14.tgz", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.15.tgz",
"integrity": "sha512-T4EDRUBVZYRldYApjEJiU0e1stYWaRAX7CuSnKzrpwdZKo53zGV8/pqfzV6FfwNl9YThD2OumQYvqtvjvgG7aQ==", "integrity": "sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -2026,9 +2026,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/fast-uri": { "node_modules/fast-uri": {
"version": "3.1.5", "version": "3.1.7",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz",
"integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -2551,9 +2551,9 @@
} }
}, },
"node_modules/js-yaml": { "node_modules/js-yaml": {
"version": "4.3.1", "version": "4.3.2",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "webavp-desktop", "name": "webavp-desktop",
"version": "0.1.0", "version": "0.1.1-mac.1",
"private": true, "private": true,
"description": "Standalone desktop shell for Alta Video Player.", "description": "Standalone desktop shell for Alta Video Player.",
"desktopName": "alta-video-player.desktop", "desktopName": "alta-video-player.desktop",
+53
View File
@@ -0,0 +1,53 @@
"""Package an existing macOS app with Unix symlinks intact (stdlib only)."""
import argparse
import os
from pathlib import Path
import stat
import time
import zipfile
def package(app, output):
app = Path(app).resolve()
output = Path(output).resolve()
assert app.is_dir() and app.suffix == '.app'
with zipfile.ZipFile(output, 'w', compression=zipfile.ZIP_DEFLATED, compresslevel=6) as archive:
def add(path):
st = path.lstat()
name = path.relative_to(app.parent).as_posix()
is_dir = stat.S_ISDIR(st.st_mode)
info = zipfile.ZipInfo(name + ('/' if is_dir else ''), time.localtime(st.st_mtime)[:6])
info.create_system = 3
info.external_attr = st.st_mode << 16
if is_dir:
info.external_attr |= 0x10
info.compress_type = zipfile.ZIP_STORED if is_dir else zipfile.ZIP_DEFLATED
if stat.S_ISLNK(st.st_mode):
archive.writestr(info, os.readlink(path).encode('utf-8'))
elif is_dir:
archive.writestr(info, b'')
for child in sorted(path.iterdir()):
add(child)
elif stat.S_ISREG(st.st_mode):
with path.open('rb') as src, archive.open(info, 'w') as dst:
while chunk := src.read(1024 * 1024):
dst.write(chunk)
else:
raise ValueError(f'Unsupported file type: {path}')
add(app)
with zipfile.ZipFile(output) as archive:
assert archive.testzip() is None
for item in archive.infolist():
source = app.parent / item.filename.rstrip('/')
assert (item.external_attr >> 16) == source.lstat().st_mode
if source.is_symlink():
assert archive.read(item).decode('utf-8') == os.readlink(source)
print(output)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('app')
parser.add_argument('output')
args = parser.parse_args()
package(args.app, args.output)