Skip to content
Snippets Groups Projects
Verified Commit fa13e4b3 authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Implement improved EXIF support

parent 55dfe092
No related branches found
No related tags found
1 merge request!1Replace entire project structure
Pipeline #2580 failed
img {
max-width: 200px;
max-height: 200px;
}
......@@ -7,9 +7,17 @@ export interface Image {
updated_at: string,
original_name: string,
mime_type: string,
state: string,
state: ImageState,
metadata: {
[key: string]: string
},
url: string,
}
export enum ImageState {
CREATED = "created",
QUEUED = "queued",
IN_PROGRESS = "in_progress",
DONE = "done",
ERROR = "error"
}
......@@ -9,14 +9,12 @@ export default function ImageList() {
<div>
<p>{status}</p>
<p>{error as string}</p>
<ul>
{data?.map(image => (
<ImageView
key={image.id}
image={image}
/>
))}
</ul>
{data?.map(image => (
<ImageView
key={image.id}
image={image}
/>
))}
</div>
);
}
import React, {Fragment} from "react";
import {ImageMetadata, ratioToTime} from "../metadata/ImageMetadata";
import {ratioToFloat} from "../metadata/Ratio";
import {ExposureMode} from "../metadata/ExposureMode";
import {ExposureProgram} from "../metadata/ExposureProgram";
import {LightSource} from "../metadata/LightSource";
import {MeteringMode} from "../metadata/MeteringMode";
import {WhiteBalance} from "../metadata/WhiteBalance";
import {SceneMode} from "../metadata/SceneMode";
import {ContrastProcessing} from "../metadata/ContrastProcessing";
import {SharpnessProcessing} from "../metadata/SharpnessProcessing";
import {SubjectDistanceRange} from "../metadata/SubjectDistanceRange";
import {BrightnessMedium, Camera, Copyright, Event, Exposure, PhotoCamera, ZoomIn} from "@material-ui/icons";
import {AngleAcute, ArrowExpandHorizontal, Blur, CameraTimer, Flash, WhiteBalanceIncandescent} from "mdi-material-ui";
import {FlashMode} from "../metadata/FlashMode";
import {ListItem, ListItemIcon, ListItemText} from "@material-ui/core";
export interface ImageMetadataViewProps {
metadata: ImageMetadata
}
export default function ImageMetadataView({metadata}: ImageMetadataViewProps) {
return (
<Fragment>
{metadata.make !== undefined && (
<ListItem dense>
<ListItemIcon><PhotoCamera/></ListItemIcon>
<ListItemText primary="Make" secondary={metadata.make}/>
</ListItem>
)}
{metadata.model !== undefined && (
<ListItem dense>
<ListItemText inset primary="Model" secondary={metadata.model}/>
</ListItem>
)}
{metadata.software !== undefined && (
<ListItem dense>
<ListItemText inset primary="Software" secondary={metadata.software}/>
</ListItem>
)}
{metadata.copyright !== undefined && (
<ListItem dense>
<ListItemIcon><Copyright/></ListItemIcon>
<ListItemText primary="Copyright" secondary={metadata.copyright}/>
</ListItem>
)}
{metadata.dateTimeCreated !== undefined && (
<ListItem dense>
<ListItemIcon><Event/></ListItemIcon>
<ListItemText primary="Created At" secondary={metadata.dateTimeCreated.toISOString()}/>
</ListItem>
)}
{metadata.dateTimeDigitized !== undefined && (
<ListItem dense>
<ListItemText inset primary="Digitized At"
secondary={metadata.dateTimeDigitized.toISOString()}/>
</ListItem>
)}
{metadata.dateTimeOriginal !== undefined && (
<ListItem dense>
<ListItemText inset primary="Shot At" secondary={metadata.dateTimeOriginal.toISOString()}/>
</ListItem>
)}
{metadata.digitalZoomRatio !== undefined && (
<ListItem dense>
<ListItemIcon><ZoomIn/></ListItemIcon>
<ListItemText primary="Zoom" secondary={`${ratioToFloat(metadata.digitalZoomRatio)}x`}/>
</ListItem>
)}
{metadata.focalLength !== undefined && (
<ListItem dense>
<ListItemIcon><AngleAcute/></ListItemIcon>
<ListItemText primary="Focal Length" secondary={`${ratioToFloat(metadata.focalLength)}mm`}/>
</ListItem>
)}
{metadata.focalLength35mm !== undefined && (
<ListItem dense>
<ListItemText inset primary="35mm equivalent"
secondary={`${ratioToFloat(metadata.focalLength35mm)}mm`}/>
</ListItem>
)}
{metadata.shutterSpeed !== undefined && (
<ListItem dense>
<ListItemIcon><CameraTimer/></ListItemIcon>
<ListItemText primary="Shutter Speed" secondary={ratioToTime(metadata.shutterSpeed)}/>
</ListItem>
)}
{metadata.aperture !== undefined && (
<ListItem dense>
<ListItemIcon><Camera/></ListItemIcon>
<ListItemText primary="Aperture" secondary={ratioToFloat(metadata.aperture)}/>
</ListItem>
)}
{metadata.isoSpeedRating !== undefined && (
<ListItem dense>
<ListItemIcon>ISO</ListItemIcon>
<ListItemText primary="ISO" secondary={metadata.isoSpeedRating}/>
</ListItem>
)}
{metadata.exposure !== undefined && (
<ListItem dense>
<ListItemIcon><Exposure/></ListItemIcon>
<ListItemText primary="Exposure" secondary={ratioToFloat(metadata.exposure)}/>
</ListItem>
)}
{metadata.exposureMode !== undefined && (
<ListItem dense>
<ListItemText inset primary="Mode" secondary={ExposureMode[metadata.exposureMode]}/>
</ListItem>
)}
{metadata.exposureProgram !== undefined && (
<ListItem dense>
<ListItemText inset primary="Program" secondary={ExposureProgram[metadata.exposureProgram]}/>
</ListItem>
)}
{metadata.meteringMode !== undefined && (
<ListItem dense>
<ListItemText inset primary="Metering mode" secondary={MeteringMode[metadata.meteringMode]}/>
</ListItem>
)}
{metadata.flash !== undefined && (
<Fragment>
<ListItem dense>
<ListItemIcon><Flash/></ListItemIcon>
<ListItemText primary="Flash"
secondary={metadata.flash.available ? "Available" : "Unavailable"}/>
</ListItem>
<ListItem dense>
<ListItemText inset primary="Fired" secondary={metadata.flash.fired ? "Yes" : "No"}/>
</ListItem>
<ListItem dense>
<ListItemText inset primary="Red Eye Reduction"
secondary={metadata.flash.redEyeReduction ? "Yes" : "No"}/>
</ListItem>
{metadata.flash.mode !== undefined && (
<ListItem dense>
<ListItemText inset primary="Mode" secondary={FlashMode[metadata.flash.mode]}/>
</ListItem>
)}
{metadata.flash.strength !== undefined && (
<ListItem dense>
<ListItemText inset primary="Strength" secondary={`${metadata.flash.strength} BCPS`}/>
</ListItem>
)}
<ListItem dense>
<ListItemText inset primary="Strobe Detection"
secondary={!metadata.flash.strobeDetection.available ? "Unvailable" :
metadata.flash.strobeDetection.detected ? "Strobe detected" :
"No strobe detected"}
/>
</ListItem>
</Fragment>
)}
{metadata.lightSource !== undefined && (
<ListItem dense>
<ListItemIcon><WhiteBalanceIncandescent/></ListItemIcon>
<ListItemText primary="Light Source" secondary={LightSource[metadata.lightSource]}/>
</ListItem>
)}
{metadata.whiteBalance !== undefined && (
<ListItem dense>
<ListItemIcon><WhiteBalanceIncandescent/></ListItemIcon>
<ListItemText primary="White balance" secondary={WhiteBalance[metadata.whiteBalance]}/>
</ListItem>
)}
{metadata.subjectDistance !== undefined && (
<ListItem dense>
<ListItemIcon><ArrowExpandHorizontal/></ListItemIcon>
<ListItemText primary="Subject Distance" secondary={metadata.subjectDistance}/>
</ListItem>
)}
{metadata.subjectDistanceRange !== undefined && (
<ListItem dense>
<ListItemIcon><ArrowExpandHorizontal/></ListItemIcon>
<ListItemText primary="Subject Distance Range"
secondary={SubjectDistanceRange[metadata.subjectDistanceRange]}/>
</ListItem>
)}
{metadata.sceneMode !== undefined && (
<p><b>Scene Mode</b>: {SceneMode[metadata.sceneMode]}</p>
)}
{metadata.contrast !== undefined && (
<ListItem dense>
<ListItemIcon><BrightnessMedium/></ListItemIcon>
<ListItemText primary="Contrast Processing" secondary={ContrastProcessing[metadata.contrast]}/>
</ListItem>
)}
{metadata.sharpness !== undefined && (
<ListItem dense>
<ListItemIcon><Blur/></ListItemIcon>
<ListItemText primary="Sharpness Processing" secondary={SharpnessProcessing[metadata.sharpness]}/>
</ListItem>
)}
</Fragment>
);
}
import {Image} from "../api/model/Image";
import React, {Fragment, useMemo, useState} from "react";
import React, {useMemo, useState} from "react";
import {useUpdateImage} from "../api/useUpdateImage";
import {useDeleteImage} from "../api/useDeleteImage";
import {parseMetadata, ratioToTime} from "../metadata/ImageMetadata";
import {ratioToFloat} from "../metadata/Ratio";
import {ExposureMode} from "../metadata/ExposureMode";
import {ExposureProgram} from "../metadata/ExposureProgram";
import {LightSource} from "../metadata/LightSource";
import {MeteringMode} from "../metadata/MeteringMode";
import {WhiteBalance} from "../metadata/WhiteBalance";
import {SceneMode} from "../metadata/SceneMode";
import {ContrastProcessing} from "../metadata/ContrastProcessing";
import {SharpnessProcessing} from "../metadata/SharpnessProcessing";
import {SubjectDistanceRange} from "../metadata/SubjectDistanceRange";
import {BrightnessMedium, Camera, Copyright, Event, Exposure, PhotoCamera, ZoomIn} from "@material-ui/icons";
import {AngleAcute, ArrowExpandHorizontal, Blur, CameraTimer, Flash, WhiteBalanceIncandescent} from "mdi-material-ui";
import {FlashMode} from "../metadata/FlashMode";
import {parseMetadata} from "../metadata/ImageMetadata";
import ImageMetadataView from "./ImageMetadataView";
import {Button, List, ListItem, ListItemIcon, ListItemText, TextField} from "@material-ui/core";
import {Event, Info} from "@material-ui/icons";
import {File, Tag} from "mdi-material-ui";
export interface ImageProps {
image: Image
......@@ -37,135 +28,70 @@ export default function ImageView({image}: ImageProps) {
<p>RemoveError: {JSON.stringify(removeError, null, 2)}</p>
<p>UpdateLoading: {JSON.stringify(updateLoading, null, 2)}</p>
<p>RemoveLoading: {JSON.stringify(removeLoading, null, 2)}</p>
<p>{image.id}</p>
<p>{image.owner}</p>
<label>
Title
<input
type="text"
value={title}
onChange={({target: {value}}) =>
setTitle(value)}
/>
</label>
<br/>
<label>
Description
<input
type="text"
value={description}
onChange={({target: {value}}) =>
setDescription(value)}
/>
</label>
<p>{image.original_name}</p>
<p>{image.mime_type}</p>
<p>{image.created_at}</p>
<p>{image.updated_at}</p>
<p>{image.state}</p>
<h3>Metadata</h3>
{metadata.make !== undefined && (
<p><b>Make</b>: {metadata.make}</p>
)}
{metadata.model !== undefined && (
<p><PhotoCamera/><b>Model</b>: {metadata.model}</p>
)}
{metadata.software !== undefined && (
<p><b>Software</b>: {metadata.software}</p>
)}
{metadata.copyright !== undefined && (
<p><Copyright/><b>Copyright</b>: {metadata.copyright}</p>
)}
{metadata.dateTimeCreated !== undefined && (
<p><Event/><b>DateTime Created</b>: {metadata.dateTimeCreated?.toISOString()}</p>
)}
{metadata.dateTimeDigitized !== undefined && (
<p><Event/><b>DateTime Digitized</b>: {metadata.dateTimeDigitized?.toISOString()}</p>
)}
{metadata.dateTimeOriginal !== undefined && (
<p><Event/><b>DateTime Original</b>: {metadata.dateTimeOriginal?.toISOString()}</p>
)}
{metadata.digitalZoomRatio !== undefined && (
<p><ZoomIn/><b>Digital Zoom</b>: {ratioToFloat(metadata.digitalZoomRatio)}</p>
)}
{metadata.exposure !== undefined && (
<p><Exposure/><b>Exposure</b>: {ratioToFloat(metadata.exposure)}</p>
)}
{metadata.exposureMode !== undefined && (
<p><Exposure/><b>Exposure Mode</b>: {ExposureMode[metadata.exposureMode]}</p>
)}
{metadata.exposureProgram !== undefined && (
<p><Exposure/><b>Exposure Program</b>: {ExposureProgram[metadata.exposureProgram]}</p>
)}
{metadata.shutterSpeed !== undefined && (
<p><CameraTimer/><b>Shutter Speed</b>: {ratioToTime(metadata.shutterSpeed)}</p>
)}
{metadata.aperture !== undefined && (
<p><Camera/><b>Aperture</b>: {ratioToFloat(metadata.aperture)}</p>
)}
{metadata.focalLength !== undefined && (
<p><AngleAcute/><b>Focal Length</b>: {ratioToFloat(metadata.focalLength)}mm</p>
)}
{metadata.focalLength35mm !== undefined && (
<p><AngleAcute/><b>Focal Length (35mm equivalent)</b>: {ratioToFloat(metadata.focalLength35mm)}mm</p>
)}
{metadata.isoSpeedRating !== undefined && (
<p><b>ISO</b>: {metadata.isoSpeedRating}</p>
)}
{metadata.flash !== undefined && (
<Fragment>
<p><Flash/><b>Flash</b></p>
<p><b>Available</b>: {metadata.flash.available ? "Yes" : "No"}</p>
<p><b>Fired</b>: {metadata.flash.fired ? "Yes" : "No"}</p>
<p><b>Red Eye Reduction</b>: {metadata.flash.redEyeReduction ? "Yes" : "No"}</p>
<p><b>Strobe Detection Available</b>: {metadata.flash.strobeDetection.available ? "Yes" : "No"}</p>
<p><b>Strobe Detection Used</b>: {metadata.flash.strobeDetection.detected ? "Yes" : "No"}</p>
{metadata.flash.mode !== undefined && (
<p><b>Flash Mode</b>: {FlashMode[metadata.flash.mode]}</p>
)}
</Fragment>
)}
{metadata.lightSource !== undefined && (
<p><WhiteBalanceIncandescent/><b>Light source</b>: {LightSource[metadata.lightSource]}</p>
)}
{metadata.meteringMode !== undefined && (
<p><b>Metering mode</b>: {MeteringMode[metadata.meteringMode]}</p>
)}
{metadata.whiteBalance !== undefined && (
<p><WhiteBalanceIncandescent/><b>White balance</b>: {WhiteBalance[metadata.whiteBalance]}</p>
)}
{metadata.sceneMode !== undefined && (
<p><b>Scene Mode</b>: {SceneMode[metadata.sceneMode]}</p>
)}
{metadata.contrast !== undefined && (
<p><BrightnessMedium/><b>Contrast Processing</b>: {ContrastProcessing[metadata.contrast]}</p>
)}
{metadata.sharpness !== undefined && (
<p><Blur/><b>Sharpness Processing</b>: {SharpnessProcessing[metadata.sharpness]}</p>
)}
{metadata.subjectDistance !== undefined && (
<p><ArrowExpandHorizontal/><b>Subject Distance</b>: {metadata.subjectDistance}</p>
)}
{metadata.subjectDistanceRange !== undefined && (
<p><ArrowExpandHorizontal/><b>Subject Distance
Range</b>: {SubjectDistanceRange[metadata.subjectDistanceRange]}</p>
)}
<img src={image.url + "t"} alt=""/>
<br/>
<input
type="submit"
value="Save"
<img src={image.url + "l"} alt=""/>
<List dense>
<ListItem dense>
<ListItemIcon><Info/></ListItemIcon>
<ListItemText primary="Id" secondary={image.id}/>
</ListItem>
<ListItem dense>
<ListItemText inset primary="Owner" secondary={image.owner}/>
</ListItem>
<ListItem dense>
<ListItemText inset primary="State" secondary={image.state}/>
</ListItem>
<ListItem dense>
<ListItemIcon><Tag/></ListItemIcon>
{/* TODO: Fix this ugly nesting */}
<ListItemText
primary="Title"
secondary={<TextField
fullWidth
value={title}
onChange={({target: {value}}) =>
setTitle(value)}
/>}
/>
</ListItem>
<ListItem dense>
{/* TODO: Fix this ugly nesting */}
<ListItemText
inset
primary="Description"
secondary={<TextField
fullWidth
value={description}
onChange={({target: {value}}) =>
setDescription(value)}
/>}
/>
</ListItem>
<ListItem dense>
<ListItemIcon><File/></ListItemIcon>
<ListItemText primary="Filename" secondary={image.original_name}/>
</ListItem>
<ListItem dense>
<ListItemText inset primary="MIME Type" secondary={image.mime_type}/>
</ListItem>
<ListItem dense>
<ListItemIcon><Event/></ListItemIcon>
<ListItemText primary="Uploaded At" secondary={image.created_at}/>
</ListItem>
<ListItem dense>
<ListItemText inset primary="Modified At" secondary={image.updated_at}/>
</ListItem>
<ImageMetadataView metadata={metadata}/>
</List>
<Button
onClick={() => update({
...image,
title,
description,
})}
/>
<input
type="submit"
value="Delete"
>Save</Button>
<Button
onClick={() => remove(image)}
/>
>Delete</Button>
</div>
)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment