channel-worker 2.5.7 → 2.5.9
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/upload_facebook.js +58 -19
package/package.json
CHANGED
|
@@ -321,21 +321,49 @@ async function run({ page, payload, log }) {
|
|
|
321
321
|
// 3) Dismiss any onboarding/tutorial dialog.
|
|
322
322
|
await dismissBsOnboarding(page, log);
|
|
323
323
|
|
|
324
|
-
// 3) Click "Thêm video"
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
|
|
324
|
+
// 3) Click "Thêm video" / "Tải lên" INSIDE the Reels modal — opens the
|
|
325
|
+
// native OS file picker. Catch the filechooser event and inject the
|
|
326
|
+
// video file path.
|
|
327
|
+
//
|
|
328
|
+
// CRITICAL: scope every candidate to the Reels modal ([role='dialog']
|
|
329
|
+
// titled "Tạo thước phim"). The page-wall background still has a
|
|
330
|
+
// "Thêm video" / "Ảnh-video" button in the "Tạo bài viết" widget —
|
|
331
|
+
// without scoping, the locator can match that instead, which opens
|
|
332
|
+
// the regular "Tạo bài viết" composer ON TOP of the Reels modal and
|
|
333
|
+
// the rest of the script lands in the wrong composer.
|
|
334
|
+
log('info', '[fb-pw] click "Thêm video" / "Tải lên" inside Reels modal + filechooser…');
|
|
335
|
+
// Locate the Reels modal first. Two title variants (vi + en).
|
|
336
|
+
const reelsDialog = page
|
|
337
|
+
.locator("[role='dialog']")
|
|
338
|
+
.filter({ has: page.locator(":scope :text-matches('^Tạo thước phim$|^Create reel$|^Create a reel$', 'i')") })
|
|
339
|
+
.first();
|
|
340
|
+
if (!(await reelsDialog.isVisible({ timeout: 6000 }).catch(() => false))) {
|
|
341
|
+
await dumpInventory(page, log, 'no-reels-modal');
|
|
342
|
+
await dumpFailure(page, 'no-reels-modal', log);
|
|
343
|
+
throw new Error('FB Reels modal "Tạo thước phim" did not open after clicking Thước phim entry');
|
|
344
|
+
}
|
|
345
|
+
// Candidates inside the modal only:
|
|
346
|
+
// - "Tải lên" — the explicit blue upload button at the bottom (preferred)
|
|
347
|
+
// - "Thêm video" — the drop-zone label (clickable on some variants)
|
|
348
|
+
// - English equivalents
|
|
328
349
|
const addVideoCandidates = [
|
|
329
|
-
"div[role='button']:has-text('
|
|
330
|
-
"
|
|
331
|
-
"[aria-label='
|
|
332
|
-
"[
|
|
350
|
+
reelsDialog.locator("div[role='button']:has-text('Tải lên')"),
|
|
351
|
+
reelsDialog.locator("button:has-text('Tải lên')"),
|
|
352
|
+
reelsDialog.locator("[aria-label='Tải lên']"),
|
|
353
|
+
reelsDialog.locator("div[role='button']:has-text('Upload')"),
|
|
354
|
+
reelsDialog.locator("[aria-label='Upload']"),
|
|
355
|
+
reelsDialog.locator("div[role='button']:has-text('Thêm video')"),
|
|
356
|
+
reelsDialog.locator("div[role='button']:has-text('Add video')"),
|
|
357
|
+
reelsDialog.locator("[aria-label='Thêm video']"),
|
|
358
|
+
reelsDialog.locator("[aria-label='Add video']"),
|
|
333
359
|
];
|
|
334
360
|
let videoSet = false;
|
|
335
|
-
for (const
|
|
361
|
+
for (const loc of addVideoCandidates) {
|
|
336
362
|
if (videoSet) break;
|
|
337
|
-
const
|
|
338
|
-
if (
|
|
363
|
+
const count = await loc.count().catch(() => 0);
|
|
364
|
+
if (count === 0) continue;
|
|
365
|
+
const btn = loc.first();
|
|
366
|
+
if (!(await btn.isVisible().catch(() => false))) continue;
|
|
339
367
|
try {
|
|
340
368
|
const [chooser] = await Promise.all([
|
|
341
369
|
page.waitForEvent('filechooser', { timeout: 8000 }),
|
|
@@ -343,27 +371,27 @@ async function run({ page, payload, log }) {
|
|
|
343
371
|
]);
|
|
344
372
|
await chooser.setFiles(videoPath);
|
|
345
373
|
videoSet = true;
|
|
346
|
-
log('info', `[fb-pw] video file set via "${
|
|
374
|
+
log('info', `[fb-pw] video file set via modal-scoped "${(await btn.innerText().catch(() => '')).slice(0, 30)}" button`);
|
|
347
375
|
} catch (e) {
|
|
348
|
-
log('info', `[fb-pw]
|
|
376
|
+
log('info', `[fb-pw] modal upload candidate did not open picker: ${e.message.slice(0, 80)}`);
|
|
349
377
|
}
|
|
350
378
|
}
|
|
351
379
|
if (!videoSet) {
|
|
352
|
-
// Fallback —
|
|
353
|
-
//
|
|
354
|
-
const fi =
|
|
380
|
+
// Fallback — find an input[type='file'] that lives INSIDE the Reels
|
|
381
|
+
// modal (not the page-wall composer's hidden input).
|
|
382
|
+
const fi = reelsDialog.locator("input[type='file']").last();
|
|
355
383
|
if (await fi.count().catch(() => 0) > 0) {
|
|
356
384
|
try {
|
|
357
385
|
await fi.setInputFiles(videoPath);
|
|
358
386
|
videoSet = true;
|
|
359
|
-
log('info',
|
|
360
|
-
} catch (e) { log('info', `[fb-pw] fallback input setInputFiles failed: ${e.message.slice(0, 80)}`); }
|
|
387
|
+
log('info', '[fb-pw] video file set via modal-scoped fallback input[type=file]');
|
|
388
|
+
} catch (e) { log('info', `[fb-pw] modal fallback input setInputFiles failed: ${e.message.slice(0, 80)}`); }
|
|
361
389
|
}
|
|
362
390
|
}
|
|
363
391
|
if (!videoSet) {
|
|
364
392
|
await dumpInventory(page, log, 'no-add-video');
|
|
365
393
|
await dumpFailure(page, 'no-add-video', log);
|
|
366
|
-
throw new Error('FB "Thêm video" trigger not found — could not start video upload');
|
|
394
|
+
throw new Error('FB Reels modal: "Thêm video"/"Tải lên" trigger not found — could not start video upload');
|
|
367
395
|
}
|
|
368
396
|
|
|
369
397
|
// 4) Wait for the video to finish processing on FB's side before we can
|
|
@@ -1271,6 +1299,17 @@ async function run({ page, payload, log }) {
|
|
|
1271
1299
|
throw new Error(`FB publish: thumbnail_url provided but the "Chỉnh sửa hình thu nhỏ" overlay was never detected before publish (step ${step + 1}). Refusing to ship without custom thumb. Inspect dump screenshots in Temp\\cm-worker-pw\\.`);
|
|
1272
1300
|
}
|
|
1273
1301
|
log('info', `[fb-pw] click publish "${pub.verb}" via "${pub.sel}" (step ${step + 1})`);
|
|
1302
|
+
// DRY-RUN MODE (temporary, for QA): skip the actual publish click so
|
|
1303
|
+
// we can verify the upstream flow (modal scoping, video upload, thumb
|
|
1304
|
+
// step, metadata fill) without spawning real Reels every iteration.
|
|
1305
|
+
// Throws a recognisable error so the daemon marks the cmd failed
|
|
1306
|
+
// without retrying. REMOVE this block once the page-wall composer
|
|
1307
|
+
// fix is verified end-to-end.
|
|
1308
|
+
if (process.env.FB_PW_DRY_RUN === '1' || payload._dry_run === true) {
|
|
1309
|
+
await dumpInventory(page, log, `dry-run-at-publish-${step + 1}`);
|
|
1310
|
+
await dumpFailure(page, `dry-run-at-publish-${step + 1}`, log);
|
|
1311
|
+
throw new Error(`[fb-pw] DRY-RUN: reached publish step ${step + 1} successfully. Pub button found via "${pub.sel}" verb="${pub.verb}". Thumb=${customThumbDone ? 'uploaded' : 'NOT uploaded'}. NOT clicking. Inspect dump screenshots in Temp\\cm-worker-pw\\.`);
|
|
1312
|
+
}
|
|
1274
1313
|
// Snapshot the captured-IDs list RIGHT BEFORE the publish click. The
|
|
1275
1314
|
// network listener captures from EVERY graph response, including the
|
|
1276
1315
|
// pre-publish page wall feed (~100+ IDs). Only IDs captured AFTER
|