Refactoring the file/directory parsing code away from the properties model of the metadata.
This commit is contained in:
parent
72c827c3f2
commit
cad37df18b
2 changed files with 200 additions and 167 deletions
|
|
@ -3,12 +3,9 @@
|
|||
*/
|
||||
package org.forkalsrud.album.exif;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -18,31 +15,15 @@ import java.util.Date;
|
|||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import org.forkalsrud.album.db.DirectoryDatabase;
|
||||
import org.forkalsrud.album.db.DirectoryProps;
|
||||
|
||||
import com.drew.imaging.jpeg.JpegMetadataReader;
|
||||
import com.drew.metadata.Directory;
|
||||
import com.drew.metadata.Metadata;
|
||||
import com.drew.metadata.MetadataException;
|
||||
import com.drew.metadata.exif.ExifDirectory;
|
||||
import com.drew.metadata.jpeg.JpegDirectory;
|
||||
|
||||
/**
|
||||
* @author knut
|
||||
*/
|
||||
public class DirectoryEntry extends Entry {
|
||||
|
||||
private static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DirectoryEntry.class);
|
||||
|
||||
final static String CACHE_FILE = "cache.properties";
|
||||
final static String OVERRIDE_FILE = "album.properties";
|
||||
|
||||
|
|
@ -59,7 +40,7 @@ public class DirectoryEntry extends Entry {
|
|||
throw new RuntimeException("Use DirectoryEntry only for directories: " + file);
|
||||
}
|
||||
this.db = db;
|
||||
cache = db.get(file.getAbsolutePath());
|
||||
cache = db.load(file.getAbsolutePath());
|
||||
if (cache == null) {
|
||||
cache = new DirectoryProps();
|
||||
}
|
||||
|
|
@ -114,12 +95,12 @@ public class DirectoryEntry extends Entry {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
Properties cachedProps = isCacheCurrent() ? cache : generateCache();
|
||||
Properties combined = new Properties();
|
||||
DirectoryProps cachedProps = isCacheCurrent() ? cache : generateCache();
|
||||
DirectoryProps combined = new DirectoryProps();
|
||||
combined.putAll(cachedProps);
|
||||
File override = new File(file, OVERRIDE_FILE);
|
||||
if (override.exists() && override.isFile() && override.canRead()) {
|
||||
Properties overrideProps = loadFromFile(override);
|
||||
DirectoryProps overrideProps = loadFromFile(override);
|
||||
combined.putAll(overrideProps);
|
||||
}
|
||||
populate(combined);
|
||||
|
|
@ -135,162 +116,24 @@ public class DirectoryEntry extends Entry {
|
|||
return file.lastModified() <= cache.getTimestamp();
|
||||
}
|
||||
|
||||
Properties loadFromFile(File propFile) throws IOException {
|
||||
DirectoryProps loadFromFile(File propFile) throws IOException {
|
||||
|
||||
Properties props = new Properties();
|
||||
DirectoryProps props = new DirectoryProps();
|
||||
FileInputStream fis = new FileInputStream(propFile);
|
||||
props.load(fis);
|
||||
fis.close();
|
||||
props.setTimestamp(propFile.lastModified());
|
||||
return props;
|
||||
}
|
||||
|
||||
Properties generateCache() throws IOException {
|
||||
DirectoryProps generateCache() throws IOException {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
DirectoryProps props = new DirectoryProps();
|
||||
generateFileEntries(props);
|
||||
long end = System.currentTimeMillis();
|
||||
props.setTimestamp(end);
|
||||
DirectoryProps props = new DirectoryMetadataGenerator(file).generate();
|
||||
db.store(file.getAbsolutePath(), props);
|
||||
log.info("Time to generate properties for " + file.getPath() + ": " + (end - start)/1000d + " s");
|
||||
return props;
|
||||
}
|
||||
|
||||
|
||||
void generateFileEntries(Properties props) throws IOException {
|
||||
|
||||
File[] files = file.listFiles();
|
||||
for (File f : files) {
|
||||
|
||||
String name = f.getName();
|
||||
if (f.isDirectory()) {
|
||||
if ("CVS".equals(name)) {
|
||||
continue;
|
||||
}
|
||||
if (f.isHidden()) {
|
||||
continue;
|
||||
}
|
||||
generateDirectoryProperties(props, f);
|
||||
continue;
|
||||
}
|
||||
if (file.isHidden()) {
|
||||
continue;
|
||||
}
|
||||
if (CACHE_FILE.equals(name) || OVERRIDE_FILE.equals(name)) {
|
||||
// cache.properties and album.properties
|
||||
continue;
|
||||
}
|
||||
if (name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".JPG")) {
|
||||
String base = "file." + f.getName() + ".";
|
||||
Map<String, String> p = generateThumbnailProperties(f);
|
||||
for (Map.Entry<String, String> e : p.entrySet()) {
|
||||
props.setProperty(base + e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void generateDirectoryProperties(Properties props, File f) {
|
||||
|
||||
String name = f.getName();
|
||||
props.setProperty("dir." + name, "present");
|
||||
}
|
||||
|
||||
private Map<String, String> generateThumbnailProperties(File f)
|
||||
throws IOException {
|
||||
HashMap<String, String> props = new HashMap<String, String>();
|
||||
String name = f.getName();
|
||||
boolean hasDate = false;
|
||||
boolean hasOrientation = false;
|
||||
boolean hasDim = false;
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-HHmmss");
|
||||
NumberFormat nf = new DecimalFormat("0");
|
||||
|
||||
Metadata metadata;
|
||||
try {
|
||||
metadata = JpegMetadataReader.readMetadata(f);
|
||||
Directory exifDirectory = metadata.getDirectory(ExifDirectory.class);
|
||||
if (exifDirectory.containsTag(ExifDirectory.TAG_ORIENTATION)) {
|
||||
int orientation = exifDirectory.getInt(ExifDirectory.TAG_ORIENTATION);
|
||||
props.put("orientation", nf.format(orientation));
|
||||
hasOrientation = true;
|
||||
}
|
||||
if (exifDirectory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_WIDTH) &&
|
||||
exifDirectory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT)) {
|
||||
int width = exifDirectory.getInt(ExifDirectory.TAG_EXIF_IMAGE_WIDTH);
|
||||
int height = exifDirectory.getInt(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT);
|
||||
props.put("dimensions", new Dimension(width, height).toString());
|
||||
hasDim = true;
|
||||
}
|
||||
if (exifDirectory.containsTag(ExifDirectory.TAG_DATETIME_ORIGINAL)) {
|
||||
Date captureDate = getExifDate(exifDirectory, ExifDirectory.TAG_DATETIME_ORIGINAL);
|
||||
if (captureDate != null) {
|
||||
props.put("captureDate", sdf.format(captureDate));
|
||||
hasDate = true;
|
||||
}
|
||||
}
|
||||
if (exifDirectory.containsTag(ExifDirectory.TAG_USER_COMMENT)) {
|
||||
String comment = exifDirectory.getString(ExifDirectory.TAG_USER_COMMENT);
|
||||
props.put("comment", comment);
|
||||
}
|
||||
Directory jpegDirectory = metadata.getDirectory(JpegDirectory.class);
|
||||
if (jpegDirectory.containsTag(JpegDirectory.TAG_JPEG_IMAGE_WIDTH) &&
|
||||
jpegDirectory.containsTag(JpegDirectory.TAG_JPEG_IMAGE_HEIGHT)) {
|
||||
int width = jpegDirectory.getInt(JpegDirectory.TAG_JPEG_IMAGE_WIDTH);
|
||||
int height = jpegDirectory.getInt(JpegDirectory.TAG_JPEG_IMAGE_HEIGHT);
|
||||
props.put("dimensions", new Dimension(width, height).toString());
|
||||
hasDim = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("problem reading file " + f.getPath(), e);
|
||||
}
|
||||
props.put("etag", Integer.toHexString(name.hashCode() + Long.valueOf(f.lastModified()).hashCode()));
|
||||
if (!hasDate) {
|
||||
props.put("captureDate", sdf.format(new Date(f.lastModified())));
|
||||
}
|
||||
if (!hasOrientation) {
|
||||
props.put("orientation", "1");
|
||||
}
|
||||
if (!hasDim) {
|
||||
Dimension dim = decodeImageForDimensions(f);
|
||||
if (dim != null) {
|
||||
props.put("dimensions", dim.toString());
|
||||
hasDim = true;
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
private Date getExifDate(Directory exifDirectory, int tagName) throws MetadataException {
|
||||
|
||||
String dateStr = (String)exifDirectory.getObject(tagName);
|
||||
if (" : : : : ".equals(dateStr)) {
|
||||
return null;
|
||||
}
|
||||
return exifDirectory.getDate(tagName);
|
||||
}
|
||||
|
||||
Dimension decodeImageForDimensions(File file) throws IOException {
|
||||
|
||||
String suffix = null;
|
||||
String name = file.getName();
|
||||
if (name.indexOf('.') > 0) {
|
||||
suffix = name.substring(name.lastIndexOf('.') + 1);
|
||||
}
|
||||
Iterator<ImageReader> readers = ImageIO.getImageReadersBySuffix(suffix);
|
||||
if (!readers.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
ImageReader reader = readers.next();
|
||||
ImageInputStream iis = ImageIO.createImageInputStream(file);
|
||||
reader.setInput(iis, true);
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
BufferedImage img = reader.read(0, param);
|
||||
iis.close();
|
||||
return new Dimension(img.getWidth(), img.getHeight());
|
||||
}
|
||||
|
||||
private void sort() {
|
||||
|
||||
Collections.sort(children, sort);
|
||||
|
|
@ -312,7 +155,7 @@ public class DirectoryEntry extends Entry {
|
|||
}
|
||||
|
||||
|
||||
private void populate(Properties props) throws ParseException {
|
||||
private void populate(DirectoryProps props) throws ParseException {
|
||||
|
||||
String coverFileName = props.getProperty("cover");
|
||||
String caption = props.getProperty("caption");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,190 @@
|
|||
package org.forkalsrud.album.exif;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import org.forkalsrud.album.db.DirectoryProps;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.drew.imaging.jpeg.JpegMetadataReader;
|
||||
import com.drew.metadata.Directory;
|
||||
import com.drew.metadata.Metadata;
|
||||
import com.drew.metadata.MetadataException;
|
||||
import com.drew.metadata.exif.ExifDirectory;
|
||||
import com.drew.metadata.jpeg.JpegDirectory;
|
||||
|
||||
public class DirectoryMetadataGenerator {
|
||||
|
||||
private static Logger log = LoggerFactory.getLogger(DirectoryMetadataGenerator.class);
|
||||
final static String CACHE_FILE = "cache.properties";
|
||||
final static String OVERRIDE_FILE = "album.properties";
|
||||
|
||||
File dir;
|
||||
|
||||
|
||||
public DirectoryMetadataGenerator(File f) {
|
||||
this.dir = f;
|
||||
}
|
||||
|
||||
|
||||
public DirectoryProps generate() throws IOException {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
DirectoryProps props = new DirectoryProps();
|
||||
generateFileEntries(props);
|
||||
long end = System.currentTimeMillis();
|
||||
props.setTimestamp(end);
|
||||
log.info("Time to generate properties for " + dir.getPath() + ": " + (end - start)/1000d + " s");
|
||||
return props;
|
||||
}
|
||||
|
||||
|
||||
private void generateFileEntries(Properties props) throws IOException {
|
||||
|
||||
File[] files = dir.listFiles();
|
||||
for (File f : files) {
|
||||
|
||||
String name = f.getName();
|
||||
if (f.isDirectory()) {
|
||||
if ("CVS".equals(name)) {
|
||||
continue;
|
||||
}
|
||||
if (f.isHidden()) {
|
||||
continue;
|
||||
}
|
||||
generateDirectoryProperties(props, f);
|
||||
continue;
|
||||
}
|
||||
if (f.isHidden()) {
|
||||
continue;
|
||||
}
|
||||
if (CACHE_FILE.equals(name) || OVERRIDE_FILE.equals(name)) {
|
||||
// cache.properties and album.properties
|
||||
continue;
|
||||
}
|
||||
if (name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".JPG")) {
|
||||
String base = "file." + f.getName() + ".";
|
||||
Map<String, String> p = generateThumbnailProperties(f);
|
||||
for (Map.Entry<String, String> e : p.entrySet()) {
|
||||
props.setProperty(base + e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void generateDirectoryProperties(Properties props, File f) {
|
||||
|
||||
String name = f.getName();
|
||||
props.setProperty("dir." + name, "present");
|
||||
}
|
||||
|
||||
private Map<String, String> generateThumbnailProperties(File f) throws IOException {
|
||||
|
||||
HashMap<String, String> props = new HashMap<String, String>();
|
||||
String name = f.getName();
|
||||
boolean hasDate = false;
|
||||
boolean hasOrientation = false;
|
||||
boolean hasDim = false;
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-HHmmss");
|
||||
NumberFormat nf = new DecimalFormat("0");
|
||||
|
||||
Metadata metadata;
|
||||
try {
|
||||
metadata = JpegMetadataReader.readMetadata(f);
|
||||
Directory exifDirectory = metadata.getDirectory(ExifDirectory.class);
|
||||
if (exifDirectory.containsTag(ExifDirectory.TAG_ORIENTATION)) {
|
||||
int orientation = exifDirectory.getInt(ExifDirectory.TAG_ORIENTATION);
|
||||
props.put("orientation", nf.format(orientation));
|
||||
hasOrientation = true;
|
||||
}
|
||||
if (exifDirectory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_WIDTH) &&
|
||||
exifDirectory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT)) {
|
||||
int width = exifDirectory.getInt(ExifDirectory.TAG_EXIF_IMAGE_WIDTH);
|
||||
int height = exifDirectory.getInt(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT);
|
||||
props.put("dimensions", new Dimension(width, height).toString());
|
||||
hasDim = true;
|
||||
}
|
||||
if (exifDirectory.containsTag(ExifDirectory.TAG_DATETIME_ORIGINAL)) {
|
||||
Date captureDate = getExifDate(exifDirectory, ExifDirectory.TAG_DATETIME_ORIGINAL);
|
||||
if (captureDate != null) {
|
||||
props.put("captureDate", sdf.format(captureDate));
|
||||
hasDate = true;
|
||||
}
|
||||
}
|
||||
if (exifDirectory.containsTag(ExifDirectory.TAG_USER_COMMENT)) {
|
||||
String comment = exifDirectory.getString(ExifDirectory.TAG_USER_COMMENT);
|
||||
props.put("comment", comment);
|
||||
}
|
||||
Directory jpegDirectory = metadata.getDirectory(JpegDirectory.class);
|
||||
if (jpegDirectory.containsTag(JpegDirectory.TAG_JPEG_IMAGE_WIDTH) &&
|
||||
jpegDirectory.containsTag(JpegDirectory.TAG_JPEG_IMAGE_HEIGHT)) {
|
||||
int width = jpegDirectory.getInt(JpegDirectory.TAG_JPEG_IMAGE_WIDTH);
|
||||
int height = jpegDirectory.getInt(JpegDirectory.TAG_JPEG_IMAGE_HEIGHT);
|
||||
props.put("dimensions", new Dimension(width, height).toString());
|
||||
hasDim = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("problem reading file " + f.getPath(), e);
|
||||
}
|
||||
props.put("etag", Integer.toHexString(name.hashCode() + Long.valueOf(f.lastModified()).hashCode()));
|
||||
if (!hasDate) {
|
||||
props.put("captureDate", sdf.format(new Date(f.lastModified())));
|
||||
}
|
||||
if (!hasOrientation) {
|
||||
props.put("orientation", "1");
|
||||
}
|
||||
if (!hasDim) {
|
||||
Dimension dim = decodeImageForDimensions(f);
|
||||
if (dim != null) {
|
||||
props.put("dimensions", dim.toString());
|
||||
hasDim = true;
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
private Date getExifDate(Directory exifDirectory, int tagName) throws MetadataException {
|
||||
|
||||
String dateStr = (String)exifDirectory.getObject(tagName);
|
||||
if (" : : : : ".equals(dateStr)) {
|
||||
return null;
|
||||
}
|
||||
return exifDirectory.getDate(tagName);
|
||||
}
|
||||
|
||||
private Dimension decodeImageForDimensions(File file) throws IOException {
|
||||
|
||||
String suffix = null;
|
||||
String name = file.getName();
|
||||
if (name.indexOf('.') > 0) {
|
||||
suffix = name.substring(name.lastIndexOf('.') + 1);
|
||||
}
|
||||
Iterator<ImageReader> readers = ImageIO.getImageReadersBySuffix(suffix);
|
||||
if (!readers.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
ImageReader reader = readers.next();
|
||||
ImageInputStream iis = ImageIO.createImageInputStream(file);
|
||||
reader.setInput(iis, true);
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
BufferedImage img = reader.read(0, param);
|
||||
iis.close();
|
||||
return new Dimension(img.getWidth(), img.getHeight());
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue