grain.social is a photo sharing platform built on atproto.
1export const tags = [
2 "DateTimeOriginal",
3 "ExposureTime",
4 "FNumber",
5 "Flash",
6 "FocalLengthIn35mmFormat",
7 "ISO",
8 "LensMake",
9 "LensModel",
10 "Make",
11 "Model",
12];
13
14export const SCALE_FACTOR = 1000000;
15
16export type Exif = Record<
17 string,
18 number | string | boolean | Array<number | string> | undefined | Date
19>;
20
21export function normalizeExif(
22 exif: Exif,
23 scale: number = SCALE_FACTOR,
24): Exif {
25 const normalized: Record<
26 string,
27 number | string | boolean | Array<number | string> | undefined
28 > = {};
29
30 for (const [key, value] of Object.entries(exif)) {
31 const camelKey = key[0].toLowerCase() + key.slice(1);
32
33 if (typeof value === "number") {
34 normalized[camelKey] = Math.round(value * scale);
35 } else if (Array.isArray(value)) {
36 normalized[camelKey] = value.map((v) =>
37 typeof v === "number" ? Math.round(v * scale) : v
38 );
39 } else if (value instanceof Date) {
40 normalized[camelKey] = value.toISOString();
41 } else if (typeof value === "string") {
42 normalized[camelKey] = value;
43 } else if (typeof value === "boolean") {
44 normalized[camelKey] = value;
45 } else if (value === undefined) {
46 normalized[camelKey] = undefined;
47 } else {
48 // fallback for unknown types
49 normalized[camelKey] = String(value);
50 }
51 }
52
53 return normalized;
54}