channel-worker 2.5.52 → 2.5.53

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.52",
3
+ "version": "2.5.53",
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": {
@@ -1255,24 +1255,58 @@ async function runOnce({ page, payload, log }) {
1255
1255
  await page.locator(editBtn.selector).click({ timeout: 5000 });
1256
1256
  await page.waitForTimeout(2500);
1257
1257
 
1258
- // Wait for the thumb-edit modal to mount. Header text =
1259
- // "Chỉnh sửa hình thu nhỏ".
1258
+ // Wait for the thumb editor to mount. FB serves (A/B, seen
1259
+ // 2026-08-19) TWO variants and any given account can get either:
1260
+ // modal — a separate [role='dialog'] titled "Chỉnh sửa hình
1261
+ // thu nhỏ" (the original form).
1262
+ // nested — the editor renders INSIDE the reel-composer dialog
1263
+ // ("Cài đặt thước phim"): no own dialog, and the
1264
+ // header sits too deep in innerText for a 300-char
1265
+ // signature check. Recognize it by its upload button
1266
+ // (aria "Tải hình thu nhỏ tùy chỉnh lên…"), which only
1267
+ // exists while the editor is open, and tag the nearest
1268
+ // container that also holds Lưu + Hủy so the scoped
1269
+ // selectors below keep working for both variants.
1270
+ // Check modal FIRST so old-UI accounts behave exactly as before.
1260
1271
  await page.evaluate(() => document.querySelectorAll("[__fbpw_thumb_edit__]").forEach((el) => el.removeAttribute('__fbpw_thumb_edit__'))).catch(() => {});
1261
- const thumbDialogReady = await page.evaluate(() => {
1272
+ const detectThumbEditor = () => page.evaluate(() => {
1262
1273
  document.querySelectorAll('[__fbpw_thumb_dialog__]').forEach((el) => el.removeAttribute('__fbpw_thumb_dialog__'));
1274
+ const vis = (el) => {
1275
+ const r = el.getBoundingClientRect();
1276
+ if (r.width < 8 || r.height < 8) return false;
1277
+ const cs = getComputedStyle(el);
1278
+ return cs.visibility !== 'hidden' && cs.display !== 'none' && cs.opacity !== '0';
1279
+ };
1263
1280
  for (const dlg of document.querySelectorAll("[role='dialog']")) {
1264
- const r = dlg.getBoundingClientRect();
1265
- if (r.width < 8 || r.height < 8) continue;
1266
- const cs = getComputedStyle(dlg);
1267
- if (cs.visibility === 'hidden' || cs.display === 'none' || cs.opacity === '0') continue;
1281
+ if (!vis(dlg)) continue;
1268
1282
  const sig = `${dlg.getAttribute('aria-label') || ''}\n${(dlg.innerText || '').slice(0, 300)}`;
1269
1283
  if (!/Chỉnh sửa hình thu nhỏ|Edit thumbnail/i.test(sig)) continue;
1270
1284
  dlg.setAttribute('__fbpw_thumb_dialog__', '1');
1271
- return true;
1285
+ return 'modal';
1272
1286
  }
1273
- return false;
1274
- }).catch(() => false);
1275
- if (!thumbDialogReady) throw new Error('thumbnail editor dialog did not mount');
1287
+ const uploadBtn = [...document.querySelectorAll('[aria-label]')].find((el) => {
1288
+ const al = el.getAttribute('aria-label') || '';
1289
+ return /Tải hình thu nhỏ|thumbnail/i.test(al) && /Tải|upload/i.test(al) && vis(el);
1290
+ });
1291
+ if (uploadBtn) {
1292
+ const hasBtn = (root, re) => [...root.querySelectorAll("[role='button'], button")]
1293
+ .some((b) => re.test((b.innerText || '').trim()) && vis(b));
1294
+ let box = uploadBtn.parentElement;
1295
+ while (box && box !== document.body && !(hasBtn(box, /^(Lưu|Save)$/) && hasBtn(box, /^(Hủy|Cancel)$/))) box = box.parentElement;
1296
+ if (box === document.body) box = null;
1297
+ (box || uploadBtn.closest("[role='dialog']") || document.body).setAttribute('__fbpw_thumb_dialog__', '1');
1298
+ return 'nested';
1299
+ }
1300
+ return null;
1301
+ }).catch(() => null);
1302
+ let thumbEditorVia = await detectThumbEditor();
1303
+ const mountDeadline = Date.now() + 12_000;
1304
+ while (!thumbEditorVia && Date.now() < mountDeadline) {
1305
+ await page.waitForTimeout(2000);
1306
+ thumbEditorVia = await detectThumbEditor();
1307
+ }
1308
+ if (!thumbEditorVia) throw new Error('thumbnail editor dialog did not mount');
1309
+ log('info', `[fb-pw] page-wall thumb — editor mounted (variant=${thumbEditorVia})`);
1276
1310
 
1277
1311
  // Click "Tải lên" button — opens OS file picker. Use filechooser
1278
1312
  // race to inject the file path directly. Scope every action to the
@@ -1355,6 +1389,11 @@ async function runOnce({ page, payload, log }) {
1355
1389
  const RETRY_AFTER_MS = 12_000;
1356
1390
  let lastRetryAt = Date.now();
1357
1391
  while (Date.now() < closeDeadline) {
1392
+ // "Editor still open" must cover both variants: the modal
1393
+ // form (dialog whose text leads with the header) and the
1394
+ // nested form (no own dialog — its upload button, aria
1395
+ // "Tải hình thu nhỏ tùy chỉnh lên…", only exists while the
1396
+ // editor is showing).
1358
1397
  const still = await page.evaluate(() => {
1359
1398
  const dlgs = document.querySelectorAll("[role='dialog']");
1360
1399
  for (const dlg of dlgs) {
@@ -1364,6 +1403,15 @@ async function runOnce({ page, payload, log }) {
1364
1403
  if (r.width > 8 && r.height > 8) return true;
1365
1404
  }
1366
1405
  }
1406
+ for (const el of document.querySelectorAll('[aria-label]')) {
1407
+ const al = el.getAttribute('aria-label') || '';
1408
+ if (!(/Tải hình thu nhỏ|thumbnail/i.test(al) && /Tải|upload/i.test(al))) continue;
1409
+ const r = el.getBoundingClientRect();
1410
+ if (r.width < 8 || r.height < 8) continue;
1411
+ const cs = getComputedStyle(el);
1412
+ if (cs.visibility === 'hidden' || cs.display === 'none' || cs.opacity === '0') continue;
1413
+ return true;
1414
+ }
1367
1415
  return false;
1368
1416
  }).catch(() => false);
1369
1417
  if (!still) { modalClosed = true; break; }