mirror of
https://github.com/mitb-archive/filebot
synced 2024-12-23 08:18:52 -05:00
Refactor CmdlineInterface with proper types for all parameters
This commit is contained in:
parent
b42149e927
commit
7a0a36b528
@ -15,6 +15,7 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import net.filebot.media.XattrMetaInfoProvider;
|
||||
import net.filebot.similarity.MetricAvg;
|
||||
@ -40,7 +41,6 @@ import net.filebot.web.TVMazeClient;
|
||||
import net.filebot.web.TheTVDBClient;
|
||||
import net.filebot.web.TheTVDBClientV1;
|
||||
import net.filebot.web.VideoHashSubtitleService;
|
||||
import one.util.streamex.StreamEx;
|
||||
|
||||
/**
|
||||
* Reuse the same web service client so login, cache, etc. can be shared.
|
||||
@ -72,29 +72,39 @@ public final class WebServices {
|
||||
public static final XattrMetaInfoProvider XattrMetaData = new XattrMetaInfoProvider();
|
||||
public static final ID3Lookup MediaInfoID3 = new ID3Lookup();
|
||||
|
||||
public static EpisodeListProvider[] getEpisodeListProviders() {
|
||||
return new EpisodeListProvider[] { TheTVDB, AniDB, TheMovieDB_TV, TVmaze };
|
||||
public static Datasource[] getServices() {
|
||||
return new Datasource[] { TheMovieDB, OMDb, TheTVDB, AniDB, TheMovieDB_TV, TVmaze, AcoustID, MediaInfoID3, XattrMetaData, OpenSubtitles, Shooter, TheTVDBv2, FanartTV };
|
||||
}
|
||||
|
||||
public static MovieIdentificationService[] getMovieIdentificationServices() {
|
||||
return new MovieIdentificationService[] { TheMovieDB, OMDb };
|
||||
}
|
||||
|
||||
public static SubtitleProvider[] getSubtitleProviders() {
|
||||
return new SubtitleProvider[] { OpenSubtitles };
|
||||
}
|
||||
|
||||
public static VideoHashSubtitleService[] getVideoHashSubtitleServices(Locale locale) {
|
||||
if (locale.equals(Locale.CHINESE))
|
||||
return new VideoHashSubtitleService[] { OpenSubtitles, Shooter };
|
||||
else
|
||||
return new VideoHashSubtitleService[] { OpenSubtitles };
|
||||
public static EpisodeListProvider[] getEpisodeListProviders() {
|
||||
return new EpisodeListProvider[] { TheTVDB, AniDB, TheMovieDB_TV, TVmaze };
|
||||
}
|
||||
|
||||
public static MusicIdentificationService[] getMusicIdentificationServices() {
|
||||
return new MusicIdentificationService[] { AcoustID, MediaInfoID3 };
|
||||
}
|
||||
|
||||
public static SubtitleProvider[] getSubtitleProviders(Locale locale) {
|
||||
return new SubtitleProvider[] { OpenSubtitles };
|
||||
}
|
||||
|
||||
public static VideoHashSubtitleService[] getVideoHashSubtitleServices(Locale locale) {
|
||||
// special support for 射手网 for Chinese language subtitles
|
||||
if (locale.equals(Locale.CHINESE)) {
|
||||
return new VideoHashSubtitleService[] { OpenSubtitles, Shooter };
|
||||
}
|
||||
|
||||
return new VideoHashSubtitleService[] { OpenSubtitles };
|
||||
}
|
||||
|
||||
public static Datasource getService(String name) {
|
||||
return getService(name, getServices());
|
||||
}
|
||||
|
||||
public static EpisodeListProvider getEpisodeListProvider(String name) {
|
||||
return getService(name, getEpisodeListProviders());
|
||||
}
|
||||
@ -107,8 +117,10 @@ public final class WebServices {
|
||||
return getService(name, getMusicIdentificationServices());
|
||||
}
|
||||
|
||||
public static <T extends Datasource> T getService(String name, T[] services) {
|
||||
return StreamEx.of(services).findFirst(it -> it.getIdentifier().equalsIgnoreCase(name) || it.getName().equalsIgnoreCase(name)).orElse(null);
|
||||
public static <T extends Datasource> T getService(String name, T... services) {
|
||||
return stream(services).filter(it -> {
|
||||
return it.getIdentifier().equalsIgnoreCase(name) || it.getName().equalsIgnoreCase(name);
|
||||
}).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public static final ExecutorService requestThreadPool = Executors.newCachedThreadPool();
|
||||
@ -127,7 +139,8 @@ public final class WebServices {
|
||||
private SearchResult merge(SearchResult prime, List<SearchResult> group) {
|
||||
int id = prime.getId();
|
||||
String name = prime.getName();
|
||||
String[] aliasNames = StreamEx.of(group).flatMap(it -> stream(it.getAliasNames())).remove(name::equals).distinct().toArray(String[]::new);
|
||||
|
||||
String[] aliasNames = group.stream().flatMap(it -> stream(it.getAliasNames())).filter(n -> !n.equals(name)).distinct().toArray(String[]::new);
|
||||
return new SearchResult(id, name, aliasNames);
|
||||
}
|
||||
|
||||
@ -138,7 +151,7 @@ public final class WebServices {
|
||||
Future<List<SearchResult>> localSearch = requestThreadPool.submit(() -> localIndex.get().search(query));
|
||||
|
||||
// combine alias names into a single search results, and keep API search name as primary name
|
||||
Map<Integer, SearchResult> results = StreamEx.of(apiSearch.get()).append(localSearch.get()).groupingBy(SearchResult::getId, collectingAndThen(toList(), group -> merge(group.get(0), group)));
|
||||
Map<Integer, SearchResult> results = Stream.concat(apiSearch.get().stream(), localSearch.get().stream()).collect(groupingBy(SearchResult::getId, collectingAndThen(toList(), group -> merge(group.get(0), group))));
|
||||
|
||||
return sortBySimilarity(results.values(), singleton(query), getSeriesMatchMetric());
|
||||
}
|
||||
|
@ -2,15 +2,20 @@ package net.filebot.cli;
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static net.filebot.Logging.*;
|
||||
import static net.filebot.hash.VerificationUtilities.*;
|
||||
import static net.filebot.subtitle.SubtitleUtilities.*;
|
||||
import static net.filebot.util.FileUtilities.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.kohsuke.args4j.Argument;
|
||||
@ -22,6 +27,14 @@ import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler;
|
||||
|
||||
import net.filebot.Language;
|
||||
import net.filebot.StandardRenameAction;
|
||||
import net.filebot.WebServices;
|
||||
import net.filebot.format.ExpressionFileFilter;
|
||||
import net.filebot.format.ExpressionFilter;
|
||||
import net.filebot.format.ExpressionFormat;
|
||||
import net.filebot.hash.HashType;
|
||||
import net.filebot.subtitle.SubtitleFormat;
|
||||
import net.filebot.subtitle.SubtitleNaming;
|
||||
import net.filebot.web.Datasource;
|
||||
import net.filebot.web.SortOrder;
|
||||
|
||||
public class ArgumentBean {
|
||||
@ -194,12 +207,61 @@ public class ArgumentBean {
|
||||
return SortOrder.forName(order);
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return new Locale(lang);
|
||||
public ExpressionFormat getExpressionFormat() throws Exception {
|
||||
return format == null ? null : new ExpressionFormat(format);
|
||||
}
|
||||
|
||||
public ExpressionFilter getExpressionFilter() throws Exception {
|
||||
return filter == null ? null : new ExpressionFilter(filter);
|
||||
}
|
||||
|
||||
public FileFilter getExpressionFileFilter() throws Exception {
|
||||
return filter == null ? null : new ExpressionFileFilter(filter);
|
||||
}
|
||||
|
||||
public Datasource getDatasource() {
|
||||
return db == null ? null : WebServices.getService(db);
|
||||
}
|
||||
|
||||
public String getSearchQuery() {
|
||||
return query == null || query.isEmpty() ? null : query;
|
||||
}
|
||||
|
||||
public File getOutputPath() {
|
||||
return output == null ? null : new File(output);
|
||||
}
|
||||
|
||||
public File getAbsoluteOutputFolder() throws Exception {
|
||||
return output == null ? null : new File(output).getCanonicalFile();
|
||||
}
|
||||
|
||||
public SubtitleFormat getSubtitleOutputFormat() {
|
||||
return output == null ? null : getSubtitleFormatByName(output);
|
||||
}
|
||||
|
||||
public SubtitleNaming getSubtitleNamingFormat() {
|
||||
return optional(format).map(SubtitleNaming::forName).orElse(SubtitleNaming.MATCH_VIDEO_ADD_LANGUAGE_TAG);
|
||||
}
|
||||
|
||||
public HashType getOutputHashType() {
|
||||
// support --output checksum.sfv
|
||||
return optional(output).map(File::new).map(f -> getHashType(f)).orElseGet(() -> {
|
||||
// support --format SFV
|
||||
return optional(format).map(k -> getHashTypeByExtension(k)).orElse(HashType.SFV);
|
||||
});
|
||||
}
|
||||
|
||||
public Charset getEncoding() {
|
||||
return encoding == null ? null : Charset.forName(encoding);
|
||||
}
|
||||
|
||||
public Language getLanguage() {
|
||||
return Language.findLanguage(lang);
|
||||
// find language code for any input (en, eng, English, etc)
|
||||
return optional(lang).map(Language::findLanguage).orElseThrow(error("Illegal language code", lang));
|
||||
}
|
||||
|
||||
public boolean isStrict() {
|
||||
return !nonStrict;
|
||||
}
|
||||
|
||||
public Level getLogLevel() {
|
||||
@ -226,4 +288,12 @@ public class ArgumentBean {
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
private static <T> Optional<T> optional(T value) {
|
||||
return Optional.ofNullable(value);
|
||||
}
|
||||
|
||||
private static Supplier<CmdlineException> error(String message, Object value) {
|
||||
return () -> new CmdlineException(message + ": " + value);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,16 +5,15 @@ import static net.filebot.util.ExceptionUtilities.*;
|
||||
import static net.filebot.util.FileUtilities.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import javax.script.Bindings;
|
||||
import javax.script.SimpleBindings;
|
||||
|
||||
import net.filebot.MediaTypes;
|
||||
import net.filebot.StandardRenameAction;
|
||||
|
||||
public class ArgumentProcessor {
|
||||
|
||||
@ -57,47 +56,47 @@ public class ArgumentProcessor {
|
||||
|
||||
// print episode info
|
||||
if (args.list) {
|
||||
List<String> lines = cli.fetchEpisodeList(args.query, args.format, args.db, args.order, args.filter, args.lang);
|
||||
List<String> lines = cli.fetchEpisodeList(args.getDatasource(), args.getSearchQuery(), args.getExpressionFormat(), args.getExpressionFilter(), args.getSortOrder(), args.getLanguage().getLocale());
|
||||
lines.forEach(System.out::println);
|
||||
return lines.isEmpty() ? 1 : 0;
|
||||
}
|
||||
|
||||
// print media info
|
||||
if (args.mediaInfo) {
|
||||
List<String> lines = cli.getMediaInfo(args.getFiles(true), args.format, args.filter);
|
||||
List<String> lines = cli.getMediaInfo(args.getFiles(true), args.getExpressionFileFilter(), args.getExpressionFormat());
|
||||
lines.forEach(System.out::println);
|
||||
return lines.isEmpty() ? 1 : 0;
|
||||
}
|
||||
|
||||
// revert files
|
||||
if (args.revert) {
|
||||
List<File> files = cli.revert(args.getFiles(false), args.filter, "TEST".equalsIgnoreCase(args.action));
|
||||
List<File> files = cli.revert(args.getFiles(false), args.getExpressionFileFilter(), args.getRenameAction());
|
||||
return files.isEmpty() ? 1 : 0;
|
||||
}
|
||||
|
||||
// file operations
|
||||
Collection<File> files = new LinkedHashSet<File>(args.getFiles(true));
|
||||
Set<File> files = new LinkedHashSet<File>(args.getFiles(true));
|
||||
|
||||
if (args.extract) {
|
||||
files.addAll(cli.extract(files, args.output, args.conflict, null, true));
|
||||
files.addAll(cli.extract(files, args.getOutputPath(), args.getConflictAction(), null, true));
|
||||
}
|
||||
|
||||
if (args.getSubtitles) {
|
||||
files.addAll(cli.getMissingSubtitles(files, args.db, args.query, args.lang, args.output, args.encoding, args.format, !args.nonStrict));
|
||||
files.addAll(cli.getMissingSubtitles(files, args.getSearchQuery(), args.getLanguage(), args.getSubtitleOutputFormat(), args.getEncoding(), args.getSubtitleNamingFormat(), args.isStrict()));
|
||||
}
|
||||
|
||||
if (args.rename) {
|
||||
cli.rename(files, StandardRenameAction.forName(args.action), args.conflict, args.output, args.format, args.db, args.query, args.order, args.filter, args.lang, !args.nonStrict);
|
||||
cli.rename(files, args.getRenameAction(), args.getConflictAction(), args.getAbsoluteOutputFolder(), args.getExpressionFormat(), args.getDatasource(), args.getSearchQuery(), args.getSortOrder(), args.getExpressionFilter(), args.getLanguage().getLocale(), args.isStrict());
|
||||
}
|
||||
|
||||
if (args.check) {
|
||||
// check verification file
|
||||
if (containsOnly(files, MediaTypes.getDefaultFilter("verification"))) {
|
||||
if (!cli.check(files)) {
|
||||
throw new Exception("Data corruption detected"); // one or more hashes mismatch
|
||||
throw new Exception("Data corruption detected"); // one or more hashes do not match
|
||||
}
|
||||
} else {
|
||||
cli.compute(files, args.output, args.encoding);
|
||||
cli.compute(files, args.getOutputPath(), args.getOutputHashType(), args.getEncoding());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,32 +2,42 @@ package net.filebot.cli;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import net.filebot.Language;
|
||||
import net.filebot.RenameAction;
|
||||
import net.filebot.format.ExpressionFilter;
|
||||
import net.filebot.format.ExpressionFormat;
|
||||
import net.filebot.hash.HashType;
|
||||
import net.filebot.subtitle.SubtitleFormat;
|
||||
import net.filebot.subtitle.SubtitleNaming;
|
||||
import net.filebot.web.Datasource;
|
||||
import net.filebot.web.SortOrder;
|
||||
|
||||
public interface CmdlineInterface {
|
||||
|
||||
List<File> rename(Collection<File> files, RenameAction action, String conflict, String output, String format, String db, String query, String sortOrder, String filter, String lang, boolean strict) throws Exception;
|
||||
List<File> rename(Collection<File> files, RenameAction action, ConflictAction conflict, File output, ExpressionFormat format, Datasource db, String query, SortOrder order, ExpressionFilter filter, Locale locale, boolean strict) throws Exception;
|
||||
|
||||
List<File> rename(Map<File, File> renameMap, RenameAction renameAction, String conflict) throws Exception;
|
||||
List<File> rename(Map<File, File> rename, RenameAction action, ConflictAction conflict) throws Exception;
|
||||
|
||||
List<File> revert(Collection<File> files, String filter, boolean test) throws Exception;
|
||||
List<File> revert(Collection<File> files, FileFilter filter, RenameAction action) throws Exception;
|
||||
|
||||
List<File> getSubtitles(Collection<File> files, String db, String query, String lang, String output, String encoding, String format, boolean strict) throws Exception;
|
||||
List<File> getSubtitles(Collection<File> files, String query, Language language, SubtitleFormat output, Charset encoding, SubtitleNaming format, boolean strict) throws Exception;
|
||||
|
||||
List<File> getMissingSubtitles(Collection<File> files, String db, String query, String lang, String output, String encoding, String format, boolean strict) throws Exception;
|
||||
List<File> getMissingSubtitles(Collection<File> files, String query, Language language, SubtitleFormat output, Charset encoding, SubtitleNaming format, boolean strict) throws Exception;
|
||||
|
||||
boolean check(Collection<File> files) throws Exception;
|
||||
|
||||
File compute(Collection<File> files, String output, String encoding) throws Exception;
|
||||
File compute(Collection<File> files, File output, HashType hash, Charset encoding) throws Exception;
|
||||
|
||||
List<String> fetchEpisodeList(String query, String format, String db, String sortOrder, String filter, String lang) throws Exception;
|
||||
List<String> fetchEpisodeList(Datasource db, String query, ExpressionFormat format, ExpressionFilter filter, SortOrder order, Locale locale) throws Exception;
|
||||
|
||||
List<String> getMediaInfo(Collection<File> files, String format, String filter) throws Exception;
|
||||
List<String> getMediaInfo(Collection<File> files, FileFilter filter, ExpressionFormat format) throws Exception;
|
||||
|
||||
List<File> extract(Collection<File> files, String output, String conflict, FileFilter filter, boolean forceExtractAll) throws Exception;
|
||||
List<File> extract(Collection<File> files, File output, ConflictAction conflict, FileFilter filter, boolean forceExtractAll) throws Exception;
|
||||
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ import static net.filebot.util.RegularExpressions.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
@ -37,7 +36,6 @@ import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
@ -49,7 +47,6 @@ import net.filebot.RenameAction;
|
||||
import net.filebot.StandardRenameAction;
|
||||
import net.filebot.archive.Archive;
|
||||
import net.filebot.archive.FileMapper;
|
||||
import net.filebot.format.ExpressionFileFilter;
|
||||
import net.filebot.format.ExpressionFilter;
|
||||
import net.filebot.format.ExpressionFormat;
|
||||
import net.filebot.format.MediaBindingBean;
|
||||
@ -59,7 +56,6 @@ import net.filebot.hash.VerificationFileWriter;
|
||||
import net.filebot.media.AutoDetection;
|
||||
import net.filebot.media.AutoDetection.Group;
|
||||
import net.filebot.media.AutoDetection.Type;
|
||||
import net.filebot.media.MediaDetection;
|
||||
import net.filebot.media.VideoQuality;
|
||||
import net.filebot.media.XattrMetaInfoProvider;
|
||||
import net.filebot.similarity.CommonSequenceMatcher;
|
||||
@ -93,30 +89,25 @@ import net.filebot.web.VideoHashSubtitleService;
|
||||
public class CmdlineOperations implements CmdlineInterface {
|
||||
|
||||
@Override
|
||||
public List<File> rename(Collection<File> files, RenameAction action, String conflict, String output, String formatExpression, String db, String query, String sortOrder, String filterExpression, String lang, boolean strict) throws Exception {
|
||||
ExpressionFormat format = (formatExpression != null) ? new ExpressionFormat(formatExpression) : null;
|
||||
ExpressionFilter filter = (filterExpression != null) ? new ExpressionFilter(filterExpression) : null;
|
||||
File outputDir = (output != null && output.length() > 0) ? new File(output).getAbsoluteFile() : null;
|
||||
Locale locale = getLanguage(lang).getLocale();
|
||||
ConflictAction conflictAction = ConflictAction.forName(conflict);
|
||||
|
||||
if (getMovieIdentificationService(db) != null) {
|
||||
// movie mode
|
||||
return renameMovie(files, action, conflictAction, outputDir, format, getMovieIdentificationService(db), query, filter, locale, strict);
|
||||
public List<File> rename(Collection<File> files, RenameAction action, ConflictAction conflict, File output, ExpressionFormat format, Datasource db, String query, SortOrder order, ExpressionFilter filter, Locale locale, boolean strict) throws Exception {
|
||||
// movie mode
|
||||
if (db instanceof MovieIdentificationService) {
|
||||
return renameMovie(files, action, conflict, output, format, (MovieIdentificationService) db, query, filter, locale, strict);
|
||||
}
|
||||
|
||||
if (getEpisodeListProvider(db) != null) {
|
||||
// tv series mode
|
||||
return renameSeries(files, action, conflictAction, outputDir, format, getEpisodeListProvider(db), query, SortOrder.forName(sortOrder), filter, locale, strict);
|
||||
// series mode
|
||||
if (db instanceof EpisodeListProvider) {
|
||||
return renameSeries(files, action, conflict, output, format, (EpisodeListProvider) db, query, order, filter, locale, strict);
|
||||
}
|
||||
|
||||
if (getMusicIdentificationService(db) != null) {
|
||||
// music mode
|
||||
return renameMusic(files, action, conflictAction, outputDir, format, getMusicIdentificationService(db));
|
||||
// music mode
|
||||
if (db instanceof MusicIdentificationService) {
|
||||
return renameMusic(files, action, conflict, output, format, (MusicIdentificationService) db);
|
||||
}
|
||||
|
||||
if (XattrMetaData.getIdentifier().equalsIgnoreCase(db)) {
|
||||
return renameFiles(files, action, conflictAction, outputDir, format, XattrMetaData, filter, strict);
|
||||
// generic file / xattr mode
|
||||
if (db instanceof XattrMetaInfoProvider) {
|
||||
return renameFiles(files, action, conflict, output, format, (XattrMetaInfoProvider) db, filter, strict);
|
||||
}
|
||||
|
||||
// auto-detect mode for each fileset
|
||||
@ -128,16 +119,16 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
for (Type key : it.getKey().types()) {
|
||||
switch (key) {
|
||||
case Movie:
|
||||
results.addAll(renameMovie(it.getValue(), action, conflictAction, outputDir, format, TheMovieDB, query, filter, locale, strict));
|
||||
results.addAll(renameMovie(it.getValue(), action, conflict, output, format, TheMovieDB, query, filter, locale, strict));
|
||||
break;
|
||||
case Series:
|
||||
results.addAll(renameSeries(it.getValue(), action, conflictAction, outputDir, format, TheTVDB, query, SortOrder.forName(sortOrder), filter, locale, strict));
|
||||
results.addAll(renameSeries(it.getValue(), action, conflict, output, format, TheTVDB, query, order, filter, locale, strict));
|
||||
break;
|
||||
case Anime:
|
||||
results.addAll(renameSeries(it.getValue(), action, conflictAction, outputDir, format, AniDB, query, SortOrder.forName(sortOrder), filter, locale, strict));
|
||||
results.addAll(renameSeries(it.getValue(), action, conflict, output, format, AniDB, query, order, filter, locale, strict));
|
||||
break;
|
||||
case Music:
|
||||
results.addAll(renameMusic(it.getValue(), action, conflictAction, outputDir, format, MediaInfoID3, AcoustID));
|
||||
results.addAll(renameMusic(it.getValue(), action, conflict, output, format, MediaInfoID3, AcoustID));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -146,13 +137,17 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
}
|
||||
}
|
||||
|
||||
if (results.isEmpty()) {
|
||||
throw new CmdlineException("Failed to identify or process any files");
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<File> rename(Map<File, File> renameMap, RenameAction renameAction, String conflict) throws Exception {
|
||||
public List<File> rename(Map<File, File> renameMap, RenameAction renameAction, ConflictAction conflict) throws Exception {
|
||||
// generic rename function that can be passed any set of files
|
||||
return renameAll(renameMap, renameAction, ConflictAction.forName(conflict), null);
|
||||
return renameAll(renameMap, renameAction, conflict, null);
|
||||
}
|
||||
|
||||
public List<File> renameSeries(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFormat format, EpisodeListProvider db, String query, SortOrder sortOrder, ExpressionFilter filter, Locale locale, boolean strict) throws Exception {
|
||||
@ -658,7 +653,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
return new ArrayList<File>(renameLog.values());
|
||||
}
|
||||
|
||||
private static File nextAvailableIndexedName(File file) {
|
||||
protected static File nextAvailableIndexedName(File file) {
|
||||
File parent = file.getParentFile();
|
||||
String name = getName(file);
|
||||
String ext = getExtension(file);
|
||||
@ -666,17 +661,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<File> getSubtitles(Collection<File> files, String db, String query, String languageName, String output, String csn, String format, boolean strict) throws Exception {
|
||||
Language language = getLanguage(languageName);
|
||||
SubtitleNaming naming = getSubtitleNaming(format);
|
||||
|
||||
// use all or only selected subtitle services
|
||||
Predicate<Datasource> serviceFilter = service -> db == null ? true : db.contains(service.getName()) || db.contains(service.getIdentifier());
|
||||
|
||||
// when rewriting subtitles to target format an encoding must be defined, default to UTF-8
|
||||
Charset outputEncoding = csn != null ? Charset.forName(csn) : output != null ? UTF_8 : null;
|
||||
SubtitleFormat outputFormat = output != null ? getSubtitleFormatByName(output) : null;
|
||||
|
||||
public List<File> getSubtitles(Collection<File> files, String query, Language language, SubtitleFormat output, Charset encoding, SubtitleNaming format, boolean strict) throws Exception {
|
||||
// ignore anything that is not a video
|
||||
files = filter(files, VIDEO_FILES);
|
||||
|
||||
@ -696,14 +681,14 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
|
||||
// lookup subtitles by hash
|
||||
for (VideoHashSubtitleService service : getVideoHashSubtitleServices(language.getLocale())) {
|
||||
if (remainingVideos.isEmpty() || !serviceFilter.test(service) || !requireLogin(service)) {
|
||||
if (remainingVideos.isEmpty() || !requireLogin(service)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
log.fine("Looking up subtitles by hash via " + service.getName());
|
||||
Map<File, List<SubtitleDescriptor>> options = lookupSubtitlesByHash(service, remainingVideos, language.getName(), false, strict);
|
||||
Map<File, File> downloads = downloadSubtitleBatch(service, options, outputFormat, outputEncoding, naming);
|
||||
Map<File, File> downloads = downloadSubtitleBatch(service, options, output, encoding, format);
|
||||
remainingVideos.removeAll(downloads.keySet());
|
||||
subtitleFiles.addAll(downloads.values());
|
||||
} catch (Exception e) {
|
||||
@ -711,15 +696,15 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
}
|
||||
}
|
||||
|
||||
for (SubtitleProvider service : getSubtitleProviders()) {
|
||||
if (strict || remainingVideos.isEmpty() || !serviceFilter.test(service) || !requireLogin(service)) {
|
||||
for (SubtitleProvider service : getSubtitleProviders(language.getLocale())) {
|
||||
if (strict || remainingVideos.isEmpty() || !requireLogin(service)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
log.fine(format("Looking up subtitles by name via %s", service.getName()));
|
||||
Map<File, List<SubtitleDescriptor>> options = findSubtitlesByName(service, remainingVideos, language.getName(), query, false, strict);
|
||||
Map<File, File> downloads = downloadSubtitleBatch(service, options, outputFormat, outputEncoding, naming);
|
||||
Map<File, File> downloads = downloadSubtitleBatch(service, options, output, encoding, format);
|
||||
remainingVideos.removeAll(downloads.keySet());
|
||||
subtitleFiles.addAll(downloads.values());
|
||||
} catch (Exception e) {
|
||||
@ -735,7 +720,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
return subtitleFiles;
|
||||
}
|
||||
|
||||
private static boolean requireLogin(Object service) {
|
||||
protected static boolean requireLogin(Object service) {
|
||||
if (service instanceof OpenSubtitlesClient) {
|
||||
OpenSubtitlesClient osdb = (OpenSubtitlesClient) service;
|
||||
if (osdb.isAnonymous()) {
|
||||
@ -746,47 +731,38 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<File> getMissingSubtitles(Collection<File> files, String db, String query, String languageName, String output, String csn, String format, boolean strict) throws Exception {
|
||||
// sanity check
|
||||
for (File f : files) {
|
||||
if (!f.exists()) {
|
||||
throw new FileNotFoundException(f.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public List<File> getMissingSubtitles(Collection<File> files, String query, Language language, SubtitleFormat output, Charset encoding, SubtitleNaming format, boolean strict) throws Exception {
|
||||
List<File> videoFiles = filter(filter(files, VIDEO_FILES), new FileFilter() {
|
||||
|
||||
// save time on repeating filesystem calls
|
||||
private Map<File, List<File>> cache = new HashMap<File, List<File>>();
|
||||
|
||||
private SubtitleNaming naming = getSubtitleNaming(format);
|
||||
|
||||
// get language code suffix for given language (.eng)
|
||||
private String languageCode = Language.getStandardLanguageCode(getLanguage(languageName).getName());
|
||||
|
||||
public boolean matchesLanguageCode(File f) {
|
||||
Locale languageSuffix = MediaDetection.releaseInfo.getSubtitleLanguageTag(getName(f));
|
||||
Language language = Language.getLanguage(languageSuffix);
|
||||
if (language != null) {
|
||||
return language.getISO3().equalsIgnoreCase(languageCode);
|
||||
Language languageSuffix = Language.getLanguage(releaseInfo.getSubtitleLanguageTag(getName(f)));
|
||||
if (languageSuffix != null) {
|
||||
return languageSuffix.getCode().equals(language.getCode());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(File video) {
|
||||
if (!video.isFile()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<File> subtitleFiles = cache.computeIfAbsent(video.getParentFile(), parent -> {
|
||||
return getChildren(parent, SUBTITLE_FILES);
|
||||
});
|
||||
|
||||
// can't tell which subtitle belongs to which file -> if any subtitles exist skip the whole folder
|
||||
if (naming == SubtitleNaming.ORIGINAL) {
|
||||
if (format == SubtitleNaming.ORIGINAL) {
|
||||
return subtitleFiles.size() == 0;
|
||||
}
|
||||
|
||||
return subtitleFiles.stream().allMatch(f -> {
|
||||
if (isDerived(f, video)) {
|
||||
return naming != SubtitleNaming.MATCH_VIDEO && !matchesLanguageCode(f);
|
||||
return format != SubtitleNaming.MATCH_VIDEO && !matchesLanguageCode(f);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@ -798,16 +774,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
return getSubtitles(videoFiles, db, query, languageName, output, csn, format, strict);
|
||||
}
|
||||
|
||||
private SubtitleNaming getSubtitleNaming(String format) {
|
||||
SubtitleNaming naming = SubtitleNaming.forName(format);
|
||||
if (naming != null) {
|
||||
return naming;
|
||||
} else {
|
||||
return SubtitleNaming.MATCH_VIDEO_ADD_LANGUAGE_TAG;
|
||||
}
|
||||
return getSubtitles(videoFiles, query, language, output, encoding, format, strict);
|
||||
}
|
||||
|
||||
private Map<File, File> downloadSubtitleBatch(Datasource service, Map<File, List<SubtitleDescriptor>> subtitles, SubtitleFormat outputFormat, Charset outputEncoding, SubtitleNaming naming) {
|
||||
@ -834,19 +801,25 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
MemoryFile subtitleFile = fetchSubtitle(descriptor);
|
||||
|
||||
// subtitle filename is based on movie filename
|
||||
String ext = getExtension(subtitleFile.getName());
|
||||
String extension = getExtension(subtitleFile.getName());
|
||||
ByteBuffer data = subtitleFile.getData();
|
||||
|
||||
if (outputFormat != null || outputEncoding != null) {
|
||||
// adjust extension of the output file
|
||||
if (outputFormat != null) {
|
||||
ext = outputFormat.getFilter().extension(); // adjust extension of the output file
|
||||
extension = outputFormat.getFilter().extension();
|
||||
}
|
||||
|
||||
log.finest(format("Export [%s] as [%s / %s]", subtitleFile.getName(), outputFormat, outputEncoding.displayName(Locale.ROOT)));
|
||||
// default to UTF-8 if no other encoding is given
|
||||
if (outputEncoding == null) {
|
||||
outputEncoding = UTF_8;
|
||||
}
|
||||
|
||||
log.finest(format("Export [%s] as [%s / %s]", subtitleFile.getName(), outputFormat, outputEncoding));
|
||||
data = exportSubtitles(subtitleFile, outputFormat, 0, outputEncoding);
|
||||
}
|
||||
|
||||
File destination = new File(movieFile.getParentFile(), naming.format(movieFile, descriptor, ext));
|
||||
File destination = new File(movieFile.getParentFile(), naming.format(movieFile, descriptor, extension));
|
||||
log.info(format("Writing [%s] to [%s]", subtitleFile.getName(), destination.getName()));
|
||||
|
||||
writeFile(data, destination);
|
||||
@ -893,18 +866,6 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
return probableMatches.size() <= 5 ? probableMatches : probableMatches.subList(0, 5); // trust that the correct match is in the Top 3
|
||||
}
|
||||
|
||||
private Language getLanguage(String lang) throws Exception {
|
||||
// try to look up by language code
|
||||
Language language = Language.findLanguage(lang);
|
||||
|
||||
if (language == null) {
|
||||
// unable to lookup language
|
||||
throw new CmdlineException("Illegal language code: " + lang);
|
||||
}
|
||||
|
||||
return language;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check(Collection<File> files) throws Exception {
|
||||
// only check existing hashes
|
||||
@ -918,10 +879,14 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public File compute(Collection<File> files, String output, String csn) throws Exception {
|
||||
public File compute(Collection<File> files, File output, HashType hash, Charset encoding) throws Exception {
|
||||
// ignore folders and any sort of special files
|
||||
files = filter(files, FILES);
|
||||
|
||||
if (files.isEmpty()) {
|
||||
throw new CmdlineException("No files: " + files);
|
||||
}
|
||||
|
||||
// find common parent folder of all files
|
||||
File[] fileList = files.toArray(new File[0]);
|
||||
File[][] pathArray = new File[fileList.length][];
|
||||
@ -933,38 +898,22 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
File[] common = csm.matchFirstCommonSequence(pathArray);
|
||||
|
||||
if (common == null) {
|
||||
throw new CmdlineException("Paths must be on the same filesystem: " + files);
|
||||
throw new CmdlineException("All paths must be on the same filesystem: " + files);
|
||||
}
|
||||
|
||||
// last element in the common sequence must be the root folder
|
||||
File root = common[common.length - 1];
|
||||
|
||||
// create verification file
|
||||
File outputFile;
|
||||
HashType hashType;
|
||||
|
||||
if (output != null && getExtension(output) != null) {
|
||||
// use given filename
|
||||
hashType = getHashTypeByExtension(getExtension(output));
|
||||
outputFile = new File(root, output);
|
||||
} else {
|
||||
// auto-select the filename based on folder and type
|
||||
hashType = (output != null) ? getHashTypeByExtension(output) : HashType.SFV;
|
||||
outputFile = new File(root, root.getName() + "." + hashType.getFilter().extension());
|
||||
if (output == null) {
|
||||
output = new File(root, root.getName() + '.' + hash.getFilter().extension());
|
||||
} else if (!output.isAbsolute()) {
|
||||
output = new File(root, output.getPath());
|
||||
}
|
||||
|
||||
if (hashType == null) {
|
||||
throw new CmdlineException("Illegal output type: " + output);
|
||||
}
|
||||
log.info(format("Compute %s hash for %s files [%s]", hash, files.size(), output));
|
||||
compute(root, files, output, hash, encoding);
|
||||
|
||||
if (files.isEmpty()) {
|
||||
throw new CmdlineException("No files: " + files);
|
||||
}
|
||||
|
||||
log.info(format("Compute %s hash for %s files [%s]", hashType, files.size(), outputFile));
|
||||
compute(root.getPath(), files, outputFile, hashType, csn);
|
||||
|
||||
return outputFile;
|
||||
return output;
|
||||
}
|
||||
|
||||
private boolean check(File verificationFile, File root) throws Exception {
|
||||
@ -1004,16 +953,16 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
return status;
|
||||
}
|
||||
|
||||
private void compute(String root, Collection<File> files, File outputFile, HashType hashType, String csn) throws IOException, Exception {
|
||||
private void compute(File root, Collection<File> files, File outputFile, HashType hashType, Charset encoding) throws IOException, Exception {
|
||||
// compute hashes recursively and write to file
|
||||
VerificationFileWriter out = new VerificationFileWriter(outputFile, hashType.getFormat(), csn != null ? csn : "UTF-8");
|
||||
VerificationFileWriter out = new VerificationFileWriter(outputFile, hashType.getFormat(), encoding != null ? encoding : UTF_8);
|
||||
|
||||
try {
|
||||
for (File it : files) {
|
||||
if (it.isHidden() || MediaTypes.getDefaultFilter("verification").accept(it))
|
||||
continue;
|
||||
|
||||
String relativePath = normalizePathSeparators(it.getPath().replace(root, "")).substring(1);
|
||||
String relativePath = normalizePathSeparators(it.getPath().substring(root.getPath().length() + 1)); // skip root and first slash
|
||||
String hash = computeHash(it, hashType);
|
||||
log.info(format("%s %s", hash, relativePath));
|
||||
|
||||
@ -1028,17 +977,13 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> fetchEpisodeList(String query, String expression, String db, String sortOrderName, String filterExpression, String languageName) throws Exception {
|
||||
if (query == null || query.isEmpty()) {
|
||||
public List<String> fetchEpisodeList(Datasource db, String query, ExpressionFormat format, ExpressionFilter filter, SortOrder order, Locale locale) throws Exception {
|
||||
if (query == null) {
|
||||
throw new IllegalArgumentException("query is not defined");
|
||||
}
|
||||
|
||||
// find series on the web and fetch episode list
|
||||
ExpressionFormat format = expression == null ? null : new ExpressionFormat(expression);
|
||||
ExpressionFilter filter = filterExpression == null ? null : new ExpressionFilter(filterExpression);
|
||||
EpisodeListProvider service = db == null ? TheTVDB : getEpisodeListProvider(db);
|
||||
SortOrder sortOrder = SortOrder.forName(sortOrderName);
|
||||
Locale locale = getLanguage(languageName).getLocale();
|
||||
EpisodeListProvider service = db instanceof EpisodeListProvider ? (EpisodeListProvider) db : TheTVDB;
|
||||
|
||||
// search and select search result
|
||||
List<SearchResult> options = selectSearchResult(query, service.search(query, locale), false, false);
|
||||
@ -1047,7 +992,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
}
|
||||
|
||||
// fetch episodes and apply filter
|
||||
List<Episode> episodes = applyExpressionFilter(service.getEpisodeList(options.get(0), sortOrder, locale), filter);
|
||||
List<Episode> episodes = applyExpressionFilter(service.getEpisodeList(options.get(0), order, locale), filter);
|
||||
Map<File, Episode> context = new EntryList<File, Episode>(null, episodes);
|
||||
|
||||
return episodes.stream().map(episode -> {
|
||||
@ -1056,49 +1001,52 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMediaInfo(Collection<File> files, String format, String filter) throws Exception {
|
||||
ExpressionFormat formatter = new ExpressionFormat(format != null && format.length() > 0 ? format : "{fn} [{resolution} {vc} {channels} {ac} {minutes}m]");
|
||||
List<File> selection = filter(files, filter == null || filter.isEmpty() ? f -> true : new ExpressionFileFilter(new ExpressionFilter(filter), false));
|
||||
public List<String> getMediaInfo(Collection<File> files, FileFilter filter, ExpressionFormat format) throws Exception {
|
||||
List<File> selection = filter(files, FILES);
|
||||
|
||||
// apply custom filter
|
||||
if (filter != null) {
|
||||
selection = filter(selection, filter);
|
||||
}
|
||||
|
||||
// default expression format if not set
|
||||
ExpressionFormat formatter = format != null ? format : new ExpressionFormat("{fn} [{resolution} {vc} {channels} {ac} {minutes}m]");
|
||||
|
||||
// lazy format
|
||||
return new FunctionList<File, String>(selection, f -> formatter.format(new MediaBindingBean(xattr.getMetaInfo(f), f, null)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<File> revert(Collection<File> files, String filter, boolean test) throws Exception {
|
||||
public List<File> revert(Collection<File> files, FileFilter filter, RenameAction action) throws Exception {
|
||||
if (files.isEmpty()) {
|
||||
throw new CmdlineException("Expecting at least one input path");
|
||||
}
|
||||
|
||||
FileFilter fileFilter = filter == null || filter.isEmpty() ? f -> true : new ExpressionFileFilter(new ExpressionFilter(filter), false);
|
||||
Set<File> whitelist = new HashSet<File>(files);
|
||||
Map<File, File> history = HistorySpooler.getInstance().getCompleteHistory().getRenameMap();
|
||||
|
||||
return history.entrySet().stream().filter(it -> {
|
||||
File original = it.getKey();
|
||||
File current = it.getValue();
|
||||
return Stream.of(current, original).flatMap(f -> listPath(f).stream()).anyMatch(whitelist::contains) && current.exists() && fileFilter.accept(current);
|
||||
return Stream.of(current, original).flatMap(f -> listPath(f).stream()).anyMatch(whitelist::contains) && current.exists() && (filter == null || filter.accept(current));
|
||||
}).map(it -> {
|
||||
File original = it.getKey();
|
||||
File current = it.getValue();
|
||||
|
||||
log.info(format("Revert [%s] to [%s]", current, original));
|
||||
if (test) {
|
||||
return original;
|
||||
}
|
||||
|
||||
try {
|
||||
return StandardRenameAction.revert(current, original);
|
||||
} catch (Exception e) {
|
||||
log.warning(format("Failed to revert file: %s", e.getMessage()));
|
||||
return null;
|
||||
if (action.canRevert()) {
|
||||
try {
|
||||
return StandardRenameAction.revert(current, original);
|
||||
} catch (Exception e) {
|
||||
log.warning("Failed to revert file: " + e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}).filter(Objects::nonNull).collect(toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<File> extract(Collection<File> files, String output, String conflict, FileFilter filter, boolean forceExtractAll) throws Exception {
|
||||
ConflictAction conflictAction = ConflictAction.forName(conflict);
|
||||
|
||||
public List<File> extract(Collection<File> files, File output, ConflictAction conflict, FileFilter filter, boolean forceExtractAll) throws Exception {
|
||||
// only keep single-volume archives or first part of multi-volume archives
|
||||
List<File> archiveFiles = filter(files, Archive.VOLUME_ONE_FILTER);
|
||||
List<File> extractedFiles = new ArrayList<File>();
|
||||
@ -1106,11 +1054,11 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
for (File file : archiveFiles) {
|
||||
Archive archive = Archive.open(file);
|
||||
try {
|
||||
File outputFolder = new File(output != null ? output : getName(file));
|
||||
if (!outputFolder.isAbsolute()) {
|
||||
outputFolder = new File(file.getParentFile(), outputFolder.getPath());
|
||||
File outputFolder = output;
|
||||
|
||||
if (outputFolder == null || !outputFolder.isAbsolute()) {
|
||||
outputFolder = new File(file.getParentFile(), outputFolder == null ? getName(file) : outputFolder.getPath()).getCanonicalFile();
|
||||
}
|
||||
outputFolder = outputFolder.getCanonicalFile(); // normalize weird paths
|
||||
|
||||
log.info(format("Read archive [%s] and extract to [%s]", file.getName(), outputFolder));
|
||||
FileMapper outputMapper = new FileMapper(outputFolder);
|
||||
@ -1135,14 +1083,14 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
|
||||
boolean skip = true;
|
||||
for (FileInfo future : filter == null || forceExtractAll ? outputMapping : selection) {
|
||||
if (conflictAction == ConflictAction.AUTO) {
|
||||
if (conflict == ConflictAction.AUTO) {
|
||||
skip &= (future.toFile().exists() && future.getLength() == future.toFile().length());
|
||||
} else {
|
||||
skip &= (future.toFile().exists());
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip || conflictAction == ConflictAction.OVERRIDE) {
|
||||
if (!skip || conflict == ConflictAction.OVERRIDE) {
|
||||
if (filter == null || forceExtractAll) {
|
||||
log.finest("Extracting files " + outputMapping);
|
||||
|
||||
|
@ -6,14 +6,14 @@ import static net.filebot.media.XattrMetaInfo.*;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
import javax.script.ScriptException;
|
||||
|
||||
public class ExpressionFileFilter implements FileFilter {
|
||||
|
||||
private final ExpressionFilter filter;
|
||||
private final boolean error;
|
||||
private ExpressionFilter filter;
|
||||
|
||||
public ExpressionFileFilter(ExpressionFilter filter, boolean error) {
|
||||
this.filter = filter;
|
||||
this.error = error;
|
||||
public ExpressionFileFilter(String expression) throws ScriptException {
|
||||
this.filter = new ExpressionFilter(expression);
|
||||
}
|
||||
|
||||
public ExpressionFilter getExpressionFilter() {
|
||||
@ -25,9 +25,9 @@ public class ExpressionFileFilter implements FileFilter {
|
||||
try {
|
||||
return filter.matches(new MediaBindingBean(xattr.getMetaInfo(f), f, null));
|
||||
} catch (Exception e) {
|
||||
debug.warning(format("Expression failed: %s", e));
|
||||
return error;
|
||||
debug.warning("Filter expression failed: " + e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ public enum HashType {
|
||||
public ExtensionFileFilter getFilter() {
|
||||
return MediaTypes.getDefaultFilter("verification/sfv");
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
MD5 {
|
||||
@ -44,7 +43,6 @@ public enum HashType {
|
||||
public ExtensionFileFilter getFilter() {
|
||||
return MediaTypes.getDefaultFilter("verification/md5sum");
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
SHA1 {
|
||||
@ -69,7 +67,6 @@ public enum HashType {
|
||||
public String toString() {
|
||||
return "SHA1";
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
SHA256 {
|
||||
@ -94,7 +91,6 @@ public enum HashType {
|
||||
public String toString() {
|
||||
return "SHA2";
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
ED2K {
|
||||
|
@ -1,28 +1,28 @@
|
||||
|
||||
package net.filebot.hash;
|
||||
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Date;
|
||||
|
||||
import net.filebot.Settings;
|
||||
|
||||
|
||||
public class VerificationFileWriter implements Closeable {
|
||||
|
||||
protected PrintWriter out;
|
||||
protected VerificationFormat format;
|
||||
|
||||
|
||||
public VerificationFileWriter(File file, VerificationFormat format, String charset) throws IOException {
|
||||
this(new PrintWriter(file, charset), format, charset);
|
||||
public VerificationFileWriter(File file, VerificationFormat format, Charset charset) throws IOException {
|
||||
this(new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset)), false), format, charset);
|
||||
}
|
||||
|
||||
|
||||
public VerificationFileWriter(PrintWriter out, VerificationFormat format, String charset) {
|
||||
public VerificationFileWriter(PrintWriter out, VerificationFormat format, Charset charset) {
|
||||
this.out = out;
|
||||
this.format = format;
|
||||
|
||||
@ -30,19 +30,16 @@ public class VerificationFileWriter implements Closeable {
|
||||
writeHeader(charset);
|
||||
}
|
||||
|
||||
|
||||
protected void writeHeader(String charset) {
|
||||
protected void writeHeader(Charset charset) {
|
||||
out.format("; Generated by %s %s on %tF at %<tT%n", Settings.getApplicationName(), Settings.getApplicationVersion(), new Date());
|
||||
out.format("; charset=%s%n", charset);
|
||||
out.format(";%n");
|
||||
}
|
||||
|
||||
|
||||
public void write(String path, String hash) {
|
||||
out.format("%s%n", format.format(path, hash));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
out.close();
|
||||
|
@ -57,7 +57,7 @@ public class Preset {
|
||||
}
|
||||
|
||||
public ExpressionFileFilter getIncludeFilter() {
|
||||
return getInputFolder() == null ? null : getValue(includes, expression -> new ExpressionFileFilter(new ExpressionFilter(expression), false));
|
||||
return getInputFolder() == null ? null : getValue(includes, expression -> new ExpressionFileFilter(expression));
|
||||
}
|
||||
|
||||
public ExpressionFormat getFormat() {
|
||||
|
@ -1,6 +1,7 @@
|
||||
|
||||
package net.filebot.ui.sfv;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -11,35 +12,29 @@ import net.filebot.hash.VerificationFileWriter;
|
||||
import net.filebot.ui.transfer.TextFileExportHandler;
|
||||
import net.filebot.util.FileUtilities;
|
||||
|
||||
|
||||
class ChecksumTableExportHandler extends TextFileExportHandler {
|
||||
|
||||
private final ChecksumTableModel model;
|
||||
|
||||
|
||||
public ChecksumTableExportHandler(ChecksumTableModel model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canExport() {
|
||||
return model.getRowCount() > 0 && defaultColumn() != null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void export(PrintWriter out) {
|
||||
export(new VerificationFileWriter(out, model.getHashType().getFormat(), "UTF-8"), defaultColumn(), model.getHashType());
|
||||
export(new VerificationFileWriter(out, model.getHashType().getFormat(), UTF_8), defaultColumn(), model.getHashType());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDefaultFileName() {
|
||||
return getDefaultFileName(defaultColumn());
|
||||
}
|
||||
|
||||
|
||||
protected File defaultColumn() {
|
||||
// select first column that is not a verification file column
|
||||
for (File root : model.getChecksumColumns()) {
|
||||
@ -50,9 +45,8 @@ class ChecksumTableExportHandler extends TextFileExportHandler {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void export(File file, File column) throws IOException {
|
||||
VerificationFileWriter writer = new VerificationFileWriter(file, model.getHashType().getFormat(), "UTF-8");
|
||||
VerificationFileWriter writer = new VerificationFileWriter(file, model.getHashType().getFormat(), UTF_8);
|
||||
|
||||
try {
|
||||
export(writer, column, model.getHashType());
|
||||
@ -61,7 +55,6 @@ class ChecksumTableExportHandler extends TextFileExportHandler {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void export(VerificationFileWriter out, File column, HashType type) {
|
||||
for (ChecksumRow row : model.rows()) {
|
||||
ChecksumCell cell = row.getChecksum(column);
|
||||
@ -76,7 +69,6 @@ class ChecksumTableExportHandler extends TextFileExportHandler {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String getDefaultFileName(File column) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
|
@ -124,15 +124,18 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleProvider, Subtitl
|
||||
|
||||
private final SubtitleDropTarget downloadDropTarget = new SubtitleDropTarget.Download() {
|
||||
|
||||
public Locale getLocale() {
|
||||
return languageComboBox.getModel().getSelectedItem() == ALL_LANGUAGES ? Locale.ROOT : languageComboBox.getModel().getSelectedItem().getLocale();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VideoHashSubtitleService[] getVideoHashSubtitleServices() {
|
||||
Locale locale = languageComboBox.getModel().getSelectedItem() == ALL_LANGUAGES ? Locale.ROOT : languageComboBox.getModel().getSelectedItem().getLocale();
|
||||
return WebServices.getVideoHashSubtitleServices(locale);
|
||||
return WebServices.getVideoHashSubtitleServices(getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubtitleProvider[] getSubtitleProviders() {
|
||||
return WebServices.getSubtitleProviders();
|
||||
return WebServices.getSubtitleProviders(getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -182,7 +185,7 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleProvider, Subtitl
|
||||
|
||||
@Override
|
||||
protected SubtitleProvider[] getSearchEngines() {
|
||||
return WebServices.getSubtitleProviders();
|
||||
return WebServices.getSubtitleProviders(getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user