+ move shared scripts to github

This commit is contained in:
Reinhard Pointner 2013-11-29 17:18:31 +00:00
parent 4843bb55d2
commit ad3d34eeba
29 changed files with 0 additions and 1519 deletions

View File

@ -656,15 +656,6 @@
</target>
<target name="upload-scripts" depends="login">
<scp todir="${sf.user}:${sf.password}@${deploy.website}" trust="yes" verbose="true" sftp="true">
<fileset dir="${dir.website}">
<include name="scripts/**" />
</fileset>
</scp>
</target>
<target name="deploy-test-package" depends="svn-update, fatjar, login">
<!-- deploy fatjar -->
<input message="Mark:" addproperty="mark" />

View File

@ -1,3 +0,0 @@
options +indexes
redirect 301 /scripts/utorrent-postprocess.groovy /scripts/amc.groovy

View File

@ -1,385 +0,0 @@
// filebot -script "fn:amc" --output "X:/media" --action copy --conflict override --def subtitles=en music=y artwork=y "ut_dir=%D" "ut_file=%F" "ut_kind=%K" "ut_title=%N" "ut_label=%L" "ut_state=%S"
def input = []
def failOnError = _args.conflict == 'fail'
// print input parameters
_args.bindings?.each{ _log.fine("Parameter: $it.key = $it.value") }
args.each{ _log.fine("Argument: $it") }
args.findAll{ !it.exists() }.each{ throw new Exception("File not found: $it") }
// check user-defined pre-condition
if (tryQuietly{ !(ut_state ==~ ut_state_allow) }) {
throw new Exception("Invalid state: ut_state = $ut_state (expected $ut_state_allow)")
}
// check ut mode vs standalone mode
if ((args.size() > 0 && (tryQuietly{ ut_dir }?.size() > 0 || tryQuietly{ ut_file }?.size() > 0)) || (args.size() == 0 && (tryQuietly{ ut_dir } == null && tryQuietly{ ut_file } == null))) {
throw new Exception("Conflicting arguments: pass in either file arguments or ut_dir/ut_file parameters but not both")
}
// enable/disable features as specified via --def parameters
def music = tryQuietly{ music.toBoolean() }
def subtitles = tryQuietly{ subtitles.toBoolean() ? ['en'] : subtitles.split(/[ ,|]+/).findAll{ it.length() >= 2 } }
def artwork = tryQuietly{ artwork.toBoolean() }
def backdrops = tryQuietly{ backdrops.toBoolean() }
def clean = tryQuietly{ clean.toBoolean() }
def exec = tryQuietly{ exec.toString() }
// array of xbmc/plex hosts
def xbmc = tryQuietly{ xbmc.split(/[ ,|]+/) }
def plex = tryQuietly{ plex.split(/[ ,|]+/) }
// extra options, myepisodes updates and email notifications
def deleteAfterExtract = tryQuietly{ deleteAfterExtract.toBoolean() }
def excludeList = tryQuietly{ new File(_args.output, excludeList) }
def myepisodes = tryQuietly{ myepisodes.split(':', 2) }
def gmail = tryQuietly{ gmail.split(':', 2) }
def pushover = tryQuietly{ pushover.toString() }
// user-defined filters
def minFileSize = tryQuietly{ minFileSize.toLong() }; if (minFileSize == null) { minFileSize = 0 };
// series/anime/movie format expressions
def format = [
tvs: tryQuietly{ seriesFormat } ?: '''TV Shows/{n}/{episode.special ? "Special" : "Season "+s.pad(2)}/{n} - {episode.special ? "S00E"+special.pad(2) : s00e00} - {t.replaceAll(/[`´ʻ]/, "'").replaceAll(/[!?.]+$/).replacePart(', Part $1')}{".$lang"}''',
anime: tryQuietly{ animeFormat } ?: '''Anime/{n}/{n} - {sxe} - {t.replaceAll(/[!?.]+$/).replaceAll(/[`´ʻ]/, "'").replacePart(', Part $1')}''',
mov: tryQuietly{ movieFormat } ?: '''Movies/{n} ({y})/{n} ({y}){" CD$pi"}{".$lang"}''',
music: tryQuietly{ musicFormat } ?: '''Music/{n}/{album+'/'}{pi.pad(2)+'. '}{artist} - {t}'''
]
// force movie/series/anime logic
def forceMovie(f) {
tryQuietly{ ut_label } =~ /^(?i:Movie|Couch.Potato)/ || f.dir.path =~ /\b(?i:Movies)\b/ || f.path =~ /(?<=tt)\\d{7}/ || tryQuietly{ f.metadata?.object?.class.name =~ /Movie/ }
}
def forceSeries(f) {
tryQuietly{ ut_label } =~ /^(?i:TV|Kids.Shows)/ || f.dir.path =~ /\b(?i:TV.Shows)\b/ || parseEpisodeNumber(f.path) || parseDate(f.path) || f.path =~ /(?i:Season)\D?[0-9]{1,2}\D/ || tryQuietly{ f.metadata?.object?.class.name =~ /Episode/ }
}
def forceAnime(f) {
tryQuietly{ ut_label } =~ /^(?i:Anime)/ || f.dir.path =~ /\b(?i:Anime)\b/ || (f.isVideo() && (f.name =~ "[\\(\\[]\\p{XDigit}{8}[\\]\\)]" || getMediaInfo(file:f, format:'''{media.AudioLanguageList} {media.TextCodecList}''').tokenize().containsAll(['Japanese', 'ASS'])))
}
def forceIgnore(f) {
tryQuietly{ ut_label } =~ /^(?i:ebook|other|ignore)/ || f.path =~ tryQuietly{ ignore }
}
// specify how to resolve input folders, e.g. grab files from all folders except disk folders
def resolveInput(f) {
if (f.isDirectory() && !f.isDisk())
return f.listFiles().toList().findResults{ resolveInput(it) }
else
return f
}
// collect input fileset as specified by the given --def parameters
if (args.empty) {
// assume we're called with utorrent parameters (account for older and newer versions of uTorrents)
if (ut_kind == 'single' || (ut_kind != 'multi' && ut_dir && ut_file)) {
input += new File(ut_dir, ut_file) // single-file torrent
} else {
input += resolveInput(ut_dir as File) // multi-file torrent
}
} else {
// assume we're called normally with arguments
input += args.findResults{ resolveInput(it) }
}
// flatten nested file structure
input = input.flatten()
// extract archives (zip, rar, etc) that contain at least one video file
def extractedArchives = []
def tempFiles = []
input = input.flatten{ f ->
if (f.isArchive() || f.hasExtension('001')) {
def extractDir = new File(f.dir, f.nameWithoutExtension)
def extractFiles = extract(file: f, output: new File(extractDir, f.dir.name), conflict: 'skip', filter: { it.isArchive() || it.isVideo() || it.isSubtitle() || (music && it.isAudio()) }, forceExtractAll: true) ?: []
if (extractFiles.size() > 0) {
extractedArchives += f
tempFiles += extractDir
tempFiles += extractFiles
}
return extractFiles
}
return f
}
// sanitize input
input = input.findAll{ it?.exists() }.collect{ it.canonicalFile }.unique()
// process only media files
input = input.findAll{ f -> (f.isVideo() && !tryQuietly{ f.hasExtension('iso') && !f.isDisk() }) || f.isSubtitle() || (f.isDirectory() && f.isDisk()) || (music && f.isAudio()) }
// ignore clutter files
input = input.findAll{ f -> !(f.path =~ /\b(?i:sample|trailer|extras|music.video|scrapbook|behind.the.scenes|extended.scenes|deleted.scenes|s\d{2}c\d{2}|mini.series)\b/ || (f.isFile() && f.length() < minFileSize)) }
// check and update exclude list (e.g. to make sure files are only processed once)
if (excludeList) {
// check excludes from previous runs
def excludePathSet = excludeList.exists() ? excludeList.text.split('\n') as HashSet : []
input = input.findAll{ f -> !excludePathSet.contains(f.path) }
// update excludes with input of this run
excludePathSet += input
excludePathSet.join('\n').saveAs(excludeList)
}
// print input fileset
input.each{ f -> _log.finest("Input: $f") }
// artwork/nfo utility
if (artwork || xbmc || plex) { include('fn:lib/htpc') }
// group episodes/movies and rename according to XBMC standards
def groups = input.groupBy{ f ->
// skip auto-detection if possible
if (forceIgnore(f))
return []
if (f.isAudio() && !f.isVideo()) // PROCESS MUSIC FOLDER BY FOLDER
return [music: f.dir.name]
if (forceMovie(f))
return [mov: detectMovie(f, false)]
if (forceSeries(f))
return [tvs: detectSeriesName(f) ?: detectSeriesName(f.dir.listFiles{ it.isVideo() })]
if (forceAnime(f))
return [anime: detectSeriesName(f) ?: detectSeriesName(f.dir.listFiles{ it.isVideo() })]
def tvs = detectSeriesName(f)
def mov = detectMovie(f, false)
_log.fine("$f.name [series: $tvs, movie: $mov]")
// DECIDE EPISODE VS MOVIE (IF NOT CLEAR)
if (tvs && mov) {
def norm = { s -> s.ascii().normalizePunctuation().lower().space(' ') }
def dn = norm(guessMovieFolder(f)?.name ?: '')
def fn = norm(f.nameWithoutExtension)
def sn = norm(tvs)
def mn = norm(mov.name)
/**
println '--- EPISODE FILTER (POS) ---'
println parseEpisodeNumber(fn, true) || parseDate(fn)
println ([dn, fn].find{ it =~ sn && matchMovie(it, true) == null } && (parseEpisodeNumber(stripReleaseInfo(fn.after(sn), false), false) || fn.after(sn) =~ /\D\d{1,2}\D{1,3}\d{1,2}\D/) && matchMovie(fn, true) == null)
println (fn.after(sn) ==~ /.{0,3} - .+/ && matchMovie(fn, true) == null)
println f.dir.listFiles{ it.isVideo() && (dn =~ sn || norm(it.name) =~ sn) && it.name =~ /\d{1,3}/}.findResults{ it.name.matchAll(/\d{1,3}/) as Set }.unique().size() >= 10
println '--- EPISODE FILTER (NEG) ---'
println (mov.year >= 1950 && f.listPath().reverse().take(3).find{ it.name =~ mov.year })
println (mn =~ sn && [dn, fn].find{ it =~ /(19|20)\d{2}/ })
println '--- MOVIE FILTER (POS) ---'
println (similarity(mn, fn) >= 0.8 || [dn, fn].find{ it.findAll( ~/\d{4}/ ).findAll{ y -> [mov.year-1, mov.year, mov.year+1].contains(y.toInteger()) }.size() > 0 } != null)
println ([dn, fn].find{ it =~ mn && !(it.after(mn) =~ /\b\d{1,3}\b/) && (similarity(it, mn) > 0.2 + similarity(it, sn)) } != null)
println (detectMovie(f, true) && [dn, fn].find{ it =~ /(19|20)\d{2}/ } != null)
**/
// S00E00 | 2012.07.21 | One Piece 217 | Firefly - Serenity | [Taken 1, Taken 2, Taken 3, Taken 4, ..., Taken 10]
if ((parseEpisodeNumber(fn, true) || parseDate(fn) || ([dn, fn].find{ it =~ sn && matchMovie(it, true) == null } && (parseEpisodeNumber(stripReleaseInfo(fn.after(sn), false), false) || fn.after(sn) =~ /\D\d{1,2}\D{1,3}\d{1,2}\D/) && matchMovie(fn, true) == null) || (fn.after(sn) ==~ /.{0,3} - .+/ && matchMovie(fn, true) == null) || f.dir.listFiles{ it.isVideo() && (dn =~ sn || norm(it.name) =~ sn) && it.name =~ /\d{1,3}/}.findResults{ it.name.matchAll(/\d{1,3}/) as Set }.unique().size() >= 10 || mov.year < 1900) && !( (mov.year >= 1950 && f.listPath().reverse().take(3).find{ it.name =~ mov.year }) || (mn =~ sn && [dn, fn].find{ it =~ /(19|20)\d{2}/ }) ) ) {
_log.fine("Exclude Movie: $mov")
mov = null
} else if ((similarity(mn, fn) >= 0.8 || [dn, fn].find{ it.findAll( ~/\d{4}/ ).findAll{ y -> [mov.year-1, mov.year, mov.year+1].contains(y.toInteger()) }.size() > 0 } != null) || ([dn, fn].find{ it =~ mn && !(it.after(mn) =~ /\b\d{1,3}\b/) && (similarity(it, mn) > 0.2 + similarity(it, sn)) } != null) || (detectMovie(f, true) && [dn, fn].find{ it =~ /(19|20)\d{2}/ } != null)) {
_log.fine("Exclude Series: $tvs")
tvs = null
}
}
// CHECK CONFLICT
if (((mov && tvs) || (!mov && !tvs))) {
if (failOnError) {
throw new Exception("Media detection failed")
} else {
_log.fine("Unable to differentiate: [$f.name] => [$tvs] VS [$mov]")
return [tvs: null, mov: null, anime: null]
}
}
return [tvs: tvs, mov: mov, anime: null]
}
// log movie/series/anime detection results
groups.each{ group, files -> _log.finest("Group: $group => ${files*.name}") }
// process each batch
groups.each{ group, files ->
// fetch subtitles (but not for anime)
if (subtitles && !group.anime && files.findAll{ it.isVideo() }.size() > 0) {
subtitles.each{ languageCode ->
def subtitleFiles = getMissingSubtitles(file:files, output:'srt', encoding:'UTF-8', lang:languageCode, strict:true) ?: []
files += subtitleFiles
tempFiles += subtitleFiles // if downloaded for temporarily extraced files delete later
}
}
// EPISODE MODE
if ((group.tvs || group.anime) && !group.mov) {
// choose series / anime config
def config = group.tvs ? [name:group.tvs, format:format.tvs, db:'TheTVDB', seasonFolder:true ]
: [name:group.anime, format:format.anime, db:'AniDB', seasonFolder:false]
def dest = rename(file: files, format: config.format, db: config.db)
if (dest && artwork) {
dest.mapByFolder().each{ dir, fs ->
_log.finest "Fetching artwork for $dir from TheTVDB"
def sxe = fs.findResult{ eps -> parseEpisodeNumber(eps) }
def options = TheTVDB.search(detectSeriesName(fs), _args.locale)
if (options.isEmpty()) {
_log.warning "TV Series not found: $config.name"
return
}
options = options.sortBySimilarity(config.name, { s -> s.name })
fetchSeriesArtworkAndNfo(config.seasonFolder ? dir.dir : dir, dir, options[0], sxe && sxe.season > 0 ? sxe.season : 1)
}
}
if (dest == null && failOnError) {
throw new Exception("Failed to rename series: $config.name")
}
}
// MOVIE MODE
else if (group.mov && !group.tvs && !group.anime) {
def dest = rename(file:files, format:format.mov, db:'TheMovieDB')
if (dest && artwork) {
dest.mapByFolder().each{ dir, fs ->
_log.finest "Fetching artwork for $dir from TheMovieDB"
def movieFile = fs.findAll{ it.isVideo() }.sort{ it.length() }.reverse().findResult{ it }
fetchMovieArtworkAndNfo(dir, detectMovie(movieFile), movieFile, backdrops)
}
}
if (dest == null && failOnError) {
throw new Exception("Failed to rename movie: $group.mov")
}
}
// MUSIC MODE
else if (group.music) {
def dest = rename(file:files, format:format.music, db:'AcoustID')
if (dest == null && failOnError) {
throw new Exception("Failed to rename music: $group.music")
}
}
}
// skip notifications if nothing was renamed anyway
if (getRenameLog().isEmpty()) {
return
}
// run program on newly processed files
if (exec) {
getRenameLog().each{ from, to ->
def command = getMediaInfo(format: exec, file: to)
_log.finest("Execute: $command")
execute(command)
}
}
// make XMBC scan for new content and display notification message
if (xbmc) {
xbmc.each{ host ->
_log.info "Notify XBMC: $host"
_guarded{
showNotification(host, 9090, 'FileBot', "Finished processing ${tryQuietly { ut_title } ?: input*.dir.name.unique()} (${getRenameLog().size()} files).", 'http://www.filebot.net/images/icon.png')
scanVideoLibrary(host, 9090)
}
}
}
// make Plex scan for new content
if (plex) {
plex.each{
_log.info "Notify Plex: $it"
refreshPlexLibrary(it)
}
}
// mark episodes as 'acquired'
if (myepisodes) {
_log.info 'Update MyEpisodes'
executeScript('fn:update-mes', [login:myepisodes.join(':'), addshows:true], getRenameLog().values())
}
if (pushover) {
// include webservice utility
include('fn:lib/ws')
_log.info 'Sending Pushover notification'
Pushover(pushover).send("Finished processing ${tryQuietly { ut_title } ?: input*.dir.name.unique()} (${getRenameLog().size()} files).")
}
// send status email
if (gmail) {
// ant/mail utility
include('fn:lib/ant')
// send html mail
def renameLog = getRenameLog()
def emailTitle = tryQuietly { ut_title } ?: input*.dir.name.unique()
sendGmail(
subject: "[FileBot] ${emailTitle}",
message: XML {
html {
body {
p("FileBot finished processing ${emailTitle} (${renameLog.size()} files).");
hr(); table {
th("Parameter"); th("Value")
_args.bindings.findAll{ param -> param.key =~ /^ut_/ }.each{ param ->
tr { [param.key, param.value].each{ td(it)} }
}
}
hr(); table {
th("Original Name"); th("New Name"); th("New Location")
renameLog.each{ from, to ->
tr { [from.name, to.name, to.parent].each{ cell -> td{ nobr{ code(cell) } } } }
}
}
hr(); small("// Generated by ${net.sourceforge.filebot.Settings.applicationIdentifier} on ${new Date().dateString} at ${new Date().timeString}")
}
}
},
messagemimetype: 'text/html',
to: tryQuietly{ mailto } ?: gmail[0] + '@gmail.com', // mail to self by default
user: gmail[0], password: gmail[1]
)
}
if (deleteAfterExtract) {
extractedArchives.each{ a ->
_log.finest("Delete archive $a")
a.delete()
a.dir.listFiles().toList().findAll{ v -> v.name.startsWith(a.nameWithoutExtension) && v.extension ==~ /r\d+/ }.each{ v ->
_log.finest("Delete archive volume $v")
v.delete()
}
}
}
// clean empty folders, clutter files, etc after move
if (clean) {
if (['COPY', 'HARDLINK'].find{ it.equalsIgnoreCase(_args.action) } && tempFiles.size() > 0) {
_log.info 'Clean temporary extracted files'
// delete extracted files
tempFiles.findAll{ it.isFile() }.sort().each{
_log.finest "Delete $it"
it.delete()
}
// delete remaining empty folders
tempFiles.findAll{ it.isDirectory() }.sort().reverse().each{
_log.finest "Delete $it"
if (it.getFiles().isEmpty()) it.deleteDir()
}
}
// deleting remaining files only makes sense after moving files
if ('MOVE'.equalsIgnoreCase(_args.action)) {
def cleanerInput = !args.empty ? args : ut_kind == 'multi' && ut_dir ? [ut_dir as File] : []
cleanerInput = cleanerInput.findAll{ f -> f.exists() }
if (cleanerInput.size() > 0) {
_log.info 'Clean clutter files and empty folders'
executeScript('fn:cleaner', args.empty ? [root:true] : [root:false], cleanerInput)
}
}
}

View File

@ -1,53 +0,0 @@
// filebot -script fn:artwork.tmdb /path/to/movies/
def override = _args.conflict == 'override'
/*
* Fetch movie artwork. The movie is determined using the parent folders name.
*/
// artwork/nfo helpers
include('lib/htpc')
args.eachMediaFolder{ dir ->
// fetch only missing artwork by default
if (!override && dir.hasFile{it.name == 'movie.nfo'} && dir.hasFile{it.name == 'poster.jpg'} && dir.hasFile{it.name == 'fanart.jpg'}) {
println "Skipping $dir"
return
}
def videos = dir.listFiles{ it.isVideo() }
def query = _args.query
def options = []
if (query) {
// manual search
options = TheMovieDB.searchMovie(query, _args.locale)
// sort by relevance
options = options.sortBySimilarity(query, { it.name })
} else {
// auto-detection
options = net.sourceforge.filebot.media.MediaDetection.detectMovie(videos[0], null, TheMovieDB, _args.locale, true)
}
if (options.isEmpty()) {
println "Movie not found: $query"
return
}
// auto-select movie
def movie = options[0]
// maybe require user input
if (options.size() != 1 && !_args.nonStrict && !java.awt.GraphicsEnvironment.headless) {
movie = javax.swing.JOptionPane.showInputDialog(null, 'Please select Movie:', dir.path, 3, null, options.toArray(), movie)
if (movie == null) return null
}
println "$dir => $movie"
try {
fetchMovieArtworkAndNfo(dir, movie, dir.getFiles{ it.isVideo() }.sort{ it.length() }.reverse().findResult{ it }, true, override)
} catch(e) {
println "${e.class.simpleName}: ${e.message}"
}
}

View File

@ -1,52 +0,0 @@
// filebot -script fn:artwork.tvdb /path/to/media/
def override = _args.conflict == 'override'
/*
* Fetch series and season banners for all tv shows. Series name is auto-detected if possible or the folder name is used.
*/
// artwork/nfo helpers
include('lib/htpc')
args.eachMediaFolder{ dir ->
// fetch only missing artwork by default
if (!override && dir.hasFile{it.name == 'banner.jpg'}) {
println "Skipping $dir"
return
}
def videos = dir.listFiles{ it.isVideo() }
def query = _args.query ?: detectSeriesName(videos, _args.locale)
def sxe = videos.findResult{ parseEpisodeNumber(it) }
if (query == null) {
query = dir.dir.hasFile{ it.name =~ /Season/ && it.isDirectory() } ? dir.dir.name : dir.name
}
println "$dir => Search by $query"
def options = TheTVDB.search(query, _args.locale)
if (options.isEmpty()) {
println "TV Series not found: $query"
return
}
// sort by relevance
options = options.sortBySimilarity(query, { it.name })
// auto-select series
def series = options[0]
// maybe require user input
if (options.size() != 1 && !_args.nonStrict && !java.awt.GraphicsEnvironment.headless) {
series = javax.swing.JOptionPane.showInputDialog(null, 'Please select TV Show:', dir.path, 3, null, options.toArray(), series)
if (series == null) return
}
// auto-detect structure
def seriesDir = [dir.dir, dir].sortBySimilarity(series.name, { it.name })[0]
def season = sxe && sxe.season > 0 ? sxe.season : 1
println "$dir => $series"
fetchSeriesArtworkAndNfo(seriesDir, dir, series, season, override)
}

View File

@ -1,9 +0,0 @@
// filebot -script fn:chkall <folder>
/*
* Check all sfv/md5/sha1 files and stop if a conflict is found
*/
args.getFiles().findAll { it.isVerification() }.each {
if (!check(file:it))
throw new Exception("*ERROR*")
}

View File

@ -1,41 +0,0 @@
// filebot -script fn:cleaner [--action test] /path/to/media/
def deleteRootFolder = tryQuietly{ root.toBoolean() }
/*
* Delete orphaned "clutter" files like nfo, jpg, etc and sample files
*/
def isClutter(f) {
// white list
def ignore = tryQuietly{ ignore } ?: /extrathumbs/
if (f.path =~ "(?i)\\b($ignore)\\b") return false
// black list
def exts = tryQuietly{ exts } ?: /jpg|jpeg|png|gif|nfo|xml|htm|html|log|srt|sub|idx|md5|sfv|txt|rtf|url|db|dna|log/
def terms = tryQuietly{ terms } ?: /sample|trailer|extras|deleted.scenes|music.video|scrapbook/
def maxsize = tryQuietly{ maxsize as Long } ?: 100 * 1024 * 1024
// path contains blacklisted terms or extension is blacklisted
return (f.extension ==~ "(?i)($exts)" || f.path =~ "(?i)\\b($terms)\\b") && f.length() < maxsize
}
def clean(f) {
println "Delete $f"
// do a dry run via --action test
if (_args.action == 'test') {
return false
}
return f.isDirectory() ? f.deleteDir() : f.delete()
}
// memoize media folder status for performance
def hasMediaFiles = { dir -> dir.getFiles().find{ (it.isVideo() || it.isAudio()) && !isClutter(it) } }.memoize()
// delete clutter files in orphaned media folders
args.getFiles{ isClutter(it) && !hasMediaFiles(it.dir) }.each { clean(it) }
// delete empty folders but exclude given args
args.getFolders().sort().reverse().each { if (it.listFiles().length == 0) { if (deleteRootFolder || !args.contains(it)) clean(it) } }

View File

@ -1,26 +0,0 @@
// OpenSubtitles
console.print('Enter OpenSubtitles username: ')
def osdbUser = console.readLine()
console.print('Enter OpenSubtitles password: ')
def osdbPwd = console.readLine()
setLogin('osdb.user', osdbUser, osdbPwd)
/* --------------------------------------------------------------------- */
import net.sourceforge.filebot.*
if (osdbUser) {
console.print('Testing OpenSubtitles... ')
WebServices.OpenSubtitles.setUser(osdbUser, osdbPwd)
WebServices.OpenSubtitles.login()
console.println('OK')
}
/* --------------------------------------------------------------------- */
def setLogin(key, user, pwd) {
Settings.forPackage(WebServices.class).put(key, [user, pwd].join(':'))
}

View File

@ -1,30 +0,0 @@
def escapeShell(String arg) {
return '"' + arg.replaceAll(/["$`<>^\\"]/, /\\$0/) + '"'
}
if (java.awt.GraphicsEnvironment.headless && console != null) {
// CLI mode
console.printf('Enter: ')
def s = console.readLine()
console.println('\n' + escapeShell(s) + '\n')
System.exit(0)
} else {
// GUI mode
new groovy.swing.SwingBuilder().edt{
frame(title: 'Escape Tool', size: [350, 230], show: true, defaultCloseOperation: javax.swing.JFrame.EXIT_ON_CLOSE) {
gridLayout(cols: 1, rows: 2)
scrollPane{
textArea id: 'value', lineWrap: true, font: new java.awt.Font('Monospaced', 0, 16)
}
scrollPane{
textArea id: 'escape', lineWrap: true, text: bind(source:value, sourceProperty:'text', converter: { escapeShell(it) }), font: new java.awt.Font('Monospaced', 0, 16)
}
}
}
System.in.read() // wait for GUI to close
}

View File

@ -1,10 +0,0 @@
// filebot -script fn:extract <folder>
/*
* Auto-extract all zip and rar archives.
*/
args.getFiles{ it.isArchive() }.each {
def output = extract(file:it)
output.each{ println "Extracted: " + it.path }
}

View File

@ -1,16 +0,0 @@
// filebot -script fn:history --format "$from => $to"
// use --format parameter to specify your own output format
def format = _args.format ?: '[$from.name] => [$to.name]'
def template = new groovy.text.GStringTemplateEngine().createTemplate(format)
// use args to list history only for the given folders if desired
def accept(from, to) {
args.empty ? true : args.find{ to.absolutePath.startsWith(it.absolutePath) } && to.exists()
}
getRenameLog(true).each { from, to ->
if (accept(from, to))
println template.make(from:from, to:to)
}

View File

@ -1,33 +0,0 @@
// filebot -script fn:housekeeping /path/to/folder/ --output /output/folder/ --format <expression>
/*
* Watch folder for new tv shows and automatically move/rename new episodes
*/
// check for new media files once every 5 minutes
def updateFrequency = 5 * 60 * 1000
// spawn daemon thread
Thread.startDaemon {
while (sleep(updateFrequency) || true) {
// extract all
if (_args.extract) {
extract(file:args.getFiles{ it.isArchive() }, output:'.')
}
// subtitles for all
if (_args.getSubtitles) {
getMissingSubtitles(file:args.getFiles{ it.isVideo() }, output:'srt')
}
// rename all
if (_args.rename) {
args.eachMediaFolder {
rename(folder:it)
}
}
}
}
println "Press ENTER to abort"
console.readLine() // keep script running until aborted by user

View File

@ -1,53 +0,0 @@
/**
* Log into a remote host and run a given command.
*
* e.g.
* sshexec(command: "ps", host: "filebot.sf.net", username: "rednoah", password: "correcthorsebatterystaple")
*/
def sshexec(param) {
param << [trust: true] // auto-trust remote hosts
_guarded {
ant().sshexec(param)
}
}
/**
* Send email via smtp.
*
* e.g.
* sendmail(mailhost:'smtp.gmail.com', mailport:'587', ssl:'no', enableStartTLS:'yes', user:'rednoah@gmail.com', password:'correcthorsebatterystaple', from:'rednoah@gmail.com', to:'someone@gmail.com', subject:'Hello Ant World', message:'Dear Ant, ...')
*/
def sendmail(param) {
def sender = param.remove('from')
def recipient = param.remove('to')
_guarded {
ant().mail(param) {
from(address:sender)
to(address:recipient)
}
}
}
/**
* Send email using gmail default settings.
*
* e.g.
* sendGmail(subject:'Hello Ant World', message:'Dear Ant, ...', to:'someone@gmail.com', user:'rednoah', password:'correcthorsebatterystaple')
*/
def sendGmail(param) {
param << [mailhost:'smtp.gmail.com', mailport:'587', ssl:'no', enableStartTLS:'yes']
param << [user:param.username ? param.remove('username') + '@gmail.com' : param.user]
param << [from: param.from ?: param.user]
sendmail(param)
}
def ant() {
return new AntBuilder()
}

View File

@ -1,286 +0,0 @@
import static net.sourceforge.filebot.WebServices.*
import static groovy.json.StringEscapeUtils.*
import groovy.xml.*
import net.sourceforge.filebot.mediainfo.*
/**
* XBMC helper functions
*/
def scanVideoLibrary(host, port) {
_guarded {
telnet(host, port) { writer, reader ->
writer.println("""{"jsonrpc":"2.0","method":"VideoLibrary.Scan","id":1}""")
}
}
}
def showNotification(host, port, title, message, image) {
_guarded {
telnet(host, port) { writer, reader ->
writer.println("""{"jsonrpc":"2.0","method":"GUI.ShowNotification","params":{"title":"${escapeJavaScript(title)}","message":"${escapeJavaScript(message)}", "image":"${escapeJavaScript(image)}"},"id":1}""")
}
}
}
/**
* Plex helpers
*/
def refreshPlexLibrary(server, port = 32400) {
_guarded {
new URL("http://$server:$port/library/sections/all/refresh").get()
}
}
/**
* TheTVDB artwork/nfo helpers
*/
def fetchSeriesBanner(outputFile, series, bannerType, bannerType2, season, override, locale) {
if (outputFile.exists() && !override) {
_log.finest "Banner already exists: $outputFile"
return outputFile
}
// select and fetch banner
def banner = [locale, null].findResult { TheTVDB.getBanner(series, [BannerType:bannerType, BannerType2:bannerType2, Season:season, Language:it]) }
if (banner == null) {
_log.finest "Banner not found: $outputFile / $bannerType:$bannerType2"
return null
}
_log.finest "Fetching $outputFile => $banner"
return banner.url.saveAs(outputFile)
}
def fetchSeriesFanart(outputFile, series, type, season, override, locale) {
if (outputFile.exists() && !override) {
_log.finest "Fanart already exists: $outputFile"
return outputFile
}
def fanart = [locale, null].findResult{ lang -> FanartTV.getSeriesArtwork(series.seriesId).find{ type == it.type && (season == null || season == it.season) && (lang == null || lang == it.language) }}
if (fanart == null) {
_log.finest "Fanart not found: $outputFile / $type"
return null
}
_log.finest "Fetching $outputFile => $fanart"
return fanart.url.saveAs(outputFile)
}
def fetchSeriesNfo(outputFile, seriesInfo, override, locale) {
def i = seriesInfo
XML {
tvshow {
title(i.name)
sorttitle([i.name, i.firstAired as String].findAll{ it?.length() > 0 }.join('::'))
year(i.firstAired?.year)
rating(i.rating)
votes(i.ratingCount)
plot(i.overview)
runtime(i.runtime)
mpaa(i.contentRating)
id(i.id)
episodeguide {
url(cache:"${i.id}.xml", "http://www.thetvdb.com/api/1D62F2F90030C444/series/${i.id}/all/${locale.language}.zip")
}
genre(i.genres?.size() > 0 ? i.genres[0] : null)
thumb(i.bannerUrl)
premiered(i.firstAired)
status(i.status)
studio(i.network)
i.actors?.each{ n ->
actor {
name(n)
}
}
tvdb(id:i.id, "http://www.thetvdb.com/?tab=series&id=${i.id}")
}
}
.saveAs(outputFile)
}
def fetchSeriesArtworkAndNfo(seriesDir, seasonDir, series, season, override = false, locale = _args.locale) {
_guarded {
// fetch nfo
def seriesInfo = TheTVDB.getSeriesInfo(series, locale)
fetchSeriesNfo(seriesDir['tvshow.nfo'], seriesInfo, override, locale)
// fetch series banner, fanart, posters, etc
["680x1000", null].findResult{ fetchSeriesBanner(seriesDir['poster.jpg'], series, "poster", it, null, override, locale) }
["graphical", null].findResult{ fetchSeriesBanner(seriesDir['banner.jpg'], series, "series", it, null, override, locale) }
// fetch highest resolution fanart
["1920x1080", "1280x720", null].findResult{ fetchSeriesBanner(seriesDir["fanart.jpg"], series, "fanart", it, null, override, locale) }
// fetch season banners
if (seasonDir != seriesDir) {
fetchSeriesBanner(seasonDir["poster.jpg"], series, "season", "season", season, override, locale)
fetchSeriesBanner(seasonDir["banner.jpg"], series, "season", "seasonwide", season, override, locale)
}
// fetch fanart
fetchSeriesFanart(seriesDir['clearart.png'], series, 'clearart', null, override, locale)
fetchSeriesFanart(seriesDir['logo.png'], series, 'clearlogo', null, override, locale)
fetchSeriesFanart(seriesDir['landscape.jpg'], series, 'tvthumb', null, override, locale)
// fetch season fanart
if (seasonDir != seriesDir) {
fetchSeriesFanart(seasonDir['landscape.jpg'], series, 'seasonthumb', season, override, locale)
}
}
}
/**
* TheMovieDB artwork/nfo helpers
*/
def fetchMovieArtwork(outputFile, movieInfo, category, override, locale) {
if (outputFile.exists() && !override) {
_log.finest "Artwork already exists: $outputFile"
return outputFile
}
// select and fetch artwork
def artwork = TheMovieDB.getArtwork(movieInfo.id as String)
def selection = [locale.language, 'en', null].findResult{ l -> artwork.find{ (l == it.language || l == null) && it.category == category } }
if (selection == null) {
_log.finest "Artwork not found: $outputFile"
return null
}
_log.finest "Fetching $outputFile => $selection"
return selection.url.saveAs(outputFile)
}
def fetchAllMovieArtwork(outputFolder, movieInfo, category, override, locale) {
// select and fetch artwork
def artwork = TheMovieDB.getArtwork(movieInfo.id as String)
def selection = [locale.language, 'en', null].findResults{ l -> artwork.findAll{ (l == it.language || l == null) && it.category == category } }.flatten().findAll{ it?.url }.unique()
if (selection == null) {
_log.finest "Artwork not found: $outputFolder"
return null
}
selection.eachWithIndex{ s, i ->
def outputFile = new File(outputFolder, "$category-${(i+1).pad(2)}.jpg")
if (outputFile.exists() && !override) {
_log.finest "Artwork already exists: $outputFile"
} else {
_log.finest "Fetching $outputFile => $s"
s.url.saveAs(outputFile)
}
}
}
def fetchMovieFanart(outputFile, movieInfo, type, diskType, override, locale) {
if (outputFile.exists() && !override) {
_log.finest "Fanart already exists: $outputFile"
return outputFile
}
def fanart = [locale, null].findResult{ lang -> FanartTV.getMovieArtwork(movieInfo.id).find{ type == it.type && (diskType == null || diskType == it.diskType) && (lang == null || lang == it.language) }}
if (fanart == null) {
_log.finest "Fanart not found: $outputFile / $type"
return null
}
_log.finest "Fetching $outputFile => $fanart"
return fanart.url.saveAs(outputFile)
}
def fetchMovieNfo(outputFile, movieInfo, movieFile, override) {
def i = movieInfo
def mi = _guarded{ movieFile ? MediaInfo.snapshot(movieFile) : null }
XML {
movie {
title(i.name)
originaltitle(i.originalName)
sorttitle([i.collection, i.name, i.released as String].findAll{ it?.length() > 0 }.join('::'))
set(i.collection)
year(i.released?.year)
rating(i.rating)
votes(i.votes)
mpaa(i.certification)
id("tt" + (i.imdbId ?: 0).pad(7))
plot(i.overview)
tagline(i.tagline)
runtime(i.runtime)
genre(i.genres?.size() > 0 ? i.genres[0] : null)
director(i.director)
i.cast?.each{ a ->
actor {
name(a.name)
role(a.character)
}
}
i.trailers?.each{ t ->
t.sources.each { s, v ->
trailer(type:t.type, name:t.name, size:s, v)
}
}
fileinfo {
streamdetails {
mi?.each { kind, streams ->
def section = kind.toString().toLowerCase()
streams.each { s ->
if (section == 'video') {
video {
codec((s.'Encoded_Library/Name' ?: s.'CodecID/Hint' ?: s.'Format').replaceAll(/[ ].+/, '').trim())
aspect(s.'DisplayAspectRatio')
width(s.'Width')
height(s.'Height')
}
}
if (section == 'audio') {
audio {
codec((s.'CodecID/Hint' ?: s.'Format').replaceAll(/\p{Punct}/, '').trim())
language(s.'Language/String3')
channels(s.'Channel(s)')
}
}
if (section == 'text') {
subtitle {
language(s.'Language/String3')
}
}
}
}
}
}
imdb(id:"tt" + (i.imdbId ?: 0).pad(7), "http://www.imdb.com/title/tt" + (i.imdbId ?: 0).pad(7))
tmdb(id:i.id, "http://www.themoviedb.org/movie/${i.id}")
}
}
.saveAs(outputFile)
}
def fetchMovieArtworkAndNfo(movieDir, movie, movieFile = null, fetchAll = false, override = false, locale = _args.locale) {
_guarded {
def movieInfo = TheMovieDB.getMovieInfo(movie, locale)
// fetch nfo
fetchMovieNfo(movieDir['movie.nfo'], movieInfo, movieFile, override)
// generate url files
[[db:'imdb', id:movieInfo.imdbId, url:"http://www.imdb.com/title/tt" + (movieInfo.imdbId ?: 0).pad(7)], [db:'tmdb', id:movieInfo.id, url:"http://www.themoviedb.org/movie/${movieInfo.id}"]].each{
if (it.id > 0) {
def content = "[InternetShortcut]\nURL=${it.url}\n"
content.saveAs(new File(movieDir, "${it.db}.url"))
}
}
// fetch series banner, fanart, posters, etc
fetchMovieArtwork(movieDir['poster.jpg'], movieInfo, 'posters', override, locale)
fetchMovieArtwork(movieDir['fanart.jpg'], movieInfo, 'backdrops', override, locale)
fetchMovieFanart(movieDir['clearart.png'], movieInfo, 'movieart', null, override, locale)
fetchMovieFanart(movieDir['logo.png'], movieInfo, 'movielogo', null, override, locale)
['bluray', 'dvd', null].findResult { diskType -> fetchMovieFanart(movieDir['disc.png'], movieInfo, 'moviedisc', diskType, override, locale) }
if (fetchAll) {
fetchAllMovieArtwork(movieDir['backdrops'], movieInfo, 'backdrops', override, locale)
}
}
}

View File

@ -1,78 +0,0 @@
/****************************************************************************
* MyEpisodes
* http://www.myepisodes.com
****************************************************************************/
import org.jsoup.Jsoup
import org.jsoup.Connection.Method
import net.sourceforge.filebot.Cache
def MyEpisodes(username, password) {
return new MyEpisodesScraper(username:username, password:password)
}
class MyEpisodesScraper {
def username
def password
def cache = Cache.getCache('web-datasource-lv2')
def session = [:]
def login = {
def response = Jsoup.connect('http://www.myepisodes.com/login.php').data('username', username, 'password', password, 'action', 'Login', 'u', '').method(Method.POST).execute()
session << response.cookies()
return response.parse()
}
def get = { url ->
if (session.isEmpty()) {
login()
}
def response = Jsoup.connect(url).cookies(session).method(Method.GET).execute()
session << response.cookies()
def html = response.parse()
if (html.select('#frmLogin')) {
session.clear()
throw new Exception('Login failed')
}
return html
}
def getShows = {
def shows = cache.get('MyEpisodes.Shows')
if (shows == null) {
shows = ['other', 'A'..'Z'].flatten().findResults{ section ->
get("http://myepisodes.com/shows.php?list=${section}").select('a').findResults{ a ->
try {
return [id:a.absUrl('href').match(/showid=(\d+)/).toInteger(), name:a.text().trim()]
} catch(e) {
return null
}
}
}.flatten().sort{ it.name }
cache.put('MyEpisodes.Shows', shows)
}
return shows
}
def getShowList = {
get("http://www.myepisodes.com/shows.php?type=manage").select('option').findResults{ option ->
try {
return [id:option.attr('value').toInteger(), name:option.text().trim()]
} catch(e) {
return null
}
}
}
def addShow = { showid ->
get("http://www.myepisodes.com/views.php?type=manageshow&mode=add&showid=${showid}")
}
def update = { showid, season, episode, tick = 'acquired', value = '1' ->
get("http://www.myepisodes.com/myshows.php?action=Update&showid=${showid}&season=${season}&episode=${episode}&${tick}=${value}")
}
}

View File

@ -1,22 +0,0 @@
/****************************************************************************
* Pushover
* https://pushover.net
****************************************************************************/
def Pushover(user, token = 'wcckDz3oygHSU2SdIptvnHxJ92SQKK') {
new PushoverClient(user:user, token:token)
}
class PushoverClient {
def user
def token
def endpoint = new URL('https://api.pushover.net/1/messages.xml')
def send = { text, parameters = [:] ->
// inject default post parameters
parameters << [token:token, user:user, message:text as String]
// post and process response
endpoint.post(parameters).text.xml
}
}

View File

@ -1,27 +0,0 @@
// filebot -script fn:mi /path/to/media/ "MediaIndex.csv"
/*
* Print media info of all video files to CSV file
*/
def model = '''Name;Container;Resolution;Video Codec;Video Format;Audio Codec;Audio Format;Audio Language(s);Duration;File Size;Folder Size;Folder Count;Path'''
def template = '''{fn};{cf};{resolution};{vc};{vf};{ac};{af};{media.AudioLanguageList};{media.DurationString3};{file.length()};{folder.listFiles().sum{ it.length() }};{folder.listFiles().sum{ it.isFile() ? 1 : 0 }};{file.getCanonicalPath()}'''
// sanity check
if (args.size() != 2) throw new Exception('Invalid arguments:' + args)
// open destination file (writing files requires -trust-script)
args[1].withWriter{ output ->
// print header
output.writeLine(model)
// print info for each video file (sorted by filename)
args[0].getFiles{ it.isVideo() }.sort{ a, b -> a.name.compareToIgnoreCase(b.name) }.each{
def mi = getMediaInfo(file:it, format:template)
// print to console
println mi
// append to file
output.writeLine(mi)
}
}

View File

@ -1,16 +0,0 @@
// filebot -script fn:renall <options> <folder> --def target=[file|folder]
/*
* Rename all tv shows, anime or movies folder by folder using given or default options.
*/
def target = tryQuietly{ target } ?: 'file' // target files by default
args.eachMediaFolder {
if (it.isDisk())
return rename(file:it) // rename disk folders instead of files regardless of mode
switch(target) {
case 'file' : return rename(folder:it) // rename files within each folder
case 'folder' : return rename(file:it) // rename folders as if they were files
}
}

View File

@ -1,28 +0,0 @@
// filebot -script fn:replace --action copy --filter "[.]srt$" --def "e=[.](eng|english)" "r=.en"
// imports
import net.sourceforge.filebot.StandardRenameAction
// parameters
def action = StandardRenameAction.forName(_args.action)
def accept = { f -> _args.filter ? f.path =~ _args.filter : true }
// rename
args.getFiles{ accept(it) }.each{
if (it.path =~ e) {
def nfile = new File(it.path.replaceAll(e, r))
// override files only when --conflict override is set
if (!it.equals(nfile)) {
if (nfile.exists() && _args.conflict == 'override' && action != StandardRenameAction.TEST) {
nfile.delete() // resolve conflict
}
if (!nfile.exists()) {
println action.rename(it, nfile)
} else {
println "Skipped $nfile"
}
}
}
}

View File

@ -1,24 +0,0 @@
// filebot -script fn:revert <file or folder>
def accept(from, to) {
args.find{ to.absolutePath.startsWith(it.absolutePath) } && to.exists()
}
def revert(from, to) {
def action = net.sourceforge.filebot.StandardRenameAction.forName(_args.action)
println "[$action] Revert [$from] to [$to]"
if (!from.canonicalFile.equals(to.canonicalFile)) {
action.rename(from, to) // reverse-rename only if path has changed
}
// reset extended attributes
tryQuietly{ to.xattr.clear() }
}
getRenameLog(true).reverseEach { from, to ->
if (accept(from, to))
revert(to, from)
}

View File

@ -1,50 +0,0 @@
// filebot -script fn:sortivo <folder> --output path/to/folder [-non-strict]
// process only media files
def input = args.getFiles{ it.isVideo() || it.isSubtitle() }
// ignore clutter files
input = input.findAll{ !(it.path =~ /\b(?i:sample|trailer|extras|deleted.scenes|music.video|scrapbook)\b/) }
// print input fileset
input.each{ println "Input: $it" }
/*
* Move/Rename a mix of episodes and movies that are all in the same folder.
*/
def groups = input.groupBy{ f ->
def tvs = detectSeriesName(f)
def mov = (parseEpisodeNumber(f) || parseDate(f)) ? null : detectMovie(f, false) // skip movie detection if we can already tell it's an episode
println "$f.name [series: $tvs, movie: $mov]"
// DECIDE EPISODE VS MOVIE (IF NOT CLEAR)
if (tvs && mov) {
def norm = { s -> s.lower().space(' ') }
def fn = norm(f.nameWithoutExtension)
def sn = norm(tvs)
def mn = norm(mov.name)
// S00E00 | 2012.07.21 | One Piece 217 | Firefly - Serenity | [Taken 1, Taken 2, Taken 3, Taken 4, ..., Taken 10]
if (parseEpisodeNumber(fn, true) || parseDate(fn) || (fn =~ sn && parseEpisodeNumber(fn.after(sn), false)) || fn.after(sn) =~ / - .+/ || f.dir.listFiles{ it.isVideo() && norm(it.name) =~ sn && it.name =~ /\b\d{1,3}\b/}.size() >= 10) {
println "Exclude Movie: $mov"
mov = null
} else if ((detectMovie(f, true) && fn =~ /(19|20)\d{2}/) || (fn =~ mn && !(fn.after(mn) =~ /\b\d{1,3}\b/))) {
println "Exclude Series: $tvs"
tvs = null
}
}
return [tvs:tvs, mov:mov]
}
groups.each{ group, files ->
// EPISODE MODE
if (group.tvs && !group.mov) {
rename(file:files, format:'TV Shows/{n}/{episode.special ? "Special" : "Season "+s}/{n} - {episode.special ? "S00E"+special.pad(2) : s00e00} - {t}', db:'TheTVDB')
}
// MOVIE MODE
if (group.mov && !group.tvs) {
rename(file:files, format:'Movies/{n} ({y})/{n} ({y}){" CD$pi"}', db:'TheMovieDB')
}
}

View File

@ -1,65 +0,0 @@
// PERSONALIZED SETTINGS
def episodeDir = '''/in/TV'''
def episodeFormat = '''/out/TV/{n}/{"Season ${s.pad(2)}"}/{n} - {s00e00} - {t}'''
def movieDir = '''/in/Movies'''
def movieFormat = '''/out/Movies/{n} ({y})/{n} ({y}){" CD$pi"}'''
// XBMC ON LOCAL MACHINE
def xbmc = ['localhost'] // (use [] to not notify any XBMC instances about updates)
// ignore chunk, part, par and hidden files
def incomplete(f) { f.name =~ /[.]incomplete|[.]chunk|[.]par$|[.]dat$/ }
// extract completed multi-volume rar files
[episodeDir, movieDir].getFolders{ !it.hasFile{ incomplete(it) } && it.hasFile{ it =~ /[.]rar$/ } }.each{ dir ->
// extract all archives found in this folder
def paths = extract(folder:dir)
// delete original archive volumes after successful extraction
if (paths != null && !paths.isEmpty()) {
dir.listFiles{ it =~ /[.]rar$|[.]r[\d]+$/ }*.delete()
}
}
/*
* Fetch subtitles and sort into folders
*/
episodeDir.getFolders{ !it.hasFile{ incomplete(it) } && it.hasFile{ it.isVideo() } }.each{ dir ->
println "Processing $dir"
def files = dir.listFiles{ it.isVideo() }
// fetch subtitles
files += getSubtitles(file:files)
// sort episodes / subtitles
rename(file:files, db:'TheTVDB', format:episodeFormat)
}
movieDir.getFolders{ !it.hasFile{ incomplete(it) } && it.hasFile{ it.isVideo() } }.each{ dir ->
println "Processing $dir"
def files = dir.listFiles{ it.isVideo() }
// fetch subtitles
files += getSubtitles(file:files)
// sort movies / subtitles
rename(file:files, db:'TheMovieDB', format:movieFormat)
}
// make XBMC scan for new content
xbmc.each { host ->
telnet(host, 9090) { writer, reader ->
// API call for latest XBMC release
def msg = '{"id":1,"method":"VideoLibrary.Scan","params":[],"jsonrpc":"2.0"}'
// API call for XBMC Dharma-Release or older
// def msg = '{"id":1,"method":"VideoLibrary.ScanForContent","params":[],"jsonrpc":"2.0"}'
writer.println(msg)
}
}

View File

@ -1,13 +0,0 @@
// filebot -script fn:src <folder>
/*
* Fetch subtitles, rename and calculate checksums for all video files
*/
args.eachMediaFolder {
getMissingSubtitles(folder:it)
def renamedFiles = rename(folder:it)
compute(file:renamedFiles.findAll{ it.isVideo() })
}

View File

@ -1,8 +0,0 @@
// filebot -script fn:suball <options> <folder>
/*
* Get subtitles for all your media files
*/
args.eachMediaFolder {
getMissingSubtitles(folder:it)
}

View File

@ -1,11 +0,0 @@
// filebot -script fn:sysenv
println '# Java System Properties #'
_system.each{
println "$it.key: $it.value"
}
println '# Environment Variables #'
_environment.each{
println "$it.key: $it.value"
}

View File

@ -1,98 +0,0 @@
// filebot -script fn:sysinfo
// FileBot 2.62 (r993)
println net.sourceforge.filebot.Settings.applicationIdentifier
// JNA Native: 3.5.0
try {
print 'JNA Native: '
println com.sun.jna.Native.nativeVersion
} catch(Throwable error) {
println error.cause
}
// MediaInfo: MediaInfoLib - v0.7.48
try {
print 'MediaInfo: '
println net.sourceforge.filebot.mediainfo.MediaInfo.version()
} catch(Throwable error) {
println error.cause
}
// 7-Zip-JBinding: OK
try {
print '7-Zip-JBinding: '
net.sourceforge.filebot.archive.SevenZipLoader.requireNativeLibraries() // load 7-Zip-JBinding native libs
println 'OK'
} catch(Throwable error) {
println error
}
// chromaprint-tools
try {
print 'chromaprint-tools: '
def fpcalc = System.getProperty('net.sourceforge.filebot.AcoustID.fpcalc', 'fpcalc')
def version = [fpcalc, '-version'].execute().text.trim() ?: 'fpcalc -version failed'
println "$version ($fpcalc)"
} catch(Throwable error) {
println error
}
// Extended File Attributes
try {
print 'Extended Attributes: '
if (net.sourceforge.filebot.Settings.useExtendedFileAttributes()){
// create new temp file
def f = new File(net.sourceforge.filebot.Settings.applicationFolder, '.xattr-test')
f.createNewFile() && f.deleteOnExit()
// xattr write, read and verify
def xattr = new net.sourceforge.filebot.media.MetaAttributes(f)
def payload = new Date()
xattr.setObject(payload)
assert xattr.getObject() == payload
println 'OK'
} else {
println 'DISABLED'
}
} catch(Throwable error) {
println error
}
// GIO and GVFS
try {
if (net.sourceforge.filebot.Settings.useGVFS()) {
print 'GVFS: '
assert net.sourceforge.filebot.gio.GVFS.defaultVFS != null
println 'OK'
}
} catch(Throwable error) {
println error
}
// Groovy Engine: 2.1.7
println 'Groovy Engine: ' + groovy.lang.GroovySystem.version
// Java(TM) SE Runtime Environment 1.6.0_30 (headless)
println net.sourceforge.filebot.Settings.javaRuntimeIdentifier
// 32-bit Java HotSpot(TM) Client VM
println String.format('%d-bit %s', com.sun.jna.Platform.is64Bit() ? 64 : 32, _system['java.vm.name'])
// Windows 7 (x86)
println String.format('%s (%s)', _system['os.name'], _system['os.arch'])
// check for updates
try {
def update = new XmlSlurper().parse('http://filebot.net/update.xml')
def latestRev = update.revision.text() as int
def latestApp = update.name.text()
if (latestRev > net.sourceforge.filebot.Settings.applicationRevisionNumber) {
println "\n--- UPDATE AVAILABLE: $latestApp (r$latestRev) ---\n"
}
} catch(Throwable error) {
// ignore
}

View File

@ -1,38 +0,0 @@
// filebot -script fn:update-mes "X:/path/to/episodes" --def login=user:pwd addshows=y tick=acquired
def mesacc = login.split(':')
def mesadd = tryQuietly{ addshows.toBoolean() }
def mesupdate = tryQuietly { tick } ?: 'acquired'
def mesvalue = tryQuietly { value } ?: '1'
// import myepisodes scraper
include('lib/scraper')
def mes = MyEpisodes(mesacc[0], mesacc[1])
def myshows = mes.getShowList()
// series name => series key (e.g. Doctor Who (2005) => doctorwho)
def collationKey = { s -> s == null ? '' : s.replaceAll(/^(?i)(The|A)\b/, '').replaceAll(/(?<!\d)\d{4}$/).replaceAll(/\W/).lower() }
args.getFiles().findAll{ it.isVideo() && parseEpisodeNumber(it) && detectSeriesName(it) }.groupBy{ detectSeriesName(it) }.each{ series, files ->
def show = myshows.find{ collationKey(it.name) == collationKey(series) }
if (show == null && mesadd) {
show = mes.getShows().find{ collationKey(it.name) == collationKey(series) }
if (show == null) {
println "[failure] '$series' not found"
return
}
mes.addShow(show.id)
println "[added] $show.name"
}
files.each{
if (show != null) {
def sxe = parseEpisodeNumber(it)
mes.update(show.id, sxe.season, sxe.episode, mesupdate, mesvalue)
println "[$mesupdate] $show.name $sxe [$it.name]"
} else {
println "[failure] '$series' has not been added [$it.name]"
}
}
}

View File

@ -1,22 +0,0 @@
// filebot -script fn:watcher /path/to/folder/ --output /output/folder/ --format <expression>
// watch folders and print files that were added/modified
def watchman = args.watch { changes ->
// extract all
if (_args.extract)
changes += extract(file:changes.findAll{ it.isArchive() }, output:'.')
// subtitles for all
if (_args.getSubtitles)
changes += getMissingSubtitles(file:changes.findAll{ it.isVideo() }, output:'srt')
// rename all
if (_args.rename)
rename(file:changes)
}
watchman.commitDelay = 5 * 1000 // default = 5s
watchman.commitPerFolder = true // default = true
println "Waiting for events"
if (console) { console.readLine() } else { sleep(Long.MAX_VALUE) } // keep running and watch for changes

View File

@ -1,13 +0,0 @@
// filebot -script fn:xattr --action clear /path/to/files
args.getFiles{ it.xattr.size() > 0 }.each{
println it
it.xattr.each{ k, v ->
println "\t$k: $v"
}
// clear xattr mode
if (_args.action == 'clear') {
it.xattr.clear()
println '*** CLEARED ***'
}
}