diff --git a/source/net/filebot/cli/CmdlineOperations.java b/source/net/filebot/cli/CmdlineOperations.java index bff94b3e..1417b2a9 100644 --- a/source/net/filebot/cli/CmdlineOperations.java +++ b/source/net/filebot/cli/CmdlineOperations.java @@ -73,6 +73,7 @@ import net.filebot.web.AudioTrack; import net.filebot.web.Datasource; import net.filebot.web.Episode; import net.filebot.web.EpisodeListProvider; +import net.filebot.web.MappedEpisode; import net.filebot.web.Movie; import net.filebot.web.MovieIdentificationService; import net.filebot.web.MoviePart; @@ -220,7 +221,7 @@ public class CmdlineOperations implements CmdlineInterface { // filter episodes and apply custom mappings episodes = applyExpressionFilter(episodes, filter); - episodes = applyExpressionMapper(episodes, mapper, Episode.class); + episodes = applyEpisodeExpressionMapper(episodes, mapper); for (List filesPerType : mapByMediaExtension(filter(batch, VIDEO_FILES, SUBTITLE_FILES)).values()) { matches.addAll(matchEpisodes(filesPerType, episodes, strict)); @@ -882,8 +883,11 @@ public class CmdlineOperations implements CmdlineInterface { log.fine(format("Apply filter [%s] on [%d] items", filter.getExpression(), input.size())); + // support context bindings + Map context = new EntryList(null, input); + return input.stream().filter(it -> { - if (filter.matches(new MediaBindingBean(it, null, new EntryList(null, input)))) { + if (filter.matches(new MediaBindingBean(it, null, context))) { log.finest(format("Include [%s]", it)); return true; } @@ -891,20 +895,23 @@ public class CmdlineOperations implements CmdlineInterface { }).collect(toList()); } - protected List applyExpressionMapper(List input, ExpressionMapper mapper, Class type) { + protected List applyEpisodeExpressionMapper(List episodes, ExpressionMapper mapper) { if (mapper == null) { - return input; + return episodes; } - log.fine(format("Apply mapper [%s] on [%d] items", mapper.getExpression(), input.size())); + log.fine(format("Apply mapper [%s] on [%d] items", mapper.getExpression(), episodes.size())); - return input.stream().map(it -> { + // support episode list context + Map context = new EntryList(null, episodes); + + return episodes.stream().map(episode -> { try { - T result = (T) mapper.map(new MediaBindingBean(it, null, new EntryList(null, input)), type); - log.finest(format("Map [%s] to [%s]", it, result)); - return result; + Episode mapping = mapper.map(new MediaBindingBean(episode, null, context), Episode.class); + log.finest(format("Map [%s] to [%s]", episode, mapping)); + return new MappedEpisode(episode, mapping); } catch (Exception e) { - debug.warning(format("Exclude [%s] due to map failure: %s", it, e)); + debug.warning(format("Exclude [%s] due to map failure: %s", episode, e)); return null; } }).filter(Objects::nonNull).distinct().collect(toList()); @@ -1078,7 +1085,7 @@ public class CmdlineOperations implements CmdlineInterface { // filter episodes and apply custom mappings episodes = applyExpressionFilter(episodes, filter); - episodes = applyExpressionMapper(episodes, mapper, Episode.class); + episodes = applyEpisodeExpressionMapper(episodes, mapper); return episodes; } diff --git a/source/net/filebot/format/ExpressionMapper.java b/source/net/filebot/format/ExpressionMapper.java index dde5f100..6808e7d5 100644 --- a/source/net/filebot/format/ExpressionMapper.java +++ b/source/net/filebot/format/ExpressionMapper.java @@ -26,11 +26,11 @@ public class ExpressionMapper { return expression; } - public Object map(Object value, Class type) throws ScriptException { + public T map(Object value, Class type) throws ScriptException { return map(new ExpressionBindings(value), type); } - public Object map(Bindings bindings, Class type) throws ScriptException { + public T map(Bindings bindings, Class type) throws ScriptException { // use privileged bindings so we are not restricted by the script sandbox Bindings priviledgedBindings = PrivilegedInvocation.newProxy(Bindings.class, bindings, AccessController.getContext()); @@ -42,7 +42,7 @@ public class ExpressionMapper { Object value = compiledExpression.eval(context); // value as target type - return DefaultTypeTransformation.castToType(value, type); + return (T) DefaultTypeTransformation.castToType(value, type); } } diff --git a/source/net/filebot/media/MetaAttributes.java b/source/net/filebot/media/MetaAttributes.java index 0d628e36..ad787367 100644 --- a/source/net/filebot/media/MetaAttributes.java +++ b/source/net/filebot/media/MetaAttributes.java @@ -19,6 +19,7 @@ import net.filebot.MetaAttributeView; import net.filebot.vfs.SimpleFileInfo; import net.filebot.web.AudioTrack; import net.filebot.web.Episode; +import net.filebot.web.MappedEpisode; import net.filebot.web.Movie; import net.filebot.web.MoviePart; import net.filebot.web.MultiEpisode; @@ -28,7 +29,7 @@ public class MetaAttributes { public static final String FILENAME_KEY = "net.filebot.filename"; public static final String METADATA_KEY = "net.filebot.metadata"; - public static final Map JSON_TYPE_MAP = unmodifiableMap(Stream.of(Episode.class, MultiEpisode.class, Movie.class, MoviePart.class, AudioTrack.class, SimpleFileInfo.class).collect(toMap(Class::getName, Class::getSimpleName))); + public static final Map JSON_TYPE_MAP = unmodifiableMap(Stream.of(Episode.class, MultiEpisode.class, MappedEpisode.class, Movie.class, MoviePart.class, AudioTrack.class, SimpleFileInfo.class).collect(toMap(Class::getName, Class::getSimpleName))); private final BasicFileAttributeView fileAttributeView; private final MetaAttributeView metaAttributeView; diff --git a/source/net/filebot/web/MappedEpisode.java b/source/net/filebot/web/MappedEpisode.java new file mode 100644 index 00000000..930cff4c --- /dev/null +++ b/source/net/filebot/web/MappedEpisode.java @@ -0,0 +1,122 @@ +package net.filebot.web; + +import java.util.Set; + +public class MappedEpisode extends Episode { + + protected Episode mapping; + + public MappedEpisode(Episode original, Episode mapping) { + super(original); + this.mapping = mapping; + } + + public Episode getOriginal() { + return new Episode(this); + } + + public Episode getMapping() { + return mapping; + } + + @Override + public String getSeriesName() { + return mapping.getSeriesName(); + } + + @Override + public Integer getEpisode() { + return mapping.getEpisode(); + } + + @Override + public Integer getSeason() { + return mapping.getSeason(); + } + + @Override + public String getTitle() { + return mapping.getTitle(); + } + + @Override + public Integer getAbsolute() { + return mapping.getAbsolute(); + } + + @Override + public Integer getSpecial() { + return mapping.getSpecial(); + } + + @Override + public SimpleDate getAirdate() { + return mapping.getAirdate(); + } + + @Override + public Integer getId() { + return mapping.getId(); + } + + @Override + public SeriesInfo getSeriesInfo() { + return mapping.getSeriesInfo(); + } + + @Override + public Set getSeriesNames() { + return mapping.getSeriesNames(); + } + + @Override + public boolean isAnime() { + return mapping.isAnime(); + } + + @Override + public boolean isRegular() { + return mapping.isRegular(); + } + + @Override + public boolean isSpecial() { + return mapping.isSpecial(); + } + + @Override + public boolean equals(Object obj) { + return mapping.equals(obj); + } + + @Override + public int hashCode() { + return mapping.hashCode(); + } + + @Override + public MappedEpisode clone() { + return new MappedEpisode(this, mapping); + } + + @Override + public MappedEpisode derive(Integer season, Integer episode) { + return new MappedEpisode(this, mapping.derive(season, episode)); + } + + @Override + public MappedEpisode deriveSpecial(Integer special) { + return new MappedEpisode(this, mapping.deriveSpecial(special)); + } + + @Override + public MappedEpisode derive(String seriesName, Integer season, Integer episode, Integer absolute, Integer special) { + return new MappedEpisode(this, mapping.derive(seriesName, season, episode, absolute, special)); + } + + @Override + public String toString() { + return mapping.toString(); + } + +}