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:
parent
55491a137d
commit
07037b34ce
|
@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue