Skip to content

Instantly share code, notes, and snippets.

@culmat
Created May 22, 2021 08:51
Show Gist options
  • Save culmat/6991fd01c4d05e5636b2f22b4c733b8b to your computer and use it in GitHub Desktop.
Save culmat/6991fd01c4d05e5636b2f22b4c733b8b to your computer and use it in GitHub Desktop.
package culmat.photo;
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Consumer;
public class ExifParser implements AutoCloseable {
final String EOO = "{ready77}";
final List<String> args = new LinkedList<>(asList("exiftool","-q","-stay_open","True","-@","-"));
Process lazyProc;
IO lazyIO;
boolean closed;
byte[] newLine = System.getProperty("line.separator").getBytes();
byte[] blank = " ".getBytes();
byte[] execute = (System.getProperty("line.separator")+"-execute77"+System.getProperty("line.separator")).getBytes();
class IO implements AutoCloseable{
final BufferedReader err, out;
public IO(OutputStream in, InputStream out, InputStream err) {
this.in = in;
this.out = new BufferedReader(new InputStreamReader(out));
this.err = new BufferedReader(new InputStreamReader(err));
}
final OutputStream in;
@Override
public void close() throws Exception {
in.close();
out.close();
err.close();
}
}
private IO IO() throws IOException {
if(lazyProc == null) {
lazyProc = new ProcessBuilder(args).start();
lazyIO = new IO(
lazyProc.getOutputStream(),
lazyProc.getInputStream(),
lazyProc.getErrorStream());
}
return lazyIO;
}
public ExifParser(String ... commonargs) {
if(commonargs.length > 0) {
// common args must be at the end of the arg list
args.add("-common_args");
args.addAll(asList(commonargs));
}
}
public Map<String, String> getTags(Path path, String ... tags) throws IOException {
final Map<String, String> ret = new TreeMap<>();
getMeta(path, (s)-> {
String[] pair = s.split("\\s*:\\s*",2);
ret.put(pair[0], pair[1]);
}, stream(tags).map(s -> "-"+s).toList().toArray(new String[0]));
return ret;
}
@SuppressWarnings("resource")
public void getMeta(Path path, Consumer<String> consumer, String ... args) throws IOException {
if(closed) throw new IllegalStateException("parser already closed");
for (String a : args) {
IO().in.write(a.getBytes());
IO().in.write(newLine);
}
IO().in.write(path.toAbsolutePath().normalize().toString().getBytes());
IO().in.write(execute);
IO().in.flush();
String line;
while (!(line = IO().out.readLine()).equals(EOO) ) {
consumer.accept(line);
}
}
@SuppressWarnings("resource")
@Override
public void close() throws Exception {
if(lazyProc != null) {
IO().in.write("-stay_open\nFalse\n".getBytes());
IO().in.flush();
lazyIO.close();
lazyProc.destroy();
}
lazyProc = null;
lazyIO = null;
closed = true;
}
public static void main(String[] args) throws Exception {
Path image = Paths.get("/home/XYZ/Pictures/2015/07/2015-07-03_22-41-14_250.jpg");
try (ExifParser exifTool = new ExifParser("-s")) {
for (int i = 0; i < 2; i++) {
exifTool.getMeta(
image,
System.out::println,
"-ShutterCount","-SerialNumber");
System.out.println(exifTool.getTags(
image,
"ShutterCount","SerialNumber"));
}
exifTool.getMeta(
image,
System.out::println);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment