tooling: scheduled profiles/full-sweep list posts+reels by browser (chrome-sync per profile/tab, early stop for weekly); GDL_LISTING=api fallback; reels-tab sweep note in MANUAL-RUN

This commit is contained in:
2026-10-23 00:49:45 -04:00
parent 1b67765d8b
commit fe6ebdd75b
5 changed files with 95 additions and 20 deletions
+10 -5
View File
@@ -54,16 +54,21 @@ for p in withaseul cher_ryppo zindoriyam; do ~/gdl/posts-sync.sh $p; done
minutes, mostly the 12–20 s gap between fetches. minutes, mostly the 12–20 s gap between fetches.
- It prints `no new posts for <user>` when a profile is caught up. - It prints `no new posts for <user>` when a profile is caught up.
- Logs: `~/gdl/logs/posts-<user>-*.log`. - Logs: `~/gdl/logs/posts-<user>-*.log`.
- Reels also show up in the main grid. `~/gdl/run-reels-all.sh` sweeps the separate Reels tab - **Then sweep the Reels tab too:** `~/gdl/run-reels-all.sh` (~15 min). Most reels never
too (~15 min) and is rarely needed. appear in the main grid. On 2026-10-23 only 3 of heejin's 114 reels did, so `posts-sync.sh`
alone misses them. `reels-sync.sh <user>` does a single profile.
**Once a gallery-dl release fixes the listing**, one command covers posts, reels, stories and **Or the whole thing in one command**, the same run the weekly timer does: gallery-dl for
highlights for all six profiles: stories and highlights, then the browser for every profile's posts grid and Reels tab, each
stopping once it reaches archived content:
```sh ```sh
~/gdl/run-profiles.sh # ~5 min ~/gdl/run-profiles.sh
``` ```
Once a gallery-dl release fixes the listing, `GDL_LISTING=api ~/gdl/run-profiles.sh` does posts
and reels through gallery-dl again (~5 min).
For long runs, detach: For long runs, detach:
`setsid nohup ~/gdl/run-profiles.sh >/dev/null 2>&1 &`, then `setsid nohup ~/gdl/run-profiles.sh >/dev/null 2>&1 &`, then
`tail -f ~/gdl/logs/$(ls -1t ~/gdl/logs | head -1)`. `tail -f ~/gdl/logs/$(ls -1t ~/gdl/logs | head -1)`.
+14 -8
View File
@@ -327,9 +327,15 @@ newest-first, so after 2 scroll rounds in a row that turned up nothing unarchive
(`--max-known-rounds`, round 1 exempt because pinned posts sit on top) there is nothing newer to (`--max-known-rounds`, round 1 exempt because pinned posts sit on top) there is nothing newer to
find. First run, `heejin`: 3 scrolls, 36 posts seen, 30 already held, 6 new. find. First run, `heejin`: 3 scrolls, 36 posts seen, 30 already held, 6 new.
Same caveats as reels: it drives the signed-in Chrome on `:9222`, so it is by hand, not on a **Scheduled since 2026-10-23.** `gdl-cron.sh profiles` and `full-sweep` now list posts *and*
timer. Once a gallery-dl release fixes the listing, `run-profiles.sh` covers posts again and reels this way: gallery-dl only does `--only stories,highlights`, then `chrome-sync.sh` runs per
this goes back to being a fallback. profile for the `posts` grid and the `reels` tab (both tabs needed: on 10-23 only 3 of heejin's
114 reels were in the main grid). `profiles` uses the early stop (2 known rounds) on both tabs,
and `full-sweep` scrolls every grid to the bottom. A failed browser step fails the run, which
shows on Healthchecks. This reverses the earlier "by hand only" decision on purpose: the API
listing is broken and the owner chose the browser. The trade-off is that unattended runs now
need Chrome up on `:9222` with a live session, and they open and close their own tabs in it.
`GDL_LISTING=api` restores the all-gallery-dl run once a release fixes gh#9829.
**Deploy scripts atomically, never in place.** bash reads a script as it runs, so overwriting **Deploy scripts atomically, never in place.** bash reads a script as it runs, so overwriting
`gdl-cron.sh` under a running sync makes bash resume at the same byte offset in the *new* text. `gdl-cron.sh` under a running sync makes bash resume at the same byte offset in the *new* text.
@@ -836,11 +842,11 @@ after — left behind for inspection, same as `gdl-cron.sh`'s `staging-*`.
Verified end to end against `zindoriyam` on 2026-08-27: 26 reels found on the Verified end to end against `zindoriyam` on 2026-08-27: 26 reels found on the
page, 10 new ones fetched and published cleanly on the first run. page, 10 new ones fetched and published cleanly on the first run.
**Not wired into `gdl-cron.sh` or the timers.** Both scripts drive your **Not wired into `gdl-cron.sh` or the timers** (as of 2026-08; **superseded 2026-10-23**: the
actual browser session rather than firing a background request, and are weekly and quarterly runs now use the browser listing, see "Posts grid by browser" above). Both
slower by design (real scrolling, not an API call) — both good reasons not scripts drive your actual browser session rather than firing a background request, and are
to run this unattended without deciding that deliberately. Today it is a slower by design (real scrolling, not an API call), both good reasons not to run this
per-profile, by-hand tool only. unattended without deciding that deliberately.
For all six ARTMS profiles in one go, `~/gdl/run-reels-all.sh` just calls For all six ARTMS profiles in one go, `~/gdl/run-reels-all.sh` just calls
`reels-sync.sh` once per profile in `artms_account_links.txt`, same `reels-sync.sh` once per profile in `artms_account_links.txt`, same
+5 -1
View File
@@ -9,7 +9,8 @@
# Usage: GDL_TAB=reels|posts ./chrome-sync.sh <username-or-profile-url> # Usage: GDL_TAB=reels|posts ./chrome-sync.sh <username-or-profile-url>
# Normally called through reels-sync.sh / posts-sync.sh, which set GDL_TAB. # Normally called through reels-sync.sh / posts-sync.sh, which set GDL_TAB.
# #
# Deliberately NOT wired into gdl-cron.sh or the timers -- see TOOLING.md. # Also run by gdl-cron.sh (profiles, full-sweep) once per profile and tab since
# 2026-10-23, with GDL_MAX_KNOWN_ROUNDS set -- see TOOLING.md "Posts grid by browser".
# Exits non-zero if either step does. Everything is logged. # Exits non-zero if either step does. Everything is logged.
set -eu set -eu
@@ -29,6 +30,8 @@ SLEEP="${GDL_SLEEP:-5 10}"
RATE="${GDL_RATE:-500K}" RATE="${GDL_RATE:-500K}"
SCROLL_PAUSE="${GDL_SCROLL_PAUSE:-2.0 3.5}" SCROLL_PAUSE="${GDL_SCROLL_PAUSE:-2.0 3.5}"
MAX_IDLE_ROUNDS="${GDL_MAX_IDLE_ROUNDS:-3}" MAX_IDLE_ROUNDS="${GDL_MAX_IDLE_ROUNDS:-3}"
# Empty = instagram-chrome-scrape.py's own default (2 for posts, 0 = scroll to the end for reels).
MAX_KNOWN_ROUNDS="${GDL_MAX_KNOWN_ROUNDS:-}"
PATH="$HOME/.local/bin:$PATH"; export PATH PATH="$HOME/.local/bin:$PATH"; export PATH
@@ -72,6 +75,7 @@ set +e
--index "$INDEX" \ --index "$INDEX" \
--scroll-pause $SCROLL_PAUSE \ --scroll-pause $SCROLL_PAUSE \
--max-idle-rounds "$MAX_IDLE_ROUNDS" \ --max-idle-rounds "$MAX_IDLE_ROUNDS" \
${MAX_KNOWN_ROUNDS:+--max-known-rounds "$MAX_KNOWN_ROUNDS"} \
--out "$URLS_FILE" 2>&1 | tee -a "$LOG" --out "$URLS_FILE" 2>&1 | tee -a "$LOG"
scrape_status=${PIPESTATUS[0]} scrape_status=${PIPESTATUS[0]}
set -e set -e
+63 -3
View File
@@ -10,6 +10,11 @@
# EDITED after we archived them, and by far the most # EDITED after we archived them, and by far the most
# expensive thing here -- see TOOLING.md before running. # expensive thing here -- see TOOLING.md before running.
# #
# Posts and reels are LISTED by scrolling the signed-in Chrome (chrome-sync.sh,
# per profile and tab) rather than by gallery-dl's listing API, which broke on
# 2026-10-22 (gh#9829) while single-post fetches kept working. gallery-dl still
# does stories and highlights. GDL_LISTING=api restores the old all-API run.
#
# Exits non-zero if the sync does, so cron mails you. Everything is logged. # Exits non-zero if the sync does, so cron mails you. Everything is logged.
set -eu set -eu
@@ -41,6 +46,15 @@ SLEEP_REQUEST="${GDL_SLEEP_REQUEST:-12 20}"
SLEEP="${GDL_SLEEP:-5 10}" SLEEP="${GDL_SLEEP:-5 10}"
RATE="${GDL_RATE:-500K}" RATE="${GDL_RATE:-500K}"
LISTING="${GDL_LISTING:-browser}"
case "$LISTING" in
browser|api) ;;
*) echo "GDL_LISTING must be browser or api, not '$LISTING'" >&2; exit 2 ;;
esac
GDL_PYTHON="${GDL_PYTHON:-$HOME/.local/share/pipx/venvs/gallery-dl/bin/python3}"
# Set per mode below when posts/reels go through the browser: empty = no browser step.
BROWSER_KNOWN_ROUNDS=""
case "$MODE" in case "$MODE" in
# --min-interval 8, not the 20h default. The floor exists to stop an ABORTED # --min-interval 8, not the 20h default. The floor exists to stop an ABORTED
# RESTART re-enumerating profiles -- a minutes-to-hours concern. At 20h the # RESTART re-enumerating profiles -- a minutes-to-hours concern. At 20h the
@@ -50,10 +64,23 @@ case "$MODE" in
# request per profile, so the worst case 8h permits is roughly twelve # request per profile, so the worst case 8h permits is roughly twelve
# requests in a day instead of six. # requests in a day instead of six.
stories) ARGS="--only stories --min-interval 8" ;; stories) ARGS="--only stories --min-interval 8" ;;
profiles) ARGS="--only posts,reels,stories,highlights --abort 50" ;; # Browser listing: stop scrolling a grid after 2 rounds of nothing unarchived,
# the browser's equivalent of --abort 50.
profiles)
if [ "$LISTING" = browser ]; then
ARGS="--only stories,highlights --abort 50"; BROWSER_KNOWN_ROUNDS=2
else
ARGS="--only posts,reels,stories,highlights --abort 50"
fi ;;
# No --abort: the whole point of full-sweep is enumerating to the end, so it # No --abort: the whole point of full-sweep is enumerating to the end, so it
# is the only run that notices carousels edited after we archived them. # is the only run that notices carousels edited after we archived them. The
full-sweep) ARGS="--only posts,reels,stories,highlights" ;; # browser equivalent is scrolling every grid to the bottom (0 = no early stop).
full-sweep)
if [ "$LISTING" = browser ]; then
ARGS="--only stories,highlights"; BROWSER_KNOWN_ROUNDS=0
else
ARGS="--only posts,reels,stories,highlights"
fi ;;
# Renamed 2026-08-22: "full" was misleading (it is the abort-LIMITED run) and # Renamed 2026-08-22: "full" was misleading (it is the abort-LIMITED run) and
# "sweep" did not say it was the exhaustive one. Catch the old names rather # "sweep" did not say it was the exhaustive one. Catch the old names rather
# than failing with a bare error, in case something still passes them. # than failing with a bare error, in case something still passes them.
@@ -125,6 +152,39 @@ set +e
status=${PIPESTATUS[0]} status=${PIPESTATUS[0]}
set -e set -e
# Posts and reels by browser: scrape each profile's grid and Reels tab, fetch only
# the new shortcodes as single posts. chrome-sync.sh logs and publishes on its own;
# its output is copied into this run's log so the Healthchecks body shows it too.
if [ -n "$BROWSER_KNOWN_ROUNDS" ]; then
mapfile -t PROFILES < <("$GDL_PYTHON" -c "
import sys, importlib.util
from pathlib import Path
spec = importlib.util.spec_from_file_location('gdl_sync', Path('$GDL_HOME/gdl-sync.py'))
gdl = importlib.util.module_from_spec(spec)
sys.modules['gdl_sync'] = gdl
spec.loader.exec_module(gdl)
for p in gdl.read_urls_file(Path('$GDL_HOME/artms_account_links.txt')):
print(p)
")
browser_failed=0
for p in "${PROFILES[@]}"; do
for tab in posts reels; do
echo "==> browser: $p / $tab" | tee -a "$LOG"
set +e
GDL_TAB=$tab GDL_MAX_KNOWN_ROUNDS=$BROWSER_KNOWN_ROUNDS \
"$GDL_HOME/chrome-sync.sh" "$p" 2>&1 | tee -a "$LOG" >/dev/null
s=${PIPESTATUS[0]}
set -e
if [ "$s" -ne 0 ]; then
echo " browser $p / $tab FAILED (exit $s)" | tee -a "$LOG" >&2
browser_failed=$((browser_failed + 1))
fi
done
done
echo "browser listing: ${#PROFILES[@]} profile(s), $browser_failed step(s) failed" | tee -a "$LOG"
[ "$browser_failed" -gt 0 ] && [ "$status" -eq 0 ] && status=1
fi
# A stories run that skipped every source is NOT a success. It means the # A stories run that skipped every source is NOT a success. It means the
# min-interval floor blocked the one surface that cannot be backfilled, and # min-interval floor blocked the one surface that cannot be backfilled, and
# without this it looks identical to a clean run: exit 0, "0 step(s) failed". # without this it looks identical to a clean run: exit 0, "0 step(s) failed".
+3 -3
View File
@@ -5,9 +5,9 @@
# version of running reels-sync.sh six times by hand, which is what the # version of running reels-sync.sh six times by hand, which is what the
# 2026-09-03 run was before this script existed. # 2026-09-03 run was before this script existed.
# #
# Deliberately not wired into gdl-cron.sh or a timer -- same reasoning as # A by-hand full sweep of every Reels tab (no early stop). The weekly
# reels-sync.sh itself: it drives your actual browser session and is slower # gdl-cron.sh profiles run lists reels through chrome-sync.sh itself, with an
# by design, both good reasons to run it deliberately, not unattended. # early stop, since 2026-10-23 -- see TOOLING.md "Posts grid by browser".
# #
# Usage: ./reels-all.sh # Usage: ./reels-all.sh
# GDL_ARTMS_LINKS=/path/to/list ./reels-all.sh # override the profile list # GDL_ARTMS_LINKS=/path/to/list ./reels-all.sh # override the profile list