Source
This app’s code and revision history are public. Download a copy or explore the files below.
Public
The app’s code and revision history are public.
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 46e773698d4102c284d30369
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
src/lib/store.ts
// Recordings are kept in this browser's IndexedDB until the person deletes them. Nothing is sent anywhere.
// While a recording runs, its pieces are written here as the browser hands them over (every second or
// two), so a closed or crashed tab loses only the last few seconds: the next visit puts the pieces back
// together (see recover() in Recorder.tsx).
import type { Mode } from "./capture";
export type Recording = {
id: string;
name: string;
created: number;
mode: Mode;
/** The file's media type, such as video/mp4 or audio/webm. */
type: string;
/** Seconds; 0 when it could not be read. */
duration: number;
size: number;
/** True while the recording is being made, or when it was cut off before it was saved. */
partial: boolean;
};
type Store = "recordings" | "files" | "chunks";
let opening: Promise<IDBDatabase> | null = null;
function db(): Promise<IDBDatabase> {
opening ??= new Promise<IDBDatabase>((resolve, reject) => {
const req = indexedDB.open("record", 1);
req.onupgradeneeded = () => {
req.result.createObjectStore("recordings", { keyPath: "id" });
req.result.createObjectStore("files"); // the video or audio, by recording id
req.result.createObjectStore("chunks"); // pieces of a recording in progress, by [recording id, piece number]
};
req.onsuccess = () => resolve(req.result);
req.onerror = () => reject(req.error);
}).catch((err) => {
opening = null;
throw err;
});
return opening;
}
async function run<T = undefined>(stores: Store[], mode: IDBTransactionMode, op: (t: IDBTransaction) => IDBRequest<T> | void): Promise<T> {
const tx = (await db()).transaction(stores, mode);
const req = op(tx);
return new Promise<T>((resolve, reject) => {
tx.oncomplete = () => resolve(req ? req.result : (undefined as T));
tx.onerror = tx.onabort = () => reject(tx.error);
});
}
const pieces = (id: string) => IDBKeyRange.bound([id, 0], [id, Infinity]);
export const allRecordings = () => run<Recording[]>(["recordings"], "readonly", (t) => t.objectStore("recordings").getAll());
export const getFile = (id: string) => run<Blob | undefined>(["files"], "readonly", (t) => t.objectStore("files").get(id));
export const putRecording = (r: Recording) => run(["recordings"], "readwrite", (t) => void t.objectStore("recordings").put(r));
/** Saves a finished recording and its file, and drops the pieces it was made from. */
export const saveRecording = (r: Recording, file: Blob) =>
run(["recordings", "files", "chunks"], "readwrite", (t) => {
t.objectStore("recordings").put(r);
t.objectStore("files").put(file, r.id);
t.objectStore("chunks").delete(pieces(r.id));
});
export const deleteRecording = (id: string) =>
run(["recordings", "files", "chunks"], "readwrite", (t) => {
t.objectStore("recordings").delete(id);
t.objectStore("files").delete(id);
t.objectStore("chunks").delete(pieces(id));
});
export const putChunk = (id: string, n: number, data: Blob) => run(["chunks"], "readwrite", (t) => void t.objectStore("chunks").put(data, [id, n]));
export const chunksOf = (id: string) => run<Blob[]>(["chunks"], "readonly", (t) => t.objectStore("chunks").getAll(pieces(id)));
export const lockName = (id: string) => `record-${id}`;
/** Asks the browser not to clear recordings when the device runs low on space. */
export function keepStored() {
void navigator.storage?.persist?.().catch(() => false);
}
Change history
Point store links to openapps.free
Bind agent address in policy
Link OpenApps pages and add contribution strip
Bounded source sync
Bounded source sync
Bounded source sync