UNPKG

exiftool-vendored

Version:
1,039 lines 73.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const source_map_support_1 = __importDefault(require("source-map-support")); source_map_support_1.default.install(); const batch_cluster_1 = require("batch-cluster"); const globule_1 = __importDefault(require("globule")); const node_fs_1 = __importDefault(require("node:fs")); const node_os_1 = __importDefault(require("node:os")); const node_path_1 = __importDefault(require("node:path")); const node_process_1 = __importDefault(require("node:process")); const progress_1 = __importDefault(require("progress")); const Array_1 = require("../Array"); const ExifTool_1 = require("../ExifTool"); const ExifToolVendoredTags_1 = require("../ExifToolVendoredTags"); const GeolocationTags_1 = require("../GeolocationTags"); const ICCProfileTags_1 = require("../ICCProfileTags"); const ImageDataHashTag_1 = require("../ImageDataHashTag"); const IPTCApplicationRecordTags_1 = require("../IPTCApplicationRecordTags"); const Maybe_1 = require("../Maybe"); const MWGTags_1 = require("../MWGTags"); const Number_1 = require("../Number"); const ReadTask_1 = require("../ReadTask"); const String_1 = require("../String"); const Times_1 = require("../Times"); /** * Unfortunately, TypeScript has a limit on the complexity of types it can * represent. * * If we build a union type of all the tags ExifTool knows about (which is north * of 15,000 tags last time we checked), we exceed that limit and get `error * TS2590: Expression produces a union type that is too complex to represent`. * * So, we have to hack around this by only including the N "most popular" and * "most important" tags. * * Note that this limit is much less than that threshold (which was about 3,100 * in 2023), but remember to account for the 8+ static interfaces we're also * including in the final Tags union. */ const MAX_TAGS = 2700; // Static interfaces that Tags extends - these need special handling to avoid // name collisions and to ensure their tag names appear in TagMetadata.json const StaticInterfaceMetadata = [ { name: "ExifToolVendoredTags", group: "Library", // Library-added tags tagNames: ExifToolVendoredTags_1.ExifToolVendoredTagNames, }, { name: "GeolocationTags", group: "ExifTool", // ExifTool's geolocation feature tagNames: GeolocationTags_1.GeolocationTagNames, }, { name: "ImageDataHashTag", group: "Composite", // Computed by ExifTool tagNames: ImageDataHashTag_1.ImageDataHashTagNames, }, { name: "ICCProfileTags", group: "ICC_Profile", tagNames: ICCProfileTags_1.ICCProfileTagNames, }, { name: "IPTCApplicationRecordTags", group: "IPTC", tagNames: IPTCApplicationRecordTags_1.IPTCApplicationRecordTagNames, }, { name: "MWGCollectionsTags", group: "XMP", // MWG (Metadata Working Group) uses XMP tagNames: MWGTags_1.MWGCollectionsTagNames, }, { name: "MWGKeywordTags", group: "XMP", // MWG (Metadata Working Group) uses XMP tagNames: MWGTags_1.MWGKeywordTagNames, }, ]; // These tags are important enough that we want to ensure they're always in the // final Tags interface. // // If you're relying on a tag that gets inadvertently dropped in some release, // consider opening a PR to restore it by adding it here: const RequiredTags = { ExifToolVersion: { t: "string", grp: "ExifTool" }, // < ExifTool stores the version as a float (!!) which causes 12.30 to become 12.3. AccelerometerX: { t: "number", grp: "MakerNotes", doc: "Accelerometer X-axis reading. Panasonic: positive is acceleration to the left.\nAlso found in DJI and Lytro cameras. Used for motion/orientation detection.", }, AccelerometerY: { t: "number", grp: "MakerNotes", doc: "Accelerometer Y-axis reading. Panasonic: positive is acceleration backwards.\nAlso found in DJI and Lytro cameras. Used for motion/orientation detection.", }, AccelerometerZ: { t: "number", grp: "MakerNotes", doc: "Accelerometer Z-axis reading. Panasonic: positive is acceleration upward.\nAlso found in DJI and Lytro cameras. Used for motion/orientation detection.", }, Album: { t: "string", grp: "XMP", value: "Twilight Dreams" }, Aperture: { t: "number", grp: "Composite", doc: "Calculated aperture value derived from FNumber or ApertureValue.\nRead-only composite tag. To write, modify FNumber or ApertureValue instead.", see: "https://exiftool.org/TagNames/Composite.html", }, ApertureValue: { t: "number", grp: "EXIF", doc: "Lens aperture in APEX units. Secondary source for Aperture composite (FNumber takes priority).\nFormula: ApertureValue = 2 × log₂(FNumber). To write aperture, prefer FNumber as it's more intuitive.", see: "https://exiftool.org/TagNames/EXIF.html", }, Artist: { t: "string", grp: "EXIF", doc: "Image creator/photographer name. ExifTool trims trailing whitespace.\nWhen MWG module is loaded, this becomes a list-type tag synchronized with XMP-dc:Creator and IPTC:By-line.", see: "https://exiftool.org/TagNames/EXIF.html", }, AspectRatio: { t: "string", grp: "MakerNotes", value: "3:2" }, AutoRotate: { t: "number | string", grp: "MakerNotes" }, AvgBitrate: { t: "number | string", grp: "Composite", doc: "Average bitrate for video/audio files, calculated from media data size divided by duration.\nRead-only composite tag for QuickTime-based formats (MOV, MP4, etc.).", see: "https://exiftool.org/TagNames/Composite.html", }, BodySerialNumber: { t: "string", grp: "MakerNotes" }, BracketSettings: { t: "string", grp: "MakerNotes", doc: "Panasonic exposure bracketing configuration.\nValues: 'No Bracket', '3 Images, Sequence 0/-/+', '3 Images, Sequence -/0/+',\n'5 Images, Sequence 0/-/+', '5 Images, Sequence -/0/+', '7 Images, Sequence 0/-/+', '7 Images, Sequence -/0/+'.", }, BurstID: { t: "string", grp: "XMP" }, BurstUUID: { t: "string", grp: "MakerNotes" }, CameraID: { t: "string", grp: "MakerNotes" }, CameraOrientation: { t: "string", grp: "MakerNotes" }, CameraSerialNumber: { t: "string", grp: "EXIF", doc: "Camera serial number. DNG tag 0xc62f, writable string in IFD0.", }, Comment: { t: "string", grp: "File", doc: "Comment text. JPEG COM segment, GIF comment, or other file-level comment.\nNot an XMP tag despite common confusion.", }, Compass: { t: "number", grp: "MakerNotes", doc: "Ricoh Theta compass heading direction in degrees.\nCamera orientation sensor data.", }, ColorSpace: { t: "number | string", grp: "EXIF", doc: "Color space of image data. EXIF mandatory tag.\nStandard values: 1 (sRGB), 0xFFFF (Uncalibrated).\nNon-standard values: 2 (Adobe RGB, some cameras), 0xFFFD (Wide Gamut RGB, Sony), 0xFFFE (ICC Profile, Sony).\nAdobe RGB is typically indicated by 'Uncalibrated' with InteropIndex='R03'.", see: "https://exiftool.org/TagNames/EXIF.html", }, ContainerDirectory: { t: "ContainerDirectoryItem[] | Struct[]", grp: "XMP", doc: "XMP container directory structure containing list of container items.\nStruct type with flattened fields when Struct option is disabled.", see: "https://exiftool.org/TagNames/XMP.html", }, Copyright: { t: "string", grp: "EXIF", doc: "Copyright notice for the image. MWG composite tag that reconciles EXIF:Copyright, IPTC:CopyrightNotice, and XMP-dc:Rights.\nWriting updates all three locations to maintain MWG synchronization.", see: "https://exiftool.org/TagNames/MWG.html", }, Country: { t: "string", grp: "XMP" }, CountryCode: { t: "string", grp: "XMP" }, CreateDate: { t: "ExifDateTime | ExifDate | string | number", grp: "EXIF", doc: "When an image was digitized (captured by camera sensor). MWG composite tag that reconciles EXIF:CreateDate, IPTC digital creation fields, and XMP-xmp:CreateDate.\nDistinct from DateTimeOriginal (when photo was taken) - useful for scanned images.\nFor MOV/MP4 videos, use CreateDate instead of DateTimeOriginal.", see: "https://exiftool.org/TagNames/MWG.html", }, CreationTime: { t: "ExifDateTime | string", grp: "XMP" }, CropAngle: { t: "number", grp: "XMP", doc: "Crop rotation angle. Primarily from XMP-crs (Camera Raw Settings/Adobe Camera Raw) namespace.\nAlso found in Canon DPP recipe files.", see: "https://exiftool.org/TagNames/XMP.html#crs", }, CropBottom: { t: "number", grp: "XMP" }, CropHeight: { t: "number", grp: "XMP" }, CropLeft: { t: "number", grp: "XMP" }, CropRight: { t: "number", grp: "XMP" }, CropTop: { t: "number", grp: "XMP" }, CropWidth: { t: "number", grp: "XMP" }, DateCreated: { t: "ExifDateTime | string", grp: "XMP" }, DateTime: { t: "ExifDateTime | string", grp: "XMP" }, DateTimeCreated: { t: "ExifDateTime | string", grp: "Composite", doc: "Composite tag combining IPTC:DateCreated (YYYYMMDD) and IPTC:TimeCreated (with timezone offset).\nRead-only composite - to write, set IPTC:DateCreated and IPTC:TimeCreated individually.", see: "https://exiftool.org/TagNames/Composite.html", }, DateTimeDigitized: { t: "ExifDateTime | string", grp: "XMP" }, DateTimeGenerated: { t: "ExifDateTime | string", grp: "APP1" }, DateTimeOriginal: { t: "ExifDateTime | string", grp: "EXIF", doc: "When a photo was taken (shutter actuation time). MWG composite tag that reconciles EXIF:DateTimeOriginal, IPTC date/time created, and XMP-photoshop DateTimeOriginal.\nThis is the most commonly used timestamp for photos. Different from CreateDate (digitization) and ModifyDate (file modification).", see: "https://exiftool.org/TagNames/MWG.html", }, DateTimeUTC: { t: "ExifDateTime | string", grp: "MakerNotes" }, Description: { t: "string", grp: "XMP", doc: "Image caption or description. MWG composite tag that reconciles EXIF:ImageDescription, IPTC:Caption-Abstract, and XMP-dc:Description.\nWriting updates all three locations for MWG compliance. Supports language variants via RFC 3066 codes (e.g., 'Description-fr').", see: "https://exiftool.org/TagNames/MWG.html", }, DOF: { t: "string", grp: "Composite", doc: "Calculated depth of field based on focal length, aperture, and focus distance.\nWARNING: This value may be incorrect if the image has been resized.\nRead-only composite tag.", see: "https://exiftool.org/TagNames/Composite.html", }, Duration: { t: "number | string", grp: "QuickTime", doc: "Video/audio duration. QuickTime: stored in time scale units, converted to seconds using TimeScale.\nExifTool formats as 'H:MM:SS' or seconds. Some iPhone live-photo MOV videos may show key frame time instead of total duration.", see: "https://exiftool.org/TagNames/QuickTime.html", }, Error: { t: "string", grp: "ExifTool" }, ExifImageHeight: { t: "number", grp: "EXIF" }, ExifImageWidth: { t: "number", grp: "EXIF" }, ExifVersion: { t: "string", grp: "EXIF", doc: "EXIF specification version (e.g., '0232' for EXIF 2.32). EXIF mandatory tag.\nStored as 4-byte ASCII without separators. ExifTool accepts '2.32' format when writing.\nSome files incorrectly include null terminators which ExifTool removes.", see: "https://exiftool.org/TagNames/EXIF.html", }, ExposureCompensation: { t: "number | string", grp: "EXIF", doc: "Exposure bias/compensation in EV units (e.g., -0.67, +1.0). Also called ExposureBiasValue in EXIF spec.\nSigned value indicating deviation from metered exposure.", see: "https://exiftool.org/TagNames/EXIF.html", }, ExposureProgram: { t: "number | string", grp: "EXIF", doc: "Camera exposure mode.\nValues: 0 (Not Defined), 1 (Manual), 2 (Program AE), 3 (Aperture-priority AE), 4 (Shutter speed priority AE), 5 (Creative/Slow speed), 6 (Action/High speed), 7 (Portrait), 8 (Landscape).\nValue 9 (Bulb) is non-standard but used by some Canon models.", see: "https://exiftool.org/TagNames/EXIF.html", }, ExposureTime: { t: "string", grp: "EXIF", doc: "Shutter speed in seconds (e.g., '1/250'). Primary source for ShutterSpeed composite.\nTo write shutter speed, use this tag directly. BulbDuration takes priority in ShutterSpeed composite if present and > 0.", see: "https://exiftool.org/TagNames/EXIF.html", }, EyeDetection: { t: "string", grp: "MakerNotes", doc: "Canon eye detection AF setting. Values: 'On' or 'Off'.\nEnables autofocus tracking on detected eyes.", }, FaceDetection: { t: "string", grp: "MakerNotes", doc: "Sony face detection setting. Values: 'On' (16) or 'Off' (1).\nIndicates whether face detection was enabled during capture.", }, FacesDetected: { t: "number", grp: "MakerNotes", doc: "Number of faces detected by the camera during capture.\nFound in MakerNotes from Canon, Nikon, Sony, Panasonic, Olympus, Fujifilm, Pentax, Ricoh, and others.", }, FacesRecognized: { t: "number", grp: "MakerNotes", doc: "Panasonic: number of faces matched to user-registered known faces.\nRelated tags: RecognizedFace*Name, RecognizedFace*Position.", }, FaceRecognition: { t: "string", grp: "MakerNotes", doc: "Samsung face recognition setting. Values: 'On' or 'Off'.\nWhen enabled, camera attempts to match faces to registered profiles.", }, FileAccessDate: { t: "ExifDateTime | string", grp: "File", doc: "File system access date/time. Not stored metadata - file system property.\nWritable on some systems. Changes when file is read.", see: "https://exiftool.org/TagNames/File.html", }, FileCreateDate: { t: "ExifDateTime | string", grp: "File", doc: "File system creation date/time. Not stored metadata - file system property.\nWritable on some systems. Different from image capture date.", see: "https://exiftool.org/TagNames/File.html", }, FileInodeChangeDate: { t: "ExifDateTime | string", grp: "File", doc: "File system inode change date/time (Unix/Linux). Not stored metadata - file system property.\nChanges when file metadata (permissions, ownership) or content changes. Not available on Windows.", see: "https://exiftool.org/TagNames/File.html", }, FileModifyDate: { t: "ExifDateTime | string", grp: "File", doc: "File system modification date/time. Not stored metadata - file system property.\nWritable. Different from EXIF ModifyDate which tracks user edits.", see: "https://exiftool.org/TagNames/File.html", }, FileName: { t: "string", grp: "File", doc: "Name of the file. Not stored metadata - intrinsic file property.\nWritable: can rename files. May include full path to set Directory simultaneously.", see: "https://exiftool.org/TagNames/File.html", }, FileSize: { t: "string", grp: "File", doc: "Size of the file. Not stored metadata - intrinsic file property.\nRead-only. Uses SI prefixes by default (1 kB = 1000 bytes).", see: "https://exiftool.org/TagNames/File.html", }, FileType: { t: "string", grp: "File", doc: "File type determined from file content, not extension. Not stored metadata - intrinsic file property.\nRead-only.", see: "https://exiftool.org/TagNames/File.html", }, FileTypeExtension: { t: "string", grp: "File", doc: "Recommended file extension for this file type. Not stored metadata - intrinsic file property.\nRead-only.", see: "https://exiftool.org/TagNames/File.html", }, Flash: { t: "number | string", grp: "EXIF", doc: "Flash status and mode as bitfield. Common values:\n0x00 (No Flash), 0x01 (Fired), 0x05 (Fired, Return not detected), 0x07 (Fired, Return detected),\n0x10 (Off, Did not fire), 0x18 (Auto, Did not fire), 0x19 (Auto, Fired),\n0x41 (Fired, Red-eye reduction), 0x59 (Auto, Fired, Red-eye reduction).\nBit 0: fired, Bit 1-2: return detection, Bit 3-4: mode, Bit 5: function present, Bit 6: red-eye mode.", see: "https://exiftool.org/TagNames/EXIF.html", }, FNumber: { t: "number", grp: "EXIF", doc: "Lens aperture as f-number (e.g., 2.8, 5.6). Primary source for Aperture composite.\nTo write aperture, use this tag - it's more intuitive than ApertureValue (which uses APEX units).", see: "https://exiftool.org/TagNames/EXIF.html", }, Fnumber: { t: "string", grp: "APP12", doc: "F-number from APP12 segment (Ducky tags, uncommon). Note lowercase 'n'.\nDistinct from standard EXIF:FNumber. Use FNumber instead for consistency.", see: "https://exiftool.org/TagNames/APP12.html", }, FocalLength: { t: "string", grp: "EXIF", doc: "Lens focal length in millimeters. Actual focal length, not 35mm equivalent.\nFor 35mm equivalent, see FocalLengthIn35mmFormat or FocalLength35efl composite.", see: "https://exiftool.org/TagNames/EXIF.html", }, GPSAltitude: { t: "number", grp: "EXIF", doc: "GPS altitude in meters. Always stored as positive value; sign determined by GPSAltitudeRef.\nComposite GPSAltitude combines this with Ref to return signed value with 'Above/Below Sea Level' text.", see: "https://exiftool.org/TagNames/GPS.html", }, GPSAltitudeRef: { t: "number", grp: "EXIF", doc: "GPS altitude reference.\nValues: 0 (Above Sea Level), 1 (Below Sea Level), 2 (Positive Sea Level, sea-level ref), 3 (Negative Sea Level, sea-level ref).\nValues 2-3 added in EXIF 3.0 for ellipsoidal vs sea-level reference.", }, GPSDateTime: { t: "ExifDateTime | string", grp: "Composite", doc: "GPS timestamp combining GPSDateStamp and GPSTimeStamp fields.\nRead-only composite from multiple GPS sources including manufacturer-specific implementations.", see: "https://exiftool.org/TagNames/Composite.html", }, // We normally ask exiftool to render these as decimals, not DMS (degrees/minutes/seconds): GPSLatitude: { t: "number | string", grp: "EXIF", value: "37 deg 46' 29.64\" N", doc: "GPS latitude stored as three rationals (degrees, minutes, seconds). Always positive; hemisphere from GPSLatitudeRef.\nExifTool accepts decimal degrees, DMS, or mixed formats when writing. Composite GPSLatitude returns signed decimal.", see: "https://exiftool.org/TagNames/GPS.html", }, GPSLatitudeRef: { t: "string", grp: "EXIF", doc: "GPS latitude hemisphere.\nValid values: 'N' (North), 'S' (South). When writing, ExifTool accepts signed numbers or direction strings.", }, GPSLongitude: { t: "number | string", grp: "EXIF", value: "122 deg 25' 9.85\" W", doc: "GPS longitude stored as three rationals (degrees, minutes, seconds). Always positive; hemisphere from GPSLongitudeRef.\nExifTool accepts decimal degrees, DMS, or mixed formats when writing. Composite GPSLongitude returns signed decimal.", see: "https://exiftool.org/TagNames/GPS.html", }, GPSLongitudeRef: { t: "string", grp: "EXIF", doc: "GPS longitude hemisphere.\nValid values: 'E' (East), 'W' (West)", }, GPSPosition: { t: "string", grp: "Composite", doc: "Combined GPS latitude and longitude. Writable composite tag.\nWhen written, updates GPSLatitude, GPSLatitudeRef, GPSLongitude, GPSLongitudeRef.\nAccepts Google Maps coordinates (right-click format).", see: "https://exiftool.org/TagNames/Composite.html", }, GPSSpeed: { t: "string", grp: "EXIF", doc: "GPS speed of camera movement during capture. Units determined by GPSSpeedRef (K=km/h, M=mph, N=knots).\nMust be paired with GPSSpeedRef for meaningful interpretation.", see: "https://exiftool.org/TagNames/GPS.html", }, GPSSpeedRef: { t: "string", grp: "EXIF", doc: "GPS speed measurement unit.\nValid values: 'K' (km/h), 'M' (mph), 'N' (knots)", }, GPSTimeStamp: { t: "ExifTime | string", grp: "EXIF", doc: "UTC time of GPS fix. When writing, date is stripped off if present, and time is adjusted to UTC if it includes a timezone", see: "https://exiftool.org/TagNames/GPS.html", }, History: { t: "ResourceEvent[] | ResourceEvent | string", grp: "XMP", doc: "Tracks editing history of the document. XMP-xmpMM (Media Management) struct type.\nFlattened fields: HistoryAction, HistoryChanged, HistoryInstanceID, HistoryParameters, HistorySoftwareAgent, HistoryWhen.", see: "https://exiftool.org/TagNames/XMP.html#xmpMM", }, ImageDataMD5: { t: "string", grp: "File", doc: "MD5 hash of the actual image data (excluding metadata). Computed by ExifTool, not stored in file.\nUseful for detecting identical images with different metadata.", see: "https://exiftool.org/TagNames/File.html", }, ImageDescription: { t: "string", grp: "EXIF" }, ImageHeight: { t: "number", grp: "File", doc: "Image height in pixels. File-level property derived from file analysis.\nRead-only.", see: "https://exiftool.org/TagNames/File.html", }, ImageNumber: { t: "number | string", grp: "XMP", doc: "Image number/identifier. XMP-aux namespace.\nMay be numeric or string with leading zeros (e.g., '0001').", see: "https://exiftool.org/TagNames/XMP.html#aux", }, ImageSize: { t: "number | string", grp: "Composite", doc: "Image dimensions combining width and height from various metadata fields.\nRead-only composite derived from ImageWidth, ImageHeight, ExifImageWidth, ExifImageHeight, or RawImageCroppedSize.", see: "https://exiftool.org/TagNames/Composite.html", }, ImageUniqueID: { t: "string", grp: "EXIF", doc: "Unique identifier for the image, typically a 32-character hex string.\nUseful for image deduplication, tracking identity across edits, and linking related files (e.g., RAW + JPEG pairs).\nMay also appear in MakerNotes and XMP.", see: "https://exiftool.org/TagNames/EXIF.html", }, ImageWidth: { t: "number", grp: "File", doc: "Image width in pixels. File-level property derived from file analysis.\nRead-only.", see: "https://exiftool.org/TagNames/File.html", }, InternalSerialNumber: { t: "string", grp: "MakerNotes" }, ISO: { t: "number", grp: "EXIF", doc: "Camera ISO sensitivity rating. In EXIF, this is an array (int16u[n]) that can contain multiple values.\nHistorically called ISOSpeedRatings in EXIF 2.2, renamed to PhotographicSensitivity in EXIF 2.3.", see: "https://exiftool.org/TagNames/EXIF.html", }, ISOSpeed: { t: "number", grp: "EXIF" }, JpgFromRaw: { t: "BinaryField", grp: "EXIF", doc: "Embedded JPEG preview extracted from RAW files. Binary data type.\nAccess via BinaryField to get raw bytes or base64 encoding.", see: "https://exiftool.org/TagNames/EXIF.html", }, Keywords: { t: "string | string[]", grp: "IPTC", doc: "Searchable subject terms for image content. MWG composite tag that reconciles IPTC:Keywords and XMP-dc:Subject.\nMulti-value fields use semicolon-space ('; ') separators when represented as string.\nIPTC constraint: max 64 characters per keyword. Character encoding depends on IPTC:CodedCharacterSet; UTF8 recommended.", see: "https://exiftool.org/TagNames/MWG.html", }, Lens: { t: "string", grp: "Composite", doc: "Lens identification from focal length range, primarily for Canon cameras.\nRead-only composite. For more detailed lens identification, see LensID.", see: "https://exiftool.org/TagNames/Composite.html", }, LensID: { t: "string", grp: "Composite", doc: "Identifies actual lens model using manufacturer-specific lookup tables from partial type information.\nConfigurable: may be extended with user-defined lenses via ExifTool configuration.\nDifferent derivation logic for Canon (focal lengths), Nikon (LensIDNumber), Ricoh (LensFirmware), and others (XMP-aux:LensID).\nRead-only composite.", see: "https://exiftool.org/TagNames/Composite.html", }, LensInfo: { t: "string", grp: "EXIF" }, LensMake: { t: "string", grp: "EXIF" }, LensModel: { t: "string", grp: "EXIF" }, LensSerialNumber: { t: "string", grp: "EXIF" }, LensSpec: { t: "string", grp: "MakerNotes" }, LensType: { t: "string", grp: "MakerNotes" }, LensType2: { t: "string", grp: "MakerNotes" }, LensType3: { t: "string", grp: "MakerNotes" }, LightReading: { t: "number", grp: "MakerNotes", doc: "Pentax light meter reading. Calibration varies by camera model.\nFor Optio WP, add 6 to get approximate LV (Light Value).", }, Make: { t: "string", grp: "EXIF", doc: "Camera/device manufacturer. ExifTool automatically removes trailing whitespace.\nUsed internally by ExifTool for vendor-specific tag handling.", see: "https://exiftool.org/TagNames/EXIF.html", }, MaxDataRate: { t: "string", grp: "RIFF" }, MediaCreateDate: { t: "ExifDateTime | string", grp: "QuickTime", doc: "Creation date/time for QuickTime/MOV/MP4 media track. Stored as seconds since 1904-01-01 UTC.\nWARNING: Many cameras incorrectly store local time instead of UTC. ExifTool does not assume timezone unless QuickTimeUTC option is set.\nFor MOV/MP4 videos, use this tag instead of DateTimeOriginal.\nCannot be truly deleted (set to zero instead) as it's part of binary structure.", see: "https://exiftool.org/TagNames/QuickTime.html", }, Megapixels: { t: "number", grp: "Composite", doc: "Total megapixels calculated from ImageSize composite field.\nRead-only composite.", see: "https://exiftool.org/TagNames/Composite.html", }, MeteringMode: { t: "number | string", grp: "EXIF", doc: "Light metering mode used during capture.\nValues: 0 (Unknown), 1 (Average), 2 (Center-weighted average), 3 (Spot), 4 (Multi-spot), 5 (Multi-segment/Pattern/Evaluative), 6 (Partial), 255 (Other).", see: "https://exiftool.org/TagNames/EXIF.html", }, MetadataDate: { t: "ExifDateTime | string", grp: "XMP", doc: "Date when metadata was last modified. XMP-xmp namespace.", see: "https://exiftool.org/TagNames/XMP.html#xmp", }, MIMEType: { t: "string", grp: "File", doc: "MIME type of the file. Not stored metadata - intrinsic file property.\nRead-only, determined from file content.", see: "https://exiftool.org/TagNames/File.html", }, Model: { t: "string", grp: "EXIF", doc: "Camera/device model name. ExifTool automatically removes trailing whitespace.\nUsed internally by ExifTool for vendor-specific tag handling.", see: "https://exiftool.org/TagNames/EXIF.html", }, ModifyDate: { t: "ExifDateTime | string", grp: "EXIF", doc: "When the file was last modified by a user (not automatic processes). MWG composite tag that reconciles EXIF:ModifyDate and XMP-xmp:ModifyDate.\nShould reflect intentional user edits, not automatic metadata updates. Different from file system modification time.", see: "https://exiftool.org/TagNames/MWG.html", }, Notes: { t: "string", grp: "XMP", value: "Album description" }, Orientation: { t: "number", grp: "EXIF", doc: "Image orientation. Standard EXIF tag 0x112 in IFD0.\nValid values: 1 (Horizontal/normal), 2 (Mirror horizontal), 3 (Rotate 180°), 4 (Mirror vertical), 5 (Mirror horizontal + rotate 270° CW), 6 (Rotate 90° CW), 7 (Mirror horizontal + rotate 90° CW), 8 (Rotate 270° CW).\nMost images use values 1, 3, 6, and 8.", see: "https://exiftool.org/TagNames/EXIF.html", }, OriginalCreateDateTime: { t: "ExifDateTime | string", grp: "XMP" }, OffsetTime: { t: "string", grp: "EXIF", doc: "Timezone offset for ModifyDate (e.g., '+05:30', '-08:00', 'Z'). EXIF 2.31+ tag.\nUsed by SubSecModifyDate composite to produce timezone-aware timestamps.\nWriting SubSecModifyDate automatically updates this field.", see: "https://exiftool.org/TagNames/EXIF.html", }, OffsetTimeDigitized: { t: "string", grp: "EXIF", doc: "Timezone offset for CreateDate (e.g., '+05:30', '-08:00', 'Z'). EXIF 2.31+ tag.\nUsed by SubSecCreateDate composite to produce timezone-aware timestamps.\nWriting SubSecCreateDate automatically updates this field.", see: "https://exiftool.org/TagNames/EXIF.html", }, OffsetTimeOriginal: { t: "string", grp: "EXIF", doc: "Timezone offset for DateTimeOriginal (e.g., '+05:30', '-08:00', 'Z'). EXIF 2.31+ tag.\nUsed by SubSecDateTimeOriginal composite to produce timezone-aware timestamps.\nWriting SubSecDateTimeOriginal automatically updates this field.", see: "https://exiftool.org/TagNames/EXIF.html", }, PersonInImage: { t: "string | string[]", grp: "XMP", doc: "Name(s) of person(s) shown in the image. XMP-iptcExt namespace.\nSimpler alternative to MWG RegionInfo when face coordinates aren't needed.\nMulti-value field; array when multiple people identified.", see: "https://exiftool.org/TagNames/XMP.html#iptcExt", }, PersonInImageWDetails: { t: "Struct | Struct[]", grp: "XMP", doc: "Structured details about person(s) shown in the image. XMP-iptcExt namespace.\nIncludes PersonId, PersonName, PersonCharacteristic, PersonDescription.\nMore detailed than PersonInImage; IPTC Extension 2014+ standard.", see: "https://exiftool.org/TagNames/XMP.html#iptcExt", }, PreviewImage: { t: "BinaryField", grp: "Composite", doc: "Embedded preview image data extracted from the file.\nCRITICAL: Writable for updating existing embedded images, but cannot create or delete previews.\nCan only modify previews that already exist in the file.", see: "https://exiftool.org/TagNames/Composite.html", }, PreviewImageSize: { t: "string", grp: "Composite", value: "816x459", doc: "Dimensions of the embedded preview image (e.g., '816x459').\nRead-only composite. Useful for checking preview size without extracting binary data.", see: "https://exiftool.org/TagNames/Composite.html", }, Rating: { t: "number", grp: "XMP", doc: "Star rating for the image. MWG composite tag from XMP-xmp:Rating.\nValid values: -1 (rejected), 0 (unrated), 1-5 (star rating)\nNote: Rating may appear in EXIF but that's non-standard per MWG. Only XMP writes are supported.", see: "https://exiftool.org/TagNames/MWG.html", }, RatingPercent: { t: "number", grp: "XMP", value: 99, doc: "Microsoft-specific percentage rating (1-99).", see: "https://exiftool.org/forum/index.php?topic=3567.msg16210#msg16210", }, RAWFileType: { t: "string", grp: "MakerNotes", doc: "Sony RAW compression setting: 'Compressed RAW', 'Uncompressed RAW', or 'Lossless Compressed RAW'.\nIntroduced 2015 for uncompressed 14-bit RAW support.", }, RegistryID: { t: "Struct[]", grp: "XMP", doc: "Registry identifiers for the image. XMP-iptcExt namespace struct type.\nFlattened fields: RegistryEntryRole, RegistryItemID, RegistryOrganisationID.", see: "https://exiftool.org/TagNames/XMP.html#iptcExt", }, RegionAppliedToDimensionsH: { t: "number", grp: "XMP", doc: "Height of image when regions were defined. From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0).", see: "https://exiftool.org/TagNames/MWG.html", }, RegionAppliedToDimensionsUnit: { t: "string", grp: "XMP", value: "pixel", doc: "Unit for AppliedToDimensions (typically 'pixel'). From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0).", see: "https://exiftool.org/TagNames/MWG.html", }, RegionAppliedToDimensionsW: { t: "number", grp: "XMP", doc: "Width of image when regions were defined. From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0).", see: "https://exiftool.org/TagNames/MWG.html", }, RegionAreaH: { t: "number | number[]", grp: "XMP", doc: "Height of region(s) as normalized value (0-1). From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0). Array when multiple regions.", see: "https://exiftool.org/TagNames/MWG.html", }, RegionAreaUnit: { t: "string", grp: "XMP", value: "normalized", doc: "Unit for RegionArea coordinates (typically 'normalized'). From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0).", see: "https://exiftool.org/TagNames/MWG.html", }, RegionAreaW: { t: "number | number[]", grp: "XMP", doc: "Width of region(s) as normalized value (0-1). From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0). Array when multiple regions.", see: "https://exiftool.org/TagNames/MWG.html", }, RegionAreaX: { t: "number | number[]", grp: "XMP", doc: "Horizontal center of region(s) as normalized value (0-1). From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0). Array when multiple regions.", see: "https://exiftool.org/TagNames/MWG.html", }, RegionAreaY: { t: "number | number[]", grp: "XMP", doc: "Vertical center of region(s) as normalized value (0-1). From XMP-mwg-rs namespace.\nFlattened from RegionInfo struct (requires struct=0). Array when multiple regions.", see: "https://exiftool.org/TagNames/MWG.html", }, RegionInfo: { t: "Struct", grp: "XMP", doc: "MWG face/region metadata structure containing AppliedToDimensions and RegionList.\nWith struct=1 (default), contains nested objects. With struct=0, fields are flattened.\nUsed by Lightroom, Picasa, Windows Photo Gallery, digiKam, and other photo organizers.", see: "https://exiftool.org/TagNames/MWG.html", }, RegionInfoMP: { t: "Struct", grp: "XMP", doc: "Microsoft Photo region metadata structure (XMP-MP namespace).\nAlternative format to MWG regions, used by Windows Photo Gallery and some Microsoft tools.", see: "https://exiftool.org/TagNames/XMP.html#MP", }, RegionName: { t: "string | string[]", grp: "XMP", doc: "Name(s) of identified region(s), typically person names for face regions.\nFrom XMP-mwg-rs namespace. Flattened from RegionInfo struct (requires struct=0).\nFor Lightroom compatibility, also add names to Keywords/Subject.", see: "https://exiftool.org/TagNames/MWG.html", }, RegionPersonDisplayName: { t: "string | string[]", grp: "XMP", doc: "Display name(s) for person(s) in face regions.\nFrom XMP-MP namespace (Microsoft Photo), not XMP-mwg-rs.\nFlattened from RegionInfoMP struct (requires struct=0).", see: "https://exiftool.org/TagNames/XMP.html#MP", }, RegionRotation: { t: "number | number[]", grp: "XMP", doc: "Rotation angle(s) of region(s) in degrees.\nFrom XMP-mwg-rs namespace. Flattened from RegionInfo struct (requires struct=0).", see: "https://exiftool.org/TagNames/MWG.html", }, RegionType: { t: "string | string[]", grp: "XMP", doc: "Type(s) of region(s) identified.\nValid values: 'Face', 'Pet', 'BarCode', 'Focus'.\nFrom XMP-mwg-rs namespace. Flattened from RegionInfo struct (requires struct=0).", see: "https://exiftool.org/TagNames/MWG.html", }, Rotation: { t: "number", grp: "Composite", doc: "Degrees of clockwise camera rotation for QuickTime/MP4 video files.\nSpecial writable: Writing this tag updates QuickTime MatrixStructure for all tracks with a non-zero image size simultaneously.\nDifferent from EXIF Orientation tag.", see: "https://exiftool.org/TagNames/Composite.html", }, RunTimeValue: { t: "number", grp: "MakerNotes" }, SerialNumber: { t: "string", grp: "MakerNotes" }, ShutterCount: { t: "number", grp: "MakerNotes" }, ShutterCount2: { t: "number", grp: "MakerNotes" }, ShutterCount3: { t: "number", grp: "MakerNotes" }, ShutterSpeed: { t: "string", grp: "Composite", doc: "Shutter speed combining ExposureTime, ShutterSpeedValue, and BulbDuration.\nRead-only composite tag. Format typically fractional (e.g., '1/250').", see: "https://exiftool.org/TagNames/Composite.html", }, SonyDateTime2: { t: "ExifDateTime | string", grp: "MakerNotes" }, SonyExposureTime: { t: "string", grp: "MakerNotes" }, SonyFNumber: { t: "number", grp: "MakerNotes" }, SonyISO: { t: "number", grp: "MakerNotes" }, SourceFile: { t: "string", grp: "ExifTool", value: "path/to/file.jpg" }, SubSecCreateDate: { t: "ExifDateTime | string", grp: "Composite", doc: "Creation date with subsecond precision, merging EXIF:CreateDate, SubSecTimeDigitized, and OffsetTimeDigitized.\nWritable composite: writing updates all three fields simultaneously for high-precision timestamps with timezone information.", see: "https://exiftool.org/TagNames/Composite.html", }, SubSecDateTimeOriginal: { t: "ExifDateTime | string", grp: "Composite", doc: "Original datetime with subsecond precision, combining EXIF:DateTimeOriginal, SubSecTimeOriginal, and OffsetTimeOriginal.\nWritable composite: writing updates all three fields simultaneously. Represents when the photo was originally taken with high precision.", see: "https://exiftool.org/TagNames/Composite.html", }, SubSecModifyDate: { t: "ExifDateTime | string", grp: "Composite", doc: "Modification timestamp with subsecond precision, combining EXIF:ModifyDate, SubSecTime, and OffsetTime.\nWritable composite: writing updates all three fields simultaneously. Represents when the file was last modified with high precision.", see: "https://exiftool.org/TagNames/Composite.html", }, SubSecTime: { t: "number", grp: "EXIF" }, SubSecTimeDigitized: { t: "number", grp: "EXIF" }, SubjectDetection: { t: "string", grp: "MakerNotes", doc: "Nikon Z series subject detection mode.\nValues: 'Off', 'Auto', 'People', 'Animals', 'Vehicles', 'Birds', 'Airplanes'.\nIndicates what the camera's AI was configured to detect during capture.", }, ThumbnailImage: { t: "BinaryField", grp: "EXIF", doc: "Embedded thumbnail image data. Binary data type.\nWritable for updating existing thumbnails, but cannot create or delete thumbnails.", }, ThumbnailLength: { t: "number", grp: "EXIF", doc: "Size in bytes of the embedded JPEG thumbnail.\nUseful for checking thumbnail availability without extracting binary data.", }, ThumbnailOffset: { t: "number", grp: "EXIF", doc: "Byte offset of the embedded JPEG thumbnail within the file.\nUsed with ThumbnailLength to locate thumbnail data.", }, ThumbnailTIFF: { t: "BinaryField", grp: "Composite", doc: "Embedded TIFF thumbnail image data. Composite tag rebuilt from component EXIF tags.\nRead-only - derived from SubfileType, Compression, ImageWidth, ImageHeight, etc.", }, TimeStamp: { t: "ExifDateTime | string", grp: "MakerNotes" }, TimeZone: { t: "string", grp: "MakerNotes" }, TimeZoneOffset: { t: "number | string", grp: "EXIF" }, Title: { t: "string", grp: "XMP", doc: "Image title. XMP-dc (Dublin Core) namespace - use this standard schema instead of non-standard XMP-xmp:Title.\nSupports language variants via RFC 3066 codes (e.g., 'Title-fr'). Using 'x-default' language code preserves other existing languages when writing.", see: "https://exiftool.org/TagNames/XMP.html#dc", }, Versions: { t: "Version[] | Version | string", grp: "XMP", doc: "Version history of the document. XMP-xmpMM (Media Management) struct type.\nFlattened fields include VersionsComments, VersionsEvent, etc.", see: "https://exiftool.org/TagNames/XMP.html#xmpMM", }, WhiteBalance: { t: "number | string", grp: "EXIF", doc: "White balance mode. Standard EXIF values: 0 (Auto), 1 (Manual).\nMakerNotes often contain more detailed WhiteBalance with values like Daylight, Cloudy, Tungsten, Fluorescent, Flash, Custom, etc.\nEXIF:WhiteBalance has lower priority than MakerNotes version when both exist.", see: "https://exiftool.org/TagNames/EXIF.html", }, Warning: { t: "string", grp: "ExifTool" }, XPComment: { t: "string", grp: "EXIF" }, XPKeywords: { t: "string", grp: "EXIF" }, XPSubject: { t: "string", grp: "EXIF" }, XPTitle: { t: "string", grp: "EXIF" }, }; // ☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠ // ☠☠ AVAST YE! ☠☠ // ☠☠ ☠☠ // ☠☠ BEYOND THIS POINT, THERE BE NAUGHT BUT TANGLED RIGGING ☠☠ // ☠☠ AND CODE THAT'D MAKE A KRAKEN WEEP. TURN BACK, ☠☠ // ☠☠ LEST YE FIND YERSELF IN DAVY JONES' ☠☠ // ☠☠ LOCKER, WITH NAUGHT BUT BUGS FER COMPANY. ☠☠ // ☠☠ ☠☠ // ☠☠ YE'VE BEEN WARNED, YE BRAVE AND FOOLISH SOUL. ☠☠ // ☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠☠ // // no srsly, this code is _really_ bad, but I wanted a Tags interface, more // than I felt shame in writing this, so it's here. I'm sorry. Everything // _else_ has tests, honest. // const js_docs = { CompositeTags: [ "These are tags are derived from the values of one or more other tags.", "Only a few are writable directly.", "@see https://exiftool.org/TagNames/Composite.html", ], EXIFTags: ["@see https://exiftool.org/TagNames/EXIF.html"], ExifToolTags: ["These tags are added by `exiftool`."], FileTags: [ "These tags are not metadata fields, but are intrinsic to the content of a", "given file. ExifTool can't write to many of these tags.", ], FlashPixTags: ["@see https://exiftool.org/TagNames/FlashPix.html"], GeolocationTags: [ "These tags are only available if {@link ExifToolOptions.geolocation} is true", "and the file has valid GPS location data.", ], IPTCTags: ["@see https://exiftool.org/TagNames/IPTC.html"], PhotoshopTags: ["@see https://exiftool.org/TagNames/Photoshop.html"], XMPTags: ["@see https://exiftool.org/TagNames/XMP.html"], }; // If we don't do tag pruning, TypeScript fails with // error TS2590: Expression produces a union type that is too complex to represent. // This is a set of regexp patterns that match tags that are (probably) // ignorable: const ExcludedTagRe = new RegExp([ "_", "A[ab]{3,}", "AEC", "AFR", "AFS", "AFStatus_", "AFTrace", "AFV", "ASF\\d", "AtmosphericTrans", "AWB", "CAM\\d", "CameraTemperature", "ChroSupC", "DayltConv", "DefConv", "DefCor", "Face\\d", "FCS\\d", "HJR", "IM[a-z]", "IncandConv", "Kelvin_?WB", "Label\\d", "Value\\d", "Mask_", "MODE", "MTR", "O[a-f]+Revision", "PF\\d\\d", "PictureWizard", "PiP", "Planck", "PF\\d", "ProfileLook", "R2[A-Z]", // noooo artooo "RecallShoot", "STB\\d", "Tag[\\d_]+", "TL84", "WB[_\\d]", "YhiY", "\\w{6,}\\d{1,2}$", ].join("|")); function sortBy(arr, f) { return arr .filter((ea) => ea != null) .map((item, idx) => ({ item, cmp: (0, Maybe_1.map)(f(item, idx), (ea) => [ea, idx]), })) .filter((ea) => ea.cmp != null) .sort((a, b) => cmp(a.cmp, b.cmp)) .map((ea) => ea.item); } // Benchmark on 3900X: 53s for 10778 files with defaults // 23s for 10778 files with these overrides: const exiftool = new ExifTool_1.ExifTool({ maxProcs: node_os_1.default.cpus().length, // if we use straight defaults, we're load-testing those defaults. streamFlushMillis: 2, minDelayBetweenSpawnMillis: 0, geolocation: true, // maxTasksPerProcess: 100, // < uncomment to verify proc wearing works }); function ellipsize(str, max) { str = "" + str; return str.length < max ? str : str.slice(0, max - 8) + "…" + str.slice(-7); } // ☠☠ NO SRSLY STOP SCROLLING IT REALLY IS BAD ☠☠ (0, batch_cluster_1.setLogger)(batch_cluster_1.Log.withLevels(batch_cluster_1.Log.withTimestamps(batch_cluster_1.Log.filterLevels({ trace: console.log, debug: console.log, info: console.log, warn: console.warn, error: console.error, }, node_process_1.default.env.LOG ?? "info")))); node_process_1.default.on("uncaughtException", (error) => { console.error("Caught uncaughtException: " + error); }); node_process_1.default.on("unhandledRejection", (reason) => { console.error("Caught unhandledRejection: " + reason); }); function usage() { console.log("Usage: `npm run mktags IMG_DIR`"); console.log("\nRebuilds src/Tags.ts from tags found in IMG_DIR."); node_process_1.default.exit(1); } function cmp(a, b) { return a > b ? 1 : a < b ? -1 : 0; } const roots = node_process_1.default.argv.slice(2); if (roots.length === 0) throw new Error("USAGE: mktags <path to image directory>"); const pattern = "**/*.+(3fr|avi|jpg|mov|mp4|cr2|cr3|nef|orf|raf|arw|rw2|dng)"; const files = roots .map((root) => { (0, batch_cluster_1.logger)().info("Scanning " + root + "/" + pattern + "..."); return globule_1.default.find(pattern, { srcBase: root, nocase: true, nodir: true, absolute: true, }); }) .reduce((prev, curr) => prev.concat(curr)); if (files.length === 0) { console.error(`No files found in ${roots}`); usage(); } (0, batch_cluster_1.logger)().info("Found " + files.length + " files...", files.slice(0, 7)); function valueType(value) { if (value == null) return; if (Array.isArray(value)) { const types = (0, Array_1.uniq)((0, Array_1.compact)(value.map((ea) => valueType(ea)))); return (types.length === 1 ? types[0] : "any") + "[]"; } if (typeof value === "object") { const ctor = value.constructor.name; if (ctor === "Object") { return "Struct"; } if (ctor.startsWith("ExifDate") || ctor.startsWith("ExifTime") || ctor.endsWith("Field")) { return ctor + " | string"; } return ctor; } else { return typeof value; } } // except CountingMap. Isn't it cute? Not ashamed of you, little guy! class CountingMap { size = 0; m = new Map(); add(...arr) { this.size += arr.length; for (const ea of arr) { this.m.set(ea, 1 + (this.m.get(ea) ?? 0)); } } byCountDesc() { return Array.from(this.m.keys()).sort((a, b) => cmp(this.m.get(b), this.m.get(a))); } topN(n) { return this.byCountDesc().slice(0, n); } /** * @param p [0,1] * @return the values found in the top p of values */ byP(p) { const min = p * this.size; return this.byCountDesc().filter((ea) => (this.m.get(ea) ?? 0) > min); } } function sigFigs(i, digits) { if (i === 0 || digits === 0) return 0; const pow = Math.pow(10, digits - Math.round(Math.ceil(Math.log10(Math.abs(i))))); return Math.round(i * pow) / pow; } function toStr(o) { if (o == null) return ""; else if ((0, Number_1.isNumber)(o)) return sigFigs(o, 8); else if ((0, String_1.isString)(o)) return `"${ellipsize(o, 65)}"`; else if ((0, String_1.isString)(o.rawValue)) return `"${ellipsize(o.rawValue, 65)}"`; else return ellipsize(JSON.stringify(o), 65); } function exampleToS(examples) { return examples.length > 1 ? toStr(examples) : toStr(examples[0]); } function getOrSet(m, k, valueThunk) { const prior = m.get(k); if (prior != null) { return prior; } else { const