Experiment with new CachedResource framework

This commit is contained in:
Reinhard Pointner 2016-03-08 14:41:30 +00:00
parent 1f53b540dc
commit 3af533fd07
4 changed files with 90 additions and 48 deletions

View File

@ -19,6 +19,7 @@ public class Cache {
public static final Duration ONE_DAY = Duration.ofDays(1);
public static final Duration ONE_WEEK = Duration.ofDays(7);
public static final Duration ONE_MONTH = Duration.ofDays(30);
public static Cache getCache(String name, CacheType type) {
return CacheManager.getInstance().getCache(name, type);

View File

@ -1,8 +1,55 @@
package net.filebot;
import java.util.function.Function;
@FunctionalInterface
public interface Resource<R> {
R get() throws Exception;
default Resource<R> memoize() {
return new MemoizedResource<R>(this);
}
default <T> Resource<T> transform(Function<R, T> function) {
return new TransformedResource<R, T>(this, function);
}
}
class MemoizedResource<R> implements Resource<R> {
private final Resource<R> resource;
private R value;
public MemoizedResource(Resource<R> resource) {
this.resource = resource;
}
@Override
public R get() throws Exception {
synchronized (resource) {
if (value == null) {
value = resource.get();
}
return value;
}
}
}
class TransformedResource<R, T> implements Resource<T> {
private final Resource<R> resource;
private final Function<R, T> function;
public TransformedResource(Resource<R> resource, Function<R, T> function) {
this.resource = resource;
this.function = function;
}
@Override
public T get() throws Exception {
return function.apply(resource.get());
}
}

View File

@ -22,7 +22,6 @@ import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
@ -447,15 +446,15 @@ public class MediaDetection {
}
public static List<String> matchSeriesByDirectMapping(Collection<File> files) throws Exception {
Map<Pattern, String> seriesDirectMappings = releaseInfo.getSeriesDirectMappings();
Map<Pattern, String> patterns = releaseInfo.getSeriesMappings();
List<String> matches = new ArrayList<String>();
for (File file : files) {
for (Entry<Pattern, String> it : seriesDirectMappings.entrySet()) {
if (it.getKey().matcher(getName(file)).find()) {
matches.add(it.getValue());
}
patterns.forEach((pattern, seriesName) -> {
if (pattern.matcher(getName(file)).find()) {
matches.add(seriesName);
}
});
}
return matches;

View File

@ -337,8 +337,8 @@ public class ReleaseInfo {
return compile("(?<!\\p{Alnum})" + or(quoteAll(terms)) + "(?!\\p{Alnum})", CASE_INSENSITIVE | UNICODE_CHARACTER_CLASS);
}
public Movie[] getMovieList() throws Exception {
return movieIndex.get();
public Map<Pattern, String> getSeriesMappings() throws Exception {
return seriesMappings.get();
}
public TheTVDBSearchResult[] getTheTVDBIndex() throws Exception {
@ -349,26 +349,14 @@ public class ReleaseInfo {
return anidbIndex.get();
}
public Movie[] getMovieList() throws Exception {
return movieIndex.get();
}
public SubtitleSearchResult[] getOpenSubtitlesIndex() throws Exception {
return osdbIndex.get();
}
private Map<Pattern, String> seriesDirectMappings;
public Map<Pattern, String> getSeriesDirectMappings() throws Exception {
if (seriesDirectMappings == null) {
Map<Pattern, String> mappings = new LinkedHashMap<Pattern, String>();
for (String line : seriesDirectMappingsResource.get()) {
String[] tsv = line.split("\t", 2);
if (tsv.length == 2) {
mappings.put(compile("(?<!\\p{Alnum})(" + tsv[0] + ")(?!\\p{Alnum})", CASE_INSENSITIVE | UNICODE_CHARACTER_CLASS), tsv[1]);
}
}
seriesDirectMappings = unmodifiableMap(mappings);
}
return seriesDirectMappings;
}
private static FolderEntryFilter diskFolderFilter;
public FileFilter getDiskFolderFilter() {
@ -404,26 +392,24 @@ public class ReleaseInfo {
return roots;
}
// fetch release group names online and try to update the data every other day
protected final Resource<Map<Pattern, String>> seriesMappings = lines("url.series-mappings", Cache.ONE_WEEK).transform(lines -> {
Map<Pattern, String> map = new LinkedHashMap<Pattern, String>(lines.length);
stream(lines).map(s -> s.split("\t", 2)).filter(v -> v.length == 2).forEach(v -> {
Pattern pattern = compile("(?<!\\p{Alnum})(" + v[0] + ")(?!\\p{Alnum})", CASE_INSENSITIVE | UNICODE_CHARACTER_CLASS);
map.put(pattern, v[1]);
});
return unmodifiableMap(map);
}).memoize();
protected final Resource<String[]> releaseGroup = patternResource("url.release-groups");
protected final Resource<String[]> queryBlacklist = patternResource("url.query-blacklist");
protected final Resource<String[]> excludeBlacklist = patternResource("url.exclude-blacklist");
protected final Resource<String[]> seriesDirectMappingsResource = patternResource("url.series-mappings");
protected final Resource<String[]> releaseGroup = lines("url.release-groups", Cache.ONE_WEEK);
protected final Resource<String[]> queryBlacklist = lines("url.query-blacklist", Cache.ONE_WEEK);
protected final Resource<String[]> excludeBlacklist = lines("url.exclude-blacklist", Cache.ONE_WEEK);
protected final Resource<Movie[]> movieIndex = tsvResource("url.movie-list", this::parseMovie, Movie[]::new);
protected final Resource<TheTVDBSearchResult[]> tvdbIndex = tsvResource("url.thetvdb-index", this::parseSeries, TheTVDBSearchResult[]::new);
protected final Resource<AnidbSearchResult[]> anidbIndex = tsvResource("url.anidb-index", this::parseAnime, AnidbSearchResult[]::new);
protected final Resource<SubtitleSearchResult[]> osdbIndex = tsvResource("url.osdb-index", this::parseSubtitle, SubtitleSearchResult[]::new);
protected final Resource<TheTVDBSearchResult[]> tvdbIndex = tsv("url.thetvdb-index", Cache.ONE_WEEK, this::parseSeries, TheTVDBSearchResult[]::new);
protected final Resource<AnidbSearchResult[]> anidbIndex = tsv("url.anidb-index", Cache.ONE_WEEK, this::parseAnime, AnidbSearchResult[]::new);
private Movie parseMovie(String[] v) {
int imdbid = parseInt(v[0]);
int tmdbid = parseInt(v[1]);
int year = parseInt(v[2]);
String name = v[3];
String[] aliasNames = copyOfRange(v, 4, v.length);
return new Movie(name, aliasNames, year, imdbid > 0 ? imdbid : -1, tmdbid > 0 ? tmdbid : -1, null);
}
protected final Resource<Movie[]> movieIndex = tsv("url.movie-list", Cache.ONE_MONTH, this::parseMovie, Movie[]::new);
protected final Resource<SubtitleSearchResult[]> osdbIndex = tsv("url.osdb-index", Cache.ONE_MONTH, this::parseSubtitle, SubtitleSearchResult[]::new);
private TheTVDBSearchResult parseSeries(String[] v) {
int id = parseInt(v[0]);
@ -439,6 +425,15 @@ public class ReleaseInfo {
return new AnidbSearchResult(aid, primaryTitle, aliasNames);
}
private Movie parseMovie(String[] v) {
int imdbid = parseInt(v[0]);
int tmdbid = parseInt(v[1]);
int year = parseInt(v[2]);
String name = v[3];
String[] aliasNames = copyOfRange(v, 4, v.length);
return new Movie(name, aliasNames, year, imdbid > 0 ? imdbid : -1, tmdbid > 0 ? tmdbid : -1, null);
}
private SubtitleSearchResult parseSubtitle(String[] v) {
String kind = v[0];
int score = parseInt(v[1]);
@ -449,20 +444,20 @@ public class ReleaseInfo {
return new SubtitleSearchResult(name, aliasNames, year, imdbId, -1, Locale.ENGLISH, SubtitleSearchResult.Kind.forName(kind), score);
}
protected Resource<String[]> patternResource(String name) {
return resource(name, Cache.ONE_WEEK, s -> {
protected Resource<String[]> lines(String name, Duration expirationTime) {
return resource(name, expirationTime, s -> {
return s.length() > 0 ? s : null;
}, String[]::new);
}, String[]::new).memoize();
}
protected <T> Resource<T[]> tsvResource(String name, Function<String[], T> parse, IntFunction<T[]> generator) {
return resource(name, Cache.ONE_WEEK, s -> {
protected <A> Resource<A[]> tsv(String name, Duration expirationTime, Function<String[], A> parse, IntFunction<A[]> generator) {
return resource(name, expirationTime, s -> {
String[] v = s.split("\t");
return v.length > 0 ? parse.apply(v) : null;
}, generator);
}, generator).memoize();
}
protected <T> Resource<T[]> resource(String name, Duration expirationTime, Function<String, T> parse, IntFunction<T[]> generator) {
protected <A> Resource<A[]> resource(String name, Duration expirationTime, Function<String, A> parse, IntFunction<A[]> generator) {
return () -> {
Cache cache = Cache.getCache("data", CacheType.Persistent);
byte[] bytes = cache.bytes(name, n -> new URL(getProperty(n))).expire(expirationTime).get();