It's not complete by any means, but...

Running FileBot from the console, Groovy scripting, shell scripts, etc
Post Reply
User avatar
Wolfie
Posts: 153
Joined: 27 Oct 2015, 02:59

It's not complete by any means, but...

Post by Wolfie »

I thought I would share my episode naming script.

Shell: Select all

{ 
	// test comment line
	/* test comment line */
	def maps = include "P:/FileBot/config.groovy"
	def map = maps['DEFAULTS'] + maps['TV'], root = any{map['root']}{root}{}
	def groups = allOf{map['shows']}{map['groups'].join('|')}{anime}.join('|')
	def slashes = /(?:\\|\/)/, tmdbid = any{tmdbid}{"XXX"}, tvdbid = any{tvdbid}{"XXX"}, imdbid = any{imdbid}{"ttXXX"}
	def folders = map['folders'], anime = (any{anime}{} ? (map['anime'] ? map['anime'] : null) :null)
	def mediaID = any{"{imdb=${imdbid}}"}{"{tmdb=${tmdbid}}"}{"{tvdb=${tvdbid}}"}{}
	def talkshows = (map['talkshows'] !== true ? map['talkshows'] : 'TALK' ) 
	def franchises = map['franchises'], franchise = any{getMatch(franchises)}{}, fap = getMatch(map.faps)
	
	
	rlRegex=/(tmdb${tmdbid}|tvdb${tvdbid}|${imdbid})([^0-9]|$)/
	rlRegex=/(tmdb${tmdbid}|tvdb${tvdbid}|${imdbid})/
	def regex = [
		'root':		/^([a-z]:\\?|\/)/,
		'folders':	folders.values().join('|'),
		'keys':		folders.keySet().join('|'),
	]
	
	
	// Functions
	def theValue(k,p,r) { if (fn =~ r || n =~ r || /^${imdbid}$/ =~r || /^tmdb${tmdbid}$/ =~r || /^tvdb${tvdbid}$/ =~r ) return [group:k, path:p, regex:r, IDs:[imdb:imdbid,tmdb:tmdbid,tvdb:tvdbid] ] }
	def getMatch(array) { array.findResult{ k, v -> v.findResult{ p="", a -> (any{a.length()}{} ? ( theValue((k),(p),a) ) : a.findResult{ r -> return theValue((k),(p),r) } ) } } }
	def getRoot(root, path) { return ( any{path.match(/^([a-z]:|\/)/)}{} ? path : root ) }
	def rootPath(root, path) { return ( any{path.match(/^([a-z]:|\/)/)}{} ? path : "${root}/${path}" ) }
	def validateName(name) { return name.colon(' - ').replaceAll('/','; ').replaceAll(/\.\.\.+/,'…').validateFileName() }
	def validatePath(name) { return name.colon(' - ').replaceAll(/\.\.\.+/,'…').validateFileName() }
	def customFind(map) { return map.find{k,v -> fn=~v} }
	def fpm(path,regex) { return path.match(/(?<=\\|\/)(${regex})(?=\\|\/)/) }
	
	def readLines(file) { 
		def col = /(tt|tmdb|tvdb)[0-9]+/
		def one = any{lines(config+'/'+file).find{ it =~ "^${rlRegex}" }.after(/\t/).before(/[#\t*]/)}{}
		def two = any{lines(config+'/'+file).find{ it =~ rlRegex }.before(col).before(/\t/)}{}
		return any{one}{two}{}
		return "(( ${file} || ${imdbid} || ${rlRegex} || ${one} || ${two} ))"
	}
	
	
	def original=any{original}{fn}, source=any{source.upper()}{}, ac=any{ac.match(/^.{1,6}$/)}{}, vc1=any{vc}{}
	def dir=any{readLines(map['collections']).find{ it =~ findRegex }.after(/^(tt|tmdb|tvdb)\d+\s+/).before(/[#\t*]/)}{}
	def year=any{readLines(map['overrides'])}{y}{d.year}{episodelist[0].airdate.year}{'0000'}
	def vc=any{video[0].codec.tokenize('/')[0].lower().match(/^v_mpeg.$/).replaceAll(/^v_mpeg4$/,'x264').replaceAll(/^v_mpegh$/,'HEVC')}{vc}{}
	def s=any{s}{file.path.match(/s\d+e\d+[-. ]/).match(/(?<=s)\d+/)}{00}, s00e00=any{s00e00.match(/S\d+E\d+.*/)}{"S${s.pad(2)}E${e.pad(2)}"}{}
	def name=n.removeAll(/\s*\((00|19|20)\d\d\)\s*$/).colon(' - ').validateFileName(), show="${name} (${year})", ep=s00e00.lower()
	def group=any{group}{"${any{original}{fn}}".match(/(?:-)(<?=\.[a-zA-Z\d]{1,15})([a-zA-Z\d]{1,15})(\.\[[a-z\d]{8}\])?$/)}{}
	def meta=allOf{allOf{original.upper().matchAll(/(?<=\.)(internal|multi|proper|repack)(?=.)/).unique().join('.')}{orig}{source}{vf}{vc}{ac}.join('.')}{group}.join('-')
	def title=allOf{"${episode}".after(/${n}/).after(/-[\dx& ]*/).after(/(Special [\d ]*)?[ -]*/).replaceAll('/','; ').replaceAll(/\.\.\.+/,'…')}{meta}.join('.')
	
	def dest=any{fap.path.match(regex['root'])}{folders[fpm(file.path,regex['keys'])]}{fpm(file.path,regex['folders'])}{folders[map['dd']]}{map['dd']}, full=any{dest.match(regex['root'])}{}
	def TV=(any{fap.path}{} ? fap.path: any{franchise}{file.path.match(/${root}${slashes}(?:${regex['folders']}|{$groups})${slashes}/)}{readLines(map['collections'])}{anime?'Anime':map['shows']} )
	
	//return allOf{0}{dest}{1}{regex['folders']}{2}{}{3}{file.path}{4}{file.path.match(/(?<=\\|\/)(${regex['keys']})(?=\\|\/)/)}{5}
	
	def path=[
		root:	allOf{full?"":root}{dest}.join('/'),
		tv:		TV,
		folder:	allOf{any{dir}{show}}{mediaID}.join(' '),
		season:	"Season ${(episode.special || episode.special == 0 ? '00':s.pad(2))}",
		name:	allOf{allOf{show}{ep}{title}.join(' - ')}{".[${crc32}]"}{subt}.join('').colon(' - ').validateFileName()
	]
	
	if (any{genres.toString().match(/(?i)Talk ?Shows?/)}{file.path.match(/(?i)(\\(_ )?TALK(SHOWS?)?\\)/)})
		{ path['season']=airdate.format('yyyy-MM MMMM'); ep=airdate; path['tv']=(talkshows?"TALK":path['tv']); show=name.removeAll(/^The /); path['folder']=any{dir}{show}  }
	
	return path.values().join('/')
	
}
And the 'config.groovy' that goes with it (so far, still a WIP (work in progress)...

Groovy: Select all

[
	"DEFAULTS":[
		"root":"P:/",
		"dd":"08",
		"collections":"collections.txt",
		"overrides":"overrides.txt",
		"folders":[
			"01":"_01",
			"02":"_02",
			"03":"_03",
			"04":"_04",
			"05":"_05",
			"06":"_06",
			"08":"_08",
			"E1":"_E1",
			"E2":"_E2",
			"E3":"_E3",
			"E4":"_E4",
			"26":"_E4",
		],
	],

	"TV":[
		"shows": "TV",
		"talkshows":true,
		"anime":false,
		"franchises":[
			"DC": [
				"Superman .+",
				"Lois and Clark.+",
			],
			"StarTrek": "^1Star ?Trek|tt0112178|74550",
			"StarWars": "^(Star ?Wars|(The ?)?Mandelorian|Obi-?Wan ?Kenobi)",
		],

	//	FAPS = File Alternate Path Structure
	// 	Fapping let's you choose destinations based on filename or title association.
	//  
	//	Formats:
	//		"faps" = [group:[path:"single regex query"]]
	//		"faps" = [group:[path:[multiple regex queries, comma separated]]]
	//	Group is a title to identify what the paths/patters are for.
	//	Path is alternative path (relative or static) to use.
	//	Regex to pattern to match.  Filename, IMDb (tt prefixed), and TVDb (tvdb prefixed) supported.
	//
	//  Note:  Starting the path with a letter/colon, or with a forward slash, will make it the static path instead of a relative one.
	//         This will override the value used for "shows" above.
	//	
		"faps":[
			"Marvel":[
				'Marvel/': /^(The Amazing Spider-Man)/,
			],
			"DC":[
				'DC/': [
					"^(Arrow|tt2193021)", "^(The Flash|tt0098798|tt3107288)", "^(DC'?s Legends of Tomorrow|tt4532368)",
					"^(Batwoman|tt8712204)", "^(Supergirl|tt4016454)", "^(Black Lightning|tt6045840)",
					"^(Superman ?(and|&) ?Lois|tt11192306)",
				]
			],
		],
		"groups":[
			"DC(shows)?",
			"Marvel(TV)?",
			"Star ?Trek",
		],
	],

	"MOVIES":[
		"dd":"06",
		"films": "MOVIES",
		"collectionsFolder":"_",
		"editions":[
			//	IMDbID || TMDbID
			//  ID: [ mins:edition name ]
			"tt0078346":[
				"188": "Extended",
				"151": "Special",
			],
			"tt2975590":[
				"182": "Ultimate",
				"151": "Theatrical",
			],
		],
	]
]
Post Reply