channel-worker 2.5.27 → 2.5.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "channel-worker",
3
- "version": "2.5.27",
3
+ "version": "2.5.28",
4
4
  "description": "Channel Manager worker daemon — runs on remote machines to execute video pipeline jobs",
5
5
  "main": "lib/daemon.js",
6
6
  "bin": {
@@ -114,28 +114,24 @@ async function collectResultVideoUrls(page) {
114
114
  // skippable ad; non-skippable ads have no button (we wait them out). Multiple
115
115
  // pre-rolls can chain, so this is called every tick of the watch loop.
116
116
  async function trySkipAd(page) {
117
- return page.evaluate(() => {
118
- // ONLY click inside the player's real skip-ad button (stable YT classes).
119
- // A loose text match like /skip/i clicked random buttons every tick on
120
- // videos with no ad keep it strict to the ad-skip control.
121
- const sels = [
122
- '.ytp-ad-skip-button-modern',
123
- '.ytp-ad-skip-button',
124
- '.ytp-skip-ad-button',
125
- '.ytp-ad-skip-button-container button',
126
- ];
127
- for (const s of sels) {
128
- const btn = document.querySelector(s);
129
- if (btn && btn.offsetParent !== null) { btn.click(); return true; }
130
- }
131
- // Strict text fallback require an ad-specific phrase, NOT bare "skip"/"bỏ qua".
132
- const phrases = /bỏ qua quảng cáo|skip ad\b|skip ads/i;
133
- for (const b of document.querySelectorAll('.ytp-ad-skip-button-text, .ytp-ad-skip-button button, button')) {
134
- const t = (b.innerText || b.textContent || '').trim();
135
- if (t && phrases.test(t) && b.offsetParent !== null) { b.click(); return true; }
117
+ // Use Playwright's real .click() (trusted pointer event) a synthetic
118
+ // el.click() from page.evaluate is ignored by YouTube's player, so the ad
119
+ // never actually skipped (saw 52 "skips" with the ad still playing). The
120
+ // skip button only becomes clickable ~5s into the ad; an early click times
121
+ // out return false → retried next tick.
122
+ const sels = [
123
+ '.ytp-ad-skip-button-modern',
124
+ '.ytp-ad-skip-button',
125
+ '.ytp-skip-ad-button',
126
+ '.ytp-ad-skip-button-container button',
127
+ ];
128
+ for (const s of sels) {
129
+ const loc = page.locator(s).first();
130
+ if (await loc.isVisible().catch(() => false)) {
131
+ try { await loc.click({ timeout: 1500 }); return true; } catch { /* not yet clickable */ }
136
132
  }
137
- return false;
138
- }).catch(() => false);
133
+ }
134
+ return false;
139
135
  }
140
136
 
141
137
  // True while an ad is ACTUALLY playing (pre/mid-roll). Relies on the player's
@@ -187,7 +183,8 @@ async function watchVideo(page, url, minSec, maxSec, log) {
187
183
  await page.waitForTimeout(TICK);
188
184
  }
189
185
  if (skips) log('info', `[warmup] ${url.slice(-11)} — skipped ${skips} ad tick(s), real watch ${Math.round(realWatched/1000)}s`);
190
- return true;
186
+ // Only count as a watched video if real (non-ad) content actually played.
187
+ return realWatched > 0;
191
188
  } catch (e) {
192
189
  log('warn', `[warmup] watch failed (${url.slice(-11)}): ${String(e.message || e).slice(0, 100)}`);
193
190
  return false;