mirror of
https://github.com/mitb-archive/filebot
synced 2025-03-09 13:59:49 -04:00
++ Command Line Interface ++ ヾ(@⌒ー⌒@)ノ
This commit is contained in:
parent
faf6095e3d
commit
af60f6b6f1
@ -1,83 +0,0 @@
|
||||
|
||||
package net.sourceforge.filebot;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.kohsuke.args4j.Argument;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
import net.sourceforge.filebot.ui.transfer.FileTransferable;
|
||||
import net.sourceforge.tuned.FileUtilities;
|
||||
|
||||
|
||||
public class ArgumentBean {
|
||||
|
||||
@Option(name = "-help", usage = "Print this help message")
|
||||
private boolean help = false;
|
||||
|
||||
@Option(name = "-clear", usage = "Clear application settings")
|
||||
private boolean clear = false;
|
||||
|
||||
@Option(name = "-open", usage = "Open file", metaVar = "<file>")
|
||||
private boolean open = false;
|
||||
|
||||
@Option(name = "--sfv", usage = "Open SFV panel", metaVar = "<file>")
|
||||
private boolean sfv = false;
|
||||
|
||||
@Argument
|
||||
private List<File> arguments = new LinkedList<File>();
|
||||
|
||||
|
||||
public boolean help() {
|
||||
return help;
|
||||
}
|
||||
|
||||
|
||||
public boolean clear() {
|
||||
return clear;
|
||||
}
|
||||
|
||||
|
||||
public boolean open() {
|
||||
return open;
|
||||
}
|
||||
|
||||
|
||||
public boolean sfv() {
|
||||
return sfv || (open && FileUtilities.containsOnly(arguments, MediaTypes.getDefaultFilter("verification")));
|
||||
}
|
||||
|
||||
|
||||
public List<File> arguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
|
||||
public FileTransferable transferable() {
|
||||
List<File> files = new ArrayList<File>(arguments.size());
|
||||
|
||||
for (File argument : arguments) {
|
||||
if (argument.exists()) {
|
||||
try {
|
||||
// path may be relative, use absolute path
|
||||
files.add(argument.getCanonicalFile());
|
||||
} catch (IOException e) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.SEVERE, e.toString(), e);
|
||||
}
|
||||
} else {
|
||||
// file doesn't exist
|
||||
Logger.getLogger(getClass().getName()).log(Level.WARNING, String.format("Invalid File: %s", argument));
|
||||
}
|
||||
}
|
||||
|
||||
return new FileTransferable(files);
|
||||
}
|
||||
|
||||
}
|
@ -23,10 +23,13 @@ import javax.swing.UIManager;
|
||||
import org.kohsuke.args4j.CmdLineParser;
|
||||
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sourceforge.filebot.cli.ArgumentBean;
|
||||
import net.sourceforge.filebot.cli.ArgumentProcessor;
|
||||
import net.sourceforge.filebot.format.ExpressionFormat;
|
||||
import net.sourceforge.filebot.ui.MainFrame;
|
||||
import net.sourceforge.filebot.ui.SinglePanelFrame;
|
||||
import net.sourceforge.filebot.ui.panel.sfv.SfvPanelBuilder;
|
||||
import net.sourceforge.filebot.ui.transfer.FileTransferable;
|
||||
|
||||
|
||||
public class Main {
|
||||
@ -42,57 +45,69 @@ public class Main {
|
||||
// parse arguments
|
||||
final ArgumentBean argumentBean = initializeArgumentBean(args);
|
||||
|
||||
if (argumentBean.help()) {
|
||||
printUsage(argumentBean);
|
||||
if (argumentBean.printHelp()) {
|
||||
new CmdLineParser(argumentBean).printUsage(System.out);
|
||||
|
||||
// just print help message and exit afterwards
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
if (argumentBean.clear()) {
|
||||
if (argumentBean.clearUserData()) {
|
||||
// clear preferences and cache
|
||||
Settings.forPackage(Main.class).clear();
|
||||
CacheManager.getInstance().clearAll();
|
||||
}
|
||||
|
||||
try {
|
||||
// use native laf an all platforms
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(Main.class.getName()).log(Level.WARNING, e.getMessage(), e);
|
||||
if (argumentBean.runCLI()) {
|
||||
// run cmdline interface and then exit
|
||||
System.exit(new ArgumentProcessor().process(argumentBean));
|
||||
}
|
||||
|
||||
// Start user interface
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
JFrame frame;
|
||||
|
||||
if (argumentBean.sfv()) {
|
||||
// single panel frame
|
||||
frame = new SinglePanelFrame(new SfvPanelBuilder()).publish(argumentBean.transferable());
|
||||
} else {
|
||||
// default frame
|
||||
frame = new MainFrame();
|
||||
}
|
||||
|
||||
frame.setLocationByPlatform(true);
|
||||
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
|
||||
try {
|
||||
// restore previous size and location
|
||||
restoreWindowBounds(frame, Settings.forPackage(MainFrame.class));
|
||||
// use native laf an all platforms
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (Exception e) {
|
||||
// don't care, doesn't make a difference
|
||||
Logger.getLogger(Main.class.getName()).log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
|
||||
// start application
|
||||
frame.setVisible(true);
|
||||
startUserInterface(argumentBean);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static void startUserInterface(ArgumentBean args) {
|
||||
JFrame frame;
|
||||
|
||||
if (args.openSFV()) {
|
||||
// single panel frame
|
||||
FileTransferable files = new FileTransferable(args.getFiles(false));
|
||||
frame = new SinglePanelFrame(new SfvPanelBuilder()).publish(files);
|
||||
} else {
|
||||
// default frame
|
||||
frame = new MainFrame();
|
||||
}
|
||||
|
||||
frame.setLocationByPlatform(true);
|
||||
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
|
||||
try {
|
||||
// restore previous size and location
|
||||
restoreWindowBounds(frame, Settings.forPackage(MainFrame.class));
|
||||
} catch (Exception e) {
|
||||
// don't care, doesn't make a difference
|
||||
}
|
||||
|
||||
// start application
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
|
||||
private static void restoreWindowBounds(final JFrame window, final Settings settings) {
|
||||
// store bounds on close
|
||||
window.addWindowListener(new WindowAdapter() {
|
||||
@ -184,15 +199,4 @@ public class Main {
|
||||
return argumentBean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Print command line argument usage.
|
||||
*/
|
||||
private static void printUsage(ArgumentBean argumentBean) {
|
||||
System.out.println("Options:");
|
||||
|
||||
CmdLineParser parser = new CmdLineParser(argumentBean);
|
||||
parser.printUsage(System.out);
|
||||
}
|
||||
|
||||
}
|
||||
|
131
source/net/sourceforge/filebot/cli/ArgumentBean.java
Normal file
131
source/net/sourceforge/filebot/cli/ArgumentBean.java
Normal file
@ -0,0 +1,131 @@
|
||||
|
||||
package net.sourceforge.filebot.cli;
|
||||
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import org.kohsuke.args4j.Argument;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
import net.sourceforge.filebot.MediaTypes;
|
||||
import net.sourceforge.filebot.WebServices;
|
||||
import net.sourceforge.filebot.format.ExpressionFormat;
|
||||
import net.sourceforge.filebot.ui.Language;
|
||||
import net.sourceforge.filebot.web.EpisodeListProvider;
|
||||
import net.sourceforge.filebot.web.MovieIdentificationService;
|
||||
|
||||
|
||||
public class ArgumentBean {
|
||||
|
||||
@Option(name = "-rename-series", usage = "Rename episodes", metaVar = "folder")
|
||||
public boolean renameSeries;
|
||||
|
||||
@Option(name = "-rename-movie", usage = "Rename movie", metaVar = "folder")
|
||||
public boolean renameMovie;
|
||||
|
||||
@Option(name = "-get-subtitles", usage = "Fetch subtitles", metaVar = "folder")
|
||||
public boolean getSubtitles;
|
||||
|
||||
@Option(name = "--format", usage = "Episode naming scheme", metaVar = "expression")
|
||||
public String format = "{n} - {s+'x'}{e.pad(2)} - {t}";
|
||||
|
||||
@Option(name = "--q", usage = "Search query", metaVar = "name")
|
||||
public String query = null;
|
||||
|
||||
@Option(name = "--db", usage = "Episode database")
|
||||
public String db = null;
|
||||
|
||||
@Option(name = "--lang", usage = "Language", metaVar = "language code")
|
||||
public String lang = "en";
|
||||
|
||||
@Option(name = "-help", usage = "Print this help message")
|
||||
public boolean help = false;
|
||||
|
||||
@Option(name = "-open", usage = "Open file", metaVar = "<file>")
|
||||
public boolean open = false;
|
||||
|
||||
@Option(name = "-clear", usage = "Clear application settings")
|
||||
public boolean clear = false;
|
||||
|
||||
@Argument
|
||||
public List<String> arguments = new ArrayList<String>();
|
||||
|
||||
|
||||
public boolean runCLI() {
|
||||
return getSubtitles || renameSeries || renameMovie;
|
||||
}
|
||||
|
||||
|
||||
public boolean printHelp() {
|
||||
return help;
|
||||
}
|
||||
|
||||
|
||||
public boolean openSFV() {
|
||||
return open && containsOnly(getFiles(false), MediaTypes.getDefaultFilter("verification"));
|
||||
}
|
||||
|
||||
|
||||
public boolean clearUserData() {
|
||||
return clear;
|
||||
}
|
||||
|
||||
|
||||
public List<File> getFiles(boolean resolveFolders) {
|
||||
List<File> files = new ArrayList<File>();
|
||||
|
||||
// resolve given paths
|
||||
for (String argument : arguments) {
|
||||
try {
|
||||
File file = new File(argument).getCanonicalFile();
|
||||
|
||||
// resolve folders
|
||||
files.addAll(resolveFolders && file.isDirectory() ? listFiles(singleton(file), 0) : singleton(file));
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
|
||||
public ExpressionFormat getEpisodeFormat() throws ScriptException {
|
||||
return new ExpressionFormat(format);
|
||||
}
|
||||
|
||||
|
||||
public Language getLanguage() {
|
||||
Language language = Language.getLanguage(lang);
|
||||
|
||||
if (language == null)
|
||||
throw new IllegalArgumentException("Illegal language code: " + lang);
|
||||
|
||||
return language;
|
||||
}
|
||||
|
||||
|
||||
public EpisodeListProvider getEpisodeListProvider() throws Exception {
|
||||
if (db == null)
|
||||
return WebServices.TVRage;
|
||||
|
||||
return (EpisodeListProvider) WebServices.class.getField(db).get(null);
|
||||
}
|
||||
|
||||
|
||||
public MovieIdentificationService getMovieIdentificationService() throws Exception {
|
||||
if (db == null)
|
||||
return WebServices.OpenSubtitles;
|
||||
|
||||
return (MovieIdentificationService) WebServices.class.getField(db).get(null);
|
||||
}
|
||||
|
||||
}
|
371
source/net/sourceforge/filebot/cli/ArgumentProcessor.java
Normal file
371
source/net/sourceforge/filebot/cli/ArgumentProcessor.java
Normal file
@ -0,0 +1,371 @@
|
||||
|
||||
package net.sourceforge.filebot.cli;
|
||||
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static net.sourceforge.filebot.MediaTypes.*;
|
||||
import static net.sourceforge.filebot.cli.CLILogging.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.AbstractMap.SimpleImmutableEntry;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import net.sourceforge.filebot.WebServices;
|
||||
import net.sourceforge.filebot.format.EpisodeBindingBean;
|
||||
import net.sourceforge.filebot.format.ExpressionFormat;
|
||||
import net.sourceforge.filebot.similarity.Match;
|
||||
import net.sourceforge.filebot.similarity.Matcher;
|
||||
import net.sourceforge.filebot.similarity.NameSimilarityMetric;
|
||||
import net.sourceforge.filebot.similarity.SeriesNameMatcher;
|
||||
import net.sourceforge.filebot.similarity.SimilarityMetric;
|
||||
import net.sourceforge.filebot.ui.Language;
|
||||
import net.sourceforge.filebot.ui.panel.rename.HistorySpooler;
|
||||
import net.sourceforge.filebot.ui.panel.rename.MatchSimilarityMetric;
|
||||
import net.sourceforge.filebot.vfs.ArchiveType;
|
||||
import net.sourceforge.filebot.vfs.MemoryFile;
|
||||
import net.sourceforge.filebot.web.Episode;
|
||||
import net.sourceforge.filebot.web.EpisodeListProvider;
|
||||
import net.sourceforge.filebot.web.MovieDescriptor;
|
||||
import net.sourceforge.filebot.web.MovieIdentificationService;
|
||||
import net.sourceforge.filebot.web.SearchResult;
|
||||
import net.sourceforge.filebot.web.SubtitleDescriptor;
|
||||
import net.sourceforge.filebot.web.SubtitleProvider;
|
||||
import net.sourceforge.filebot.web.VideoHashSubtitleService;
|
||||
|
||||
|
||||
public class ArgumentProcessor {
|
||||
|
||||
public int process(ArgumentBean args) throws Exception {
|
||||
try {
|
||||
SortedSet<File> files = new TreeSet<File>(args.getFiles(true));
|
||||
|
||||
if (args.getSubtitles) {
|
||||
List<File> subtitles = getSubtitles(files, args.query, args.getLanguage());
|
||||
files.addAll(subtitles);
|
||||
}
|
||||
|
||||
if (args.renameSeries) {
|
||||
renameSeries(files, args.query, args.getEpisodeFormat(), args.getEpisodeListProvider(), args.getLanguage().toLocale());
|
||||
}
|
||||
|
||||
if (args.renameMovie) {
|
||||
renameMovie(files, args.getMovieIdentificationService(), args.getLanguage().toLocale());
|
||||
}
|
||||
|
||||
CLILogger.fine("Done ヾ(@⌒ー⌒@)ノ");
|
||||
return 0;
|
||||
} catch (Exception e) {
|
||||
CLILogger.severe(e.getMessage());
|
||||
CLILogger.fine("Failure (°_°)");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void renameSeries(Collection<File> files, String query, ExpressionFormat format, EpisodeListProvider datasource, Locale locale) throws Exception {
|
||||
List<File> mediaFiles = filter(files, VIDEO_FILES, SUBTITLE_FILES);
|
||||
|
||||
if (mediaFiles.isEmpty()) {
|
||||
throw new IllegalArgumentException("No video or subtitle files: " + files);
|
||||
}
|
||||
|
||||
// auto-detect series name if not given
|
||||
if (query == null) {
|
||||
Collection<String> possibleNames = new SeriesNameMatcher().matchAll(mediaFiles.toArray(new File[0]));
|
||||
|
||||
if (possibleNames.size() == 1) {
|
||||
query = possibleNames.iterator().next();
|
||||
CLILogger.config("Auto-detected series name: " + possibleNames);
|
||||
} else {
|
||||
throw new Exception("Failed to auto-detect series name: " + possibleNames);
|
||||
}
|
||||
}
|
||||
|
||||
CLILogger.fine(format("Fetching episode data for [%s] from [%s]", query, datasource.getName()));
|
||||
|
||||
// find series on the web
|
||||
SearchResult hit = selectSearchResult(query, datasource.search(query, locale));
|
||||
|
||||
// fetch episode list
|
||||
List<Episode> episodes = datasource.getEpisodeList(hit, locale);
|
||||
|
||||
List<Match<File, Episode>> matches = new ArrayList<Match<File, Episode>>();
|
||||
matches.addAll(match(filter(mediaFiles, VIDEO_FILES), episodes));
|
||||
matches.addAll(match(filter(mediaFiles, SUBTITLE_FILES), episodes));
|
||||
|
||||
if (matches.isEmpty()) {
|
||||
throw new RuntimeException("Unable to match files to episode data");
|
||||
}
|
||||
|
||||
// map old files to new paths by applying formatting and validating filenames
|
||||
Map<File, String> renameMap = new LinkedHashMap<File, String>();
|
||||
|
||||
for (Match<File, Episode> match : matches) {
|
||||
File file = match.getValue();
|
||||
String newName = format.format(new EpisodeBindingBean(match.getCandidate(), file));
|
||||
|
||||
if (isInvalidFileName(newName)) {
|
||||
CLILogger.config("Stripping invalid characters from new name: " + newName);
|
||||
newName = validateFileName(newName);
|
||||
}
|
||||
|
||||
renameMap.put(file, newName + "." + getExtension(file));
|
||||
}
|
||||
|
||||
// rename episodes
|
||||
renameAll(renameMap);
|
||||
}
|
||||
|
||||
|
||||
public void renameMovie(Collection<File> files, MovieIdentificationService datasource, Locale locale) throws Exception {
|
||||
File[] movieFiles = filter(files, VIDEO_FILES).toArray(new File[0]);
|
||||
|
||||
if (movieFiles.length <= 0) {
|
||||
throw new IllegalArgumentException("No video files: " + files);
|
||||
}
|
||||
|
||||
CLILogger.fine(format("Looking up movie by filehash via [%s]", datasource.getName()));
|
||||
|
||||
// match movie hashes online
|
||||
MovieDescriptor[] movieByFileHash = datasource.getMovieDescriptors(movieFiles, locale);
|
||||
|
||||
// map old files to new paths by applying formatting and validating filenames
|
||||
Map<File, String> renameMap = new LinkedHashMap<File, String>();
|
||||
|
||||
for (int i = 0; i < movieFiles.length; i++) {
|
||||
if (movieByFileHash[i] != null) {
|
||||
String newName = movieByFileHash[i].toString();
|
||||
|
||||
if (isInvalidFileName(newName)) {
|
||||
CLILogger.config("Stripping invalid characters from new path: " + newName);
|
||||
newName = validateFileName(newName);
|
||||
}
|
||||
|
||||
renameMap.put(movieFiles[i], newName + "." + getExtension(movieFiles[i]));
|
||||
} else {
|
||||
CLILogger.warning("No matching movie: " + movieFiles[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// handle subtitle files
|
||||
for (File subtitleFile : filter(files, SUBTITLE_FILES)) {
|
||||
// check if subtitle corresponds to a movie file (same name, different extension)
|
||||
for (int i = 0; i < movieByFileHash.length; i++) {
|
||||
if (movieByFileHash != null) {
|
||||
String subtitleName = getName(subtitleFile);
|
||||
String movieName = getName(movieFiles[i]);
|
||||
|
||||
if (subtitleName.equalsIgnoreCase(movieName)) {
|
||||
String movieDestinationName = renameMap.get(movieFiles[i]);
|
||||
renameMap.put(subtitleFile, getNameWithoutExtension(movieDestinationName) + "." + getExtension(subtitleFile));
|
||||
|
||||
// movie match found, we're done
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rename episodes
|
||||
renameAll(renameMap);
|
||||
}
|
||||
|
||||
|
||||
public List<File> getSubtitles(Collection<File> files, String query, Language language) throws Exception {
|
||||
// match movie hashes online
|
||||
Set<File> videos = new TreeSet<File>(filter(files, VIDEO_FILES));
|
||||
List<File> downloadedSubtitles = new ArrayList<File>();
|
||||
|
||||
if (videos.isEmpty()) {
|
||||
throw new IllegalArgumentException("No video files: " + files);
|
||||
}
|
||||
|
||||
// lookup subtitles by hash
|
||||
for (VideoHashSubtitleService service : WebServices.getVideoHashSubtitleServices()) {
|
||||
if (videos.isEmpty())
|
||||
break;
|
||||
|
||||
CLILogger.fine("Looking up subtitles by filehash via " + service.getName());
|
||||
|
||||
for (Entry<File, List<SubtitleDescriptor>> it : service.getSubtitleList(videos.toArray(new File[0]), language.getName()).entrySet()) {
|
||||
if (it.getValue() != null && it.getValue().size() > 0) {
|
||||
// auto-select first element if there are multiple hash matches for the same video files
|
||||
File subtitle = fetchSubtitle(it.getValue().get(0), it.getKey());
|
||||
|
||||
// download complete, cross this video off the list
|
||||
videos.remove(it.getKey());
|
||||
downloadedSubtitles.add(subtitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lookup subtitles by query and filename
|
||||
if (query != null && videos.size() > 0) {
|
||||
for (SubtitleProvider service : WebServices.getSubtitleProviders()) {
|
||||
try {
|
||||
CLILogger.fine(format("Searching for [%s] at [%s]", query, service.getName()));
|
||||
SearchResult searchResult = selectSearchResult(query, service.search(query));
|
||||
|
||||
CLILogger.config("Retrieving subtitles for " + searchResult.getName());
|
||||
List<SubtitleDescriptor> subtitles = service.getSubtitleList(searchResult, language.getName());
|
||||
|
||||
for (File video : videos.toArray(new File[0])) {
|
||||
String filename = getName(video); // get name without extension
|
||||
|
||||
for (SubtitleDescriptor descriptor : subtitles) {
|
||||
if (filename.equalsIgnoreCase(descriptor.getName())) {
|
||||
File subtitle = fetchSubtitle(descriptor, video);
|
||||
|
||||
// download complete, cross this video off the list
|
||||
videos.remove(video);
|
||||
downloadedSubtitles.add(subtitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CLILogger.warning(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no subtitles for remaining video files
|
||||
for (File video : videos) {
|
||||
CLILogger.warning("No matching subtitles found: " + video);
|
||||
}
|
||||
|
||||
return downloadedSubtitles;
|
||||
}
|
||||
|
||||
|
||||
private File fetchSubtitle(SubtitleDescriptor descriptor, File movieFile) throws Exception {
|
||||
// fetch subtitle archive
|
||||
CLILogger.info(format("Fetching [%s.%s]", descriptor.getName(), descriptor.getType()));
|
||||
ByteBuffer downloadedData = descriptor.fetch();
|
||||
|
||||
// extract subtitles from archive
|
||||
ArchiveType type = ArchiveType.forName(descriptor.getType());
|
||||
MemoryFile subtitleData;
|
||||
|
||||
if (type != ArchiveType.UNDEFINED) {
|
||||
// extract subtitle from archive
|
||||
subtitleData = type.fromData(downloadedData).iterator().next();
|
||||
} else {
|
||||
// assume that the fetched data is the subtitle
|
||||
subtitleData = new MemoryFile(descriptor.getName() + "." + descriptor.getType(), downloadedData);
|
||||
}
|
||||
|
||||
// subtitle filename is based on movie filename
|
||||
String subtitleFileName = getNameWithoutExtension(movieFile.getName()) + "." + getExtension(subtitleData.getName());
|
||||
File destination = new File(movieFile.getParentFile(), validateFileName(subtitleFileName));
|
||||
|
||||
CLILogger.config(format("Writing [%s] to [%s]", subtitleData.getName(), destination.getName()));
|
||||
writeFile(subtitleData.getData(), destination);
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
|
||||
private void renameAll(Map<File, String> renameMap) {
|
||||
// rename files
|
||||
List<Entry<File, File>> renameLog = new ArrayList<Entry<File, File>>();
|
||||
|
||||
try {
|
||||
for (Entry<File, String> it : renameMap.entrySet()) {
|
||||
try {
|
||||
// rename file, throw exception on failure
|
||||
File destination = rename(it.getKey(), it.getValue());
|
||||
CLILogger.info(format("Renamed [%s] to [%s]", it.getKey(), it.getValue()));
|
||||
|
||||
// remember successfully renamed matches for history entry and possible revert
|
||||
renameLog.add(new SimpleImmutableEntry<File, File>(it.getKey(), destination));
|
||||
} catch (IOException e) {
|
||||
CLILogger.warning(format("Failed to rename [%s]", it.getKey()));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// could not rename one of the files, revert all changes
|
||||
CLILogger.severe(e.getMessage());
|
||||
|
||||
// revert rename operations in reverse order
|
||||
for (ListIterator<Entry<File, File>> iterator = renameLog.listIterator(renameLog.size()); iterator.hasPrevious();) {
|
||||
Entry<File, File> mapping = iterator.previous();
|
||||
|
||||
// revert rename
|
||||
if (mapping.getValue().renameTo(mapping.getKey())) {
|
||||
// remove reverted rename operation from log
|
||||
CLILogger.info("Reverted filename: " + mapping.getKey());
|
||||
} else {
|
||||
// failed to revert rename operation
|
||||
CLILogger.severe("Failed to revert filename: " + mapping.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (renameLog.size() > 0) {
|
||||
// update rename history
|
||||
HistorySpooler.getInstance().append(renameMap.entrySet());
|
||||
|
||||
// printer number of renamed files if any
|
||||
CLILogger.fine(format("Renamed %d files", renameLog.size()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<Match<File, Episode>> match(List<File> files, List<Episode> episodes) throws Exception {
|
||||
// strict SxE metric, don't allow in-between values
|
||||
SimilarityMetric metric = new SimilarityMetric() {
|
||||
|
||||
@Override
|
||||
public float getSimilarity(Object o1, Object o2) {
|
||||
return MatchSimilarityMetric.EpisodeIdentifier.getSimilarity(o1, o2) >= 1 ? 1 : 0;
|
||||
}
|
||||
};
|
||||
|
||||
// fail-fast matcher
|
||||
Matcher<File, Episode> matcher = new Matcher<File, Episode>(files, episodes, true, new SimilarityMetric[] { metric });
|
||||
List<Match<File, Episode>> matches = matcher.match();
|
||||
|
||||
for (File failedMatch : matcher.remainingValues()) {
|
||||
CLILogger.warning("No matching episode: " + failedMatch.getName());
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
|
||||
private SearchResult selectSearchResult(String query, Iterable<SearchResult> searchResults) throws IllegalArgumentException {
|
||||
// auto-select most probable search result
|
||||
List<SearchResult> probableMatches = new ArrayList<SearchResult>();
|
||||
|
||||
// use name similarity metric
|
||||
SimilarityMetric metric = new NameSimilarityMetric();
|
||||
|
||||
// find probable matches using name similarity > 0.9
|
||||
for (SearchResult result : searchResults) {
|
||||
if (metric.getSimilarity(query, result.getName()) > 0.9) {
|
||||
probableMatches.add(result);
|
||||
}
|
||||
}
|
||||
|
||||
if (probableMatches.size() != 1) {
|
||||
throw new IllegalArgumentException("Failed to auto-select search result: " + probableMatches);
|
||||
}
|
||||
|
||||
return probableMatches.get(0);
|
||||
}
|
||||
|
||||
}
|
56
source/net/sourceforge/filebot/cli/CLILogging.java
Normal file
56
source/net/sourceforge/filebot/cli/CLILogging.java
Normal file
@ -0,0 +1,56 @@
|
||||
|
||||
package net.sourceforge.filebot.cli;
|
||||
|
||||
|
||||
import static java.lang.System.*;
|
||||
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
public class CLILogging extends Handler {
|
||||
|
||||
public static final Logger CLILogger = createCommandlineLogger("net.sourceforge.filebot.cli");
|
||||
|
||||
|
||||
private static Logger createCommandlineLogger(String name) {
|
||||
Logger log = Logger.getLogger(name);
|
||||
log.setLevel(Level.ALL);
|
||||
|
||||
// don't use parent handlers
|
||||
log.setUseParentHandlers(false);
|
||||
|
||||
// CLI handler
|
||||
log.addHandler(new CLILogging());
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
// print messages to stdout
|
||||
out.println(record.getMessage());
|
||||
if (record.getThrown() != null) {
|
||||
record.getThrown().printStackTrace(out);
|
||||
}
|
||||
|
||||
// flush every message immediately
|
||||
out.flush();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() throws SecurityException {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
}
|
@ -41,6 +41,11 @@ public class Language {
|
||||
}
|
||||
|
||||
|
||||
public Locale toLocale() {
|
||||
return new Locale(getCode());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Language clone() {
|
||||
return new Language(code, name);
|
||||
@ -102,4 +107,5 @@ public class Language {
|
||||
|
||||
return getLanguages(codes.toArray(new String[0]));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ class EpisodeBindingDialog extends JDialog {
|
||||
|
||||
|
||||
public void setEpisode(Episode episode) {
|
||||
episodeTextField.setText(episode == null ? "" : EpisodeFormat.getDefaultInstance().format(episode));
|
||||
episodeTextField.setText(episode == null ? "" : EpisodeFormat.SeasonEpisode.format(episode));
|
||||
}
|
||||
|
||||
|
||||
@ -255,7 +255,7 @@ class EpisodeBindingDialog extends JDialog {
|
||||
|
||||
public Episode getEpisode() {
|
||||
try {
|
||||
return EpisodeFormat.getDefaultInstance().parseObject(episodeTextField.getText());
|
||||
return EpisodeFormat.Default.parseObject(episodeTextField.getText());
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class EpisodeExpressionFormatter implements MatchFormatter {
|
||||
|
||||
@Override
|
||||
public String preview(Match<?, ?> match) {
|
||||
return EpisodeFormat.getSeasonEpisodeInstance().format(match.getValue());
|
||||
return EpisodeFormat.SeasonEpisode.format(match.getValue());
|
||||
}
|
||||
|
||||
|
||||
|
@ -289,7 +289,7 @@ class EpisodeFormatDialog extends JDialog {
|
||||
|
||||
// restore episode
|
||||
try {
|
||||
episode = EpisodeFormat.getDefaultInstance().parseObject(persistentSampleEpisode.getValue());
|
||||
episode = EpisodeFormat.Default.parseObject(persistentSampleEpisode.getValue());
|
||||
} catch (Exception e) {
|
||||
// default sample
|
||||
episode = new Episode("Dark Angel", 3, 1, "Labyrinth", 42, null, new Date(2009, 6, 1));
|
||||
@ -465,7 +465,7 @@ class EpisodeFormatDialog extends JDialog {
|
||||
sample = new EpisodeBindingBean(episode, file);
|
||||
|
||||
// remember
|
||||
persistentSampleEpisode.setValue(episode == null ? "" : EpisodeFormat.getDefaultInstance().format(sample.getEpisode()));
|
||||
persistentSampleEpisode.setValue(episode == null ? "" : EpisodeFormat.Default.format(sample.getEpisode()));
|
||||
persistentSampleFile.setValue(file == null ? "" : sample.getMediaFile().getAbsolutePath());
|
||||
|
||||
// reevaluate everything
|
||||
|
@ -5,6 +5,7 @@ package net.sourceforge.filebot.ui.panel.rename;
|
||||
import java.io.File;
|
||||
|
||||
import net.sourceforge.filebot.similarity.Match;
|
||||
import net.sourceforge.filebot.vfs.AbstractFile;
|
||||
import net.sourceforge.tuned.FileUtilities;
|
||||
|
||||
|
||||
|
@ -58,6 +58,9 @@ class History {
|
||||
|
||||
|
||||
public List<Element> elements() {
|
||||
if (elements == null)
|
||||
return emptyList();
|
||||
|
||||
return unmodifiableList(elements);
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ import java.util.logging.Logger;
|
||||
import net.sourceforge.filebot.ui.panel.rename.History.Element;
|
||||
|
||||
|
||||
final class HistorySpooler {
|
||||
public final class HistorySpooler {
|
||||
|
||||
private static final HistorySpooler instance = new HistorySpooler();
|
||||
|
||||
@ -58,7 +58,9 @@ final class HistorySpooler {
|
||||
}
|
||||
|
||||
// append to session history
|
||||
sessionHistory.add(sequence);
|
||||
if (sequence.size() > 0) {
|
||||
sessionHistory.add(sequence);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -16,6 +16,7 @@ import net.sourceforge.filebot.similarity.NumericSimilarityMetric;
|
||||
import net.sourceforge.filebot.similarity.SeasonEpisodeMetric;
|
||||
import net.sourceforge.filebot.similarity.SimilarityMetric;
|
||||
import net.sourceforge.filebot.similarity.SeasonEpisodeMatcher.SxE;
|
||||
import net.sourceforge.filebot.vfs.AbstractFile;
|
||||
import net.sourceforge.filebot.web.Date;
|
||||
import net.sourceforge.filebot.web.Episode;
|
||||
import net.sourceforge.tuned.FileUtilities;
|
||||
|
@ -22,6 +22,7 @@ import net.sourceforge.filebot.hash.VerificationFileReader;
|
||||
import net.sourceforge.filebot.torrent.Torrent;
|
||||
import net.sourceforge.filebot.ui.transfer.ArrayTransferable;
|
||||
import net.sourceforge.filebot.ui.transfer.FileTransferablePolicy;
|
||||
import net.sourceforge.filebot.vfs.AbstractFile;
|
||||
import net.sourceforge.filebot.web.Episode;
|
||||
import net.sourceforge.tuned.FastFile;
|
||||
|
||||
|
@ -4,12 +4,12 @@ package net.sourceforge.filebot.ui.panel.rename;
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static net.sourceforge.filebot.ui.NotificationLogging.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
import static net.sourceforge.tuned.ui.TunedUtilities.*;
|
||||
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -90,31 +90,6 @@ class RenameAction extends AbstractAction {
|
||||
}
|
||||
|
||||
|
||||
private File rename(File file, String path) throws IOException {
|
||||
File destination = new File(path);
|
||||
|
||||
// resolve destination
|
||||
if (!destination.isAbsolute()) {
|
||||
// same folder, different name
|
||||
destination = new File(file.getParentFile(), path);
|
||||
}
|
||||
|
||||
// make sure we that we can create the destination folder structure
|
||||
File destinationFolder = destination.getParentFile();
|
||||
|
||||
// create parent folder if necessary
|
||||
if (!destinationFolder.isDirectory() && !destinationFolder.mkdirs()) {
|
||||
throw new IOException("Failed to create folder: " + destinationFolder);
|
||||
}
|
||||
|
||||
if (!file.renameTo(destination)) {
|
||||
throw new IOException("Failed to rename file: " + file.getName());
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
|
||||
private Iterable<Entry<File, String>> validate(Map<File, String> renameMap, Window parent) {
|
||||
final List<Entry<File, String>> source = new ArrayList<Entry<File, String>>(renameMap.entrySet());
|
||||
|
||||
|
@ -17,6 +17,7 @@ import javax.swing.TransferHandler;
|
||||
import net.sourceforge.filebot.ui.transfer.ByteBufferTransferable;
|
||||
import net.sourceforge.filebot.ui.transfer.ClipboardHandler;
|
||||
import net.sourceforge.filebot.ui.transfer.TransferableExportHandler;
|
||||
import net.sourceforge.filebot.vfs.MemoryFile;
|
||||
|
||||
|
||||
class MemoryFileListExportHandler implements TransferableExportHandler, ClipboardHandler {
|
||||
|
@ -55,6 +55,7 @@ import net.miginfocom.swing.MigLayout;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
import net.sourceforge.filebot.ui.panel.subtitle.SubtitlePackage.Download.Phase;
|
||||
import net.sourceforge.filebot.ui.transfer.DefaultTransferHandler;
|
||||
import net.sourceforge.filebot.vfs.MemoryFile;
|
||||
import net.sourceforge.tuned.ExceptionUtilities;
|
||||
import net.sourceforge.tuned.ui.ListView;
|
||||
import net.sourceforge.tuned.ui.TunedUtilities;
|
||||
@ -294,7 +295,7 @@ class SubtitleDownloadComponent extends JComponent {
|
||||
fc.setSelectedFile(new File(validateFileName(file.getName())));
|
||||
|
||||
if (fc.showSaveDialog(getWindow(this)) == JFileChooser.APPROVE_OPTION) {
|
||||
write(file.getData(), fc.getSelectedFile());
|
||||
writeFile(file.getData(), fc.getSelectedFile());
|
||||
}
|
||||
} else {
|
||||
// multiple files
|
||||
@ -307,7 +308,7 @@ class SubtitleDownloadComponent extends JComponent {
|
||||
for (Object object : selection) {
|
||||
MemoryFile file = (MemoryFile) object;
|
||||
File destination = new File(folder, validateFileName(file.getName()));
|
||||
write(file.getData(), destination);
|
||||
writeFile(file.getData(), destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ import javax.swing.SwingWorker;
|
||||
import javax.swing.event.SwingPropertyChangeSupport;
|
||||
|
||||
import net.sourceforge.filebot.ui.Language;
|
||||
import net.sourceforge.filebot.vfs.ArchiveType;
|
||||
import net.sourceforge.filebot.vfs.MemoryFile;
|
||||
import net.sourceforge.filebot.web.SubtitleDescriptor;
|
||||
import net.sourceforge.tuned.FileUtilities;
|
||||
|
||||
|
@ -14,6 +14,7 @@ import javax.swing.border.CompoundBorder;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
import net.sourceforge.filebot.vfs.ArchiveType;
|
||||
import net.sourceforge.tuned.ui.AbstractFancyListCellRenderer;
|
||||
|
||||
|
||||
|
@ -3,14 +3,12 @@ package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
|
||||
|
||||
import static java.lang.Math.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
@ -22,6 +20,7 @@ import net.sourceforge.filebot.subtitle.SubRipWriter;
|
||||
import net.sourceforge.filebot.subtitle.SubtitleElement;
|
||||
import net.sourceforge.filebot.subtitle.SubtitleFormat;
|
||||
import net.sourceforge.filebot.subtitle.SubtitleReader;
|
||||
import net.sourceforge.filebot.vfs.MemoryFile;
|
||||
import net.sourceforge.tuned.ByteBufferInputStream;
|
||||
|
||||
|
||||
@ -91,21 +90,7 @@ final class SubtitleUtilities {
|
||||
}
|
||||
|
||||
// write to file
|
||||
write(encoding.encode(CharBuffer.wrap(buffer)), destination);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write {@link ByteBuffer} to {@link File}.
|
||||
*/
|
||||
public static void write(ByteBuffer data, File destination) throws IOException {
|
||||
FileChannel fileChannel = new FileOutputStream(destination).getChannel();
|
||||
|
||||
try {
|
||||
fileChannel.write(data);
|
||||
} finally {
|
||||
fileChannel.close();
|
||||
}
|
||||
writeFile(encoding.encode(CharBuffer.wrap(buffer)), destination);
|
||||
}
|
||||
|
||||
|
||||
|
@ -4,7 +4,7 @@ package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
|
||||
import static javax.swing.BorderFactory.*;
|
||||
import static javax.swing.JOptionPane.*;
|
||||
import static net.sourceforge.filebot.ui.panel.subtitle.SubtitleUtilities.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
@ -718,7 +718,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog {
|
||||
return null;
|
||||
|
||||
// save to file
|
||||
write(data, destination);
|
||||
writeFile(data, destination);
|
||||
|
||||
return destination;
|
||||
} catch (Exception e) {
|
||||
|
@ -1,13 +1,13 @@
|
||||
|
||||
package net.sourceforge.filebot.ui.panel.rename;
|
||||
package net.sourceforge.filebot.vfs;
|
||||
|
||||
|
||||
class AbstractFile {
|
||||
public class AbstractFile {
|
||||
|
||||
private final String name;
|
||||
private final long length;
|
||||
|
||||
|
||||
|
||||
public AbstractFile(String name, long length) {
|
||||
this.name = name;
|
||||
this.length = length;
|
@ -1,12 +1,13 @@
|
||||
|
||||
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
package net.sourceforge.filebot.vfs;
|
||||
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
|
||||
|
||||
enum ArchiveType {
|
||||
public enum ArchiveType {
|
||||
|
||||
ZIP {
|
||||
|
||||
@Override
|
||||
@ -31,8 +32,7 @@ enum ArchiveType {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
public static ArchiveType forName(String name) {
|
||||
if ("zip".equalsIgnoreCase(name))
|
||||
return ZIP;
|
@ -1,11 +1,11 @@
|
||||
|
||||
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
package net.sourceforge.filebot.vfs;
|
||||
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
||||
class MemoryFile {
|
||||
public class MemoryFile {
|
||||
|
||||
private final String path;
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
package net.sourceforge.filebot.vfs;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
@ -17,7 +17,7 @@ import de.innosystec.unrar.exception.RarException;
|
||||
import de.innosystec.unrar.rarfile.FileHeader;
|
||||
|
||||
|
||||
class RarArchive implements Iterable<MemoryFile> {
|
||||
public class RarArchive implements Iterable<MemoryFile> {
|
||||
|
||||
private final ByteBuffer data;
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||
package net.sourceforge.filebot.vfs;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
@ -14,7 +14,7 @@ import net.sourceforge.tuned.ByteBufferInputStream;
|
||||
import net.sourceforge.tuned.ByteBufferOutputStream;
|
||||
|
||||
|
||||
class ZipArchive implements Iterable<MemoryFile> {
|
||||
public class ZipArchive implements Iterable<MemoryFile> {
|
||||
|
||||
private final ByteBuffer data;
|
||||
|
@ -106,7 +106,7 @@ public class Episode implements Serializable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return EpisodeFormat.getSeasonEpisodeInstance().format(this);
|
||||
return EpisodeFormat.SeasonEpisode.format(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,18 +12,11 @@ import java.util.regex.Pattern;
|
||||
|
||||
public class EpisodeFormat extends Format {
|
||||
|
||||
private boolean includeAirdate = true;
|
||||
private boolean includeSpecial = true;
|
||||
public static final EpisodeFormat SeasonEpisode = new EpisodeFormat(true, false);
|
||||
public static final EpisodeFormat Default = new EpisodeFormat(true, true);
|
||||
|
||||
|
||||
public static EpisodeFormat getSeasonEpisodeInstance() {
|
||||
return new EpisodeFormat(true, false);
|
||||
}
|
||||
|
||||
|
||||
public static EpisodeFormat getDefaultInstance() {
|
||||
return new EpisodeFormat(true, true);
|
||||
}
|
||||
private final boolean includeAirdate;
|
||||
private final boolean includeSpecial;
|
||||
|
||||
|
||||
public EpisodeFormat(boolean includeSpecial, boolean includeAirdate) {
|
||||
|
@ -9,6 +9,9 @@ import java.util.Locale;
|
||||
|
||||
public interface MovieIdentificationService {
|
||||
|
||||
public String getName();
|
||||
|
||||
|
||||
public List<MovieDescriptor> searchMovie(String query, Locale locale) throws Exception;
|
||||
|
||||
|
||||
|
@ -6,10 +6,13 @@ import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@ -26,7 +29,32 @@ import com.ibm.icu.text.CharsetMatch;
|
||||
|
||||
public final class FileUtilities {
|
||||
|
||||
public static byte[] readAll(File source) throws IOException {
|
||||
public static File rename(File source, String newPath) throws IOException {
|
||||
File destination = new File(newPath);
|
||||
|
||||
// resolve destination
|
||||
if (!destination.isAbsolute()) {
|
||||
// same folder, different name
|
||||
destination = new File(source.getParentFile(), newPath);
|
||||
}
|
||||
|
||||
// make sure we that we can create the destination folder structure
|
||||
File destinationFolder = destination.getParentFile();
|
||||
|
||||
// create parent folder if necessary
|
||||
if (!destinationFolder.isDirectory() && !destinationFolder.mkdirs()) {
|
||||
throw new IOException("Failed to create folder: " + destinationFolder);
|
||||
}
|
||||
|
||||
if (!source.renameTo(destination)) {
|
||||
throw new IOException("Failed to rename file: " + source.getName());
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
|
||||
public static byte[] readFile(File source) throws IOException {
|
||||
InputStream in = new FileInputStream(source);
|
||||
|
||||
try {
|
||||
@ -46,6 +74,17 @@ public final class FileUtilities {
|
||||
}
|
||||
|
||||
|
||||
public static void writeFile(ByteBuffer data, File destination) throws IOException {
|
||||
FileChannel fileChannel = new FileOutputStream(destination).getChannel();
|
||||
|
||||
try {
|
||||
fileChannel.write(data);
|
||||
} finally {
|
||||
fileChannel.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Reader createTextReader(File file) throws IOException {
|
||||
CharsetDetector detector = new CharsetDetector();
|
||||
detector.setDeclaredEncoding("UTF-8"); // small boost for UTF-8 as default encoding
|
||||
|
@ -1,43 +0,0 @@
|
||||
|
||||
package net.sourceforge.filebot;
|
||||
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.kohsuke.args4j.CmdLineException;
|
||||
import org.kohsuke.args4j.CmdLineParser;
|
||||
|
||||
|
||||
public class ArgumentBeanTest {
|
||||
|
||||
@Test
|
||||
public void clear() throws Exception {
|
||||
ArgumentBean bean = parse("-clear", "--sfv", "One Piece", "Naruto");
|
||||
|
||||
assertTrue(bean.clear());
|
||||
assertFalse(bean.help());
|
||||
|
||||
assertEquals("One Piece", bean.arguments().get(0).toString());
|
||||
assertEquals("Naruto", bean.arguments().get(1).toString());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void noClear() throws Exception {
|
||||
ArgumentBean bean = parse("--sfv", "One Piece.sfv");
|
||||
|
||||
assertFalse(bean.help());
|
||||
assertFalse(bean.clear());
|
||||
assertEquals("One Piece.sfv", bean.arguments().get(0).toString());
|
||||
}
|
||||
|
||||
|
||||
private static ArgumentBean parse(String... args) throws CmdLineException {
|
||||
ArgumentBean bean = new ArgumentBean();
|
||||
|
||||
new CmdLineParser(bean).parseArgument(args);
|
||||
|
||||
return bean;
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ import net.sourceforge.filebot.web.WebTestSuite;
|
||||
|
||||
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses( { SimilarityTestSuite.class, WebTestSuite.class, ArgumentBeanTest.class, ExpressionFormatTest.class, VerificationFormatTest.class, MatchModelTest.class, MatchSimilarityMetricTest.class, SubtitleReaderTestSuite.class })
|
||||
@SuiteClasses( { SimilarityTestSuite.class, WebTestSuite.class, ExpressionFormatTest.class, VerificationFormatTest.class, MatchModelTest.class, MatchSimilarityMetricTest.class, SubtitleReaderTestSuite.class })
|
||||
public class FileBotTestSuite {
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user