diff --git a/build.xml b/build.xml index 49b63a4f..98c635b1 100644 --- a/build.xml +++ b/build.xml @@ -181,6 +181,10 @@ + + + + diff --git a/lib/native/mac-x86_64/libxattrj.dylib b/lib/native/mac-x86_64/libxattrj.dylib new file mode 100644 index 00000000..51687e40 Binary files /dev/null and b/lib/native/mac-x86_64/libxattrj.dylib differ diff --git a/lib/xattrj.jar b/lib/xattrj.jar new file mode 100644 index 00000000..12ed6d4a Binary files /dev/null and b/lib/xattrj.jar differ diff --git a/source/net/filebot/MetaAttributeView.java b/source/net/filebot/MetaAttributeView.java index 74dbb4ef..8a207dca 100644 --- a/source/net/filebot/MetaAttributeView.java +++ b/source/net/filebot/MetaAttributeView.java @@ -10,13 +10,30 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.UserDefinedFileAttributeView; import java.util.AbstractMap; +import java.util.Arrays; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; +import org.securityvision.xattrj.Xattrj; + +import com.sun.jna.Platform; + public class MetaAttributeView extends AbstractMap { - private final UserDefinedFileAttributeView attributeView; - private final Charset encoding; + // UserDefinedFileAttributeView (for Windows and Linux) OR 3rd party Xattrj (for Mac) because UserDefinedFileAttributeView is not supported (Oracle Java 7/8) + private Object xattr; + private File file; + private Charset encoding; + + private static Xattrj MacXattrSupport; + + private static synchronized Xattrj getMacXattrSupport() throws Throwable { + if (MacXattrSupport == null) { + MacXattrSupport = new Xattrj(); + } + return MacXattrSupport; + } public MetaAttributeView(File file) throws IOException { Path path = file.getCanonicalFile().toPath(); @@ -28,49 +45,98 @@ public class MetaAttributeView extends AbstractMap { path = link; } - attributeView = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class); - if (attributeView == null) { - throw new IOException("UserDefinedFileAttributeView is not supported"); + xattr = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class); + + // try 3rd party Xattrj library + if (xattr == null) { + if (Platform.isMac()) { + try { + xattr = getMacXattrSupport(); + } catch (Throwable e) { + throw new IOException("Unable to load library: xattrj", e); + } + } else { + throw new IOException("UserDefinedFileAttributeView is not supported"); + } } - encoding = Charset.forName("UTF-8"); + + this.file = path.toFile(); + this.encoding = Charset.forName("UTF-8"); } @Override public String get(Object key) { try { - ByteBuffer buffer = ByteBuffer.allocate(attributeView.size(key.toString())); - attributeView.read(key.toString(), buffer); - buffer.flip(); + if (xattr instanceof UserDefinedFileAttributeView) { + UserDefinedFileAttributeView attributeView = (UserDefinedFileAttributeView) xattr; + ByteBuffer buffer = ByteBuffer.allocate(attributeView.size(key.toString())); + attributeView.read(key.toString(), buffer); + buffer.flip(); - return encoding.decode(buffer).toString(); - } catch (IOException e) { - return null; + return encoding.decode(buffer).toString(); + } + + if (xattr instanceof Xattrj) { + Xattrj macXattr = (Xattrj) xattr; + return macXattr.readAttribute(file, key.toString()); + } + } catch (Exception e) { + // ignore } + + return null; } @Override public String put(String key, String value) { try { - if (value == null || value.isEmpty()) { - attributeView.delete(key); - } else { - attributeView.write(key, encoding.encode(value)); + if (xattr instanceof UserDefinedFileAttributeView) { + UserDefinedFileAttributeView attributeView = (UserDefinedFileAttributeView) xattr; + if (value == null || value.isEmpty()) { + attributeView.delete(key); + } else { + attributeView.write(key, encoding.encode(value)); + } } - } catch (IOException e) { + + if (xattr instanceof Xattrj) { + Xattrj macXattr = (Xattrj) xattr; + if (value == null || value.isEmpty()) { + macXattr.removeAttribute(file, key); + } else { + macXattr.writeAttribute(file, key, value); + } + } + } catch (Exception e) { throw new IllegalStateException(e); } + return null; // since we don't know the old value } + public List list() throws IOException { + if (xattr instanceof UserDefinedFileAttributeView) { + UserDefinedFileAttributeView attributeView = (UserDefinedFileAttributeView) xattr; + return attributeView.list(); + } + + if (xattr instanceof Xattrj) { + Xattrj macXattr = (Xattrj) xattr; + return Arrays.asList(macXattr.listAttributes(file)); + } + + return null; + } + @Override public Set> entrySet() { try { Set> entries = new LinkedHashSet>(); - for (String name : attributeView.list()) { + for (String name : this.list()) { entries.add(new AttributeEntry(name)); } return entries; - } catch (IOException e) { + } catch (Exception e) { throw new IllegalStateException(e); } } @@ -78,28 +144,10 @@ public class MetaAttributeView extends AbstractMap { @Override public void clear() { try { - for (String it : attributeView.list()) { - attributeView.delete(it); + for (String key : this.list()) { + this.put(key, null); } - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - @Override - public int size() { - try { - return attributeView.list().size(); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - @Override - public boolean isEmpty() { - try { - return attributeView.list().isEmpty(); - } catch (IOException e) { + } catch (Exception e) { throw new IllegalStateException(e); } } diff --git a/source/net/filebot/cli/ScriptShellMethods.java b/source/net/filebot/cli/ScriptShellMethods.java index 26eabd56..edcc7dd1 100644 --- a/source/net/filebot/cli/ScriptShellMethods.java +++ b/source/net/filebot/cli/ScriptShellMethods.java @@ -24,6 +24,7 @@ import java.util.Map; import net.filebot.MediaTypes; import net.filebot.MetaAttributeView; +import net.filebot.Settings; import net.filebot.media.MediaDetection; import net.filebot.media.MetaAttributes; import net.filebot.similarity.NameSimilarityMetric; @@ -362,17 +363,24 @@ public class ScriptShellMethods { public static MetaAttributeView getXattr(File self) { try { - return new MetaAttributeView(self); + if (Settings.useExtendedFileAttributes()) { + return new MetaAttributeView(self); + } } catch (Exception e) { - return null; + // ignore } + return null; } public static Object getMetadata(File self) { try { - return new MetaAttributes(self); + if (Settings.useExtendedFileAttributes()) { + return new MetaAttributes(self); + } } catch (Exception e) { - return null; + // ignore } + return null; } + } diff --git a/source/net/filebot/media/MetaAttributes.java b/source/net/filebot/media/MetaAttributes.java index db54c5e2..3cd67f68 100644 --- a/source/net/filebot/media/MetaAttributes.java +++ b/source/net/filebot/media/MetaAttributes.java @@ -61,8 +61,8 @@ public class MetaAttributes { } public void clear() { - metaAttributeView.remove(FILENAME_KEY); - metaAttributeView.remove(METADATA_KEY); + metaAttributeView.put(FILENAME_KEY, null); + metaAttributeView.put(METADATA_KEY, null); } } diff --git a/source/net/filebot/ui/rename/HistoryDialog.java b/source/net/filebot/ui/rename/HistoryDialog.java index 80b0ec26..d4ef5bc7 100644 --- a/source/net/filebot/ui/rename/HistoryDialog.java +++ b/source/net/filebot/ui/rename/HistoryDialog.java @@ -68,6 +68,8 @@ import net.filebot.History; import net.filebot.History.Element; import net.filebot.History.Sequence; import net.filebot.ResourceManager; +import net.filebot.Settings; +import net.filebot.media.MetaAttributes; import net.filebot.ui.transfer.FileExportHandler; import net.filebot.ui.transfer.FileTransferablePolicy; import net.filebot.ui.transfer.LoadAction; @@ -534,8 +536,13 @@ class HistoryDialog extends JDialog { for (Entry entry : getRenameMap(directory).entrySet()) { try { - moveRename(entry.getKey(), entry.getValue()); + File destination = moveRename(entry.getKey(), entry.getValue()); count++; + + // remove xattr that may have been set + if (Settings.useExtendedFileAttributes()) { + new MetaAttributes(destination).clear(); + } } catch (Exception e) { Logger.getLogger(HistoryDialog.class.getName()).log(Level.SEVERE, e.getMessage(), e); }