Add Post-Processing Scripts via Rename Settings
You can add custom post-processing scripts via
Renaming Settings ➔
Post Process ➔
New Script.




Add Post-Processing Scripts via the --apply option from Terminal
--apply can be added to -rename, -find and -mediainfo commands to run your custom code on newly renamed files or previously renamed files.Shell: Select all
--apply '{ source, target, metadata -> println "$source | $target | $metadata" }'Shell: Select all
--apply /path/to/apply.groovySome Examples
- Hello World
- Run Command
- Refresh Plex
- Refresh Plex (Partial Scan)
- Refresh Emby / Jellyfin
- Refresh Kodi
- Refresh Radarr
- Refresh Sonarr
- Notify via Discord
- Notify via Mattermost
- Notify via ntfy
- Notify via Pushover
- Send custom HTML report via Email
- Add YouTube trailer link
- Add subtitles in multiple languages
- Move subtitle files to a subs folder
- Delete duplicates
- Delete clutter files
- Fetch Movie Artwork Collection
- Fetch Series Artwork Collection
- Generate custom NFO files
- Generate custom Finder tags
- Generate .plexmatch files
- Generate SFV files
- Copy xattr metadata to companion files
- Set xattr metadata on source files
- Set Finder Comments
- Set Media Title
- Relocate files in Radarr / Sonarr
- Relocate files in qBT
- Merge video and external subtitles
- Delete ._* metadata files
e.g. Run Command
Run Command after processing:Groovy: Select all
system 'say', '-v', 'Trinoids', 'Rename Complete'
Groovy: Select all
system '/path/to/script.sh', *args
Groovy: Select all
{ source, target ->
system '/path/to/script.sh', source, target
}
e.g. Refresh Plex
Refresh Plex via a HTTP request with X-Plex-Token authentication:Groovy: Select all
def host = '127.0.0.1'
def auth = 'YOUR_PLEX_TOKEN'
curl "http://${host}:32400/library/sections/all/refresh?X-Plex-Token=${auth}"
e.g. Refresh Plex (Partial Scan)
Refresh Plex via dedicated HTTP requests for each library and target folder path:Groovy: Select all
def host = '127.0.0.1'
def auth = 'YOUR_PLEX_TOKEN'
def sections = "http://${host}:32400/library/sections"
def header = ['X-Plex-Token': auth]
// request library sections
def libraryRoot = [:].withDefault{ [] }
curl(header, sections).'Directory'.'Location'.collect{ location ->
def key = location.'..'.'@key'.text()
def path = location.'@path'.text()
def root = path.split(/[\\\/]/).last()
libraryRoot[root] += [key: key, path: path]
}
// guess remote file path
def requests = [] as Set
args.collect{ f -> f.dir.path.split(/[\\\/]/).tail() }.unique().each{ components ->
components.eachWithIndex{ c, i ->
libraryRoot[c].each{ r ->
if (i < components.size() - 1) {
requests += [section: r.key, path: [r.path, *components[i+1..-1]].join('/')]
} else {
requests += [section: r.key, path: r.path]
}
}
}
}
// send refresh requests
requests.each{ r ->
curl(header, "${sections}/${r.section}/refresh".toURL(path: r.path))
}
e.g. Refresh Emby / Jellyfin
Refresh Emby / Jellyfin via a HTTP request:Groovy: Select all
def host = '127.0.0.1'
def auth = 'YOUR_API_KEY'
curl "http://${host}:8096/Library/Refresh?api_key=${auth}", [:]
e.g. Refresh Kodi
Refresh Kodi via a HTTP request:Groovy: Select all
def host = '127.0.0.1'
def port = 8080
curl "http://${host}:${port}/jsonrpc", [jsonrpc: '2.0', method: 'VideoLibrary.Scan', id: 1]
e.g. Refresh Radarr
Refresh Radarr via HTTP requests:Groovy: Select all
def host = '127.0.0.1'
def port = 8310
def auth = 'YOUR_API_KEY'
def ids = model.findAll{ it.type =~ /Movie/ }.findResults{ it.tmdbId } as Set
ids.each{ id ->
def r = curl "http://${host}:${port}/api/v3/movie?tmdbId=${id}", 'X-Api-Key': auth
r.each{ m ->
curl "http://${host}:${port}/api/v3/command", [name: 'rescanMovie', movieId: m.id], 'X-Api-Key': auth
}
}
e.g. Refresh Sonarr
Refresh Sonarr via HTTP requests:Groovy: Select all
def host = '127.0.0.1'
def port = 8989
def auth = 'YOUR_API_KEY'
def ids = model.findAll{ it.type =~ /Episode/ }.findResults{ it.tvdbId } as Set
ids.each{ id ->
def r = curl "http://${host}:${port}/api/v3/series?tvdbId=${id}", 'X-Api-Key': auth
r.each{ s ->
curl "http://${host}:${port}/api/v3/command", [name: 'rescanSeries', seriesId: s.id], 'X-Api-Key': auth
}
}
e.g. Notify via Discord
Send alerts to a Discord channel via Server Settings ➔ Integrations ➔ Webhooks:Groovy: Select all
{ source, target ->
curl 'https://discord.com/api/webhooks/YOUR_WEBHOOK', [content: """```[${action}] from [${source.name}] to [${target.name}]```"""]
}e.g. Notify via Mattermost
Send alerts to a Mattermost channel via Incoming Webhooks:Groovy: Select all
{ source, target ->
curl 'http://YOUR_SITE/hooks/YOUR_WEBHOOK', [text: """```[${action}] from [${source.name}] to [${target.name}]```"""]
}e.g. Notify via ntfy
Send messages to a ntfy.sh topic:Groovy: Select all
{ source, target ->
curl 'https://ntfy.sh/YOUR_TOPIC', """[${action}] from [${source.name}] to [${target.name}]""", 'X-Priority':'low', 'X-Tags':'white_check_mark'
}e.g. Notify via Pushover
Send Pushover notifications:
Groovy: Select all
submit 'https://api.pushover.net/1/messages.json', [
token: '<YOUR APPLICATION TOKEN>',
user: '<YOUR USER KEY>',
html: 1,
title: 'FileBot has processed ' + model.size() + ' file(s)',
message: XML{
model.each{ source, target, metadata ->
p{
b(type.match('Episode': '📺 ', 'Movie': '🎬 ')){
a(href: type.match('Episode': "https://www.themoviedb.org/tv/$id", 'Movie': "https://www.themoviedb.org/movie/$id"), target.nameWithoutExtension)
}
br(); span("☆ $rating • $vf • $acf • $bytes • $hours")
br(); span(source.name)
}
}
}.removeAll(/\R/)
]
e.g. Add YouTube trailer link
Create a trailer.url (i.e. InternetShortcut file) that links to the trailer on YouTube in each movie folder:Groovy: Select all
{ source, target ->
def extra = movie.extras.find{ it.type == /Trailer/ && it.site == /YouTube/ }
if (extra) {
INI(target.dir / 'trailer.url') {
InternetShortcut {
URL "https://www.youtube.com/watch?v=${extra.key}"
}
}
}
}e.g. Add subtitles in multiple languages
Fetch normal subtitles (excluding forced and HI subtitles) in multiple languages via Exact Search if possible or Fuzzy Search if necessary, but only if the file at hand doesn't already have embedded subtitles:Groovy: Select all
{ source, target ->
if (!textLanguages) {
getSubtitles target, 'en', 'de', 'fr', strict: false, filter: { !it.forced && !it.HI }
}
}e.g. Move subtitle files to a dedicated subs folder
Move subtitle files to a dedicated subs/ folder:Groovy: Select all
{ source, target ->
if (target.video) {
target.dir.listFiles().each{
if (it.subtitle && it.isDerived(target)) {
it.move(target.dir / 'subs' / it.name)
}
}
}
}
e.g. Delete duplicates
Delete logical duplicates that are of lower quality from the target folder if the newly added file is of better quality:Groovy: Select all
{ source, target, metadata ->
target.dir.listFiles{ it.video && it != target && it.metadata == metadata }.each{
if (target.isBetter(it)) {
trash(it)
} else {
reveal(it)
}
}
}
e.g. Delete clutter files
Delete *.txt files and *.nfo files from the source folder:Groovy: Select all
{ source, target ->
trash source.dir.listFiles{ it.extension ==~ /txt|nfo/ }
}
e.g. Set xattr metadata on source files
Set xattr metadata on the source file as well:Groovy: Select all
{ source, target, metadata ->
source.metadata = metadata
}Groovy: Select all
{ source, target, metadata ->
json.saveAs(source.dir / '.xattr' / f.name / 'net.filebot.metadata')
}e.g. Set Finder Comments
Run osascript to set the Finder Comments to the original file name:Groovy: Select all
{ source, target ->
// reveal file in Finder
system 'open', '-R', target
// set Finder Comment
system 'osascript', '-e', """
tell app "Finder"
set f to (POSIX file "$target" as alias)
set c to "$original"
set comment of f to c
end tell
"""
}e.g. Set Media Title
Run mkvpropedit or AtomicParsley to set the media title property to the original file name:Groovy: Select all
{ source, target ->
if (ext == /mkv/) {
system 'mkvpropedit', target, '--edit', 'info', '--set', "title=$original"
}
if (ext == /mp4/) {
system 'AtomicParsley', target, '--title', original, '--overWrite'
}
}Groovy: Select all
system 'filebot', '-script', 'fn:tags', *argse.g. Hello World
The Hello World example that will get you started with any possible custom use case:Groovy: Select all
// RUN ONCE
{
println "args = $args" // args ... list of target files
println "model.n = $model.n" // model.* ... any binding for all matches
println "model.f = $model.f" // ⋮
println "action = $action" // action ... move, copy, etc
}
// RUN ONCE FOR EACH FILE
{ source, target, metadata ->
println "source = $source" // source ... source file
println "target = $target" // target ... target file
println "metadata = $metadata" // metadata ... movie, episode, etc
println "n = $n" // * ... any binding for this match
println "type = $type" // ⋮
println "f = $f" // ⋮
println "ext = $ext" // ⋮
}
Additional Examples and Use Cases
- Fetch Movie Artwork Collection
- Fetch Series Artwork Collection
- Generate custom NFO files
- Generate custom Finder tags
- Generate .plexmatch files
- Copy xattr metadata to companion files
- Relocate files in Radarr / Sonarr
- Relocate files in qBT
- Set Jellyfin DateCreated property
- Merge video and external subtitles
- Delete ._* metadata files
- Send custom HTML report via Email