The Compleat Barks Disney Reader is a desktop application designed for browsing, searching, and reading the complete collection of Carl Barks' Disney comics, specifically tailored for the Fantagraphics book series.
Built with the Kivy framework in Python, it provides a rich, cross-platform user experience with a focus on intuitive navigation and a visually engaging interface.
Just want the app? Download the standalone executable for Windows, macOS, or Linux from the project website — no Python setup needed. The rest of this README is for running or building from source.
- Comprehensive Browsing: Navigate the entire collection in multiple ways:
- Chronological: View stories in order based on their original submission date.
- Series: Browse by comic book series (e.g., Walt Disney's Comics and Stories, Donald Duck, Uncle Scrooge).
- Categories: Explore stories grouped by tags, such as characters, themes, or story types.
- Search: Quickly find titles by name or stories by specific tags using the integrated search boxes.
- Rich User Interface: A dynamic 'TreeView' provides easy navigation, while the main view displays context-aware background art and information related to the selected comic or category.
- Full Keyboard & Remote Navigation: Browse, open, and read comics using just the arrow keys, Enter, and Escape — designed to work from the couch with a TV remote as well as with a mouse or touchscreen.
- Integrated Comic Reader: A fullscreen, touch/click-friendly reader with intuitive page navigation (click left/right side of the screen) and a "go to page" dropdown for jumping directly to a specific page.
- No Censorship: Every attempt has been made to remove Disney and Fantagraphics censorship. This has been done using an override mechanism where censored pages in the original are overridden by fixed pages in the reader.
- Smart Loading: Asynchronously loads comic images in the background for a smooth, non-blocking user experience.
- Customizable Viewing: Supports optional override images for controversially restored or fixed pages, which can be toggled via a checkbox for specific titles.
- Persistent State: Remembers the last-read page for each comic and the last selected node on startup, allowing you to pick up right where you left off.
| Main Window | Reading a Story |
|---|---|
![]() |
![]() |
| Selecting a Title | Settings |
![]() |
![]() |
More screenshots (including the full index and speech bubble index) are on the project website.
- Python: 3.13 or newer.
- Fantagraphics Comic Archives: You must have access to the digital versions of the Fantagraphics Carl Barks
Library, typically as
.zipor.cbzfiles. - Python Dependencies: The required Python packages can be installed via
uv.
Follow these steps to get the application running on your local machine. To develop it (the tests, the gates, the standalone build) or run the overnight suite, set the machine up with docs/setup.md instead: system packages, tools, the sibling repositories, git hooks, secrets and data.
1. Clone the required repository
git clone https://github.com/glk1001/barks-compleat-reader.git
cd barks-compleat-reader2. Install Dependencies
You need to use uv for package management and a Python virtual environment.
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install the required packages
uv sync
And install just to conveniently run commands.
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin3. Initial Run and Configuration
The first time you run the application, it will create a configuration file. You will then need to configure the path to your comic archives.
just reader
- When the application opens, click the Settings icon in the action bar.
- In the settings panel, locate the setting for the "Fantagraphics Volumes Directory".
- Set this to the absolute path of the folder containing your Fantagraphics comic book archive files (the
.zipor.cbzfiles). - Close the settings panel and restart the application.
The application will now load the titles from your specified directory.
- Run the application:
just reader
- Navigation: Use the
TreeViewon the left to browse the collection. - View Info: Click on a title in the tree to see its related comic art, publication details, and other information in the main panel at the bottom.
- Read a Comic: In the bottom right corner of the main panel, click the highlighted comic inset image to open the comic book reader.
-
Unit tests (about 3,700, mocked Kivy, run in CI on Linux, macOS and Windows):
uv run pytest -
GUI path tests (98, Linux only): boot the real app from a scratch profile on a nested X display and drive it with the keyboard or by tapping, waiting only on lines the app logs. They need the reader's data directories and a few X tools;
bash scripts/gui-probe.sh doctorsays what is missing. Each passing test is also held to a set of teardown checks: the window is the size it booted at, the log holds no error or stray key, what a read persisted matches what it logged, the final frame is drawn, and every duration the app logged (tree build, image loads, a comic's pages, the volumes, the index) is within a loose budget. Runbash scripts/run_gui_tests.sh --calibrateonce on a new machine: it records the suite's durations and writes this machine's budgets to.benchmarks/gui-timings.json(not committed); until then the committed budgets, from the desktop, apply. The check is skipped with a warning when the load exceeds what the cores and the run's own workers account for, and turned off byBARKS_GUI_NO_BUDGETS=1. Three tests look for leaks: each goes round the same trip (a wiki page, a comic, the main index) six times and asks the app, after a full garbage collection, how many widgets and textures it holds (barks_reader.core.memory_census); the count may not climb after the first two rounds.bash scripts/run_gui_tests.sh # visible, on a Xephyr window on the second monitor bash scripts/run_gui_tests.sh --headless # on Xvfb, four workers, about three minutesOptions (each may be combined; anything else goes to
pytest, e.g.-k searchor-x):Option What it does --headlessRun on Xvfb with no window; no desktop session needed --workers NParallel workers, one nested display each (headless default 4) --screen WxHNested screen size, e.g. --screen 1920x1080(default900x1300)--prebuilt 0|1Read comics from the Fantagraphics volumes or the prebuilt archives --png-images 0|1View images from the PNG panels or the JPG panels zip --ini key=valueAny other Barks Reader setting for the run (repeatable) --app PATHRun the suite against a built executable instead of the workspace --soakRun only the random walk (200 keys from four screens; BARKS_GUI_WALK_STEPS,BARKS_GUI_WALK_SEED)--calibrateRecord the durations and write this machine's timing budgets --touchTap by real touch as well as by click, on a virtual touchscreen (one worker; needs the udev rule below) --keep-logsKeep every passing test's artifacts too, as a failure's --quietPrint only failures and the summary --progressAs --quiet, plus a line per test as it finishes; the full output goes topytest.logA failure leaves a screenshot, the app log, the keys sent and the scratch profile under
build/gui-tests/<run>/. In a visible run the test window takes the keyboard when it appears; click back to your own window and do not type into it. The tests may press only the remote's six keys (Escape, Enter and the arrows); a check in the lint gates,scripts/check_gui_keys.py, reads them and fails on any other key unless the call carries# desktop key: <why>. -
Tap tests (11, in
test_taps.py, part of the GUI suite): drive the reader as a touchscreen laptop's user does. They tap the page margins, the fullscreen top margin, tree nodes, menu buttons and popups, the main and speech-bubble index items, and the search box. A test never taps a pixel it worked out itself. It asks the app where its tappable widgets are and taps the one it names, so a layout or theme change moves the tap with the widget. By default a tap is a click, which is what a touch reaches the app as on Linux, so the tap tests run headless and in parallel with the rest.--touchmakes each tap a real touch too, on a virtual touchscreen created before each boot (scripts/gui_touch.py). With it, the tap tests turn the virtual keyboard setting on, so the app reads the touchscreen itself: the search box must then show the virtual keyboard, and a margin tap must still turn exactly one page. Touch mode needs a udev rule, installed once with sudo. It lets theinputgroup create the device, and it hides the device from the desktop so no tap lands on your real screen:sudo cp scripts/udev/70-barks-gui-touch.rules /etc/udev/rules.d/ sudo udevadm control --reload sudo udevadm trigger --action=change --sysname-match=uinputYou must be in the
inputgroup (sudo usermod -aG input $USER, then log in again).BARKS_PROBE_TOUCH=1 bash scripts/gui-probe.sh doctorchecks all three. Then:bash scripts/run_gui_tests.sh --headless --touch -k test_taps # about three minutesSelect the tap tests with
-k: a test file given as a path is added to the suite, so it runs the whole suite on one worker. Touch mode is Linux only for now; on Windows a tap is a click. The design is indocs/plans/touch-gui-tests.md. -
The settings matrix runs the GUI suite once per setting the default run never sees (the other two colour themes, double-page mode, the virtual keyboard, the title-info switches off, the censorship fixes on), headless, every variant even after one fails, with a pass/fail table at the end. About twenty minutes; for a nightly or a release.
bash scripts/run_gui_matrix.sh # every variant bash scripts/run_gui_matrix.sh --list # the variants and their settings bash scripts/run_gui_matrix.sh --only double-page # one of them (comma-separated for more) bash scripts/run_gui_matrix.sh --app ./barks-reader-linux # every variant against a build -
The GUI overnight run runs every GUI test there is, every way it can run: the suite as configured, then on each panels source and each comic source, the settings matrix, a landscape 1920x1080 screen, the tap tests by real touch (skipped when the udev rule is missing), and the random-walk soak from three seeds. With
--app PATHit also runs the suite, the settings matrix and one soak seed against a built executable: a setting that loads files only it needs (the virtual keyboard's layouts) and a walk's deep paths (lazy imports) test the packaging, which the workspace run cannot. It keeps going after a failure, prints a pass/fail table with timings, and exits non-zero if anything failed. It holds off sleep while it runs and warns when on battery. Each stage shows its number and start time, then a line per test as it finishes, andsummary.txtis rewritten after every stage. Well over an hour. The whole-repo overnight run below runs it as one of its stages.bash scripts/run_gui_overnight.sh # every stage bash scripts/run_gui_overnight.sh --list # the stages bash scripts/run_gui_overnight.sh --only touch,soak # some of them (or --skip) bash scripts/run_gui_overnight.sh --app ./barks-reader # add a built executableEach stage's output, and the summary, go to
build/gui-tests/overnight-<stamp>/. -
Stopping a run. Ctrl-C, closing the terminal, or
killof the runner (the overnight runs print their pid) stopsrun_gui_tests.sh, the matrix, either overnight run, and everything they started: pytest, and each display's app, X server and touchscreen, which the probe runs in sessions of their own that no signal reaches. Onlykill -9leaves anything behind, and the next run clears that before it starts. To clear it by hand:bash scripts/gui-probe.sh cleanup # what dead runs left; a live run is left alone bash scripts/gui-probe.sh cleanup --all # every display, including one started by hand -
The GUI suite on Windows: the same tests through
scripts/gui_probe.py, which sends real keys withSendInputto the reader on the real desktop. One visible worker, about 15 minutes; leave the machine alone while it runs. Needs.env.runtimepointing at a reader profile and data pack (uv run python scripts/gui_probe.py doctorchecks).uv run python scripts/run_gui_tests.py # the suite (-k, -x: pytest args) uv run python scripts/run_gui_tests.py --angle # on a VirtualBox VM: draw through Direct3D uv run python scripts/run_gui_tests.py --calibrate # record this machine's timing budgetspytest's whole output goes to
build/gui-tests/<run>/pytest.logas it runs. For a VirtualBox VM, turn 3D acceleration off and discard off on its disk; why is indocs/plans/cross-platform-gui-tests.md. To run the reader itself on such a VM (a workspace run or the Windows exe, which bundles Kivy's ANGLE DLLs for this), set the user environment variableKIVY_GL_BACKEND=angle_sdl2: with 3D acceleration off there is no OpenGL 2.0, and ANGLE draws through Direct3D instead. Real Windows machines need nothing set. -
The GUI suite on macOS: the same runner and probe, with Quartz events for input: keys go to the reader's process alone, clicks land on its window. One visible worker, about 45 minutes on a 2-core VirtualBox guest; leave the desktop alone while it runs. The terminal it runs from needs Accessibility and Screen Recording (System Preferences, Security & Privacy, Privacy), and the reader runs through
scripts/macos/with-soft-gl.sh, so a guest with no GPU draws on Apple's software OpenGL.uv run python scripts/gui_probe.py doctorchecks the machine; the whole setup isdocs/setup.md, "A macOS VirtualBox guest".uv run python scripts/run_gui_tests.py # the suite (-k, -x: pytest args) uv run python scripts/run_gui_tests.py --calibrate # once: this machine's timing budgets -
A built executable:
bash scripts/smoke-test-build.sh ./barks-reader-linux(or the Windows.exe, or the macOS.zip) launches a build in an empty directory as far as its first-run installer's "data pack missing" message, which proves the packaged program runs at all; every CI build leg does the same to what it built. -
The data pack:
uv run scripts/validate-barks-reader-files.pychecks what the reader consumes, title by title: the config, the Reader Files, the Fantagraphics volumes, the prebuilt comics in the configured directory, every title's panel files, the layout the reader builds for each comic (with the panel-segments JSONs it reads, present and no older than their pages), and the wiki: every story page joins its title and back again. Needs the data directories and.env.runtime. Run it nightly beside the settings matrix.uv run scripts/validate-barks-reader-files.py # everything (about a minute and a half) uv run scripts/validate-barks-reader-files.py --titles-only # only the per-title phases uv run scripts/validate-barks-reader-files.py --title "Lost in the Andes!" # or --volume 1-5 uv run scripts/validate-barks-reader-files.py --full-load-check # also decode every source page uv run scripts/validate-barks-reader-files.py --strict-wiki # fail on stories with no page yet uv run scripts/validate-barks-reader-files.py --wiki-bundle PATH # check another wiki bundleA story with no wiki page yet is counted and warned about, not failed, unless
--strict-wiki. So is a title listed inscripts/known-missing-insets.txt, whose inset is not made yet; a listed title that has its inset fails, so the list is pruned as they land. The wiki checked is the one the reader's settings select (the live bundle whenuse_live_wiki_bundleis on, else the copy in Reader Files). The build tree under~/Books/Carl Barksis not checked here: that is the build gate's job, inbarks-comic-building. Title search needs no data, so it is a unit test that CI runs (src/barks-fantagraphics/tests/test_title_search.py). -
Everything else (lint, type checks, spelling, benchmarks):
bash scripts/full-lint.sh, or with--with-gui-testto include the GUI suite. -
The whole-repo overnight run runs everything worth a night that the gates (pre-commit, pre-push, CI) leave out, because it is slow or needs local data: the checks on the real data pack and the sibling repos, the full lint and test suite from a synced venv, the tests in random order and against upgraded dependencies, a Nuitka build and its smoke test, the GUI overnight run against that build, GUI timing drift, and a slice of mutation testing. The data checks come first: when the data is broken, what fails later is only a symptom. Like the GUI overnight run, it runs every stage even after one fails, holds off sleep, warns when on battery, and exits non-zero if any stage failed (a warning does not count). Linux only.
bash scripts/run_overnight.sh # every stage bash scripts/run_overnight.sh --list # the stages bash scripts/run_overnight.sh --only validate,pytest # some of them (or --skip) bash scripts/run_overnight.sh --app ./barks-reader-linux # use this build, skip buildingStage What it runs validatevalidate-barks-reader-files.pyon the real data pack (above), with--full-load-check --strict-wikipanel-sourcescheck-barks-panel-sources.py: every PNG panel belongs to a titlebuild-checkThe ../barks-comic-buildingintegrity checker on the whole build tree: every check, with--check-panel-segment-image-sizewiki-ordercheck_wiki_story_order.pyon the sibling barks-wiki bundle; warns onlywiki-copyThe wiki copy shipped in Reader Files, which validatedoes not see while the settings use the live bundle: a broken join fails; a copy that differs from a fresh export of the live bundle (barks-wiki'sexport_reader_wiki.py, into a temp dir) warnslintfull-lint.sh, with the benchmarks against their baselineaudituv audit, known CVEs in the locked dependencies; warns onlypytestuv sync --locked, then the whole suite with coverage, kept for thecoveragestagerandom-orderThe suite shuffled by pytest-randomly; the log's first line gives the seed and the command that replays itdep-driftThe suite against every dependency upgraded as far as pyproject.tomlallows, in a venv of its own;uv.lockis put backsiblingsThe sibling repos that use barks-fantagraphicsandcomic-utils, against this checkout: their owntyandpyrefly, then their tests where they have anybuildbuild.sh, the Nuitka executable; skipped with--appsmokesmoke-test-build.sh --press-escapeon that build (or on--app PATH), as CI doesguirun_gui_overnight.sh, the built app's stages too (the suite, the matrix, one soak seed), with a longer soak on new seeds each night; the workspace app's coverage is measured (BARKS_PROBE_COVERAGE, about 10% slower)gui-timingsThe GUI suite once more, then each timing's third-slowest test against .benchmarks/gui-timings.json(one slow sample is not a drift); warns on drift well before a budget would failcoverageThe unit suite's and the GUI tests' coverage, each and combined, and an HTML report of what nothing tests. Fails when the combined total falls more than a point below its best ( coverage_floor.py,.benchmarks/coverage.json); judged only whenpytestandguiboth passedgraphifygraphify update ., the knowledge graph; skipped if graphify is not installedmutationmutmut.shon one seventh ofcore/, a different slice each weekday; warns when a module has more survivors than the last time it was mutatedBARKS_OVERNIGHT_SOAK_STEPS(default 1000) andBARKS_OVERNIGHT_SOAK_SEEDS(default three seeds from the day of the year) set the soak;BARKS_OVERNIGHT_MUTATION_DAY(1-7) picks the mutation slice. Each stage's output goes tobuild/overnight/<stamp>/<stage>.log, andsummary.txtthere, rewritten after every stage, holds each one's result (passed, FAILED, WARNED, skipped or stopped) and time.On another machine.
copy-to-overnight-host.sh HOSTsets one up over ssh: it clones the four repos beside each other, writes.env.runtimethere with only itsBARKS_lines, and copies (rsync) the data the run reads (the prebuilt comics, the PNG panels, the Fantagraphics volumes, the reader's files and the profile, about 36 GB) to the same paths under that machine's home, then runscheck-overnight-host.shthere, which says what is still missing. The comic build tree (about 330 GB) is left behind: run there with--skip build-check. It installs no system packages or tools: those, the polkit rule that keeps the machine awake without a password over ssh, and the rest of a clean machine's setup are in docs/setup.md. Once there, record a benchmark baseline (the lint stage compares against it) and calibrate the GUI timings; over ssh there is no display, so both run on Xvfb.bash scripts/copy-to-overnight-host.sh --dry-run user@host # what it would do bash scripts/copy-to-overnight-host.sh user@host # do it (reruns copy only changes) bash scripts/check-overnight-host.sh # on the other machine xvfb-run -a bash scripts/record_benchmark_baseline.sh # once, there bash scripts/run_gui_tests.sh --headless --calibrate # once, there bash scripts/run_overnight.sh --skip build-check # thereStarted over ssh without the polkit rule in docs/setup.md, the run asks for a password ("Authentication is required for an application to inhibit system sleep"); the stages go on meanwhile, but the machine may sleep.
The GUI suite's design, its log-marker contract and its history are in
docs/plans/gui-test-suite.md; the runner's options are also described at the top of
scripts/run_gui_tests.sh.
- Install the dependencies (Nuitka is a dev dependency):
uv sync - Run the build command:
bash scripts/build.sh - This will create a standalone executable in the repo root: a single-file binary on Linux
('barks-reader-linux') and Windows ('barks-reader-win.exe'), and a zipped
.appbundle on macOS ('barks-reader-macos.zip' or 'barks-reader-macos-x64.zip', depending on architecture).
Use 'barks-reader-win.exe'.
- Windows will probably block the download. The app is unsigned and every release is a
brand-new file with no download reputation, so this is a false positive — but it can
stop you at two separate points, and they need different fixes:
- While downloading — either the browser refuses the file as "unsafe", or Defender
reports a virus (typically
Program:Win32/Wacapew.C!ml). If it was Defender, the browser's "Keep anyway" appears to do nothing, because the file is already quarantined and there is nothing left to keep. Clear it in Windows Security → Virus & threat protection → Protection history → find the entry → Actions → Allow, then download again. To skip the browser's own check entirely:curl.exe -L -o barks-reader-win.exe <link> - On first run — SmartScreen shows "Windows protected your PC". Click "More info", then "Run anyway".
- While downloading — either the browser refuses the file as "unsafe", or Defender
reports a virus (typically
- Put the
.exein its own folder that you can write to (e.g.C:\Users\<you>\BarksReader), notC:\Program Files. The app writes itsconfig/directory, install log and unpacked data beside the executable, so it needs write access there. - Place
barks-reader-data-1.barkspackandbarks-reader-data-2.barkspackin that same folder, next to the.exe. They are zip archives under another name; don't unzip them — the app does that itself. - Launch the app. The first run unpacks the data packs, writes the config, and shows a success popup; subsequent launches go straight to the reader.
Use 'barks-reader-macos.zip' on Apple Silicon Macs and 'barks-reader-macos-x64.zip' on Intel Macs.
- Get the app bundle
barks-reader-macos.app. Safari unzips downloads automatically by default, so it is probably already in your Downloads folder beside the zip. If you only have the zip (other browsers, or Safari with "Open safe files" turned off), double-click it in Finder. (Note: a zip downloaded from a GitHub Actions run's artifacts page is wrapped in an extra zip layer, so you may need to unzip twice; a file attached to a GitHub Release comes as-is.) - Put the
.appin its own folder (e.g.~/BarksReader/), not straight into/Applications. The first-run installer looks for the data packs beside the bundle, and also writes itsconfig/directory, install log, and data there. - Place
barks-reader-data-1.barkspackandbarks-reader-data-2.barkspackin that same folder, next to the.app. Don't unzip them. (The.barkspackextension exists so Safari's "Open safe files" setting leaves them alone; a.zipwould be auto-expanded into a folder the installer can't use.) - Clear Gatekeeper — the app is unsigned, so a downloaded copy is quarantined and macOS will
refuse to open it. Either:
- try to open it once, then go to System Settings → Privacy & Security, scroll down and click "Open Anyway" (required on macOS 15+, where right-click → Open no longer works for unsigned apps); or
- remove the quarantine flag in Terminal:
xattr -dr com.apple.quarantine ~/BarksReader/barks-reader-macos.app
- Launch the app. The first run unpacks the data packs, writes the config, and shows a
success popup; subsequent launches go straight to the reader. If the install fails, the
log is written beside the
.appasbarks-reader-installer-<timestamp>.log.
Releases are assembled by CI — don't upload executables by hand.
- Update
FALLBACK_TAGinwebsite/app.htmlto the upcoming tag (it's the no-API fallback for the website's download links) and commit, so the tagged commit is self-consistent. - Tag that commit and push:
git tag v1.2.3-alpha.2 # or plain v1.2.3 for a full release git push origin main v1.2.3-alpha.2 - The
Build Verificationworkflow builds all four executables, then itsAssemble draft releasejob creates a draft release for the tag: pre-release flag set automatically (any tag with a-suffix, e.g.-alpha.2, is a pre-release), release notes auto-generated from commits, and links to the data packs included. Watch it withgh run watchor on the Actions tab. - Review the draft on 'Releases' on GitHub:
check all four executables are attached and "Set as a pre-release" is ticked as expected,
edit the notes if needed, and click "Publish release". Nothing is public until then.
(If something is wrong, delete the draft —
gh release delete <tag> --cleanup-tagalso removes the tag — fix, and re-tag.) - Verify the published result: the release page shows its "Pre-release" badge, and on https://glk1001.github.io/barks-compleat-reader/website/app.html the masthead badge shows the new tag and the executable buttons download from it — the website tracks the newest app release via the GitHub API (pre-releases included). Old releases can stay as history.
The ~1GB data packs (barks-reader-data-1.barkspack, barks-reader-data-2.barkspack) live on their own
dedicated data-vN release, not on app releases, because they rarely change — the current
tag is whatever DATA_TAG in website/app.html names. When they do change, rebuild them
(bash scripts/build-data-zips.sh — no exe build needed) and run:
bash scripts/upload-data-zips.sh
The script (options: --zips-dir <dir>, --dry-run, --yes):
- Auto-numbers the next tag from the highest existing
data-vNrelease. - Warns if both zips are byte-for-byte the same size as the previous data release's — a sign the data didn't actually change.
- Uploads both zips to a draft release first, so a failed or partial ~2GB upload is never publicly visible, and verifies the uploaded byte sizes match the local files.
- Publishes the release, marked pre-release so it can never become GitHub's "Latest" (the website's version resolver depends on that).
- Points
DATA_TAGinwebsite/app.htmland the data-pack links in.github/workflows/build.ymlat the new tag, and prints the exact commit and push commands. Push them straight away: until that lands, the live website's data-pack buttons name the new pack files under the old tag and 404.
It refuses to start with uncommitted changes to tracked files, so the tag bump is always its own commit.
Publishing a data release creates its data-vN tag, but that push doesn't waste CI: the
build workflow skips app builds for data-* tags (they'd build four executables and attach
nothing).
The website's two videos (website/demo.mp4 ~3MB, website/walkthrough.mp4 ~29MB) are not
in the repository either, but for the opposite reason to the data packs: both are re-recorded
whenever the app changes, and none of that history is ever wanted back. The hero is the
smaller file but it autoplays on the landing tab, which makes it the heaviest thing most
visitors ever fetch — so serving it from the release keeps that off Pages as well. They live
on a website-assets release under a fixed tag, re-uploaded in place, so the URLs on the
page never change and a re-recording needs no commit at all:
bash scripts/upload-website-videos.sh
The script (options: --only <file>, --dry-run, --yes) creates the release on first use,
refuses to upload if VIDEO_TAG in website/app.html names a different tag or if a chapter
manifest has drifted from its video's length (which would put every chapter button out of
step), and verifies the uploaded byte sizes afterwards.
The posters and chapter manifests do stay in git — a few KB each, and the page needs the
posters immediately for layout. A release asset plays inline despite GitHub serving it as
application/octet-stream; Content-Disposition: attachment: that header governs navigations
and downloads, not media subresources, and Range requests are honoured so chapter seeking
works.
Licensed under the Apache License, Version 2.0.
The licence covers this project's own source code. The Carl Barks comics themselves — the artwork, characters and story text — are the copyright of their respective owners and are not covered by it; see NOTICE. Intended for personal, non-commercial use with a legally owned collection.



