Sort Groups
This commit is contained in:
parent
7bcfd7aa19
commit
419b71b08e
|
@ -7,7 +7,6 @@ import static java.util.stream.Collectors.*;
|
||||||
import static net.filebot.Logging.*;
|
import static net.filebot.Logging.*;
|
||||||
import static net.filebot.MediaTypes.*;
|
import static net.filebot.MediaTypes.*;
|
||||||
import static net.filebot.media.MediaDetection.*;
|
import static net.filebot.media.MediaDetection.*;
|
||||||
import static net.filebot.media.XattrMetaInfo.*;
|
|
||||||
import static net.filebot.similarity.Normalization.*;
|
import static net.filebot.similarity.Normalization.*;
|
||||||
import static net.filebot.util.FileUtilities.*;
|
import static net.filebot.util.FileUtilities.*;
|
||||||
import static net.filebot.util.StringUtilities.*;
|
import static net.filebot.util.StringUtilities.*;
|
||||||
|
@ -16,10 +15,15 @@ import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.TreeSet;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
@ -62,7 +66,7 @@ public class AutoDetection {
|
||||||
|
|
||||||
private static final Pattern MOVIE_PATTERN = Pattern.compile("[\\\\/]Movies[\\\\/]", CASE_INSENSITIVE);
|
private static final Pattern MOVIE_PATTERN = Pattern.compile("[\\\\/]Movies[\\\\/]", CASE_INSENSITIVE);
|
||||||
private static final Pattern SERIES_PATTERN = Pattern.compile("[\\\\/](?:TV.Shows|TV.Series)[\\\\/]|tv[sp]-|EP\\d{1,3}|Season\\D?\\d{1,2}|\\d{4}.S\\d{2}", CASE_INSENSITIVE);
|
private static final Pattern SERIES_PATTERN = Pattern.compile("[\\\\/](?:TV.Shows|TV.Series)[\\\\/]|tv[sp]-|EP\\d{1,3}|Season\\D?\\d{1,2}|\\d{4}.S\\d{2}", CASE_INSENSITIVE);
|
||||||
private static final Pattern ANIME_PATTERN = Pattern.compile("[\\\\/]Anime[\\\\/]|[\\(\\[](\\p{XDigit}{8})[\\]\\)]|[\\[][A-Z]+Subs[\\]]", CASE_INSENSITIVE);
|
private static final Pattern ANIME_PATTERN = Pattern.compile("[\\\\/]Anime[\\\\/]|[\\(\\[]\\p{XDigit}{8}[\\]\\)]|[\\[][A-Z]+Subs[\\]]", CASE_INSENSITIVE);
|
||||||
|
|
||||||
private Predicate<File> forceIgnore = f -> false;
|
private Predicate<File> forceIgnore = f -> false;
|
||||||
private Predicate<File> forceMusic = f -> AUDIO_FILES.accept(f) && !VIDEO_FILES.accept(f);
|
private Predicate<File> forceMusic = f -> AUDIO_FILES.accept(f) && !VIDEO_FILES.accept(f);
|
||||||
|
@ -70,8 +74,11 @@ public class AutoDetection {
|
||||||
private Predicate<File> forceSeries = f -> find(f.getPath(), SERIES_PATTERN) || isEpisode(f, true);
|
private Predicate<File> forceSeries = f -> find(f.getPath(), SERIES_PATTERN) || isEpisode(f, true);
|
||||||
private Predicate<File> forceAnime = f -> find(f.getPath(), ANIME_PATTERN);
|
private Predicate<File> forceAnime = f -> find(f.getPath(), ANIME_PATTERN);
|
||||||
|
|
||||||
public Map<Group, List<File>> group() {
|
public Map<Group, Set<File>> group() {
|
||||||
return stream(files).collect(groupingBy(f -> {
|
// sort keys and values
|
||||||
|
Map<Group, Set<File>> groups = new TreeMap<Group, Set<File>>();
|
||||||
|
|
||||||
|
stream(files).parallel().collect(groupingByConcurrent(f -> {
|
||||||
Group group = new Group();
|
Group group = new Group();
|
||||||
try {
|
try {
|
||||||
if (forceIgnore.test(f)) {
|
if (forceIgnore.test(f)) {
|
||||||
|
@ -81,44 +88,37 @@ public class AutoDetection {
|
||||||
return group.music(f);
|
return group.music(f);
|
||||||
}
|
}
|
||||||
if (forceMovie.test(f)) {
|
if (forceMovie.test(f)) {
|
||||||
return group.movie(detectMovie(f, false));
|
return group.movie(getMovieMatches(f, false));
|
||||||
}
|
}
|
||||||
if (forceSeries.test(f)) {
|
if (forceSeries.test(f)) {
|
||||||
return group.series(detectSeries(f, false));
|
return group.series(getSeriesMatches(f, false));
|
||||||
}
|
}
|
||||||
if (forceAnime.test(f)) {
|
if (forceAnime.test(f)) {
|
||||||
return group.anime(detectSeries(f, true));
|
return group.anime(getSeriesMatches(f, true));
|
||||||
}
|
}
|
||||||
return detectGroup(f);
|
return detectGroup(f);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
debug.log(Level.SEVERE, e.getMessage(), e);
|
debug.log(Level.SEVERE, e.getMessage(), e);
|
||||||
}
|
}
|
||||||
return group;
|
return group;
|
||||||
}));
|
})).forEach((k, v) -> groups.put(k, new TreeSet<File>(v)));
|
||||||
|
|
||||||
|
return groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> detectSeries(File f, boolean anime) throws Exception {
|
private List<String> getSeriesMatches(File f, boolean anime) throws Exception {
|
||||||
List<String> names = detectSeriesNames(singleton(f), anime, locale);
|
List<String> names = detectSeriesNames(singleton(f), anime, locale);
|
||||||
if (names.isEmpty()) {
|
if (names.size() > 0) {
|
||||||
names = detectSeriesNames(getVideoFiles(f.getParentFile()), anime, locale);
|
return names;
|
||||||
}
|
}
|
||||||
return names;
|
List<File> episodes = getVideoFiles(f.getParentFile());
|
||||||
|
if (episodes.size() >= 5) {
|
||||||
|
return detectSeriesNames(episodes, anime, locale);
|
||||||
|
}
|
||||||
|
return emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Movie> detectMovie(File file, boolean strict) throws Exception {
|
private List<Movie> getMovieMatches(File file, boolean strict) throws Exception {
|
||||||
// 1. xattr
|
|
||||||
Object metaObject = xattr.getMetaInfo(file);
|
|
||||||
if (metaObject instanceof Movie) {
|
|
||||||
return singletonList((Movie) metaObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. perfect filename match
|
|
||||||
Movie match = matchMovie(file, 4);
|
|
||||||
if (match != null) {
|
|
||||||
return singletonList(match);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. run full-fledged movie detection
|
|
||||||
return MediaDetection.detectMovie(file, WebServices.TheMovieDB, locale, strict);
|
return MediaDetection.detectMovie(file, WebServices.TheMovieDB, locale, strict);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,8 +129,8 @@ public class AutoDetection {
|
||||||
private Group detectGroup(File f) throws Exception {
|
private Group detectGroup(File f) throws Exception {
|
||||||
Group group = new Group();
|
Group group = new Group();
|
||||||
|
|
||||||
List<String> s = detectSeries(f, false);
|
List<String> s = getSeriesMatches(f, false);
|
||||||
List<Movie> m = detectMovie(f, false);
|
List<Movie> m = getMovieMatches(f, false);
|
||||||
|
|
||||||
if (m.isEmpty() && s.isEmpty())
|
if (m.isEmpty() && s.isEmpty())
|
||||||
return group;
|
return group;
|
||||||
|
@ -244,20 +244,18 @@ public class AutoDetection {
|
||||||
return getChildren(f.getParentFile()).stream().filter(VIDEO_FILES::accept).filter(it -> {
|
return getChildren(f.getParentFile()).stream().filter(VIDEO_FILES::accept).filter(it -> {
|
||||||
return find(dn, snm) || find(normalize(it.getName()), snm);
|
return find(dn, snm) || find(normalize(it.getName()), snm);
|
||||||
}).map(it -> {
|
}).map(it -> {
|
||||||
return streamMatches(it.getName(), EPISODE_NUMBERS).map(Integer::new).sorted().distinct().collect(toList());
|
return streamMatches(it.getName(), EPISODE_NUMBERS).map(Integer::new).collect(toSet());
|
||||||
}).filter(it -> it.size() > 0).distinct().count() >= 10;
|
}).filter(it -> it.size() > 0).distinct().count() >= 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean episodeWithoutNumbers() throws Exception {
|
public boolean episodeWithoutNumbers() throws Exception {
|
||||||
return find(asn, DASH) && getMovieMatch().isEmpty();
|
return find(asn, DASH) && getMovieMatches(f, true).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean episodeNumbers() throws Exception {
|
public boolean episodeNumbers() throws Exception {
|
||||||
if (getMovieMatch().isEmpty()) {
|
String n = stripReleaseInfo(asn, false);
|
||||||
String n = stripReleaseInfo(asn, false);
|
if (parseEpisodeNumber(n, false) != null || NUMBER_PAIR.matcher(n).find()) {
|
||||||
if (parseEpisodeNumber(n, false) != null || NUMBER_PAIR.matcher(n).find()) {
|
return Stream.of(dn, fn).anyMatch(it -> snm.matcher(it).find()) && getMovieMatches(f, true).isEmpty();
|
||||||
return Stream.of(dn, fn).anyMatch(it -> snm.matcher(it).find() && getMovieMatch().isEmpty());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -271,7 +269,7 @@ public class AutoDetection {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean exactMovieMatch() throws Exception {
|
public boolean exactMovieMatch() throws Exception {
|
||||||
return detectMovie(f, true).size() > 0 && Stream.of(dn, fn).anyMatch(it -> find(it, YEAR));
|
return getMovieMatches(f, true).size() > 0 && Stream.of(dn, fn).anyMatch(it -> find(it, YEAR));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsMovieName() {
|
public boolean containsMovieName() {
|
||||||
|
@ -294,15 +292,6 @@ public class AutoDetection {
|
||||||
return m.getEffectiveNamesWithoutYear().stream().map(this::normalize).anyMatch(fn::contains);
|
return m.getEffectiveNamesWithoutYear().stream().map(this::normalize).anyMatch(fn::contains);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Movie> m_match = null;
|
|
||||||
|
|
||||||
private List<Movie> getMovieMatch() {
|
|
||||||
if (m_match == null) {
|
|
||||||
m_match = MediaDetection.matchMovieName(asList(dn, fn), true, 0);
|
|
||||||
}
|
|
||||||
return m_match;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
|
@ -333,7 +322,7 @@ public class AutoDetection {
|
||||||
Movie, Series, Anime, Music;
|
Movie, Series, Anime, Music;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Group extends EnumMap<Type, Object> {
|
public static class Group extends EnumMap<Type, Object> implements Comparable<Group> {
|
||||||
|
|
||||||
public Group() {
|
public Group() {
|
||||||
super(Type.class);
|
super(Type.class);
|
||||||
|
@ -377,6 +366,17 @@ public class AutoDetection {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Group other) {
|
||||||
|
if (size() != other.size()) {
|
||||||
|
return Integer.compare(size(), other.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream(Type.values()).mapToInt(t -> {
|
||||||
|
return Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER).compare(Objects.toString(get(t), null), Objects.toString(other.get(t), null));
|
||||||
|
}).filter(i -> i != 0).findFirst().orElse(0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue