channel-worker 2.5.21 → 2.5.23
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 +51 -29
package/package.json
CHANGED
|
@@ -283,7 +283,7 @@ async function run({ page, payload, log }) {
|
|
|
283
283
|
} = payload || {};
|
|
284
284
|
if (!video_url) throw new Error('No video_url provided');
|
|
285
285
|
|
|
286
|
-
log('info', '[fb-pw] selectors version=2026.06.
|
|
286
|
+
log('info', '[fb-pw] selectors version=2026.06.19d-reels-link-fallback');
|
|
287
287
|
|
|
288
288
|
page.on('dialog', (d) => { d.accept().catch(() => {}); });
|
|
289
289
|
|
|
@@ -454,9 +454,31 @@ async function run({ page, payload, log }) {
|
|
|
454
454
|
.filter({ has: page.locator(":scope :text-matches('^Tạo thước phim$|^Create reel$|^Create a reel$', 'i')") })
|
|
455
455
|
.first();
|
|
456
456
|
if (!(await reelsDialog.isVisible({ timeout: 6000 }).catch(() => false))) {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
457
|
+
// FALLBACK: some accounts render a different UI from the create-post
|
|
458
|
+
// "Thước phim" entry → the composer modal never mounts. Click the Reels
|
|
459
|
+
// link (any <a href*='/reels'>) instead, then re-wait for the modal.
|
|
460
|
+
log('warn', '[fb-pw] Reels modal not open — fallback: click <a href*="/reels"> then re-wait…');
|
|
461
|
+
let xpClicked = false;
|
|
462
|
+
try {
|
|
463
|
+
const reelsLink = page.locator("xpath=//a[contains(@href, '/reels')]").first();
|
|
464
|
+
if (await reelsLink.isVisible({ timeout: 3000 }).catch(() => false)) {
|
|
465
|
+
await reelsLink.click({ timeout: 4000 });
|
|
466
|
+
xpClicked = true;
|
|
467
|
+
log('info', '[fb-pw] clicked Reels link via xpath //a[contains(@href,"/reels")]');
|
|
468
|
+
} else {
|
|
469
|
+
log('warn', '[fb-pw] fallback: no visible <a href*="/reels"> found');
|
|
470
|
+
}
|
|
471
|
+
} catch (e) { log('warn', `[fb-pw] fallback Reels-link click failed: ${e.message.slice(0, 80)}`); }
|
|
472
|
+
if (xpClicked) {
|
|
473
|
+
await page.waitForTimeout(5000);
|
|
474
|
+
await dismissBsOnboarding(page, log).catch(() => {});
|
|
475
|
+
}
|
|
476
|
+
if (!(await reelsDialog.isVisible({ timeout: 8000 }).catch(() => false))) {
|
|
477
|
+
await dumpInventory(page, log, 'no-reels-modal');
|
|
478
|
+
await dumpFailure(page, 'no-reels-modal', log);
|
|
479
|
+
throw new Error('FB Reels modal "Tạo thước phim" did not open (kể cả sau fallback xpath //a[href*="/reels"])');
|
|
480
|
+
}
|
|
481
|
+
log('info', '[fb-pw] Reels modal opened after xpath /reels fallback');
|
|
460
482
|
}
|
|
461
483
|
// Candidates inside the modal only:
|
|
462
484
|
// - "Tải lên" — the explicit blue upload button at the bottom (preferred)
|
|
@@ -1914,12 +1936,30 @@ async function run({ page, payload, log }) {
|
|
|
1914
1936
|
}
|
|
1915
1937
|
}
|
|
1916
1938
|
|
|
1917
|
-
// (a.2)
|
|
1918
|
-
//
|
|
1919
|
-
//
|
|
1920
|
-
//
|
|
1921
|
-
//
|
|
1922
|
-
//
|
|
1939
|
+
// (a.2) Graph API network capture — AUTHORITATIVE. The publish-mutation
|
|
1940
|
+
// response carries the JUST-published reel ID. Only consider IDs
|
|
1941
|
+
// captured AFTER the publish-click snapshot (pre-snapshot IDs are the
|
|
1942
|
+
// page-wall feed pre-render, NOT the publish mutation). Take the FIRST
|
|
1943
|
+
// new ID (the mutation response comes back before feed-reload
|
|
1944
|
+
// responses). Prefer this over scraping the reels tab: the new reel
|
|
1945
|
+
// often isn't on the tab yet (still processing), so the tab's "first
|
|
1946
|
+
// tile" is a STALE older reel — which is the bug this ordering fixes.
|
|
1947
|
+
if (!postUrl && capturedReelIds.length > capturedReelIdsSnapshotLen) {
|
|
1948
|
+
const newIds = capturedReelIds.slice(capturedReelIdsSnapshotLen);
|
|
1949
|
+
const preSet = new Set(capturedReelIds.slice(0, capturedReelIdsSnapshotLen));
|
|
1950
|
+
const trulyNew = newIds.filter((id) => !preSet.has(id));
|
|
1951
|
+
if (trulyNew.length) {
|
|
1952
|
+
const firstNew = trulyNew[0];
|
|
1953
|
+
postUrl = `https://www.facebook.com/reel/${firstNew}/`;
|
|
1954
|
+
log('info', `[fb-pw] post URL from network (FIRST new ID after snapshot): ${postUrl} (snapshotLen=${capturedReelIdsSnapshotLen} total=${capturedReelIds.length} new=${trulyNew.length})`);
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
// (a.3) LAST-RESORT: first reel tile on the profile reels tab. UNRELIABLE —
|
|
1959
|
+
// the just-published reel may not be on the tab yet (still
|
|
1960
|
+
// processing), so the first tile can be a STALE older reel. Only used
|
|
1961
|
+
// when every authoritative source above (fresh-timestamp tile,
|
|
1962
|
+
// title-match, inline CTA, network capture) returned nothing.
|
|
1923
1963
|
if (!postUrl) {
|
|
1924
1964
|
const tileHref = await page.evaluate(() => {
|
|
1925
1965
|
const anchors = document.querySelectorAll("a[role='link']");
|
|
@@ -1934,25 +1974,7 @@ async function run({ page, payload, log }) {
|
|
|
1934
1974
|
}).catch(() => null);
|
|
1935
1975
|
if (tileHref) {
|
|
1936
1976
|
postUrl = tileHref.href.startsWith('http') ? tileHref.href : `https://www.facebook.com${tileHref.href}`;
|
|
1937
|
-
log('
|
|
1938
|
-
}
|
|
1939
|
-
}
|
|
1940
|
-
|
|
1941
|
-
// (b) Graph API network capture — only consider IDs captured AFTER the
|
|
1942
|
-
// publish click snapshot (pre-snapshot IDs are from the page-wall
|
|
1943
|
-
// feed pre-render, NOT the publish mutation). Take the FIRST new ID
|
|
1944
|
-
// since FB's publish-mutation response comes back before any
|
|
1945
|
-
// feed-reload responses.
|
|
1946
|
-
if (!postUrl && capturedReelIds.length > capturedReelIdsSnapshotLen) {
|
|
1947
|
-
const newIds = capturedReelIds.slice(capturedReelIdsSnapshotLen);
|
|
1948
|
-
// Filter out IDs that appeared in the pre-snapshot to be safe (Set
|
|
1949
|
-
// diff). Then take the FIRST new one.
|
|
1950
|
-
const preSet = new Set(capturedReelIds.slice(0, capturedReelIdsSnapshotLen));
|
|
1951
|
-
const trulyNew = newIds.filter((id) => !preSet.has(id));
|
|
1952
|
-
if (trulyNew.length) {
|
|
1953
|
-
const firstNew = trulyNew[0];
|
|
1954
|
-
postUrl = `https://www.facebook.com/reel/${firstNew}/`;
|
|
1955
|
-
log('info', `[fb-pw] post URL from network (FIRST new ID after snapshot): ${postUrl} (snapshotLen=${capturedReelIdsSnapshotLen} total=${capturedReelIds.length} new=${trulyNew.length})`);
|
|
1977
|
+
log('warn', `[fb-pw] post URL from FIRST reel tile on reels_tab (LAST-RESORT, may be STALE): ${postUrl}`);
|
|
1956
1978
|
}
|
|
1957
1979
|
}
|
|
1958
1980
|
|