diff --git a/source/net/filebot/mediainfo/ImageMetadata.java b/source/net/filebot/mediainfo/ImageMetadata.java index e872d5a7..93c571c5 100644 --- a/source/net/filebot/mediainfo/ImageMetadata.java +++ b/source/net/filebot/mediainfo/ImageMetadata.java @@ -136,7 +136,7 @@ public class ImageMetadata { try { return Optional.ofNullable(extract.apply(metadata)); } catch (Exception e) { - debug.warning(e::toString); + debug.finest(format("Failed to extract image metadata: %s", e)); } return Optional.empty(); } diff --git a/source/net/filebot/resources/search.exif.png b/source/net/filebot/resources/search.exif.png new file mode 100644 index 00000000..0a7ef900 Binary files /dev/null and b/source/net/filebot/resources/search.exif.png differ diff --git a/source/net/filebot/resources/search.exif@2x.png b/source/net/filebot/resources/search.exif@2x.png new file mode 100644 index 00000000..e8608c23 Binary files /dev/null and b/source/net/filebot/resources/search.exif@2x.png differ diff --git a/source/net/filebot/ui/rename/FormatDialog.properties b/source/net/filebot/ui/rename/FormatDialog.properties index 16e098ce..3e4f7444 100644 --- a/source/net/filebot/ui/rename/FormatDialog.properties +++ b/source/net/filebot/ui/rename/FormatDialog.properties @@ -49,14 +49,14 @@ music.example[4]: {n}/{album+'/'}{pi.pad(2)+'. '} {t} music.example[5]: {home}/Media/{plex} # simple filename without extension -file.example[0]: {i.pad(3)} - {n} -# simple filter -file.example[1]: {n.after('-')} +file.example[0]: {i.pad(3)} - {n.after('-')} # remove special characters -file.example[2]: {n.ascii().normalizePunctuation().space('_')} +file.example[1]: {n.ascii().normalizePunctuation().space('_')} # remove release info patterns -file.example[3]: {n.stripReleaseInfo().space(' ')} +file.example[2]: {n.stripReleaseInfo().space(' ')} # generate name from iTunes ID3 tags -file.example[4]: {media.Album} - {media.PartID} - {media.Title} +file.example[3]: {media.Album} - {media.PartID} - {media.Title} # restore original filename via embedded title metadata (if possible) -file.example[5]: {any{mediaTitle}{n}} +file.example[4]: {any{mediaTitle}{n}} +# sort by Last-Modified or Date-Taken +file.example[5]: {dt.format('yyyy-MM-dd HH\u2236mm\u2236ss')} diff --git a/source/net/filebot/ui/rename/PhotoFileMatcher.java b/source/net/filebot/ui/rename/PhotoFileMatcher.java new file mode 100644 index 00000000..1a13aac1 --- /dev/null +++ b/source/net/filebot/ui/rename/PhotoFileMatcher.java @@ -0,0 +1,58 @@ +package net.filebot.ui.rename; + +import static net.filebot.Logging.*; +import static net.filebot.util.FileUtilities.*; + +import java.awt.Component; +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Locale; + +import javax.swing.Icon; + +import net.filebot.ResourceManager; +import net.filebot.mediainfo.ImageMetadata; +import net.filebot.similarity.Match; +import net.filebot.web.Datasource; +import net.filebot.web.SortOrder; + +public class PhotoFileMatcher implements Datasource, AutoCompleteMatcher { + + public static final PhotoFileMatcher INSTANCE = new PhotoFileMatcher(); + + @Override + public String getIdentifier() { + return "exif"; + } + + @Override + public String getName() { + return "Exif Metadata"; + } + + @Override + public Icon getIcon() { + return ResourceManager.getIcon("search.exif"); + } + + @Override + public List> match(Collection files, boolean strict, SortOrder order, Locale locale, boolean autodetection, Component parent) throws Exception { + List> matches = new ArrayList>(); + + for (File f : filter(files, ImageMetadata.SUPPORTED_FILE_TYPES)) { + try { + ImageMetadata metadata = new ImageMetadata(f); + if (metadata.getDateTaken().isPresent()) { + matches.add(new Match(f, f)); // photo mode is the same as generic file mode (but only select photo files) + } + } catch (Exception e) { + debug.warning(format("%s [%s]", e, f)); + } + } + + return matches; + } + +} diff --git a/source/net/filebot/ui/rename/PlainFileMatcher.java b/source/net/filebot/ui/rename/PlainFileMatcher.java index 68ec530e..4addf231 100644 --- a/source/net/filebot/ui/rename/PlainFileMatcher.java +++ b/source/net/filebot/ui/rename/PlainFileMatcher.java @@ -17,6 +17,8 @@ import net.filebot.web.SortOrder; public class PlainFileMatcher implements Datasource, AutoCompleteMatcher { + public static final PlainFileMatcher INSTANCE = new PlainFileMatcher(); + @Override public String getIdentifier() { return "file"; diff --git a/source/net/filebot/ui/rename/Preset.java b/source/net/filebot/ui/rename/Preset.java index a8f453c1..85e226da 100644 --- a/source/net/filebot/ui/rename/Preset.java +++ b/source/net/filebot/ui/rename/Preset.java @@ -18,7 +18,6 @@ import net.filebot.format.ExpressionFileFilter; import net.filebot.format.ExpressionFileFormat; import net.filebot.format.ExpressionFilter; import net.filebot.format.ExpressionFormat; -import net.filebot.media.XattrMetaInfoProvider; import net.filebot.web.Datasource; import net.filebot.web.EpisodeListProvider; import net.filebot.web.MovieIdentificationService; @@ -120,11 +119,12 @@ public class Preset { return new MusicMatcher((MusicIdentificationService) db); } - if (db instanceof XattrMetaInfoProvider) { - return XATTR_FILE_MATCHER; + // PhotoFileMatcher / XattrFileMatcher / PlainFileMatcher + if (db instanceof AutoCompleteMatcher) { + return (AutoCompleteMatcher) db; } - return PLAIN_FILE_MATCHER; // default to plain file matcher + throw new IllegalStateException("Illegal datasource: " + db); } @Override @@ -132,13 +132,12 @@ public class Preset { return name; } - public static final XattrFileMatcher XATTR_FILE_MATCHER = new XattrFileMatcher(); - public static final PlainFileMatcher PLAIN_FILE_MATCHER = new PlainFileMatcher(); - public static Datasource[] getSupportedServices() { - Stream services = Stream.of(getEpisodeListProviders(), getMovieIdentificationServices(), getMusicIdentificationServices()).flatMap(Stream::of); - services = Stream.concat(services, Stream.of(XATTR_FILE_MATCHER, PLAIN_FILE_MATCHER)); - return services.toArray(Datasource[]::new); + return Stream.of(getEpisodeListProviders(), getMovieIdentificationServices(), getMusicIdentificationServices(), getGenericFileMatcherServices()).flatMap(Stream::of).toArray(Datasource[]::new); + } + + public static Datasource[] getGenericFileMatcherServices() { + return new Datasource[] { PhotoFileMatcher.INSTANCE, XattrFileMatcher.INSTANCE, PlainFileMatcher.INSTANCE }; } public static StandardRenameAction[] getSupportedActions() { diff --git a/source/net/filebot/ui/rename/XattrFileMatcher.java b/source/net/filebot/ui/rename/XattrFileMatcher.java index dbb093bb..26c3edc4 100644 --- a/source/net/filebot/ui/rename/XattrFileMatcher.java +++ b/source/net/filebot/ui/rename/XattrFileMatcher.java @@ -13,6 +13,8 @@ import net.filebot.web.SortOrder; public class XattrFileMatcher extends XattrMetaInfoProvider implements AutoCompleteMatcher { + public static final XattrFileMatcher INSTANCE = new XattrFileMatcher(); + @Override public List> match(Collection files, boolean strict, SortOrder order, Locale locale, boolean autodetection, Component parent) throws Exception { List> matches = new ArrayList>();