diff --git a/source/net/filebot/ui/filter/TypeTool.java b/source/net/filebot/ui/filter/TypeTool.java index 302da02a..9a839812 100644 --- a/source/net/filebot/ui/filter/TypeTool.java +++ b/source/net/filebot/ui/filter/TypeTool.java @@ -1,21 +1,30 @@ package net.filebot.ui.filter; import static java.util.Collections.*; +import static java.util.stream.Collectors.*; import static javax.swing.BorderFactory.*; +import static net.filebot.Logging.*; import static net.filebot.MediaTypes.*; +import static net.filebot.Settings.*; import static net.filebot.media.MediaDetection.*; import static net.filebot.util.FileUtilities.*; import java.io.File; import java.io.FileFilter; import java.util.ArrayList; +import java.util.EnumMap; +import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.logging.Level; import javax.swing.JScrollPane; import javax.swing.tree.DefaultTreeModel; @@ -54,17 +63,14 @@ class TypeTool extends Tool { List groups = new ArrayList(); - for (Entry it : getMetaTypes().entrySet()) { - List selection = filter(filesAndFolders, it.getValue()); - if (selection.size() > 0) { - groups.add(createStatisticsNode(it.getKey(), selection)); + // meta type groups + groupParallel(filesAndFolders).forEach((type, files) -> { + if (files.size() > 0) { + groups.add(createStatisticsNode(type.getLabel(), files)); } + }); - if (Thread.interrupted()) { - throw new CancellationException(); - } - } - + // file type groups SortedMap extensionGroups = new TreeMap(String.CASE_INSENSITIVE_ORDER); for (Entry> it : mapByExtension(filter(filesAndFolders, FILES)).entrySet()) { @@ -83,19 +89,43 @@ class TypeTool extends Tool { return new DefaultTreeModel(new FolderNode("Types", groups)); } - public Map getMetaTypes() { - Map types = new LinkedHashMap(); - types.put("Movie", f -> VIDEO_FILES.accept(f) && isMovie(f, true)); - types.put("Episode", f -> VIDEO_FILES.accept(f) && isEpisode(f, true)); - types.put("Disk Folder", getDiskFolderFilter()); - types.put("Video", VIDEO_FILES); - types.put("Subtitle", SUBTITLE_FILES); - types.put("Audio", AUDIO_FILES); - types.put("Archive", ARCHIVE_FILES); - types.put("Verification", VERIFICATION_FILES); - types.put("Extras", getClutterFileFilter()); - types.put("Clutter", getClutterTypeFilter()); - return types; + protected Map> groupParallel(List files) { + Map> metaTypes = new EnumMap>(MetaType.class); + + ExecutorService threadPool = Executors.newFixedThreadPool(getPreferredThreadPoolSize()); + try { + files.stream().collect(toMap(f -> f, f -> threadPool.submit(() -> classify(f)), (a, b) -> a, LinkedHashMap::new)).forEach((f, classes) -> { + if (!classes.isCancelled()) { + try { + for (MetaType type : classes.get()) { + metaTypes.computeIfAbsent(type, t -> new ArrayList()).add(f); + } + } catch (InterruptedException e) { + throw new CancellationException(); + } catch (Exception e) { + debug.log(Level.SEVERE, e, e::toString); + } + } + + if (Thread.interrupted()) { + throw new CancellationException(); + } + }); + } finally { + threadPool.shutdownNow(); + } + + return metaTypes; + } + + protected Set classify(File f) { + Set classes = EnumSet.noneOf(MetaType.class); + for (MetaType t : MetaType.values()) { + if (t.accept(f)) { + classes.add(t); + } + } + return classes; } @Override @@ -103,4 +133,44 @@ class TypeTool extends Tool { tree.setModel(model); } + public static enum MetaType implements FileFilter { + + MOVIE("Movie", f -> isMovie(f, true)), + + EPISODE("Episode", f -> VIDEO_FILES.accept(f) && isEpisode(f, true)), + + DISK_FOLDER("Disk Folder", getDiskFolderFilter()), + + VIDEO("Video", VIDEO_FILES), + + SUBTITLE("Subtitle", SUBTITLE_FILES), + + AUDIO("Audio", AUDIO_FILES), + + ARCHIVE("Archive", ARCHIVE_FILES), + + VERIFICATION("Verification", VERIFICATION_FILES), + + EXTRAS("Extras", getClutterFileFilter()), + + CLUTTER("Clutter", getClutterTypeFilter()); + + private final String label; + private final FileFilter filter; + + private MetaType(String label, FileFilter filter) { + this.label = label; + this.filter = filter; + } + + public String getLabel() { + return label; + } + + @Override + public boolean accept(File f) { + return filter.accept(f); + } + } + }