# ai_read.md — yjm_win2rtc 로컬 PC 기준

## 최신 기준: v79 준비 — 안전 정리 + 좌표 싱크 완벽화

이 파일이 다음 작업의 기준이다. 대화 내용보다 현재 ZIP 소스와 이 `ai_read.md`를 우선한다.

- 장비: 로컬 PC 통합 ZIP 안의 Python/RTC/WS 파트이다.
- 기준 경로: `D:\_St\yjm_win2rtc`
- v79의 목표는 기능 추가가 아니라 **안전 정리와 좌표 싱크 완벽화**이다.
- 서버 PHP는 이 작업에서 건드리지 않는다.
- 모든 bat는 디버깅을 위해 `echo on`을 유지한다.

## v79에서 가장 중요한 안전 규칙

자동 클릭 프로그램은 빠르게 움직이면 위험하다.

```text
모든 재시도 최소 2초 이상
모든 오류 후 최소 2초 이상
hover 실패 후 다음 확인도 최소 2초 이상
WS 재요청도 최소 2초 이상
위치 못 잡으면 다음 후보 탐색 금지, 즉시 중단
중단 시 mouseUp 후 더 이상 이동하지 않음
```

폐기할 위험 방식:

```text
dom_as_screen
client_plus
window_plus
other_mp4
여러 후보 빠른 순회
0.5초 안팎 재시도
hover 미확인 클릭
작은 원본 인스타 아이콘 직접 클릭
```

## 좌표 싱크 최신 구조

좌표 판단은 py 혼자 하지 않는다.

```text
EXT JS = DOM 기준 실제 마우스 위치와 hover 대상 판정
PY     = screen 절대 좌표로 마우스를 이동하고 클릭 직전 검증 요청
```

폐쇄 루프:

```text
1. JS가 목표 기준점/버튼의 예상 screen 좌표를 py에 보낸다.
2. py가 천천히 moveTo 한다.
3. JS mousemove/hover 이벤트가 실제 event.clientX/Y, event.screenX/Y, rank, media_type, zone을 py에 되돌린다.
4. py가 예상과 실제를 비교한다.
5. 맞으면 클릭, 틀리면 “위치 못 잡음” 표시 후 중단한다.
```

목표 공식:

```text
screen_x = dom_origin_screen_x + client_x
screen_y = dom_origin_screen_y + client_y
```

하지만 최종 클릭은 이 공식만 믿지 않고, JS의 실제 hover/status 피드백이 일치할 때만 허용한다.

## DOM 원점 기준점

- Chrome title은 `❤️❤️❤️`로 시작한다. py는 이 Chrome 창을 우선 찾는다.
- EXT는 DOM viewport `0,0`에 노란 10px 네모 기준점을 표시한다.
- py 플로팅 창 기준 표시도 세모가 아니라 10px 네모로 바꾸는 방향을 우선한다.
- 사용자가 py 네모와 EXT 네모를 맞추거나, py가 JS 피드백을 받아 조금씩 이동해 자동 싱크할 수 있다.
- scale 1.0, Chrome zoom 100%, Windows 배율 고정이면 DOM 원점 한 점을 우선 기준으로 삼는다.
- 4점 좌표는 기본이 아니라 검증 모드로만 둔다.

## 4점 검증 모드

4점 좌표 패널은 필수가 아니다. 문제 상황 검증용이다.

- 우측 EXT 패널에서 `_` / `ㅁ` 버튼으로 접기/펼치기를 한다.
- 접힘 상태는 `ㅁ`만 보인다.
- 펼침 상태는 `_`와 4점 좌표/마커 상태가 보인다.
- py가 TL/TR/BL/BR 예상 좌표를 천천히 클릭하고, EXT가 event.screen/client 결과를 반환하면 싱크 검증이 가능하다.
- 4점 검증 실패 시 자동 클릭은 금지한다.

## py 로그는 EXT 화면에도 보여야 한다

py 콘솔/Tk 로그만으로는 부족하다. py의 상태는 WS 응답 또는 status push 형태로 EXT에 전달되어야 한다.

필수 state:

```text
ws_connected
ws_disconnected
coord_sync_ok
coord_sync_fail
position_not_found
hover_miss
click_blocked
auto_waiting
auto_stopped
already_exists
metadata_updated
downloaded
download_failed
```

EXT는 이 state를 보고 즉시 버튼 상태와 자동 실행 큐를 바꿔야 한다.

## 다운로드/중복 기준

파일명과 중복키는 계속 v71 기준이다.

```text
파일 stem = 인스타어카운트_수집시각_No000_shortcode
중복키 = shortcode + asset_kind + media_index
```

- 로컬에 이미 있는 파일은 다시 다운로드하지 않는다.
- 이미 있으면 `.md5`와 상위 JSON의 메타데이터만 오늘 날짜 기준으로 갱신한다.
- EXT에 `이미 있음`, `META 갱신됨` 상태를 보낸다.
- 성공/이미 있음이 확인된 mp4/jpg 버튼은 비활성화한다.

## v79에서 우선 수정할 파일

```text
host/yjm_save_dialog_auto_enter_ws.py
host/ig_auto_click_download.py
host/ig_auto_download.py
ai_read.md
host/ai_read.md
```

필요 시 Extension 파일과 같이 수정한다.

```text
../IG_Sorter_Yello/js/instagram_inject.js
../IG_Sorter_Yello/content_instagram.js
../IG_Sorter_Yello/background.js
../IG_Sorter_Yello/ai_read.md
```

## 구현 우선순위

```text
1. py 로그/state를 EXT 화면으로 전달
2. EXT JS가 state를 보고 버튼 비활성/중단 처리
3. 모든 재시도 최소 2초
4. fallback 후보 난사 제거
5. 위치 못 잡으면 “위치 못 잡음” 후 중단
6. DOM 0,0 네모 기준점 + JS 실시간 마우스 피드백 싱크
7. 이미 다운로드된 파일은 클릭 금지, 메타만 갱신
```

## v79 구현 반영 메모 — 2026-06-29

이번 v79 코딩 반영 기준:

- EXT 패널 버전을 v79로 올렸다.
- 우측 4점 검증 패널은 기본 숨김이며 `ㅁ`/`_` 토글로 접고 펼친다.
- v80에서는 TTS 단계 선택을 EXT 우측 패널에 추가했으나, v81 기준에서는 DOM 원점보정 패널의 중복 TTS 선택은 제거한다.
- JS는 mousemove 기준으로 `screen/client/event_origin/dx/dy`를 계산해서 py로 보낸다.
- py는 `ig_rtc_mouse_sync`로 좌표 싱크 상태를 저장한다. `ig_rtc_py_setting`으로 들어오는 EXT TTS 값은 v81부터 py TTS 설정을 변경하지 않는다.
- py 상태/로그는 WS 응답과 side/content를 통해 EXT 화면 `igblue_py_state`로 전달한다. JS는 state를 보고 화면 로그와 버튼 상태를 갱신한다.
- py 플로팅 기준점은 세모가 아니라 10px급 노란 네모로 바꿨다.
- 자동 클릭 후보는 fixed overlay의 요청 media 버튼만 사용한다. `client_plus/window_plus/dom_as_screen/other_mp4/grid` 후보 난사는 금지한다.
- 후보가 없거나 JS hover에 `screen_x/screen_y`가 없으면 “위치 못 잡음”으로 중단한다.
- hover 실패/오류/차단 전 대기는 최소 2초로 고정한다.
- side.js 자동 실행 payload도 후보 1개, 재시도 최소 2초, legacy fallback false 기준으로 낮췄다.

아직 별도 다음 단계로 남길 것:

- 실제 다운로드 완료 후 `.md5/index` 메타만 갱신하는 세부 정책은 기존 `ig_media_exists/ig_media_downloaded` 흐름에 연결되어 있으나, 더 정교한 UI 표시는 다음 버전에서 추가 검증한다.
- 브라우저 창 이동 감지 후 자동 재싱크 안내는 기본 데이터 통신 구조를 넣었고, 완전 자동 재싱크 루프는 다음 버전에서 테스트 후 강화한다.

## v80 소규모 반영 메모 — 2026-06-29

이번 수정은 v79의 씽크/안전 흐름을 유지하면서 TTS 안내를 보강한다.

- TTS 선택 표시를 `0 미사용 / 1 중요 / 2 진행 / 3 디버그`로 바꿨다.
- TTS 설정 변경 시 EXT 화면과 JS TTS 안내 문구도 같은 라벨을 사용한다.
- v81부터 py는 EXT가 보낸 `tts_level`을 기준으로 삼지 않는다. py TTS는 py 플로팅 프로그램의 [설정] 창에서 별도로 선택한다.
- 자동 클릭 첫 단계에서 py가 `❤️❤️❤️` 크롬 창을 찾는다.
- 찾기 시작: `하트 3개 크롬 창을 찾습니다`
- 찾음: `하트 3개 크롬 창을 찾았습니다`
- 못 찾음: `하트 3개 크롬 창을 찾지 못했습니다. 인스타그램 창을 실행해주세요` 후 자동 클릭을 차단한다.
- py TTS는 pip 패키지 추가 없이 Windows 기본 PowerShell/SAPI를 사용한다. `requirements.txt` 추가 없음.
- PowerShell 실행 파일은 `powershell.exe / powershell / pwsh.exe / pwsh` 순서로 찾는다.

## v80 추가 기준 — TTS 옵션 위치와 초기 안내

- TTS 단계 표기는 `0 미사용 / 1 중요 / 2 진행 / 3 디버그`로 고정한다.
- EXT/브라우저 TTS 설정은 side.html 상단 `채널고정 / ↗ / Goto Instagram / Clean & Refresh` 줄 우측에 둔다.
- 좁은 side/popup 폭에서는 해당 줄이 자동 줄바꿈되어도 된다.
- side의 TTS 선택값은 chrome.storage.local에 저장하고 content/main inject로 전달하지만, v81부터 py TTS 레벨은 덮어쓰지 않는다.
- 기본값은 EXT/py 양쪽 모두 `0 미사용`이다. 사용자가 필요한 쪽만 1/2/3 단계로 올린다.
- py TTS는 pip 설치 없이 Windows 기본 PowerShell/SAPI를 우선 사용한다. requirements.txt에 TTS 전용 패키지를 추가하지 않는다.

## v81 수정 기준 — TTS 분리와 4점 검증 위치 고정

이번 기준은 v80의 TTS/검증 UI에서 생긴 중복과 위치 흔들림을 정리한다.

- EXT/브라우저 TTS와 PY TTS는 분리한다.
- 기본값은 양쪽 모두 `0 미사용`이다.
- side.html 상단의 TTS 선택은 EXT/브라우저 화면 안내용이다.
- py TTS 단계는 py 플로팅 프로그램의 `설정` 창에서 따로 고른다.
- DOM `원점 보정` 패널 안의 중복 TTS 선택 UI는 제거한다. DOM 패널은 최대한 간소화한다.
- EXT에서 오는 `tts_level`은 더 이상 py의 `tts_level`을 덮어쓰지 않는다.
- py TTS는 pip 설치 없이 Windows PowerShell/SAPI를 계속 사용한다.
- 4점 검증 패널은 `_`/`ㅁ` 토글 바로 아래에 둔다.
- `SyncLog`/디버그 텍스트는 4점 검증 패널 아래에 둔다. 디버그 텍스트 크기가 바뀌어도 4점 검증 위치가 움직이면 안 된다.
- 4점 검증은 계속 기본 필수 보정이 아니라 검증 모드다.

## v82 수정 기준 — 표시 버전/눈 3단계/TTS 즉시중단/중복체크 선요청

이번 기준은 v81 테스트에서 확인된 UI/안전 문제를 정리한다.

- manifest와 main DOM 표시 버전을 `2.3.8.82 / v82`로 맞춘다.
- main 우측 눈 아이콘은 3단계로 동작한다.
  - 0: 전체 보임
  - 1: 바로가기만 보임 (`1 / Prev / Current / Next / Last`)
  - 2: 눈 아이콘만 보임
- 4점 검증 패널은 `_`/`ㅁ` 토글 바로 아래에 유지한다. `SyncLog`/디버그 텍스트가 커져도 4점 검증 위치가 밀리면 안 된다.
- EXT TTS를 `0 미사용`으로 바꾸면 `speechSynthesis.cancel()`로 즉시 현재 음성을 중단한다.
- PY TTS도 py 설정창에서 `0 미사용`을 저장하면 현재 PowerShell/SAPI TTS 프로세스를 즉시 중단하고 이후 새 음성을 차단한다.
- py 설정창의 `PY TTS 단계 (로컬 음성)` 항목을 명확히 보이게 유지한다. EXT/브라우저 TTS와 PY TTS는 계속 분리한다.
- background.js 다운로드 흐름은 `status` 선검사로 오판하지 않는다. 먼저 `ig_media_exists` 중복 체크를 요청하고, 그 요청이 실패할 때만 다운로드를 중단한다.
- `로컬 중복관리 없이 다운로드할 수 없습니다` 같은 혼동되는 문구는 사용하지 않는다. 대신 `로컬 중복 체크 실패 / 다운로드 중단`처럼 실제 원인을 표시한다.

## v83 수정 기준 — shortcode 중복 표시/로컬 열기/클립보드 단축키

- manifest와 main DOM 표시 버전을 `2.3.8.83 / v83`로 맞춘다.
- 중복 다운로드 판단/표시는 `No040` 같은 정렬 순번이 아니라 `shortcode + asset_kind + media_index`를 핵심으로 한다.
- 이미 다운로드된 파일이면 화면/응답에 `shortcode`, `asset_kind`, `media_index`, 실제 `file_path`, `json_path`를 표시한다.
- 로컬 중복 존재 시 다운로드하지 않고 `.md5` 및 parent index JSON에 오늘 조회/정렬 메타를 추가한다.
- 다운로드 완료/이미 존재 상태의 진행창에는 전체 경로를 보여주고 `파일열기` / `폴더열기` 버튼으로 py에 열기 요청을 보낸다.
- py는 `ig_open_file` / `ig_open_folder` 요청을 받아 실제 파일 또는 탐색기 폴더를 연다.
- py 플로팅 설정창에 `클립보드 복사 단축키`를 추가한다. 기본값은 `ctrl+shift+1`이다.
- py는 단축키를 감지하면 현재 marker origin, mouse screen/client, dx/dy, state, shortcode, file_path, json_path를 클립보드에 복사한다.
- 이 단축키는 마우스가 움직이면 좌표가 바뀌는 문제를 피하기 위한 상태 캡처용이다.

## v84 긴급 반영 메모 — 2026-06-29

- py는 최근 인스타 URL, 최근 다운로드 파일 전체 경로, index JSON 경로, 최근 media state를 설정 JSON에 저장한다.
- 재실행 후 클립보드 복사 단축키를 눌러도 마지막 `file_path/json_path/shortcode`가 비어 있지 않게 유지한다.
- 로컬 파일이 확실히 존재할 때만 중복 차단한다. 서버/yellow 중복만 있고 로컬 파일 경로가 없으면 다운로드 허용으로 본다.

## v85 긴급 수정 기준 — 계정/업로드일 파일명 적용

- 다운로드 파일명은 반드시 기존 인젝션/랭크 라벨에 들어있는 실제 media.username / owner_username 값을 우선 사용한다.
- `instagram_`, `explore_`, `reels_`, `reel_`, `p_` 같은 URL/fallback 값이 실제 작성자 계정이 아니면 파일명 앞에 쓰지 않는다.
- 파일명 날짜는 현재 시각/수집시각/first_loaded_stamp가 아니라 인스타 업로드 시각(`created_at`, `taken_at`, `caption.created_at`)을 `YYYYMMDD_HHMMSS`로 변환한 값만 사용한다.
- 업로드 시각을 못 얻은 경우에는 현재 날짜를 대신 넣지 않고 `upload_unknown`으로 표시한다.
- 단일 동영상 화면(`/reel/SHORTCODE/`, `/reels/SHORTCODE/`, `/p/SHORTCODE/`)에서는 No 순번을 파일명에 넣지 않는다.
- No 순번은 여러 개 목록 화면에서만 보조 표시/정렬 기준으로 사용한다.
- main inject의 `showToast` 누락으로 개별 릴스 다운로드 클릭 시 발생하던 ReferenceError를 방지한다.
- RTC/ws8771 미연결은 다운로드 차단 사유가 아니다. 중복체크 실패 시에는 경고만 띄우고 다운로드는 허용한다.
- main 우측 눈 버전 표시는 v85 단일 기준으로만 표시한다.


## v86 좁은 수정 기준 — main labels 단일 출처 복구

이번 v86은 v85 보강으로 생긴 깜빡임을 좁게 제거한다.

- main 우측 No/rank labels는 반드시 `side.js`가 정렬/필터/다운로드 위치 기준으로 만든 값만 사용한다.
- `content_instagram.js`는 page-world media 원본을 background/side로 전달만 한다.
- `content_instagram.js`에서 임시 `No`, `download_root`, `download_account`, `download_prefix`를 만들지 않는다.
- 오류나 정보 부족 시 `instagram`, `IGblue` 같은 기본값으로 덮어쓰지 않는다. 오류/부족 상태를 표시하고 side 기준 재전송을 기다린다.
- `Goto Instagram` 버튼은 인스타 대상 존재 여부와 무관하게 항상 활성화한다.
- side 상단 TTS `0 미사용` 선택은 막지 않으며, 사용자가 선택한 값은 늦은 초기화 값이 덮어쓰지 않는다.
- manifest/main 표시 버전은 `2.3.8.86 / v86` 기준이다.

## v87 반영 메모 — heart_position_tracker 단독 host 1차 적용

이번 v87은 AIWK_COMMON 합류가 아니다. v86 단독 구조를 유지하면서 로컬 host 아래에 하트 Chrome 기준점 추적 모듈만 좁게 추가한다.

- 신규 파일: `host/heart_position_tracker.py`
- 위치: `D:\_St\yjm_win2rtc\host\heart_position_tracker.py`
- 이 모듈은 마우스 이동/클릭을 절대 하지 않는다.
- 역할은 `❤️❤️❤️` Chrome 창과 EXT의 노란 DOM 0,0 기준점만 추적해서 read-only snapshot을 갱신하는 것이다.
- 하트 Chrome 창이 없거나 노란 DOM 기준점이 없거나 snapshot이 오래되면 `ok=false`로 둔다.
- 기존 좌표를 기본값/fallback으로 만들어내지 않는다.
- `yjm_save_dialog_auto_enter_ws.py`는 시작 시 heart tracker thread를 실행한다.
- 자동 클릭 precheck는 fresh heart snapshot이 없으면 이동/클릭을 차단한다.
- `ig_auto_click_download.py`는 좌표 진단에서 `heart_position_tracker` snapshot의 DOM origin을 우선 사용한다.
- 기존 EXT 파일명/label v86 정책은 건드리지 않았다.
- AIWK_COMMON/AIWK_PC 합류는 아직 하지 않았다. 완성 후 최종 후보지로 이동 판단한다.
