ADS.md: add tb.load_frame_baner_v2 details, native postMessage pattern,
spot ID location matrix, VAST flow walkthrough, policy rationale Findings from 8148 spot-ID rotation audit (2026-05-02): - Banner partial loader source: views/static/js/lib/common/js/tbanner.etlua (shared lib, lazy-load via user events / timeout, hidden-tab handling) - Native banners use postMessage from iframe→parent for auto-height (ASG admin wraps TrafficStars/ExoClick code which postMessages dimensions) - Spot IDs живуть у 4 місцях: ad-config.js + 9 banner partials + layout.etlua cooldown regex (popunder ID hardcoded в asgsl matcher) - VAST flow detailed: pjs_play_btn → asgInVideoImpression → VastPreroll.show() → .asg-container → _asgAdDone callback → _revealPjs() - pop-priority + show-1-skip-1 = intentional rev/UX balance (popunder rare, VAST throttled to 50%) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
87
docs/ADS.md
87
docs/ADS.md
@@ -51,6 +51,57 @@ Tool: **terser** (concat + minify). Triggered by `~/git-save-all.sh` коли so
|
|||||||
|
|
||||||
Cache-bust: md5 → `?v=...` у `layout.etlua` (детально у [DEPLOY.md](DEPLOY.md#cache-bust-механізм)).
|
Cache-bust: md5 → `?v=...` у `layout.etlua` (детально у [DEPLOY.md](DEPLOY.md#cache-bust-механізм)).
|
||||||
|
|
||||||
|
### Banner partial loader — `tb.load_frame_baner_v2`
|
||||||
|
|
||||||
|
Source: `views/static/js/lib/common/js/tbanner.etlua` (shared lib з кешуючою етлуа-обгорткою) + `tbanner_min.etlua` (minified inline). Підключається у layout.etlua через `<% render("static.js.lib.common.js.tbanner_min") %>`.
|
||||||
|
|
||||||
|
**Сигнатура:** `tb.load_frame_baner_v2(spot_url, dom_target, timeouts, attrs [, callback])`
|
||||||
|
- `spot_url` — `//a5.g--o.info/api/spots/<spot_id>?p=1`
|
||||||
|
- `dom_target` — `#selector` куди append iframe
|
||||||
|
- `timeouts` — `{event_min_timeout, event_max_timeout}` ms
|
||||||
|
- `attrs` — `{height, width, ...}` iframe attributes / style
|
||||||
|
- `callback` — викликається після append (для native — інсталяція postMessage listener-у)
|
||||||
|
|
||||||
|
**Lazy-load logic:**
|
||||||
|
- Чекає user event (`scroll`, `mousemove`, `touchstart`, `resize`, `mouseenter`, `click`) АБО `event_min_timeout` (раніше)
|
||||||
|
- Якщо tab hidden → відкладає до `visibilitychange`
|
||||||
|
- `event_max_timeout` — fallback hard limit
|
||||||
|
- Counter per banner — окрема черга для кожного instance
|
||||||
|
- iframe attrs default: `sandbox="allow-scripts allow-popups allow-forms allow-same-origin"`, `loading="lazy"`, `class="na"`
|
||||||
|
|
||||||
|
### Native banners — auto-height через postMessage
|
||||||
|
|
||||||
|
Native (`tbn1`, `tbn2`, `tbn3`) — adaptive ads з невідомою висотою. Pattern:
|
||||||
|
|
||||||
|
1. **На стороні ASG** користувач прописує код банера (TrafficStars або ExoClick) + кастом-CSS для responsive grid + кастом-JS який postMessage-ить розмір.
|
||||||
|
2. **На стороні сервера** ASG обертає у iframe (через `/api/spots/<id>` endpoint).
|
||||||
|
3. **Frontend** ловить `window.message` event від iframe → парсить JSON `{url, height}` → встановлює `height` attribute на iframe з `transition: all .3s`.
|
||||||
|
|
||||||
|
**Network providers за ASG (приклад інтеграції на adспy admin side):**
|
||||||
|
- **TrafficStars** — `cdn.tsyndicate.com/sdk/v1/n.js` + `NativeAd({element_id, spot, type: "label-under", cols:4, rows:1, ...})`
|
||||||
|
- **ExoClick** — `a.magsrv.com/ad-provider.js` + `<ins class="eas..." data-zoneid="..." data-keywords="%KW%" data-sub="%SUB1%"></ins>` + `(AdProvider).push({"serve":{}})`
|
||||||
|
|
||||||
|
Обидва вкладають кастомний `<style>` з 4-col responsive grid (≥739px: 4 cols, 450-738: 2, <450: 1). Плюс postMessage скрипт що шле parent розмір на load/resize/mutation.
|
||||||
|
|
||||||
|
ASG керує цими SDK runtime-no — ми бачимо тільки iframe.
|
||||||
|
|
||||||
|
### Spot ID location matrix (8148 — 16 spots)
|
||||||
|
|
||||||
|
| Тип | Файл | Spot ID location |
|
||||||
|
|-----|------|-----------------|
|
||||||
|
| popunder | `views/static/js/ad-config.js` | `popunder.spot` |
|
||||||
|
| VAST/in-video | `views/static/js/ad-config.js` | `vast.spot` |
|
||||||
|
| popunder cooldown matcher | `views/layout.etlua` | inline regex `(?:^|\|)<id>=` (must sync з ad-config) |
|
||||||
|
| desktop footer ×4 | `views/modules/banners/footer_descktop_adspy.etlua` | hardcoded URL `spots/<id>?p=1` |
|
||||||
|
| mobile footer/header/middle | `views/modules/banners/{footer,header,middle}_mobile_adspy.etlua` | same |
|
||||||
|
| desktop sidebar ×2 | `views/modules/banners/embed_sidebar_adspy.etlua` | same |
|
||||||
|
| mobile sidebar ×2 | `views/modules/banners/embed_mobile_sidebar_adspy.etlua` | same |
|
||||||
|
| native (3 partials) | `views/modules/banners/native_*adspy.etlua` | same |
|
||||||
|
|
||||||
|
**При зміні spot IDs (rotation / re-creation):** оновити ВСІ перелічені місця, потім `bash /home/w4/git-save-all.sh "msg"` — auto-rebuild bundle + cache-bust.
|
||||||
|
|
||||||
|
⚠️ **Не забути `layout.etlua` cooldown regex** — окрема hardcoded reference на popunder spot ID (для localStorage `asgsl` cookie matching).
|
||||||
|
|
||||||
## Monitoring
|
## Monitoring
|
||||||
|
|
||||||
### adspyglass dashboard — best diagnostic signal
|
### adspyglass dashboard — best diagnostic signal
|
||||||
@@ -104,8 +155,40 @@ tb.load_frame_baner_v2("//surstrom.com/api/spots/72437?p=1","#tb0",{...},{...});
|
|||||||
- [ ] PSI вплив реклами — % LCP / TBT (per pattern)?
|
- [ ] PSI вплив реклами — % LCP / TBT (per pattern)?
|
||||||
- [ ] Lazy-load ads below-fold — реалізовано (видно `loading="lazy"` на iframes)?
|
- [ ] Lazy-load ads below-fold — реалізовано (видно `loading="lazy"` на iframes)?
|
||||||
|
|
||||||
|
## VAST flow (8148 detailed)
|
||||||
|
|
||||||
|
### User experience
|
||||||
|
|
||||||
|
1. Користувач клацає play на `pjs_play_btn` (PlayerJS player)
|
||||||
|
2. `asgInVideoImpression` event від ASG SDK → `_decideAndApply` ловить → запускає `VastPreroll.show()`
|
||||||
|
3. ASG іnject-ить `.asg-container` з video preroll
|
||||||
|
4. `MutationObserver` ловить container → pin pointer + z-index 5000, добавляє progress bar (`.vast-progress` yellow #ffd000)
|
||||||
|
5. Coordinated mute: video у container синхронізується з `_vastMuted` localStorage (per-tab persist)
|
||||||
|
6. Після VAST done (video.ended OR `currentTime >= duration - 0.2`) → callback `window._asgAdDone()` → main video starts via `window._revealPjs()`
|
||||||
|
7. `enableMediaPlayHijack: true` блокує race у HTMLMediaElement.play() якщо `_adsLocked` — попереджає 1-frame audio artifact коли VAST стартує над main video
|
||||||
|
|
||||||
|
### Cooldown management
|
||||||
|
|
||||||
|
- ASG локальне сховище: `localStorage.asgsl` — pipe-separated `<spot_id>=<key>:<value>` записи. Ключ `global_rr` = "remaining ratio" (timestamp до якого spot у cooldown).
|
||||||
|
- Дублюючі cookies: `_popRr`, `_vastRr`. Реплікуються з asgsl при impression event.
|
||||||
|
- Декрипт пам'яті: дві ключові події з ASG — `asgPopunderImpression`, `asgInVideoImpression` — тригерять запис cooldown.
|
||||||
|
|
||||||
|
### Mode decision (`AdCore._decide`)
|
||||||
|
|
||||||
|
- `popActive = true && vastActive = true` → `none` (поки активна `recoverFromNone`)
|
||||||
|
- `popActive = false` → `pop` (priority)
|
||||||
|
- `popActive = true && vastActive = false` → `vast`
|
||||||
|
- AND `vastPolicy "show-1-skip-1"` skip pattern на половину VAST imps
|
||||||
|
|
||||||
|
### Policy choice
|
||||||
|
|
||||||
|
`pop-priority` policy + `show-1-skip-1` для VAST — **навмисний баланс ревеню vs UX**:
|
||||||
|
- Popunder показується відносно рідко (cap від ASG високий)
|
||||||
|
- VAST спрацьовує часто, тому штучно скачуємо до 50% (`show-1-skip-1`) — менше тиску на юзера, не вбиває engagement
|
||||||
|
- Якщо колись треба максимізувати ревеню → `vastPolicy: 'show-always'`
|
||||||
|
|
||||||
## Open questions for developer
|
## Open questions for developer
|
||||||
|
|
||||||
1. Як здійснюється mirror swap — sed-replace у всіх template/JS файлах через Адміна, runtime config, чи інший механізм?
|
1. Як здійснюється mirror swap — sed-replace у всіх template/JS файлах через Адміна, runtime config, чи інший механізм?
|
||||||
2. 8148 ad-bundle — план поширити цю архітектуру на інші сайти, чи це 1-of-1?
|
2. Які pattern використовується для НЕ-77 сайтів (17 без adspy banner modules) — special-case structure, "no ads" сайти, чи щось інше?
|
||||||
3. Які pattern використовується для НЕ-77 сайтів (17 без adspy banner modules) — special-case structure, "no ads" сайти, чи щось інше?
|
3. SDK URLs (`nDNVal3.js`, `9iO21Eb.js`) — чи стабільні чи можуть змінитися при mirror rotation?
|
||||||
|
|||||||
Reference in New Issue
Block a user