Files
vtube/docs/MIGRATION_PLAYBOOK.md

216 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)
0. **Footprint — копіювати SDK URLs з 8148 reference.** ASG admin генерує **унікальний filename per spot** (`nDNVal3.js`, `9iO21Eb.js`, etc.) хоч content однаковий — anti-fingerprint feature. **При migration отримати з ASG admin per-site SDK URLs** (popunder + vast — 2 окремі), update `views/static/js/ad-config.js` `popunder.sdk` та `vast.sdk` per site. Інакше adblockers/fingerprints ловлять shared signature across all sites → easier to block. Discovery 2026-05-05: 14 sites + 8148 reference all мали same `nDNVal3.js`/`9iO21Eb.js` — global footprint.
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