* improved matching using a 2-level narrowing episode identifier metric sequence

This commit is contained in:
Reinhard Pointner 2011-11-22 16:08:36 +00:00
parent e7d697df0a
commit 4b5f512fcf
4 changed files with 86 additions and 51 deletions

View File

@ -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

View File

@ -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;
}
}

View File

@ -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<Object, Collection<SxE>> matchCache = synchronizedMap(new WeakHashMap<Object, Collection<SxE>>(64, 4));
private final Map<Object, Collection<SxE>> transformCache = synchronizedMap(new WeakHashMap<Object, Collection<SxE>>(64, 4));
@Override
@ -77,7 +42,7 @@ public enum MatchSimilarityMetric implements SimilarityMetric {
return emptySet();
}
Collection<SxE> result = matchCache.get(object);
Collection<SxE> 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<Object, Date> matchCache = synchronizedMap(new WeakHashMap<Object, Date>(64, 4));
private final Map<Object, Date> transformCache = synchronizedMap(new WeakHashMap<Object, Date>(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 };
}
}

View File

@ -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<Match<File, Episode>> m = new Matcher<File, Episode>(files, episodes, true, metrics).match();
assertEquals("Greek - S01E19 - No Campus for Old Rules", m.get(0).getValue().getName());