channel-worker 2.5.7 → 2.5.8

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.7",
3
+ "version": "2.5.8",
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": {
@@ -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" opens the native OS file picker. Catch the
325
- // filechooser event and inject the video file path. Multiple aria/text
326
- // candidates so we survive minor i18n drift.
327
- log('info', '[fb-pw] click "Thêm video" + filechooser…');
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('Thêm video')",
330
- "div[role='button']:has-text('Add video')",
331
- "[aria-label='Thêm video']",
332
- "[aria-label='Add video']",
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 sel of addVideoCandidates) {
361
+ for (const loc of addVideoCandidates) {
336
362
  if (videoSet) break;
337
- const btn = await firstVisible(page.locator(sel), 3);
338
- if (!btn) continue;
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 "${sel}"`);
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] "Thêm video" via "${sel}" did not open picker: ${e.message.slice(0, 80)}`);
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 — some BS variants expose the hidden input directly. Try
353
- // setInputFiles on input[type='file'] anywhere on the page that accepts video.
354
- const fi = page.locator("input[type='file']").last();
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', `[fb-pw] video file set via fallback input[type='file']`);
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