Files
Archive/scripts/build-portable.ps1
T
ncakanghanandClaude Opus 5 8aa74c1a8d
CI / windows-x64 (push) Canceled after 0s
CI / windows-arm64-cross (push) Canceled after 0s
CI / windows-arm64-native (push) Canceled after 0s
CI / macos (push) Canceled after 0s
영상 중복 탐지 완성 + 간단 편집(회전·자르기) + 동시 재생 + 별점·태그 세트
중복 탐지
- 이미지 근접쌍 밴드 색인 + 스트리밍 클러스터, 후보 검증(타일 다수결·크롭 해시)
- 색 지문으로 색만 다른 오탐 제거, 회전·반전본 탐지(8변형 질의, 선택 옵션)
- 오디오 지문(chromaprint)을 같은 디코드 패스에서 뽑아 레터박스 사본을 잡는다.
  배경음만 같은 남남은 시각 확인 패스(트림 축 프레임 거리)로 걸러낸다 — 실측 9 vs 29.
- 비교 재생(CompareView): 여러 사본을 offset 정렬해 나란히 재생
- 지문 생성 속도 조절(느림·보통·빠름) — 재시작 없이 반영

간단 편집 (원본을 직접 고침, Ctrl+Z로 되돌림)
- 회전·반전: JPEG은 EXIF 방향 태그 2바이트만, MP4는 회전 행렬만 — 화질 손실 0.
  방향값 전이표는 군의 성질(90도x4=제자리)로 검산하고, 실제 화소를 transpose=1과
  비교해 "시계 방향"이 정말 시계인지 확인한다.
- 자르기: 화면 좌표 기준(회전 메타가 붙어 있어도 어긋나지 않는다)
- 구간 자르기: 스트림 복사라 무손실. 필름스트립 타임라인 + 손잡이 끌기 + 구간 미리보기.
- 되돌릴 수 없는 편집(자르기·반전)은 원본을 .archive_trash/edits로 옮겨 보관
- 편집이 수정시각을 바꾸지 않는다 — 기본 정렬이 촬영일이라 항목이 튀면 다시 찾아야 한다

동시 재생
- 고른 영상을 최대 9개 격자에 놓고 함께 재생. 소리는 한 칸만.
- 실시간 변환이 필요한 코덱은 2개까지만 — 그 이상은 ffmpeg가 기계를 멈춘다

그 외
- 별점(스냅샷 flags 남는 비트에 얹어 항목당 바이트 증가 0)
- 태그: 검색 없이 클릭으로 붙이기, 세트+단축키, 이름 수정 시 붙은 항목에 전파
- 파일 로깅(tracing-appender) — 릴리스는 콘솔이 없어 stdout만으로는 원인을 못 찾았다
- ARM64 크로스 컴파일 스크립트(관리자 권한 없이 VS 카탈로그에서 조립)
- DB 마이그레이션 v7~v10

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 16:18:24 +09:00

146 lines
7.4 KiB
PowerShell

# Archive portable build (no installer). ASCII only - PS 5.1 reads BOM-less files as ANSI.
#
# Output: dist-portable\Archive-<arch>\ (exe + ffmpeg/ffprobe sidecars + portable.marker)
# The app already supports portable mode: src-tauri/src/paths.rs uses <exe>\data when a
# `data\` folder or `portable.marker` sits next to the exe, instead of %APPDATA%\Archive.
#
# Usage: .\scripts\build-portable.ps1 [-Arch arm64|x64] [-Zip]
#
# ARM64 needs the MSVC cross tools ("MSVC ... ARM64 build tools" in the VS installer) and
# `rustup target add aarch64-pc-windows-msvc`. The script checks both and says what is missing.
param(
[ValidateSet("arm64", "x64")]
[string]$Arch = "arm64",
[switch]$Zip
)
$ErrorActionPreference = "Stop"
$root = Split-Path $PSScriptRoot -Parent
Set-Location $root
$triple = if ($Arch -eq "arm64") { "aarch64-pc-windows-msvc" } else { "x86_64-pc-windows-msvc" }
# vcvarsall argument: cross builds need "<host>_<target>", native is just the target.
$vcArg = if ($Arch -eq "arm64") { "x64_arm64" } else { "x64" }
Write-Host "[portable] target $triple"
# --- toolchain checks -------------------------------------------------------
if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) {
$cargoBin = Join-Path $env:USERPROFILE ".cargo\bin"
if (Test-Path (Join-Path $cargoBin "cargo.exe")) { $env:PATH = "$cargoBin;$env:PATH" }
else { throw "cargo not found. Install Rust (https://rustup.rs) and reopen the shell." }
}
$installed = & rustup target list --installed
if ($installed -notcontains $triple) {
throw "Rust target missing: $triple`n fix: rustup target add $triple"
}
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
if (-not (Test-Path $vswhere)) { throw "vswhere.exe not found - is Visual Studio installed?" }
$vsPath = & $vswhere -latest -products * -property installationPath
if (-not $vsPath) { throw "No Visual Studio installation found." }
$vcvars = Join-Path $vsPath "VC\Auxiliary\Build\vcvarsall.bat"
if (-not (Test-Path $vcvars)) { throw "vcvarsall.bat not found at $vcvars" }
# ARM64 needs a cross toolchain. Prefer the one installed in Visual Studio; fall back to the
# local copy laid out by fetch-arm64-toolchain.ps1 (that script explains why it exists).
$localTc = $null
if ($Arch -eq "arm64") {
$hasArm = Get-ChildItem (Join-Path $vsPath "VC\Tools\MSVC") -Directory -ErrorAction SilentlyContinue |
ForEach-Object { Test-Path (Join-Path $_.FullName "bin\Hostx64\arm64\cl.exe") } |
Where-Object { $_ }
if (-not $hasArm) {
$localTc = Get-ChildItem (Join-Path $root ".toolchain\arm64\VC\Tools\MSVC") -Directory -ErrorAction SilentlyContinue |
Sort-Object Name | Select-Object -Last 1
if (-not $localTc -or -not (Test-Path (Join-Path $localTc.FullName "bin\Hostx64\arm64\cl.exe"))) {
throw @"
MSVC ARM64 cross tools missing.
fix (no admin): .\scripts\fetch-arm64-toolchain.ps1
or (Visual Studio): Installer -> Modify -> Individual components
-> "MSVC v14x - VS 20xx C++ ARM64/ARM64EC build tools"
"@
}
Write-Host "[portable] using local toolchain: $($localTc.FullName)"
}
}
# --- sidecars ---------------------------------------------------------------
$ffmpeg = Join-Path $root "src-tauri\binaries\ffmpeg-$triple.exe"
$ffprobe = Join-Path $root "src-tauri\binaries\ffprobe-$triple.exe"
foreach ($f in @($ffmpeg, $ffprobe)) {
if (-not (Test-Path $f)) { throw "sidecar missing: $f" }
}
# --- build ------------------------------------------------------------------
# vcvarsall must be loaded into THIS process, so run it in cmd and import the env back.
# With the local toolchain we ask vcvarsall for the plain x64 env (it has no arm64 target)
# and then splice the ARM64 compiler/libs in front of it ourselves.
$vcvarsArg = if ($localTc) { "x64" } else { $vcArg }
Write-Host "[portable] loading MSVC env ($vcvarsArg)"
$envDump = & cmd /c "`"$vcvars`" $vcvarsArg >nul 2>&1 && set"
if ($LASTEXITCODE -ne 0) { throw "vcvarsall $vcvarsArg failed" }
foreach ($line in $envDump) {
if ($line -match '^([^=]+)=(.*)$') { Set-Item -Path "env:$($matches[1])" -Value $matches[2] }
}
if ($localTc) {
# Headers are architecture-neutral and already come from vcvarsall/the SDK.
# Only the compiler binaries and the CRT import libs differ, so override just those:
# PATH -> Hostx64\arm64 first (cl.exe, link.exe, lib.exe target ARM64)
# LIB -> the ARM64 CRT, then the SDK's arm64 libs, dropping the x64 entries
$armBin = Join-Path $localTc.FullName "bin\Hostx64\arm64"
$armLib = Join-Path $localTc.FullName "lib\arm64"
if (-not (Test-Path $armLib)) { throw "ARM64 CRT libs missing: $armLib" }
# x64 host tools stay reachable behind the ARM64 ones - some build scripts need them.
$env:PATH = "$armBin;$env:PATH"
$sdkLibs = ($env:LIB -split ';' | Where-Object { $_ -match '\\Windows Kits\\' } |
ForEach-Object { $_ -replace '\\x64$', '\arm64' }) -join ';'
$env:LIB = "$armLib;$sdkLibs"
$env:VSCMD_ARG_TGT_ARCH = "arm64"
# cc-rs (libsqlite3-sys, aws-lc-sys, ...) picks its compiler per target triple.
# **clang-cl, not cl.exe**: aws-lc-sys and ring ship their aarch64 assembly as .S, and
# cl.exe answers "unrecognized source file type" and silently SKIPS those files - the
# build then dies at link time on a missing .o. clang-cl assembles them and stays
# MSVC-compatible, so the objects link against the same CRT.
$llvmBin = Join-Path $root ".toolchain\llvm\bin"
$armClang = Join-Path $llvmBin "clang-cl.exe"
if (-not (Test-Path $armClang)) { throw "clang-cl missing - run .\scripts\fetch-arm64-toolchain.ps1" }
# On PATH too: some build scripts (ring) look up plain "clang" themselves rather than
# honouring CC_<triple>, and cc-rs resolves the compiler family by running it.
$env:PATH = "$llvmBin;$env:PATH"
$env:CC_aarch64_pc_windows_msvc = $armClang
$env:CFLAGS_aarch64_pc_windows_msvc = "--target=aarch64-pc-windows-msvc"
$env:AR_aarch64_pc_windows_msvc = Join-Path $armBin "lib.exe"
$env:CARGO_TARGET_AARCH64_PC_WINDOWS_MSVC_LINKER = Join-Path $armBin "link.exe"
}
Write-Host "[portable] tauri build (no bundle)"
& npx tauri build --target $triple --no-bundle
if ($LASTEXITCODE -ne 0) { throw "build failed" }
# --- assemble ---------------------------------------------------------------
$exe = Join-Path $root "src-tauri\target\$triple\release\archive.exe"
if (-not (Test-Path $exe)) { throw "built exe not found: $exe" }
$outDir = Join-Path $root "dist-portable\Archive-$Arch"
if (Test-Path $outDir) { Remove-Item $outDir -Recurse -Force }
New-Item -ItemType Directory -Path $outDir -Force | Out-Null
Copy-Item $exe (Join-Path $outDir "Archive.exe")
# Drop the triple suffix: locate() looks for both, and the plain name is what a user expects.
Copy-Item $ffmpeg (Join-Path $outDir "ffmpeg.exe")
Copy-Item $ffprobe (Join-Path $outDir "ffprobe.exe")
# Marker = keep the library DB and thumbnails inside this folder, not in %APPDATA%.
Set-Content -Path (Join-Path $outDir "portable.marker") -Value "" -Encoding ascii
$size = [math]::Round(((Get-ChildItem $outDir -Recurse | Measure-Object Length -Sum).Sum / 1MB), 1)
Write-Host "[portable] $outDir ($size MB)"
Get-ChildItem $outDir | ForEach-Object { " {0,-16} {1,10:N0}" -f $_.Name, $_.Length }
if ($Zip) {
$zipPath = Join-Path $root "dist-portable\Archive-$Arch.zip"
if (Test-Path $zipPath) { Remove-Item $zipPath -Force }
Compress-Archive -Path $outDir -DestinationPath $zipPath
Write-Host "[portable] $zipPath"
}