diff --git a/source/net/sourceforge/filebot/ui/panel/sfv/ChecksumComputationTask.java b/source/net/sourceforge/filebot/ui/panel/sfv/ChecksumComputationTask.java index 765b453f..b6b16dc8 100644 --- a/source/net/sourceforge/filebot/ui/panel/sfv/ChecksumComputationTask.java +++ b/source/net/sourceforge/filebot/ui/panel/sfv/ChecksumComputationTask.java @@ -12,7 +12,7 @@ import javax.swing.SwingWorker; public class ChecksumComputationTask extends SwingWorker { - private static final int MAX_READ_LENGTH = 200 * 1024; // 200 KB + private static final int CHUNK_SIZE = 20 * 1024; private File file; @@ -27,29 +27,25 @@ public class ChecksumComputationTask extends SwingWorker { CheckedInputStream cis = new CheckedInputStream(new FileInputStream(file), new CRC32()); long length = file.length(); - long done = 0; - int bufferLength = (int) Math.min(length, MAX_READ_LENGTH); - - // don't allow bufferLength == 0 - if (bufferLength < 1) - bufferLength = 1; - - byte[] buffer = new byte[bufferLength]; - - int bytesRead = 0; - - while ((bytesRead = cis.read(buffer)) >= 0) { - if (isCancelled()) - break; + if (length > 0) { + long done = 0; - done += bytesRead; + int bufferLength = (int) Math.min(length, CHUNK_SIZE); - if (length > 0) { + byte[] buffer = new byte[bufferLength]; + + int bytesRead = 0; + + while ((bytesRead = cis.read(buffer)) >= 0) { + if (isCancelled()) + break; + + done += bytesRead; + int progress = (int) ((done * 100) / length); setProgress(progress); } - } cis.close(); diff --git a/source/net/sourceforge/filebot/web/HtmlUtil.java b/source/net/sourceforge/filebot/web/HtmlUtil.java index 483e52b3..ac42f802 100644 --- a/source/net/sourceforge/filebot/web/HtmlUtil.java +++ b/source/net/sourceforge/filebot/web/HtmlUtil.java @@ -23,7 +23,7 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; -public class HtmlUtil { +class HtmlUtil { private static Charset getCharset(String contentType) { if (contentType != null) { diff --git a/source/net/sourceforge/filebot/web/OpenSubtitlesHasher.java b/source/net/sourceforge/filebot/web/OpenSubtitlesHasher.java new file mode 100644 index 00000000..4febdcc3 --- /dev/null +++ b/source/net/sourceforge/filebot/web/OpenSubtitlesHasher.java @@ -0,0 +1,112 @@ + +package net.sourceforge.filebot.web; + + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; + + +/** + * Hash code is based on Media Player Classic. In natural language it calculates: size + 64bit + * checksum of the first and last 64k (even if they overlap because the file is smaller than + * 128k). + */ +class OpenSubtitlesHasher { + + /** + * Size of the chunks that will be hashed in bytes (64 KB) + */ + private static final int HASH_CHUNK_SIZE = 64 * 1024; + + /** + * Size of the checksum in bytes (64 Bit) + */ + private static final int HASH_SIZE = 8; + + + public static String computeHash(File file) throws IOException { + long size = file.length(); + long chunkSizeForFile = Math.min(HASH_CHUNK_SIZE, size); + + FileChannel fileChannel = new FileInputStream(file).getChannel(); + + BigInteger head = computeHashForChunk(fileChannel, 0, chunkSizeForFile); + BigInteger tail = computeHashForChunk(fileChannel, Math.max(size - HASH_CHUNK_SIZE, 0), chunkSizeForFile); + + fileChannel.close(); + + // size + head + tail + BigInteger bigHash = BigInteger.valueOf(size).add(head.add(tail)); + + byte[] hash = getTrailingBytes(bigHash.toByteArray(), HASH_SIZE); + return format(new BigInteger(1, hash)); + } + + + private static BigInteger computeHashForChunk(FileChannel fileChannel, long start, long size) throws IOException { + MappedByteBuffer buffer = fileChannel.map(MapMode.READ_ONLY, start, size); + + BigInteger bigHash = BigInteger.ZERO; + byte[] bytes = new byte[HASH_SIZE]; + + while (buffer.hasRemaining()) { + buffer.get(bytes, 0, Math.min(HASH_SIZE, buffer.remaining())); + + // BigInteger expects a big-endian byte-order, so we reverse the byte array + bigHash = bigHash.add(new BigInteger(1, reverse(bytes))); + } + + return bigHash; + } + + + private static String format(BigInteger hash) { + // 1 byte -> 2 hex digits + int minLength = HASH_SIZE * 2; + + StringBuffer sb = new StringBuffer(minLength); + + sb.append(hash.toString(16)); + + while (sb.length() < minLength) { + sb.insert(0, "0"); + } + + return sb.toString(); + } + + + /** + * copy the last n bytes to a new array + * + * @param bytes original array + * @param n number of trailing bytes + * @return new array + */ + private static byte[] getTrailingBytes(byte[] src, int n) { + int length = Math.min(src.length, n); + + byte[] dest = new byte[length]; + + int offsetSrc = Math.max(src.length - n, 0); + System.arraycopy(src, offsetSrc, dest, 0, length); + + return dest; + } + + + private static byte[] reverse(byte[] bytes) { + byte[] reverseBytes = new byte[bytes.length]; + + for (int forward = 0, backward = bytes.length; forward < bytes.length; ++forward) + reverseBytes[forward] = bytes[--backward]; + + return reverseBytes; + } + +}