mirror of
https://github.com/mitb-archive/filebot
synced 2024-12-21 15:28:52 -05:00
Refactor AnimeList client
This commit is contained in:
parent
ff422c39fa
commit
4a19490f01
@ -65,7 +65,7 @@ import net.filebot.mediainfo.MediaInfoException;
|
||||
import net.filebot.similarity.Normalization;
|
||||
import net.filebot.similarity.SimilarityComparator;
|
||||
import net.filebot.util.FileUtilities;
|
||||
import net.filebot.web.AnimeLists;
|
||||
import net.filebot.web.AnimeList;
|
||||
import net.filebot.web.AudioTrack;
|
||||
import net.filebot.web.Episode;
|
||||
import net.filebot.web.EpisodeFormat;
|
||||
@ -797,7 +797,7 @@ public class MediaBindingBean {
|
||||
}
|
||||
|
||||
if (AniDB.getIdentifier().equals(e.getSeriesInfo().getDatabase())) {
|
||||
return AnimeLists.AniDB.map(e, AnimeLists.TheTVDB).map(this::createBindingObject).orElse(null); // map AniDB to TheTVDB bindings
|
||||
return new AnimeList().map(e, AnimeList.getDB(e), AnimeList.DB.TheTVDB).map(this::createBindingObject).orElse(null); // map AniDB to TheTVDB bindings
|
||||
}
|
||||
|
||||
return createBindingObject(fetchEpisode(e, SortOrder.Airdate, null));
|
||||
@ -1173,17 +1173,15 @@ public class MediaBindingBean {
|
||||
});
|
||||
}
|
||||
|
||||
@Define("AnimeLists")
|
||||
@Define("AnimeList")
|
||||
public DynamicBindings getAnimeLists() {
|
||||
return new DynamicBindings(AnimeLists::names, k -> {
|
||||
return new DynamicBindings(AnimeList.DB::names, k -> {
|
||||
if (infoObject instanceof Episode) {
|
||||
Episode e = getEpisode();
|
||||
AnimeLists origin = AnimeLists.forName(e.getSeriesInfo().getDatabase());
|
||||
AnimeLists destination = AnimeLists.forName(k);
|
||||
if (origin == destination) {
|
||||
if (AnimeList.getDB(e) == AnimeList.getDB(k)) {
|
||||
return e;
|
||||
}
|
||||
return origin.map(e, destination).orElse(e);
|
||||
return new AnimeList().map(e, AnimeList.getDB(e), AnimeList.getDB(k)).orElse(e);
|
||||
}
|
||||
return undefined(k);
|
||||
});
|
||||
@ -1197,7 +1195,6 @@ public class MediaBindingBean {
|
||||
debug.warning("Failed to retrieve primary series info: " + e); // default to seriesInfo property
|
||||
}
|
||||
}
|
||||
|
||||
return getSeriesInfo();
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,9 @@ import java.io.StringWriter;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
@ -31,19 +32,27 @@ import net.filebot.CacheType;
|
||||
import net.filebot.Resource;
|
||||
import net.filebot.WebServices;
|
||||
|
||||
public enum AnimeLists {
|
||||
public class AnimeList {
|
||||
|
||||
AniDB, TheTVDB;
|
||||
private Model model;
|
||||
|
||||
public Optional<Episode> map(Episode episode, AnimeLists destination) throws Exception {
|
||||
public AnimeList() throws Exception {
|
||||
this.model = MODEL.get();
|
||||
}
|
||||
|
||||
public Model getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public Optional<Episode> map(Episode episode, DB source, DB destination) throws Exception {
|
||||
int id = episode.getSeriesInfo().getId();
|
||||
int series = getSeasonNumber(episode);
|
||||
int series = getSeasonNumber(source, episode);
|
||||
|
||||
return find(id, series).map(a -> {
|
||||
return find(source, id, series).map(a -> {
|
||||
// auto-align mode
|
||||
if (a.defaulttvdbseason == null) {
|
||||
try {
|
||||
return destination.mapAutoAligned(a, episode);
|
||||
return mapAutoAligned(destination, a, episode);
|
||||
} catch (Exception e) {
|
||||
debug.warning(e::toString);
|
||||
}
|
||||
@ -51,76 +60,94 @@ public enum AnimeLists {
|
||||
}
|
||||
|
||||
// offset mode
|
||||
int s = getSeasonNumber(episode);
|
||||
int e = getEpisodeNumber(episode);
|
||||
int s = getSeasonNumber(source, episode);
|
||||
int e = episode.isSpecial() ? episode.getSpecial() : episode.getEpisode();
|
||||
|
||||
// check explicit episode mapping
|
||||
if (a.mapping != null) {
|
||||
for (Mapping m : a.mapping) {
|
||||
if (s == getSeason(m)) {
|
||||
Optional<Integer> episodeMapping = destination.getEpisodeNumber(m, e);
|
||||
if (s == getSeason(source, m)) {
|
||||
Optional<Integer> episodeMapping = getEpisodeNumber(source, m, e);
|
||||
if (episodeMapping.isPresent()) {
|
||||
return destination.derive(a, episode, destination.getSeason(m), episodeMapping.get());
|
||||
return derive(destination, a, episode, getSeason(destination, m), episodeMapping.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply default season
|
||||
s = destination.getSeason(a, episode);
|
||||
s = getSeason(destination, a, episode);
|
||||
|
||||
// apply episode offset
|
||||
e += destination.getEpisodeNumberOffset(a);
|
||||
e += getEpisodeNumberOffset(destination, a);
|
||||
|
||||
return destination.derive(a, episode, s, e);
|
||||
return derive(destination, a, episode, s, e);
|
||||
}).findFirst();
|
||||
}
|
||||
|
||||
private Episode derive(Entry a, Episode episode, int s, int e) {
|
||||
private Episode derive(DB db, Entry a, Episode episode, int s, int e) {
|
||||
if (s == 0) {
|
||||
// special
|
||||
return this == AniDB ? episode.derive(a.name, null, null, null, e) : episode.deriveSpecial(e);
|
||||
switch (db) {
|
||||
case AniDB:
|
||||
return episode.derive(a.name, null, null, null, e);
|
||||
default:
|
||||
return episode.deriveSpecial(e);
|
||||
}
|
||||
} else {
|
||||
// regular
|
||||
return this == AniDB ? episode.derive(a.name, null, e, null, null) : episode.derive(s, e);
|
||||
switch (db) {
|
||||
case AniDB:
|
||||
return episode.derive(a.name, null, e, null, null);
|
||||
default:
|
||||
return episode.derive(s, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<Integer> map(int id, int s, AnimeLists destination) throws Exception {
|
||||
return find(id, s).map(destination::getId).findFirst();
|
||||
public Optional<Integer> map(int id, int s, DB source, DB destination) throws Exception {
|
||||
return find(source, id, s).map(a -> getId(destination, a)).findFirst();
|
||||
}
|
||||
|
||||
protected Episode mapAutoAligned(Entry a, Episode episode) throws Exception {
|
||||
switch (this) {
|
||||
case TheTVDB:
|
||||
return WebServices.TheTVDB.getEpisodeList(a.tvdbid, SortOrder.Airdate, Locale.ENGLISH).stream().filter(e -> {
|
||||
return episode.getEpisode() != null && episode.getEpisode().equals(e.getAbsolute());
|
||||
}).findFirst().orElse(null);
|
||||
protected Episode mapAutoAligned(DB db, Entry a, Episode episode) throws Exception {
|
||||
switch (db) {
|
||||
case AniDB:
|
||||
return WebServices.AniDB.getEpisodeList(a.anidbid, SortOrder.Absolute, Locale.ENGLISH).stream().filter(e -> {
|
||||
return episode.getAbsolute() != null && episode.getAbsolute().equals(e.getEpisode());
|
||||
}).findFirst().orElse(null);
|
||||
default:
|
||||
return WebServices.TheTVDB.getEpisodeList(a.tvdbid, SortOrder.Airdate, Locale.ENGLISH).stream().filter(e -> {
|
||||
return episode.getEpisode() != null && episode.getEpisode().equals(e.getAbsolute());
|
||||
}).findFirst().orElse(null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Optional<Integer> getEpisodeNumber(Mapping m, Integer e) {
|
||||
protected Optional<Integer> getEpisodeNumber(DB db, Mapping m, Integer e) {
|
||||
if (m.numbers != null) {
|
||||
switch (this) {
|
||||
case TheTVDB:
|
||||
return Optional.ofNullable(m.numbers.get(e));
|
||||
switch (db) {
|
||||
case AniDB:
|
||||
return m.numbers.entrySet().stream().filter(it -> e.equals(it.getValue())).map(it -> it.getKey()).findFirst();
|
||||
return stream(m.numbers).filter(i -> e == i[0]).map(i -> i[1]).findFirst();
|
||||
default:
|
||||
return stream(m.numbers).filter(i -> e == i[1]).map(i -> i[0]).findFirst();
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
protected int getEpisodeNumberOffset(Entry a) {
|
||||
return a.episodeoffset == null ? 0 : this == TheTVDB ? a.episodeoffset : -a.episodeoffset;
|
||||
protected int getEpisodeNumberOffset(DB db, Entry a) {
|
||||
if (a.episodeoffset == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (db) {
|
||||
case AniDB:
|
||||
return -a.episodeoffset;
|
||||
default:
|
||||
return a.episodeoffset;
|
||||
}
|
||||
}
|
||||
|
||||
protected int getSeasonNumber(Episode e) {
|
||||
protected int getSeasonNumber(DB db, Episode e) {
|
||||
// special episode
|
||||
if (e.isSpecial()) {
|
||||
return 0;
|
||||
@ -128,39 +155,64 @@ public enum AnimeLists {
|
||||
|
||||
// regular absolute episode
|
||||
if (e.getSeason() == null) {
|
||||
return this == AniDB ? 1 : -1;
|
||||
switch (db) {
|
||||
case AniDB:
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// regular SxE episode
|
||||
return e.getSeason();
|
||||
}
|
||||
|
||||
protected int getEpisodeNumber(Episode e) {
|
||||
return e.isSpecial() ? e.getSpecial() : e.getEpisode();
|
||||
protected int getSeason(DB db, Mapping m) {
|
||||
switch (db) {
|
||||
case AniDB:
|
||||
return m.anidbseason;
|
||||
default:
|
||||
return m.tvdbseason;
|
||||
}
|
||||
}
|
||||
|
||||
protected int getSeason(Mapping m) {
|
||||
return this == AniDB ? m.anidbseason : m.tvdbseason;
|
||||
protected int getSeason(DB db, Entry a, Episode e) {
|
||||
if (e.isSpecial()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (db) {
|
||||
case AniDB:
|
||||
return 1;
|
||||
default:
|
||||
return a.defaulttvdbseason;
|
||||
}
|
||||
}
|
||||
|
||||
protected int getSeason(Entry a, Episode e) {
|
||||
return e.isSpecial() ? 0 : this == AniDB ? 1 : a.defaulttvdbseason;
|
||||
}
|
||||
|
||||
protected int getId(Entry a) {
|
||||
return this == AniDB ? a.anidbid : a.tvdbid;
|
||||
protected int getId(DB db, Entry a) {
|
||||
switch (db) {
|
||||
case AniDB:
|
||||
return a.anidbid;
|
||||
default:
|
||||
return a.tvdbid;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isValid(Entry a) {
|
||||
return a.anidbid != null && a.tvdbid != null;
|
||||
}
|
||||
|
||||
public Stream<Entry> find(int id) throws Exception {
|
||||
return stream(MODEL.get().anime).filter(this::isValid).filter(a -> id == getId(a));
|
||||
public Stream<Entry> find(DB db, int id) throws Exception {
|
||||
return stream(model.anime).filter(this::isValid).filter(a -> id == getId(db, a));
|
||||
}
|
||||
|
||||
public Stream<Entry> find(int id, int s) throws Exception {
|
||||
return this == AniDB ? find(id) : find(id).filter(a -> a.defaulttvdbseason == null || s == a.defaulttvdbseason);
|
||||
public Stream<Entry> find(DB db, int id, int s) throws Exception {
|
||||
switch (db) {
|
||||
case AniDB:
|
||||
return find(db, id);
|
||||
default:
|
||||
return find(db, id).filter(a -> a.defaulttvdbseason == null || s == a.defaulttvdbseason);
|
||||
}
|
||||
}
|
||||
|
||||
protected static Cache getCache() {
|
||||
@ -173,13 +225,39 @@ public enum AnimeLists {
|
||||
// NOTE: GitHub only supports If-None-Match (If-Modified-Since is ignored)
|
||||
Cache cache = getCache();
|
||||
|
||||
return cache.bytes(file, AnimeLists::getResource).fetch(fetchIfNoneMatch(url -> file, cache)).expire(Cache.ONE_MONTH).get();
|
||||
return cache.bytes(file, AnimeList::getResource).fetch(fetchIfNoneMatch(url -> file, cache)).expire(Cache.ONE_MONTH).get();
|
||||
}
|
||||
|
||||
protected static URL getResource(String file) throws Exception {
|
||||
return new URL("https://raw.githubusercontent.com/ScudLee/anime-lists/master/" + file);
|
||||
}
|
||||
|
||||
public static DB getDB(Episode e) {
|
||||
return DB.forName(e.getSeriesInfo().getDatabase());
|
||||
}
|
||||
|
||||
public static DB getDB(String s) {
|
||||
return DB.forName(s);
|
||||
}
|
||||
|
||||
public enum DB {
|
||||
|
||||
AniDB, TheTVDB;
|
||||
|
||||
public static List<String> names() {
|
||||
return stream(values()).map(Enum::name).collect(toList());
|
||||
}
|
||||
|
||||
public static DB forName(String name) {
|
||||
for (DB db : values()) {
|
||||
if (db.name().equalsIgnoreCase(name)) {
|
||||
return db;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(String.format("%s not in %s", name, asList(values())));
|
||||
}
|
||||
}
|
||||
|
||||
@XmlRootElement(name = "anime-list")
|
||||
public static class Model {
|
||||
|
||||
@ -242,12 +320,39 @@ public enum AnimeLists {
|
||||
|
||||
@XmlJavaTypeAdapter(NumberMapAdapter.class)
|
||||
@XmlValue
|
||||
public Map<Integer, Integer> numbers;
|
||||
public int[][] numbers;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return marshal(this, Mapping.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class NumberAdapter extends XmlAdapter<String, Integer> {
|
||||
|
||||
@Override
|
||||
public Integer unmarshal(String s) throws Exception {
|
||||
return matchInteger(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String marshal(Integer i) throws Exception {
|
||||
return Objects.toString(i, "");
|
||||
}
|
||||
}
|
||||
|
||||
private static class NumberMapAdapter extends XmlAdapter<String, int[][]> {
|
||||
|
||||
@Override
|
||||
public int[][] unmarshal(String s) throws Exception {
|
||||
return tokenize(s, SEMICOLON).map(m -> matchIntegers(m)).filter(m -> m.size() == 2).map(m -> m.stream().mapToInt(i -> i).toArray()).toArray(int[][]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String marshal(int[][] m) throws Exception {
|
||||
return stream(m).map(e -> join(IntStream.of(e).boxed(), "-")).collect(joining(";"));
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T unmarshal(byte[] bytes, Class<T> type) throws Exception {
|
||||
@ -267,44 +372,4 @@ public enum AnimeLists {
|
||||
}
|
||||
}
|
||||
|
||||
private static class NumberAdapter extends XmlAdapter<String, Integer> {
|
||||
|
||||
@Override
|
||||
public Integer unmarshal(String s) throws Exception {
|
||||
return matchInteger(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String marshal(Integer i) throws Exception {
|
||||
return String.valueOf(i);
|
||||
}
|
||||
}
|
||||
|
||||
private static class NumberMapAdapter extends XmlAdapter<String, Map<Integer, Integer>> {
|
||||
|
||||
@Override
|
||||
public Map<Integer, Integer> unmarshal(String s) throws Exception {
|
||||
return tokenize(s, SEMICOLON).map(m -> matchIntegers(m)).filter(m -> m.size() >= 2).collect(toMap(m -> m.get(0), m -> m.get(1)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String marshal(Map<Integer, Integer> m) throws Exception {
|
||||
return m.entrySet().stream().map(e -> join(Stream.of(e.getKey(), e.getValue()), "-")).collect(joining(";"));
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> names() {
|
||||
return stream(values()).map(Enum::name).collect(toList());
|
||||
}
|
||||
|
||||
public static AnimeLists forName(String name) {
|
||||
for (AnimeLists db : values()) {
|
||||
if (db.name().equalsIgnoreCase(name)) {
|
||||
return db;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("%s not in %s", name, asList(values())));
|
||||
}
|
||||
|
||||
}
|
@ -55,7 +55,7 @@ public final class EpisodeUtilities {
|
||||
if (episode.isAnime() && episode.isRegular()) {
|
||||
return mapEpisode(episode, e -> {
|
||||
try {
|
||||
return AnimeLists.forName(e.getSeriesInfo().getDatabase()).map(e, AnimeLists.TheTVDB).orElse(e);
|
||||
return new AnimeList().map(e, AnimeList.getDB(e), AnimeList.DB.TheTVDB).orElse(e);
|
||||
} catch (Exception ioe) {
|
||||
debug.warning(ioe::toString);
|
||||
return e;
|
||||
|
@ -194,7 +194,6 @@ public enum XEM {
|
||||
return db;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("%s not in %s", name, asList(values())));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user