💻 システム開発

SCALE_mikke_撮影の文字化け対策

最終更新 2026年07月31日 / 31_システム開発部/_SCALE_mikke_撮影の文字化け対策.md

mikke! 撮影の「文字化け」対策(2026-07-27 恒久・大串指示)

大串FB「文字化けしてる」「文字化け定期的に起きるから起きないように体制修正してね」(2026-07-27)
実装=core/tools/shot_common.mjs / core/tools/shoot_fullpage.mjs(ver219)

「文字化け」の正体

見出しが団子に潰れる現象(実例=J.SECURITY の / Peoplecunity)。フォントの問題ではない。

参考サイトのFVは「同じ座標に複数の見出しを重ね置きし、表示中の1枚以外を opacity:0 / visibility:hidden で隠す」作りが多い(切り替え表示)。
撮影ツールの reveal()=「隠れている要素を表示に戻す」処理が、この裏面まで全部表示に倒すため、
People / Security / Community が同じ位置に重なって写る。

なぜ「定期的に」再発していたのか(真因)

撮り直しても直らない構造になっていた。

  1. 採用ゲートが「中身が写らない」と判定 → 保存せず
  2. 保存しないので 古い(文字化けした)画像がそのまま残る
  3. 後段の検品は既存ファイルを見るので 「要確認 0 件」と表示される
  4. → 壊れたまま本番に出続け、しばらくして大串が見つける

しかも J.SECURITY が撮れなかった理由も実測で判明した。段階測定:

処理 色数 輪郭
freezeAnim 後 248種 24.4%
oneSlide 後 72種 2.6%
dedupeOverlap 後 8種 1.3%

重なり解消の2処理が、FVの画像スライドごと消していた(FVが3枚のスライドで1画面を構成する作りだと、1枚化=画面の2/3が消える)。
一覧サムネ側(shoot_mikke.mjs)はこの2処理を通さないので、同じサイトが正常に撮れていた。この差分が答えだった。

入れた体制(4段構え・ver219)

1. 予防 — reveal() に構造判定

同じ親の中でほぼ同座標に重なる兄弟は、表示に倒すのを1枚だけにする(表示中があればそれ、無ければ先頭)。
- 幅の一致を条件にしてはいけない。切り替わる見出しは文字数で幅が変わる(実測 People=219px / Community=360px)。左上の一致だけで見る。
- 既存の防御は class*="swiper|slick|splide|slider|carousel" という名前頼みで、.mv__title のような独自命名を素通りしていた。名前は無限に増やせないので構造で見る。

2. 復旧 — 中身が消えたら重なり解消を外して撮り直す

isPoorShot を踏んだら、リロードして oneSlide / dedupeOverlap を飛ばしてもう一度撮る。
文字の重なりは①の reveal が面倒を見るので、外しても団子にならない。
→ J.SECURITY の PC/SP 両方が 撮影成功に転じた。

3. 検出 — 採用ゲートの重なり検査を強化

従来の検査は今回を素通りしていた。理由は2つ:
- 対象タグに span が無かった(犯人は <span class="mv-title-text">
- 幅と高さの一致を要求していた(幅141px差で別物と判定)

span を対象に加え、「同タグ+同先頭クラス+同座標」なら大きさが違っても検出。
- ただし テキスト要素限定H1/H2/H3/P/SPAN)。IMG/SVG の重なりはスライダーFVでは正常で、含めると撮影が通らなくなる(SHOGUN PIZZA で実測)。

4. 可視化 — 古い画像が残っている事実を必ず知らせる

保存できなかった時、既存ファイルがあれば警告に 「古い画像が残っています → ファイル名(撮影日)」 を明示。
黙って古いものを残すと「撮り直したのに直らない」が延々続く。

運用メモ

  • どうしても撮れないサイトは python3 tools/derive_shot.py --fv-from-thumb <id>(一覧サムネからFVを作る・大串承認済みの正規手段)
  • 撮り直し後の本番確認は必ずキャッシュを迂回する(?cb=xxx を付ける)。ブラウザキャッシュで古い画像が見え、直っていないと誤認する
  • 既知の別件: SHOGUN PIZZA は「ロゴ重ね」で採用ゲートに弾かれる(ver207 から既知・FVはサムネ由来)

関連

追記(2026-07-31)— 今度は「撮影がFVを消していた」

大串FB「詳細ページのスマホ画像おかしくなってる」= CAFE TORTUE の SP が真っ黒。
段階測定(各処理の前後で色数・最頻色占有率を測る)で犯人を特定した。

処理 色数 単色率
待機後 450種 9%(正常)
killCurtain 後 12種 97%(破壊)
(killCurtain修正後)dedupeOverlap 後 12種 97%(破壊)
  • 犯人1 = killCurtainopening|intro|splash|loading|loader|curtain|mask|transition という名前だけで全画面要素を消していた。
    Vegas.js の背景スライダー .vegas-slide vegas-transition-fadetransition に一致し、FVの背景そのものを幕として消していた。
  • 犯人2 = dedupeOverlap:同じスライド2枚を「重なり」とみなして消していた。
  • → どちらも 「絵の中身(背景画像/大きなメディア/スライダーライブラリ名)を持つ器は消さない」 構造ガードで根治。
    判断に迷うときは 「消さない」に倒す。幕が残った画像は検査で弾けるが、FVを消した白画像は「撮れている」ように見えてしまう

同時に入れた保護(どれも汎用)

  1. 退化防止ガード:既存より半分以下に劣化する保存を拒否。
    Promise.race のタイムアウトは撮影処理を止めないため、打ち切り後に劣化画像が正常画像を上書きしていた
    (実際に PC 175KB→15KB の事故が発生。git から復元済み)。
  2. PC/SP の独立化:旧実装は直列で、PCが失敗するとSPを撮りに行かなかった
    CAFE TORTUE は PC が既存優先で止まるため、SPだけ黒いまま永久に直らなかった。
  3. flatRatio(最頻色の占有率)を新設:ロゴだけの画面は輪郭比をすり抜けるので、面積で判定する。
    別セッションが同日に同じ弱点(ON THE TRIP の「真っ黒+ロゴ」)を別角度から直しており、コンフリクトを統合した。

結果: CAFE TORTUE SP 9KB → 78KB で正常化。fondesk / J.SECURITY で回帰なしを確認(fondesk 右下の細かい重なりは変更前と同一=既存課題)。

併走の教訓

この日は mikke! を2セッションが同時に触り、同じファイルの同じ関数を両方が直した。
shot_common.mjs でコンフリクトしたが、内容は補完的(相手=保存しない判定の強化/こちら=撮り直し判定の強化)だったため統合できた。
同じ領域を並行で触ると、片方が知らずにもう片方の前提を崩す。1システム=1セッションを守るのが安全。