← Back

Case Study

Open Source · 2026

Open Source

Independent Product  ·  Design, Build & Release  ·  Jul 2026

Moodle
Downloader

A desktop app that keeps every lecture slide, tutorial sheet and assignment brief published on a university Moodle filed into weekly folders — on its own, all semester, for students who have never opened a terminal.

0

Passwords handled

3h

Auto-sync cadence

13

Folders per unit

PythonPlaywrightBeautifulSoupCustomTkinterPyInstaller
View on GitHub
02 · Problem

Course material arrives late, and never in one place.

Every unit publishes files across a dozen nested sections, and rarely all at once — slides land before class, worksheets during the week, solutions days later. Keeping a clean local copy means revisiting every unit, every week, and remembering what was already downloaded.

Existing browser extensions only shortened the clicking: the student still had to open each course page and decide what to grab. The real problem was not download speed — it was that the task depended on a person remembering to do it at all.

Root causes identified

Staggered publishing

Solutions and extra reading appear days after the lecture — one pass over a unit is never enough.

Deeply nested structure

Weeks are sub-sections of a parent section, each holding further sub-sections; nothing sits on the course home page.

Authentication friction

Any automation has to survive single sign-on and multi-factor authentication without ever holding a password.

Non-technical audience

The people with this problem are not going to install Python, edit a config file, or run a command.

03 · Process

As-Is → To-Be

As-Is · Manual

1

Remember to check Moodle

2

Log in, open each unit

3

Expand every week section

4

Compare against local folder

5

Download the new files

6

File them by hand

Weekly, per unit · Easy to forget · Late files missed

To-Be · Automated

1

App wakes on its own

2

Session restored silently

3

Every section crawled

4

Manifest diffed

5

Only new files fetched

6

Filed into the right week

Every 3 hours · Nothing to remember · Late files self-collect

Before

Manual, weekly

Task removed, not shortened

After

Unattended

04 · Product

Two screens, then never again

Distribution was treated as part of the design, not an afterthought. The whole product is a single 70 MB file: no runtime to install, no admin rights, no command line. Setup asks two questions and shows a tick-list of courses it discovered by itself; after that the window exists only to answer “is it working?”

05 · Discovery

Reading the system before automating it

The first crawler returned nothing. The course home page carries no file links at all: this Moodle runs a custom course format where weeks are sub-sections of a Learning section, and each week holds further sub-sections (Own-time, Real-time, Wrap-up) that actually contain the material.

Mapping it meant fetching real pages and reading the markup: section numbers are not sequential (week 1 is section 7, week 2 is section 11), so the crawler walks section links breadth-first, builds a parent tree, and lets a child inherit its parent's week.

Course discovery uses the same AJAX endpoint the Moodle dashboard calls, which also exposes which courses the student has starred — so the tool can offer a list instead of asking anyone to copy an ID.

Assumed — flat

Course page

↓ expected file links here ↓

Week 1
Week 2
Week 3

0 files found

Actual — nested

Course → “Learning” section

↓ week sub-sections (7, 11, 15 …) ↓

Week N

↓ inherits week from parent ↓

Own-time
Real-time
Wrap-up

Week 01 folder
06 · Architecture

How one sync runs

01

Wake

A logon trigger and a three-hour repeat start the sync silently; a lock file guarantees only one run at a time.

02

Session restore

Saved cookies are re-injected into a real browser context. If the session lapsed, renewal happens off-screen — invisible unless a human is genuinely required.

03

Crawl

Every section page is fetched breadth-first, following sub-section links until the whole tree is mapped.

04

Classify

Section titles are matched to weeks by configurable patterns; children inherit their parent's week, and assessment activities are routed separately.

05

Diff

Each candidate file is checked against a manifest of everything downloaded so far — and against the disk, so deleted files come back.

06

File

New files only are written into the matching week folder; replaced versions land beside the originals instead of overwriting them.

07 · Security

Never hold what you don't need to hold

Asking students for their university password would have made the tool trivial to build and impossible to trust. Instead the login runs where it belongs: a real browser window on the institution's own sign-on page, complete with multi-factor authentication. The application never sees a credential.

What it does keep is the resulting session, stored locally outside the project directory so it can never be committed or synced to the cloud. Because session cookies die with the browser process, they are exported and re-injected on the next run — which is what makes unattended syncing possible at all.

Nothing leaves the machine: there is no server, no telemetry and no account. Console output prints paths relative to the download folder, so a shared screenshot never leaks a username.

University SSO + MFA

Runs in a real browser — user types, app watches

↓ issues

Session cookies

Stored locally, outside the repo and cloud folders

↓ re-injected each run

Silent headless sync

No window, no password, no server

08 · Results

Shipped outcomes

0

Passwords stored or transmitted

Login stays inside university SSO

0

Duplicate downloads across runs

Manifest-tracked incremental sync

3h

Unattended sync interval

Plus one run at every logon

13

Folders maintained per unit

Week 00–12, Assignments, _Other

1

File to download, nothing to install

No Python, no admin rights

2

Documentation languages

English and Simplified Chinese

Deliverables

Course-structure reverse engineering

Breadth-first section crawler

SSO session persistence

Incremental manifest sync

Assignment & assessment capture

Desktop GUI (setup + dashboard)

Background auto-sync service

Bilingual docs + signed release

Source code and releases
09 · Learnings

Key takeaways

01

Remove the task, not the clicks

Faster downloading still leaves a chore on the student. Only running unattended actually solves the problem the chore represents — that was a product decision, not a technical one.

02

Read the system first

The initial crawler failed because I automated an assumed structure. Fetching the real markup and mapping how weeks nest was what made everything downstream possible.

03

Trust is a design constraint

Keeping credentials out of the tool made it harder to build and is the reason it can be handed to a stranger. Security decided the architecture, not the other way round.

04

Distribution is part of the product

For a non-technical audience, a single double-clickable file with a window is the product. A perfect script nobody can install helps nobody.