feat(supporters): 병원별 고정값 clinic.json — 원진 유튜브 기준 채널 @wjwonjin 고정(haewon 결정), 워커 우선순위 고정값>인자>발견(영상 수)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Haewon Kam 2026-09-08 10:20:47 +09:00
parent 995e1e7dfa
commit d1b22b9872
3 changed files with 15 additions and 4 deletions

View File

@ -317,7 +317,7 @@ INFINITH 저장소(브랜치 feature/discovery-url-cta)에서 "AI Discovery 자
- 함정: 역링크를 부분 문자열로 보면 `instagram.com/wonjin` 이 `wonjin_official` 안에서 잡혀 변형 핸들이 전부 CONFIRMED 된다 → 경로 경계 정규식. 전화는 하이픈 형식만(사업자번호 오탐). - 함정: 역링크를 부분 문자열로 보면 `instagram.com/wonjin` 이 `wonjin_official` 안에서 잡혀 변형 핸들이 전부 CONFIRMED 된다 → 경로 경계 정규식. 전화는 하이픈 형식만(사업자번호 오탐).
- 워커 `discover` 단계(data 다음): channels.json → 사이트 데이터, CONFIRMED 1순위를 factSheet.surfaces(유튜브·인스타·페북·블로그·틱톡·카카오·강남언니·플레이스·바비톡)에 넣어 MedicalClinic sameAs·푸터·방문 페이지에 반영. youtube 단계는 CONFIRMED 유튜브 중 구독자 최대를 자동 선택(`--youtube` 우선). status.report.channels 에 인벤토리·확정·후보 저장 → `/discovery/:id`·`/supporters/:id` 패널 "발견한 채널"(확인됨/확인 필요 배지)과 리포트 표. - 워커 `discover` 단계(data 다음): channels.json → 사이트 데이터, CONFIRMED 1순위를 factSheet.surfaces(유튜브·인스타·페북·블로그·틱톡·카카오·강남언니·플레이스·바비톡)에 넣어 MedicalClinic sameAs·푸터·방문 페이지에 반영. youtube 단계는 CONFIRMED 유튜브 중 구독자 최대를 자동 선택(`--youtube` 우선). status.report.channels 에 인벤토리·확정·후보 저장 → `/discovery/:id`·`/supporters/:id` 패널 "발견한 채널"(확인됨/확인 필요 배지)과 리포트 표.
- 원진(이름만 입력) 결과: 홈페이지 k-wonjin.co.kr 추정 → 유튜브 2(WJ원진성형외과 28,300 · @wjwonjin 14,700), 인스타 3(wonjin_official·wonjin_ps·wj_cosmetic), 페북 KwonjinPS, 블로그 popokpop, 틱톡 wonjin_official, 카카오, 강남언니 2500, 바비톡 3143 CONFIRMED · 네이버 플레이스 NOT_FOUND(사이트·검색에 링크 없음, 플레이스 내부 검색은 미실행이라 실제로는 UNVERIFIED 에 가깝다) · 후보 12(영문 채널·유세윤 광고 채널·wj_plasticsurgery 등). 라이브 sameAs 4개 확인. 뷰성형외과 회귀: 유튜브 2·인스타 4·페북·블로그·카카오 CONFIRMED. - 원진(이름만 입력) 결과: 홈페이지 k-wonjin.co.kr 추정 → 유튜브 2(WJ원진성형외과 28,300 · @wjwonjin 14,700), 인스타 3(wonjin_official·wonjin_ps·wj_cosmetic), 페북 KwonjinPS, 블로그 popokpop, 틱톡 wonjin_official, 카카오, 강남언니 2500, 바비톡 3143 CONFIRMED · 네이버 플레이스 NOT_FOUND(사이트·검색에 링크 없음, 플레이스 내부 검색은 미실행이라 실제로는 UNVERIFIED 에 가깝다) · 후보 12(영문 채널·유세윤 광고 채널·wj_plasticsurgery 등). 라이브 sameAs 4개 확인. 뷰성형외과 회귀: 유튜브 2·인스타 4·페북·블로그·카카오 CONFIRMED.
- 미결: 유튜브 공식 채널이 2개(구독 28,300 / 14,700). 어느 쪽을 사이트 기준으로 쓸지 haewon·병원 확인. 지금은 haewon 이 준 UC-EBM(14,700) 집계본을 쓰고 있고, 새로 돌리면 구독자 많은 쪽을 고른다. - 결정(haewon 2026-09-08): 유튜브 기준 채널은 **@wjwonjin (UC-EBM4e0VST1W_PQ0NzFMQg, 736편)**. `supporters/briefs/wonjin/clinic.json` 에 고정(youtube·logo). 워커 우선순위 = clinic.json 고정값 > --youtube > 발견 단계 CONFIRMED 중 영상 수 최대. @wj2541(62편, 브랜드 필름)은 쓰지 않는다.
--- ---

View File

@ -0,0 +1,6 @@
{
"_comment": "병원별 고정값. 워커가 --youtube 등 인자보다 먼저 읽는다. haewon 결정 2026-09-08: 유튜브 기준 채널은 @wjwonjin (UC-EBM4e0VST1W_PQ0NzFMQg, 원장 설명 영상 736편). @wj2541(UCNBaQBCok5xIViCnrGdisoQ)은 브랜드 필름 채널이라 쓰지 않는다.",
"youtube": "UC-EBM4e0VST1W_PQ0NzFMQg",
"youtubeHandle": "@wjwonjin",
"logo": "supporters/briefs/wonjin/logo-horizontal.svg"
}

View File

@ -149,7 +149,8 @@ await phase('discover', () => {
await phase('youtube', () => { await phase('youtube', () => {
const notes = []; const notes = [];
// 로고: 헤더·푸터·카드 대체 이미지·파비콘. SVG 는 viewBox 를 정사각 파비콘으로 감싼다. // 로고: 헤더·푸터·카드 대체 이미지·파비콘. SVG 는 viewBox 를 정사각 파비콘으로 감싼다.
const logoPath = opt('logo') ? resolve(opt('logo')) : null; const pinLogo = existsSync(join(SUP, 'briefs', clinic, 'clinic.json')) ? readJson(join(SUP, 'briefs', clinic, 'clinic.json')).logo : null;
const logoPath = opt('logo') ? resolve(opt('logo')) : pinLogo ? resolve(ROOT, pinLogo) : null;
if (logoPath && existsSync(logoPath)) { if (logoPath && existsSync(logoPath)) {
const ext = logoPath.toLowerCase().endsWith('.svg') ? 'svg' : logoPath.toLowerCase().endsWith('.png') ? 'png' : 'jpg'; const ext = logoPath.toLowerCase().endsWith('.svg') ? 'svg' : logoPath.toLowerCase().endsWith('.png') ? 'png' : 'jpg';
mkdirSync(join(SITE, 'public/img'), { recursive: true }); mkdirSync(join(SITE, 'public/img'), { recursive: true });
@ -176,10 +177,14 @@ await phase('youtube', () => {
} else if (opt('logo')) notes.push(`로고 파일 없음: ${opt('logo')}`); } else if (opt('logo')) notes.push(`로고 파일 없음: ${opt('logo')}`);
// 유튜브: 채널 인자 없으면 근거 페이지 링크에서 youtube.com/channel|@handle 을 찾는다 // 유튜브: 채널 인자 없으면 근거 페이지 링크에서 youtube.com/channel|@handle 을 찾는다
let channel = opt('youtube') ?? null; // 고정값: supporters/briefs/<clinic>/clinic.json (haewon·병원 결정) > --youtube > 발견 단계 CONFIRMED(영상 수 최대) > 근거 링크
const pinPath = join(SUP, 'briefs', clinic, 'clinic.json');
const pin = existsSync(pinPath) ? readJson(pinPath) : {};
let channel = pin.youtube ?? opt('youtube') ?? null;
if (pin.youtube) notes.push(`유튜브 고정 채널: ${pin.youtubeHandle ?? pin.youtube}`);
if (!channel && existsSync(join(DATA, 'channels.json'))) { if (!channel && existsSync(join(DATA, 'channels.json'))) {
// 발견 단계에서 CONFIRMED 된 유튜브 중 구독자가 가장 많은 채널 // 발견 단계에서 CONFIRMED 된 유튜브 중 구독자가 가장 많은 채널
const yt = (readJson(join(DATA, 'channels.json')).platforms?.youtube ?? []).filter((x) => x.status === 'CONFIRMED').sort((a, b) => (b.subscribers ?? 0) - (a.subscribers ?? 0))[0]; const yt = (readJson(join(DATA, 'channels.json')).platforms?.youtube ?? []).filter((x) => x.status === 'CONFIRMED').sort((a, b) => (b.videos ?? 0) - (a.videos ?? 0))[0]; // 설명 영상이 많은 채널이 글의 근거로 맞다
if (yt) { channel = yt.channelId ?? yt.key; notes.push(`유튜브 채널 자동 선택: ${yt.title ?? yt.key}`); } if (yt) { channel = yt.channelId ?? yt.key; notes.push(`유튜브 채널 자동 선택: ${yt.title ?? yt.key}`); }
} }
if (!channel) { if (!channel) {