Skip to content

Commit

Permalink
ReadTask improvements
Browse files Browse the repository at this point in the history
- DRY up option pick for ReadTask to fix missing fields
- Add new ignoreZeroZeroLatLon option
  • Loading branch information
mceachen committed Dec 1, 2023
1 parent ab096a1 commit 5a6c914
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 54 deletions.
20 changes: 11 additions & 9 deletions src/DefaultExifToolOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,16 @@ export const DefaultExifToolOptions: Omit<
versionCommand: new VersionTask().command,
healthCheckIntervalMillis: 30000,
healthCheckCommand: "-ver\n-execute\n",
useMWG: false,

backfillTimezones: true,
defaultVideosToUTC: true,
geoTz: geoTz,
ignoreZeroZeroLatLon: true,
imageHashType: false,
includeImageDataMD5: undefined,
inferTimezoneFromDatestamps: false, // to retain prior behavior
inferTimezoneFromDatestampTags: [...CapturedAtTagNames],
isIgnorableError: isIgnorableWarning,
numericTags: [
"*Duration*",
"GPSAltitude",
Expand All @@ -46,12 +55,5 @@ export const DefaultExifToolOptions: Omit<
"Orientation",
"Rotation",
],
includeImageDataMD5: undefined,
imageHashType: false,
defaultVideosToUTC: true,
backfillTimezones: true,
inferTimezoneFromDatestamps: false, // to retain prior behavior
inferTimezoneFromDatestampTags: [...CapturedAtTagNames],
geoTz: geoTz,
isIgnorableError: isIgnorableWarning,
useMWG: false,
})
11 changes: 2 additions & 9 deletions src/ExifTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { pick } from "./Pick"
import { PreviewTag } from "./PreviewTag"
import { Json, Literal, RawTags } from "./RawTags"
import { ReadRawTask } from "./ReadRawTask"
import { ReadTask, ReadTaskOptions } from "./ReadTask"
import { ReadTask, ReadTaskOptionFields, ReadTaskOptions } from "./ReadTask"
import { ResourceEvent } from "./ResourceEvent"
import { RewriteAllTagsTask } from "./RewriteAllTagsTask"
import { blank, notBlank } from "./String"
Expand Down Expand Up @@ -258,14 +258,7 @@ export class ExifTool {
return this.enqueueTask(() =>
ReadTask.for(file, {
optionalArgs,
...pick(
this.options,
"numericTags",
"useMWG",
"imageHashType",
"defaultVideosToUTC",
"geoTz"
),
...pick(this.options, ...ReadTaskOptionFields),
...options,
})
) as any // < no way to know at compile time if we're going to get back a T!
Expand Down
8 changes: 8 additions & 0 deletions src/ExifToolOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,14 @@ export interface ExifToolOptions
*/
inferTimezoneFromDatestampTags: (keyof Tags)[]

/**
* Some software uses a GPS position of (0,0) as a synonym for "unset". If
* this option is true, and GPSLatitude and GPSLongitude are both 0, then
* those values will be returned, but the TZ will not be inferred from that
* location.
*/
ignoreZeroZeroLatLon: boolean

/**
* `ExifTool` has a shebang line that assumes a valid `perl` is installed at
* `/usr/bin/perl`.
Expand Down
29 changes: 17 additions & 12 deletions src/ReadTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ const PassthroughTags = [
"DateDisplayFormat",
]

export const ReadTaskOptionFields = [
"backfillTimezones",
"defaultVideosToUTC",
"geoTz",
"ignoreZeroZeroLatLon",
"imageHashType",
"includeImageDataMD5",
"inferTimezoneFromDatestamps",
"inferTimezoneFromDatestampTags",
"numericTags",
"useMWG",
] as const

const NullIsh = ["undef", "null", "undefined"]

export function nullish(s: string | undefined): s is undefined {
Expand All @@ -42,18 +55,7 @@ export function nullish(s: string | undefined): s is undefined {

export const DefaultReadTaskOptions = {
optionalArgs: [] as string[],
...pick(
DefaultExifToolOptions,
"numericTags",
"useMWG",
"includeImageDataMD5",
"imageHashType",
"defaultVideosToUTC",
"backfillTimezones",
"inferTimezoneFromDatestamps",
"inferTimezoneFromDatestampTags",
"geoTz"
),
...pick(DefaultExifToolOptions, ...ReadTaskOptionFields),
} as const

export type ReadTaskOptions = typeof DefaultReadTaskOptions
Expand Down Expand Up @@ -186,6 +188,9 @@ export class ReadTask extends ExifToolTask<Tags> {
#extractLatLon = lazy(() => {
this.lat ??= this.#latlon("GPSLatitude", "S", 90)
this.lon ??= this.#latlon("GPSLongitude", "W", 180)
if (this.options.ignoreZeroZeroLatLon && this.lat === 0 && this.lon === 0) {
this.invalidLatLon = true
}
if (this.invalidLatLon) {
this.lat = this.lon = undefined
}
Expand Down
74 changes: 50 additions & 24 deletions src/WriteTask.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,31 +281,57 @@ describe("WriteTask", function () {
return Math.random() * (max - min) + min
}

describe("round-trips GPS values (attempt to reproduce #131)", () => {
// Verify there's no shenanigans with negative, zero, or positive
// lat/lon combinations:
for (const GPSLatitude of [
randomFloat(-89, 1),
0,
39.1132577,
randomFloat(1, 89),
]) {
for (const GPSLongitude of [
randomFloat(-179, 1),
-84.6907715,
0,
randomFloat(1, 179),
]) {
it(JSON.stringify({ GPSLatitude, GPSLongitude }), async () => {
const f = await dest()
await exiftool.write(f, { GPSLatitude, GPSLongitude })
const tags = await exiftool.read(f)
expect(tags.GPSLatitude).to.be.closeTo(GPSLatitude, 0.001)
expect(tags.GPSLongitude).to.be.closeTo(GPSLongitude, 0.001)
})
for (const ignoreZeroZeroLatLon of [false, true]) {
describe(
"round-trips GPS values (attempt to reproduce #131): " +
JSON.stringify({ ignoreZeroZeroLatLon }),
() => {
// Verify there's no shenanigans with negative, zero, or positive
// lat/lon combinations:
for (const GPSLatitude of [
randomFloat(-89, -1),
0,
39.1132577,
randomFloat(1, 89),
]) {
for (const GPSLongitude of [
randomFloat(-179, -1),
-84.6907715,
0,
randomFloat(1, 179),
]) {
it(
JSON.stringify({ GPSLatitude, GPSLongitude }),
async () => {
exiftool.options.ignoreZeroZeroLatLon =
ignoreZeroZeroLatLon
const f = await dest()
await exiftool.write(f, { GPSLatitude, GPSLongitude })
const tags = await exiftool.read(f)
if (
ignoreZeroZeroLatLon &&
GPSLatitude === 0 &&
GPSLongitude === 0
) {
expect(tags.GPSLatitude).to.eql(undefined)
expect(tags.GPSLongitude).to.eql(undefined)
} else {
expect(tags.GPSLatitude).to.be.closeTo(
GPSLatitude,
0.001
)
expect(tags.GPSLongitude).to.be.closeTo(
GPSLongitude,
0.001
)
}
}
)
}
}
}
}
})
)
}

it("round-trips a struct tag", async () => {
const struct: Struct[] = [
Expand Down

0 comments on commit 5a6c914

Please sign in to comment.