001package net.filebot.web; 002 003import static java.util.Arrays.*; 004import static java.util.Collections.*; 005import static net.filebot.util.RegularExpressions.*; 006 007import java.io.Serializable; 008import java.util.List; 009import java.util.Locale; 010 011import net.filebot.Cache; 012import net.filebot.Cache.TypedCache; 013import net.filebot.CacheType; 014 015public abstract class AbstractEpisodeListProvider implements EpisodeListProvider { 016 017 protected abstract List<SearchResult> fetchSearchResult(String query, Locale locale) throws Exception; 018 019 protected abstract SeriesData fetchSeriesData(SearchResult searchResult, SortOrder sortOrder, Locale locale) throws Exception; 020 021 @Override 022 public List<SearchResult> search(String query, Locale locale) throws Exception { 023 return getSearchCache(locale).computeIfAbsent(query.toLowerCase(locale), it -> { 024 return fetchSearchResult(query, locale); 025 }); 026 } 027 028 @Override 029 public List<SearchResult> lookup(String id, Locale locale) throws Exception { 030 // handle as numeric series id 031 SearchResult series = id(id); 032 if (series != null) { 033 return singletonList(series); 034 } 035 036 // handle as search query 037 return search(id, locale); 038 } 039 040 @Override 041 public SearchResult id(String query) throws Exception { 042 if (DIGIT.matcher(query).matches()) { 043 return new SearchResult(Integer.parseInt(query)); 044 } 045 return null; 046 } 047 048 @Override 049 public SortOrder vetoRequestParameter(SortOrder order) { 050 return order == null ? SortOrder.Airdate : order; 051 } 052 053 @Override 054 public Locale vetoRequestParameter(Locale locale) { 055 return getLanguageCode(locale) == null ? Locale.ENGLISH : locale; 056 } 057 058 @Override 059 public List<Episode> getEpisodeList(SearchResult searchResult, SortOrder sortOrder, Locale locale) throws Exception { 060 return getSeriesData(searchResult, sortOrder, locale).getEpisodeList(); 061 } 062 063 @Override 064 public List<Episode> getEpisodeList(int id, SortOrder order, Locale locale) throws Exception { 065 return getEpisodeList(new SearchResult(id), order, locale); 066 } 067 068 @Override 069 public SeriesInfo getSeriesInfo(SearchResult searchResult, Locale locale) throws Exception { 070 return getSeriesData(searchResult, null, locale).getSeriesInfo(); 071 } 072 073 @Override 074 public SeriesInfo getSeriesInfo(int id, Locale locale) throws Exception { 075 return getSeriesInfo(new SearchResult(id), locale); 076 } 077 078 @Override 079 public List<? extends SearchResult> getIndex() throws Exception { 080 return emptyList(); 081 } 082 083 protected SeriesData getSeriesData(SearchResult searchResult, SortOrder order, Locale locale) throws Exception { 084 // override preferences if requested parameters are not supported 085 SortOrder requestOrder = vetoRequestParameter(order); 086 Locale requestLanguage = vetoRequestParameter(locale); 087 088 return getDataCache(requestOrder, requestLanguage).computeIfAbsent(searchResult.getId(), it -> { 089 return fetchSeriesData(searchResult, requestOrder, requestLanguage); 090 }); 091 } 092 093 protected String getLanguageCode(Locale locale) { 094 if (locale == null) { 095 return null; 096 } 097 098 String language = locale.getLanguage(); 099 100 // Note: ISO 639 is not a stable standard— some languages' codes have changed. 101 // Locale's constructor recognizes both the new and the old codes for the languages whose codes have changed, 102 // but this function always returns the old code. 103 switch (language) { 104 case "iw": 105 return "he"; // Hebrew 106 case "in": 107 return "id"; // Indonesian 108 case "": 109 return null; // empty language code 110 default: 111 return language; 112 } 113 } 114 115 protected Cache getCache(String section) { 116 return Cache.getCache(getName() + "_" + section, CacheType.Daily); 117 } 118 119 protected TypedCache<List<SearchResult>> getSearchCache(Locale locale) { 120 return getCache("search_" + getLanguageCode(locale)).castList(SearchResult.class); 121 } 122 123 protected TypedCache<SeriesData> getDataCache(SortOrder order, Locale locale) { 124 return getCache("data_" + order.ordinal() + "_" + getLanguageCode(locale)).cast(SeriesData.class); 125 } 126 127 protected static class SeriesData implements Serializable { 128 129 public SeriesInfo seriesInfo; 130 public Episode[] episodeList; 131 132 public SeriesData() { 133 // used by serializer 134 } 135 136 public SeriesData(SeriesInfo seriesInfo, List<Episode> episodeList) { 137 this.seriesInfo = seriesInfo; 138 this.episodeList = episodeList.toArray(new Episode[0]); 139 } 140 141 public SeriesInfo getSeriesInfo() { 142 return seriesInfo.clone(); 143 } 144 145 public List<Episode> getEpisodeList() { 146 return unmodifiableList(asList(episodeList)); 147 } 148 } 149 150}