// Copyright 2014-2025 Jesper Larsson
//
// This file is part of Klipspringer, <https://klipspringer.avadeaux.net/>
//
// Klipspringer is free software: you can redistribute it and/or modify it under the terms of the
// GNU General Public License as published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// Klipspringer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with Klipspringer. If
// not, see <https://www.gnu.org/licenses/>.
package net.avadeaux.klipspringer;
import net.avadeaux.klipspringer.codec.*;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
/** Utility that extracts pictures from a FLAC file. */
public class FlacPictures extends FlacDecoder implements AudioDecoder.PictureTarget {
private interface Fnamer {
String fname(File flacFileName, int picNo, String mime, String type, String desc, ByteBuffer data);
}
private static Fnamer descriptiveFnamer = new Fnamer() {
public String fname(File flacFileName, int picNo, String mime, String type, String desc, ByteBuffer data) {
StringBuilder fnam = new StringBuilder(flacFileName.getName());
int sufPos = fnam.lastIndexOf(".");
if (sufPos > 0) { fnam.delete(sufPos, fnam.length()); }
fnam.append("-"+picNo);
if (type != null && type.length() > 0) { fnam.append("-").append(type); }
return fnam.toString();
}
};
private static Fnamer hashFnamer = new Fnamer() {
public String fname(File flacFileName, int picNo, String mime, String type, String desc, ByteBuffer data) {
int pos = data.position();
MessageDigest hash;
try {
hash = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException ex) { throw new Error(ex); }
hash.update(data);
data.position(pos);
return HexFormat.of().formatHex(hash.digest());
}
};
private final File flacFileName, picDir;
private final Fnamer fnamer;
private final PrintStream messages;
private int picCount = 0;
public FlacPictures(File flacFileName, File picDir, Fnamer fnamer, PrintStream messages) throws IOException {
super(flacFileName.toString(), new Target() {
public boolean write(ByteBuffer data) { return false; }
public void metadata(PcmFormat format, long totalFrames) { }
public void close() { }
});
this.flacFileName = flacFileName;
this.picDir = picDir;
this.fnamer = fnamer;
this.messages = messages;
}
public void picture(String mime, String type, String desc, ByteBuffer data) throws IOException {
String fbase = fnamer.fname(flacFileName, ++picCount, mime, type, desc, data);
if (messages != null) {
messages.println(flacFileName+" "+mime+" picture"
+(mime != null && mime.length() > 0 ? ", "+type : "")
+(desc != null && desc.length() > 0 ? ", "+desc : "")
+" "+fbase);
}
if (picDir != null) {
Files.createDirectories(picDir.toPath());
if ("-->".equals(mime)) {
PrintStream pf = new PrintStream(new FileOutputStream(new File(picDir, fbase+".url")));
pf.println("[InternetShortcut]");
pf.println("URL="+StandardCharsets.UTF_8.decode(data));
pf.println("Comment=Extracted from "+flacFileName);
if (desc != null && desc.length() > 0) { pf.println("Desc="+desc); }
} else {
RandomAccessFile f = new RandomAccessFile(new File(picDir, fbase+"."+mime.substring(mime.lastIndexOf('/')+1)), "rw");
f.setLength(0);
f.getChannel().write(data);
f.close();
}
}
}
public static void main(String[] args) {
String helpText = "Arguments: [options] flacfile\n\n"
+"Options:\n"
+"-o <path> Directory of output files\n"
+"-q Do not print information of extracted pictures\n"
+"-l Do not output to files\n"
+"-s Use SHA-1 file names (default)\n"
+"-d Use descriptive names\n"
+"-h or --h Print this message\n"
+"-- No more options, next argument is flac file\n";
File odir = new File(".");
PrintStream messages = System.out;
Fnamer fnamer = hashFnamer;
int argPos = 0;
while (argPos < args.length) {
String o = args[argPos];
if (o.charAt(0) != '-') { break; }
argPos++;
if ("--" .equals(o)) { break; }
else if ("-o" .equals(o)) { odir = new File(args[argPos++]); }
else if ("-q" .equals(o)) { messages = null; }
else if ("-l" .equals(o)) { odir = null; }
else if ("-s" .equals(o)) { fnamer = hashFnamer; }
else if ("-d" .equals(o)) { fnamer = descriptiveFnamer; }
else if (o.startsWith("-h") || o.startsWith("--h")) { System.out.println(helpText); return; }
else {
System.err.println("Unregognized option "+o+" (use -help for usage)");
System.exit(2);
}
}
if (argPos != args.length-1) {
System.err.print("Invalid arguments (use -help for usage):");
for (int i = 0; i < args.length; i++) { System.err.print(" "+args[i]); }
System.err.println();
System.exit(2);
}
try {
FlacPictures decoder = new FlacPictures(new File(args[argPos]), odir, fnamer, messages);
decoder.decodeMetadata();
} catch (Throwable th) {
System.err.println(th);
if (System.getProperty("klipspringer.debug") != null) { th.printStackTrace(); }
System.exit(1);
}
}
}
Version: v4.3.2.2 (2026-05-16T17:03:34+02:00)
Raw file
Source code overview
Klipspringer home