Support multi-episode objects where each episode member has the same SxE numbers

@see https://www.filebot.net/forums/viewtopic.php?f=8&t=3456
This commit is contained in:
Reinhard Pointner 2016-02-26 16:35:59 +00:00
parent 55491a137d
commit 07037b34ce
4 changed files with 67 additions and 84 deletions

View File

@ -1,6 +1,8 @@
package net.filebot.similarity;
import static java.util.Arrays.*;
import static java.util.Collections.*;
import static net.filebot.web.EpisodeUtilities.*;
import java.io.File;
import java.util.ArrayList;
@ -12,6 +14,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate;
import net.filebot.media.SmartSeasonEpisodeMatcher;
import net.filebot.similarity.SeasonEpisodeMatcher.SxE;
@ -55,20 +58,19 @@ public class EpisodeMatcher extends Matcher<File, Object> {
File file = it.getValue();
Set<Integer> uniqueFiles = normalizeIdentifierSet(parseEpisodeIdentifer(file));
if (uniqueFiles.size() < 2)
continue;
Set<Integer> uniqueEpisodes = normalizeIdentifierSet(episodeIdentifierSets.get(file));
if (uniqueEpisodes.size() < 2)
continue;
if (uniqueFiles.equals(uniqueEpisodes)) {
List<Episode> episodes = episodeSets.get(file);
if (isMultiEpisode(episodes)) {
MultiEpisode episode = new MultiEpisode(episodes.toArray(new Episode[0]));
disjointMatchCollection.add(new Match<File, Object>(file, episode));
modified = true;
if (episodes.size() > 1) {
Episode[] episodeSequence = episodes.stream().sorted(episodeComparator()).distinct().toArray(Episode[]::new);
if (isMultiEpisode(episodeSequence)) {
MultiEpisode episode = new MultiEpisode(episodeSequence);
disjointMatchCollection.add(new Match<File, Object>(file, episode));
modified = true;
}
}
}
}
@ -123,40 +125,30 @@ public class EpisodeMatcher extends Matcher<File, Object> {
return identifier;
}
private boolean isMultiEpisode(List<Episode> episodes) {
// sanity check that there is valid episode data for at least two episodes
if (episodes.size() < 2)
private boolean isMultiEpisode(Episode[] episodes) {
if (episodes.length < 2)
return false;
// check episode sequence integrity
Integer seqIndex = null;
for (Episode it : episodes) {
// any illegal episode object breaks the chain
if (it == null)
return false;
if (it.getEpisode() == null && it.getSpecial() == null)
Integer num = it != null ? it.getEpisode() != null ? it.getEpisode() : it.getSpecial() : null;
if (num == null)
return false;
// non-sequential episode index breaks the chain
// non-sequential next episode index breaks the chain (same episode is OK since DVD numbering allows for multiple episodes to share the same SxE numbers)
if (seqIndex != null) {
Integer num = it.getEpisode() != null ? it.getEpisode() : it.getSpecial();
if (!num.equals(seqIndex + 1)) {
if (!(num.equals(seqIndex + 1) || num.equals(seqIndex))) {
return false;
}
}
seqIndex = it.getEpisode() != null ? it.getEpisode() : it.getSpecial();
seqIndex = num;
}
// check drill-down integrity
String seriesName = null;
for (Episode ep : episodes) {
if (seriesName != null && !seriesName.equals(ep.getSeriesName()))
return false;
seriesName = ep.getSeriesName();
}
return true;
return stream(episodes).map(Episode::getSeriesName).allMatch(Predicate.isEqual(episodes[0].getSeriesName()));
}
}

View File

@ -82,7 +82,7 @@ public class Episode implements Serializable {
}
public List<Integer> getNumbers() {
return Arrays.asList(season, episode, special);
return Arrays.asList(season, episode, special, absolute);
}
public List<String> getSeriesNames() {
@ -107,7 +107,7 @@ public class Episode implements Serializable {
public boolean equals(Object obj) {
if (obj instanceof Episode) {
Episode other = (Episode) obj;
return equals(season, other.season) && equals(episode, other.episode) && equals(seriesName, other.seriesName) && equals(title, other.title) && equals(special, other.special);
return equals(season, other.season) && equals(episode, other.episode) && equals(absolute, other.absolute) && equals(special, other.special) && equals(seriesName, other.seriesName) && equals(title, other.title);
}
return false;
@ -122,7 +122,7 @@ public class Episode implements Serializable {
@Override
public int hashCode() {
return Arrays.hashCode(new Object[] { seriesName, season, episode, title, special });
return Arrays.hashCode(new Object[] { season, episode, absolute, special, seriesName, title });
}
@Override

View File

@ -1,5 +1,6 @@
package net.filebot.web;
import static java.util.stream.Collectors.*;
import static net.filebot.similarity.Normalization.*;
import static net.filebot.util.StringUtilities.*;
@ -16,7 +17,7 @@ import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class EpisodeFormat extends Format {
@ -101,31 +102,23 @@ public class EpisodeFormat extends Format {
}
public String formatMultiTitle(List<Episode> episodes) {
return episodes.stream().map(e -> removeTrailingBrackets(e.getTitle())).distinct().collect(Collectors.joining(" & "));
return episodes.stream().map(e -> removeTrailingBrackets(e.getTitle())).distinct().collect(joining(" & "));
}
public String formatMultiRangeSxE(List<Episode> episodes) {
return getSeasonEpisodeNumbers(episodes).entrySet().stream().map(it -> {
if (it.getKey() >= 0) {
// season episode format
return String.format("%01dx%02d-%02d", it.getKey(), it.getValue().first(), it.getValue().last());
} else {
// absolute episode format
return String.format("%02d-%02d", it.getValue().first(), it.getValue().last());
}
}).collect(Collectors.joining("-"));
return formatMultiRangeNumbers(episodes, "%01dx", "%02d");
}
public String formatMultiRangeS00E00(List<Episode> episodes) {
return formatMultiRangeNumbers(episodes, "S%02d", "E%02d");
}
public String formatMultiRangeNumbers(List<Episode> episodes, String seasonFormat, String episodeFormat) {
return getSeasonEpisodeNumbers(episodes).entrySet().stream().map(it -> {
if (it.getKey() >= 0) {
// season episode format
return String.format("S%02dE%02d-E%02d", it.getKey(), it.getValue().first(), it.getValue().last());
} else {
// absolute episode format
return String.format("E%02d-E%02d", it.getValue().first(), it.getValue().last());
}
}).collect(Collectors.joining("-"));
String s = it.getKey() >= 0 ? String.format(seasonFormat, it.getKey()) : "";
String e = IntStream.of(it.getValue().first(), it.getValue().last()).distinct().mapToObj(i -> String.format(episodeFormat, i)).collect(joining("-"));
return s + e;
}).collect(joining(" - "));
}
private SortedMap<Integer, SortedSet<Integer>> getSeasonEpisodeNumbers(Iterable<Episode> episodes) {

View File

@ -33,42 +33,40 @@ public final class EpisodeUtilities {
}
public static Comparator<Episode> episodeComparator() {
return new Comparator<Episode>() {
@Override
public int compare(Episode a, Episode b) {
int diff = compareValue(a.getSeriesName(), b.getSeriesName());
if (diff != 0)
return diff;
diff = compareValue(a.getSeason(), b.getSeason());
if (diff != 0)
return diff;
diff = compareValue(a.getEpisode(), b.getEpisode());
if (diff != 0)
return diff;
diff = compareValue(a.getSpecial(), b.getSpecial());
if (diff != 0)
return diff;
return compareValue(a.getTitle(), b.getTitle());
}
private <T> int compareValue(Comparable<T> o1, T o2) {
if (o1 == null && o2 == null)
return 0;
if (o1 == null && o2 != null)
return Integer.MAX_VALUE;
if (o1 != null && o2 == null)
return Integer.MIN_VALUE;
return o1.compareTo(o2);
}
};
return NUMBERS_COMPARATOR;
}
public static final Comparator<Episode> NUMBERS_COMPARATOR = new Comparator<Episode>() {
@Override
public int compare(Episode a, Episode b) {
int diff = compareValue(a.getSeason(), b.getSeason());
if (diff != 0)
return diff;
diff = compareValue(a.getEpisode(), b.getEpisode());
if (diff != 0)
return diff;
diff = compareValue(a.getSpecial(), b.getSpecial());
if (diff != 0)
return diff;
return compareValue(a.getAbsolute(), b.getAbsolute());
}
private <T> int compareValue(Comparable<T> o1, T o2) {
if (o1 == null && o2 == null)
return 0;
if (o1 == null && o2 != null)
return Integer.MAX_VALUE;
if (o1 != null && o2 == null)
return Integer.MIN_VALUE;
return o1.compareTo(o2);
}
};
private EpisodeUtilities() {
throw new UnsupportedOperationException();
}