This project automates downloading photos/videos for parent accounts on Learning Genie. It wraps Playwright to log in, pulls the Notes API for every enrolled child, and runs a bash downloader that saves the media with local EXIF timestamps.
- Playwright-based login that captures persistent storage state and required headers (
X-UID, timezone, etc.). - CLI commands:
login: interactive login to refresh auth storage.fetch: download raw Notes JSON for a single enrollment/date range.sync: multi-enrollment workflow that logs in if needed, fetches only new entries, and runs the downloader per child.
- Incremental sync: tracks the latest synced timestamp per enrollment in a state file so reruns only fetch new media.
- Per-child timezone detection so downloaded media gets EXIF data in local time.
- Docker container with Chromium/Playwright,
jq,supercronic, and an optional cron schedule viaCRON_EXPRESSION.
- Node.js 20+
- npm
- Playwright dependencies (for Chromium):
npx playwright install sudo npx playwright install-deps
- Runtime dependencies used by the downloader:
jq,aria2c,exiftool,date(typically preinstalled on most Linux distributions).
-
Install dependencies:
npm install
-
Login once to capture auth state (this stores cookies + X-UID under
auth.storage.json):LG_USER="you@example.com" LG_PASS="secret" node ./lg.mjs login --headful
--headfulis optional; omit it if you trust headless login. -
Run a one-off sync:
LG_USER="you@example.com" LG_PASS="secret" \ node ./lg.mjs sync \ --start 2025-09-01 --end 2025-09-30 \ --outdir ./downloads
- Omitting
--start/--endwill fetch “all history”, but subsequent runs will only pull new media (the CLI records the last-synced timestamp per child). - Add
--enrollment <GUID>to limit to a specific child. - Output JSONs are written to
input.json(or suffixed copies per child) before the downloader runs.
- Omitting
-
Run the downloader script directly (if needed):
./learning-genie-download.sh input.json ./downloads
The project ships with a Dockerfile based on mcr.microsoft.com/playwright:v1.55.1-jammy and installs everything needed (Chromium, dependencies, jq, supercronic).
docker build -t learning-genie-sync .docker run --rm \
-e LG_USER='you@example.com' \
-e LG_PASS='secret' \
-v /path/on/host:/data \
learning-genie-sync/datais the default volume used for persistent auth + downloads. The container writes:/data/auth.storage.json/data/<ChildName>/...(media)
- You can pass extra CLI args via
SYNC_ARGS, for example to restrict a date range:docker run --rm \ -e LG_USER='you@example.com' \ -e LG_PASS='secret' \ -e SYNC_ARGS='--start 2025-09-01 --end 2025-09-30' \ -v /path/on/host:/data \ learning-genie-sync
Set CRON_EXPRESSION with any standard 5-field cron string. The entrypoint performs a sync immediately, then schedules future runs with supercronic.
docker run --rm \
-e LG_USER='you@example.com' \
-e LG_PASS='secret' \
-e CRON_EXPRESSION='0 18 * * 1-5' \
-v /path/on/host:/data \
learning-genie-syncExample above runs at 6 pm every weekday.
| Name | Required | Default | Description |
|---|---|---|---|
LG_USER |
Yes | — | Parent account email |
LG_PASS |
Yes | — | Parent account password |
CRON_EXPRESSION |
No | (unset) | Cron schedule for recurring syncs |
SYNC_ARGS |
No | (empty) | Extra CLI flags for lg.mjs sync |
OUTDIR |
No | /data |
Root directory for downloads (maps to host volume) |
OUTFILE |
No | /tmp/input.json |
Base JSON output file per sync |
AUTH_PATH |
No | /data/auth.storage.json |
Storage state path |
STATE_PATH |
No | /data/sync-state.json |
Persistent per-enrollment watermark store |
LOCAL_TZ |
No | (Derived per child) | Override timezone for downloader (rarely needed) |
node lg.mjs login --headful→ capture fresh auth after password changes or whenX-UIDbecomes invalid.node lg.mjs fetch --enrollment <GUID> --start YYYY-MM-DD --end YYYY-MM-DD --out out.json→ inspect raw Notes.npm run sync(after settingLG_USER/LG_PASS) → convenience wrapper aroundnode lg.mjs sync.- The downloader expects
jq,aria2c, andexiftool; the Docker image ships with these, but on bare metal you may need to install them.
- Learning Genie occasionally returns duplicate or overlapping Note pages; the CLI dedupes on note/media IDs but may still download revised content if metadata changes.
- During login, Playwright saves the storage state with captured headers (
X-UID, timezone). If those headers are missing (e.g., network issues), rerun the login command. - Cron jobs run within the container timezone (UTC). When specifying
CRON_EXPRESSION, convert to UTC if your host uses a different timezone.
MIT (see LICENSE if provided).