[DOCS] Custom Post-Processing Scripts

Running FileBot from the console, Groovy scripting, shell scripts, etc
Post Reply
User avatar
rednoah
The Source
Posts: 23419
Joined: 16 Nov 2011, 08:59
Location: Taipei
Contact:

[DOCS] Custom Post-Processing Scripts

Post by rednoah »

Add Post-Processing Scripts via Rename Settings

You can add custom post-processing scripts via Icon Renaming SettingsIcon Post ProcessIcon New Script.

ScreenshotScreenshotScreenshot


:idea: Use Right-ClickRun or CTRL+R to do a test run and check debug output:

ScreenshotScreenshot




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.groovy




e.g. Run Command

Run Command after processing:

Groovy: Select all

system 'say', '-v', 'Trinoids', 'Rename Complete'
Run Command after processing and pass along all target file paths via $@ at once:

Groovy: Select all

system '/path/to/script.sh', *args
Run Command for each newly processed file, passing the source file path and target file path as $1 and $2 respectively:

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. Set xattr metadata on source files

Set xattr metadata on the source file as well:

Groovy: Select all

{ source, target, metadata ->
	source.metadata = metadata
}

e.g. Add subtitles in multiple languages

Fetch subtitles in multiple languages via Exact Search if possible or Fuzzy Search if necessary:

Groovy: Select all

{ source, target ->
	getSubtitles target, 'en', 'de', 'fr', strict:false
}

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 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'
	}
}

e.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

:idea: Please read the FAQ and How to Request Help.
User avatar
rednoah
The Source
Posts: 23419
Joined: 16 Nov 2011, 08:59
Location: Taipei
Contact:

How about sharing your post-processing scripts?

Post by rednoah »

⭑⭑ Please share your scripts, or ideas for scripts, so that we may grow the list of example post-processing scripts ready for copy & paste. ⭑⭑
:idea: Please read the FAQ and How to Request Help.
Post Reply