Page 1 of 2
Where do I find all the documentation?
Posted: 06 Aug 2016, 00:30
by Wolfie
Okay, been trying to figure this out to no avail (obviously)...
I have a subfolder where all "small movies" go into. Basically, anything with an actual run time of under an hour (60 minutes), so that things like TV specials or just one-off shows/etc can automatically go into there. The problem is when there is a movie that is broken into multiple parts and one or more of those parts are under 60 minutes. I've tried a few different ways that either don't work at all, or work one way but not the other. Here are a couple of examples of what I've tried...
Code: Select all
{if (minutes < 60 && ! pi) (code)}
{if (minutes < 60 && (any{pi}{y} != {y})) (code)}
The overall code for the entire naming is much bigger, but that's the one area I'm having an issue with. I've tried comparing 'pi' to null/undefined, negating it, so on. I more or less need a to test if it's undefined as well as it having a value. I'm sure there's a way to do it that I don't know of, since there appears to be many 'hidden' expressions/functions available that don't seem to be all on one page. (The 'expressions' help page lists some common ones but there are a lot of things not listed on that page.)
So, how can I get a true result for 'pi' being undefined and where can I find out about more functions that are seemingly like easter eggs to find and use?
Re: Trouble with an 'if' condition (minutes/parts)
Posted: 06 Aug 2016, 02:34
by Wolfie
Okay managed to figure it out though it's odd that other similar attempts failed.
Code: Select all
{if ((any{pn}{"X"} == "X" && minutes < 60)) (code)}
Re: Trouble with an 'if' condition (minutes/parts)
Posted: 06 Aug 2016, 05:39
by rednoah
1.
{minutes} is based on the duration MediaInfo of a given file. Multi-Part movies are tricky, but let's just assume that
{pi} is working and that it's only defined for multi-part movies, and throw an exception when it's not defined.
@see
viewtopic.php?f=5&t=1895
2.
The "correct" solution is probably one that combines all the
{minutes} for all model objects that are the same movie:
Code: Select all
{model.findAll{it.id == id}.minutes.sum()}
You could also use the movie runtime (from TheMovieDB, not based on file content) which is probably what you want here:
3.
The built-in help should contain everything you need:
You can click the
source link to see the code (and thus every single binding and it's implementation):
http://www.filebot.net/naming.html
Re: Trouble with an 'if' condition (minutes/parts)
Posted: 06 Aug 2016, 06:34
by Wolfie
rednoah wrote:1.
{minutes} is based on the duration MediaInfo of a given file. Multi-Part movies are tricky, but let's just assume that
{pi} is working and that it's only defined for multi-part movies, and throw an exception when it's not defined.
@see
viewtopic.php?f=5&t=1895
That's actually why I prefer to use 'minutes,' as it gives the actual duration of the video itself vs durations that may include time for commercials. I've seen ones where the reported time is 60 minutes but the time of the video is like 45. I'm fairly certain that the 15 minute difference is for the commercials shown when it originally aired on TV.
Is there a way to compare for an exception? In this instance, I managed to figure something out using the any{}{} function, but checking using an 'if' might be useful at times too.
rednoah wrote:2.
The "correct" solution is probably one that combines all the
{minutes} for all model objects that are the same movie:
Code: Select all
{model.findAll{it.id == id}.minutes.sum()}
You could also use the movie runtime (from TheMovieDB, not based on file content) which is probably what you want here:
I tried using 'info.runtime' but for some reason, it wouldn't let me compare when an integer and when I tried with a string, it was unreliable.
Thanks for the reply. I'll check out the links you provided.
Re: Trouble with an 'if' condition (minutes/parts)
Posted: 06 Aug 2016, 06:51
by Wolfie
rednoah wrote:1.
{minutes} is based on the duration MediaInfo of a given file. Multi-Part movies are tricky, but let's just assume that
{pi} is working and that it's only defined for multi-part movies, and throw an exception when it's not defined.
@see
viewtopic.php?f=5&t=1895
Just to clarify, {pi} is working when there is a number. The issue was in finding a way to check if there wasn't a number.
rednoah wrote:3.
The built-in help should contain everything you need:
You can click the
source link to see the code (and thus every single binding and it's implementation):
http://www.filebot.net/naming.html
Both of those links I had already seen. The problem is, the 'naming.html' one doesn't show use of the any{}{} function and neither show the use of if-then-else. There are also many other functions not shown that apparently can be used. So was hoping there was a link to somewhere with a more comprehension list to look at.
Re: Trouble with an 'if' condition (minutes/parts)
Posted: 06 Aug 2016, 07:32
by rednoah
1.
You don't need to check, because
pi itself will unwind the expression if it's undefined:
Code: Select all
{pi; 'This is a multi-part movie'}
Code: Select all
{any{' CD'+pi}{' (Single File Movie)'}}
With
any you can avoid most of the
if-then-else bloat. But if you really want "value of pi or 0 if pi is undefined", then
any{pi}{0} will do that.
e.g.
Code: Select all
any {code that uses pi and fails if pi is undefined} {code for when pi is undefined} {code for the next fallback} ...
2.
The
any and
allOf functions are documented
here. Then there's
csv and
readLines and that's pretty much it. There's a link to the
source at the bottom.
The rest is just Groovy:
Syntax,
Operators,
Closures,
Semantics and of course the entire
Java API plus
Groovy Enhancements if you need a complete Object/Method reference.
That's why it's best to copy & paste examples and play with it. The comprehensive list of 10.000+ classes and 100.000+ methods is just a bit overwhelming when you just need
String.replace(target, replacement).

Re: Trouble with an 'if' condition (minutes/parts)
Posted: 06 Aug 2016, 10:02
by Wolfie
What about 'File()'? I see where someone used that and would like to read up on it. Where would that be at?
Re: Trouble with an 'if' condition (minutes/parts)
Posted: 06 Aug 2016, 10:13
by rednoah
This one is probably about the File class of the Java API:
https://docs.oracle.com/javase/8/docs/a ... /File.html
e.g. get current working directory:
e.g. if you want to check if a path exists you might create a
File object for the given path so you can then call the
File.exists() method. I don't think there's any good use case for
new File(...) in FileBot format expressions though.
Note that
{file} will also give you a
File object (and not a
String object).
Re: Trouble with an 'if' condition (minutes/parts)
Posted: 06 Aug 2016, 10:20
by Wolfie
Re: Trouble with an 'if' condition (minutes/parts)
Posted: 06 Aug 2016, 10:27
by Wolfie
Oh and what about things similar to {home}? Is there a list of defined values (such as {home} and others) in one of those links?
Re: Trouble with an 'if' condition (minutes/parts)
Posted: 06 Aug 2016, 10:36
by rednoah
This code is about reading CSV files.
Groovy defines
File.splitEachLine which is great for reading CSV files.
SyAccursed knows Groovy so he just did it the Groovy way.
FileBot has the
csv(String path) function built-in:
I recommend doing it the easy way. But if you know Java/Groovy you can do it a million different ways.
@see
viewtopic.php?f=5&t=2#p12156
Re: Trouble with an 'if' condition (minutes/parts)
Posted: 06 Aug 2016, 10:43
by rednoah
{home} is part of the built-in examples. Works the same as any of the other object/mediainfo bindings, except it's always the same regardless of what object/file match you're formatting.
{self} will self-reference the binding object, so the default String representation will show you all binding names.
If you click
source you will find the implementation details of all bindings:
https://github.com/filebot/filebot/blob ... .java#L897
PS: Note that
these 1000+ character formats are extreme overkill and nobody (including the original author) is expected to understand how it works and what it does exactly.
Re: Where do I find all the documentation?
Posted: 06 Aug 2016, 12:49
by Wolfie
Here is what I have so far and it seems to be working fine... (Watch it blow up in my face...)
Code: Select all
~path to...~/Movies/{def x,cleanName=n.replaceAll(/:/, ' -').replaceAll(/\?/, '');new File('P:/collections.txt').splitEachLine(':'){if (it[0].matches(/.*${imdbid}.*/)) x=it[1]};A:{any{if (any{collection}{x}) ("_ COLLECTIONS _/${any{collection}{x}}/${y} - ${cleanName}/${cleanName}")}{if (any{pn}{"X"} == "X" && minutes < 60) ("_ MINI MOVIES _/${cleanName} (${y})/${cleanName}")}{"${az}/${cleanName} (${y})/${cleanName}"}}} ({y}){'.CD'+pi}{'.'+minutes+'m'}{'.'+certification}{'.'+resolution}{'.'+source}{'.'+crc32}
In the 'collections.txt' file, I have imdbid's seperated by ;'s and at the end of a list, a colon (:) and the collection name. Helps when there are movies that are part of a collection but not registering as such (not blaming filebot for that, that's user contributed data). I have the James Bond collection on BluRay (got it for a good deal from Amazon) and almost none of the movies are part of a James Bond Collection.
Maybe I missed it, but is there an existing 'name' value that is already cleaned for file name usage? I turn the :'s into -'s and chop off ?'s and will add other illegal characters I come across. However, would feel better if there was a pre-cleansed name or a function to cleanse with an option to override certain characters... Something like this:
Code: Select all
n.cleanse() <-- default
n.cleanse(":","-") <-- substitute :'s with -'s
n.cleanse(":?","_-_") <-- substitute :'s and ?'s with _-_'s
Except for optional substitutions, all characters considered 'illegal' by the filesystem get removed completely.
If something like this already exists... what is it?

Re: Where do I find all the documentation?
Posted: 06 Aug 2016, 14:53
by rednoah
1.
WTF? My eyes are bleeding...
I'd keep my
collections.csv file in a simple
Key;Value format:
The rest can be simplified (and made readable):
Code: Select all
{any{collection}
{csv('X:/collections.csv')[imdbid]}
{if (minutes < 60 && type != 'MoviePart') 'Mini Movies'}
{az}
}
/{ny}/{plex.name}
*
{fn} doesn't do filename validation, but
{plex.name} does
* No
{y} {n} special case for collections (just don't do it, it looks terrible, even if it sorts nicely

)
EDIT:
On second thought, if you want a collections folder and do "Collections/" +
null value you'll get "Collectionsnull" which breaks our
any cascade. Using
String.concat(String) instead of
String.plus(Object) will do the trick and make things break when they ought to break.
This might be more close to your spec:
Code: Select all
{any{'Collections/'.concat(collection)}
{'Collections/'.concat(csv('X:/collections.csv')[imdbid])}
...
}
2.
FileBot will force valid filenames anyway on rename, but if you want to do it in the format you can:
String.tr() should be perfect for any simple character-by-character replacement.
Re: Where do I find all the documentation?
Posted: 06 Aug 2016, 20:33
by Wolfie
rednoah wrote:1.
WTF? My eyes are bleeding...
I'd keep my
collections.csv file in a simple
Key;Value format:
The rest can be simplified (and made readable):
Code: Select all
{any{collection}
{csv('X:/collections.csv')[imdbid]}
{if (minutes < 60 && type != 'MoviePart') 'Mini Movies'}
{az}
}
/{ny}/{plex.name}
Will that handle a line like this?
Code: Select all
tt0055928;tt0057076;tt0058150;tt0059800;tt0062512;tt0064757:James Bond Collection
(With the ;'s changed to something else and the : changed to a ; instead.)
To me, it looks better to have multiple titles grouped together to a collection. Still using a few lines to do it, but it's all neatly packed together instead of a bunch of lines with one title to the same collection. If there's a way to have it make a hit using the same line above (again changing the use of :'s and ;'s) then that would prove to be easier.
rednoah wrote:
*
{fn} doesn't do filename validation, but
{plex.name} does
* No
{y} {n} special case for collections (just don't do it, it looks terrible, even if it sorts nicely

)
For non collection folder names, it does the "{n} {y}" style. Inside of a collection, I like it to be sorted by year released, since that is *usually* the sequence of events or the best way to watch them, etc. It also doesn't look terrible to me, since the year is a fixed length, it actual name of the movies still line up just fine.
rednoah wrote:
EDIT:
On second thought, if you want a collections folder and do "Collections/" +
null value you'll get "Collectionsnull" which breaks our
any cascade. Using
String.concat(String) instead of
String.plus(Object) will do the trick and make things break when they ought to break.
This might be more close to your spec:
Code: Select all
{any{'Collections/'.concat(collection)}
{'Collections/'.concat(csv('X:/collections.csv')[imdbid])}
...
}
Would this have the same effect?
Code: Select all
{'Collections/'.concat(any{collection}{csv('X:/collections.csv')[imdbid]})}
rednoah wrote:2.
FileBot will force valid filenames anyway on rename, but if you want to do it in the format you can:
String.tr() should be perfect for any simple character-by-character replacement.
I tried testing .tr() but it wouldn't work for me. However, doing {n.replaceAll(/[:]/,' -').validateFileName()} does and I think gives slightly better control. I can make the desired changes before it does it's cleansing. Much appreciated.
Don't suppose there's some sort of a secret editor that gives more viewing space and let's you use multiple lines, etc...?

Re: Where do I find all the documentation?
Posted: 06 Aug 2016, 21:12
by Wolfie
Did come across something that I wish I could say is a bug, but arguably it's not. {info.runtime} sometimes returns values as strings vs integers. I've seen before where sometimes it will have a value and "min" as well, depending on the data source, which is why I say it's arguably not a bug. Noticed it happening again but {info.runtime} would display something (vs being null) and so I added .toInteger() to it and it started working.
Re: Where do I find all the documentation?
Posted: 06 Aug 2016, 21:13
by rednoah
1.
Your csv line format will not work. The built-in
csv function expects simple
Key;Value lines and return a
Map<String, String> object.
2.
If none of the Closures passed to
any() yield any results, then it'll return
null and
concat will fail with an NPE, which is what we want here.
3.
tr is about replacing characters. 1 char can only be replaced by 1 other char. Use the replace method for simple String replace:
I recommend using the built-in colon method:
(this will take care of
French-style colons with
Thin Space)
Re: Where do I find all the documentation?
Posted: 06 Aug 2016, 21:24
by rednoah
info.runtime is a String value of whatever the database says. TheMovieDB should give you nice numbers (if somebody has entered the data) but with OMDb all bets are off and you might get String values like "90 min" or "1h 30min" etc.
info.runtime.toInteger() will give you a number, or an exception if it's null or not a number.
Re: Where do I find all the documentation?
Posted: 06 Aug 2016, 21:41
by Wolfie
If there was a way to do a regex search with csv, then that would definitely be better than what I posted.
With .tr(), when I have .tr('','') then it shows up fine, but if I put anything in either (or both), even just one character in each, then the information just disappears.
Will switch to the .colon() expression, would make it a bit cleaner, especially since it's the only item I can think of at the moment that I'd want changed like that anyways.
So info.runtime isn't too reliable I take it? Is there any sort of conversion available to convert it from "1h 30min" to "90min" which could then get changed to "90"? Or is that not an option?
Doing my best to come up with something that will rarely need any tweaking, despite what I might throw at it. If a part of a movie is encountered, even if it is an only file and thus, doesn't get a part number assigned, would be preferable for it to still be sorted properly (and reliably), but sounds like I might have to accept some chance of it not working as desired. (No fault to filebot, of course.)
Re: Where do I find all the documentation?
Posted: 06 Aug 2016, 22:15
by rednoah
1.
If I had to parse a text file format like yours, I'd do it like this:
Code: Select all
{readLines('/path/to/file').find{ it =~ imdbid }.after(':')}
2.
String.tr() doesn't seem to work because FileBot somehow interferes with the Groovy runtime.
3.
There is really no reason not to use TheMovieDB anymore. The API is stable and serves good data. The same cannot be said of OMDb (and weird inconsistent runtime values are just one example).
I'd sort all movies with known runtime > 60 into the az folders, and default everything else into the shitty shorts folder. It's either gonna be short movies, or shitty movies where nobody has bothered entering runtime info yet.

Re: Where do I find all the documentation?
Posted: 07 Aug 2016, 00:37
by Wolfie
1. Very nice. Odd though...
Using that directly within concat works, but if I set it to a variable first and then try to use that, it fails. Of course, since the value is only needed once, it being inside of concat gets the job done. The variable gets the value, because if I remove the concat part and use the variable directly, it appears. Some sort of a bug perhaps?
2. Okay so it's not just me. Phew.
3. I use TMDB mostly, but there are a few things that OMDb has that TMDB doesn't (as far as matching content goes). I think I've come across two or three items where OMDB had a match that TMDB didn't.
Also, they're not necessarily 'shitty' shorts. Just some stuff that might be a one-time thing and the popular thing to do is to label it as a movie (since it certainly doesn't qualify as a TV series). But if it's 60 min or under, then to me, that's not really a movie. A movie is generally over an hour and while there are some things that aren't movies that go over an hour, this at least catches most of those.
Using that 'collections' file, I can more easily group stuff together, multiple items per line, making it much nicer to deal with. I appreciate that solution, it's great!
Just to mention this, I use the P drive as my Plex system drive. Within Plex itself, I redirected the database and other files to P (for Plex obviously), which makes it easier if I have to do a reinstall, also no worries of system drive filling up and crashing the computer, etc. Still doing a little reorganizing, but goal is to have M being the media drive (and using Windows Storage Spaces, a drive that can continue to expand in size when needed).
One issue I'm noticing, and it seems to be random, but when picking different files to test the expressions on, some will load up (download) the meta data right away. Others, nothing, even after repeated attempts. Then after awhile, it might work. If I try a different file, that has a chance of working, but certain ones just continue to fail no matter what. So I doubt I'm being blocked for some reason, but it working for some and not for others tells me that the API is working fine. So I'm baffled. Any ideas? (I've already cleared the cache.)
Re: Where do I find all the documentation?
Posted: 07 Aug 2016, 01:18
by Wolfie
Oh and here's what I have now, in case you're wondering or if anyone else is hoping to try new things...
Code: Select all
...path.to.media.../Movies/{def cleanName=n.colon(" -").validateFileName();A:{any{"_ COLLECTIONS _/".concat(any{MC:{readLines('P:/collections.txt').find{ it =~ imdbid }.after(':')}}{collection})+"/${y} - ${cleanName}"}{if (minutes <= 60 && info.runtime.toInteger() < 75) ("_ MINI MOVIES _/${cleanName} (${y})")}{"${az}/${cleanName} (${y})"}+"/${cleanName} (${y})"}} {'.CD'+pi}{'.'+minutes+'m'}{'.'+certification}{'.'+resolution}{'.'+source}{'.'+crc32}
Re: Where do I find all the documentation?
Posted: 07 Aug 2016, 10:15
by rednoah
1.
Did you mean something like this? Works fine as far as I can tell:
Code: Select all
{def x = readLines('/path/to/file').find{ it =~ imdbid }.after(':'); 'Collections/'.concat(x)}

You are the first person to ever use block labels in format expressions... I completely forgot Java/Groovy even had those:
Code: Select all
MC:{readLines('P:/collections.txt').find{ it =~ imdbid }.after(':')}
@see
http://groovy-lang.org/semantics.html#_ ... statements
3.
Do you have any logs or examples? You can make FileBot dump all the network responses to the log if you enabled it with the
Developer Options / Java System Properties so you can see what exactly the database is sending back.
Re: Where do I find all the documentation?
Posted: 07 Aug 2016, 11:27
by Wolfie
1. When I would try it, it wouldn't take. The 'def' would be before the A: block, which could have something to do with it.
2. I was sort of forced into it. Without them, it fusses at me with an error message and tells me to use them.
3. Will have to make a log.
Re: Where do I find all the documentation?
Posted: 07 Aug 2016, 11:50
by Wolfie
Okay, enabled the options in the filebot.l4j.ini file, cleared the cache, then ran it.
Loaded it up, it showed the check for the latest version. Went to 'edit format' and it showed the response (from omdb?) for the file it had marked as the guinea pig. When I clicked the button to choose a new file, a lot of scrolling happened, but with information on the same file. When I pick a file that failed previously, nothing happens. When I pick another file (all while remaining in the "movie bindings" dialog box), data would scroll. So, seems as though certain files just aren't getting a response (or aren't sending requests).
It works just fine when trying to do matches for renaming, which is where it truly counts. It's just the 'bindings' area that it's being weird.