refactoring moved videoMetadata extraction to seperate service
This commit is contained in:
parent
e49a7f9ec9
commit
a98d35ad66
|
@ -5,6 +5,7 @@ import piexif from 'piexifjs';
|
|||
import { FileTypeInfo } from 'types/upload';
|
||||
import { logError } from 'utils/sentry';
|
||||
import { ParsedExtractedMetadata } from './metadataService';
|
||||
import { getUNIXTime } from 'utils/upload';
|
||||
|
||||
const EXIF_TAGS_NEEDED = [
|
||||
'DateTimeOriginal',
|
||||
|
@ -128,22 +129,6 @@ export async function getRawExif(
|
|||
return exifData;
|
||||
}
|
||||
|
||||
export function getUNIXTime(dateTime: Date) {
|
||||
try {
|
||||
if (!dateTime) {
|
||||
return null;
|
||||
}
|
||||
const unixTime = dateTime.getTime() * 1000;
|
||||
if (unixTime <= 0) {
|
||||
return null;
|
||||
} else {
|
||||
return unixTime;
|
||||
}
|
||||
} catch (e) {
|
||||
logError(e, 'getUNIXTime failed', { dateTime });
|
||||
}
|
||||
}
|
||||
|
||||
function getEXIFLocation(exifData): Location {
|
||||
if (!exifData.latitude || !exifData.longitude) {
|
||||
return NULL_LOCATION;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { FILE_TYPE } from 'constants/file';
|
||||
import { logError } from 'utils/sentry';
|
||||
import { getExifData, getUNIXTime } from './exifService';
|
||||
import { getExifData } from './exifService';
|
||||
import {
|
||||
Metadata,
|
||||
ParsedMetadataJSON,
|
||||
|
@ -9,15 +9,7 @@ import {
|
|||
} from 'types/upload';
|
||||
import { NULL_LOCATION } from 'constants/upload';
|
||||
import { splitFilenameAndExtension } from 'utils/file';
|
||||
import ffmpegService from 'services/ffmpegService';
|
||||
|
||||
enum VideoMetadata {
|
||||
CREATION_TIME = 'creation_time',
|
||||
APPLE_CONTENT_IDENTIFIER = 'com.apple.quicktime.content.identifier',
|
||||
APPLE_LIVE_PHOTO_IDENTIFIER = 'com.apple.quicktime.live-photo.auto',
|
||||
APPLE_CREATION_DATE = 'com.apple.quicktime.creationdate',
|
||||
APPLE_LOCATION_ISO = 'com.apple.quicktime.location.ISO6709',
|
||||
}
|
||||
import { getVideoMetadata } from './videoMetadataService';
|
||||
|
||||
interface ParsedMetadataJSONWithTitle {
|
||||
title: string;
|
||||
|
@ -43,7 +35,7 @@ export async function extractMetadata(
|
|||
if (fileTypeInfo.fileType === FILE_TYPE.IMAGE) {
|
||||
extractedMetadata = await getExifData(receivedFile, fileTypeInfo);
|
||||
} else if (fileTypeInfo.fileType === FILE_TYPE.VIDEO) {
|
||||
extractedMetadata = await ffmpegService.extractMetadata(receivedFile);
|
||||
extractedMetadata = await getVideoMetadata(receivedFile);
|
||||
}
|
||||
|
||||
const metadata: Metadata = {
|
||||
|
@ -135,59 +127,3 @@ export async function parseMetadataJSON(
|
|||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
export function parseFFmpegExtractedMetadata(encodedMetadata: Uint8Array) {
|
||||
const metadataString = new TextDecoder().decode(encodedMetadata);
|
||||
const metadataPropertyArray = metadataString.split('\n');
|
||||
const metadataKeyValueArray = metadataPropertyArray.map((property) =>
|
||||
property.split('=')
|
||||
);
|
||||
const validKeyValuePairs = metadataKeyValueArray.filter(
|
||||
(keyValueArray) => keyValueArray.length === 2
|
||||
) as Array<[string, string]>;
|
||||
|
||||
const metadataMap = Object.fromEntries(validKeyValuePairs);
|
||||
|
||||
const location = parseAppleISOLocation(
|
||||
metadataMap[VideoMetadata.APPLE_LOCATION_ISO]
|
||||
);
|
||||
|
||||
const creationTime = parseCreationTime(
|
||||
metadataMap[VideoMetadata.APPLE_CREATION_DATE] ??
|
||||
metadataMap[VideoMetadata.CREATION_TIME]
|
||||
);
|
||||
const parsedMetadata: ParsedExtractedMetadata = {
|
||||
creationTime,
|
||||
location: {
|
||||
latitude: location.latitude,
|
||||
longitude: location.longitude,
|
||||
},
|
||||
};
|
||||
return parsedMetadata;
|
||||
}
|
||||
|
||||
function parseAppleISOLocation(isoLocation: string) {
|
||||
let location = NULL_LOCATION;
|
||||
if (isoLocation) {
|
||||
const [latitude, longitude] = isoLocation
|
||||
.match(/(\+|-)\d+\.*\d+/g)
|
||||
.map((x) => parseFloat(x));
|
||||
|
||||
location = { latitude, longitude };
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
function parseCreationTime(creationTime: string) {
|
||||
let dateTime = null;
|
||||
if (creationTime) {
|
||||
dateTime = new Date(creationTime);
|
||||
if (isNaN(dateTime.getTime())) {
|
||||
dateTime = null;
|
||||
}
|
||||
}
|
||||
if (dateTime) {
|
||||
dateTime = getUNIXTime(dateTime);
|
||||
}
|
||||
return dateTime;
|
||||
}
|
||||
|
|
66
src/services/upload/videoMetadataService.ts
Normal file
66
src/services/upload/videoMetadataService.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
import { NULL_LOCATION } from 'constants/upload';
|
||||
import ffmpegService from 'services/ffmpegService';
|
||||
import { getUNIXTime } from 'utils/upload';
|
||||
import { ParsedExtractedMetadata } from './metadataService';
|
||||
|
||||
enum VideoMetadata {
|
||||
CREATION_TIME = 'creation_time',
|
||||
APPLE_CONTENT_IDENTIFIER = 'com.apple.quicktime.content.identifier',
|
||||
APPLE_LIVE_PHOTO_IDENTIFIER = 'com.apple.quicktime.live-photo.auto',
|
||||
APPLE_CREATION_DATE = 'com.apple.quicktime.creationdate',
|
||||
APPLE_LOCATION_ISO = 'com.apple.quicktime.location.ISO6709',
|
||||
}
|
||||
|
||||
export function getVideoMetadata(file: File) {
|
||||
return ffmpegService.extractMetadata(file);
|
||||
}
|
||||
|
||||
export function parseFFmpegExtractedMetadata(encodedMetadata: Uint8Array) {
|
||||
const metadataString = new TextDecoder().decode(encodedMetadata);
|
||||
const metadataPropertyArray = metadataString.split('\n');
|
||||
const metadataKeyValueArray = metadataPropertyArray.map((property) =>
|
||||
property.split('=')
|
||||
);
|
||||
const validKeyValuePairs = metadataKeyValueArray.filter(
|
||||
(keyValueArray) => keyValueArray.length === 2
|
||||
) as Array<[string, string]>;
|
||||
|
||||
const metadataMap = Object.fromEntries(validKeyValuePairs);
|
||||
|
||||
const location = parseAppleISOLocation(
|
||||
metadataMap[VideoMetadata.APPLE_LOCATION_ISO]
|
||||
);
|
||||
|
||||
const creationTime = parseCreationTime(
|
||||
metadataMap[VideoMetadata.APPLE_CREATION_DATE] ??
|
||||
metadataMap[VideoMetadata.CREATION_TIME]
|
||||
);
|
||||
const parsedMetadata: ParsedExtractedMetadata = {
|
||||
creationTime,
|
||||
location: {
|
||||
latitude: location.latitude,
|
||||
longitude: location.longitude,
|
||||
},
|
||||
};
|
||||
return parsedMetadata;
|
||||
}
|
||||
|
||||
function parseAppleISOLocation(isoLocation: string) {
|
||||
let location = NULL_LOCATION;
|
||||
if (isoLocation) {
|
||||
const [latitude, longitude] = isoLocation
|
||||
.match(/(\+|-)\d+\.*\d+/g)
|
||||
.map((x) => parseFloat(x));
|
||||
|
||||
location = { latitude, longitude };
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
function parseCreationTime(creationTime: string) {
|
||||
let dateTime = null;
|
||||
if (creationTime) {
|
||||
dateTime = getUNIXTime(new Date(creationTime));
|
||||
}
|
||||
return dateTime;
|
||||
}
|
|
@ -49,3 +49,15 @@ export function segregateMetadataAndMediaFiles(
|
|||
});
|
||||
return { mediaFiles, metadataJSONFiles };
|
||||
}
|
||||
|
||||
export function getUNIXTime(dateTime: Date) {
|
||||
if (!dateTime || isNaN(dateTime.getTime())) {
|
||||
return null;
|
||||
}
|
||||
const unixTime = dateTime.getTime() * 1000;
|
||||
if (unixTime <= 0) {
|
||||
return null;
|
||||
} else {
|
||||
return unixTime;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue