2013-02-13 05:58:13 +00:00
|
|
|
|
// 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"
|
2012-04-28 18:15:15 +00:00
|
|
|
|
def input = []
|
2012-07-08 06:29:07 +00:00
|
|
|
|
def failOnError = _args.conflict == 'fail'
|
2012-04-26 11:25:58 +00:00
|
|
|
|
|
2012-04-28 18:15:15 +00:00
|
|
|
|
// print input parameters
|
2013-02-15 09:50:23 +00:00
|
|
|
|
_args.bindings?.each{ _log.fine("Parameter: $it.key = $it.value") }
|
|
|
|
|
args.each{ _log.fine("Argument: $it") }
|
2013-02-12 17:07:59 +00:00
|
|
|
|
args.findAll{ !it.exists() }.each{ throw new Exception("File not found: $it") }
|
2012-04-28 18:15:15 +00:00
|
|
|
|
|
2012-12-10 18:07:03 +00:00
|
|
|
|
// check user-defined pre-condition
|
2013-05-18 15:30:25 +00:00
|
|
|
|
if (tryQuietly{ !(ut_state ==~ ut_state_allow) }) {
|
2012-12-10 18:07:03 +00:00
|
|
|
|
throw new Exception("Invalid state: ut_state = $ut_state (expected $ut_state_allow)")
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-27 02:11:04 +00:00
|
|
|
|
// check ut mode vs standalone mode
|
2013-09-23 15:30:10 +00:00
|
|
|
|
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))) {
|
2013-06-27 02:11:04 +00:00
|
|
|
|
throw new Exception("Conflicting arguments: pass in either file arguments or ut_dir/ut_file parameters but not both")
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-01 06:04:47 +00:00
|
|
|
|
// enable/disable features as specified via --def parameters
|
2013-02-02 03:21:56 +00:00
|
|
|
|
def music = tryQuietly{ music.toBoolean() }
|
2013-01-19 17:04:15 +00:00
|
|
|
|
def subtitles = tryQuietly{ subtitles.toBoolean() ? ['en'] : subtitles.split(/[ ,|]+/).findAll{ it.length() >= 2 } }
|
2012-07-30 10:01:03 +00:00
|
|
|
|
def artwork = tryQuietly{ artwork.toBoolean() }
|
2012-12-16 13:26:39 +00:00
|
|
|
|
def backdrops = tryQuietly{ backdrops.toBoolean() }
|
2013-01-05 04:25:21 +00:00
|
|
|
|
def clean = tryQuietly{ clean.toBoolean() }
|
2013-03-01 07:40:50 +00:00
|
|
|
|
def exec = tryQuietly{ exec.toString() }
|
2012-07-30 10:01:03 +00:00
|
|
|
|
|
|
|
|
|
// array of xbmc/plex hosts
|
2012-08-01 06:04:47 +00:00
|
|
|
|
def xbmc = tryQuietly{ xbmc.split(/[ ,|]+/) }
|
|
|
|
|
def plex = tryQuietly{ plex.split(/[ ,|]+/) }
|
2012-07-30 10:01:03 +00:00
|
|
|
|
|
2013-11-17 20:37:11 +00:00
|
|
|
|
// extra options, myepisodes updates and email notifications
|
2013-11-23 15:33:26 +00:00
|
|
|
|
def deleteAfterExtract = tryQuietly{ deleteAfterExtract.toBoolean() }
|
2013-11-17 20:37:11 +00:00
|
|
|
|
def excludeList = tryQuietly{ new File(_args.output, excludeList) }
|
|
|
|
|
def myepisodes = tryQuietly{ myepisodes.split(':', 2) }
|
2012-07-30 16:59:09 +00:00
|
|
|
|
def gmail = tryQuietly{ gmail.split(':', 2) }
|
2013-03-01 07:40:50 +00:00
|
|
|
|
def pushover = tryQuietly{ pushover.toString() }
|
2012-07-30 16:59:09 +00:00
|
|
|
|
|
2013-05-04 12:09:51 +00:00
|
|
|
|
// user-defined filters
|
2013-05-17 18:48:31 +00:00
|
|
|
|
def minFileSize = tryQuietly{ minFileSize.toLong() }; if (minFileSize == null) { minFileSize = 0 };
|
2012-10-14 05:19:35 +00:00
|
|
|
|
|
|
|
|
|
// series/anime/movie format expressions
|
|
|
|
|
def format = [
|
2013-04-02 15:34:25 +00:00
|
|
|
|
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"}''',
|
2013-02-22 11:37:08 +00:00
|
|
|
|
anime: tryQuietly{ animeFormat } ?: '''Anime/{n}/{n} - {sxe} - {t.replaceAll(/[!?.]+$/).replaceAll(/[`´‘’ʻ]/, "'").replacePart(', Part $1')}''',
|
2013-01-10 19:30:16 +00:00
|
|
|
|
mov: tryQuietly{ movieFormat } ?: '''Movies/{n} ({y})/{n} ({y}){" CD$pi"}{".$lang"}''',
|
2013-02-15 09:50:23 +00:00
|
|
|
|
music: tryQuietly{ musicFormat } ?: '''Music/{n}/{album+'/'}{pi.pad(2)+'. '}{artist} - {t}'''
|
2012-10-14 05:19:35 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2012-07-30 16:59:09 +00:00
|
|
|
|
// force movie/series/anime logic
|
|
|
|
|
def forceMovie(f) {
|
2013-11-23 04:53:59 +00:00
|
|
|
|
tryQuietly{ ut_label } =~ /^(?i:Movie|Couch.Potato)/ || f.dir.path =~ /(?i:Movies)/ || f.path =~ /(?<=tt)\\d{7}/ || tryQuietly{ f.metadata?.object?.class.name =~ /Movie/ }
|
2012-07-30 16:59:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def forceSeries(f) {
|
2013-11-23 04:53:59 +00:00
|
|
|
|
tryQuietly{ ut_label } =~ /^(?i:TV|Kids.Shows)/ || f.dir.path =~ /(?i:TV.Shows)/ || parseEpisodeNumber(f.path) || parseDate(f.path) || f.path =~ /(?i:Season)\D?[0-9]{1,2}\D/ || tryQuietly{ f.metadata?.object?.class.name =~ /Episode/ }
|
2012-07-30 16:59:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def forceAnime(f) {
|
2013-11-23 04:53:59 +00:00
|
|
|
|
tryQuietly{ ut_label } =~ /^(?i:Anime)/ || f.dir.path =~ /(?i:Anime)/ || (f.isVideo() && (f.name =~ "[\\(\\[]\\p{XDigit}{8}[\\]\\)]" || getMediaInfo(file:f, format:'''{media.AudioLanguageList} {media.TextCodecList}''').tokenize().containsAll(['Japanese', 'ASS'])))
|
2012-07-30 16:59:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def forceIgnore(f) {
|
2013-11-23 04:53:59 +00:00
|
|
|
|
tryQuietly{ ut_label } =~ /^(?i:ebook|other|ignore)/ || f.dir.path =~ /(?i:other)/ || f.path =~ tryQuietly{ ignore }
|
2012-07-30 16:59:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-30 10:01:03 +00:00
|
|
|
|
|
2012-10-30 09:48:34 +00:00
|
|
|
|
// 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
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-30 10:01:03 +00:00
|
|
|
|
// collect input fileset as specified by the given --def parameters
|
2012-10-18 16:34:38 +00:00
|
|
|
|
if (args.empty) {
|
2012-11-09 11:19:35 +00:00
|
|
|
|
// 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)) {
|
2012-07-13 14:33:39 +00:00
|
|
|
|
input += new File(ut_dir, ut_file) // single-file torrent
|
2012-07-16 12:19:13 +00:00
|
|
|
|
} else {
|
2012-10-30 09:48:34 +00:00
|
|
|
|
input += resolveInput(ut_dir as File) // multi-file torrent
|
2012-07-13 14:33:39 +00:00
|
|
|
|
}
|
2012-04-28 18:15:15 +00:00
|
|
|
|
} else {
|
2012-07-13 14:33:39 +00:00
|
|
|
|
// assume we're called normally with arguments
|
2012-10-30 09:48:34 +00:00
|
|
|
|
input += args.findResults{ resolveInput(it) }
|
2012-04-28 18:15:15 +00:00
|
|
|
|
}
|
2012-04-26 11:25:58 +00:00
|
|
|
|
|
2012-07-31 16:17:15 +00:00
|
|
|
|
|
2012-10-30 09:48:34 +00:00
|
|
|
|
// flatten nested file structure
|
|
|
|
|
input = input.flatten()
|
|
|
|
|
|
2012-07-31 16:17:15 +00:00
|
|
|
|
// extract archives (zip, rar, etc) that contain at least one video file
|
2013-11-24 13:47:22 +00:00
|
|
|
|
def extractedArchives = []
|
2013-04-09 09:12:20 +00:00
|
|
|
|
def tempFiles = []
|
|
|
|
|
input = input.flatten{ f ->
|
|
|
|
|
if (f.isArchive() || f.hasExtension('001')) {
|
2013-04-13 06:47:28 +00:00
|
|
|
|
def extractDir = new File(f.dir, f.nameWithoutExtension)
|
2013-11-17 19:15:23 +00:00
|
|
|
|
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) ?: []
|
2013-11-24 13:47:22 +00:00
|
|
|
|
|
|
|
|
|
if (extractFiles.size() > 0) {
|
|
|
|
|
extractedArchives += f
|
|
|
|
|
tempFiles += extractDir
|
|
|
|
|
tempFiles += extractFiles
|
2013-11-17 20:37:11 +00:00
|
|
|
|
}
|
2013-04-09 09:12:20 +00:00
|
|
|
|
return extractFiles
|
|
|
|
|
}
|
|
|
|
|
return f
|
|
|
|
|
}
|
2012-04-26 11:25:58 +00:00
|
|
|
|
|
2012-08-13 08:03:11 +00:00
|
|
|
|
// sanitize input
|
|
|
|
|
input = input.findAll{ it?.exists() }.collect{ it.canonicalFile }.unique()
|
|
|
|
|
|
2012-04-26 11:25:58 +00:00
|
|
|
|
// process only media files
|
2013-09-18 02:39:30 +00:00
|
|
|
|
input = input.findAll{ f -> (f.isVideo() && !tryQuietly{ f.hasExtension('iso') && !f.isDisk() }) || f.isSubtitle() || (f.isDirectory() && f.isDisk()) || (music && f.isAudio()) }
|
2012-04-26 11:25:58 +00:00
|
|
|
|
|
2012-05-31 12:10:50 +00:00
|
|
|
|
// ignore clutter files
|
2013-11-07 19:35:29 +00:00
|
|
|
|
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)) }
|
2012-05-31 12:10:50 +00:00
|
|
|
|
|
2013-11-09 05:54:11 +00:00
|
|
|
|
// 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)
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-28 18:15:15 +00:00
|
|
|
|
// print input fileset
|
2012-07-30 16:59:09 +00:00
|
|
|
|
input.each{ f -> _log.finest("Input: $f") }
|
2012-04-26 11:25:58 +00:00
|
|
|
|
|
2012-07-28 10:21:30 +00:00
|
|
|
|
// artwork/nfo utility
|
2013-11-23 04:32:09 +00:00
|
|
|
|
if (artwork || xbmc || plex) { include('fn:lib/htpc') }
|
2012-04-29 05:28:38 +00:00
|
|
|
|
|
2012-04-26 11:25:58 +00:00
|
|
|
|
// group episodes/movies and rename according to XBMC standards
|
2012-07-08 12:01:19 +00:00
|
|
|
|
def groups = input.groupBy{ f ->
|
2012-07-30 16:59:09 +00:00
|
|
|
|
// skip auto-detection if possible
|
|
|
|
|
if (forceIgnore(f))
|
|
|
|
|
return []
|
2013-01-14 17:26:59 +00:00
|
|
|
|
if (f.isAudio() && !f.isVideo()) // PROCESS MUSIC FOLDER BY FOLDER
|
2013-01-10 19:30:16 +00:00
|
|
|
|
return [music: f.dir.name]
|
2012-07-30 16:59:09 +00:00
|
|
|
|
if (forceMovie(f))
|
2012-08-09 05:06:28 +00:00
|
|
|
|
return [mov: detectMovie(f, false)]
|
2012-07-30 16:59:09 +00:00
|
|
|
|
if (forceSeries(f))
|
2012-08-09 05:06:28 +00:00
|
|
|
|
return [tvs: detectSeriesName(f) ?: detectSeriesName(f.dir.listFiles{ it.isVideo() })]
|
2012-07-30 16:59:09 +00:00
|
|
|
|
if (forceAnime(f))
|
2012-08-09 05:06:28 +00:00
|
|
|
|
return [anime: detectSeriesName(f) ?: detectSeriesName(f.dir.listFiles{ it.isVideo() })]
|
2012-07-30 16:59:09 +00:00
|
|
|
|
|
|
|
|
|
|
2012-07-08 12:01:19 +00:00
|
|
|
|
def tvs = detectSeriesName(f)
|
2012-07-30 16:59:09 +00:00
|
|
|
|
def mov = detectMovie(f, false)
|
2013-02-15 09:50:23 +00:00
|
|
|
|
_log.fine("$f.name [series: $tvs, movie: $mov]")
|
2012-04-26 11:25:58 +00:00
|
|
|
|
|
|
|
|
|
// DECIDE EPISODE VS MOVIE (IF NOT CLEAR)
|
|
|
|
|
if (tvs && mov) {
|
2013-03-16 16:13:50 +00:00
|
|
|
|
def norm = { s -> s.ascii().normalizePunctuation().lower().space(' ') }
|
2012-07-27 02:14:49 +00:00
|
|
|
|
def dn = norm(guessMovieFolder(f)?.name ?: '')
|
2012-07-21 14:32:55 +00:00
|
|
|
|
def fn = norm(f.nameWithoutExtension)
|
|
|
|
|
def sn = norm(tvs)
|
|
|
|
|
def mn = norm(mov.name)
|
|
|
|
|
|
2013-08-15 17:41:21 +00:00
|
|
|
|
/**
|
|
|
|
|
println '--- EPISODE FILTER (POS) ---'
|
|
|
|
|
println parseEpisodeNumber(fn, true) || parseDate(fn)
|
2013-09-01 09:20:01 +00:00
|
|
|
|
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)
|
2013-08-15 17:41:21 +00:00
|
|
|
|
println (fn.after(sn) ==~ /.{0,3} - .+/ && matchMovie(fn, true) == null)
|
2013-10-15 10:54:23 +00:00
|
|
|
|
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
|
2013-08-15 17:41:21 +00:00
|
|
|
|
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}/ })
|
|
|
|
|
**/
|
|
|
|
|
|
2012-07-21 14:32:55 +00:00
|
|
|
|
// S00E00 | 2012.07.21 | One Piece 217 | Firefly - Serenity | [Taken 1, Taken 2, Taken 3, Taken 4, ..., Taken 10]
|
2013-10-15 10:54:23 +00:00
|
|
|
|
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}/ }) ) ) {
|
2013-02-15 09:50:23 +00:00
|
|
|
|
_log.fine("Exclude Movie: $mov")
|
2012-04-26 11:25:58 +00:00
|
|
|
|
mov = null
|
2013-07-24 12:32:11 +00:00
|
|
|
|
} else if (similarity(mn, fn) >= 0.8 || [dn, fn].find{ it =~ /\b/+mov.year+/\b/ } || [dn, fn].find{ it =~ mn && !(it.after(mn) =~ /\b\d{1,3}\b/) && !(it.before(mn).contains(sn)) } || (detectMovie(f, true) && [dn, fn].find{ it =~ /(19|20)\d{2}/ })) {
|
2013-02-15 09:50:23 +00:00
|
|
|
|
_log.fine("Exclude Series: $tvs")
|
2012-07-08 13:13:18 +00:00
|
|
|
|
tvs = null
|
2012-04-26 11:25:58 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-07-08 06:29:07 +00:00
|
|
|
|
|
|
|
|
|
// CHECK CONFLICT
|
2013-07-24 12:32:11 +00:00
|
|
|
|
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]
|
|
|
|
|
}
|
2012-07-08 06:29:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-30 16:59:09 +00:00
|
|
|
|
return [tvs: tvs, mov: mov, anime: null]
|
2012-04-26 11:25:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-30 16:59:09 +00:00
|
|
|
|
// log movie/series/anime detection results
|
2012-07-31 00:00:46 +00:00
|
|
|
|
groups.each{ group, files -> _log.finest("Group: $group => ${files*.name}") }
|
2012-07-30 16:59:09 +00:00
|
|
|
|
|
|
|
|
|
// process each batch
|
2012-04-26 11:25:58 +00:00
|
|
|
|
groups.each{ group, files ->
|
2013-02-02 03:21:56 +00:00
|
|
|
|
// fetch subtitles (but not for anime)
|
2013-11-07 19:35:29 +00:00
|
|
|
|
if (subtitles && !group.anime && files.findAll{ it.isVideo() }.size() > 0) {
|
2013-01-19 17:04:15 +00:00
|
|
|
|
subtitles.each{ languageCode ->
|
2013-04-08 19:04:01 +00:00
|
|
|
|
def subtitleFiles = getMissingSubtitles(file:files, output:'srt', encoding:'UTF-8', lang:languageCode, strict:true) ?: []
|
2013-02-26 16:36:53 +00:00
|
|
|
|
files += subtitleFiles
|
|
|
|
|
tempFiles += subtitleFiles // if downloaded for temporarily extraced files delete later
|
2013-01-19 17:04:15 +00:00
|
|
|
|
}
|
2012-07-30 10:01:03 +00:00
|
|
|
|
}
|
2012-04-29 05:28:38 +00:00
|
|
|
|
|
2012-04-26 11:25:58 +00:00
|
|
|
|
// EPISODE MODE
|
2012-07-30 16:59:09 +00:00
|
|
|
|
if ((group.tvs || group.anime) && !group.mov) {
|
|
|
|
|
// choose series / anime config
|
2012-11-24 00:45:47 +00:00
|
|
|
|
def config = group.tvs ? [name:group.tvs, format:format.tvs, db:'TheTVDB', seasonFolder:true ]
|
2012-11-24 21:56:09 +00:00
|
|
|
|
: [name:group.anime, format:format.anime, db:'AniDB', seasonFolder:false]
|
2012-07-30 16:59:09 +00:00
|
|
|
|
def dest = rename(file: files, format: config.format, db: config.db)
|
2012-07-30 10:01:03 +00:00
|
|
|
|
if (dest && artwork) {
|
2012-07-08 06:29:07 +00:00
|
|
|
|
dest.mapByFolder().each{ dir, fs ->
|
2013-03-06 08:34:43 +00:00
|
|
|
|
_log.finest "Fetching artwork for $dir from TheTVDB"
|
2012-07-27 06:29:28 +00:00
|
|
|
|
def sxe = fs.findResult{ eps -> parseEpisodeNumber(eps) }
|
2013-11-01 11:21:47 +00:00
|
|
|
|
def options = TheTVDB.search(detectSeriesName(fs), _args.locale)
|
2012-05-31 12:10:50 +00:00
|
|
|
|
if (options.isEmpty()) {
|
2013-03-06 08:34:43 +00:00
|
|
|
|
_log.warning "TV Series not found: $config.name"
|
2012-05-31 12:10:50 +00:00
|
|
|
|
return
|
|
|
|
|
}
|
2012-07-30 16:59:09 +00:00
|
|
|
|
options = options.sortBySimilarity(config.name, { s -> s.name })
|
2012-11-24 00:45:47 +00:00
|
|
|
|
fetchSeriesArtworkAndNfo(config.seasonFolder ? dir.dir : dir, dir, options[0], sxe && sxe.season > 0 ? sxe.season : 1)
|
2012-04-28 18:15:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-07-30 10:01:03 +00:00
|
|
|
|
if (dest == null && failOnError) {
|
2012-07-30 16:59:09 +00:00
|
|
|
|
throw new Exception("Failed to rename series: $config.name")
|
2012-07-30 10:01:03 +00:00
|
|
|
|
}
|
2012-04-26 11:25:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MOVIE MODE
|
2013-11-07 19:35:29 +00:00
|
|
|
|
else if (group.mov && !group.tvs && !group.anime) {
|
2012-10-14 05:19:35 +00:00
|
|
|
|
def dest = rename(file:files, format:format.mov, db:'TheMovieDB')
|
2012-07-30 10:01:03 +00:00
|
|
|
|
if (dest && artwork) {
|
2012-07-08 06:29:07 +00:00
|
|
|
|
dest.mapByFolder().each{ dir, fs ->
|
2013-03-06 08:34:43 +00:00
|
|
|
|
_log.finest "Fetching artwork for $dir from TheMovieDB"
|
2013-11-01 11:21:47 +00:00
|
|
|
|
def movieFile = fs.findAll{ it.isVideo() }.sort{ it.length() }.reverse().findResult{ it }
|
|
|
|
|
fetchMovieArtworkAndNfo(dir, detectMovie(movieFile), movieFile, backdrops)
|
2012-05-31 12:10:50 +00:00
|
|
|
|
}
|
2012-04-28 18:15:15 +00:00
|
|
|
|
}
|
2012-07-30 10:01:03 +00:00
|
|
|
|
if (dest == null && failOnError) {
|
|
|
|
|
throw new Exception("Failed to rename movie: $group.mov")
|
|
|
|
|
}
|
2012-04-26 11:25:58 +00:00
|
|
|
|
}
|
2013-01-10 19:30:16 +00:00
|
|
|
|
|
|
|
|
|
// MUSIC MODE
|
2013-11-07 19:35:29 +00:00
|
|
|
|
else if (group.music) {
|
2013-01-10 19:30:16 +00:00
|
|
|
|
def dest = rename(file:files, format:format.music, db:'AcoustID')
|
|
|
|
|
if (dest == null && failOnError) {
|
|
|
|
|
throw new Exception("Failed to rename music: $group.music")
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-26 11:25:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-06 04:03:59 +00:00
|
|
|
|
// skip notifications if nothing was renamed anyway
|
|
|
|
|
if (getRenameLog().isEmpty()) {
|
|
|
|
|
return
|
|
|
|
|
}
|
2012-04-26 11:25:58 +00:00
|
|
|
|
|
2013-03-01 07:40:50 +00:00
|
|
|
|
// 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)
|
2013-02-26 19:40:24 +00:00
|
|
|
|
}
|
2012-07-30 10:01:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-03-01 07:40:50 +00:00
|
|
|
|
// make XMBC scan for new content and display notification message
|
|
|
|
|
if (xbmc) {
|
|
|
|
|
xbmc.each{ host ->
|
2013-03-06 08:34:43 +00:00
|
|
|
|
_log.info "Notify XBMC: $host"
|
2013-03-01 07:40:50 +00:00
|
|
|
|
_guarded{
|
2013-03-09 17:15:46 +00:00
|
|
|
|
showNotification(host, 9090, 'FileBot', "Finished processing ${tryQuietly { ut_title } ?: input*.dir.name.unique()} (${getRenameLog().size()} files).", 'http://www.filebot.net/images/icon.png')
|
2013-03-01 07:40:50 +00:00
|
|
|
|
scanVideoLibrary(host, 9090)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// make Plex scan for new content
|
|
|
|
|
if (plex) {
|
|
|
|
|
plex.each{
|
2013-03-06 08:34:43 +00:00
|
|
|
|
_log.info "Notify Plex: $it"
|
2013-03-01 07:40:50 +00:00
|
|
|
|
refreshPlexLibrary(it)
|
|
|
|
|
}
|
2012-04-28 18:15:15 +00:00
|
|
|
|
}
|
2012-07-30 16:59:09 +00:00
|
|
|
|
|
2012-12-07 16:47:54 +00:00
|
|
|
|
// mark episodes as 'acquired'
|
|
|
|
|
if (myepisodes) {
|
2013-03-06 08:34:43 +00:00
|
|
|
|
_log.info 'Update MyEpisodes'
|
2013-11-23 04:32:09 +00:00
|
|
|
|
executeScript('fn:update-mes', [login:myepisodes.join(':'), addshows:true], getRenameLog().values())
|
2012-12-07 16:47:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-22 08:14:21 +00:00
|
|
|
|
if (pushover) {
|
|
|
|
|
// include webservice utility
|
2013-11-23 04:32:09 +00:00
|
|
|
|
include('fn:lib/ws')
|
2013-01-22 08:14:21 +00:00
|
|
|
|
|
2013-03-06 08:34:43 +00:00
|
|
|
|
_log.info 'Sending Pushover notification'
|
2013-01-22 08:14:21 +00:00
|
|
|
|
Pushover(pushover).send("Finished processing ${tryQuietly { ut_title } ?: input*.dir.name.unique()} (${getRenameLog().size()} files).")
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-30 16:59:09 +00:00
|
|
|
|
// send status email
|
2013-01-06 04:03:59 +00:00
|
|
|
|
if (gmail) {
|
2012-07-30 16:59:09 +00:00
|
|
|
|
// ant/mail utility
|
2013-11-23 04:32:09 +00:00
|
|
|
|
include('fn:lib/ant')
|
2012-07-30 16:59:09 +00:00
|
|
|
|
|
|
|
|
|
// send html mail
|
|
|
|
|
def renameLog = getRenameLog()
|
2012-10-29 14:46:25 +00:00
|
|
|
|
def emailTitle = tryQuietly { ut_title } ?: input*.dir.name.unique()
|
2012-07-30 16:59:09 +00:00
|
|
|
|
|
|
|
|
|
sendGmail(
|
2012-10-29 09:56:17 +00:00
|
|
|
|
subject: "[FileBot] ${emailTitle}",
|
2012-07-30 16:59:09 +00:00
|
|
|
|
message: XML {
|
|
|
|
|
html {
|
|
|
|
|
body {
|
2012-10-29 09:56:17 +00:00
|
|
|
|
p("FileBot finished processing ${emailTitle} (${renameLog.size()} files).");
|
2012-07-30 16:59:09 +00:00
|
|
|
|
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 ->
|
2012-08-12 04:38:14 +00:00
|
|
|
|
tr { [from.name, to.name, to.parent].each{ cell -> td{ nobr{ code(cell) } } } }
|
2012-07-30 16:59:09 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
hr(); small("// Generated by ${net.sourceforge.filebot.Settings.applicationIdentifier} on ${new Date().dateString} at ${new Date().timeString}")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
2013-02-05 15:44:18 +00:00
|
|
|
|
messagemimetype: 'text/html',
|
2012-07-31 07:46:33 +00:00
|
|
|
|
to: tryQuietly{ mailto } ?: gmail[0] + '@gmail.com', // mail to self by default
|
2012-07-30 16:59:09 +00:00
|
|
|
|
user: gmail[0], password: gmail[1]
|
|
|
|
|
)
|
|
|
|
|
}
|
2013-01-05 04:25:21 +00:00
|
|
|
|
|
2013-11-24 13:47:22 +00:00
|
|
|
|
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()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-05 04:25:21 +00:00
|
|
|
|
// clean empty folders, clutter files, etc after move
|
|
|
|
|
if (clean) {
|
2013-03-06 08:34:43 +00:00
|
|
|
|
if (['COPY', 'HARDLINK'].find{ it.equalsIgnoreCase(_args.action) } && tempFiles.size() > 0) {
|
|
|
|
|
_log.info 'Clean temporary extracted files'
|
2013-01-29 10:32:48 +00:00
|
|
|
|
// delete extracted files
|
2013-04-09 09:12:20 +00:00
|
|
|
|
tempFiles.findAll{ it.isFile() }.sort().each{
|
|
|
|
|
_log.finest "Delete $it"
|
|
|
|
|
it.delete()
|
2013-01-29 10:32:48 +00:00
|
|
|
|
}
|
|
|
|
|
// delete remaining empty folders
|
2013-04-09 09:12:20 +00:00
|
|
|
|
tempFiles.findAll{ it.isDirectory() }.sort().reverse().each{
|
|
|
|
|
_log.finest "Delete $it"
|
|
|
|
|
if (it.getFiles().isEmpty()) it.deleteDir()
|
2013-01-29 10:32:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// deleting remaining files only makes sense after moving files
|
|
|
|
|
if ('MOVE'.equalsIgnoreCase(_args.action)) {
|
2013-11-10 06:15:28 +00:00
|
|
|
|
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'
|
2013-11-23 04:32:09 +00:00
|
|
|
|
executeScript('fn:cleaner', args.empty ? [root:true] : [root:false], cleanerInput)
|
2013-11-10 06:15:28 +00:00
|
|
|
|
}
|
2013-01-29 10:32:48 +00:00
|
|
|
|
}
|
2013-01-05 04:25:21 +00:00
|
|
|
|
}
|