diff --git a/source/net/sourceforge/filebot/cli/StrictMetric.java b/source/net/sourceforge/filebot/cli/StrictMetric.java index d882a97b..a05c68fe 100644 --- a/source/net/sourceforge/filebot/cli/StrictMetric.java +++ b/source/net/sourceforge/filebot/cli/StrictMetric.java @@ -10,7 +10,7 @@ import net.sourceforge.filebot.ui.rename.MatchSimilarityMetric; public enum StrictMetric implements SimilarityMetric { - EpisodeIdentifier(MatchSimilarityMetric.EpisodeIdentifier, 1), // only allow 0 or 1 + EpisodeIdentifier(MatchSimilarityMetric.StrictEpisodeIdentifier, 1), // only allow 0 or 1 Title(MatchSimilarityMetric.SubstringFields, 2), // allow 0 or .5 or 1 Name(MatchSimilarityMetric.Name, 2); // allow 0 or .5 or 1 diff --git a/source/net/sourceforge/filebot/similarity/MetricCascade.java b/source/net/sourceforge/filebot/similarity/MetricCascade.java new file mode 100644 index 00000000..4c1647d2 --- /dev/null +++ b/source/net/sourceforge/filebot/similarity/MetricCascade.java @@ -0,0 +1,33 @@ + +package net.sourceforge.filebot.similarity; + + +import static java.lang.Math.*; + + +public class MetricCascade implements SimilarityMetric { + + private final SimilarityMetric[] cascade; + + + public MetricCascade(SimilarityMetric... cascade) { + this.cascade = cascade; + } + + + @Override + public float getSimilarity(Object o1, Object o2) { + float f = 0; + for (SimilarityMetric metric : cascade) { + f = max(f, metric.getSimilarity(o1, o2)); + + // is match, ignore remaining metrics + if (f >= 1) { + return f; + } + } + + return f; + } + +} diff --git a/source/net/sourceforge/filebot/ui/rename/MatchSimilarityMetric.java b/source/net/sourceforge/filebot/ui/rename/MatchSimilarityMetric.java index 8c2357ae..b20a5b5b 100644 --- a/source/net/sourceforge/filebot/ui/rename/MatchSimilarityMetric.java +++ b/source/net/sourceforge/filebot/ui/rename/MatchSimilarityMetric.java @@ -15,6 +15,7 @@ import java.util.WeakHashMap; import net.sourceforge.filebot.similarity.DateMetric; import net.sourceforge.filebot.similarity.FileSizeMetric; +import net.sourceforge.filebot.similarity.MetricCascade; import net.sourceforge.filebot.similarity.NameSimilarityMetric; import net.sourceforge.filebot.similarity.NumericSimilarityMetric; import net.sourceforge.filebot.similarity.SeasonEpisodeMetric; @@ -29,46 +30,10 @@ import net.sourceforge.filebot.web.Movie; public enum MatchSimilarityMetric implements SimilarityMetric { - // Match by file length (only works when matching torrents or files) - FileSize(new FileSizeMetric() { - - @Override - public float getSimilarity(Object o1, Object o2) { - // order of arguments is logically irrelevant, but we might be able to save us a call to File.length() which is quite costly - return o1 instanceof File ? super.getSimilarity(o2, o1) : super.getSimilarity(o1, o2); - } - - - @Override - protected long getLength(Object object) { - if (object instanceof AbstractFile) { - return ((AbstractFile) object).getLength(); - } - - return super.getLength(object); - } - }), - - // Match by season/episode and airdate combined - EpisodeIdentifier(new SimilarityMetric() { - - @Override - public float getSimilarity(Object o1, Object o2) { - float sxeSimilarity = SeasonEpisode.getSimilarity(o1, o2); - - // break if SxE is a perfect match already - if (sxeSimilarity >= 1) - return sxeSimilarity; - - return max(sxeSimilarity, AirDate.getSimilarity(o1, o2)); - } - - }), - // Match by season / episode numbers SeasonEpisode(new SeasonEpisodeMetric() { - private final Map> matchCache = synchronizedMap(new WeakHashMap>(64, 4)); + private final Map> transformCache = synchronizedMap(new WeakHashMap>(64, 4)); @Override @@ -77,7 +42,7 @@ public enum MatchSimilarityMetric implements SimilarityMetric { return emptySet(); } - Collection result = matchCache.get(object); + Collection result = transformCache.get(object); if (result != null) { return result; } @@ -94,7 +59,7 @@ public enum MatchSimilarityMetric implements SimilarityMetric { result = super.parse(object); } - matchCache.put(object, result); + transformCache.put(object, result); return result; } }), @@ -102,7 +67,7 @@ public enum MatchSimilarityMetric implements SimilarityMetric { // Match episode airdate AirDate(new DateMetric() { - private final Map matchCache = synchronizedMap(new WeakHashMap(64, 4)); + private final Map transformCache = synchronizedMap(new WeakHashMap(64, 4)); @Override @@ -118,17 +83,38 @@ public enum MatchSimilarityMetric implements SimilarityMetric { return episode.airdate(); } - Date result = matchCache.get(object); + Date result = transformCache.get(object); if (result != null) { return result; } result = super.parse(object); - matchCache.put(object, result); + transformCache.put(object, result); return result; } }), + // Match by episode/movie title + Title(new SubstringMetric() { + + @Override + protected String normalize(Object object) { + if (object instanceof Episode) { + object = ((Episode) object).getTitle(); + } + + if (object instanceof Movie) { + object = ((Movie) object).getName(); + } + + return normalizeObject(object); + } + }), + + // Match by combining season/episode, episode airdate and movie/episode title + GeneralEpisodeIdentifier(new MetricCascade(SeasonEpisode, AirDate, Title)), + StrictEpisodeIdentifier(new MetricCascade(SeasonEpisode, AirDate)), + // Match series title and episode title against folder structure and file name SubstringFields(new SubstringMetric() { @@ -170,7 +156,7 @@ public enum MatchSimilarityMetric implements SimilarityMetric { if (object instanceof File) { File file = (File) object; - return new Object[] { file.getParentFile(), file }; + return new Object[] { file.getParentFile().getAbsolutePath(), file }; } if (object instanceof Movie) { @@ -178,10 +164,6 @@ public enum MatchSimilarityMetric implements SimilarityMetric { return new Object[] { movie.getName(), movie.getYear() }; } - if (object instanceof AbstractFile) { - return new Object[] { (AbstractFile) object }; - } - return new Object[] { object }; } }), @@ -212,6 +194,26 @@ public enum MatchSimilarityMetric implements SimilarityMetric { // simplify file name, if possible return normalizeObject(object); } + }), + + // Match by file length (only works when matching torrents or files) + FileSize(new FileSizeMetric() { + + @Override + public float getSimilarity(Object o1, Object o2) { + // order of arguments is logically irrelevant, but we might be able to save us a call to File.length() which is quite costly + return o1 instanceof File ? super.getSimilarity(o2, o1) : super.getSimilarity(o1, o2); + } + + + @Override + protected long getLength(Object object) { + if (object instanceof AbstractFile) { + return ((AbstractFile) object).getLength(); + } + + return super.getLength(object); + } }); // inner metric @@ -257,9 +259,9 @@ public enum MatchSimilarityMetric implements SimilarityMetric { // 4. pass: match by generic name similarity (slow, but most matches will have been determined in second pass) // 5. pass: match by generic numeric similarity if (includeFileMetrics) { - return new SimilarityMetric[] { FileSize, EpisodeIdentifier, SubstringFields, Name, Numeric }; + return new SimilarityMetric[] { FileSize, GeneralEpisodeIdentifier, StrictEpisodeIdentifier, SubstringFields, Name, Numeric }; } else { - return new SimilarityMetric[] { EpisodeIdentifier, SubstringFields, Name, Numeric }; + return new SimilarityMetric[] { GeneralEpisodeIdentifier, StrictEpisodeIdentifier, SubstringFields, Name, Numeric }; } } diff --git a/test/net/sourceforge/filebot/ui/rename/MatchSimilarityMetricTest.java b/test/net/sourceforge/filebot/ui/rename/MatchSimilarityMetricTest.java index 20d86ba9..63034de9 100644 --- a/test/net/sourceforge/filebot/ui/rename/MatchSimilarityMetricTest.java +++ b/test/net/sourceforge/filebot/ui/rename/MatchSimilarityMetricTest.java @@ -60,7 +60,7 @@ public class MatchSimilarityMetricTest { episodes.add(new Episode("Veronica Mars", null, 1, 19, "Hot Dogs")); episodes.add(new Episode("Greek", null, 1, 19, "No Campus for Old Rules")); - SimilarityMetric[] metrics = new SimilarityMetric[] { EpisodeIdentifier, SubstringFields }; + SimilarityMetric[] metrics = new SimilarityMetric[] { GeneralEpisodeIdentifier, SubstringFields }; List> m = new Matcher(files, episodes, true, metrics).match(); assertEquals("Greek - S01E19 - No Campus for Old Rules", m.get(0).getValue().getName());