mirror of
https://github.com/ente-io/ente.git
synced 2025-08-08 07:28:26 +00:00
Parse
This commit is contained in:
parent
9e48010ee6
commit
cfea740511
@ -1,4 +1,5 @@
|
||||
import { haveWindow } from "@/base/env";
|
||||
import { type Location } from "@/base/location";
|
||||
import { styled } from "@mui/material";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { MapButton } from "./MapButton";
|
||||
@ -29,7 +30,7 @@ const MapBoxEnableContainer = styled(MapBoxContainer)`
|
||||
`;
|
||||
|
||||
interface MapBoxProps {
|
||||
location: { latitude: number; longitude: number };
|
||||
location: Location;
|
||||
mapEnabled: boolean;
|
||||
openUpdateMapConfirmationDialog: () => void;
|
||||
}
|
||||
|
@ -2,9 +2,11 @@ import { EnteDrawer } from "@/base/components/EnteDrawer";
|
||||
import { Titlebar } from "@/base/components/Titlebar";
|
||||
import { EllipsizedTypography } from "@/base/components/Typography";
|
||||
import { nameAndExtension } from "@/base/file";
|
||||
import type { Location } from "@/base/location";
|
||||
import log from "@/base/log";
|
||||
import type { ParsedMetadata } from "@/media/file-metadata";
|
||||
import {
|
||||
fileLocation,
|
||||
getUICreationDate,
|
||||
updateRemotePublicMagicMetadata,
|
||||
type ParsedMetadataDate,
|
||||
@ -97,16 +99,9 @@ export const FileInfo: React.FC<FileInfoProps> = ({
|
||||
const [openRawExif, setOpenRawExif] = useState(false);
|
||||
|
||||
const location = useMemo(() => {
|
||||
if (file && file.metadata) {
|
||||
if (
|
||||
(file.metadata.latitude || file.metadata.latitude === 0) &&
|
||||
!(file.metadata.longitude === 0 && file.metadata.latitude === 0)
|
||||
) {
|
||||
return {
|
||||
latitude: file.metadata.latitude,
|
||||
longitude: file.metadata.longitude,
|
||||
};
|
||||
}
|
||||
if (file) {
|
||||
const location = fileLocation(file);
|
||||
if (location) return location;
|
||||
}
|
||||
return exif?.parsed?.location;
|
||||
}, [file, exif]);
|
||||
@ -181,7 +176,7 @@ export const FileInfo: React.FC<FileInfoProps> = ({
|
||||
!mapEnabled ||
|
||||
publicCollectionGalleryContext.accessedThroughSharedURL ? (
|
||||
<Link
|
||||
href={getOpenStreetMapLink(location)}
|
||||
href={openStreetMapLink(location)}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
sx={{ fontWeight: "bold" }}
|
||||
@ -205,7 +200,7 @@ export const FileInfo: React.FC<FileInfoProps> = ({
|
||||
}
|
||||
customEndButton={
|
||||
<CopyButton
|
||||
code={getOpenStreetMapLink(location)}
|
||||
code={openStreetMapLink(location)}
|
||||
color="secondary"
|
||||
size="medium"
|
||||
/>
|
||||
@ -531,11 +526,8 @@ const BasicDeviceCamera: React.FC<{ parsedExif: ExifInfo }> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const getOpenStreetMapLink = (location: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
}) =>
|
||||
`https://www.openstreetmap.org/?mlat=${location.latitude}&mlon=${location.longitude}#map=15/${location.latitude}/${location.longitude}`;
|
||||
const openStreetMapLink = ({ latitude, longitude }: Location) =>
|
||||
`https://www.openstreetmap.org/?mlat=${latitude}&mlon=${longitude}#map=15/${latitude}/${longitude}`;
|
||||
|
||||
interface RawExifProps {
|
||||
open: boolean;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ensureElectron } from "@/base/electron";
|
||||
import log from "@/base/log";
|
||||
import type { Metadata } from "@/media/file-metadata";
|
||||
import { fileLocation, type Metadata } from "@/media/file-metadata";
|
||||
import { FileType } from "@/media/file-type";
|
||||
import { decodeLivePhoto } from "@/media/live-photo";
|
||||
import downloadManager from "@/new/photos/services/download";
|
||||
@ -1383,6 +1383,7 @@ const getGoogleLikeMetadataFile = (fileExportName: string, file: EnteFile) => {
|
||||
(metadata.modificationTime ?? metadata.creationTime) / 1000000,
|
||||
);
|
||||
const captionValue: string = file?.pubMagicMetadata?.data?.caption;
|
||||
const geoData = fileLocation(file);
|
||||
return JSON.stringify(
|
||||
{
|
||||
title: fileExportName,
|
||||
@ -1395,10 +1396,7 @@ const getGoogleLikeMetadataFile = (fileExportName: string, file: EnteFile) => {
|
||||
timestamp: modificationTime,
|
||||
formatted: formatDateTimeShort(modificationTime * 1000),
|
||||
},
|
||||
geoData: {
|
||||
latitude: metadata.latitude,
|
||||
longitude: metadata.longitude,
|
||||
},
|
||||
geoData,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
import { ensureElectron } from "@/base/electron";
|
||||
import { nameAndExtension } from "@/base/file";
|
||||
import { type Location } from "@/base/location";
|
||||
import log from "@/base/log";
|
||||
import type { UploadItem } from "@/new/photos/services/upload/types";
|
||||
import { readStream } from "@/new/photos/utils/native-stream";
|
||||
@ -16,7 +17,7 @@ import { readStream } from "@/new/photos/utils/native-stream";
|
||||
export interface ParsedMetadataJSON {
|
||||
creationTime?: number;
|
||||
modificationTime?: number;
|
||||
location?: { latitude: number; longitude: number };
|
||||
location?: Location;
|
||||
}
|
||||
|
||||
export const MAX_FILE_NAME_LENGTH_GOOGLE_EXPORT = 46;
|
||||
@ -112,53 +113,60 @@ const parseMetadataJSONText = (text: string) => {
|
||||
|
||||
const parsedMetadataJSON: ParsedMetadataJSON = {};
|
||||
|
||||
// The metadata provided by Google does not include the time zone where the
|
||||
// photo was taken, it only has an epoch seconds value.
|
||||
if (
|
||||
metadataJSON["photoTakenTime"] &&
|
||||
metadataJSON["photoTakenTime"]["timestamp"]
|
||||
) {
|
||||
parsedMetadataJSON.creationTime =
|
||||
metadataJSON["photoTakenTime"]["timestamp"] * 1e6;
|
||||
} else if (
|
||||
metadataJSON["creationTime"] &&
|
||||
metadataJSON["creationTime"]["timestamp"]
|
||||
) {
|
||||
parsedMetadataJSON.creationTime =
|
||||
metadataJSON["creationTime"]["timestamp"] * 1e6;
|
||||
}
|
||||
parsedMetadataJSON.creationTime =
|
||||
parseGTTimestamp(metadataJSON["photoTakenTime"]) ??
|
||||
parseGTTimestamp(metadataJSON["creationTime"]);
|
||||
|
||||
if (
|
||||
metadataJSON["modificationTime"] &&
|
||||
metadataJSON["modificationTime"]["timestamp"]
|
||||
) {
|
||||
parsedMetadataJSON.modificationTime =
|
||||
metadataJSON["modificationTime"]["timestamp"] * 1e6;
|
||||
}
|
||||
parsedMetadataJSON.modificationTime = parseGTTimestamp(
|
||||
metadataJSON["modificationTime"],
|
||||
);
|
||||
|
||||
if (
|
||||
metadataJSON["geoData"] &&
|
||||
(metadataJSON["geoData"]["latitude"] !== 0.0 ||
|
||||
metadataJSON["geoData"]["longitude"] !== 0.0)
|
||||
) {
|
||||
parsedMetadataJSON.location = {
|
||||
latitude: metadataJSON["geoData"]["latitude"],
|
||||
longitude: metadataJSON["geoData"]["longitude"],
|
||||
};
|
||||
} else if (
|
||||
metadataJSON["geoDataExif"] &&
|
||||
(metadataJSON["geoDataExif"]["latitude"] !== 0.0 ||
|
||||
metadataJSON["geoDataExif"]["longitude"] !== 0.0)
|
||||
) {
|
||||
parsedMetadataJSON.location = {
|
||||
latitude: metadataJSON["geoDataExif"]["latitude"],
|
||||
longitude: metadataJSON["geoDataExif"]["longitude"],
|
||||
};
|
||||
}
|
||||
parsedMetadataJSON.location =
|
||||
parseGTLocation(metadataJSON["geoData"]) ??
|
||||
parseGTLocation(metadataJSON["geoDataExif"]);
|
||||
|
||||
return parsedMetadataJSON;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a nullish epoch seconds timestamp from a field in a Google Takeout
|
||||
* JSON, converting it into epoch microseconds if it is found.
|
||||
*
|
||||
* Note that the metadata provided by Google does not include the time zone
|
||||
* where the photo was taken, it only has an epoch seconds value.
|
||||
*/
|
||||
const parseGTTimestamp = (o: unknown) => {
|
||||
if (
|
||||
o &&
|
||||
typeof o == "object" &&
|
||||
"timestamp" in o &&
|
||||
typeof o.timestamp == "number"
|
||||
) {
|
||||
const { timestamp } = o;
|
||||
if (timestamp) return timestamp * 1e6;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* A custom parser (instead of parseLatLng) that retains the existing behaviour
|
||||
* of ignoring (0, 0) lat lng pairs when reading Google Takeout JSONs.
|
||||
*/
|
||||
const parseGTLocation = (o: unknown) => {
|
||||
if (
|
||||
o &&
|
||||
typeof o == "object" &&
|
||||
"latitude" in o &&
|
||||
typeof o.latitude == "number" &&
|
||||
"longitude" in o &&
|
||||
typeof o.longitude == "number"
|
||||
) {
|
||||
const { latitude, longitude } = o;
|
||||
if (latitude !== 0 || longitude !== 0) return { latitude, longitude };
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the matching entry (if any) from {@link parsedMetadataJSONMap} for the
|
||||
* {@link fileName} and {@link collectionID} combination.
|
||||
|
25
web/packages/base/location.ts
Normal file
25
web/packages/base/location.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { nullToUndefined } from "@/utils/transform";
|
||||
|
||||
/**
|
||||
* A location, represented as a (latitude, longitude) pair.
|
||||
*/
|
||||
export interface Location {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a pair of nullish latitude and longitude values into a
|
||||
* {@link Location} if both of them are present.
|
||||
*/
|
||||
export const parseLatLng = (
|
||||
latitudeN: number | undefined | null,
|
||||
longitudeN: number | undefined | null,
|
||||
): Location | undefined => {
|
||||
const latitude = nullToUndefined(latitudeN);
|
||||
const longitude = nullToUndefined(longitudeN);
|
||||
|
||||
if (latitude === undefined || longitude === undefined) return undefined;
|
||||
|
||||
return { latitude, longitude };
|
||||
};
|
@ -1,7 +0,0 @@
|
||||
/**
|
||||
* A location, represented as a (latitude, longitude) pair.
|
||||
*/
|
||||
export interface Location {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
}
|
@ -1,14 +1,13 @@
|
||||
import { decryptMetadataJSON, encryptMetadataJSON } from "@/base/crypto";
|
||||
import { authenticatedRequestHeaders, ensureOk } from "@/base/http";
|
||||
import type { Location } from "@/base/location";
|
||||
import { apiURL } from "@/base/origins";
|
||||
import type { Location } from "@/base/types";
|
||||
import {
|
||||
type EnteFile,
|
||||
type FilePublicMagicMetadata,
|
||||
} from "@/new/photos/types/file";
|
||||
import { mergeMetadata1 } from "@/new/photos/utils/file";
|
||||
import { ensure } from "@/utils/ensure";
|
||||
import { nullToUndefined } from "@/utils/transform";
|
||||
import { z } from "zod";
|
||||
import { FileType } from "./file-type";
|
||||
|
||||
@ -773,11 +772,4 @@ export const fileLocation = (enteFile: EnteFile): Location | undefined => {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!enteFile.metadata) return undefined;
|
||||
|
||||
const latitude = nullToUndefined(enteFile.metadata.latitude);
|
||||
const longitude = nullToUndefined(enteFile.metadata.longitude);
|
||||
|
||||
if (latitude === undefined || longitude === undefined) return undefined;
|
||||
|
||||
return { latitude, longitude };
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user