Files
GhiVideo_v4/PROGRESS.md
T
b23042andClaude Fable 5 d38b842e8d GhiVideo 소스 복제 — v4 작업 시작 기준
기존 GhiVideo 저장소 HEAD의 트래킹 소스 362개 파일을 복제.
(node_modules·storage·빌드 산출물·대용량 미디어는 .gitignore 규칙대로 제외)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 14:32:51 +09:00

18 KiB
Executable File
Raw Blame History

PROGRESS.md — abcvideo 진행 상태

이 파일은 각 단계의 진행 상태를 기록합니다. 에이전트는 작업 시작/완료 시 반드시 이 파일을 업데이트합니다.


현재 상태 요약

  • 마지막 완료 단계: 구조물 시설종별(1종/2종/3종) 표시 필터 (2026-06-19)
  • 직전 완료 단계: 폴더 임포트 v2.0 데이터 형상 전용화 (2026-06-19)
  • 블로커: FFmpeg 미설치 (HLS/프레임 기능은 FFmpeg 설치 후 동작)
  • 검증 결과: VERIFICATION.md 참조 (모든 항목 통과, V3-3 RAF 버그 1건 수정 완료)
  • v2.0 전환: geoData.ts/types/geo.ts/geoStore.ts/StationBar.tsx 수정, client 빌드 통과

단계별 진행 기록

단계 1: 프로젝트 구조 세팅

  • 상태: 완료 (2026-03-24)
  • 완료 항목:
    • 루트 package.json (npm workspaces)
    • shared/src/types.ts (공유 타입)
    • server/ 스캐폴드 + tsx dev 환경
    • client/ Vite + React 18 + TypeScript + Tailwind
    • storage/ 디렉토리 구조
    • npm install 성공 (514 packages)
    • server TypeScript 컴파일 에러 없음
    • GET /api/health 응답 확인
  • 미완료/이슈:
    • better-sqlite3 ^12.8.0으로 업그레이드 (Node 24 prebuilt binary 문제)
  • 다음 단계 참고:
    • server/package.json에 better-sqlite3 ^12.8.0 사용 중
    • client Vite dev는 아직 미확인 (브라우저 확인 필요)
    • server/src/app.ts는 최소 스캐폴드 — 단계 2에서 확장

단계 2: 백엔드 서버

  • 상태: 완료 (2026-03-24)
  • 완료 항목:
    • server/src/config.ts — 환경변수 기반 설정
    • server/src/services/ffmpeg.ts — FFprobe/FFmpeg spawn 래퍼 (probeVideo, runFFmpeg, getVideoFps 등)
    • server/src/services/streaming.ts — Range Request 스트리밍 (10MB 청크, 1MB highWaterMark)
    • server/src/services/storage.ts — better-sqlite3 초기화, Annotation CRUD, 임시파일 정리
    • server/src/middleware/security.ts — pathTraversalGuard, validateMimeType, securityHeaders
    • server/src/routes/stream.ts — GET /api/stream/:videoId
    • server/src/routes/hls.ts — POST /convert, GET /status, GET /progress(SSE), GET /index.m3u8, GET /:segment
    • server/src/routes/frame.ts — GET /api/frame/:videoId?time=|frame=
    • server/src/routes/upload.ts — 청크 업로드 (init/chunk/complete/status), @tus/server 대신 자체 구현
    • server/src/routes/meta.ts — GET /api/videos, GET /api/meta/:videoId, DELETE /api/videos/:videoId
    • server/src/routes/annotations.ts — CRUD + export(vtt/srt/json/csv)
    • server/src/app.ts — 전체 통합, 헬스체크, 에러 핸들러
    • server/.env — 환경변수 파일
    • server/tsconfig.json — rootDirs 수정 (shared 타입 접근 허용)
    • GET /api/health, /api/videos, /api/annotations/:videoId 정상 응답 확인
  • 미완료/이슈:
    • @tus/server 2.3.0이 ESM-only("type":"module")라 CommonJS 서버와 호환 불가 → 자체 청크 업로드 구현으로 대체
    • FFmpeg 시스템 PATH 미등록 상태 → HLS 변환/프레임 추출은 FFmpeg 설치 후 동작
  • 다음 단계 참고:
    • 청크 업로드 클라이언트는 POST /api/upload/init → POST /api/upload/chunk (X-Upload-Id, X-Chunk-Index 헤더, application/octet-stream body) → POST /api/upload/complete 순서 사용
    • HLS 변환 완료 후 /api/hls/:videoId/index.m3u8 로 플레이리스트 접근
    • 서버는 포트 3001에서 실행 중

단계 3: 기본 플레이어

  • 상태: 완료 (2026-03-24)
  • 완료 항목:
    • client/src/types/player.ts — VideoSource, PlayerState 타입
    • client/src/store/playerStore.ts — Zustand 플레이어 상태 스토어
    • client/src/store/annotationStore.ts — 주석 Zustand 스토어
    • client/src/hooks/useVideoPlayer.ts — Video.js 8 초기화, loadLocalFile, loadServerStream, switchToHls
    • client/src/components/player/VideoPlayer.tsx — forwardRef + VideoPlayerHandle API
    • client/src/components/player/HlsConversionStatus.tsx — SSE 진행률 바
    • client/src/components/sidebar/VideoList.tsx — 서버 영상 목록
    • client/src/App.tsx — 전체 레이아웃 통합
    • npm install: video.js@^8.23.0, @videojs/http-streaming, hls.js@^1.6.15, interactjs@^1.10.27, subsrt-ts@^2.0.3
    • TypeScript 에러 없음, 빌드 성공 (dist 1.47MB)
  • 미완료/이슈:
    • interact.js 패키지명 오류 → interactjs (하이픈 없음) 으로 설치
    • 빌드 경고: chunk > 500kB (video.js 특성상 정상, 운영환경 분할 불필요)
  • 다음 단계 참고:
    • VideoPlayer는 forwardRef 사용 — App에서 playerRef.current?.loadServerStream() 호출
    • hls.js backBufferLength: 30 설정 완료
    • MemoOverlay는 interactjs 사용 (import from 'interactjs')

단계 4: 프레임 추출

  • 상태: 완료 (2026-03-24)
  • 완료 항목:
    • client/src/utils/frameCapture.ts — 단일 Canvas 재사용, captureFrame, downloadDataUrl
    • client/src/components/player/FrameCaptureButton.tsx — Shift+S 캡처 버튼
    • VideoPlayer에 캡처 통합 (getVideoElement → captureFrame → downloadDataUrl)
  • 미완료/이슈: 서버 FFmpeg 정확 추출(로컬 파일 정확 추출 시)은 미구현 — Canvas 즉시 미리보기만 구현
  • 다음 단계 참고: (없음)

단계 5: 단위 이동

  • 상태: 완료 (2026-03-24)
  • 완료 항목:
    • client/src/hooks/useFrameStep.ts — requestVideoFrameCallback FPS 감지, stepForward/stepBackward
    • client/src/hooks/useKeyboard.ts — CLAUDE.md 키보드 단축키 전체 구현
    • 프레임/초/장면 단위 이동 완성 (Space/←/→/J/L/,/./[/]/F/M/+/-/Shift+S/Shift+M/0-9)
  • 미완료/이슈: (없음)
  • 다음 단계 참고: (없음)

단계 6: 텍스트 오버레이

  • 상태: 완료 (2026-03-24)
  • 완료 항목:
    • client/src/hooks/useAnnotations.ts — 주석 CRUD API 연동
    • client/src/components/overlay/MemoOverlay.tsx — interactjs 드래그, x%/y% 좌표
    • client/src/components/sidebar/AnnotationPanel.tsx — 자막/메모 탭, 내보내기(VTT/SRT/JSON/CSV)
    • client/src/components/AddAnnotationModal.tsx — 주석 추가 모달
    • client/src/utils/timecode.ts — secondsToTimecode, timecodeToSeconds, frameToSeconds, secondsToFrame
  • 미완료/이슈:
    • WebVTT track 요소를 통한 자막형 렌더링은 미구현 (주석 저장/표시는 완성, track 연동은 단계 7)
  • 다음 단계 참고:
    • 자막형 WebVTT track 연동은 단계 7 UI/UX 통합 시 추가 가능
    • 내보내기는 /api/annotations/:videoId/export?format=vtt|srt|json|csv 백엔드 API 사용

검증 (VERIFICATION)

  • 상태: 완료 (2026-03-24)
  • 완료 항목:
    • VERIFICATION.md 작성 및 전 항목 검증 실행
    • V1 정적 검증: server/client tsc 에러 없음, 빌드 성공
    • V2 서버 API: 헬스체크/영상목록/업로드/주석 CRUD/내보내기/보안 모두 통과
    • V3 코드 품질: 10/10 통과 (MemoOverlay RAF 버그 발견 및 수정)
    • V4 파일 구조: 19개 필수 파일 전부 존재 확인
  • 수정 사항:
    • VideoPlayerHandlegetVideoElement() 추가 (VideoPlayer.tsx)
    • App.tsx RAF 루프 추가 → MemoOverlay currentTime 60fps 업데이트

기능 확장: 카메라 파라미터 보정 패널 (2026-03-26)

  • 상태: 완료
  • 완료 항목:
    • client/src/utils/geoProjection.ts — CameraParams 인터페이스 추가, projectPoint 시그니처 변경
      • yawOffset(number) → params(CameraParams) 로 통합
      • Roll 회전 수학 추가 (Rodrigues, fwd 축 기준): right_r = right0·cosR up0·sinR
      • 주점 오프셋(cx0, cy0) 핀홀 투영에 적용
      • 초점 배율(focalScale) 적용: f_eff = focalLen × focalScale
      • pxRaw/pyRaw 필드 추가 (클램프 전 원본값, FOV 이탈 감지용)
      • DEFAULT_CAMERA_PARAMS export
    • client/src/components/overlay/StationOverlay.tsx — 파라미터 패널 UI 전면 개편
      • ParamRow 컴포넌트: 슬라이더 + 직접 수치 입력(Enter/blur 커밋) 양방향 동기화
      • 자세 보정 섹션: Yaw(-180~+180°), Pitch(-45~+45°), Roll(-45~+45°)
      • 내부표정 섹션: f×(0.33.0배율), cx₀(-0.5+0.5), cy₀(-0.5~+0.5)
      • 현재 유효값 실시간 표시: yaw_eff, pitch_eff, f_eff(mm), hFOV(°)
      • 측점 Canvas 마커 구현: 다이아몬드 ◆ + 측점명 레이블 + 거리(m)
      • FOV 이탈 측점 → 가장자리 방향 화살표(drawEdgeArrow)
      • POI 마커: + 심볼 + 레이블
      • 지면 가이드선: 측점 → 화면 하단 점선
      • 나침반 HUD: hFOV를 focalScale 반영하여 동적 계산
      • 초기화 버튼, 드론 경로 체크박스
    • 빌드 성공: tsc + vite build 에러 없음 (2026-03-26)
  • 발견 사항:
    • SRT gb_roll=0.0 (전 프레임 고정) → Roll 보정은 이 영상에서 효과 없으나 다른 영상 대비 구조 완성
    • 자기 편각(-8°) 보정이 yawOffset 슬라이더의 주된 용도
    • focalScale로 실효 FOV를 조정할 수 있어 정밀 보정 가능
  • 미완료/이슈:
    • GCP 클릭 기반 자동 Resection 미구현 (yaw/pitch 자동 계산)
    • 파라미터 설정값 영속 저장(localStorage) 미구현

기능 확장: 지리정보 오버레이 + 측점 검증 (2026-03-25)

  • 상태: 완료
  • 완료 항목:
    • server/src/services/geoMatch.ts — 블렌더 방식 월드 ENU 좌표계 구현
      • geoToEnu(): 위경도 → ENU(m), 기준점 위도로 cos(lat) 고정 (전 프레임 일관성)
      • projectEnu(): 상대 ENU 벡터 + 카메라 회전 → 핀홀 투영
      • project3D(): geoToEnu + projectEnu 래퍼, origin 파라미터 지원
      • findFramesForPoi / findPoisForFrame — frames[0]를 월드 원점으로 전달
      • getDroneFrames() 신규 export
    • server/src/routes/geo.ts — yawOffset 쿼리 파라미터, GET /api/geo/frames 엔드포인트
    • client/src/utils/geoProjection.ts — 서버와 동일한 블렌더 방식 ENU 투영, ref 파라미터 추가
    • client/src/components/overlay/StationOverlay.tsx — yaw 보정 슬라이더(-180~+180°), 드론 경로 오버레이, pathFrames[0] 원점 전달
    • client/src/components/geo/StationVerify.tsx — 측점 목록 + 클릭 검증 패널 (신규)
    • client/src/App.tsx — 우측 패널에 "측점" 탭 추가
    • server/client 빌드 모두 에러 없음 확인
  • 발견 사항:
    • cos(lat) 보정을 각 프레임 위도가 아닌 기준점(frame 0) 위도로 고정해야 전 구간 일관성 보장
    • CSV yaw/pitch/roll = 짐벌(카메라) 방향 (pitch=-29.8°는 짐벌 고정 틸트, roll=0은 짐벌 보정)
    • frame 1791 기준 158K300: px=70.1% (사용자 관측 ~73%와 일치)
  • 미완료/이슈:
    • Z값(표고) 미적용 — 측점 z값이 ~0m이라 수직 오차 존재, 실제 표고 데이터 확보 후 보정 필요
    • GCP 캘리브레이션 UI 미구현 (클릭으로 yaw/pitch 오프셋 자동 계산)

지리정보 오버레이 — Python advanced_tuner_v2 포팅 (2026-03-30)

  • 상태: 완료
  • 완료 항목:
    • geoProjection.ts 재구현: R_b2w=Rz(-yaw)*Rx(pitch)*Ry(roll), R_w2c=R_align@R_b2w.T (Python 동치)
    • pitch/roll 절대값 → 오프셋 방식 (Python spn_pitch/roll 기본값 0과 동일)
    • sensorH 기본값 20.25mm (=36×9/16, 16:9 영상 기준, 기존 24mm 수직 스케일 오류 수정)
    • CameraParams에 offX/offY/offZ 추가 (Python off_x/y/z 동치)
    • geoMatch.ts: center.csv 224점 로더 추가 (getCenterlinePoints)
    • GET /api/geo/centerline 엔드포인트 추가
    • StationOverlay: center.csv 중심선 빨간 선분 시각화 (Python 방식), Catmull-Rom next6 스플라인 제거
    • 파라미터 패널: 위치 보정(offX/Y/Z), 센서 크기(senW/H) 슬라이더 추가
    • DEFAULT_CAMERA_PARAMS = Python 기본값 (모든 오프셋 0, focal=24, sensor=36)
  • 다음 단계 참고:
    • off_x/y/z 슬라이더로 GPS 위치 오차 보정 필요
    • swap_xy 옵션 미구현 (Python에는 있음, 필요시 추가)

단계 7: UI/UX 통합

  • 상태: 완료 (2026-03-24)
  • 완료 항목:
    • client/src/components/ErrorBoundary.tsx — React 에러 바운더리 (오류 발생 시 복구 UI)
    • client/src/components/HelpOverlay.tsx — 키보드 단축키 도움말 오버레이
    • client/src/main.tsx — ErrorBoundary로 App 래핑
    • client/src/hooks/useKeyboard.ts — ? 키 (Shift+Slash) → onToggleHelp 콜백 추가
    • client/src/components/player/VideoPlayer.tsx — onToggleHelp prop 수신 및 useKeyboard에 전달
    • client/src/App.tsx — showHelp 상태 + HelpOverlay 렌더링 통합
    • README.md — 프로젝트 설치/실행/단축키/환경변수 문서 작성
    • TypeScript 타입 에러 없음, 프로덕션 빌드 성공 (1,480 kB / gzip 453 kB)
  • 미완료/이슈:
    • 빌드 경고: chunk > 500kB (video.js 특성상 정상)
  • 다음 단계 참고: 전체 7단계 완료

기능 확장: 구조물 시설종별(1종/2종/3종) 표시 필터 (2026-06-19)

  • 상태: 완료
  • 목표: 구조물 시설종별에 따라 재생바(StationBar)/패널(RoutePanel) 표시 필터. 플레이어에 1/2/3종 체크박스, 기본 1종☑2종☑3종☐, localStorage 지속.
  • 완료 항목:
    • client/src/types/geo.ts: RouteStructure 에 grade?: string 추가(시설종별 원문).
    • client/src/utils/geoData.ts (parseStructures): pushFrom spec 에 gradeFallback 추가. 교량 시설종별 idx1, 터널 idx5 를 헤더명('시설종별') indexOf 우선 + 폴백으로 읽어 trim, 빈 값이면 undefined. 구교(06)는 gradeFallback 미지정 → grade undefined.
    • client/src/store/settingsStore.ts (신설): zustand + persist('ghivideo.settings'). gradeFilter 기본 {1종:true,2종:true,3종:false}, setGradeFilter 액션. KNOWN_GRADES, isGradeVisible(공유 표시규칙) export. merge 로 누락 키 기본값 보충.
    • 표시규칙 isGradeVisible: grade 없음 || KNOWN 미포함('기타'/'인상' 등) → 항상 표시, 1/2/3종은 gradeFilter[grade]===true 일 때만.
    • client/src/stationbar/StationBar.tsx: storeStructuresRaw 를 isGradeVisible 로 필터(useMemo, gradeFilter 구독) → structureMarks 가 필터된 목록 사용.
    • client/src/components/overlay/RoutePanel.tsx: filteredPois 에 isGradeVisible 조건 추가(동일 규칙, gradeFilter 구독).
    • client/src/components/player/VideoPlayer.tsx: 좌하단 컨트롤 그룹(배속 행 인근)에 "시설등급" 라벨 + 1/2/3종 체크박스 3개. showVideoControls && geoLoaded 일 때 노출. setGradeFilter 바인딩(즉시 토글).
  • 실데이터 검증(하행)회덕-대전조차장 v2.0, 좌표필터 적용 후):
    • 교량 12건: 시설종별 3종 ×10, 기타 ×2 ("인상" 1건은 깨진 좌표로 이미 필터됨)
    • 터널 2건: 시설종별 2종 ×2
    • 구교 9건: 시설종별 컬럼 없음 → grade undefined(항상 표시)
    • 기본값(1종☑2종☑3종☐) 적용 시: 3종 교량 10건 숨김, 기타 교량 2건·터널 2건(2종)·구교 9건 표시.
  • 빌드: nvm use 20 → npm run build -w client → tsc + vite 에러 0 (기존 chunk>500kB 경고만).
  • 미완료/후속:
    • 이 데이터엔 1종 구조물이 없어 1종 체크박스 효과는 다른 데이터에서 검증 필요(구조는 완성).
    • StationOverlay(영상 위 측점 마커)는 structures 미사용이라 필터 미적용 — 요구범위(재생바/패널)대로.
  • 상세 기록: docs/history/2026-06-19_시설종별-필터.md

기능 전환: 폴더 임포트 v2.0 데이터 형상 전용화 (2026-06-19)

  • 상태: 완료
  • 목표: 폴더 선택 시 v2.0 형상만 로딩. v1.0(POI_위경도값/측점_위경도값/center.csv) 지원 코드 제거.
  • 완료 항목:
    • client/src/types/geo.ts:
      • DirectionChange 타입 신설(station/from/to/atSeconds)
      • RouteStructure 에 lengthM?, category? 추가
      • FolderGeoData 에 structures: RouteStructure[], directionChanges: DirectionChange[] 추가
    • client/src/utils/geoData.ts (전면 재작성, v2.0 전용):
      • 인코딩 자동 감지: decodeBytes(buf) — 앞 3바이트 EF BB BF → UTF-8, 아니면 EUC-KR, 잔여 BOM 제거
      • readCsv(file) 시그니처 단순화(인코딩 인자 제거)
      • parseStations: building/01)측점.csv → 측점 + 방향전환점 추출(비고 정규식)
      • parsePois: _POI.csv(UTF-8) + building/02)지장물.csv(EUC-KR) 병합
      • parseStructures: 03)교량(bridge)+04)터널(tunnel)+06)구교(bridge/category='구교') → RouteStructure[], 위경도 범위(3339/124132) 밖 깨진 행 필터
      • mergeStructures: route.json structures 를 name 매칭으로 CSV 구조물에 override/augment(선택적 보정 레이어)
      • buildCenterlineFromStations: v2.0엔 center.csv 없음 → 측점 mileage 정렬 폴리라인을 중심선으로 생성
      • v1.0 코드 제거: POI_위경도값/측점_위경도값/타원체고 식별, center.csv 로더, encoding 하드코딩 분기
    • client/src/store/geoStore.ts: structures, directionChanges 적재
    • client/src/stationbar/StationBar.tsx: 구조물 마커를 geoStore.structures(CSV 유래) 우선으로 전환(기존 routeMeta.structures 대체). 위치 로직(station→lat/lon→이름매칭→mileage)은 lat/lon 지원으로 그대로 활용.
  • 실데이터 검증(하행)회덕-대전조차장 데이터(v2.0)):
    • 인코딩: 드론/POI/05)출입문/06)구교=UTF-8(BOM), 01~04=EUC-KR (BOM 자동감지로 일괄 처리)
    • directionChanges 2건: 157K930 상행→하행@125s, 158K340 하행→상행@75s
    • 측점 47개, POI 137개(_POI 114 + 지장물 23), 구조물 교량 12(깨진 1행 필터)+터널 2+구교 9
  • 빌드: nvm use 20 → npm run build -w client → tsc + vite 에러 0 (기존 chunk>500kB 경고만)
  • 미완료/후속과제:
    • 05)출입문번호.csv: 운영데이터로 재생바 아이콘이 과도해져 기본 표시 제외. 파싱 자체를 스킵(type='door' 보관 비용 대비 효용 낮음, Timeline 렌더는 bridge/tunnel/station만). 추후 필요 시 parseDoors 추가.
    • directionChanges 와이어링: 타입/스토어까지 적재 완료. StationBar barSegments 방향전환은 현재 연속 체이니지(chain) 자동 판정 로직을 유지(directionChanges 로 강제 분할은 미적용). chain 자동판정이 영상과 일치하므로 회귀 위험을 피해 후속과제로 둠.
  • 다음 단계 참고:
    • geoStore.structures/directionChanges 가 신규 노출됨 — 후속 소비처에서 활용 가능
    • StationOverlay/StationVerify/RoutePanel/RouteInfoOverlay 는 stations/pois/routeMeta 만 사용하므로 변경 불필요(검증 완료)
    • 상세 기록: docs/history/2026-06-19_v2.0-import전환.md