* ScriptShell rewrite nearing completion
This commit is contained in:
parent
396fb3508f
commit
ad0e0e2802
|
@ -9,7 +9,9 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
@ -177,6 +179,21 @@ public class History {
|
|||
return sequences.hashCode();
|
||||
}
|
||||
|
||||
public Map<File, File> getRenameMap() {
|
||||
Map<File, File> map = new LinkedHashMap<File, File>();
|
||||
for (History.Sequence seq : this.sequences()) {
|
||||
for (History.Element elem : seq.elements()) {
|
||||
File to = new File(elem.to());
|
||||
if (!to.isAbsolute()) {
|
||||
to = new File(elem.dir(), elem.to());
|
||||
}
|
||||
File from = new File(elem.dir(), elem.from());
|
||||
map.put(from, to);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static void exportHistory(History history, OutputStream output) {
|
||||
try {
|
||||
Marshaller marshaller = JAXBContext.newInstance(History.class).createMarshaller();
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
scriptBaseClass: net.sourceforge.filebot.cli.ScriptShellBaseClass
|
||||
starImport: net.sourceforge.filebot, net.sourceforge.filebot.hash, net.sourceforge.filebot.media, net.sourceforge.filebot.mediainfo, net.sourceforge.filebot.similarity, net.sourceforge.filebot.subtitle, net.sourceforge.filebot.torrent, net.sourceforge.filebot.web, net.sourceforge.filebot.util, groovy.io, groovy.xml, groovy.json, org.jsoup, java.nio.file, java.nio.file.attribute, java.util.regex
|
||||
starStaticImport: net.sourceforge.filebot.format.ExpressionFormatFunctions, net.sourceforge.filebot.WebServices, java.nio.file.Files
|
||||
starStaticImport: net.sourceforge.filebot.WebServices, net.sourceforge.filebot.media.MediaDetection, net.sourceforge.filebot.format.ExpressionFormatFunctions
|
|
@ -1,21 +1,42 @@
|
|||
package net.sourceforge.filebot.cli;
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static net.sourceforge.filebot.Settings.*;
|
||||
import static net.sourceforge.filebot.cli.CLILogging.*;
|
||||
import groovy.lang.Closure;
|
||||
import groovy.lang.MissingPropertyException;
|
||||
import groovy.lang.Script;
|
||||
import groovy.xml.MarkupBuilder;
|
||||
|
||||
import java.io.Console;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.io.StringWriter;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.script.Bindings;
|
||||
import javax.script.SimpleBindings;
|
||||
|
||||
import net.sourceforge.filebot.MediaTypes;
|
||||
import net.sourceforge.filebot.HistorySpooler;
|
||||
import net.sourceforge.filebot.Settings;
|
||||
import net.sourceforge.filebot.WebServices;
|
||||
import net.sourceforge.filebot.format.AssociativeScriptObject;
|
||||
import net.sourceforge.filebot.media.MediaDetection;
|
||||
import net.sourceforge.filebot.media.MetaAttributes;
|
||||
import net.sourceforge.filebot.util.FileUtilities;
|
||||
import net.sourceforge.filebot.web.Movie;
|
||||
|
||||
import com.sun.jna.Platform;
|
||||
|
||||
public abstract class ScriptShellBaseClass extends Script {
|
||||
|
||||
|
@ -107,7 +128,7 @@ public abstract class ScriptShellBaseClass extends Script {
|
|||
|
||||
// define global variable: _def
|
||||
public Map<String, String> get_def() {
|
||||
return getApplicationArguments().defines;
|
||||
return unmodifiableMap(getApplicationArguments().defines);
|
||||
}
|
||||
|
||||
// define global variable: _system
|
||||
|
@ -120,13 +141,21 @@ public abstract class ScriptShellBaseClass extends Script {
|
|||
return new AssociativeScriptObject(System.getenv());
|
||||
}
|
||||
|
||||
// define global variable: _types
|
||||
public MediaTypes get_types() {
|
||||
return MediaTypes.getDefault();
|
||||
// Complete or session rename history
|
||||
public Map<File, File> getRenameLog() throws IOException {
|
||||
return getRenameLog(false);
|
||||
}
|
||||
|
||||
// define global variable: _log
|
||||
public Logger get_log() {
|
||||
public Map<File, File> getRenameLog(boolean complete) throws IOException {
|
||||
if (complete) {
|
||||
return HistorySpooler.getInstance().getCompleteHistory().getRenameMap();
|
||||
} else {
|
||||
return HistorySpooler.getInstance().getSessionHistory().getRenameMap();
|
||||
}
|
||||
}
|
||||
|
||||
// define global variable: log
|
||||
public Logger getLog() {
|
||||
return CLILogger;
|
||||
}
|
||||
|
||||
|
@ -140,4 +169,95 @@ public abstract class ScriptShellBaseClass extends Script {
|
|||
return null;
|
||||
}
|
||||
|
||||
public String detectSeriesName(Object files) throws Exception {
|
||||
List<String> names = MediaDetection.detectSeriesNames(FileUtilities.asFileList(files), true, false, Locale.ENGLISH);
|
||||
return names.isEmpty() ? null : names.get(0);
|
||||
}
|
||||
|
||||
public Movie detectMovie(File file, boolean strict) {
|
||||
// 1. xattr
|
||||
try {
|
||||
return (Movie) new MetaAttributes(file).getObject();
|
||||
} catch (Exception e) {
|
||||
// ignore and move on
|
||||
}
|
||||
|
||||
// 2. perfect filename match
|
||||
try {
|
||||
List<String> names = new ArrayList<String>();
|
||||
for (File it : FileUtilities.listPathTail(file, 4, true)) {
|
||||
names.add(it.getName());
|
||||
}
|
||||
return MediaDetection.matchMovieName(names, true, 0).get(0);
|
||||
} catch (Exception e) {
|
||||
// ignore and move on
|
||||
}
|
||||
|
||||
// 3. run full-fledged movie detection
|
||||
try {
|
||||
return MediaDetection.detectMovie(file, WebServices.OpenSubtitles, WebServices.TheMovieDB, Locale.ENGLISH, strict).get(0);
|
||||
} catch (Exception e) {
|
||||
// ignore and fail
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int execute(Object... args) throws Exception {
|
||||
List<String> cmd = new ArrayList<String>();
|
||||
|
||||
if (Platform.isWindows()) {
|
||||
// normalize file separator for windows and run with cmd so any executable in PATH will just work
|
||||
cmd.add("cmd");
|
||||
cmd.add("/c");
|
||||
} else if (args.length == 1) {
|
||||
// make unix shell parse arguments
|
||||
cmd.add("sh");
|
||||
cmd.add("-c");
|
||||
}
|
||||
|
||||
for (Object it : args) {
|
||||
cmd.add(it.toString());
|
||||
}
|
||||
|
||||
ProcessBuilder process = new ProcessBuilder(cmd).inheritIO();
|
||||
return process.start().waitFor();
|
||||
}
|
||||
|
||||
public String XML(Closure<?> buildClosure) {
|
||||
StringWriter out = new StringWriter();
|
||||
MarkupBuilder builder = new MarkupBuilder(out);
|
||||
buildClosure.rehydrate(buildClosure.getDelegate(), builder, builder).call(); // call closure in MarkupBuilder context
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
public void telnet(String host, int port, Closure<?> handler) throws IOException {
|
||||
try (Socket socket = new Socket(host, port)) {
|
||||
handler.call(new PrintStream(socket.getOutputStream(), true, "UTF-8"), new InputStreamReader(socket.getInputStream(), "UTF-8"));
|
||||
}
|
||||
}
|
||||
|
||||
private enum OptionName {
|
||||
action, conflict, query, filter, format, db, order, lang, output, encoding, strict
|
||||
}
|
||||
|
||||
private Map<OptionName, Object> withDefaultOptions(Map<String, ?> map) throws Exception {
|
||||
Map<OptionName, Object> options = new EnumMap<OptionName, Object>(OptionName.class);
|
||||
|
||||
for (Entry<String, ?> it : map.entrySet()) {
|
||||
options.put(OptionName.valueOf(it.getKey()), it.getValue());
|
||||
}
|
||||
|
||||
ArgumentBean defaultValues = Settings.getApplicationArguments();
|
||||
for (OptionName missing : EnumSet.complementOf(EnumSet.copyOf(options.keySet()))) {
|
||||
if (missing == OptionName.strict) {
|
||||
options.put(missing, !defaultValues.nonStrict);
|
||||
} else {
|
||||
Object value = defaultValues.getClass().getField(missing.name()).get(defaultValues);
|
||||
options.put(missing, value);
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,342 @@
|
|||
package net.sourceforge.filebot.cli;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static java.util.Collections.*;
|
||||
import static net.sourceforge.filebot.MediaTypes.*;
|
||||
import groovy.lang.Closure;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sourceforge.filebot.MediaTypes;
|
||||
import net.sourceforge.filebot.MetaAttributeView;
|
||||
import net.sourceforge.filebot.media.MediaDetection;
|
||||
import net.sourceforge.filebot.media.MetaAttributes;
|
||||
import net.sourceforge.filebot.similarity.NameSimilarityMetric;
|
||||
import net.sourceforge.filebot.similarity.Normalization;
|
||||
import net.sourceforge.filebot.similarity.SimilarityMetric;
|
||||
import net.sourceforge.filebot.util.FileUtilities;
|
||||
import net.sourceforge.filebot.web.WebRequest;
|
||||
|
||||
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
|
||||
|
||||
import com.cedarsoftware.util.io.JsonReader;
|
||||
import com.cedarsoftware.util.io.JsonWriter;
|
||||
|
||||
public class ScriptShellMethods {
|
||||
|
||||
public static File resolve(File self, Object name) {
|
||||
return new File(self, name.toString());
|
||||
}
|
||||
|
||||
public static File resolveSibling(File self, Object name) {
|
||||
return new File(self.getParentFile(), name.toString());
|
||||
}
|
||||
|
||||
public static List<File> listFiles(File self, Closure<?> closure) {
|
||||
File[] files = self.listFiles();
|
||||
if (files == null)
|
||||
return emptyList();
|
||||
|
||||
return (List<File>) DefaultGroovyMethods.findAll(asList(files), closure);
|
||||
}
|
||||
|
||||
public static boolean isVideo(File self) {
|
||||
return VIDEO_FILES.accept(self);
|
||||
}
|
||||
|
||||
public static boolean isAudio(File self) {
|
||||
return AUDIO_FILES.accept(self);
|
||||
}
|
||||
|
||||
public static boolean isSubtitle(File self) {
|
||||
return SUBTITLE_FILES.accept(self);
|
||||
}
|
||||
|
||||
public static boolean isVerification(File self) {
|
||||
return VERIFICATION_FILES.accept(self);
|
||||
}
|
||||
|
||||
public static boolean isArchive(File self) {
|
||||
return ARCHIVE_FILES.accept(self);
|
||||
}
|
||||
|
||||
public static boolean isDisk(File self) throws Exception {
|
||||
// check disk folder
|
||||
if (self.isDirectory() && MediaDetection.isDiskFolder(self))
|
||||
return true;
|
||||
|
||||
// check disk image
|
||||
if (self.isFile() && MediaTypes.getDefaultFilter("video/iso").accept(self) && MediaDetection.isVideoDiskFile(self))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static File getDir(File self) {
|
||||
return self.getParentFile();
|
||||
}
|
||||
|
||||
public static boolean hasFile(File self, Closure<?> closure) {
|
||||
File[] files = self.listFiles();
|
||||
if (files == null)
|
||||
return false;
|
||||
|
||||
return DefaultGroovyMethods.find(asList(files), closure) != null;
|
||||
}
|
||||
|
||||
public static List<File> getFiles(File self) {
|
||||
return getFiles(self, null);
|
||||
}
|
||||
|
||||
public static List<File> getFiles(File self, Closure<?> closure) {
|
||||
return getFiles(singletonList(self), closure);
|
||||
}
|
||||
|
||||
public static List<File> getFiles(Collection<?> self) {
|
||||
return getFiles(self, null);
|
||||
}
|
||||
|
||||
public static List<File> getFiles(Collection<?> self, Closure<?> closure) {
|
||||
final List<File> roots = FileUtilities.asFileList(self.toArray());
|
||||
|
||||
List<File> files = FileUtilities.listFiles(roots);
|
||||
if (closure != null) {
|
||||
files = (List<File>) DefaultGroovyMethods.findAll(files, closure);
|
||||
}
|
||||
|
||||
return FileUtilities.sortByUniquePath(files);
|
||||
}
|
||||
|
||||
public static List<File> getFolders(File self) {
|
||||
return getFolders(self, null);
|
||||
}
|
||||
|
||||
public static List<File> getFolders(File self, Closure<?> closure) {
|
||||
return getFolders(singletonList(self), closure);
|
||||
}
|
||||
|
||||
public static List<File> getFolders(Collection<?> self) {
|
||||
return getFolders(self, null);
|
||||
}
|
||||
|
||||
public static List<File> getFolders(Collection<?> self, Closure<?> closure) {
|
||||
final List<File> roots = FileUtilities.asFileList(self.toArray());
|
||||
|
||||
List<File> folders = FileUtilities.listFolders(roots);
|
||||
if (closure != null) {
|
||||
folders = (List<File>) DefaultGroovyMethods.findAll(folders, closure);
|
||||
}
|
||||
|
||||
return FileUtilities.sortByUniquePath(folders);
|
||||
}
|
||||
|
||||
public static List<File> getMediaFolders(File self) throws IOException {
|
||||
final List<File> mediaFolders = new ArrayList<File>();
|
||||
|
||||
Files.walkFileTree(self.toPath(), new SimpleFileVisitor<Path>() {
|
||||
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
||||
File folder = dir.toFile();
|
||||
|
||||
if (FileUtilities.filter(asList(folder.listFiles()), VIDEO_FILES).size() > 0 || MediaDetection.isDiskFolder(folder)) {
|
||||
mediaFolders.add(folder);
|
||||
return FileVisitResult.SKIP_SUBTREE;
|
||||
}
|
||||
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
|
||||
return FileUtilities.sortByUniquePath(mediaFolders);
|
||||
}
|
||||
|
||||
public static String getNameWithoutExtension(File self) {
|
||||
return FileUtilities.getNameWithoutExtension(self.getName());
|
||||
}
|
||||
|
||||
public static String getNameWithoutExtension(String self) {
|
||||
return FileUtilities.getNameWithoutExtension(self);
|
||||
}
|
||||
|
||||
public static String getExtension(File self) {
|
||||
return FileUtilities.getExtension(self);
|
||||
}
|
||||
|
||||
public static String getExtension(String self) {
|
||||
return FileUtilities.getExtension(self);
|
||||
}
|
||||
|
||||
public static boolean hasExtension(File self, String... extensions) {
|
||||
return FileUtilities.hasExtension(self, extensions);
|
||||
}
|
||||
|
||||
public static boolean hasExtension(String self, String... extensions) {
|
||||
return FileUtilities.hasExtension(self, extensions);
|
||||
}
|
||||
|
||||
public static boolean isDerived(File self, File other) {
|
||||
return FileUtilities.isDerived(self, other);
|
||||
}
|
||||
|
||||
public static File validateFileName(File self) {
|
||||
return FileUtilities.validateFileName(self);
|
||||
}
|
||||
|
||||
public static String validateFileName(String self) {
|
||||
return FileUtilities.validateFileName(self);
|
||||
}
|
||||
|
||||
public static File validateFilePath(File self) {
|
||||
return FileUtilities.validateFilePath(self);
|
||||
}
|
||||
|
||||
public static File moveTo(File self, File destination) throws IOException {
|
||||
return FileUtilities.moveRename(self, destination);
|
||||
}
|
||||
|
||||
public static File copyAs(File self, File destination) throws IOException {
|
||||
return FileUtilities.copyAs(self, destination);
|
||||
}
|
||||
|
||||
public static File copyTo(File self, File destination) throws IOException {
|
||||
return FileUtilities.copyAs(self, new File(destination, self.getName()));
|
||||
}
|
||||
|
||||
public static File relativize(File self, File other) throws IOException {
|
||||
return self.getCanonicalFile().toPath().relativize(other.getCanonicalFile().toPath()).toFile();
|
||||
}
|
||||
|
||||
public static Map<File, List<File>> mapByFolder(Collection<?> files) {
|
||||
return FileUtilities.mapByFolder(FileUtilities.asFileList(files));
|
||||
}
|
||||
|
||||
public static Map<String, List<File>> mapByExtension(Collection<?> files) {
|
||||
return FileUtilities.mapByExtension(FileUtilities.asFileList(files));
|
||||
}
|
||||
|
||||
public static String normalizePunctuation(String self) {
|
||||
return Normalization.normalizePunctuation(self);
|
||||
}
|
||||
|
||||
// Web and File IO helpers
|
||||
|
||||
public static ByteBuffer fetch(URL self) throws IOException {
|
||||
return WebRequest.fetch(self);
|
||||
}
|
||||
|
||||
public static String getText(ByteBuffer self) {
|
||||
return Charset.forName("UTF-8").decode(self.duplicate()).toString();
|
||||
}
|
||||
|
||||
public static ByteBuffer post(URL self, Map<String, ?> parameters, Map<String, String> requestParameters) throws IOException {
|
||||
return WebRequest.post(self, parameters, requestParameters);
|
||||
}
|
||||
|
||||
public static ByteBuffer post(URL self, String text, Map<String, String> requestParameters) throws IOException {
|
||||
return WebRequest.post(self, text.getBytes("UTF-8"), "text/plain", requestParameters);
|
||||
}
|
||||
|
||||
public static File saveAs(ByteBuffer self, File file) throws IOException {
|
||||
// resolve relative paths
|
||||
file = file.getAbsoluteFile();
|
||||
|
||||
// make sure parent folders exist
|
||||
file.getParentFile().mkdirs();
|
||||
|
||||
return FileUtilities.writeFile(self, file);
|
||||
}
|
||||
|
||||
public static File saveAs(String self, File file) throws IOException {
|
||||
return saveAs(Charset.forName("UTF-8").encode(self), file);
|
||||
}
|
||||
|
||||
public static File saveAs(URL self, File file) throws IOException {
|
||||
// resolve relative paths
|
||||
file = file.getAbsoluteFile();
|
||||
|
||||
// make sure parent folders exist
|
||||
file.getParentFile().mkdirs();
|
||||
|
||||
org.apache.commons.io.FileUtils.copyURLToFile(self, file);
|
||||
return file;
|
||||
}
|
||||
|
||||
public static String objectToJson(Object self) throws IOException {
|
||||
return JsonWriter.objectToJson(self);
|
||||
}
|
||||
|
||||
public static Object jsonToObject(String self) throws IOException {
|
||||
return JsonReader.jsonToJava(self);
|
||||
}
|
||||
|
||||
public static FolderWatchService watch(File self, final Closure<?> callback) throws IOException {
|
||||
FolderWatchService watchService = new FolderWatchService(true) {
|
||||
|
||||
@Override
|
||||
public void processCommitSet(File[] files, File dir) {
|
||||
callback.call(asList(files));
|
||||
}
|
||||
};
|
||||
|
||||
// collect updates for 500 ms and then batch process
|
||||
watchService.setCommitDelay(500);
|
||||
watchService.setCommitPerFolder(true);
|
||||
|
||||
// start watching the given folder
|
||||
watchService.watchFolder(self);
|
||||
|
||||
return watchService;
|
||||
}
|
||||
|
||||
public static float getSimilarity(String self, String other) {
|
||||
return new NameSimilarityMetric().getSimilarity(self, other);
|
||||
}
|
||||
|
||||
public static Collection<?> getSimilarity(Collection<?> self, final Object prime, final Closure<String> toStringFunction) {
|
||||
final SimilarityMetric metric = new NameSimilarityMetric();
|
||||
List<Object> values = new ArrayList<Object>(self);
|
||||
Collections.sort(values, new Comparator<Object>() {
|
||||
|
||||
@Override
|
||||
public int compare(Object o1, Object o2) {
|
||||
String s1 = toStringFunction != null ? toStringFunction.call(o1) : o1.toString();
|
||||
String s2 = toStringFunction != null ? toStringFunction.call(o2) : o2.toString();
|
||||
|
||||
return Float.compare(metric.getSimilarity(s2, prime), metric.getSimilarity(s1, prime));
|
||||
}
|
||||
});
|
||||
return values;
|
||||
}
|
||||
|
||||
public static MetaAttributeView getXattr(File self) {
|
||||
try {
|
||||
return new MetaAttributeView(self);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Object getMetadata(File self) {
|
||||
try {
|
||||
return new MetaAttributes(self);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,14 +158,11 @@ public final class FileUtilities {
|
|||
return text.toString();
|
||||
}
|
||||
|
||||
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 File writeFile(ByteBuffer data, File destination) throws IOException {
|
||||
try (FileOutputStream stream = new FileOutputStream(destination); FileChannel channel = stream.getChannel()) {
|
||||
channel.write(data);
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
|
||||
public static List<String[]> readCSV(InputStream source, String charsetName, String separatorPattern) {
|
||||
|
@ -561,6 +558,8 @@ public final class FileUtilities {
|
|||
files.add((File) it);
|
||||
} else if (it instanceof Path) {
|
||||
files.add(((Path) it).toFile());
|
||||
} else if (it instanceof Collection<?>) {
|
||||
files.addAll(asFileList(it)); // flatten object structure
|
||||
}
|
||||
}
|
||||
return files;
|
||||
|
|
Loading…
Reference in New Issue