astro-image-exif-loader
Version:
Astro content collection loader for extracting EXIF data from images
149 lines (146 loc) • 8.07 kB
TypeScript
import * as astro from 'astro';
import { Tags } from 'exiftool-vendored';
import { Loader } from 'astro/loaders';
import { ZodTypeAny } from 'astro/zod';
type ExifToolTagKeys = keyof Tags;
type ExifPresets = "location" | "camera" | "exposure" | "datetime" | "technical" | "metadata" | "basic";
type ExifPresetTagMap = {
basic: Extract<ExifToolTagKeys, "FileSize" | "ImageWidth" | "ImageHeight">;
camera: Extract<ExifToolTagKeys, "Make" | "Model" | "LensModel" | "Lens" | "LensID" | "LensInfo" | "LensSerialNumber" | "SerialNumber" | "BodySerialNumber" | "CameraSerialNumber" | "LensMake" | "LensSpecification" | "MaxAperture" | "MinFocalLength" | "MaxFocalLength">;
exposure: Extract<ExifToolTagKeys, "ISO" | "FNumber" | "ExposureTime" | "ShutterSpeed" | "FocalLength" | "FocalLengthIn35mmFormat" | "Flash" | "WhiteBalance" | "ExposureMode" | "MeteringMode">;
datetime: Extract<ExifToolTagKeys, "DateTimeOriginal" | "CreateDate" | "DateTime">;
location: Extract<ExifToolTagKeys, "GPSLatitude" | "GPSLongitude" | "GPSAltitude" | "Country" | "State" | "City" | "Location" | "Sub-location" | "GPSAreaInformation" | "Country-PrimaryLocationCode" | "Province-State">;
technical: Extract<ExifToolTagKeys, "ColorSpace" | "Orientation" | "Software" | "SceneType" | "SceneCaptureType">;
metadata: Extract<ExifToolTagKeys, "Artist" | "Copyright" | "ImageDescription" | "Keywords" | "Title" | "Subject">;
};
interface ExifDirectoryOptions {
pattern: string | string[];
base?: string;
}
interface ExifLoaderOptions {
imagesDir?: ExifDirectoryOptions;
extensions?: string[];
presets?: ExifPresets[];
tags?: ExifToolTagKeys[];
excludeTags?: ExifToolTagKeys[];
extractAll?: boolean;
includeRawExif?: boolean;
}
interface ExifCollectionDefinition<S extends ZodTypeAny = ZodTypeAny> {
loader: Loader;
schema: S;
}
type PresetsToTagUnion<PSel extends readonly ExifPresets[] | undefined> = PSel extends readonly ExifPresets[] ? ExifPresetTagMap[PSel[number]] : never;
type CombinedSelectedTagElement<TSel extends readonly ExifToolTagKeys[] | undefined, PSel extends readonly ExifPresets[] | undefined> = (TSel extends readonly ExifToolTagKeys[] ? TSel[number] : never) | PresetsToTagUnion<PSel>;
type MaybeUndefinedSelected<TSel extends readonly ExifToolTagKeys[] | undefined, PSel extends readonly ExifPresets[] | undefined> = [TSel] extends [undefined] ? [PSel] extends [undefined] ? undefined : ReadonlyArray<CombinedSelectedTagElement<TSel, PSel>> : ReadonlyArray<CombinedSelectedTagElement<TSel, PSel>>;
interface ExifLocation {
coordinates: {
latitude: number;
longitude: number;
altitude: number | null;
} | null;
country: string | null;
state: string | null;
city: string | null;
location: string | null;
sublocation: string | null;
gpsAreaInformation: string | null;
countryCode: string | null;
provinceState: string | null;
}
interface ExifMetadata {
artist: string | null;
copyright: string | null;
description: string | null;
keywords: string | string[] | null;
rating: number | null;
title: string | null;
subject: string | string[] | null;
}
interface BaseExifData {
fileName: string;
mtime: string;
}
interface BasicExifData {
fileSize: number;
width: number | null;
height: number | null;
}
interface CameraExifData {
make: string | null;
model: string | null;
lens: string | null;
}
interface ExposureExifData {
iso: number | string | null;
aperture: number | string | null;
shutterSpeed: number | string | null;
focalLength: number | string | null;
focalLength35mm: number | string | null;
flash: number | string | null;
whiteBalance: number | string | null;
exposureMode: number | string | null;
meteringMode: number | string | null;
}
interface DateTimeExifData {
dateTime: string | null;
}
interface LocationExifData {
location: ExifLocation;
}
interface TechnicalExifData {
colorSpace: number | string | null;
orientation: number | string | null;
quality: number | string | null;
software: string | null;
sceneMode: number | string | null;
}
interface MetadataExifData {
metadata: ExifMetadata;
}
interface RawExifData {
rawExif: ExifMetadata;
}
interface CompleteExifData extends BaseExifData, BasicExifData, CameraExifData, ExposureExifData, DateTimeExifData, LocationExifData, TechnicalExifData, MetadataExifData {
}
type ExifData<T extends Record<string, boolean | undefined>> = BaseExifData & (T["basic"] extends false ? {} : BasicExifData) & (T["camera"] extends true ? CameraExifData : {}) & (T["exposure"] extends true ? ExposureExifData : {}) & (T["datetime"] extends true ? DateTimeExifData : {}) & (T["location"] extends true ? LocationExifData : {}) & (T["technical"] extends true ? TechnicalExifData : {}) & (T["metadata"] extends true ? MetadataExifData : {});
interface ImportedExifCollectionEntry<T = any> {
id: string;
collection: string;
data: T;
defaultImport: astro.ImageMetadata | null;
}
type SerializableValue<T> = T extends Date ? string : T extends {
toDate(): any;
} ? string : T extends (infer U)[] ? SerializableValue<U>[] : T extends {
num: number;
den: number;
} ? number : T extends string | number | boolean ? T : string | number | boolean | null;
type AllSerializedExifTags = {
[K in ExifToolTagKeys]?: SerializableValue<Tags[K]> | null | undefined;
};
interface BaseAlwaysFields {
fileName: string;
mtime: string;
FileSize?: number;
}
type UniversalExifData = BaseAlwaysFields & AllSerializedExifTags;
type RawExifShape = {
rawExif?: Record<string, string | number | boolean | (string | number | boolean)[] | null | undefined>;
};
type NarrowedExifData<T extends readonly ExifToolTagKeys[] | undefined, E extends readonly ExifToolTagKeys[] | undefined = undefined> = BaseAlwaysFields & (T extends readonly any[] ? E extends readonly ExifToolTagKeys[] ? Omit<Pick<AllSerializedExifTags, T[number]>, E[number]> : Pick<AllSerializedExifTags, T[number]> : T extends undefined ? {} : E extends readonly ExifToolTagKeys[] ? Omit<AllSerializedExifTags, E[number]> : AllSerializedExifTags) & RawExifShape;
type ExtractAllExifData<E extends readonly ExifToolTagKeys[] | undefined = undefined> = BaseAlwaysFields & (E extends readonly ExifToolTagKeys[] ? Omit<AllSerializedExifTags, E[number]> : AllSerializedExifTags) & RawExifShape;
type ExtractAllExifDataNoRaw<E extends readonly ExifToolTagKeys[] | undefined = undefined> = BaseAlwaysFields & (E extends readonly ExifToolTagKeys[] ? Omit<AllSerializedExifTags, E[number]> : AllSerializedExifTags);
type NarrowedExifDataNoRaw<T extends readonly ExifToolTagKeys[] | undefined, E extends readonly ExifToolTagKeys[] | undefined = undefined> = BaseAlwaysFields & (T extends readonly any[] ? E extends readonly ExifToolTagKeys[] ? Omit<Pick<AllSerializedExifTags, T[number]>, E[number]> : Pick<AllSerializedExifTags, T[number]> : T extends undefined ? {} : E extends readonly ExifToolTagKeys[] ? Omit<AllSerializedExifTags, E[number]> : AllSerializedExifTags);
declare function exifTags<T extends readonly ExifToolTagKeys[]>(...tags: T): T;
type InferExifData<T> = T extends {
schema: infer S;
} ? S extends {
_type: infer U;
} ? U : S extends {
shape: infer R;
} ? {
[K in keyof R]: any;
} : unknown : unknown;
export { exifTags };
export type { AllSerializedExifTags, BaseAlwaysFields, BaseExifData, BasicExifData, CameraExifData, CombinedSelectedTagElement, CompleteExifData, DateTimeExifData, ExifCollectionDefinition, ExifData, ExifDirectoryOptions, ExifLoaderOptions, ExifLocation, ExifMetadata, ExifPresetTagMap, ExifPresets, ExifToolTagKeys, ExposureExifData, ExtractAllExifData, ExtractAllExifDataNoRaw, ImportedExifCollectionEntry, InferExifData, LocationExifData, MaybeUndefinedSelected, MetadataExifData, NarrowedExifData, NarrowedExifDataNoRaw, PresetsToTagUnion, RawExifData, RawExifShape, SerializableValue, TechnicalExifData, UniversalExifData };