Refactor Episode / MultiEpisode / EpisodeUtilities

This commit is contained in:
Reinhard Pointner 2019-06-09 19:42:56 +07:00
parent b10ed1fdd9
commit 265eef6200
8 changed files with 122 additions and 83 deletions

View File

@ -72,7 +72,6 @@ import net.filebot.web.EpisodeFormat;
import net.filebot.web.Movie;
import net.filebot.web.MovieInfo;
import net.filebot.web.MoviePart;
import net.filebot.web.MultiEpisode;
import net.filebot.web.SeriesInfo;
import net.filebot.web.SimpleDate;
import net.filebot.web.SortOrder;
@ -151,11 +150,7 @@ public class MediaBindingBean {
@Define("s")
public Integer getSeasonNumber() {
// look up season numbers via TheTVDB for AniDB episode data
if (isAnime(getEpisode())) {
return trySeasonEpisodeForAnime(getEpisode()).getSeason();
}
return getEpisode().getSeason();
return trySeasonEpisodeForAnime(getEpisode()).getSeason();
}
@Define("e")
@ -194,7 +189,7 @@ public class MediaBindingBean {
String t = null;
if (infoObject instanceof Episode) {
t = infoObject instanceof MultiEpisode ? EpisodeFormat.SeasonEpisode.formatMultiTitle(getEpisodes()) : getEpisode().getTitle(); // implicit support for multi-episode title formatting
t = getEpisode().getTitle();
} else if (infoObject instanceof Movie) {
t = getMovieInfo().getTagline();
} else if (infoObject instanceof AudioTrack) {
@ -270,7 +265,7 @@ public class MediaBindingBean {
}
// access episode list and find minimum airdate if necessary
return getEpisodeList().stream().filter(e -> isRegular(e)).map(Episode::getAirdate).filter(Objects::nonNull).min(SimpleDate::compareTo).get();
return getEpisodeList().stream().filter(Episode::isRegular).map(Episode::getAirdate).filter(Objects::nonNull).min(SimpleDate::compareTo).get();
}
@Define("absolute")
@ -804,12 +799,12 @@ public class MediaBindingBean {
@Define("anime")
public boolean isAnimeEpisode() {
return getEpisodes().stream().anyMatch(it -> isAnime(it));
return getEpisode().isAnime();
}
@Define("regular")
public boolean isRegularEpisode() {
return getEpisodes().stream().anyMatch(it -> isRegular(it));
return getEpisode().isRegular();
}
@Define("episodelist")
@ -819,12 +814,12 @@ public class MediaBindingBean {
@Define("sy")
public List<Integer> getSeasonYears() throws Exception {
return getEpisodeList().stream().filter(e -> isRegular(e) && e.getSeason().equals(getSeasonNumber()) && e.getAirdate() != null).map(e -> e.getAirdate().getYear()).sorted().distinct().collect(toList());
return getEpisodeList().stream().filter(e -> e.isRegular() && e.getSeason().equals(getSeasonNumber()) && e.getAirdate() != null).map(e -> e.getAirdate().getYear()).sorted().distinct().collect(toList());
}
@Define("sc")
public Integer getSeasonCount() throws Exception {
return getEpisodeList().stream().filter(e -> isRegular(e) && e.getSeason() != null).map(Episode::getSeason).max(Integer::compare).get();
return getEpisodeList().stream().filter(e -> e.isRegular() && e.getSeason() != null).map(Episode::getSeason).max(Integer::compare).get();
}
@Define("mediaTitle")
@ -960,7 +955,7 @@ public class MediaBindingBean {
@Define("episodes")
public List<Episode> getEpisodes() {
return getMultiEpisodeList(getEpisode());
return streamMultiEpisode(getEpisode()).collect(toList());
}
@Define("movie")

View File

@ -4,7 +4,6 @@ import static java.util.Arrays.*;
import static java.util.stream.Collectors.*;
import static net.filebot.similarity.Normalization.*;
import static net.filebot.util.FileUtilities.*;
import static net.filebot.web.EpisodeUtilities.*;
import java.util.Objects;
@ -52,7 +51,7 @@ public class NamingStandard {
}
public String getPath(Episode e) {
if (isAnime(e)) {
if (e.isAnime()) {
// Anime
return path(getAnimeFolder(), getSeriesFolder(e), getEpisodeName(e));
} else {
@ -140,7 +139,7 @@ public class NamingStandard {
}
public String formatSeriesName(Episode e) {
if (isAnime(e)) {
if (e.isAnime()) {
// Anime
return e.getSeriesInfo().getName(); // series info name is the primary Anime name
} else {
@ -150,7 +149,7 @@ public class NamingStandard {
}
public String formatEpisodeNumbers(Episode e) {
if (isAnime(e)) {
if (e.isAnime()) {
// Anime
return EpisodeFormat.SeasonEpisode.formatSxE(e);
} else {
@ -161,7 +160,7 @@ public class NamingStandard {
public String formatEpisodeTitle(Episode e) {
// enforce title length limit by default
return truncateText(EpisodeFormat.SeasonEpisode.formatMultiTitle(getMultiEpisodeList(e)), TITLE_MAX_LENGTH);
return truncateText(EpisodeFormat.SeasonEpisode.formatMultiTitle(e), TITLE_MAX_LENGTH);
}
public static final int TITLE_MAX_LENGTH = 150;

View File

@ -74,11 +74,7 @@ public enum AnimeLists {
}
private Episode derive(Episode episode, int s, int e) {
if (s == 0) {
return episode.derive(null, null, e); // special episode
} else {
return episode.derive(s, e, null); // regular episode
}
return s == 0 ? episode.deriveSpecial(e) : episode.derive(s, e);
}
public Optional<Integer> map(int id, AnimeLists destination) throws Exception {

View File

@ -1,9 +1,7 @@
package net.filebot.web;
import java.io.Serializable;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@ -90,10 +88,6 @@ public class Episode implements Serializable {
return seriesInfo;
}
public List<Integer> getNumbers() {
return Arrays.asList(season, episode, special, absolute);
}
public Set<String> getSeriesNames() {
Set<String> names = new LinkedHashSet<String>();
if (seriesName != null) {
@ -112,6 +106,18 @@ public class Episode implements Serializable {
return names;
}
public boolean isAnime() {
return seriesInfo != null && (Objects.equals(seriesInfo.getType(), SeriesInfo.TYPE_ANIME) || Objects.equals(seriesInfo.getDatabase(), "AniDB")); // HACK: check database == AniDB for backward compatibility
}
public boolean isRegular() {
return episode != null;
}
public boolean isSpecial() {
return special != null;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Episode) {
@ -139,8 +145,16 @@ public class Episode implements Serializable {
return new Episode(this);
}
public Episode derive(Integer season, Integer episode, Integer special) {
return new Episode(getSeriesName(), season, episode, getTitle(), getAbsolute(), special, getAirdate(), getId(), getSeriesInfo());
public Episode derive(Integer season, Integer episode) {
return derive(getSeriesName(), season, episode, getAbsolute(), null);
}
public Episode deriveSpecial(Integer special) {
return derive(getSeriesName(), getSeason(), null, getAbsolute(), special);
}
public Episode derive(String seriesName, Integer season, Integer episode, Integer absolute) {
return derive(seriesName, season, episode, absolute, null);
}
public Episode derive(String seriesName, Integer season, Integer episode, Integer absolute, Integer special) {

View File

@ -1,13 +1,12 @@
package net.filebot.web;
import static java.util.stream.Collectors.*;
import static net.filebot.similarity.Normalization.*;
import static net.filebot.web.EpisodeUtilities.*;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Collection;
import java.util.Objects;
import java.util.SortedMap;
import java.util.SortedSet;
@ -27,7 +26,7 @@ public class EpisodeFormat extends Format {
@Override
public StringBuffer format(Object obj, StringBuffer sb, FieldPosition pos) {
if (obj instanceof MultiEpisode) {
return sb.append(formatMultiEpisode(((MultiEpisode) obj).getEpisodes()));
return sb.append(formatMultiEpisode(((MultiEpisode) obj)));
}
// format episode object, e.g. Dark Angel - 3x01 - Labyrinth [2009-06-01]
@ -60,19 +59,19 @@ public class EpisodeFormat extends Format {
return sb;
}
public String formatMultiEpisode(Collection<Episode> episodes) {
Function<Episode, String> seriesName = it -> it.getSeriesName();
Function<Episode, String> episodeNumber = it -> formatSxE(it);
Function<Episode, String> episodeTitle = it -> it.getTitle() == null ? "" : removeTrailingBrackets(it.getTitle());
public String formatMultiEpisode(Episode... episodes) {
Function<Episode, String> seriesName = Episode::getSeriesName;
Function<Episode, String> episodeNumber = this::formatSxE;
Function<Episode, String> episodeTitle = this::formatMultiTitle;
return Stream.of(seriesName, episodeNumber, episodeTitle).map(f -> {
return episodes.stream().map(f::apply).filter(s -> s.length() > 0).distinct().collect(joining(" & "));
return streamMultiEpisode(episodes).map(f).filter(s -> s.length() > 0).distinct().collect(joining(" & "));
}).collect(joining(" - "));
}
public String formatSxE(Episode episode) {
if (episode instanceof MultiEpisode) {
return formatMultiRangeSxE(((MultiEpisode) episode).getEpisodes());
return formatMultiRangeSxE(((MultiEpisode) episode));
}
StringBuilder sb = new StringBuilder();
@ -87,7 +86,7 @@ public class EpisodeFormat extends Format {
public String formatS00E00(Episode episode) {
if (episode instanceof MultiEpisode) {
return formatMultiRangeS00E00(((MultiEpisode) episode).getEpisodes());
return formatMultiRangeS00E00(((MultiEpisode) episode));
}
StringBuilder sb = new StringBuilder();
@ -100,8 +99,8 @@ public class EpisodeFormat extends Format {
return sb.toString();
}
public String formatMultiTitle(Collection<Episode> episodes) {
return episodes.stream().map(Episode::getTitle).filter(Objects::nonNull).map(Normalization::removeTrailingBrackets).distinct().collect(joining(" & "));
public String formatMultiTitle(Episode... episodes) {
return streamMultiEpisode(episodes).map(Episode::getTitle).filter(Objects::nonNull).map(Normalization::removeTrailingBrackets).distinct().collect(joining(" & "));
}
public String formatMultiRangeSxE(Iterable<Episode> episodes) {

View File

@ -1,6 +1,6 @@
package net.filebot.web;
import static java.util.Collections.*;
import static java.util.Arrays.*;
import static java.util.stream.Collectors.*;
import static net.filebot.Logging.*;
import static net.filebot.WebServices.*;
@ -11,34 +11,27 @@ import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
public final class EpisodeUtilities {
public static Episode mapEpisode(Episode episode, Function<Episode, Episode> mapper) {
return createEpisode(getMultiEpisodeList(episode).stream().map(mapper).sorted(EPISODE_NUMBERS_COMPARATOR).collect(toList()));
return createEpisode(streamMultiEpisode(episode).map(mapper).sorted(EPISODE_NUMBERS_COMPARATOR).toArray(Episode[]::new));
}
public static Episode selectEpisode(List<Episode> episodelist, Episode selection) {
return createEpisode(episodelist.stream().filter(getMultiEpisodeList(selection)::contains).sorted(EPISODE_NUMBERS_COMPARATOR).collect(toList()));
return createEpisode(episodelist.stream().filter(streamMultiEpisode(selection).collect(toSet())::contains).sorted(EPISODE_NUMBERS_COMPARATOR).toArray(Episode[]::new));
}
private static Episode createEpisode(List<Episode> episode) {
if (episode.isEmpty()) {
private static Episode createEpisode(Episode... episode) {
if (episode.length == 0) {
throw new IllegalArgumentException("Invalid Episode: Empty");
}
return episode.size() == 1 ? episode.get(0) : new MultiEpisode(episode);
return episode.length == 1 ? episode[0] : new MultiEpisode(episode);
}
public static List<Episode> getMultiEpisodeList(Episode e) {
return e instanceof MultiEpisode ? ((MultiEpisode) e).getEpisodes() : singletonList(e);
}
public static boolean isAnime(Episode e) {
return AniDB.getIdentifier().equals(e.getSeriesInfo().getDatabase());
}
public static boolean isRegular(Episode e) {
return e.getEpisode() != null && e.getSpecial() == null;
public static Stream<Episode> streamMultiEpisode(Episode... episodes) {
return stream(episodes).flatMap(e -> e instanceof MultiEpisode ? ((MultiEpisode) e).stream() : Stream.of(e));
}
public static List<Episode> fetchEpisodeList(Episode episode) throws Exception {
@ -59,7 +52,7 @@ public final class EpisodeUtilities {
}
public static Episode trySeasonEpisodeForAnime(Episode episode) {
if (isAnime(episode) && isRegular(episode)) {
if (episode.isAnime() && episode.isRegular()) {
return mapEpisode(episode, e -> {
try {
return AnimeLists.forName(e.getSeriesInfo().getDatabase()).map(e, AnimeLists.TheTVDB).orElse(e);

View File

@ -1,12 +1,10 @@
package net.filebot.web;
import static java.util.Arrays.*;
import static java.util.Collections.*;
import static java.util.stream.Collectors.*;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
public class MultiEpisode extends Episode implements Iterable<Episode> {
@ -24,53 +22,86 @@ public class MultiEpisode extends Episode implements Iterable<Episode> {
this.episodes = episodes.toArray(new Episode[0]);
}
public Episode[] getEpisodes() {
return episodes.clone();
}
public Episode getFirst() {
return episodes[0];
}
public Stream<Episode> stream() {
return Arrays.stream(episodes);
}
@Override
public Iterator<Episode> iterator() {
return stream(episodes).iterator();
}
public List<Episode> getEpisodes() {
return unmodifiableList(asList(episodes));
return stream().iterator();
}
@Override
public String getSeriesName() {
return episodes[0].getSeriesName();
return getFirst().getSeriesName();
}
@Override
public Integer getEpisode() {
return episodes[0].getEpisode();
return getFirst().getEpisode();
}
@Override
public Integer getSeason() {
return episodes[0].getSeason();
return getFirst().getSeason();
}
@Override
public String getTitle() {
return EpisodeFormat.SeasonEpisode.formatMultiTitle(getEpisodes());
return EpisodeFormat.SeasonEpisode.formatMultiTitle(episodes);
}
@Override
public Integer getAbsolute() {
return episodes[0].getAbsolute();
return getFirst().getAbsolute();
}
@Override
public Integer getSpecial() {
return episodes[0].getSpecial();
return getFirst().getSpecial();
}
@Override
public SimpleDate getAirdate() {
return episodes[0].getAirdate();
return getFirst().getAirdate();
}
@Override
public Integer getId() {
return episodes[0].getId();
return getFirst().getId();
}
@Override
public SeriesInfo getSeriesInfo() {
return episodes[0].getSeriesInfo();
return getFirst().getSeriesInfo();
}
public List<Integer> getNumbers() {
return stream(episodes).flatMap(e -> e.getNumbers().stream()).collect(toList());
@Override
public Set<String> getSeriesNames() {
return getFirst().getSeriesNames();
}
@Override
public boolean isAnime() {
return getFirst().isAnime();
}
@Override
public boolean isRegular() {
return getFirst().isRegular();
}
@Override
public boolean isSpecial() {
return getFirst().isSpecial();
}
@Override
@ -92,9 +123,22 @@ public class MultiEpisode extends Episode implements Iterable<Episode> {
return new MultiEpisode(episodes);
}
@Override
public MultiEpisode derive(String seriesName, Integer season, Integer episode, Integer absolute, Integer special) {
Episode[] m = new Episode[episodes.length];
for (int i = 0; i < episodes.length; i++) {
m[i] = episodes[i].derive(seriesName, season, up(episode, i), up(absolute, i), up(special, i));
}
return new MultiEpisode(m);
}
private Integer up(Integer i, int delta) {
return i == null ? null : i + delta;
}
@Override
public String toString() {
return EpisodeFormat.SeasonEpisode.formatMultiEpisode(getEpisodes());
return EpisodeFormat.SeasonEpisode.formatMultiEpisode(episodes);
}
}

View File

@ -73,9 +73,8 @@ public enum XEM {
Map<String, Number> mappedNumbers = it.getValue();
Integer e = getInteger(mappedNumbers, "episode");
Integer a = getInteger(mappedNumbers, "absolute");
Integer special = null;
return episode.derive(mappedSeriesName, mappedSeason, e, a, special);
return episode.derive(mappedSeriesName, mappedSeason, e, a);
}).collect(toList());
if (mappedEpisode.size() == 1) {