Files.
Pictures, videos, fonts and documents that change without a new release live in the project's file library, filled through the HTTP API and shown in the portal under Data › Files. Every device syncs it and serves it to the app under /storage/.
Using a file
<img src="/storage/menu/tea.webp" alt="Tea">
<video src="/storage/adverts/autumn.mp4" autoplay muted loop></video>
- A file is served at
/storage/+ its path in the library, exactly as the portal shows it. - A record usually carries the path: store
"image": "menu/tea.webp"in the record and build'/storage/' + item.imagein the app. - The files are read-only content: the player checks them, refetches anything changed and removes what the library deleted. Do not write into
storage/on your desk; it is the player's. - A stopped download resumes where it was; files are content-addressed, so nothing is fetched twice.
- File sync runs on its own, so datasets and the outbox never wait behind a large upload.
A file that has not arrived yet
The app can start before every file is down. A picture whose file has not landed answers 404 until it does. The 'files' event says which paths arrived, so you can redraw exactly those:
Arda.on('files', function (p) {
// p = {done, total, fetched, fetchedBytes, totalBytes, failed, complete, landed}
if (p.totalBytes) gauge(Math.round(100 * p.fetchedBytes / p.totalBytes));
(p.landed || []).forEach(function (path) {
// path is under /storage/, e.g. "menu/tea.webp"
var imgs = document.querySelectorAll('img[data-file="' + path + '"]');
for (var i = 0; i < imgs.length; i++) imgs[i].src = '/storage/' + path + '?v=' + Date.now();
});
if (p.complete) console.log('files up to date:', p.done, 'of', p.total, p.failed ? '(' + p.failed + ' failed)' : '');
});
- Progress events come about once a second during a transfer;
complete: truecomes exactly once at the end of every sweep, even one that fetched nothing. landedlists the paths that arrived since the previous event.- From v1.2.2 a sweep that left files behind (
failed) tries them again by itself within seconds; before, they waited for the next connection. Arda.db.status().filesanswers the same numbers at any time (nullbefore the first sweep).
Files the app cannot start without
{
"datasets": [ { "key": "menu", "needForBoot": true } ],
"files": { "needForBoot": ["logos", "menu"] }
}
"*"means every file, a word means one folder, a list means those folders.- Those folders are fetched first. The app starts once the boot datasets are current and those files are present; everything else keeps arriving behind it.
- The device's boot screen shows one bar for both ("downloading datasets n / N · downloading files x MB of y MB"), and the portal marks the folders as needed at boot.
Arda.db.status().filescarries the boot half too:bootDone,bootTotal,bootBytes,bootTotalBytes,bootComplete.
On your desk
A local run has no file library: /storage/ serves whatever is in the folder's storage/ directory, which you may fill by hand to try layouts. With "device": true the folder syncs the project's files like a device.
Checked: the event payload, including landed, is from the player's internal/client/files.go; the manifest passes ArdaForm's pack-time parser. A local run serving a hand-placed file from storage/ (200) and a missing one as 404 was observed. Not verified against a device here: the redraw example on a slow connection.