filebot/source/net/sourceforge/filebot/cli/ScriptShell.lib.groovy

259 lines
9.6 KiB
Groovy

// File selector methods
import static groovy.io.FileType.*
File.metaClass.resolve = { Object name -> new File(delegate, name.toString()) }
File.metaClass.getAt = { String name -> new File(delegate, name) }
File.metaClass.listFiles = { c -> delegate.isDirectory() ? delegate.listFiles().findAll(c) : []}
File.metaClass.isVideo = { _types.getFilter("video").accept(delegate) }
File.metaClass.isAudio = { _types.getFilter("audio").accept(delegate) }
File.metaClass.isSubtitle = { _types.getFilter("subtitle").accept(delegate) }
File.metaClass.isVerification = { _types.getFilter("verification").accept(delegate) }
File.metaClass.isArchive = { _types.getFilter("archive").accept(delegate) }
File.metaClass.getDir = { getParentFile() }
File.metaClass.hasFile = { c -> isDirectory() && listFiles().find(c) }
String.metaClass.getFiles = { c -> new File(delegate).getFiles(c) }
File.metaClass.getFiles = { c -> def files = []; traverse(type:FILES) { files += it }; return c ? files.findAll(c).sort() : files.sort() }
List.metaClass.getFiles = { c -> findResults{ it.getFiles(c) }.flatten().unique() }
String.metaClass.getFolders = { c -> new File(delegate).getFolders(c) }
File.metaClass.getFolders = { c -> def folders = []; traverse(type:DIRECTORIES, visitRoot:true) { folders += it }; return c ? folders.findAll(c).sort() : folders.sort() }
List.metaClass.getFolders = { c -> findResults{ it.getFolders(c) }.flatten().unique() }
String.metaClass.eachMediaFolder = { c -> new File(delegate).eachMediaFolder(c) }
File.metaClass.eachMediaFolder = { c -> getFolders{ it.hasFile{ it.isVideo() } }.each(c) }
List.metaClass.eachMediaFolder = { c -> getFolders{ it.hasFile{ it.isVideo() } }.each(c) }
// File utility methods
import static net.sourceforge.tuned.FileUtilities.*
File.metaClass.getNameWithoutExtension = { getNameWithoutExtension(delegate.getName()) }
File.metaClass.getPathWithoutExtension = { new File(delegate.getParentFile(), getNameWithoutExtension(delegate.getName())).getPath() }
File.metaClass.getExtension = { getExtension(delegate) }
File.metaClass.hasExtension = { String... ext -> hasExtension(delegate, ext) }
File.metaClass.isDerived = { f -> isDerived(delegate, f) }
File.metaClass.validateFileName = { validateFileName(delegate) }
File.metaClass.validateFilePath = { validateFilePath(delegate) }
File.metaClass.moveTo = { f -> moveRename(delegate, f instanceof File ? f : new File(f.toString())) }
List.metaClass.mapByFolder = { mapByFolder(delegate) }
List.metaClass.mapByExtension = { mapByExtension(delegate) }
String.metaClass.getExtension = { getExtension(delegate) }
String.metaClass.hasExtension = { String... ext -> hasExtension(delegate, ext) }
String.metaClass.validateFileName = { validateFileName(delegate) }
String.metaClass.validateFilePath = { validateFilePath(delegate) }
// Parallel helper
import java.util.concurrent.*
def parallel(List closures, int threads = Runtime.getRuntime().availableProcessors()) {
def tasks = closures.collect { it as Callable }
return Executors.newFixedThreadPool(threads).invokeAll(tasks).collect{ _guarded { it.get() } }
}
// Web and File IO helpers
import java.nio.ByteBuffer
import java.nio.charset.Charset
import static net.sourceforge.filebot.web.WebRequest.*
URL.metaClass.get = { readAll(getReader(delegate.openConnection())) }
URL.metaClass.fetch = { fetch(delegate) }
URL.metaClass.getHtml = { new XmlParser(false, false).parseText(getXmlString(getHtmlDocument(delegate))) }
ByteBuffer.metaClass.getHtml = { csn = "utf-8" -> new XmlParser(false, false).parseText(getXmlString(getHtmlDocument(new StringReader(Charset.forName(csn).decode(delegate.duplicate()).toString())))) }
URL.metaClass.post = { Map parameters -> post(delegate.openConnection(), parameters) }
URL.metaClass.post = { byte[] data, contentType = 'application/octet-stream' -> post(delegate.openConnection(), data, contentType) }
URL.metaClass.post = { String text, csn = 'utf-8' -> delegate.post(text.getBytes(csn), 'text/plain') }
ByteBuffer.metaClass.saveAs = { f -> f = f instanceof File ? f : new File(f.toString()); writeFile(delegate.duplicate(), f); f.absolutePath };
URL.metaClass.saveAs = { f -> fetch(delegate).saveAs(f) }
String.metaClass.saveAs = { f, csn = "utf-8" -> Charset.forName(csn).encode(delegate).saveAs(f) }
def telnet(host, int port, csn = 'utf-8', Closure handler) {
def socket = new Socket(host, port)
try {
handler.call(new PrintStream(socket.outputStream, true, csn), socket.inputStream.newReader(csn))
} finally {
socket.close()
}
}
// Template Engine helpers
import groovy.text.XmlTemplateEngine
import groovy.text.GStringTemplateEngine
import net.sourceforge.filebot.format.PropertyBindings
import net.sourceforge.filebot.format.UndefinedObject
Object.metaClass.applyXmlTemplate = { template -> new XmlTemplateEngine("\t", false).createTemplate(template).make(new PropertyBindings(delegate, new UndefinedObject(""))).toString() }
Object.metaClass.applyTextTemplate = { template -> new GStringTemplateEngine().createTemplate(template).make(new PropertyBindings(delegate, new UndefinedObject(""))).toString() }
// Shell helper
import static com.sun.jna.Platform.*
def execute(String... args) {
def cmd = args.toList()
if (isWindows()) {
// normalize file separator for windows and run with cmd so any executable in PATH will just work
cmd = ['cmd', '/c'] + cmd*.replace('/','\\')
}
if (!_args.trustScript) {
_log.severe("Execute failed: Script is not trusted: " + cmd)
return -1
}
// run command and print output
def process = cmd.execute()
process.waitForProcessOutput(System.out, System.err)
return process.exitValue()
}
// WatchService helper
import net.sourceforge.filebot.cli.FolderWatchService
def createWatchService(Closure callback, List folders, boolean watchTree) {
// sanity check
folders.find{ if (!it.isDirectory()) throw new Exception("Must be a folder: " + it) }
// create watch service and setup callback
def watchService = new FolderWatchService(true) {
@Override
def void processCommitSet(File[] fileset, File dir) {
callback(fileset.toList())
}
}
// collect updates for 5 minutes and then batch process
watchService.setCommitDelay(5 * 60 * 1000)
watchService.setCommitPerFolder(watchTree)
// start watching given files
folders.each { dir -> _guarded { watchService.watchFolder(dir) } }
return watchService
}
File.metaClass.watch = { c -> createWatchService(c, [delegate], true) }
List.metaClass.watch = { c -> createWatchService(c, delegate, true) }
// Season / Episode helpers
import net.sourceforge.filebot.media.*
import net.sourceforge.filebot.similarity.*
def parseEpisodeNumber(path, strict = true) {
def input = path instanceof File ? path.name : path.toString()
def sxe = new SeasonEpisodeMatcher(new SeasonEpisodeMatcher.SeasonEpisodeFilter(30, 50, 1000), strict).match(input)
return sxe == null || sxe.isEmpty() ? null : sxe[0]
}
def parseDate(path) {
return new DateMetric().parse(input)
}
def detectSeriesName(files, locale = Locale.ENGLISH) {
def names = MediaDetection.detectSeriesNames(files.findAll { it.isVideo() || it.isSubtitle() }, locale)
return names == null || names.isEmpty() ? null : names.toList()[0]
}
def detectMovie(movieFile, locale = Locale.ENGLISH, strict = true) {
def movies = MediaDetection.detectMovie(movieFile, OpenSubtitles, TheMovieDB, locale, strict)
return movies == null || movies.isEmpty() ? null : movies.toList()[0]
}
def similarity(o1, o2) {
return new NameSimilarityMetric().getSimilarity(o1, o2)
}
List.metaClass.sortBySimilarity = { prime, Closure toStringFunction = { obj -> obj.toString() } ->
return delegate.sort{ a, b -> similarity(toStringFunction(b), prime).compareTo(similarity(toStringFunction(a), prime)) }
}
// CLI bindings
def rename(args) { args = _defaults(args)
synchronized (_cli) {
_guarded { _cli.rename(_files(args), args.query, args.format, args.db, args.order, args.lang, args.strict) }
}
}
def getSubtitles(args) { args = _defaults(args)
synchronized (_cli) {
_guarded { _cli.getSubtitles(_files(args), args.query, args.lang, args.output, args.encoding, args.strict) }
}
}
def getMissingSubtitles(args) { args = _defaults(args)
synchronized (_cli) {
_guarded { _cli.getMissingSubtitles(_files(args), args.query, args.lang, args.output, args.encoding, args.strict) }
}
}
def check(args) {
synchronized (_cli) {
_guarded { _cli.check(_files(args)) }
}
}
def compute(args) { args = _defaults(args)
synchronized (_cli) {
_guarded { _cli.compute(_files(args), args.output, args.encoding) }
}
}
def fetchEpisodeList(args) { args = _defaults(args)
synchronized (_cli) {
_guarded { _cli.fetchEpisodeList(args.query, args.format, args.db, args.order, args.lang) }
}
}
def getMediaInfo(args) { args = _defaults(args)
synchronized (_cli) {
_guarded { _cli.getMediaInfo(args.file, args.format) }
}
}
/**
* Resolve folders/files to lists of one or more files
*/
def _files(args) {
def files = [];
if (args.folder)
args.folder.traverse(type:FILES, maxDepth:0) { files += it }
if (args.file)
files += args.file
return files
}
/**
* Fill in default values from cmdline arguments
*/
def _defaults(args) {
args.query = args.query ?: _args.query
args.format = args.format ?: _args.format
args.db = args.db ?: _args.db
args.order = args.order ?: _args.order
args.lang = args.lang ?: _args.lang
args.output = args.output ?: _args.output
args.encoding = args.encoding ?: _args.encoding
args.strict = args.strict ?: !_args.nonStrict
return args
}
/**
* Catch and log exceptions thrown by the closure
*/
this.metaClass._guarded = { c -> try { return c.call() } catch (e) { _log.severe("${e.class.simpleName}: ${e.message}"); return null }}