Files
vtube/docs/MIGRATION_PLAYBOOK.md

214 lines
14 KiB
Markdown
Raw Normal View History

# tubev — Site Migration Playbook
**Призначення:** robust план для міграції / refactor наступного site (player, banners, sidebar responsive, etc.). Не лінійний — **discovery-driven** з conditional branches. Кожен пункт може бути SKIP якщо feature відсутня.
> Спирається на досвід з 8148 (PlayerJS pilot), 8161 (full migration + R7 responsive sidebar), 8081 (videojs10 + bannersBlocks reference). Доповнення — у [ADS.md § Migration checklist](ADS.md#migration-checklist--legacy-videojs4--modern-playerjs--ad-bundle).
---
## Modes
- **Interactive** (default) — Claude робить Discovery, потім confirm scope з юзером перед Phase 2.
- **Autonomous batch** — юзер дає пакет sites + global instructions; Claude робить sam без апрувів, per-site report + master `~/comms/batch-summary.md`. Halt тільки на critical ambiguity. Memory: `feedback_autonomous_batch_migration`.
## Phase 1 — Discovery (ОБОВ'ЯЗКОВО спершу)
Перед будь-якими правками — Claude **запитує юзера** + інспектує наявний site, фіксує scope. Без цього — risk hidden assumptions.
### A) Scope decisions (запитати юзера)
| # | Питання | Why it matters |
|---|---------|---------------|
| A1 | **Чи мігруємо player?** (videojs4 → PlayerJS / videojs10 / залишаємо?) | Якщо ні — skip Phase 3 player. |
| A2 | **Чи замінюємо ad spot IDs?** (rotation / new account) — якщо так, дай mapping таблицю. | Якщо ні — skip banner spot rewrite. |
| A3 | **Чи треба responsive sidebar→grid swap (R7-style)?** | Tільки якщо site має sidebar з ads AND junior-grid (related/recommended thumbs). |
| A4 | **Чи зробити уніфікацію критичних inline + index.css?** | За замовчуванням так — запобігає FOUC. |
| A5 | **Spec for site** — щось унікальне (custom player, ASG-bypass, GDPR overlay, etc.)? | Невідомі вимоги дізнаємось ранo, не пізно. |
### B) Site inspection (Claude робить empirically)
| # | Check | Tool / Command |
|---|-------|----------------|
| B1 | URL pattern для video page | grep `id_index` references у layout, парсинг `mysettings.location_css == "id"` block |
| B2 | Player generation у DOM | `curl t1.<domain>/<video-url>` → grep `videojs\|PlayerJS\|pjs_container` |
| B3 | Native banners серед thumbs (1thumb_a-f чи інший module чи нема) | `grep -rn 'native_allpg_1thumb\|render.*banners' views/modules/related_video.etlua views/modules/recommended_video.etlua` |
| B4 | Sidebar layout (vi-side-col? інший?) | `grep -n 'embed_sidebar\|vi-side-col\|sidebar_adspy' views/id_index.etlua` |
| B5 | Cols-change button (для bannersBlocks observer scope) | `grep -rn 'changeNumberCols\|data-changecolumns' views/` |
| B6 | CSS layers — `index.css` + критичні `views/css/css_<page>.etlua` | `ls views/css/css_*.etlua` |
| B7 | Shared lib references — `views/static/js/lib/` git submodule? | `cd views/static/js/lib && git remote -v` |
| B8 | Cooldown regex у `layout.etlua` (popunder spot ID hardcoded) | `grep 'asgsl\|_popRr' layout.etlua` |
| B9 | `c` counter call безпеки (`if(typeof c==="function")`) | grep `tb.start_events_v2` у `id_index.etlua` |
| B10 | `lazyLoadFunc` defined inline | `grep -c 'function lazyLoadFunc' layout.etlua views/static/js/main.min.js` |
**Output Discovery phase:** короткий summary — "Site X: player Y, banners Z (mapping pending), sidebar W, cols-button A/N, etc." → юзер confirms scope перед Phase 2.
---
## Phase 2 — Conditional Task Branches
Кожна гілка SKIP-нулагається якщо Discovery показав feature відсутньою. **Кожна = окремий atomic commit.**
### Branch P (Player migration) — якщо A1=yes
→ повний 30+ checkpoint список у [ADS.md § Migration checklist](ADS.md#migration-checklist--legacy-videojs4--modern-playerjs--ad-bundle).
- Inline lazysizes wrapper якщо main.min.js його не містить (R3).
- `if(typeof c==="function")` guard (counter race fix).
- foreign 8148 classes (`vdo-blk-*`) → site-native (`vi-limiter` etc.) (R5).
- Playwright probe → pageerrors empty.
- **Cleanup dead videojs CSS** — `.video-js`, `.vjs-*`, `@keyframes vjs-*`, `.waiting .loading-spinner` rules **видалити** з `views/static/css/index.css` + `views/css/css_<page>.etlua` (для video page) AFTER player migrated. Залишити **тільки** якщо site has окремі pages що ще videojs (embed.etlua, id_xvideos.etlua module — conditional render). Не видалити `views/static/css/video-js.css` файл (used by embed iframe).
#### Flat-site CSS override (P.flat) — discovered 2026-05-05 (gocc4 batch set B)
**Trigger:** site has FLAT theme — single limiter div з `padding:56.25% 0 0` aspect-ratio trick (single-level structure, не 2-level wrapper>player).
Examples discovered: `.fplayer` (8155), `.bloque-video-vid` (8156), `.vid-blck-vid` (8162), `.vbrlmtr` (8150).
**Symptom:** після PlayerJS DOM replace — `pjs_container` виштовхується padding-trick poза visible area → ad-test fails (asg-container injection не fires bo no click target).
**Fix:** додати inline `<style>` у `id_index.etlua` перед PlayerJS DOM:
```css
.<lmtr-class>{padding:0!important;aspect-ratio:16/9;overflow:visible}
.<lmtr-class> #pjs_container{position:absolute;top:0;left:0;width:100%;height:100%}
```
Replace `<lmtr-class>` на site-native limiter (e.g. `.fplayer`).
**Detection cue:** після initial Branch P attempt → ad-test fail check #5 (`.asg-container injected on click`). Якщо це так — apply P.flat fix + retry ad-test.
**Альтернативно:** ці sites мають **2-level theme** (outer plain wrapper + inner player block, e.g. `.video-card-limiter > .video-player` у 8154) → straightforward replace, no override needed.
### Branch S (Spot ID rotation) — якщо A2=yes
- Replace IDs у `views/static/js/ad-config.js` (popunder.spot, vast.spot).
- Replace `(?:^|\|)<id>=` regex у `layout.etlua` (must sync з ad-config!).
- Replace IDs у `views/modules/banners/*adspy*.etlua` (footer/header/middle/sidebar/native).
- 1thumb_a..f spots якщо site має those modules.
- Per-site inventory таблицю — додати у [ADS.md § Per-site spot inventory](ADS.md#per-site-spot-inventory).
- Build bundle → cache-bust md5.
- ad-test → 6/6 PASS на новий popunder/vast spots.
### Branch R7 (Responsive sidebar→grid) — якщо A3=yes
Передумови: site має sidebar (`.vi-side-col` чи аналог) з ad slots AND grid (`#related_video_block` чи similar) з thumbs.
1. **Уніфікувати DOM IDs** sidebar slots (mobile + desktop templates обидва використовують ті самі ID — `sdd_a`/`sdd_b`).
2. **Move banner-load logic** з template `<script>` у `id_index.etlua` inline → один місто, server-side `isMobile()` choose spot pair, callback `() => bannersBlocks.calc()`.
3. **Inject `bannersBlocks` JS** з activateWidth (1024 default), main block selector (`#related_video_block`), thumb selector (`.video-item:not(.js-iframe_banner)`).
4. **Banner wrapper class** — match site thumb structure (8161 = `<div class="video-item">`, 8081 = `<figure>`). Inspect 1 existing thumb DOM.
5. **iframe transform scale** = thumbW / banner_natural_width (default 300) для banner content fit.
6. **Wrapper height** = scaled height (250 * scale) для row consistency.
7. **CSS hide empty sidebar:** `@media(max-width:<activateWidth>px){.<sidebar-class>{display:none}}` у обидва index.css + critical css_<page>.etlua.
8. **MutationObserver:**
- `mainBlock` — childList + class/style attrs (lazy-load thumbs)
- `document.body` — attributes ['class'] (cols-change button updates body class — без observer banners stale)
9. **Defer init** до DOMContentLoaded якщо script render-иться перед grid block.
10. **Debounce calc** ~150ms на frequent events.
### Branch C (Critical CSS sync) — якщо A4=yes (default)
Кожна правка `views/static/css/index.css` → перевірити чи selector існує у відповідному `views/css/css_<page>.etlua` → дзеркалити rule. Без цього FOUC. Memory: `feedback_critical_inline_css_sync`.
### Branch X (Site-specific) — якщо A5 показав щось
Custom план залежно від спецификy. Документуй у session notes.
---
## Phase 3 — Universal post-checks (ALWAYS, незалежно від Branch)
Усі ці checkpoints — обов'язкові, незалежно від scope. Вони catch генеричні regressions.
### CSS / DOM
- [ ] `index.css` + critical inline `css_<page>.etlua` мають однакові rules для торкнутих selectors.
- [ ] No duplicate DOM IDs на сторінці (Playwright probe `Array.from(document.querySelectorAll('[id]')).map(e=>e.id)` + count duplicates).
- [ ] No foreign-site classes (8148 `vdo-blk-*` etc.) у site-specific files.
### JS / errors
- [ ] Playwright headless `pageerror` array empty при load video page (timeout 8s).
- [ ] No `c is not defined` у inline `start_events_v2` (counter race).
- [ ] No `lazyLoadFunc is not defined` у timeline-pjs (якщо використовується).
- [ ] No `c2.min.js: Unexpected identifier` (re-minify якщо source edit).
- [ ] Console clean: no ASGB loader errors.
### Ad pipeline
- [ ] `ad-test "<test_url>"` → 6/6 PASS.
- [ ] VAST endpoint returns `<Ad>` (curl) — не empty `<VAST/>` no-fill.
- [ ] Popunder endpoint returns `renderSpot` JSON.
- [ ] CDN serves fresh bundle (`curl t1/static/js/ad-bundle.min.js | grep spot:` reflects новий config).
- [ ] adspyglass dashboard show графік не падає (ручний check 1-2h після deploy).
### Responsive (якщо Branch R7 активний)
- [ ] Width > activateWidth: banners у sidebar slots, sidebar visible.
- [ ] Width <= activateWidth: banners у grid (зайве sidebar hidden), no overlap з thumbs, row heights consistent.
- [ ] Resize transition (drag window narrow → wide → narrow): banners move properly, no duplicate, no orphan.
- [ ] Cols-change button click: banners recalc + reposition.
- [ ] Hard-refresh test (browser cache `max-age=3600`): нові правила apply.
### Player (якщо Branch P активний)
- [ ] Click play → VAST pre-roll shows (after popunder cooldown set on first visit).
- [ ] After VAST done → main video starts via `_revealPjs()` callback.
- [ ] Tooltip likes/dislikes над player chrome (z-index battle won).
- [ ] Timeline thumb hover → play icon shown.
- [ ] iOS Safari: `.no-js`/`.js` swap robitь native controls hidden.
### Diff / commit hygiene
- [ ] Atomic commits — 1 commit per logical change (Branch P / S / R7 / C / X = окремий commit).
- [ ] Commit messages пояснюють "WHY", не лише "WHAT".
- [ ] `~/git-save-all.sh` (якщо є backup_<port>) виконано.
- [ ] Push до origin (якщо SSH key authorized).
---
## Phase 4 — Production verification
- [ ] Юзер натискає button у `https://rss.g--o.info/admin/site/control` для prod deploy.
- [ ] 5-10 хв — adspyglass dashboard продовжує показувати impressions.
- [ ] Кейс real-юзер test (попросити reload + перевірити likes/timeline/banners visually).
---
## Anti-patterns (не повторювати помилок 8161 migration)
1. **Скопіювати foreign classes (`vdo-blk-lmtr`) тільки тому що 8148 так робить.** Site має native equivalent — використовуй його. R5 урок.
2. **Append external JS file (`tb.append_script`) коли можна inline.** Зайвий HTTP request. R3 урок.
3. **Edit тільки `index.css` без критичного `css_<page>.etlua`.** FOUC на first paint. Юзер скаржиться. Урок 2026-05-04.
4. **Compound bash chains з fail-prone steps** (`cd && head wrong-file`). Червоні errors дратують юзера. Урок 2026-05-04.
5. **Single-line snippet output форсити.** Терминал word-wrap ламає copy → file workflow краще. Урок 2026-05-04.
6. **Early-return у calc() тільки на widthLine** — не зловити thumb width зміну (button cols toggle). Має track обидва. R7 урок.
7. **observe лише `mainBlock` без body** — cols-button updates body class, banners stale. R7 урок.
8. **Брати template з memory без verify** — пишемо у memory одразу коли юзер каже "це знадобиться", не після compaction. Урок 2026-05-04.
---
## Cheat-sheet команд
```bash
# Discovery
grep -rn 'PATTERN' /home/nosfortube/frontend_<port>/views/
# Test ad pipeline
ad-test "https://t1.<domain>/v/<id>/<slug>"
# Empirical browser probe (write to /tmp first, не до /home/w4/playwright-tests/)
node /tmp/probe-X.mjs
# Build ad bundle (8148-only — інші sites копіювати pipeline)
bash views/static/js/build-ad-bundle.sh
# Snippet delivery до VSCode
cat > /home/nosfortube/orest/snippet.txt << EOF
...
EOF
```
## Memory rules що activate-яться під час migration
- `feedback_critical_inline_css_sync` — CSS dual-layer
- `feedback_clean_bash_no_red` — Read tool > head/cat
- `feedback_use_bots_proactively` — gocc1/gocc2 для audit/research parallel
- `feedback_pre_push_subagent` — code-reviewer на diff перед push
- `feedback_clipboard_snippet_workflow` — snippet → file
- `reference_native_banner_templates` — ASG ExoClick / TrafficStars wrappers
- `reference_ad_regression_tool` — ad-test usage
- `project_player_roadmap` — generation map sites