channel-worker 2.5.69 → 2.5.70
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 +72 -28
package/package.json
CHANGED
|
@@ -76,6 +76,22 @@ const REEL_ENTRY_SELECTORS = [
|
|
|
76
76
|
// 20 minutes, so give the real upload enough room instead of failing while FB
|
|
77
77
|
// is visibly still processing it.
|
|
78
78
|
const PUBLISH_ENABLE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
79
|
+
// Facebook can spend several seconds tearing down the nested custom-thumbnail
|
|
80
|
+
// editor before it mounts the final Reel settings CTA. During that transition
|
|
81
|
+
// there is neither a visible "Tiếp" nor "Đăng" button. The old four-poll
|
|
82
|
+
// (~10s) shortcut returned false just before the CTA appeared, producing a
|
|
83
|
+
// false no-advance failure even though the final blue "Đăng" button was ready.
|
|
84
|
+
const PUBLISH_CTA_ABSENT_GRACE_MS = 30 * 1000;
|
|
85
|
+
|
|
86
|
+
// Text observed in the two thumbnail-editor cohorts. The nested variant has no
|
|
87
|
+
// useful dialog aria-label, so checking only "Chỉnh sửa hình thu nhỏ" misses it
|
|
88
|
+
// while Facebook is processing the saved image.
|
|
89
|
+
const THUMBNAIL_EDITOR_TEXT_MARKERS = [
|
|
90
|
+
'Chỉnh sửa hình thu nhỏ',
|
|
91
|
+
'Edit thumbnail',
|
|
92
|
+
'Tải hình thu nhỏ của riêng bạn lên',
|
|
93
|
+
'Upload your own thumbnail',
|
|
94
|
+
];
|
|
79
95
|
|
|
80
96
|
async function firstVisible(locator, max = 5) {
|
|
81
97
|
const n = Math.min(await locator.count().catch(() => 0), max);
|
|
@@ -474,11 +490,14 @@ async function setVideoFile(page, inputHandle, filePath, log, tag = 'fb-pw') {
|
|
|
474
490
|
// well past the 30s upload-wait, so findByVerbs (which skips disabled buttons)
|
|
475
491
|
// finds no publish CTA — and with no "Tiếp" on the last step the loop would
|
|
476
492
|
// throw no-advance. Poll until a bottom-half publish-verb button is present
|
|
477
|
-
// AND enabled. Returns true once enabled; false on timeout, or
|
|
478
|
-
// publish button ever appears (
|
|
479
|
-
async function waitForPublishEnabled(page, verbs, log, timeoutMs, tag = 'fb-pw', {
|
|
493
|
+
// AND enabled. Returns true once enabled; false on timeout, or after a guarded
|
|
494
|
+
// absence window when no publish button ever appears (not the final step).
|
|
495
|
+
async function waitForPublishEnabled(page, verbs, log, timeoutMs, tag = 'fb-pw', {
|
|
496
|
+
onPoll = null,
|
|
497
|
+
absentGraceMs = PUBLISH_CTA_ABSENT_GRACE_MS,
|
|
498
|
+
} = {}) {
|
|
480
499
|
const deadline = Date.now() + timeoutMs;
|
|
481
|
-
let announced = false, sawPresent = false,
|
|
500
|
+
let announced = false, sawPresent = false, absentSince = 0;
|
|
482
501
|
while (Date.now() < deadline) {
|
|
483
502
|
// The final metadata form mounts lazily on some FB cohorts. The first
|
|
484
503
|
// fillMetadata() immediately after Tiếp can run before its textbox exists,
|
|
@@ -513,9 +532,16 @@ async function waitForPublishEnabled(page, verbs, log, timeoutMs, tag = 'fb-pw',
|
|
|
513
532
|
}
|
|
514
533
|
if (st.present) {
|
|
515
534
|
sawPresent = true;
|
|
535
|
+
absentSince = 0;
|
|
516
536
|
if (!announced) { log('info', `[${tag}] final step: "Đăng" disabled (video still processing) — waiting up to ${Math.round(timeoutMs / 1000)}s…`); announced = true; }
|
|
517
|
-
} else if (!sawPresent
|
|
518
|
-
|
|
537
|
+
} else if (!sawPresent) {
|
|
538
|
+
if (!absentSince) absentSince = Date.now();
|
|
539
|
+
// The final settings form mounts lazily after the thumbnail editor closes.
|
|
540
|
+
// Give that transition a real time-based grace period instead of assuming
|
|
541
|
+
// four polls means this is not the final step.
|
|
542
|
+
if (Date.now() - absentSince >= absentGraceMs) {
|
|
543
|
+
return { enabled: false, sawPresent: false };
|
|
544
|
+
}
|
|
519
545
|
}
|
|
520
546
|
await pause(page, 2500);
|
|
521
547
|
}
|
|
@@ -523,6 +549,31 @@ async function waitForPublishEnabled(page, verbs, log, timeoutMs, tag = 'fb-pw',
|
|
|
523
549
|
return { enabled: false, sawPresent };
|
|
524
550
|
}
|
|
525
551
|
|
|
552
|
+
async function isThumbnailEditorVisible(page) {
|
|
553
|
+
return page.evaluate((textMarkers) => {
|
|
554
|
+
const isVisible = (el) => {
|
|
555
|
+
const r = el.getBoundingClientRect();
|
|
556
|
+
if (r.width < 8 || r.height < 8) return false;
|
|
557
|
+
const cs = getComputedStyle(el);
|
|
558
|
+
return cs.visibility !== 'hidden' && cs.display !== 'none' && cs.opacity !== '0';
|
|
559
|
+
};
|
|
560
|
+
const markers = textMarkers.map((s) => s.toLocaleLowerCase());
|
|
561
|
+
for (const dlg of document.querySelectorAll("[role='dialog']")) {
|
|
562
|
+
if (!isVisible(dlg)) continue;
|
|
563
|
+
const txt = (dlg.innerText || '').toLocaleLowerCase();
|
|
564
|
+
if (markers.some((marker) => txt.includes(marker))) return true;
|
|
565
|
+
}
|
|
566
|
+
// Some cohorts render the editor as a nested form instead of a dialog.
|
|
567
|
+
// Its upload control is the most stable signal while the save is pending.
|
|
568
|
+
for (const el of document.querySelectorAll('[aria-label]')) {
|
|
569
|
+
if (!isVisible(el)) continue;
|
|
570
|
+
const al = (el.getAttribute('aria-label') || '').toLocaleLowerCase();
|
|
571
|
+
if ((/tải hình thu nhỏ|thumbnail/.test(al)) && (/tải|upload/.test(al))) return true;
|
|
572
|
+
}
|
|
573
|
+
return false;
|
|
574
|
+
}, THUMBNAIL_EDITOR_TEXT_MARKERS).catch(() => false);
|
|
575
|
+
}
|
|
576
|
+
|
|
526
577
|
const SAFE_RETRY_COMPOSER_CLOSED = 'FB_SAFE_RETRY_COMPOSER_CLOSED';
|
|
527
578
|
|
|
528
579
|
function safeComposerRetryError(message) {
|
|
@@ -580,7 +631,7 @@ async function runOnce({ page, payload, log }) {
|
|
|
580
631
|
} = payload || {};
|
|
581
632
|
if (!video_url) throw new Error('No video_url provided');
|
|
582
633
|
|
|
583
|
-
log('info', '[fb-pw] selectors version=2026.
|
|
634
|
+
log('info', '[fb-pw] selectors version=2026.09.01-thumb-final-cta-race');
|
|
584
635
|
|
|
585
636
|
page.on('dialog', (d) => { d.accept().catch(() => {}); });
|
|
586
637
|
|
|
@@ -1602,6 +1653,7 @@ async function runOnce({ page, payload, log }) {
|
|
|
1602
1653
|
// disabled while FB was still hashing the upload).
|
|
1603
1654
|
const closeDeadline = Date.now() + 60_000;
|
|
1604
1655
|
let modalClosed = false;
|
|
1656
|
+
let closedPolls = 0;
|
|
1605
1657
|
let retryCount = 0;
|
|
1606
1658
|
const RETRY_AFTER_MS = 12_000;
|
|
1607
1659
|
let lastRetryAt = Date.now();
|
|
@@ -1611,27 +1663,16 @@ async function runOnce({ page, payload, log }) {
|
|
|
1611
1663
|
// nested form (no own dialog — its upload button, aria
|
|
1612
1664
|
// "Tải hình thu nhỏ tùy chỉnh lên…", only exists while the
|
|
1613
1665
|
// editor is showing).
|
|
1614
|
-
const still = await page
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
const al = el.getAttribute('aria-label') || '';
|
|
1625
|
-
if (!(/Tải hình thu nhỏ|thumbnail/i.test(al) && /Tải|upload/i.test(al))) continue;
|
|
1626
|
-
const r = el.getBoundingClientRect();
|
|
1627
|
-
if (r.width < 8 || r.height < 8) continue;
|
|
1628
|
-
const cs = getComputedStyle(el);
|
|
1629
|
-
if (cs.visibility === 'hidden' || cs.display === 'none' || cs.opacity === '0') continue;
|
|
1630
|
-
return true;
|
|
1631
|
-
}
|
|
1632
|
-
return false;
|
|
1633
|
-
}).catch(() => false);
|
|
1634
|
-
if (!still) { modalClosed = true; break; }
|
|
1666
|
+
const still = await isThumbnailEditorVisible(page);
|
|
1667
|
+
if (!still) {
|
|
1668
|
+
// Require two consecutive closed samples. Facebook briefly
|
|
1669
|
+
// unmounts the nested form while swapping it for a
|
|
1670
|
+
// processing overlay; treating that single gap as closed
|
|
1671
|
+
// races the final "Đăng" button mount.
|
|
1672
|
+
if (++closedPolls >= 2) { modalClosed = true; break; }
|
|
1673
|
+
} else {
|
|
1674
|
+
closedPolls = 0;
|
|
1675
|
+
}
|
|
1635
1676
|
// Retry Lưu click after RETRY_AFTER_MS without progress —
|
|
1636
1677
|
// limit to 3 retries (so we still leave time for FB to
|
|
1637
1678
|
// finish processing on the last attempt).
|
|
@@ -2801,11 +2842,14 @@ module.exports.__testables = {
|
|
|
2801
2842
|
muteClickInterceptors,
|
|
2802
2843
|
unmuteClickInterceptors,
|
|
2803
2844
|
waitForPublishEnabled,
|
|
2845
|
+
isThumbnailEditorVisible,
|
|
2804
2846
|
hasVisibleReelComposer,
|
|
2805
2847
|
SAFE_RETRY_COMPOSER_CLOSED,
|
|
2806
2848
|
navigateFacebookHome,
|
|
2807
2849
|
navigateFacebookReelCreate,
|
|
2808
2850
|
PUBLISH_ENABLE_TIMEOUT_MS,
|
|
2851
|
+
PUBLISH_CTA_ABSENT_GRACE_MS,
|
|
2852
|
+
THUMBNAIL_EDITOR_TEXT_MARKERS,
|
|
2809
2853
|
REEL_CREATE_SELECTORS,
|
|
2810
2854
|
REEL_ENTRY_SELECTORS,
|
|
2811
2855
|
ADD_VIDEO_SELECTORS,
|