From 265eef620057772fa4abc3b377f3614f489785b5 Mon Sep 17 00:00:00 2001 From: Reinhard Pointner Date: Sun, 9 Jun 2019 19:42:56 +0700 Subject: [PATCH] Refactor Episode / MultiEpisode / EpisodeUtilities --- .../net/filebot/format/MediaBindingBean.java | 21 ++--- source/net/filebot/media/NamingStandard.java | 9 +- source/net/filebot/web/AnimeLists.java | 6 +- source/net/filebot/web/Episode.java | 30 +++++-- source/net/filebot/web/EpisodeFormat.java | 23 +++-- source/net/filebot/web/EpisodeUtilities.java | 27 +++--- source/net/filebot/web/MultiEpisode.java | 86 ++++++++++++++----- source/net/filebot/web/XEM.java | 3 +- 8 files changed, 122 insertions(+), 83 deletions(-) diff --git a/source/net/filebot/format/MediaBindingBean.java b/source/net/filebot/format/MediaBindingBean.java index ce1605b6..1332d6d6 100644 --- a/source/net/filebot/format/MediaBindingBean.java +++ b/source/net/filebot/format/MediaBindingBean.java @@ -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 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 getEpisodes() { - return getMultiEpisodeList(getEpisode()); + return streamMultiEpisode(getEpisode()).collect(toList()); } @Define("movie") diff --git a/source/net/filebot/media/NamingStandard.java b/source/net/filebot/media/NamingStandard.java index 4ce495b4..155ffbff 100644 --- a/source/net/filebot/media/NamingStandard.java +++ b/source/net/filebot/media/NamingStandard.java @@ -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; diff --git a/source/net/filebot/web/AnimeLists.java b/source/net/filebot/web/AnimeLists.java index f9aa15b9..1deb8de4 100644 --- a/source/net/filebot/web/AnimeLists.java +++ b/source/net/filebot/web/AnimeLists.java @@ -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 map(int id, AnimeLists destination) throws Exception { diff --git a/source/net/filebot/web/Episode.java b/source/net/filebot/web/Episode.java index 63fbb294..b40f67fd 100644 --- a/source/net/filebot/web/Episode.java +++ b/source/net/filebot/web/Episode.java @@ -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 getNumbers() { - return Arrays.asList(season, episode, special, absolute); - } - public Set getSeriesNames() { Set names = new LinkedHashSet(); 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) { diff --git a/source/net/filebot/web/EpisodeFormat.java b/source/net/filebot/web/EpisodeFormat.java index 624a9540..6363a499 100644 --- a/source/net/filebot/web/EpisodeFormat.java +++ b/source/net/filebot/web/EpisodeFormat.java @@ -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 episodes) { - Function seriesName = it -> it.getSeriesName(); - Function episodeNumber = it -> formatSxE(it); - Function episodeTitle = it -> it.getTitle() == null ? "" : removeTrailingBrackets(it.getTitle()); + public String formatMultiEpisode(Episode... episodes) { + Function seriesName = Episode::getSeriesName; + Function episodeNumber = this::formatSxE; + Function 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 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 episodes) { diff --git a/source/net/filebot/web/EpisodeUtilities.java b/source/net/filebot/web/EpisodeUtilities.java index 2ab86e49..664cf228 100644 --- a/source/net/filebot/web/EpisodeUtilities.java +++ b/source/net/filebot/web/EpisodeUtilities.java @@ -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 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 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) { - 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 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 streamMultiEpisode(Episode... episodes) { + return stream(episodes).flatMap(e -> e instanceof MultiEpisode ? ((MultiEpisode) e).stream() : Stream.of(e)); } public static List 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); diff --git a/source/net/filebot/web/MultiEpisode.java b/source/net/filebot/web/MultiEpisode.java index bf95d0d3..60fe66ff 100644 --- a/source/net/filebot/web/MultiEpisode.java +++ b/source/net/filebot/web/MultiEpisode.java @@ -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 { @@ -24,53 +22,86 @@ public class MultiEpisode extends Episode implements Iterable { this.episodes = episodes.toArray(new Episode[0]); } + public Episode[] getEpisodes() { + return episodes.clone(); + } + + public Episode getFirst() { + return episodes[0]; + } + + public Stream stream() { + return Arrays.stream(episodes); + } + @Override public Iterator iterator() { - return stream(episodes).iterator(); - } - - public List 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 getNumbers() { - return stream(episodes).flatMap(e -> e.getNumbers().stream()).collect(toList()); + @Override + public Set 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 { 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); } } diff --git a/source/net/filebot/web/XEM.java b/source/net/filebot/web/XEM.java index defb23c5..360e6b84 100644 --- a/source/net/filebot/web/XEM.java +++ b/source/net/filebot/web/XEM.java @@ -73,9 +73,8 @@ public enum XEM { Map 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) {