package net.filebot.web; import static java.util.Arrays.*; import static java.util.Collections.*; import static java.util.stream.Collectors.*; import static net.filebot.Logging.*; import static net.filebot.util.JsonUtilities.*; import static net.filebot.util.StringUtilities.*; import static net.filebot.web.WebRequest.*; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import net.filebot.Cache; import net.filebot.CacheType; import net.filebot.Resource; public enum XEM { AniDB, TheTVDB, Scene, Tract; public String getOriginName() { switch (this) { case TheTVDB: return "tvdb"; default: return name().toLowerCase(); } } public Integer getSeason(Integer s) { return this == AniDB ? 1 : s; } protected final Resource> haveMap = Resource.lazy(this::getHaveMap); public Optional map(Episode episode, XEM destination) throws Exception { Integer seriesId = episode.getSeriesInfo().getId(); if (!haveMap.get().contains(seriesId)) { return Optional.empty(); } Set seriesNames = episode.getSeriesNames(); Integer season = getSeason(episode.getSeason()); Map> names = getNames(seriesId); debug.finest(format("[XEM] %s", names)); Integer mappedSeason = names.entrySet().stream().filter(it -> { return it.getValue().stream().anyMatch(seriesNames::contains); }).map(it -> { return matchInteger(it.getKey()); }).filter(Objects::nonNull).findFirst().orElse(season); String mappedSeriesName = names.get("all").get(0); Map> mapping = episode.getEpisode() != null ? getSingle(seriesId, mappedSeason, episode.getEpisode()) : getSingle(seriesId, 0, episode.getSpecial()); List mappedEpisode = mapping.entrySet().stream().filter(it -> { return it.getKey().startsWith(destination.getOriginName()); }).map(it -> { debug.finest(format("[XEM] %s | %s | %s", mappedSeriesName, mappedSeason, it)); Map mappedNumbers = it.getValue(); Integer e = getInteger(mappedNumbers, "episode"); Integer a = getInteger(mappedNumbers, "absolute"); return episode.derive(mappedSeriesName, mappedSeason, e, a, null); }).collect(toList()); if (mappedEpisode.size() == 1) { return Optional.of(mappedEpisode.get(0)); } else if (mappedEpisode.size() > 1) { return Optional.of(new MultiEpisode(mappedEpisode)); } return Optional.empty(); } public List>> getAll(Integer id) throws Exception { Map parameters = new LinkedHashMap<>(2); parameters.put("origin", getOriginName()); parameters.put("id", id); Object response = request("all", parameters); return (List) asList(getArray(response, "data")); } public Map> getSingle(Integer id, Integer season, Integer episode) throws Exception { Map parameters = new LinkedHashMap<>(4); parameters.put("origin", getOriginName()); parameters.put("id", id); parameters.put("season", season); parameters.put("episode", episode); Object response = request("single", parameters); return (Map) getMap(response, "data"); } public List getAllNames() throws Exception { return getAllNames(null, null, true); } public List getAllNames(Integer season, String language, boolean defaultNames) throws Exception { List result = new ArrayList<>(); Map parameters = new LinkedHashMap<>(4); parameters.put("origin", getOriginName()); parameters.put("season", season); parameters.put("language", language); parameters.put("defaultNames", defaultNames ? "1" : "0"); Object response = request("allNames", parameters); getMap(response, "data").forEach((k, v) -> { int id = Integer.parseInt(k.toString()); List names = stream(asArray(v)).filter(Objects::nonNull).map(Objects::toString).filter(s -> s.length() > 0).collect(toList()); if (names.size() > 0) { result.add(new SearchResult(id, names.get(0), names.subList(1, names.size()))); } }); return result; } public Set getHaveMap() throws Exception { Map parameters = new LinkedHashMap<>(1); parameters.put("origin", getOriginName()); Object response = request("havemap", parameters); return stream(getArray(response, "data")).map(Object::toString).map(Integer::parseInt).collect(toSet()); } public Map> getNames(Integer id) throws Exception { Map parameters = new LinkedHashMap<>(3); parameters.put("origin", getOriginName()); parameters.put("id", id); parameters.put("defaultNames", "1"); Object response = request("names", parameters); Map> names = new HashMap<>(); getMap(response, "data").forEach((k, v) -> { names.put(k.toString(), asNamesList(v)); }); return names; } private List asNamesList(Object value) { if (value instanceof Map) { Map names = (Map) value; return names.values().stream().flatMap(a -> stream(a)).map(Object::toString).collect(toList()); } else if (value instanceof Collection) { Collection names = (Collection) value; return names.stream().map(Object::toString).collect(toList()); } return singletonList(value.toString()); } protected Object request(String path, Map parameters) throws Exception { return request(path + '?' + encodeParameters(parameters, true)); } protected Object request(String path) throws Exception { return getCache().json(path, this::getResource).expire(Cache.ONE_WEEK).get(); } protected URL getResource(String path) throws Exception { return new URL("http://thexem.de/map/" + path); } protected Cache getCache() { return Cache.getCache("xem", CacheType.Monthly); } public static List names() { return stream(values()).map(Enum::name).collect(toList()); } public static XEM forName(String name) { for (XEM db : values()) { if (db.name().equalsIgnoreCase(name) || db.getOriginName().equalsIgnoreCase(name)) { return db; } } throw new IllegalArgumentException(String.format("%s not in %s", name, asList(values()))); } }