+ ReleaseDate metric matching step for differentiating multiple shows with the same name, usually nudging things towards the more recent episode/series.
This commit is contained in:
parent
bf6cccfbbb
commit
a248021ebf
|
@ -284,6 +284,26 @@ public enum EpisodeMetrics implements SimilarityMetric {
|
|||
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
|
||||
// Match by file last modified and episode release dates
|
||||
TimeStamp(new TimeStampMetric() {
|
||||
|
||||
@Override
|
||||
public float getSimilarity(Object o1, Object o2) {
|
||||
// adjust differentiation accuracy to about a year
|
||||
return (float) (floor(super.getSimilarity(o1, o2) * 40) / 40);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long getTimeStamp(Object object) {
|
||||
if (object instanceof Episode) {
|
||||
return ((Episode) object).airdate().getTimeStamp();
|
||||
}
|
||||
|
||||
return super.getTimeStamp(object);
|
||||
}
|
||||
});
|
||||
|
||||
// inner metric
|
||||
|
@ -338,11 +358,12 @@ public enum EpisodeMetrics implements SimilarityMetric {
|
|||
// 4 pass: divide by folder / file name and show name / episode title
|
||||
// 5 pass: divide by name (rounded into n levels)
|
||||
// 6 pass: divide by generic numeric similarity
|
||||
// 7 pass: resolve remaining collisions via absolute string similarity
|
||||
// 7 pass: prefer episodes that were aired closer to the last modified date of the file
|
||||
// 8 pass: resolve remaining collisions via absolute string similarity
|
||||
if (includeFileMetrics) {
|
||||
return new SimilarityMetric[] { FileSize, new MetricCascade(FileName, EpisodeFunnel), EpisodeBalancer, SubstringFields, new MetricCascade(SubstringSequence, Name), Numeric, new NameSimilarityMetric() };
|
||||
return new SimilarityMetric[] { FileSize, new MetricCascade(FileName, EpisodeFunnel), EpisodeBalancer, SubstringFields, new MetricCascade(SubstringSequence, Name), Numeric, Name, TimeStamp, new NameSimilarityMetric() };
|
||||
} else {
|
||||
return new SimilarityMetric[] { EpisodeFunnel, EpisodeBalancer, SubstringFields, new MetricCascade(SubstringSequence, Name), Numeric, new NameSimilarityMetric() };
|
||||
return new SimilarityMetric[] { EpisodeFunnel, EpisodeBalancer, SubstringFields, new MetricCascade(SubstringSequence, Name), Numeric, Name, TimeStamp, new NameSimilarityMetric() };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
package net.sourceforge.filebot.similarity;
|
||||
|
||||
|
||||
import static java.lang.Math.*;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
||||
public class TimeStampMetric implements SimilarityMetric {
|
||||
|
||||
@Override
|
||||
public float getSimilarity(Object o1, Object o2) {
|
||||
long t1 = getTimeStamp(o1);
|
||||
long t2 = getTimeStamp(o2);
|
||||
|
||||
if (t1 <= 0 || t2 <= 0)
|
||||
return 0;
|
||||
|
||||
float min = min(t1, t2);
|
||||
float max = max(t1, t2);
|
||||
|
||||
return min / max;
|
||||
}
|
||||
|
||||
|
||||
public long getTimeStamp(Object obj) {
|
||||
if (obj instanceof File) {
|
||||
return ((File) obj).lastModified();
|
||||
}
|
||||
if (obj instanceof Number) {
|
||||
return ((Number) obj).longValue();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
|
@ -3,9 +3,8 @@ package net.sourceforge.filebot.subtitle;
|
|||
|
||||
|
||||
import static java.lang.Math.*;
|
||||
import static java.util.Arrays.*;
|
||||
import static java.util.Collections.*;
|
||||
import static net.sourceforge.filebot.MediaTypes.*;
|
||||
import static net.sourceforge.filebot.similarity.EpisodeMetrics.*;
|
||||
import static net.sourceforge.filebot.similarity.Normalization.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
|
||||
|
@ -30,6 +29,7 @@ import net.sourceforge.filebot.similarity.EpisodeMetrics;
|
|||
import net.sourceforge.filebot.similarity.Match;
|
||||
import net.sourceforge.filebot.similarity.Matcher;
|
||||
import net.sourceforge.filebot.similarity.MetricAvg;
|
||||
import net.sourceforge.filebot.similarity.MetricCascade;
|
||||
import net.sourceforge.filebot.similarity.NameSimilarityMetric;
|
||||
import net.sourceforge.filebot.similarity.SequenceMatchSimilarity;
|
||||
import net.sourceforge.filebot.similarity.SimilarityMetric;
|
||||
|
@ -46,10 +46,8 @@ public final class SubtitleUtilities {
|
|||
public static Map<File, SubtitleDescriptor> matchSubtitles(Collection<File> files, Collection<SubtitleDescriptor> subtitles, boolean strict) throws InterruptedException {
|
||||
Map<File, SubtitleDescriptor> subtitleByVideo = new LinkedHashMap<File, SubtitleDescriptor>();
|
||||
|
||||
SimilarityMetric[] metrics = EpisodeMetrics.defaultSequence(false);
|
||||
|
||||
// optimize for generic media <-> subtitle matching
|
||||
replaceAll(asList(metrics), EpisodeMetrics.SubstringFields, EpisodeMetrics.SubstringSequence);
|
||||
SimilarityMetric[] metrics = new SimilarityMetric[] { EpisodeFunnel, EpisodeBalancer, SubstringSequence, new MetricCascade(SubstringSequence, Name), Numeric, new NameSimilarityMetric() };
|
||||
|
||||
// first match everything as best as possible, then filter possibly bad matches
|
||||
Matcher<File, SubtitleDescriptor> matcher = new Matcher<File, SubtitleDescriptor>(files, subtitles, false, metrics);
|
||||
|
|
|
@ -21,34 +21,39 @@ public class Date implements Serializable {
|
|||
private int month;
|
||||
private int day;
|
||||
|
||||
|
||||
|
||||
protected Date() {
|
||||
// used by serializer
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Date(int year, int month, int day) {
|
||||
this.year = year;
|
||||
this.month = month;
|
||||
this.day = day;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getYear() {
|
||||
return year;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getMonth() {
|
||||
return month;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getDay() {
|
||||
return day;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public long getTimeStamp() {
|
||||
return new GregorianCalendar(year, month, day).getTimeInMillis();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof Date) {
|
||||
|
@ -59,29 +64,29 @@ public class Date implements Serializable {
|
|||
return super.equals(obj);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(new Object[] { year, month, day });
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%04d-%02d-%02d", year, month, day);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String format(String pattern) {
|
||||
return format(pattern, Locale.ROOT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String format(String pattern, Locale locale) {
|
||||
return new SimpleDateFormat(pattern, locale).format(new GregorianCalendar(year, month - 1, day).getTime()); // Calendar months start at 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static Date parse(String string, String pattern) {
|
||||
if (string == null || string.isEmpty())
|
||||
return null;
|
||||
|
|
Loading…
Reference in New Issue