| 158 | |
| 159 | |
| 160 | def repack_portable(portable_zip: Path, signed_exe: Path, sevenz: Path) -> None: |
| 161 | with tempfile.TemporaryDirectory() as tmp: |
| 162 | tree = Path(tmp) |
| 163 | print(f"Extracting {portable_zip.name}...") |
| 164 | with zipfile.ZipFile(portable_zip) as zf: |
| 165 | zf.extractall(tree) |
| 166 | target = tree / "scenedetect.exe" |
| 167 | if not target.exists(): |
| 168 | sys.exit(f"scenedetect.exe not found inside {portable_zip}") |
| 169 | print(" swapping in signed scenedetect.exe") |
| 170 | shutil.copy2(signed_exe, target) |
| 171 | # Preserve the unsigned AppVeyor zip alongside the signed output for |
| 172 | # diffing / recovery. Overwrite any prior .unsigned from a re-run. |
| 173 | backup = portable_zip.with_suffix(".unsigned.zip") |
| 174 | if backup.exists(): |
| 175 | backup.unlink() |
| 176 | portable_zip.rename(backup) |
| 177 | print(f" preserved original as {backup.name}") |
| 178 | print(f"Repacking {portable_zip.name} (zip / Deflate / mx=9 / mt=on)...") |
| 179 | # -mm=Deflate (not LZMA): Windows Explorer's built-in "Extract All" only |
| 180 | # supports Deflate-compressed zips; LZMA needs 7-Zip/WinRAR. Portable .zip |
| 181 | # ships to end users on clean Windows, so compat trumps ratio here. |
| 182 | # -mfb=258 -mpass=15: max-out Deflate tuning (slow, but once per release). |
| 183 | # -mmt=on: 7z parallelizes Deflate across files (not within a file), so |
| 184 | # the docs/ + thirdparty/ tree gets a real speedup; the two big binaries |
| 185 | # (scenedetect.exe, ffmpeg.exe) still each compress on a single thread. |
| 186 | # Pass top-level entries (not '*') so we don't depend on shell globbing. |
| 187 | entries = sorted(p.name for p in tree.iterdir()) |
| 188 | subprocess.run( |
| 189 | [ |
| 190 | str(sevenz), |
| 191 | "a", |
| 192 | "-tzip", |
| 193 | "-mm=Deflate", |
| 194 | "-mx=9", |
| 195 | "-mfb=258", |
| 196 | "-mpass=15", |
| 197 | "-mmt=on", |
| 198 | str(portable_zip), |
| 199 | *entries, |
| 200 | ], |
| 201 | cwd=tree, |
| 202 | check=True, |
| 203 | capture_output=True, |
| 204 | ) |
| 205 | print(f" {portable_zip.stat().st_size / (1024 * 1024):.1f} MB") |
| 206 | |
| 207 | |
| 208 | def write_manifests(staging: Path, portable_zip: Path, msi: Path) -> None: |