#!/bin/bash # # Rabeat 맥 설치 — 시험 배포용 # # curl -fsSL https://rabeat.net/install/mac | bash # # ## 왜 터미널 한 줄인가 — **지금은 이게 문턱이 제일 낮다** # # 애플 공증을 아직 못 붙였다(계정 승인 대기 중). 공증 없는 `.command` 를 더블클릭하면 # macOS 26 에서 이렇게 뜬다(2026-08-31 실측 · macOS 26.6.2 · Apple M1): # # ⚠ Apple은 … 악성 코드가 없음을 확인할 수 없습니다 # [ 휴지통으로 이동 ] ← 파란색 · **기본 단추** # [ 완료 ] # # 「열기」 단추가 아예 없고, 기본이 **삭제**다. 넘어가려면 다섯 걸음이고 마지막에 암호를 묻는다. # 그런데 **`curl` 로 받은 것에는 격리 딱지가 안 붙는다**(같은 날 실측). 그래서 이 길은 # 문턱이 아예 없다. # # **공증이 붙으면 이 파일은 없어진다.** 그때는 더블클릭 한 번으로 끝나는 설치 프로그램이 # 대신 들어간다. 이건 그때까지 쓰는 임시 다리다. # # ## 윈도우 설치 프로그램(Installer.cs)과 같은 일을 한다 # # 목록 받기 → 조각마다 SHA-256 대조 → 이미 있는 것은 건너뛰기 → 제자리에 풀기 # # 다른 점은 **목록이 따로**라는 것뿐이다(`manifest-mac.json`). 같은 목록에 맥 조각을 넣으면 # 윈도우 설치 프로그램이 `kind` 를 보기 **전에** 내려받아 버려서, 윈도우 사용자가 23MB 를 # 헛으로 받는다(코드 확인: Installer.cs 176~190행). **옛 판이 안 깨지는 게 먼저다.** set -euo pipefail BASE="${RABEAT_BASE:-https://rabeat.net}" EXT="$HOME/Library/Application Support/Adobe/CEP/extensions/com.rabeat.subtitle" DATA="$HOME/Library/Application Support/rabeat" MARK="$DATA/.설치기록" # 무엇을 이미 받았는지 — 해시로 적는다 say() { printf '\033[1m▸\033[0m %s\n' "$*"; } warn() { printf '\033[33m %s\033[0m\n' "$*"; } die() { printf '\033[31m✗ %s\033[0m\n' "$*" >&2; exit 1; } [ "$(uname)" = "Darwin" ] || die "이건 맥용입니다." # 프리미어가 켜져 있으면 확장 폴더를 갈아 끼워도 반영되지 않는다. 미리 알려 준다 — # 다 깔고 나서 "왜 안 보이지" 를 겪는 것보다 낫다. if pgrep -qx "Adobe Premiere Pro 2026" 2>/dev/null || pgrep -qf "Adobe Premiere Pro" >/dev/null 2>&1; then warn "프리미어가 켜져 있습니다. 설치는 되지만 **끄고 다시 켜야** 패널이 보입니다." fi say "목록 받는 중" MAN="$(mktemp)"; trap 'rm -f "$MAN"' EXIT curl -fsSL "$BASE/install/manifest-mac.json" -o "$MAN" || die "목록을 못 받았습니다. 인터넷을 확인해 주세요." mkdir -p "$DATA/models" "$DATA/bin" "$EXT" touch "$MARK" # JSON 을 셸로 읽는다. `python3` 은 맥에 있을 수도 없을 수도 있다(명령줄 도구를 깔아야 나온다). # **준비물이 없어야 한다**는 원칙은 설치하는 쪽에도 똑같이 적용된다. # # 줄바꿈을 지우고 `{` 로 자르면 **객체 하나가 한 줄**이 된다. 들여쓰기가 어떻든 상관없다 — # 처음에는 `},{` 를 찾아 잘랐는데 실제 목록은 `}, {` (사이에 공백) 이라 하나도 안 잘렸다. fields=$(tr -d '\n' < "$MAN" | tr '{' '\n') n=0 # `|| [ -n "$line" ]` — 끝에 줄바꿈이 없을 때 `read` 가 마지막 줄을 버리는 것을 막는다. # 지금은 `<<<` 로 넘기므로 줄바꿈이 붙어 없어도 되지만(실측: 넷 다 읽힘), 파이프로 바꾸는 # 순간 **마지막 조각이 조용히 빠진다**(파이프로 재 보니 넷 중 셋). 하필 마지막이 silero VAD # 모델이라 빠지면 무음 삭제가 엉뚱한 오류로 실패한다. 한 글자로 막을 수 있으면 막아 둔다. while IFS= read -r line || [ -n "$line" ]; do [ -n "$line" ] || continue url=$( printf '%s' "$line" | sed -n 's/.*"url"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') sha=$( printf '%s' "$line" | sed -n 's/.*"sha256"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') target=$(printf '%s' "$line" | sed -n 's/.*"target"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') kind=$( printf '%s' "$line" | sed -n 's/.*"kind"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') [ -n "$url" ] && [ -n "$sha" ] || continue n=$((n+1)) # 이미 같은 것이 깔려 있으면 건너뛴다. 1GB 를 매번 다시 받지 않으려고. # 확장(zip-ext)은 판마다 바뀌므로 늘 다시 깐다 — 크기가 작아 아깝지 않다. # 열쇠는 **kind 와 target 을 함께** 쓴다. kind 만 쓰면 모델이 둘이라 서로를 지운다 — # 두 번째 모델을 적는 순간 첫 번째 기록이 사라지고, 다음 실행에 1GB 를 또 받는다. key="$kind|$target" if [ "$kind" != "zip-ext" ] && grep -qxF "$key $sha" "$MARK" 2>/dev/null; then say "이미 최신 — $target" continue fi say "내려받는 중 — $target" TMP="$DATA/.받는중-${sha:0:16}.part" # `-C -` 로 끊긴 자리부터 잇는다. 1GB 를 처음부터 다시 받게 하면 안 된다. curl -fL --retry 3 --progress-bar -C - "$url" -o "$TMP" || die "$target 내려받기 실패" say "파일 확인 중 — $target" got=$(shasum -a 256 "$TMP" | cut -d' ' -f1) # **받은 것을 재서 대조한다.** 중간에서 바꿔치기한 파일도, 받다 깨진 파일도 여기서 걸린다. [ "$got" = "$sha" ] || { rm -f "$TMP"; die "$target 이(가) 손상됐습니다. 다시 실행해 주세요. 적힌 것 $sha 받은 것 $got"; } say "설치 중 — $target" case "$kind" in zip-ext) # 확장은 통째로 갈아 끼운다. 옛 파일이 섞이면 서명이 깨진다. rm -rf "$EXT"; mkdir -p "$EXT" /usr/bin/unzip -qo "$TMP" -d "$EXT" || die "확장 압축을 못 풀었습니다" ;; zip-bin-mac) # bin-mac.zip 은 안에 `bin/mac/` 을 이미 갖고 있다 → 데이터 폴더에 그대로 푼다. # # **`unzip` 은 경고만 있어도 1 을 돌려준다.** 2 이상이 진짜 오류다. # 2026-09-02 에 이것 때문에 맥 설치가 통째로 깨졌다 — 윈도우에서 만든 zip 의 경로가 # 역슬래시였고, 맥 unzip 이 파일은 제대로 풀면서 경고와 함께 1 을 냈다. # `|| die` 가 그걸 못 갈라서 **뒤따르는 chmod 를 통째로 건너뛰었고**, 실행 권한이 # 하나도 안 붙어 아무 기능도 안 돌았다. 코드로만 가르면 안 된다. # # `set -e` 가 걸려 있어 `cmd; rc=$?` 로 쓰면 **`rc=$?` 에 닿기 전에 죽는다.** rc=0; /usr/bin/unzip -qo "$TMP" -d "$DATA" || rc=$? [ "$rc" -le 1 ] || die "실행 파일 압축을 못 풀었습니다 (unzip $rc)" # 권한은 zip 안에도 0755 로 박아 두었다(`make-bin-mac.js`). 여기서 또 준다 — # **둘 중 하나가 빠져도 돌아야 한다.** 하위 폴더까지 훑는다(`pdf/pdftotext`). find "$DATA/bin/mac" -type f -exec chmod +x {} \; 2>/dev/null || true # **풀렸는지는 끝난 코드가 아니라 파일로 확인한다.** 이것이 없어서 지난번에 # 「설치 실패」라는 말만 보고 무엇이 없는지는 몰랐다. for _f in ffmpeg ffprobe whisper-cli whisper-vad-speech-segments; do [ -x "$DATA/bin/mac/$_f" ] || die "$_f 가 제자리에 없습니다. 다시 실행해 주세요." done ;; model) mv -f "$TMP" "$DATA/models/$target" ;; *) warn "모르는 종류라 건너뜁니다 — $kind" ;; esac [ -f "$TMP" ] && rm -f "$TMP" # 기록은 **설치가 끝난 뒤에** 남긴다. 먼저 남기면 중간에 죽었을 때 다음 실행이 건너뛴다. # 같은 열쇠의 옛 줄만 걷어낸다 — `grep` 이 아무것도 못 찾으면 1을 돌려주므로 `|| true`. grep -vF "$key " "$MARK" > "$MARK.new" 2>/dev/null || true mv -f "$MARK.new" "$MARK" printf '%s %s\n' "$key" "$sha" >> "$MARK" done <<< "$fields" [ "$n" -gt 0 ] || die "목록이 비어 있습니다. 맥 판이 아직 안 올라갔을 수 있어요." # 격리 딱지를 뗀다. curl 로 받았으면 애초에 안 붙지만, 다른 길로 온 파일이 섞여 있을 수 있다. # 붙어 있으면 실행 파일이 **대화상자도 없이 그냥 죽는다** — 원인을 찾기 어려운 종류다. xattr -dr com.apple.quarantine "$EXT" "$DATA/bin" 2>/dev/null || true echo say "다 됐습니다" echo " 확장 $EXT" echo " 자료 $DATA" echo echo " 프리미어를 **완전히 끄고**(⌘Q) 다시 켠 뒤 창 → 확장 에서 Rabeat 패널을 여세요." echo