diff --git a/source/net/filebot/StandardRenameAction.java b/source/net/filebot/StandardRenameAction.java index 478f5154..ed0132ca 100644 --- a/source/net/filebot/StandardRenameAction.java +++ b/source/net/filebot/StandardRenameAction.java @@ -11,6 +11,8 @@ import java.nio.file.LinkOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.List; +import com.sun.jna.Platform; + import net.filebot.util.FileUtilities; public enum StandardRenameAction implements RenameAction { @@ -79,6 +81,35 @@ public enum StandardRenameAction implements RenameAction { } }, + CLONE { + + @Override + public File rename(File from, File to) throws Exception { + File dest = FileUtilities.resolveDestination(from, to); + + // clonefile or reflink requires filesystem that supports copy-on-write (e.g. apfs or btrfs) + ProcessBuilder process = new ProcessBuilder(); + + if (Platform.isMac()) { + // -c copy files using clonefile + process.command("cp", "-c", "-f", from.getPath(), dest.getPath()); + } else { + // --reflink copy files using reflink + process.command("cp", "--reflink", "--force", from.isDirectory() ? "--recursive" : "--no-target-directory", from.getPath(), dest.getPath()); + } + + process.directory(from.getParentFile()); + process.inheritIO(); + + int exitCode = process.start().waitFor(); + if (exitCode != 0) { + throw new IOException(String.format("%s failed with exit code %d", process.command(), exitCode)); + } + + return dest; + } + }, + DUPLICATE { @Override @@ -91,26 +122,6 @@ public enum StandardRenameAction implements RenameAction { } }, - REFLINK { - - @Override - public File rename(File from, File to) throws Exception { - File dest = FileUtilities.resolveDestination(from, to); - - // reflink requires Linux and a filesystem that supports copy-on-write (e.g. btrfs) - ProcessBuilder process = new ProcessBuilder("cp", "--reflink", "--force", from.isDirectory() ? "--recursive" : "--no-target-directory", from.getPath(), dest.getPath()); - process.directory(from.getParentFile()); - process.inheritIO(); - - int exitCode = process.start().waitFor(); - if (exitCode != 0) { - throw new IOException(String.format("reflink: %s failed with exit code %d", process.command(), exitCode)); - } - - return dest; - } - }, - TEST { @Override @@ -136,10 +147,10 @@ public enum StandardRenameAction implements RenameAction { return "Symlink"; case HARDLINK: return "Hardlink"; + case CLONE: + return "Clone"; case DUPLICATE: return "Hardlink or Copy"; - case REFLINK: - return "Lightweight Copy"; default: return "Test"; } @@ -157,10 +168,10 @@ public enum StandardRenameAction implements RenameAction { return "Symlinking"; case HARDLINK: return "Hardlinking"; + case CLONE: + return "Cloning"; case DUPLICATE: return "Duplicating"; - case REFLINK: - return "Reflinking"; default: return "Testing"; } diff --git a/source/net/filebot/cli/ArgumentBean.java b/source/net/filebot/cli/ArgumentBean.java index 1e16d921..ac8c6b76 100644 --- a/source/net/filebot/cli/ArgumentBean.java +++ b/source/net/filebot/cli/ArgumentBean.java @@ -60,7 +60,7 @@ public class ArgumentBean { @Option(name = "--order", usage = "Episode order", metaVar = "[Airdate, Absolute, DVD]") public String order = "Airdate"; - @Option(name = "--action", usage = "Rename action", metaVar = "[move, copy, keeplink, symlink, hardlink, reflink, test]") + @Option(name = "--action", usage = "Rename action", metaVar = "[move, copy, keeplink, symlink, hardlink, clone, test]") public String action = "move"; @Option(name = "--conflict", usage = "Conflict resolution", metaVar = "[skip, override, auto, index, fail]") diff --git a/source/net/filebot/resources/action.clone.png b/source/net/filebot/resources/action.clone.png new file mode 100644 index 00000000..3bd472dd Binary files /dev/null and b/source/net/filebot/resources/action.clone.png differ diff --git a/source/net/filebot/resources/action.clone@2x.png b/source/net/filebot/resources/action.clone@2x.png new file mode 100644 index 00000000..44c3989c Binary files /dev/null and b/source/net/filebot/resources/action.clone@2x.png differ diff --git a/source/net/filebot/resources/rename.action.clone.png b/source/net/filebot/resources/rename.action.clone.png new file mode 100644 index 00000000..a3b28a6e Binary files /dev/null and b/source/net/filebot/resources/rename.action.clone.png differ diff --git a/source/net/filebot/resources/rename.action.clone@2x.png b/source/net/filebot/resources/rename.action.clone@2x.png new file mode 100644 index 00000000..3bd472dd Binary files /dev/null and b/source/net/filebot/resources/rename.action.clone@2x.png differ diff --git a/source/net/filebot/ui/rename/Preset.java b/source/net/filebot/ui/rename/Preset.java index da9c416c..ec4a5b11 100644 --- a/source/net/filebot/ui/rename/Preset.java +++ b/source/net/filebot/ui/rename/Preset.java @@ -2,6 +2,7 @@ package net.filebot.ui.rename; import static java.util.Collections.*; import static net.filebot.Logging.*; +import static net.filebot.Settings.*; import static net.filebot.WebServices.*; import static net.filebot.util.FileUtilities.*; @@ -147,7 +148,14 @@ public class Preset { } public static StandardRenameAction[] getSupportedActions() { - return new StandardRenameAction[] { StandardRenameAction.MOVE, StandardRenameAction.COPY, StandardRenameAction.KEEPLINK, StandardRenameAction.SYMLINK, StandardRenameAction.HARDLINK }; + if (isWindowsApp()) { + // CoW clones not supported on Windows + return new StandardRenameAction[] { StandardRenameAction.MOVE, StandardRenameAction.COPY, StandardRenameAction.KEEPLINK, StandardRenameAction.SYMLINK, StandardRenameAction.HARDLINK }; + } else { + // CoW clones / reflinks supported on macOS and Linux + return new StandardRenameAction[] { StandardRenameAction.MOVE, StandardRenameAction.COPY, StandardRenameAction.KEEPLINK, StandardRenameAction.SYMLINK, StandardRenameAction.HARDLINK, StandardRenameAction.CLONE }; + } + } public static Language[] getSupportedLanguages() { diff --git a/source/net/filebot/ui/rename/RenamePanel.java b/source/net/filebot/ui/rename/RenamePanel.java index 01e417e8..316aff50 100644 --- a/source/net/filebot/ui/rename/RenamePanel.java +++ b/source/net/filebot/ui/rename/RenamePanel.java @@ -22,7 +22,6 @@ import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; import java.io.File; import java.util.ArrayList; -import java.util.EnumSet; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -547,7 +546,7 @@ public class RenamePanel extends JComponent { actionPopup.addSeparator(); actionPopup.addDescription(new JLabel("Action:")); - for (StandardRenameAction action : EnumSet.of(StandardRenameAction.MOVE, StandardRenameAction.COPY, StandardRenameAction.KEEPLINK, StandardRenameAction.SYMLINK, StandardRenameAction.HARDLINK)) { + for (StandardRenameAction action : Preset.getSupportedActions()) { actionPopup.add(new SetRenameAction(action)); }