channel-worker 2.5.26 → 2.5.27

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.26",
3
+ "version": "2.5.27",
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": {
@@ -115,21 +115,22 @@ async function collectResultVideoUrls(page) {
115
115
  // pre-rolls can chain, so this is called every tick of the watch loop.
116
116
  async function trySkipAd(page) {
117
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.
118
121
  const sels = [
119
122
  '.ytp-ad-skip-button-modern',
120
123
  '.ytp-ad-skip-button',
121
124
  '.ytp-skip-ad-button',
122
- 'button.ytp-ad-skip-button-modern',
123
125
  '.ytp-ad-skip-button-container button',
124
126
  ];
125
127
  for (const s of sels) {
126
128
  const btn = document.querySelector(s);
127
129
  if (btn && btn.offsetParent !== null) { btn.click(); return true; }
128
130
  }
129
- // Text-based fallback (locale-proof) any visible button whose label is a
130
- // skip-ad phrase.
131
- const phrases = /bỏ qua quảng cáo|bỏ qua|skip ad|skip ads|skip/i;
132
- for (const b of document.querySelectorAll('button, .ytp-ad-skip-button-text')) {
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')) {
133
134
  const t = (b.innerText || b.textContent || '').trim();
134
135
  if (t && phrases.test(t) && b.offsetParent !== null) { b.click(); return true; }
135
136
  }
@@ -137,13 +138,14 @@ async function trySkipAd(page) {
137
138
  }).catch(() => false);
138
139
  }
139
140
 
140
- // True while an ad is playing on the watch page (pre/mid-roll). Used so the
141
- // watch budget only counts REAL video time, not ad time.
141
+ // True while an ad is ACTUALLY playing (pre/mid-roll). Relies on the player's
142
+ // `ad-showing` class the canonical signal YouTube toggles only during ad
143
+ // playback. Do NOT key off .ytp-ad-module (a persistent container that exists
144
+ // even with no ad → false positive that pinned the watch loop at "0s real").
142
145
  async function isAdShowing(page) {
143
146
  return page.evaluate(() => {
144
147
  const player = document.querySelector('#movie_player, .html5-video-player');
145
- if (player && player.classList.contains('ad-showing')) return true;
146
- return !!document.querySelector('.ytp-ad-player-overlay, .ytp-ad-module, .ad-showing');
148
+ return !!(player && player.classList.contains('ad-showing'));
147
149
  }).catch(() => false);
148
150
  }
149
151
 
@@ -165,7 +167,7 @@ async function watchVideo(page, url, minSec, maxSec, log) {
165
167
  log('info', `[warmup] watching ${url.slice(-11)} for ~${Math.round(watchMs / 1000)}s of REAL video (skipping ads)`);
166
168
 
167
169
  const TICK = 1000;
168
- const MAX_AD_WAIT_MS = 90_000; // cap total ad-waiting so a non-skippable ad reel can't hang the session
170
+ const MAX_AD_WAIT_MS = 60_000; // cap total ad-waiting so a non-skippable ad reel can't hang the session
169
171
  let realWatched = 0; // ms of actual (non-ad) playback counted
170
172
  let adWaited = 0; // ms spent sitting through/again skipping ads
171
173
  let scrolled = false;