diff --git a/build.xml b/build.xml index 72e02c0f..de98ba49 100644 --- a/build.xml +++ b/build.xml @@ -656,15 +656,6 @@ - - - - - - - - - diff --git a/website/scripts/.htaccess b/website/scripts/.htaccess deleted file mode 100644 index e6fa4a5f..00000000 --- a/website/scripts/.htaccess +++ /dev/null @@ -1,3 +0,0 @@ -options +indexes - -redirect 301 /scripts/utorrent-postprocess.groovy /scripts/amc.groovy \ No newline at end of file diff --git a/website/scripts/amc.groovy b/website/scripts/amc.groovy deleted file mode 100644 index e306a19e..00000000 --- a/website/scripts/amc.groovy +++ /dev/null @@ -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) - } - } -} diff --git a/website/scripts/artwork.tmdb.groovy b/website/scripts/artwork.tmdb.groovy deleted file mode 100644 index 73f38af4..00000000 --- a/website/scripts/artwork.tmdb.groovy +++ /dev/null @@ -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}" - } -} diff --git a/website/scripts/artwork.tvdb.groovy b/website/scripts/artwork.tvdb.groovy deleted file mode 100644 index eb1b187a..00000000 --- a/website/scripts/artwork.tvdb.groovy +++ /dev/null @@ -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) -} diff --git a/website/scripts/chkall.groovy b/website/scripts/chkall.groovy deleted file mode 100644 index c56e181a..00000000 --- a/website/scripts/chkall.groovy +++ /dev/null @@ -1,9 +0,0 @@ -// filebot -script fn:chkall - -/* - * 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*") -} diff --git a/website/scripts/cleaner.groovy b/website/scripts/cleaner.groovy deleted file mode 100644 index 37c6b122..00000000 --- a/website/scripts/cleaner.groovy +++ /dev/null @@ -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) } } diff --git a/website/scripts/configure.login.groovy b/website/scripts/configure.login.groovy deleted file mode 100644 index 8d93c785..00000000 --- a/website/scripts/configure.login.groovy +++ /dev/null @@ -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(':')) -} diff --git a/website/scripts/escape.groovy b/website/scripts/escape.groovy deleted file mode 100644 index a01d009c..00000000 --- a/website/scripts/escape.groovy +++ /dev/null @@ -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 -} diff --git a/website/scripts/extract.groovy b/website/scripts/extract.groovy deleted file mode 100644 index 7995dadb..00000000 --- a/website/scripts/extract.groovy +++ /dev/null @@ -1,10 +0,0 @@ -// filebot -script fn:extract - -/* - * Auto-extract all zip and rar archives. - */ -args.getFiles{ it.isArchive() }.each { - def output = extract(file:it) - - output.each{ println "Extracted: " + it.path } -} diff --git a/website/scripts/history.groovy b/website/scripts/history.groovy deleted file mode 100644 index d6ef2b11..00000000 --- a/website/scripts/history.groovy +++ /dev/null @@ -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) -} diff --git a/website/scripts/housekeeping.groovy b/website/scripts/housekeeping.groovy deleted file mode 100644 index c24f343a..00000000 --- a/website/scripts/housekeeping.groovy +++ /dev/null @@ -1,33 +0,0 @@ -// filebot -script fn:housekeeping /path/to/folder/ --output /output/folder/ --format - -/* - * 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 diff --git a/website/scripts/lib/ant.groovy b/website/scripts/lib/ant.groovy deleted file mode 100644 index 82c05802..00000000 --- a/website/scripts/lib/ant.groovy +++ /dev/null @@ -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() -} diff --git a/website/scripts/lib/htpc.groovy b/website/scripts/lib/htpc.groovy deleted file mode 100644 index 20f2bdff..00000000 --- a/website/scripts/lib/htpc.groovy +++ /dev/null @@ -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) - } - } -} diff --git a/website/scripts/lib/scraper.groovy b/website/scripts/lib/scraper.groovy deleted file mode 100644 index 76fd8f23..00000000 --- a/website/scripts/lib/scraper.groovy +++ /dev/null @@ -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}") - } - -} diff --git a/website/scripts/lib/ws.groovy b/website/scripts/lib/ws.groovy deleted file mode 100644 index dc0bcae4..00000000 --- a/website/scripts/lib/ws.groovy +++ /dev/null @@ -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 - } -} diff --git a/website/scripts/mi.groovy b/website/scripts/mi.groovy deleted file mode 100644 index 7e2c9f80..00000000 --- a/website/scripts/mi.groovy +++ /dev/null @@ -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) - } -} diff --git a/website/scripts/renall.groovy b/website/scripts/renall.groovy deleted file mode 100644 index 2fb1b8f1..00000000 --- a/website/scripts/renall.groovy +++ /dev/null @@ -1,16 +0,0 @@ -// filebot -script fn:renall --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 - } -} diff --git a/website/scripts/replace.groovy b/website/scripts/replace.groovy deleted file mode 100644 index 6a1ea021..00000000 --- a/website/scripts/replace.groovy +++ /dev/null @@ -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" - } - } - } -} diff --git a/website/scripts/revert.groovy b/website/scripts/revert.groovy deleted file mode 100644 index 8ab55fc0..00000000 --- a/website/scripts/revert.groovy +++ /dev/null @@ -1,24 +0,0 @@ -// filebot -script fn:revert - - -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) -} diff --git a/website/scripts/sortivo.groovy b/website/scripts/sortivo.groovy deleted file mode 100644 index b4457682..00000000 --- a/website/scripts/sortivo.groovy +++ /dev/null @@ -1,50 +0,0 @@ -// filebot -script fn:sortivo --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') - } -} diff --git a/website/scripts/sorty.groovy b/website/scripts/sorty.groovy deleted file mode 100644 index 5eca93dc..00000000 --- a/website/scripts/sorty.groovy +++ /dev/null @@ -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) - } -} diff --git a/website/scripts/src.groovy b/website/scripts/src.groovy deleted file mode 100644 index 1f832ff7..00000000 --- a/website/scripts/src.groovy +++ /dev/null @@ -1,13 +0,0 @@ -// filebot -script fn:src - -/* - * 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() }) -} diff --git a/website/scripts/suball.groovy b/website/scripts/suball.groovy deleted file mode 100644 index 5cc54242..00000000 --- a/website/scripts/suball.groovy +++ /dev/null @@ -1,8 +0,0 @@ -// filebot -script fn:suball - -/* - * Get subtitles for all your media files - */ -args.eachMediaFolder { - getMissingSubtitles(folder:it) -} diff --git a/website/scripts/sysenv.groovy b/website/scripts/sysenv.groovy deleted file mode 100644 index ba9a860c..00000000 --- a/website/scripts/sysenv.groovy +++ /dev/null @@ -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" -} diff --git a/website/scripts/sysinfo.groovy b/website/scripts/sysinfo.groovy deleted file mode 100644 index 08d7d3d4..00000000 --- a/website/scripts/sysinfo.groovy +++ /dev/null @@ -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 -} diff --git a/website/scripts/update-mes.groovy b/website/scripts/update-mes.groovy deleted file mode 100644 index e9b2a07f..00000000 --- a/website/scripts/update-mes.groovy +++ /dev/null @@ -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(/(? - 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]" - } - } -} diff --git a/website/scripts/watcher.groovy b/website/scripts/watcher.groovy deleted file mode 100644 index 74047770..00000000 --- a/website/scripts/watcher.groovy +++ /dev/null @@ -1,22 +0,0 @@ -// filebot -script fn:watcher /path/to/folder/ --output /output/folder/ --format - -// 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 \ No newline at end of file diff --git a/website/scripts/xattr.groovy b/website/scripts/xattr.groovy deleted file mode 100644 index 0cf4b6ee..00000000 --- a/website/scripts/xattr.groovy +++ /dev/null @@ -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 ***' - } -}