+ auto-lookup verification file
+ allow absolute paths in verification files * lots of refactoring
This commit is contained in:
parent
07ff02c0a5
commit
3ff3a85289
|
@ -51,7 +51,7 @@ public class ArgumentBean {
|
|||
|
||||
|
||||
public boolean sfv() {
|
||||
return sfv || (open && FileUtilities.containsOnly(arguments, MediaTypes.getFilter("verification")));
|
||||
return sfv || (open && FileUtilities.containsOnly(arguments, MediaTypes.getDefaultFilter("verification")));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
|
||||
package net.sourceforge.filebot;
|
||||
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.sourceforge.tuned.FileUtilities.ExtensionFileFilter;
|
||||
|
||||
|
||||
public final class FileBotUtilities {
|
||||
|
||||
/**
|
||||
* Invalid filename characters: \, /, :, *, ?, ", <, >, |, \r and \n
|
||||
*/
|
||||
public static final Pattern INVALID_CHARACTERS = Pattern.compile("[\\\\/:*?\"<>|\\r\\n]");
|
||||
|
||||
|
||||
/**
|
||||
* Strip filename of invalid characters
|
||||
*
|
||||
* @param filename original filename
|
||||
* @return valid filename stripped of invalid characters
|
||||
*/
|
||||
public static String validateFileName(CharSequence filename) {
|
||||
// strip invalid characters from filename
|
||||
return INVALID_CHARACTERS.matcher(filename).replaceAll("");
|
||||
}
|
||||
|
||||
|
||||
public static boolean isInvalidFileName(CharSequence filename) {
|
||||
return INVALID_CHARACTERS.matcher(filename).find();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A {@link Pattern} that will match checksums enclosed in brackets ("[]" or "()"). A
|
||||
* checksum string is a hex number with at least 8 digits. Capturing group 0 will contain
|
||||
* the matched checksum string.
|
||||
*/
|
||||
public static final Pattern EMBEDDED_CHECKSUM_PATTERN = Pattern.compile("(?<=\\[|\\()(\\p{XDigit}{8})(?=\\]|\\))");
|
||||
|
||||
|
||||
public static String getEmbeddedChecksum(CharSequence string) {
|
||||
Matcher matcher = EMBEDDED_CHECKSUM_PATTERN.matcher(string);
|
||||
String embeddedChecksum = null;
|
||||
|
||||
// get last match
|
||||
while (matcher.find()) {
|
||||
embeddedChecksum = matcher.group();
|
||||
}
|
||||
|
||||
return embeddedChecksum;
|
||||
}
|
||||
|
||||
|
||||
public static String removeEmbeddedChecksum(String string) {
|
||||
// match embedded checksum and surrounding brackets
|
||||
return string.replaceAll("[\\(\\[]\\p{XDigit}{8}[\\]\\)]", "");
|
||||
}
|
||||
|
||||
|
||||
public static final ExtensionFileFilter VIDEO_FILES = MediaTypes.getFilter("video");
|
||||
public static final ExtensionFileFilter SUBTITLE_FILES = MediaTypes.getFilter("subtitle");
|
||||
|
||||
|
||||
/**
|
||||
* Dummy constructor to prevent instantiation.
|
||||
*/
|
||||
private FileBotUtilities() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@ package net.sourceforge.filebot;
|
|||
|
||||
import static java.util.Collections.*;
|
||||
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -34,13 +35,12 @@ public class MediaTypes {
|
|||
}
|
||||
|
||||
|
||||
private static final MediaTypes data = unmarshal();
|
||||
private static final MediaTypes defaultInstance = unmarshal();
|
||||
|
||||
|
||||
private static MediaTypes unmarshal() {
|
||||
try {
|
||||
Unmarshaller unmarshaller = JAXBContext.newInstance(MediaTypes.class).createUnmarshaller();
|
||||
|
||||
return (MediaTypes) unmarshaller.unmarshal(MediaTypes.class.getResource("media.types"));
|
||||
} catch (JAXBException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
@ -48,15 +48,10 @@ public class MediaTypes {
|
|||
}
|
||||
|
||||
|
||||
public static ExtensionFileFilter getFilter(String name) {
|
||||
return new ExtensionFileFilter(getExtensionList(name));
|
||||
}
|
||||
|
||||
|
||||
public static List<String> getExtensionList(String name) {
|
||||
public List<String> getExtensionList(String name) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
|
||||
for (Type type : data.types) {
|
||||
for (Type type : defaultInstance.types) {
|
||||
if (type.name.startsWith(name)) {
|
||||
addAll(list, type.extensions);
|
||||
}
|
||||
|
@ -65,4 +60,24 @@ public class MediaTypes {
|
|||
return list;
|
||||
}
|
||||
|
||||
|
||||
public FileFilter getFilter(String name) {
|
||||
return new ExtensionFileFilter(getExtensionList(name));
|
||||
}
|
||||
|
||||
|
||||
public static MediaTypes getDefault() {
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
|
||||
public static ExtensionFileFilter getDefaultFilter(String name) {
|
||||
return new ExtensionFileFilter(getDefault().getExtensionList(name));
|
||||
}
|
||||
|
||||
|
||||
// some convenience filters
|
||||
public static final ExtensionFileFilter AUDIO_FILES = MediaTypes.getDefaultFilter("audio");
|
||||
public static final ExtensionFileFilter VIDEO_FILES = MediaTypes.getDefaultFilter("video");
|
||||
public static final ExtensionFileFilter SUBTITLE_FILES = MediaTypes.getDefaultFilter("subtitle");
|
||||
}
|
||||
|
|
|
@ -2,24 +2,21 @@
|
|||
package net.sourceforge.filebot.format;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.FileBotUtilities.*;
|
||||
import static net.sourceforge.filebot.MediaTypes.*;
|
||||
import static net.sourceforge.filebot.format.Define.*;
|
||||
import static net.sourceforge.filebot.hash.VerificationUtilities.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Scanner;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sf.ehcache.Element;
|
||||
import net.sourceforge.filebot.FileBotUtilities;
|
||||
import net.sourceforge.filebot.MediaTypes;
|
||||
import net.sourceforge.filebot.hash.SfvFormat;
|
||||
import net.sourceforge.filebot.hash.VerificationFileScanner;
|
||||
import net.sourceforge.filebot.hash.HashType;
|
||||
import net.sourceforge.filebot.mediainfo.MediaInfo;
|
||||
import net.sourceforge.filebot.mediainfo.MediaInfo.StreamKind;
|
||||
import net.sourceforge.filebot.web.Episode;
|
||||
|
@ -126,13 +123,13 @@ public class EpisodeBindingBean {
|
|||
File inferredMediaFile = getInferredMediaFile();
|
||||
|
||||
// try to get checksum from file name
|
||||
String checksum = FileBotUtilities.getEmbeddedChecksum(inferredMediaFile.getName());
|
||||
String checksum = getEmbeddedChecksum(inferredMediaFile.getName());
|
||||
|
||||
if (checksum != null)
|
||||
return checksum;
|
||||
|
||||
// try to get checksum from sfv file
|
||||
checksum = getChecksumFromSfvFile(inferredMediaFile.getParentFile(), inferredMediaFile, 0, 3);
|
||||
checksum = getHashFromVerificationFile(inferredMediaFile, HashType.SFV, 3);
|
||||
|
||||
if (checksum != null)
|
||||
return checksum;
|
||||
|
@ -247,43 +244,14 @@ public class EpisodeBindingBean {
|
|||
}
|
||||
|
||||
|
||||
private String getChecksumFromSfvFile(File folder, File target, int depth, int maxDepth) throws IOException {
|
||||
// stop if we reached max depth or the file system root
|
||||
if (folder == null || depth > maxDepth)
|
||||
return null;
|
||||
|
||||
// scan all sfv files in this folder
|
||||
for (File sfvFile : folder.listFiles(MediaTypes.getFilter("verification/sfv"))) {
|
||||
VerificationFileScanner scanner = new VerificationFileScanner(sfvFile, new SfvFormat());
|
||||
|
||||
try {
|
||||
while (scanner.hasNext()) {
|
||||
Entry<File, String> entry = scanner.next();
|
||||
|
||||
// resolve relative file path
|
||||
File file = new File(folder, entry.getKey().getPath());
|
||||
|
||||
if (target.equals(file)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
|
||||
return getChecksumFromSfvFile(folder.getParentFile(), target, depth + 1, maxDepth);
|
||||
}
|
||||
|
||||
|
||||
private String crc32(File file) throws IOException, InterruptedException {
|
||||
// try to get checksum from cache
|
||||
Cache cache = CacheManager.getInstance().getCache("checksum");
|
||||
|
||||
Element cacheEntry = cache.get(file);
|
||||
|
||||
if (cacheEntry != null) {
|
||||
return String.format("%08X", cacheEntry.getValue());
|
||||
try {
|
||||
return String.format("%08X", cache.get(file).getValue());
|
||||
} catch (Exception e) {
|
||||
// checksum is not cached
|
||||
}
|
||||
|
||||
// calculate checksum
|
||||
|
|
|
@ -4,6 +4,9 @@ package net.sourceforge.filebot.hash;
|
|||
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
import net.sourceforge.filebot.MediaTypes;
|
||||
import net.sourceforge.tuned.FileUtilities.ExtensionFileFilter;
|
||||
|
||||
|
||||
public enum HashType {
|
||||
|
||||
|
@ -21,6 +24,12 @@ public enum HashType {
|
|||
return new SfvFormat();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ExtensionFileFilter getFilter() {
|
||||
return MediaTypes.getDefaultFilter("verification/sfv");
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
MD5 {
|
||||
|
@ -37,6 +46,12 @@ public enum HashType {
|
|||
return new VerificationFormat();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ExtensionFileFilter getFilter() {
|
||||
return MediaTypes.getDefaultFilter("verification/md5sum");
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
SHA1 {
|
||||
|
@ -54,6 +69,12 @@ public enum HashType {
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ExtensionFileFilter getFilter() {
|
||||
return MediaTypes.getDefaultFilter("verification/sha1sum");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SHA-1";
|
||||
|
@ -67,20 +88,6 @@ public enum HashType {
|
|||
public abstract VerificationFormat getFormat();
|
||||
|
||||
|
||||
public String getExtension() {
|
||||
return name().toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
public static HashType forName(String name) {
|
||||
for (HashType value : HashType.values()) {
|
||||
if (value.name().equalsIgnoreCase(name)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// value not found
|
||||
return null;
|
||||
}
|
||||
public abstract ExtensionFileFilter getFilter();
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ public class SfvFormat extends VerificationFormat {
|
|||
return String.format("%s %s", path, hash);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pattern used to parse the lines of a sfv file.
|
||||
*
|
||||
|
@ -26,9 +27,9 @@ public class SfvFormat extends VerificationFormat {
|
|||
* | Group 1 | | Gr.2 |
|
||||
* </pre>
|
||||
*/
|
||||
private final Pattern pattern = Pattern.compile("(.+)\\s+(\\p{XDigit}{8})");
|
||||
|
||||
private final Pattern pattern = Pattern.compile("^(.+)\\s+(\\p{XDigit}{8})$");
|
||||
|
||||
|
||||
@Override
|
||||
public Entry<File, String> parseObject(String line) throws ParseException {
|
||||
Matcher matcher = pattern.matcher(line);
|
||||
|
|
|
@ -5,8 +5,8 @@ package net.sourceforge.filebot.hash;
|
|||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.text.ParseException;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
@ -16,7 +16,7 @@ import java.util.logging.Level;
|
|||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
public class VerificationFileScanner implements Iterator<Entry<File, String>>, Closeable {
|
||||
public class VerificationFileReader implements Iterator<Entry<File, String>>, Closeable {
|
||||
|
||||
private final Scanner scanner;
|
||||
|
||||
|
@ -26,15 +26,14 @@ public class VerificationFileScanner implements Iterator<Entry<File, String>>, C
|
|||
|
||||
private int lineNumber = 0;
|
||||
|
||||
|
||||
public VerificationFileScanner(File file, VerificationFormat format) throws FileNotFoundException {
|
||||
// don't use new Scanner(File) because of BUG 6368019 (http://bugs.sun.com/view_bug.do?bug_id=6368019)
|
||||
this(new Scanner(new FileInputStream(file), "UTF-8"), format);
|
||||
|
||||
public VerificationFileReader(File file, VerificationFormat format) throws IOException {
|
||||
this(new InputStreamReader(new FileInputStream(file), "UTF-8"), format);
|
||||
}
|
||||
|
||||
|
||||
public VerificationFileScanner(Scanner scanner, VerificationFormat format) {
|
||||
this.scanner = scanner;
|
||||
public VerificationFileReader(Readable source, VerificationFormat format) {
|
||||
this.scanner = new Scanner(source);
|
||||
this.format = format;
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ public class VerificationFormat extends Format {
|
|||
|
||||
private final String hashTypeHint;
|
||||
|
||||
|
||||
|
||||
public VerificationFormat() {
|
||||
this.hashTypeHint = "";
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ public class VerificationFormat extends Format {
|
|||
return String.format("%s %s*%s", hash, hashTypeHint, path);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pattern used to parse the lines of a md5 or sha1 file.
|
||||
*
|
||||
|
@ -58,9 +59,9 @@ public class VerificationFormat extends Format {
|
|||
* | Group 1 | | Group 2 |
|
||||
* </pre>
|
||||
*/
|
||||
private final Pattern pattern = Pattern.compile("(\\p{XDigit}+)\\s+(?:\\?\\w+)?\\*?(.+)");
|
||||
|
||||
private final Pattern pattern = Pattern.compile("^(\\p{XDigit}+)\\s+(?:\\?\\w+)?\\*?(.+)$");
|
||||
|
||||
|
||||
@Override
|
||||
public Entry<File, String> parseObject(String line) throws ParseException {
|
||||
Matcher matcher = pattern.matcher(line);
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
|
||||
package net.sourceforge.filebot.hash;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
public final class VerificationUtilities {
|
||||
|
||||
/**
|
||||
* A {@link Pattern} that will match checksums enclosed in brackets ("[]" or "()"). A
|
||||
* checksum string is a hex number with at least 8 digits. Capturing group 0 will contain
|
||||
* the matched checksum string.
|
||||
*/
|
||||
public static final Pattern EMBEDDED_CHECKSUM = Pattern.compile("(?<=\\[|\\()(\\p{XDigit}{8})(?=\\]|\\))");
|
||||
|
||||
|
||||
public static String getEmbeddedChecksum(CharSequence string) {
|
||||
Matcher matcher = EMBEDDED_CHECKSUM.matcher(string);
|
||||
String embeddedChecksum = null;
|
||||
|
||||
// get last match
|
||||
while (matcher.find()) {
|
||||
embeddedChecksum = matcher.group();
|
||||
}
|
||||
|
||||
return embeddedChecksum;
|
||||
}
|
||||
|
||||
|
||||
public static String removeEmbeddedChecksum(String string) {
|
||||
// match embedded checksum and surrounding brackets
|
||||
return string.replaceAll("[\\(\\[]\\p{XDigit}{8}[\\]\\)]", "");
|
||||
}
|
||||
|
||||
|
||||
public static String getHashFromVerificationFile(File file, HashType type, int maxDepth) throws IOException {
|
||||
return getHashFromVerificationFile(file.getParentFile(), file, type, 0, maxDepth);
|
||||
}
|
||||
|
||||
|
||||
private static String getHashFromVerificationFile(File folder, File target, HashType type, int depth, int maxDepth) throws IOException {
|
||||
// stop if we reached max depth or the file system root
|
||||
if (folder == null || depth > maxDepth)
|
||||
return null;
|
||||
|
||||
// scan all sfv files in this folder
|
||||
for (File verificationFile : folder.listFiles(type.getFilter())) {
|
||||
VerificationFileReader scanner = new VerificationFileReader(verificationFile, type.getFormat());
|
||||
|
||||
try {
|
||||
while (scanner.hasNext()) {
|
||||
Entry<File, String> entry = scanner.next();
|
||||
|
||||
// resolve relative file path
|
||||
File file = new File(folder, entry.getKey().getPath());
|
||||
|
||||
if (target.equals(file)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
|
||||
return getHashFromVerificationFile(folder.getParentFile(), target, type, depth + 1, maxDepth);
|
||||
}
|
||||
|
||||
|
||||
public static HashType getHashType(File verificationFile) {
|
||||
for (HashType hashType : HashType.values()) {
|
||||
if (hashType.getFilter().accept(verificationFile))
|
||||
return hashType;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dummy constructor to prevent instantiation.
|
||||
*/
|
||||
private VerificationUtilities() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
|
@ -21,12 +21,13 @@
|
|||
<extension>sfv</extension>
|
||||
</type>
|
||||
|
||||
<type name="verification/md5">
|
||||
<type name="verification/md5sum">
|
||||
<extension>md5</extension>
|
||||
</type>
|
||||
|
||||
<type name="verification/sha-1">
|
||||
|
||||
<type name="verification/sha1sum">
|
||||
<extension>sha1</extension>
|
||||
<extension>sha</extension>
|
||||
</type>
|
||||
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ public class MediaInfo implements Closeable {
|
|||
// We need to load dependencies first, because we know where our native libs are (e.g. Java Web Start Cache).
|
||||
// If we do not, the system will look for dependencies, but only in the library path.
|
||||
NativeLibrary.getInstance("zen");
|
||||
} catch (Exception e) {
|
||||
} catch (LinkageError e) {
|
||||
Logger.getLogger(MediaInfo.class.getName()).warning("Failed to preload libzen");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import static net.sourceforge.tuned.StringUtilities.*;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
|
||||
|
||||
public class MicroDVDReader extends SubtitleReader {
|
||||
|
@ -14,8 +13,8 @@ public class MicroDVDReader extends SubtitleReader {
|
|||
private double fps = 23.976;
|
||||
|
||||
|
||||
public MicroDVDReader(Scanner scanner) {
|
||||
super(scanner);
|
||||
public MicroDVDReader(Readable source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ import java.text.SimpleDateFormat;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Scanner;
|
||||
import java.util.TimeZone;
|
||||
|
||||
|
||||
|
@ -18,8 +17,8 @@ public class SubRipReader extends SubtitleReader {
|
|||
private final DateFormat timeFormat;
|
||||
|
||||
|
||||
public SubRipReader(Scanner scanner) {
|
||||
super(scanner);
|
||||
public SubRipReader(Readable source) {
|
||||
super(source);
|
||||
|
||||
// format used to parse time stamps (e.g. 00:02:26,407 --> 00:02:31,356)
|
||||
timeFormat = new SimpleDateFormat("HH:mm:ss,SSS", Locale.ROOT);
|
||||
|
|
|
@ -9,7 +9,6 @@ import java.util.Arrays;
|
|||
import java.util.EnumMap;
|
||||
import java.util.InputMismatchException;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
|
@ -21,8 +20,8 @@ public class SubStationAlphaReader extends SubtitleReader {
|
|||
private Map<EventProperty, Integer> format;
|
||||
|
||||
|
||||
public SubStationAlphaReader(Scanner scanner) {
|
||||
super(scanner);
|
||||
public SubStationAlphaReader(Readable source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import static net.sourceforge.tuned.StringUtilities.*;
|
|||
|
||||
import java.text.DateFormat;
|
||||
import java.util.InputMismatchException;
|
||||
import java.util.Scanner;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
|
@ -16,8 +15,8 @@ public class SubViewerReader extends SubtitleReader {
|
|||
private final Pattern newline = Pattern.compile(Pattern.quote("[br]"), Pattern.CASE_INSENSITIVE);
|
||||
|
||||
|
||||
public SubViewerReader(Scanner scanner) {
|
||||
super(scanner);
|
||||
public SubViewerReader(Readable source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
package net.sourceforge.filebot.subtitle;
|
||||
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
import net.sourceforge.filebot.MediaTypes;
|
||||
import net.sourceforge.tuned.FileUtilities.ExtensionFileFilter;
|
||||
|
||||
|
@ -14,7 +12,7 @@ public enum SubtitleFormat {
|
|||
|
||||
@Override
|
||||
public SubtitleReader newReader(Readable readable) {
|
||||
return new SubRipReader(new Scanner(readable));
|
||||
return new SubRipReader(readable);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -22,7 +20,7 @@ public enum SubtitleFormat {
|
|||
|
||||
@Override
|
||||
public SubtitleReader newReader(Readable readable) {
|
||||
return new MicroDVDReader(new Scanner(readable));
|
||||
return new MicroDVDReader(readable);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -30,7 +28,7 @@ public enum SubtitleFormat {
|
|||
|
||||
@Override
|
||||
public SubtitleReader newReader(Readable readable) {
|
||||
return new SubViewerReader(new Scanner(readable));
|
||||
return new SubViewerReader(readable);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -38,7 +36,7 @@ public enum SubtitleFormat {
|
|||
|
||||
@Override
|
||||
public SubtitleReader newReader(Readable readable) {
|
||||
return new SubStationAlphaReader(new Scanner(readable));
|
||||
return new SubStationAlphaReader(readable);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -46,7 +44,7 @@ public enum SubtitleFormat {
|
|||
|
||||
|
||||
public ExtensionFileFilter getFilter() {
|
||||
return MediaTypes.getFilter("subtitle/" + this);
|
||||
return MediaTypes.getDefaultFilter("subtitle/" + this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,9 +3,6 @@ package net.sourceforge.filebot.subtitle;
|
|||
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
@ -21,14 +18,8 @@ public abstract class SubtitleReader implements Iterator<SubtitleElement>, Close
|
|||
protected SubtitleElement current;
|
||||
|
||||
|
||||
public SubtitleReader(File file) throws FileNotFoundException {
|
||||
// don't use new Scanner(File) because of BUG 6368019 (http://bugs.sun.com/view_bug.do?bug_id=6368019)
|
||||
this(new Scanner(new FileInputStream(file), "UTF-8"));
|
||||
}
|
||||
|
||||
|
||||
public SubtitleReader(Scanner scanner) {
|
||||
this.scanner = scanner;
|
||||
public SubtitleReader(Readable source) {
|
||||
this.scanner = new Scanner(source);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ class FileListTransferablePolicy extends FileTransferablePolicy {
|
|||
|
||||
if (containsOnly(files, FOLDERS)) {
|
||||
loadFolders(files);
|
||||
} else if (containsOnly(files, MediaTypes.getFilter("application/torrent"))) {
|
||||
} else if (containsOnly(files, MediaTypes.getDefaultFilter("application/torrent"))) {
|
||||
loadTorrents(files);
|
||||
} else {
|
||||
loadFiles(files);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
package net.sourceforge.filebot.ui.panel.rename;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.FileBotUtilities.*;
|
||||
import static net.sourceforge.filebot.MediaTypes.*;
|
||||
import static net.sourceforge.tuned.ui.TunedUtilities.*;
|
||||
|
||||
import java.io.File;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
package net.sourceforge.filebot.ui.panel.rename;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.MediaTypes.*;
|
||||
import static net.sourceforge.tuned.ui.TunedUtilities.*;
|
||||
|
||||
import java.awt.Color;
|
||||
|
@ -13,6 +14,7 @@ import java.awt.event.WindowEvent;
|
|||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
|
@ -51,7 +53,6 @@ import javax.swing.table.TableModel;
|
|||
import javax.swing.table.TableRowSorter;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sourceforge.filebot.MediaTypes;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
import net.sourceforge.filebot.format.EpisodeBindingBean;
|
||||
import net.sourceforge.filebot.format.ExpressionFormat;
|
||||
|
@ -385,9 +386,9 @@ class EpisodeBindingDialog extends JDialog {
|
|||
|
||||
// collect media file extensions (video, audio and subtitle files)
|
||||
List<String> extensions = new ArrayList<String>();
|
||||
extensions.addAll(MediaTypes.getExtensionList("video"));
|
||||
extensions.addAll(MediaTypes.getExtensionList("audio"));
|
||||
extensions.addAll(MediaTypes.getExtensionList("subtitle"));
|
||||
Collections.addAll(extensions, VIDEO_FILES.extensions());
|
||||
Collections.addAll(extensions, AUDIO_FILES.extensions());
|
||||
Collections.addAll(extensions, SUBTITLE_FILES.extensions());
|
||||
|
||||
chooser.setFileFilter(new FileNameExtensionFilter("Media files", extensions.toArray(new String[0])));
|
||||
chooser.setMultiSelectionEnabled(false);
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
package net.sourceforge.filebot.ui.panel.rename;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.hash.VerificationUtilities.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import net.sourceforge.filebot.FileBotUtilities;
|
||||
import net.sourceforge.filebot.similarity.LengthEqualsMetric;
|
||||
import net.sourceforge.filebot.similarity.NameSimilarityMetric;
|
||||
import net.sourceforge.filebot.similarity.NumericSimilarityMetric;
|
||||
|
@ -109,7 +110,7 @@ enum MatchSimilarityMetric implements SimilarityMetric {
|
|||
}
|
||||
|
||||
// remove embedded checksum from name, if any
|
||||
return FileBotUtilities.removeEmbeddedChecksum(name);
|
||||
return removeEmbeddedChecksum(name);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package net.sourceforge.filebot.ui.panel.rename;
|
|||
|
||||
|
||||
import static java.awt.datatransfer.DataFlavor.*;
|
||||
import static net.sourceforge.filebot.hash.VerificationUtilities.*;
|
||||
import static net.sourceforge.filebot.ui.transfer.FileTransferable.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
|
||||
|
@ -19,7 +20,7 @@ import java.util.Scanner;
|
|||
|
||||
import net.sourceforge.filebot.MediaTypes;
|
||||
import net.sourceforge.filebot.hash.HashType;
|
||||
import net.sourceforge.filebot.hash.VerificationFileScanner;
|
||||
import net.sourceforge.filebot.hash.VerificationFileReader;
|
||||
import net.sourceforge.filebot.torrent.Torrent;
|
||||
import net.sourceforge.filebot.ui.transfer.ArrayTransferable;
|
||||
import net.sourceforge.filebot.ui.transfer.FileTransferablePolicy;
|
||||
|
@ -97,13 +98,13 @@ class NamesListTransferablePolicy extends FileTransferablePolicy {
|
|||
protected void load(List<File> files) throws IOException {
|
||||
List<Object> values = new ArrayList<Object>();
|
||||
|
||||
if (containsOnly(files, MediaTypes.getFilter("application/list"))) {
|
||||
if (containsOnly(files, MediaTypes.getDefaultFilter("application/list"))) {
|
||||
// list files
|
||||
loadListFiles(files, values);
|
||||
} else if (containsOnly(files, MediaTypes.getFilter("verification"))) {
|
||||
} else if (containsOnly(files, MediaTypes.getDefaultFilter("verification"))) {
|
||||
// verification files
|
||||
loadVerificationFiles(files, values);
|
||||
} else if (containsOnly(files, MediaTypes.getFilter("application/torrent"))) {
|
||||
} else if (containsOnly(files, MediaTypes.getDefaultFilter("application/torrent"))) {
|
||||
// torrent files
|
||||
loadTorrentFiles(files, values);
|
||||
} else if (containsOnly(files, FOLDERS)) {
|
||||
|
@ -139,15 +140,15 @@ class NamesListTransferablePolicy extends FileTransferablePolicy {
|
|||
|
||||
|
||||
protected void loadVerificationFiles(List<File> files, List<Object> values) throws IOException {
|
||||
for (File file : files) {
|
||||
HashType format = HashType.forName(getExtension(file));
|
||||
for (File verificationFile : files) {
|
||||
HashType type = getHashType(verificationFile);
|
||||
|
||||
// check if format is valid
|
||||
if (format == null)
|
||||
// check if type is supported
|
||||
if (type == null)
|
||||
continue;
|
||||
|
||||
// add all file names from verification file
|
||||
VerificationFileScanner scanner = new VerificationFileScanner(file, format.getFormat());
|
||||
VerificationFileReader scanner = new VerificationFileReader(verificationFile, type.getFormat());
|
||||
|
||||
try {
|
||||
while (scanner.hasNext()) {
|
||||
|
|
|
@ -3,7 +3,7 @@ package net.sourceforge.filebot.ui.panel.rename;
|
|||
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static net.sourceforge.filebot.FileBotUtilities.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
import static net.sourceforge.tuned.ui.TunedUtilities.*;
|
||||
|
||||
import java.awt.Color;
|
||||
|
@ -47,7 +47,7 @@ class ValidateDialog extends JDialog {
|
|||
list = new JList(model);
|
||||
list.setEnabled(false);
|
||||
|
||||
list.setCellRenderer(new HighlightListCellRenderer(INVALID_CHARACTERS, new CharacterHighlightPainter(new Color(0xFF4200), new Color(0xFF1200)), 4));
|
||||
list.setCellRenderer(new HighlightListCellRenderer(ILLEGAL_CHARACTERS, new CharacterHighlightPainter(new Color(0xFF4200), new Color(0xFF1200)), 4));
|
||||
|
||||
JLabel label = new JLabel("Some names contain invalid characters:");
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import static net.sourceforge.tuned.ui.TunedUtilities.*;
|
|||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.SwingWorker;
|
||||
|
@ -20,7 +21,7 @@ public class ChecksumCellRenderer extends DefaultTableCellRenderer {
|
|||
|
||||
private final SwingWorkerCellRenderer progressRenderer = new SwingWorkerCellRenderer();
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
boolean pendingWorker = false;
|
||||
|
@ -50,6 +51,8 @@ public class ChecksumCellRenderer extends DefaultTableCellRenderer {
|
|||
setText("Pending...");
|
||||
} else if (value == null && !isSelected) {
|
||||
setBackground(derive(table.getGridColor(), 0.1f));
|
||||
} else if (value instanceof FileNotFoundException) {
|
||||
setText("File not found");
|
||||
} else if (value instanceof Throwable) {
|
||||
setText(ExceptionUtilities.getRootCauseMessage((Throwable) value));
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ package net.sourceforge.filebot.ui.panel.sfv;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
@ -23,7 +22,7 @@ class ChecksumComputationTask extends SwingWorker<Map<HashType, String>, Void> {
|
|||
private final File file;
|
||||
private final HashType hashType;
|
||||
|
||||
|
||||
|
||||
public ChecksumComputationTask(File file, HashType hashType) {
|
||||
this.file = file;
|
||||
this.hashType = hashType;
|
||||
|
@ -32,9 +31,6 @@ class ChecksumComputationTask extends SwingWorker<Map<HashType, String>, Void> {
|
|||
|
||||
@Override
|
||||
protected Map<HashType, String> doInBackground() throws Exception {
|
||||
if (!file.exists())
|
||||
throw new FileNotFoundException("File not found");
|
||||
|
||||
// create hash instance
|
||||
Hash hash = hashType.newHash();
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
package net.sourceforge.filebot.ui.panel.sfv;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.hash.VerificationUtilities.*;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.io.File;
|
||||
|
@ -15,7 +17,6 @@ import java.util.Set;
|
|||
|
||||
import javax.swing.event.SwingPropertyChangeSupport;
|
||||
|
||||
import net.sourceforge.filebot.FileBotUtilities;
|
||||
import net.sourceforge.filebot.hash.HashType;
|
||||
|
||||
|
||||
|
@ -31,7 +32,7 @@ class ChecksumRow {
|
|||
*/
|
||||
private String embeddedChecksum;
|
||||
|
||||
|
||||
|
||||
public static enum State {
|
||||
UNKNOWN,
|
||||
OK,
|
||||
|
@ -39,10 +40,10 @@ class ChecksumRow {
|
|||
ERROR
|
||||
}
|
||||
|
||||
|
||||
|
||||
public ChecksumRow(String name) {
|
||||
this.name = name;
|
||||
this.embeddedChecksum = FileBotUtilities.getEmbeddedChecksum(name);
|
||||
this.embeddedChecksum = getEmbeddedChecksum(name);
|
||||
}
|
||||
|
||||
|
||||
|
@ -167,6 +168,7 @@ class ChecksumRow {
|
|||
return String.format("%s %s %s", state, name, hashes);
|
||||
}
|
||||
|
||||
|
||||
private final PropertyChangeListener updateStateListener = new PropertyChangeListener() {
|
||||
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
|
@ -178,7 +180,7 @@ class ChecksumRow {
|
|||
|
||||
private SwingPropertyChangeSupport pcs = new SwingPropertyChangeSupport(this, true);
|
||||
|
||||
|
||||
|
||||
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
||||
pcs.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
package net.sourceforge.filebot.ui.panel.sfv;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.FileBotUtilities.*;
|
||||
import static net.sourceforge.filebot.hash.VerificationUtilities.*;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
|
@ -33,7 +33,7 @@ class ChecksumTable extends JTable {
|
|||
setBackground(Color.WHITE);
|
||||
|
||||
// highlight CRC32 patterns in filenames in green and with smaller font-size
|
||||
setDefaultRenderer(String.class, new HighlightPatternCellRenderer(EMBEDDED_CHECKSUM_PATTERN));
|
||||
setDefaultRenderer(String.class, new HighlightPatternCellRenderer(EMBEDDED_CHECKSUM));
|
||||
setDefaultRenderer(ChecksumRow.State.class, new StateIconCellRenderer());
|
||||
setDefaultRenderer(ChecksumCell.class, new ChecksumCellRenderer());
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ class ChecksumTableExportHandler extends TextFileExportHandler {
|
|||
|
||||
private final ChecksumTableModel model;
|
||||
|
||||
|
||||
|
||||
public ChecksumTableExportHandler(ChecksumTableModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
@ -92,11 +92,12 @@ class ChecksumTableExportHandler extends TextFileExportHandler {
|
|||
public String getDefaultFileName(File column) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (column != null)
|
||||
sb.append(FileUtilities.getName(column));
|
||||
else
|
||||
sb.append("name");
|
||||
// append file name
|
||||
sb.append(column != null ? FileUtilities.getName(column) : "name");
|
||||
|
||||
return sb.append(".").append(model.getHashType().getExtension()).toString();
|
||||
// append file extension
|
||||
sb.append('.').append(model.getHashType().name().toLowerCase());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,22 +2,24 @@
|
|||
package net.sourceforge.filebot.ui.panel.sfv;
|
||||
|
||||
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
import static java.util.Collections.*;
|
||||
import static net.sourceforge.filebot.hash.VerificationUtilities.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.sourceforge.filebot.MediaTypes;
|
||||
import net.sourceforge.filebot.hash.HashType;
|
||||
import net.sourceforge.filebot.hash.VerificationFileScanner;
|
||||
import net.sourceforge.filebot.hash.VerificationFileReader;
|
||||
import net.sourceforge.filebot.ui.transfer.BackgroundFileTransferablePolicy;
|
||||
import net.sourceforge.tuned.ExceptionUtilities;
|
||||
import net.sourceforge.tuned.FileUtilities.ExtensionFileFilter;
|
||||
|
||||
|
||||
class ChecksumTableTransferablePolicy extends BackgroundFileTransferablePolicy<ChecksumCell> {
|
||||
|
@ -25,7 +27,7 @@ class ChecksumTableTransferablePolicy extends BackgroundFileTransferablePolicy<C
|
|||
private final ChecksumTableModel model;
|
||||
private final ChecksumComputationService computationService;
|
||||
|
||||
|
||||
|
||||
public ChecksumTableTransferablePolicy(ChecksumTableModel model, ChecksumComputationService checksumComputationService) {
|
||||
this.model = model;
|
||||
this.computationService = checksumComputationService;
|
||||
|
@ -49,10 +51,8 @@ class ChecksumTableTransferablePolicy extends BackgroundFileTransferablePolicy<C
|
|||
|
||||
@Override
|
||||
protected void prepare(List<File> files) {
|
||||
HashType type = getVerificationType(files);
|
||||
|
||||
if (type != null) {
|
||||
model.setHashType(type);
|
||||
if (files.size() == 1 && getHashType(files.get(0)) != null) {
|
||||
model.setHashType(getHashType(files.get(0)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,86 +69,75 @@ class ChecksumTableTransferablePolicy extends BackgroundFileTransferablePolicy<C
|
|||
}
|
||||
|
||||
|
||||
protected HashType getVerificationType(List<File> files) {
|
||||
for (HashType hash : HashType.values()) {
|
||||
if (containsOnly(files, new ExtensionFileFilter(hash.getExtension()))) {
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private final ThreadLocal<ExecutorService> executor = new ThreadLocal<ExecutorService>();
|
||||
private final ThreadLocal<VerificationTracker> verificationTracker = new ThreadLocal<VerificationTracker>();
|
||||
|
||||
|
||||
protected void loadVerificationFile(File file, HashType type) throws IOException {
|
||||
// don't use new Scanner(File) because of BUG 6368019 (http://bugs.sun.com/view_bug.do?bug_id=6368019)
|
||||
VerificationFileScanner scanner = new VerificationFileScanner(file, type.getFormat());
|
||||
|
||||
try {
|
||||
// root for relative file paths in verification file
|
||||
File root = file.getParentFile();
|
||||
|
||||
while (scanner.hasNext()) {
|
||||
Entry<File, String> entry = scanner.next();
|
||||
|
||||
String name = normalizeRelativePath(entry.getKey());
|
||||
String hash = entry.getValue();
|
||||
|
||||
ChecksumCell correct = new ChecksumCell(name, file, Collections.singletonMap(type, hash));
|
||||
ChecksumCell current = createComputationCell(name, root, type);
|
||||
|
||||
publish(correct, current);
|
||||
|
||||
if (Thread.interrupted()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
|
||||
private final ThreadLocal<ExecutorService> executor = new ThreadLocal<ExecutorService>();
|
||||
|
||||
|
||||
@Override
|
||||
protected void load(List<File> files) throws IOException {
|
||||
// initialize drop parameters
|
||||
executor.set(computationService.newExecutor());
|
||||
verificationTracker.set(new VerificationTracker(3));
|
||||
|
||||
try {
|
||||
HashType verificationType = getVerificationType(files);
|
||||
|
||||
if (verificationType != null) {
|
||||
// handle single verification file drop
|
||||
if (files.size() == 1 && getHashType(files.get(0)) != null) {
|
||||
loadVerificationFile(files.get(0), getHashType(files.get(0)));
|
||||
}
|
||||
// handle single folder drop
|
||||
else if (files.size() == 1 && files.get(0).isDirectory()) {
|
||||
for (File file : files.get(0).listFiles()) {
|
||||
load(file, null, files.get(0));
|
||||
}
|
||||
}
|
||||
// handle all other drops
|
||||
else {
|
||||
for (File file : files) {
|
||||
loadVerificationFile(file, verificationType);
|
||||
}
|
||||
} else if ((files.size() == 1) && files.get(0).isDirectory()) {
|
||||
// one single folder
|
||||
File file = files.get(0);
|
||||
|
||||
for (File f : file.listFiles()) {
|
||||
load(f, null, file);
|
||||
}
|
||||
} else {
|
||||
// bunch of files
|
||||
for (File f : files) {
|
||||
load(f, null, f.getParentFile());
|
||||
load(file, null, file.getParentFile());
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// supposed to happen if background execution was aborted
|
||||
// supposed to happen if background execution is aborted
|
||||
} finally {
|
||||
// shutdown executor after all tasks have been completed
|
||||
executor.get().shutdown();
|
||||
|
||||
// remove drop parameters
|
||||
executor.remove();
|
||||
verificationTracker.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void load(File file, File relativeFile, File root) throws InterruptedException {
|
||||
protected void loadVerificationFile(File file, HashType type) throws IOException, InterruptedException {
|
||||
VerificationFileReader scanner = new VerificationFileReader(file, type.getFormat());
|
||||
|
||||
try {
|
||||
// root for relative file paths in verification file
|
||||
File baseFolder = file.getParentFile();
|
||||
|
||||
while (scanner.hasNext()) {
|
||||
Entry<File, String> entry = scanner.next();
|
||||
|
||||
String name = normalizePath(entry.getKey());
|
||||
String hash = new String(entry.getValue());
|
||||
|
||||
ChecksumCell correct = new ChecksumCell(name, file, singletonMap(type, hash));
|
||||
ChecksumCell current = createComputationCell(name, baseFolder, type);
|
||||
|
||||
publish(correct, current);
|
||||
|
||||
// make this long-running operation interruptible
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
}
|
||||
} finally {
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void load(File file, File relativeFile, File root) throws IOException, InterruptedException {
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
|
||||
|
@ -161,7 +150,18 @@ class ChecksumTableTransferablePolicy extends BackgroundFileTransferablePolicy<C
|
|||
load(child, relativeFile, root);
|
||||
}
|
||||
} else {
|
||||
publish(createComputationCell(normalizeRelativePath(relativeFile), root, model.getHashType()));
|
||||
String name = normalizePath(relativeFile);
|
||||
|
||||
// publish computation cell first
|
||||
publish(createComputationCell(name, root, model.getHashType()));
|
||||
|
||||
// publish verification cell, if we can
|
||||
Map<File, String> hashByVerificationFile = verificationTracker.get().getHashByVerificationFile(file);
|
||||
|
||||
for (Entry<File, String> entry : hashByVerificationFile.entrySet()) {
|
||||
HashType hashType = verificationTracker.get().getVerificationFileType(entry.getKey());
|
||||
publish(new ChecksumCell(name, entry.getKey(), singletonMap(hashType, entry.getValue())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,10 +176,7 @@ class ChecksumTableTransferablePolicy extends BackgroundFileTransferablePolicy<C
|
|||
}
|
||||
|
||||
|
||||
protected String normalizeRelativePath(File file) {
|
||||
if (file.isAbsolute())
|
||||
throw new IllegalArgumentException("Path must be relative");
|
||||
|
||||
protected String normalizePath(File file) {
|
||||
return file.getPath().replace('\\', '/');
|
||||
}
|
||||
|
||||
|
@ -189,4 +186,97 @@ class ChecksumTableTransferablePolicy extends BackgroundFileTransferablePolicy<C
|
|||
return "files, folders and sfv files";
|
||||
}
|
||||
|
||||
|
||||
private static class VerificationTracker {
|
||||
|
||||
private final Map<File, Integer> seen = new HashMap<File, Integer>();
|
||||
private final Map<File, Map<File, String>> cache = new HashMap<File, Map<File, String>>();
|
||||
private final Map<File, HashType> types = new HashMap<File, HashType>();
|
||||
|
||||
private final int maxDepth;
|
||||
|
||||
|
||||
public VerificationTracker(int maxDepth) {
|
||||
this.maxDepth = maxDepth;
|
||||
}
|
||||
|
||||
|
||||
public Map<File, String> getHashByVerificationFile(File file) throws IOException {
|
||||
// cache all verification files
|
||||
File folder = file.getParentFile();
|
||||
int depth = 0;
|
||||
|
||||
while (folder != null && depth <= maxDepth) {
|
||||
Integer seenLevel = seen.get(folder);
|
||||
|
||||
if (seenLevel != null && seenLevel <= depth) {
|
||||
// we have completely seen this parent tree before
|
||||
break;
|
||||
}
|
||||
|
||||
if (seenLevel == null) {
|
||||
// folder we have never encountered before
|
||||
for (File verificationFile : folder.listFiles(MediaTypes.getDefaultFilter("verification"))) {
|
||||
HashType hashType = getHashType(verificationFile);
|
||||
cache.put(verificationFile, importVerificationFile(verificationFile, hashType, verificationFile.getParentFile()));
|
||||
types.put(verificationFile, hashType);
|
||||
}
|
||||
}
|
||||
|
||||
// update
|
||||
seen.put(folder, depth);
|
||||
|
||||
// step down
|
||||
folder = folder.getParentFile();
|
||||
depth++;
|
||||
}
|
||||
|
||||
// just return if we know we won't find anything
|
||||
if (cache.isEmpty()) {
|
||||
return emptyMap();
|
||||
}
|
||||
|
||||
// search all cached verification files
|
||||
Map<File, String> result = new HashMap<File, String>(2);
|
||||
|
||||
for (Entry<File, Map<File, String>> entry : cache.entrySet()) {
|
||||
String hash = entry.getValue().get(file);
|
||||
|
||||
if (hash != null) {
|
||||
result.put(entry.getKey(), hash);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public HashType getVerificationFileType(File verificationFile) {
|
||||
return types.get(verificationFile);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Completely read a verification file and resolve all relative file paths against a given base folder
|
||||
*/
|
||||
private Map<File, String> importVerificationFile(File verificationFile, HashType hashType, File baseFolder) throws IOException {
|
||||
VerificationFileReader reader = new VerificationFileReader(verificationFile, hashType.getFormat());
|
||||
Map<File, String> content = new HashMap<File, String>();
|
||||
|
||||
try {
|
||||
while (reader.hasNext()) {
|
||||
Entry<File, String> entry = reader.next();
|
||||
|
||||
// resolve relative path, the hash is probably a substring, so we compact it, for memory reasons
|
||||
content.put(new File(baseFolder, entry.getKey().getPath()), new String(entry.getValue()));
|
||||
}
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,12 +18,12 @@ class SwingWorkerCellRenderer extends JPanel implements TableCellRenderer {
|
|||
|
||||
private final JProgressBar progressBar = new JProgressBar(0, 100);
|
||||
|
||||
|
||||
|
||||
public SwingWorkerCellRenderer() {
|
||||
super(new BorderLayout());
|
||||
|
||||
// create margin for progress bar,
|
||||
// because setting margin for progress bar directly does not work (border size is not respected in the paint method)
|
||||
// set margin for progress bar on parent component,
|
||||
// because setting it on the progress bar itself does not work (border size is not respected in the paint method)
|
||||
setBorder(new EmptyBorder(2, 2, 2, 2));
|
||||
|
||||
progressBar.setStringPainted(true);
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.FileBotUtilities.*;
|
||||
import static net.sourceforge.filebot.MediaTypes.*;
|
||||
import static net.sourceforge.filebot.ui.panel.subtitle.SubtitleUtilities.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
|
|
|
@ -3,7 +3,7 @@ package net.sourceforge.filebot.ui.panel.subtitle;
|
|||
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static net.sourceforge.filebot.FileBotUtilities.*;
|
||||
import static net.sourceforge.filebot.MediaTypes.*;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.FileBotUtilities.*;
|
||||
import static net.sourceforge.filebot.MediaTypes.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Insets;
|
||||
|
@ -61,7 +61,7 @@ class SubtitlePackageCellRenderer extends AbstractFancyListCellRenderer {
|
|||
private Icon getIcon(SubtitlePackage subtitle) {
|
||||
switch (subtitle.getDownload().getPhase()) {
|
||||
case PENDING:
|
||||
if (ArchiveType.forName(subtitle.getType()) != ArchiveType.UNDEFINED || SUBTITLE_FILES.extensions().contains(subtitle.getType())) {
|
||||
if (ArchiveType.forName(subtitle.getType()) != ArchiveType.UNDEFINED || SUBTITLE_FILES.acceptExtension(subtitle.getType())) {
|
||||
return ResourceManager.getIcon("bullet.green");
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
package net.sourceforge.filebot.ui.transfer;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.FileBotUtilities.*;
|
||||
import static net.sourceforge.filebot.Settings.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
|
@ -90,9 +90,7 @@ public class ByteBufferTransferable implements Transferable {
|
|||
|
||||
@Override
|
||||
public DataFlavor[] getTransferDataFlavors() {
|
||||
return new DataFlavor[] {
|
||||
DataFlavor.javaFileListFlavor, FileTransferable.uriListFlavor
|
||||
};
|
||||
return new DataFlavor[] { DataFlavor.javaFileListFlavor, FileTransferable.uriListFlavor };
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
package net.sourceforge.filebot.ui.transfer;
|
||||
|
||||
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -13,7 +15,6 @@ import javax.swing.Icon;
|
|||
import javax.swing.JComponent;
|
||||
import javax.swing.JFileChooser;
|
||||
|
||||
import net.sourceforge.filebot.FileBotUtilities;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
|
||||
|
||||
|
@ -21,7 +22,7 @@ public class SaveAction extends AbstractAction {
|
|||
|
||||
public static final String EXPORT_HANDLER = "exportHandler";
|
||||
|
||||
|
||||
|
||||
public SaveAction(FileExportHandler exportHandler) {
|
||||
this("Save as ...", ResourceManager.getIcon("action.save"), exportHandler);
|
||||
}
|
||||
|
@ -67,7 +68,7 @@ public class SaveAction extends AbstractAction {
|
|||
|
||||
chooser.setMultiSelectionEnabled(false);
|
||||
|
||||
chooser.setSelectedFile(new File(getDefaultFolder(), FileBotUtilities.validateFileName(getDefaultFileName())));
|
||||
chooser.setSelectedFile(new File(getDefaultFolder(), validateFileName(getDefaultFileName())));
|
||||
|
||||
if (chooser.showSaveDialog((JComponent) evt.getSource()) != JFileChooser.APPROVE_OPTION)
|
||||
return;
|
||||
|
|
|
@ -5,7 +5,6 @@ package net.sourceforge.tuned;
|
|||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
|
@ -14,27 +13,12 @@ import java.util.regex.Pattern;
|
|||
|
||||
public final class FileUtilities {
|
||||
|
||||
public static final long KILO = 1024;
|
||||
public static final long MEGA = KILO * 1024;
|
||||
public static final long GIGA = MEGA * 1024;
|
||||
|
||||
|
||||
public static String formatSize(long size) {
|
||||
if (size >= MEGA)
|
||||
return String.format("%,d MB", size / MEGA);
|
||||
else if (size >= KILO)
|
||||
return String.format("%,d KB", size / KILO);
|
||||
else
|
||||
return String.format("%,d Byte", size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pattern used for matching file extensions.
|
||||
*
|
||||
* e.g. "file.txt" -> match "txt", ".hidden" -> no match
|
||||
*/
|
||||
private static final Pattern extension = Pattern.compile("(?<=.[.])\\p{Alnum}+$");
|
||||
public static final Pattern EXTENSION = Pattern.compile("(?<=.[.])\\p{Alnum}+$");
|
||||
|
||||
|
||||
public static String getExtension(File file) {
|
||||
|
@ -46,7 +30,7 @@ public final class FileUtilities {
|
|||
|
||||
|
||||
public static String getExtension(String name) {
|
||||
Matcher matcher = extension.matcher(name);
|
||||
Matcher matcher = EXTENSION.matcher(name);
|
||||
|
||||
if (matcher.find()) {
|
||||
// extension without leading '.'
|
||||
|
@ -59,21 +43,17 @@ public final class FileUtilities {
|
|||
|
||||
|
||||
public static boolean hasExtension(File file, String... extensions) {
|
||||
if (file.isDirectory())
|
||||
return false;
|
||||
|
||||
return hasExtension(file.getName(), extensions);
|
||||
// avoid native call for speed, if possible
|
||||
return hasExtension(file.getName(), extensions) && !file.isDirectory();
|
||||
}
|
||||
|
||||
|
||||
public static boolean hasExtension(String filename, String... extensions) {
|
||||
String extension = getExtension(filename);
|
||||
|
||||
if (extension != null) {
|
||||
for (String entry : extensions) {
|
||||
if (extension.equalsIgnoreCase(entry))
|
||||
return true;
|
||||
}
|
||||
for (String value : extensions) {
|
||||
if ((extension == null && value == null) || (extension != null && extension.equalsIgnoreCase(value)))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -81,7 +61,7 @@ public final class FileUtilities {
|
|||
|
||||
|
||||
public static String getNameWithoutExtension(String name) {
|
||||
Matcher matcher = extension.matcher(name);
|
||||
Matcher matcher = EXTENSION.matcher(name);
|
||||
|
||||
if (matcher.find()) {
|
||||
return name.substring(0, matcher.start() - 1);
|
||||
|
@ -137,6 +117,44 @@ public final class FileUtilities {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invalid filename characters: \, /, :, *, ?, ", <, >, |, \r and \n
|
||||
*/
|
||||
public static final Pattern ILLEGAL_CHARACTERS = Pattern.compile("[\\\\/:*?\"<>|\\r\\n]");
|
||||
|
||||
|
||||
/**
|
||||
* Strip filename of invalid characters
|
||||
*
|
||||
* @param filename original filename
|
||||
* @return valid filename stripped of invalid characters
|
||||
*/
|
||||
public static String validateFileName(CharSequence filename) {
|
||||
// strip invalid characters from filename
|
||||
return ILLEGAL_CHARACTERS.matcher(filename).replaceAll("");
|
||||
}
|
||||
|
||||
|
||||
public static boolean isInvalidFileName(CharSequence filename) {
|
||||
return ILLEGAL_CHARACTERS.matcher(filename).find();
|
||||
}
|
||||
|
||||
|
||||
public static final long KILO = 1024;
|
||||
public static final long MEGA = KILO * 1024;
|
||||
public static final long GIGA = MEGA * 1024;
|
||||
|
||||
|
||||
public static String formatSize(long size) {
|
||||
if (size >= MEGA)
|
||||
return String.format("%,d MB", size / MEGA);
|
||||
else if (size >= KILO)
|
||||
return String.format("%,d KB", size / KILO);
|
||||
else
|
||||
return String.format("%,d Byte", size);
|
||||
}
|
||||
|
||||
|
||||
public static final FileFilter FOLDERS = new FileFilter() {
|
||||
|
||||
@Override
|
||||
|
@ -190,8 +208,8 @@ public final class FileUtilities {
|
|||
}
|
||||
|
||||
|
||||
public List<String> extensions() {
|
||||
return Arrays.asList(extensions);
|
||||
public String[] extensions() {
|
||||
return extensions.clone();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,16 +4,16 @@ package net.sourceforge.filebot.subtitle;
|
|||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.StringReader;
|
||||
|
||||
import org.junit.*;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
public class MicroDVDReaderTest {
|
||||
|
||||
@Test
|
||||
public void parse() throws Exception {
|
||||
MicroDVDReader reader = new MicroDVDReader(new Scanner("{856}{900}what's the plan?"));
|
||||
MicroDVDReader reader = new MicroDVDReader(new StringReader("{856}{900}what's the plan?"));
|
||||
|
||||
SubtitleElement element = reader.next();
|
||||
|
||||
|
@ -25,7 +25,7 @@ public class MicroDVDReaderTest {
|
|||
|
||||
@Test
|
||||
public void fps() throws Exception {
|
||||
MicroDVDReader reader = new MicroDVDReader(new Scanner("{1}{1}100\n{300}{400} trim me "));
|
||||
MicroDVDReader reader = new MicroDVDReader(new StringReader("{1}{1}100\n{300}{400} trim me "));
|
||||
|
||||
SubtitleElement element = reader.next();
|
||||
|
||||
|
@ -37,7 +37,7 @@ public class MicroDVDReaderTest {
|
|||
|
||||
@Test
|
||||
public void newline() throws Exception {
|
||||
MicroDVDReader reader = new MicroDVDReader(new Scanner("\n\n{300}{400} l1|l2|l3| \n\n"));
|
||||
MicroDVDReader reader = new MicroDVDReader(new StringReader("\n\n{300}{400} l1|l2|l3| \n\n"));
|
||||
|
||||
String[] lines = reader.next().getText().split("\\n");
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@ package net.sourceforge.filebot.subtitle;
|
|||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Scanner;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
@ -20,9 +20,9 @@ public class SubRipReaderTest {
|
|||
LinkedList<SubtitleElement> list = new LinkedList<SubtitleElement>();
|
||||
|
||||
URL resource = new URL("http://www.opensubtitles.org/en/download/file/1951733951.gz");
|
||||
InputStream stream = new GZIPInputStream(resource.openStream());
|
||||
InputStream source = new GZIPInputStream(resource.openStream());
|
||||
|
||||
SubRipReader reader = new SubRipReader(new Scanner(stream, "UTF-8"));
|
||||
SubRipReader reader = new SubRipReader(new InputStreamReader(source, "UTF-8"));
|
||||
|
||||
try {
|
||||
while (reader.hasNext()) {
|
||||
|
|
|
@ -9,6 +9,14 @@ import org.junit.Test;
|
|||
|
||||
public class FileUtilitiesTest {
|
||||
|
||||
@Test
|
||||
public void hasExtension() {
|
||||
assertTrue(FileUtilities.hasExtension("abc.txt", null, "txt"));
|
||||
assertTrue(FileUtilities.hasExtension(".hidden", null, "txt"));
|
||||
assertFalse(FileUtilities.hasExtension(".hidden", "txt"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void getExtension() {
|
||||
assertEquals("txt", FileUtilities.getExtension("abc.txt"));
|
||||
|
|
Loading…
Reference in New Issue