Pick up location from C2PA or Exif
This commit is contained in:
parent
d6c8146096
commit
64b14cef40
2 changed files with 197 additions and 113 deletions
|
|
@ -88,43 +88,18 @@ const updateDetails = () => {
|
|||
});
|
||||
}
|
||||
|
||||
const exif = props.proof?.integrity?.exif;
|
||||
if (exif) {
|
||||
const getSimpleValue = (key: string): string | undefined => {
|
||||
return exif ? (exif[key] as string)?.replace(/^"(.+(?="$))"$/, "$1") : undefined;
|
||||
};
|
||||
|
||||
const toDegrees = (dms: string | undefined, direction: string) => {
|
||||
if (!dms || dms.length == 0) return undefined;
|
||||
var parts = dms.split(/deg|min|sec/);
|
||||
var d = parts[0];
|
||||
var m = parts[1];
|
||||
var s = parts[2];
|
||||
var deg = (Number(d) + Number(m) / 60 + Number(s) / 3600).toFixed(6);
|
||||
if (direction == "S" || direction == "W") {
|
||||
deg = "-" + deg;
|
||||
}
|
||||
return deg;
|
||||
};
|
||||
|
||||
// Location
|
||||
try {
|
||||
const lat = toDegrees(getSimpleValue("GPSLatitude"), getSimpleValue("GPSLatitudeRef") ?? "");
|
||||
const lon = toDegrees(getSimpleValue("GPSLongitude"), getSimpleValue("GPSLongitudeRef") ?? "");
|
||||
if (lat && lon && lat.length > 0 && lon.length > 0) {
|
||||
const location = lat + " " + lon;
|
||||
const link =
|
||||
"https://www.google.com/maps/search/?api=1&query=" + encodeURIComponent(lat) + "," + encodeURIComponent(lon);
|
||||
d.push({
|
||||
icon: "$vuetify.icons.ic_exif_location",
|
||||
title: t("file_mode.cc_location"),
|
||||
value: location,
|
||||
link: link,
|
||||
});
|
||||
}
|
||||
} catch (error) { }
|
||||
if (props.metadata?.location) {
|
||||
const lat = props.metadata.location.latitude;
|
||||
const lon = props.metadata.location.longitude;
|
||||
const location = lat + " " + lon;
|
||||
const link = "https://www.google.com/maps/search/?api=1&query=" + encodeURIComponent(lat) + "," + encodeURIComponent(lon);
|
||||
d.push({
|
||||
icon: "$vuetify.icons.ic_exif_location",
|
||||
title: t("file_mode.cc_location"),
|
||||
value: location,
|
||||
link: link,
|
||||
});
|
||||
}
|
||||
|
||||
details.value = d;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -62,13 +62,19 @@ export type Proof = {
|
|||
ai?: { inferenceResult?: AIInferenceResult };
|
||||
};
|
||||
|
||||
export type MediaMetadataGenerator = "unknown" | "camera" | "screenshot" | "ai";
|
||||
export type MediaMetadataGenerator = "unknown" | "camera" | "screenshot" | "ai";
|
||||
export type MediaMetadataPropertySource = "c2pa" | "exif" | "metadata";
|
||||
|
||||
export type MediaMetadataEdit = {
|
||||
editor: string;
|
||||
date?: Date;
|
||||
}
|
||||
};
|
||||
|
||||
export type MediaMetadataLocation = {
|
||||
latitude: string;
|
||||
longitude: string;
|
||||
source: MediaMetadataPropertySource;
|
||||
};
|
||||
|
||||
export type MediaInterventionFlags = {
|
||||
creationDate?: Date;
|
||||
|
|
@ -76,7 +82,7 @@ export type MediaInterventionFlags = {
|
|||
modified?: boolean;
|
||||
containsC2PA?: boolean;
|
||||
containsEXIF?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export type MediaMetadata = {
|
||||
device?: string;
|
||||
|
|
@ -87,6 +93,7 @@ export type MediaMetadata = {
|
|||
edits?: MediaMetadataEdit[];
|
||||
containsC2PA?: boolean;
|
||||
containsEXIF?: boolean;
|
||||
location?: MediaMetadataLocation;
|
||||
};
|
||||
|
||||
type FlagMatchRule = {
|
||||
|
|
@ -98,7 +105,7 @@ type FlagMatchRule = {
|
|||
type FlagMatchRulePathSegment = {
|
||||
object: any;
|
||||
path: string;
|
||||
}
|
||||
};
|
||||
|
||||
type FlagMatchRuleValue = {
|
||||
path: FlagMatchRulePathSegment[];
|
||||
|
|
@ -114,51 +121,92 @@ type FlagMatchInfo = {
|
|||
type FlagValue = {
|
||||
path: string;
|
||||
transform?: (value: any, match: FlagMatchRuleValue) => any;
|
||||
}
|
||||
};
|
||||
|
||||
const pathsC2PASource = (): FlagValue[] => {
|
||||
return [
|
||||
{path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data/exif:Make", transform: getExifMakeModelPrefixed },
|
||||
{path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data/exif:Model", transform: getExifMakeModelPrefixed },
|
||||
{path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=c2pa.actions|c2pa.actions.v2]/data/actions[action=c2pa.created]/softwareAgent/name"},
|
||||
{path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=c2pa.actions|c2pa.actions.v2]/data/actions[action=c2pa.created]/softwareAgent"},
|
||||
{path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=c2pa.actions|c2pa.actions.v2]/data/actions[action=c2pa.created]/../../../claim_generator"}
|
||||
{
|
||||
path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data/exif:Make",
|
||||
transform: getExifMakeModelPrefixed,
|
||||
},
|
||||
{
|
||||
path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data/exif:Model",
|
||||
transform: getExifMakeModelPrefixed,
|
||||
},
|
||||
{
|
||||
path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=c2pa.actions|c2pa.actions.v2]/data/actions[action=c2pa.created]/softwareAgent/name",
|
||||
},
|
||||
{
|
||||
path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=c2pa.actions|c2pa.actions.v2]/data/actions[action=c2pa.created]/softwareAgent",
|
||||
},
|
||||
{
|
||||
path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=c2pa.actions|c2pa.actions.v2]/data/actions[action=c2pa.created]/../../../claim_generator",
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const pathsExifSource = (): FlagValue[] => {
|
||||
return [
|
||||
{path: "integrity/exif/Make", transform: getExifMakeModelNoPrefix },
|
||||
{path: "integrity/exif/Model", transform: getExifMakeModelNoPrefix }
|
||||
{ path: "integrity/exif/Make", transform: getExifMakeModelNoPrefix },
|
||||
{ path: "integrity/exif/Model", transform: getExifMakeModelNoPrefix },
|
||||
];
|
||||
};
|
||||
|
||||
const pathsC2PACreationDate = (): FlagValue[] => {
|
||||
return [
|
||||
{path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=c2pa.actions|c2pa.actions.v2]/data/actions[action=c2pa.created]/when"},
|
||||
{path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=c2pa.actions|c2pa.actions.v2]/data/actions[action=c2pa.created]/../../metadata/dateTime"},
|
||||
{path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=c2pa.actions|c2pa.actions.v2]/data/actions[action=c2pa.created]/../../../signature_info/time"},
|
||||
{path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data/exif:DateTimeOriginal", transform: (value, match) => {
|
||||
{
|
||||
path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=c2pa.actions|c2pa.actions.v2]/data/actions[action=c2pa.created]/when",
|
||||
},
|
||||
{
|
||||
path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=c2pa.actions|c2pa.actions.v2]/data/actions[action=c2pa.created]/../../metadata/dateTime",
|
||||
},
|
||||
{
|
||||
path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=c2pa.actions|c2pa.actions.v2]/data/actions[action=c2pa.created]/../../../signature_info/time",
|
||||
},
|
||||
{
|
||||
path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data/exif:DateTimeOriginal",
|
||||
transform: (value, match) => {
|
||||
// Check if we have a timestamp offset
|
||||
let offset = extractFlagValues("../exif:OffsetTimeOriginal", match.path)?.at(0)?.value;
|
||||
return dayjs(Date.parse(value + (offset ? offset : ""))).format("lll");
|
||||
}}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
const pathsC2PACamera = (): FlagValue[] => {
|
||||
return [
|
||||
{path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data{exif:SceneType=1|directly photographed image}"},
|
||||
{path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data/exif:LensMake"},
|
||||
{path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data/exif:LensModel"},
|
||||
{
|
||||
path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data{exif:SceneType=1|directly photographed image}",
|
||||
},
|
||||
{ path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data/exif:LensMake" },
|
||||
{ path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data/exif:LensModel" },
|
||||
];
|
||||
};
|
||||
|
||||
const pathsExifCamera = (): FlagValue[] => {
|
||||
return [
|
||||
{path: "integrity/exif{SceneType=1|directly photographed image}"},
|
||||
{path: "integrity/exif/LensMake"},
|
||||
{path: "integrity/exif/LensModel"},
|
||||
{ path: "integrity/exif{SceneType=1|directly photographed image}" },
|
||||
{ path: "integrity/exif/LensMake" },
|
||||
{ path: "integrity/exif/LensModel" },
|
||||
];
|
||||
};
|
||||
|
||||
const pathsC2PALocation = (): FlagValue[] => {
|
||||
return [
|
||||
{ path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data/GPSLatitude" },
|
||||
{ path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data/GPSLongitude" },
|
||||
{ path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data/GPSLatitudeRef" },
|
||||
{ path: "integrity/c2pa/manifest_info/manifests[]/assertions[label=stds.exif]/data/GPSLongitudeRef" },
|
||||
];
|
||||
};
|
||||
|
||||
const pathsExifLocation = (): FlagValue[] => {
|
||||
return [
|
||||
{ path: "integrity/exif/GPSLatitude" },
|
||||
{ path: "integrity/exif/GPSLongitude" },
|
||||
{ path: "integrity/exif/GPSLatitudeRef" },
|
||||
{ path: "integrity/exif/GPSLongitudeRef" },
|
||||
];
|
||||
};
|
||||
|
||||
|
|
@ -166,47 +214,62 @@ const getExifValue = (val: string): string => {
|
|||
return val.replace(/^"(.+(?="$))"$/, "$1");
|
||||
};
|
||||
|
||||
const toDegrees = (dms: string | undefined, direction: string) => {
|
||||
if (!dms || dms.length == 0) return undefined;
|
||||
var parts = dms.split(/deg|min|sec/);
|
||||
var d = parts[0];
|
||||
var m = parts[1];
|
||||
var s = parts[2];
|
||||
var deg = (Number(d) + Number(m) / 60 + Number(s) / 3600).toFixed(6);
|
||||
if (direction == "S" || direction == "W") {
|
||||
deg = "-" + deg;
|
||||
}
|
||||
return deg;
|
||||
};
|
||||
|
||||
const pathsExifCreationDate = (): FlagValue[] => {
|
||||
return [
|
||||
{path: "integrity/exif/DateTimeOriginal", transform: (value, match) => {
|
||||
{
|
||||
path: "integrity/exif/DateTimeOriginal",
|
||||
transform: (value, match) => {
|
||||
let dateTimeOriginal = getExifValue(value);
|
||||
// Check if we have a timestamp offset
|
||||
let offset = extractFlagValues("../OffsetTimeOriginal", match.path)?.at(0)?.value as string;
|
||||
return dayjs(Date.parse(dateTimeOriginal + (offset ? getExifValue(offset) : ""))).format("lll");
|
||||
}}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
const getExifMakeModelNoPrefix = (ignoredvalue: any, match: FlagMatchRuleValue): string => {
|
||||
return getExifMakeModel(match, "");
|
||||
}
|
||||
};
|
||||
|
||||
const getExifMakeModelPrefixed = (ignoredvalue: any, match: FlagMatchRuleValue): string => {
|
||||
return getExifMakeModel(match, "exif:");
|
||||
}
|
||||
};
|
||||
|
||||
const getExifMakeModel = (match: FlagMatchRuleValue, prefix: string): string => {
|
||||
// Make and model
|
||||
let makeAndModel = "";
|
||||
const make = extractFlagValues(`../${prefix}Make`, match.path)?.at(0)?.value as string;
|
||||
const model = extractFlagValues(`../${prefix}Model`, match.path)?.at(0)?.value as string;
|
||||
if (make) {
|
||||
makeAndModel += getExifValue(make);
|
||||
// Make and model
|
||||
let makeAndModel = "";
|
||||
const make = extractFlagValues(`../${prefix}Make`, match.path)?.at(0)?.value as string;
|
||||
const model = extractFlagValues(`../${prefix}Model`, match.path)?.at(0)?.value as string;
|
||||
if (make) {
|
||||
makeAndModel += getExifValue(make);
|
||||
}
|
||||
if (model) {
|
||||
if (makeAndModel.length > 0) {
|
||||
makeAndModel += ", ";
|
||||
}
|
||||
if (model) {
|
||||
if (makeAndModel.length > 0) {
|
||||
makeAndModel += ", ";
|
||||
}
|
||||
makeAndModel += getExifValue(model);
|
||||
}
|
||||
return makeAndModel;
|
||||
}
|
||||
makeAndModel += getExifValue(model);
|
||||
}
|
||||
return makeAndModel;
|
||||
};
|
||||
|
||||
const ruleScreenshotMeta = (): FlagMatchRule[] => {
|
||||
return [
|
||||
{
|
||||
field:
|
||||
"name",
|
||||
field: "name",
|
||||
match: ["screenshot"],
|
||||
description: "Screen capture",
|
||||
},
|
||||
|
|
@ -260,10 +323,7 @@ const matchFlag = (rules: FlagMatchRule[], path: FlagMatchRulePathSegment[]) =>
|
|||
};
|
||||
|
||||
const extractFlagValues = (flagPath: string, path: FlagMatchRulePathSegment[]): FlagMatchRuleValue[] => {
|
||||
const getValues = (
|
||||
keys: string[],
|
||||
path: FlagMatchRulePathSegment[]
|
||||
): FlagMatchRuleValue[] | undefined => {
|
||||
const getValues = (keys: string[], path: FlagMatchRulePathSegment[]): FlagMatchRuleValue[] | undefined => {
|
||||
if (keys.length == 0 || path.length == 0) return undefined;
|
||||
|
||||
const o = path[0].object;
|
||||
|
|
@ -289,7 +349,13 @@ const extractFlagValues = (flagPath: string, path: FlagMatchRulePathSegment[]):
|
|||
}
|
||||
|
||||
let nextObject = o[key];
|
||||
let omatches: any[] = nextObject ? (Array.isArray(nextObject) ? nextObject : hasBracket ? Object.values(nextObject) : [nextObject]) : [];
|
||||
let omatches: any[] = nextObject
|
||||
? Array.isArray(nextObject)
|
||||
? nextObject
|
||||
: hasBracket
|
||||
? Object.values(nextObject)
|
||||
: [nextObject]
|
||||
: [];
|
||||
|
||||
// Any constraints controlling what array object(s) to consider?
|
||||
if (optionalConstraint) {
|
||||
|
|
@ -321,7 +387,7 @@ const extractFlagValues = (flagPath: string, path: FlagMatchRulePathSegment[]):
|
|||
return { value: oin, path: [{ object: oin, path: key + (omatches.length > 1 ? `[${i}]` : "") }, ...path] };
|
||||
});
|
||||
} else if (omatches.length == 1) {
|
||||
return getValues(keys.slice(1), [{object: omatches[0], path: key}, ...path]);
|
||||
return getValues(keys.slice(1), [{ object: omatches[0], path: key }, ...path]);
|
||||
} else {
|
||||
return omatches.reduce((res: FlagMatchRuleValue[] | undefined, oin: any, i: number) => {
|
||||
let matches = getValues(keys.slice(1), [{ object: oin, path: key + "[" + i + "]" }, ...path]);
|
||||
|
|
@ -354,20 +420,19 @@ const getFirstWithData = (flagValues: FlagValue[], path: FlagMatchRulePathSegmen
|
|||
for (let idx = 0; idx < flagValues.length; idx++) {
|
||||
const result = extractFlagValues(flagValues[idx].path, path);
|
||||
if (result.length > 0) {
|
||||
first = result[0]
|
||||
first = result[0];
|
||||
transform = flagValues[idx].transform;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (first && first.value as string) {
|
||||
if (first && (first.value as string)) {
|
||||
try {
|
||||
let val = first.value as string;
|
||||
if (val && transform) {
|
||||
val = transform(val, first);
|
||||
}
|
||||
return val;
|
||||
} catch (error) {
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
|
@ -382,21 +447,33 @@ const getFirstWithDataAsDate = (flagValues: FlagValue[], path: FlagMatchRulePath
|
|||
date = utils.parseExifDate(val);
|
||||
}
|
||||
return date;
|
||||
} catch (error) {
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const getMultiple = (flagValues: FlagValue[], path: FlagMatchRulePathSegment[]): (string | undefined)[] => {
|
||||
let results: (string | undefined)[] = new Array(flagValues.length);
|
||||
for (let idx = 0; idx < flagValues.length; idx++) {
|
||||
const result = extractFlagValues(flagValues[idx].path, path);
|
||||
if (result.length > 0) {
|
||||
results[idx] = result[0].value as string;
|
||||
} else {
|
||||
results[idx] = undefined;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
export const mediaMetadataToMediaInterventionFlags = (mediaMetadata: MediaMetadata): MediaInterventionFlags => {
|
||||
return {
|
||||
creationDate: mediaMetadata.creationDate,
|
||||
generator: mediaMetadata.generator,
|
||||
modified: mediaMetadata.edits && mediaMetadata.edits.length > 0,
|
||||
containsC2PA: mediaMetadata.containsC2PA,
|
||||
containsEXIF: mediaMetadata.containsEXIF
|
||||
}
|
||||
}
|
||||
containsEXIF: mediaMetadata.containsEXIF,
|
||||
};
|
||||
};
|
||||
|
||||
export const extractMediaMetadata = (proof?: Proof): MediaMetadata | undefined => {
|
||||
if (!proof) return undefined;
|
||||
|
|
@ -410,7 +487,7 @@ export const extractMediaMetadata = (proof?: Proof): MediaMetadata | undefined =
|
|||
valid = results.failure.length == 0 && results.success.length > 0;
|
||||
}
|
||||
|
||||
const rootMatchPath = [{object: proof, path: ""}];
|
||||
const rootMatchPath = [{ object: proof, path: "" }];
|
||||
|
||||
let source: string | undefined = undefined;
|
||||
let dateCreated: Date | undefined = undefined;
|
||||
|
|
@ -431,18 +508,28 @@ export const extractMediaMetadata = (proof?: Proof): MediaMetadata | undefined =
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
let generator: MediaMetadataGenerator = "unknown";
|
||||
let generatorSource: MediaMetadataPropertySource | undefined = undefined;
|
||||
|
||||
let digitalSourceType = extractFlagValues("integrity/c2pa/manifest_info/manifests[]/assertions[label=c2pa.actions|c2pa.actions.v2]/data/actions[action=c2pa.created]/digitalSourceType", rootMatchPath)?.at(0)?.value as string;
|
||||
let digitalSourceType = extractFlagValues(
|
||||
"integrity/c2pa/manifest_info/manifests[]/assertions[label=c2pa.actions|c2pa.actions.v2]/data/actions[action=c2pa.created]/digitalSourceType",
|
||||
rootMatchPath
|
||||
)?.at(0)?.value as string;
|
||||
if ([C2PASourceTypeScreenCapture].includes(digitalSourceType)) {
|
||||
generator = "screenshot";
|
||||
generatorSource = "c2pa";
|
||||
} else if ([C2PASourceTypeDigitalCapture, C2PASourceTypeComputationalCapture, C2PASourceTypeCompositeCapture].includes(digitalSourceType)) {
|
||||
} else if (
|
||||
[C2PASourceTypeDigitalCapture, C2PASourceTypeComputationalCapture, C2PASourceTypeCompositeCapture].includes(
|
||||
digitalSourceType
|
||||
)
|
||||
) {
|
||||
generator = "camera";
|
||||
generatorSource = "c2pa";
|
||||
} else if ([C2PASourceTypeTrainedAlgorithmicMedia, C2PASourceTypeCompositeWithTrainedAlgorithmicMedia].includes(digitalSourceType)) {
|
||||
} else if (
|
||||
[C2PASourceTypeTrainedAlgorithmicMedia, C2PASourceTypeCompositeWithTrainedAlgorithmicMedia].includes(
|
||||
digitalSourceType
|
||||
)
|
||||
) {
|
||||
generator = "ai";
|
||||
generatorSource = "c2pa";
|
||||
} else if (getFirstWithData(pathsC2PACamera(), rootMatchPath)) {
|
||||
|
|
@ -468,20 +555,41 @@ export const extractMediaMetadata = (proof?: Proof): MediaMetadata | undefined =
|
|||
if (c2paEdits.length > 0) {
|
||||
edits = c2paEdits.map((edit) => {
|
||||
return {
|
||||
editor: getFirstWithData([
|
||||
{path: "softwareAgent/name"},
|
||||
{path: "softwareAgent"},
|
||||
{path: "../../../claim_generator"}
|
||||
], edit.path) ?? "",
|
||||
date: getFirstWithDataAsDate([
|
||||
{path: "when"},
|
||||
{path: "../../metadata/dateTime"},
|
||||
{path: "../../../signature_info/time"}
|
||||
], edit.path)
|
||||
}
|
||||
editor:
|
||||
getFirstWithData(
|
||||
[{ path: "softwareAgent/name" }, { path: "softwareAgent" }, { path: "../../../claim_generator" }],
|
||||
edit.path
|
||||
) ?? "",
|
||||
date: getFirstWithDataAsDate(
|
||||
[{ path: "when" }, { path: "../../metadata/dateTime" }, { path: "../../../signature_info/time" }],
|
||||
edit.path
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Location
|
||||
let location: MediaMetadataLocation | undefined = undefined;
|
||||
let locationSource: MediaMetadataPropertySource = "c2pa"
|
||||
let [lat, lon, latref, lonref] = getMultiple(pathsC2PALocation(), rootMatchPath);
|
||||
if (!lat || !lon) {
|
||||
[lat, lon, latref, lonref] = getMultiple(pathsExifLocation(), rootMatchPath);
|
||||
locationSource = "exif"
|
||||
}
|
||||
if (lat && lon) {
|
||||
try {
|
||||
const latitude = toDegrees(getExifValue(lat), getExifValue(latref ?? ""));
|
||||
const longitude = toDegrees(getExifValue(lon), getExifValue(lonref ?? ""));
|
||||
if (latitude && longitude && latitude.length > 0 && longitude.length > 0) {
|
||||
location = {
|
||||
latitude,
|
||||
longitude,
|
||||
source: "exif",
|
||||
};
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
// Do we have any data? Else, return "undefined", we don't just want to send an object with all defaults.
|
||||
if (source === undefined && dateCreated === undefined && generator === "unknown" && (!edits || edits.length == 0)) {
|
||||
return undefined;
|
||||
|
|
@ -495,7 +603,8 @@ export const extractMediaMetadata = (proof?: Proof): MediaMetadata | undefined =
|
|||
generatorSource: generatorSource,
|
||||
edits: edits,
|
||||
containsC2PA: proof.integrity?.c2pa !== undefined,
|
||||
containsEXIF: proof.integrity?.exif !== undefined
|
||||
containsEXIF: proof.integrity?.exif !== undefined,
|
||||
location: location,
|
||||
};
|
||||
return flags;
|
||||
} catch (error) {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue