mirror of
https://github.com/mitb-archive/filebot
synced 2025-03-11 06:50:27 -04:00
Refactor ConflictDialog and make it more user-friendly
This commit is contained in:
parent
6fc47abb39
commit
2c8a657c38
@ -16,12 +16,11 @@ import java.awt.event.MouseAdapter;
|
|||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
@ -38,6 +37,7 @@ import javax.swing.table.DefaultTableCellRenderer;
|
|||||||
|
|
||||||
import net.filebot.ResourceManager;
|
import net.filebot.ResourceManager;
|
||||||
import net.filebot.UserFiles;
|
import net.filebot.UserFiles;
|
||||||
|
import net.filebot.media.VideoQuality;
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
|
||||||
class ConflictDialog extends JDialog {
|
class ConflictDialog extends JDialog {
|
||||||
@ -46,7 +46,7 @@ class ConflictDialog extends JDialog {
|
|||||||
private boolean cancel = true;
|
private boolean cancel = true;
|
||||||
|
|
||||||
public ConflictDialog(Window owner, List<Conflict> conflicts) {
|
public ConflictDialog(Window owner, List<Conflict> conflicts) {
|
||||||
super(owner, "Conflicting Files", ModalityType.DOCUMENT_MODAL);
|
super(owner, "Conflicts", ModalityType.DOCUMENT_MODAL);
|
||||||
|
|
||||||
model.setData(conflicts);
|
model.setData(conflicts);
|
||||||
|
|
||||||
@ -160,16 +160,16 @@ class ConflictDialog extends JDialog {
|
|||||||
|
|
||||||
public enum Kind {
|
public enum Kind {
|
||||||
|
|
||||||
OVERRIDE_FAILED, DUPLICATE_SOURCE, DUPLICATE_DESTINATION, DESTINATION_ALREADY_EXISTS, MISSING_EXTENSION;
|
OVERRIDE_FAILED, FILE_EXISTS, MISSING_EXTENSION, DUPLICATE, OVERLAP;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case DUPLICATE_SOURCE:
|
case OVERLAP:
|
||||||
return "Duplicate source path";
|
return "Duplicate source path";
|
||||||
case DUPLICATE_DESTINATION:
|
case DUPLICATE:
|
||||||
return "Duplicate destination path";
|
return "Duplicate destination path";
|
||||||
case DESTINATION_ALREADY_EXISTS:
|
case FILE_EXISTS:
|
||||||
return "Destination file already exists";
|
return "Destination file already exists";
|
||||||
case MISSING_EXTENSION:
|
case MISSING_EXTENSION:
|
||||||
return "Missing file extension";
|
return "Missing file extension";
|
||||||
@ -213,7 +213,7 @@ class ConflictDialog extends JDialog {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getColumnCount() {
|
public int getColumnCount() {
|
||||||
return 4;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -242,7 +242,7 @@ class ConflictDialog extends JDialog {
|
|||||||
|
|
||||||
switch (column) {
|
switch (column) {
|
||||||
case 0:
|
case 0:
|
||||||
return ResourceManager.getIcon(conflict.issues.isEmpty() ? "status.ok" : "status.error");
|
return ResourceManager.getIcon(conflict.issues.isEmpty() ? "status.ok" : "status.warning");
|
||||||
case 1:
|
case 1:
|
||||||
return conflict;
|
return conflict;
|
||||||
case 2:
|
case 2:
|
||||||
@ -258,6 +258,10 @@ class ConflictDialog extends JDialog {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
if (value == null) {
|
||||||
|
return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
}
|
||||||
|
|
||||||
File f = (File) value;
|
File f = (File) value;
|
||||||
super.getTableCellRendererComponent(table, f.getName(), isSelected, hasFocus, row, column);
|
super.getTableCellRendererComponent(table, f.getName(), isSelected, hasFocus, row, column);
|
||||||
setToolTipText(f.getPath());
|
setToolTipText(f.getPath());
|
||||||
@ -269,8 +273,12 @@ class ConflictDialog extends JDialog {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
if (value == null) {
|
||||||
|
return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
}
|
||||||
|
|
||||||
Conflict conflict = (Conflict) value;
|
Conflict conflict = (Conflict) value;
|
||||||
String label = conflict.issues.isEmpty() ? "OK" : conflict.issues.entrySet().stream().map(e -> e.getKey() + ": " + e.getValue()).collect(joining(" • "));
|
String label = conflict.issues.isEmpty() ? "OK" : conflict.issues.values().iterator().next();
|
||||||
super.getTableCellRendererComponent(table, label, isSelected, hasFocus, row, column);
|
super.getTableCellRendererComponent(table, label, isSelected, hasFocus, row, column);
|
||||||
setToolTipText(formatToolTip(conflict));
|
setToolTipText(formatToolTip(conflict));
|
||||||
return this;
|
return this;
|
||||||
@ -279,6 +287,8 @@ class ConflictDialog extends JDialog {
|
|||||||
private String formatToolTip(Conflict conflict) {
|
private String formatToolTip(Conflict conflict) {
|
||||||
StringBuilder html = new StringBuilder(64).append("<html>");
|
StringBuilder html = new StringBuilder(64).append("<html>");
|
||||||
conflict.issues.forEach((k, v) -> appendTooltipParagraph(html, k.toString(), v.toString()));
|
conflict.issues.forEach((k, v) -> appendTooltipParagraph(html, k.toString(), v.toString()));
|
||||||
|
appendTooltipParagraph(html, "Source", conflict.source.getPath());
|
||||||
|
appendTooltipParagraph(html, "Destination", conflict.destination.getPath());
|
||||||
return html.append("</html>").toString();
|
return html.append("</html>").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,46 +315,58 @@ class ConflictDialog extends JDialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Map<File, List<File>> getSourceFilesForDestination(Map<File, File> renameMap, Comparator<File> order) {
|
||||||
|
Map<File, List<File>> duplicates = renameMap.entrySet().stream().collect(groupingBy(e -> {
|
||||||
|
return resolve(e.getKey(), e.getValue());
|
||||||
|
}, mapping(e -> e.getKey(), toCollection(ArrayList::new))));
|
||||||
|
|
||||||
|
// sort duplicates by video quality
|
||||||
|
duplicates.forEach((k, v) -> v.sort(order));
|
||||||
|
|
||||||
|
return duplicates;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean check(Component parent, Map<File, File> renameMap) {
|
public static boolean check(Component parent, Map<File, File> renameMap) {
|
||||||
List<Conflict> conflicts = new ArrayList<Conflict>();
|
List<Conflict> conflicts = new ArrayList<Conflict>();
|
||||||
|
|
||||||
// sanity checks
|
Map<File, List<File>> sourceFilesForDestination = getSourceFilesForDestination(renameMap, VideoQuality.DESCENDING_ORDER);
|
||||||
Set<File> destinationFiles = new HashSet<File>();
|
|
||||||
|
|
||||||
renameMap.forEach((from, relativeDestinationPath) -> {
|
// sanity checks
|
||||||
|
renameMap.forEach((from, v) -> {
|
||||||
// resolve relative paths
|
// resolve relative paths
|
||||||
File to = resolve(from, relativeDestinationPath);
|
File to = resolve(from, v);
|
||||||
|
|
||||||
Map<Conflict.Kind, String> issues = new EnumMap<Conflict.Kind, String>(Conflict.Kind.class);
|
Map<Conflict.Kind, String> issues = new EnumMap<Conflict.Kind, String>(Conflict.Kind.class);
|
||||||
|
|
||||||
// output files must have a valid file extension
|
// output files must have a valid file extension
|
||||||
if (getExtension(to) == null && from.isFile()) {
|
if (getExtension(to) == null && from.isFile()) {
|
||||||
String details = String.format("Destination file path [%s] has no file extension. Please adjust your format.", to.getName());
|
String details = String.format("Destination file path [%s] has no file extension. Fix via Edit Format.", to.getName());
|
||||||
issues.put(Conflict.Kind.MISSING_EXTENSION, details);
|
issues.put(Conflict.Kind.MISSING_EXTENSION, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
// one file per unique output path
|
// one file per unique output path
|
||||||
if (!destinationFiles.add(to)) {
|
List<File> duplicates = sourceFilesForDestination.get(to);
|
||||||
List<String> sourceFiles = renameMap.entrySet().stream().filter(e -> e.getValue().equals(relativeDestinationPath)).map(e -> e.getKey().getName()).collect(toList());
|
File chosen = duplicates.get(0);
|
||||||
String details = String.format("Multiple source files %s all map to the same destination file [%s]. The highest-quality version will be selected.", sourceFiles, to.getName());
|
if (duplicates.size() > 1 && !from.equals(chosen)) {
|
||||||
issues.put(Conflict.Kind.DUPLICATE_DESTINATION, details);
|
String details = String.format("Multiple source files map to the same destination file %s ➔ [%s]. The highest-quality file [%s] was chosen instead of [%s].", duplicates.stream().map(File::getName).collect(toList()), to.getName(), chosen.getName(), from.getName());
|
||||||
|
issues.put(Conflict.Kind.DUPLICATE, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if input and output overlap
|
// check if input and output overlap
|
||||||
if (renameMap.containsKey(to) && !to.equals(from)) {
|
if (renameMap.containsKey(to) && !to.equals(from)) {
|
||||||
String details = String.format("Overlapping file mapping between [%s ➔ %s] and [%s ➔ %s]. Please adjust your format.", from.getName(), to.getName(), to.getName(), renameMap.get(to).getName());
|
String details = String.format("Overlapping file mapping between [%s ➔ %s] and [%s ➔ %s]. Fix via Edit Format.", from.getName(), to.getName(), to.getName(), renameMap.get(to).getName());
|
||||||
issues.put(Conflict.Kind.DUPLICATE_SOURCE, details);
|
issues.put(Conflict.Kind.OVERLAP, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if destination path already exists
|
// check if destination path already exists
|
||||||
if (to.exists() && !to.equals(from)) {
|
if (to.exists() && !to.equals(from)) {
|
||||||
String details = String.format("Destination file path [%s] already exists and will be deleted.", to.getName());
|
String details = String.format("Destination file path [%s] already exists. Fix via Override.", to.getName());
|
||||||
issues.put(Conflict.Kind.DESTINATION_ALREADY_EXISTS, details);
|
issues.put(Conflict.Kind.FILE_EXISTS, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (issues.size() > 0) {
|
if (issues.size() > 0) {
|
||||||
// allow override if this is the only issue
|
// allow override if this is the only issue
|
||||||
boolean override = issues.containsKey(Conflict.Kind.DESTINATION_ALREADY_EXISTS) && issues.size() == 1;
|
boolean override = issues.containsKey(Conflict.Kind.FILE_EXISTS) && issues.size() == 1;
|
||||||
conflicts.add(new Conflict(from, to, issues, override));
|
conflicts.add(new Conflict(from, to, issues, override));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user