channel-worker 2.5.30 → 2.5.31
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 +1 -1
- package/scripts/warmup_facebook.js +37 -5
- package/scripts/warmup_youtube.js +30 -0
package/package.json
CHANGED
|
@@ -231,8 +231,25 @@ async function clickResultByHref(page, href) {
|
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
// Read the open reel/video's playback state — used to advance early when it
|
|
235
|
+
// ENDS or STALLS instead of idling until watch_time elapses.
|
|
236
|
+
async function readVideoState(page) {
|
|
237
|
+
return page.evaluate(() => {
|
|
238
|
+
const v = document.querySelector('video');
|
|
239
|
+
if (!v) return null;
|
|
240
|
+
if (v.paused && !v.ended) { try { v.play(); } catch {} }
|
|
241
|
+
return {
|
|
242
|
+
ended: !!v.ended,
|
|
243
|
+
currentTime: Number(v.currentTime) || 0,
|
|
244
|
+
duration: (isFinite(v.duration) && v.duration > 0) ? v.duration : 0,
|
|
245
|
+
loop: !!v.loop,
|
|
246
|
+
};
|
|
247
|
+
}).catch(() => null);
|
|
248
|
+
}
|
|
249
|
+
|
|
234
250
|
// Watch whatever reel/video is currently open (after clicking a result): ensure
|
|
235
|
-
// the <video> plays, watch a random span
|
|
251
|
+
// the <video> plays, watch a random span — but ADVANCE EARLY if a non-looping
|
|
252
|
+
// video ends/stalls (don't sit on a finished video). Mid-view scroll. Best-effort.
|
|
236
253
|
async function watchCurrent(page, minSec, maxSec, log) {
|
|
237
254
|
try {
|
|
238
255
|
await page.waitForTimeout(randInt(2500, 4500)); // let the reel viewer mount
|
|
@@ -244,12 +261,27 @@ async function watchCurrent(page, minSec, maxSec, log) {
|
|
|
244
261
|
return true;
|
|
245
262
|
}).catch(() => false);
|
|
246
263
|
if (!hasVideo) { log('info', '[warmup-fb] no <video> after click — skipping'); return false; }
|
|
264
|
+
|
|
247
265
|
const watchMs = randInt(minSec, maxSec) * 1000;
|
|
248
266
|
log('info', `[warmup-fb] watching reel for ~${Math.round(watchMs / 1000)}s (url=${page.url().slice(-28)})`);
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
267
|
+
const TICK = 1000;
|
|
268
|
+
let watched = 0, scrolled = false, lastT = -1, stallTicks = 0;
|
|
269
|
+
while (watched < watchMs) {
|
|
270
|
+
const st = await readVideoState(page);
|
|
271
|
+
// A non-looping video that ended/stalled → stop waiting, go to next.
|
|
272
|
+
if (st && !st.loop) {
|
|
273
|
+
if (st.ended || (st.duration > 0 && st.currentTime >= st.duration - 0.6)) {
|
|
274
|
+
log('info', `[warmup-fb] reel ended at ${Math.round(st.currentTime)}s — advancing`);
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
if (Math.abs(st.currentTime - lastT) < 0.15) { stallTicks++; if (stallTicks >= 6) { log('info', '[warmup-fb] reel stalled — advancing'); break; } }
|
|
278
|
+
else stallTicks = 0;
|
|
279
|
+
lastT = st.currentTime;
|
|
280
|
+
}
|
|
281
|
+
if (!scrolled && watched >= watchMs / 2) { await organicScroll(page, randInt(1, 2)); scrolled = true; }
|
|
282
|
+
await page.waitForTimeout(TICK);
|
|
283
|
+
watched += TICK;
|
|
284
|
+
}
|
|
253
285
|
return true;
|
|
254
286
|
} catch (e) {
|
|
255
287
|
log('info', `[warmup-fb] watch failed: ${String(e.message || e).slice(0, 90)}`);
|
|
@@ -145,6 +145,23 @@ async function isAdShowing(page) {
|
|
|
145
145
|
}).catch(() => false);
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
// Read the main <video> playback state. Used to advance early when a video
|
|
149
|
+
// ENDS or STALLS instead of sitting idle on a finished video until watch_time
|
|
150
|
+
// elapses (the "video chạy hết rồi không nhảy" report). Returns null if no video.
|
|
151
|
+
async function readVideoState(page) {
|
|
152
|
+
return page.evaluate(() => {
|
|
153
|
+
const v = document.querySelector('video');
|
|
154
|
+
if (!v) return null;
|
|
155
|
+
if (v.paused && !v.ended) { try { v.play(); } catch {} } // nudge autoplay
|
|
156
|
+
return {
|
|
157
|
+
ended: !!v.ended,
|
|
158
|
+
currentTime: Number(v.currentTime) || 0,
|
|
159
|
+
duration: (isFinite(v.duration) && v.duration > 0) ? v.duration : 0,
|
|
160
|
+
loop: !!v.loop,
|
|
161
|
+
};
|
|
162
|
+
}).catch(() => null);
|
|
163
|
+
}
|
|
164
|
+
|
|
148
165
|
// Watch one video: navigate, SKIP any ads (so we watch the real video, not the
|
|
149
166
|
// ad), then let the real content play for a random span — counting only
|
|
150
167
|
// non-ad time toward the budget — with a small mid-view scroll like a real
|
|
@@ -168,6 +185,7 @@ async function watchVideo(page, url, minSec, maxSec, log) {
|
|
|
168
185
|
let adWaited = 0; // ms spent sitting through/again skipping ads
|
|
169
186
|
let scrolled = false;
|
|
170
187
|
let skips = 0;
|
|
188
|
+
let lastT = -1, stallTicks = 0;
|
|
171
189
|
|
|
172
190
|
while (realWatched < watchMs) {
|
|
173
191
|
const adOn = await isAdShowing(page);
|
|
@@ -177,6 +195,18 @@ async function watchVideo(page, url, minSec, maxSec, log) {
|
|
|
177
195
|
if (adWaited >= MAX_AD_WAIT_MS) { log('warn', `[warmup] ${url.slice(-11)} ads exceeded ${MAX_AD_WAIT_MS/1000}s — moving on`); break; }
|
|
178
196
|
} else {
|
|
179
197
|
realWatched += TICK;
|
|
198
|
+
// Advance early if the (non-looping) video ENDED or STALLED, instead of
|
|
199
|
+
// idling on a finished video until watch_time runs out.
|
|
200
|
+
const st = await readVideoState(page);
|
|
201
|
+
if (st && !st.loop) {
|
|
202
|
+
if (st.ended || (st.duration > 0 && st.currentTime >= st.duration - 0.6)) {
|
|
203
|
+
log('info', `[warmup] ${url.slice(-11)} ended at ${Math.round(st.currentTime)}s — advancing`);
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
if (Math.abs(st.currentTime - lastT) < 0.15) { stallTicks++; if (stallTicks >= 6) { log('info', `[warmup] ${url.slice(-11)} stalled — advancing`); break; } }
|
|
207
|
+
else stallTicks = 0;
|
|
208
|
+
lastT = st.currentTime;
|
|
209
|
+
}
|
|
180
210
|
// Mid-view scroll once, roughly halfway through real watch time.
|
|
181
211
|
if (!scrolled && realWatched >= watchMs / 2) { await organicScroll(page, randInt(1, 2)); scrolled = true; }
|
|
182
212
|
}
|