How about sharing our format expressions?

All about user-defined episode / movie format expressions
User avatar
devster
Posts: 149
Joined: 06 Jun 2017, 22:56

Re: How about sharing our format expressions?

Post by devster » 20 Jun 2017, 22:37

I'll try joining the big ones with my formats.
First of all the command I use:

Code: Select all

filebot -script fn:amc --action test --output "/<disk>/Media" --conflict --def clean=y unsorted=y subtitles=en artwork=y excludeList=".excludes" ut_dir="$ARG_PATH" ut_kind="multi" ut_title="$ARG_NAME" ut_label="$ARG_LABEL" exec="chmod 664 '{file}'" --def @/<disk>/scripts/pushover.txt --def [email protected]/<disk>/scripts/movies.groovy [email protected]/<disk>/scripts/tvshows.groovy
I got these mostly thanks to @rednoah and @Ithiel but I just found interesting stuff in DevXen post that I want to add in the near future.
Here's the Movie one:

Code: Select all

{ norm = { it.upperInitial()
             .lowerTrail()
             .replaceTrailingBrackets()
             .replaceAll(/[`´‘’ʻ""“”]/, "'")
             .replaceAll(/[:|]/, " - ")
             .replaceAll(/[?]/, "!")
             .replaceAll(/[*\s]+/, " ")
             .replaceAll(/\b[IiVvXx]+\b/, { it.upper() })
             .replaceAll(/\b[0-9](?i:th|nd|rd)\b/, { it.lower() })
             .replaceFirst(/^(?i)(The)\s(.+)/, /$2, $1/) } }
{ transl = { it.transliterate("Any-Latin; NFD; NFC; Title") }
  isLatin = { java.text.Normalizer.normalize(it, java.text.Normalizer.Form.NFD)
                                  .replaceAll(/\p{InCombiningDiacriticalMarks}+/, "") ==~ /^\p{InBasicLatin}+$/ } }
{ allOf
  {"Movies"}
  // Movies directory
  {n.colon(" - ") + " ($y, $director)"}
  // File name
  { allOf 
    { isLatin(primaryTitle) ? primaryTitle : transl(primaryTitle) }
    {" ($y)"}
    // tags + a few more variants
    { specials = { allOf 
                     {tags}
                     { fn.findAll(/(?i:alternate[ ._-]cut|limited|proper|repack)/)*.upperInitial()*.lowerTrail()*.replaceAll(/[._-]/, " ") }
                     .flatten().sort() }
      specials().size() > 0 ? specials().join(", ").replaceAll(/^/, " - ") : "" }
    {" PT $pi"}
    {" ["}
    { allOf
      // Video stream
      {[vf,vc].join(" ")}
      { allOf
        // Audio stream and language
        {[channels,ac].join(" ")}
        { def a = audioLanguages
          a.size() > 1 ? a.ISO3.join(", ").upperInitial() : a.name.first() }
        .join(" ") }
      {source}
      .join(" - ") }
    {"]"}
    {"-" + group}
    {subt}
    .join("") }
  .join("/") }
Which generates something like:
  • From /<disk>/download/movies/少林足球 (Uncut).mp4 to /<disk>/Media/Movies/Shaolin Soccer (2001, Stephen Chow)/Shǎo Lín Zú Qiú (2001) - Uncut [1080p x264 - 5.1 AAC - Blu-ray].mp4
  • From /<disk>/download/movies/少林足球 (Uncut).eng.srt to /<disk>/Media/Movies/Shaolin Soccer (2001, Stephen Chow)/Shǎo Lín Zú Qiú (2001) - Uncut [1080p x264 - 5.1 AAC - Blu-ray].eng.srt
  • From /<disk>/download/movies/GHOST IN THE SHELL (1995)-FGT.mkv to /<disk>/Media/Movies/Ghost in the Shell (1995, Mamoru Oshii)/GHOST IN THE SHELL (1995) [1080p x264 - 2.0 AC3 Eng, Jpn - BluRay]-FGT.mkv
I'm still trying to mess with the transliteration (it should be "Shàolín Zúqiú", and for some reason TheMovieDB has that horrible title for Ghost in the Shell instead of the correct 攻殻機動隊 which transliterates into Kōkaku Kidōtai), however the result isn't bad.
And here the TV Shows one, this is greener and less polished:

Code: Select all

{ norm = { it.upperInitial()
             .lowerTrail()
             .replaceTrailingBrackets()
             .replaceAll(/[`´‘’ʻ""“”]/, "'")
             .replaceAll(/[:|]/, " - ")
             .replaceAll(/[?]/, "!")
             .replaceAll(/[*\s]+/, " ")
             .replaceAll(/\b[IiVvXx]+\b/, { it.upper() })
             .replaceAll(/\b[0-9](?i:th|nd|rd)\b/, { it.lower() })
             .replaceFirst(/^(?i)(The)\s(.+)/, /$2, $1/) } } 
{ allOf
  {"TV Shows"}
  { allOf 
      { (norm(n) == norm(primaryTitle)) ? norm(n) : norm(n) + ' [' + norm(primaryTitle) + ']' }
      { "($y)" }
    .join(" ") }
  { episode.special ? 'Specials' : 'Season ' + s.pad(2) }
  { allOf
    { norm(n) }
    { episode.special ? 'S00E' + special.pad(2) : s00e00 }
    { allOf 
      { norm(t) }
      {"PT $pi"}
      { allOf 
        { allOf 
          {"["}
          { allOf
            {[vf,vc].join(" ")}
            {[channels,ac].join(" ")}
            {source}
            .join(" - ") }
          {"]"}
          .join("") }
        {"-$group"}
        {subt}
        .join("") }
      .join(" ") }
    .join(" - ") }
  .join("/") }
And has the following example output:
  • From /<disk>/download/tvshows/Boris - Stagione 1/Boris.1x01.Il mio primo giorno.FFT.sat.ITA.avi to /<disk>/Media/TV Shows/Boris (2007)/Season 01/Boris - S01E01 - Il Mio Primo Giorno [480p DivX - 2.0 MP3]-iTA.avi
  • From /<disk>/download/tvshows/Boris - Stagione 1/Boris.1x02.L'anello del conte.FFT.sat.ITA.avi to /<disk>/Media/TV Shows/Boris (2007)/Season 01/Boris - S01E02 - L'anello Del Conte [480p DivX - 2.0 MP3]-iTA.avi
In this case sat isn't recognized as a source (SatRIP from Sky Italy), and iTA is flagged as the group instead of FFT, but these are minor things.
I only work in black and sometimes very, very dark grey. (Batman)

Cutha
Posts: 13
Joined: 21 Aug 2017, 09:16

Re: How about sharing our format expressions?

Post by Cutha » 21 Aug 2017, 10:11

The man hours or more like man days that I have could have saved if I had known about filebot! Fantastic tool and community.

I started using filebot a few days ago and quickly ran into a problem with naming my files based on audio stream. Turns out some of my movies do not have the best track set to the default and/or to audio[0]. I noticed others on here also wanting to have the "best" audio shown in the file name, or at least tag it with Atmos or DTS-X if it had it.

Once I started working with it I couldn't stop until I had it the way I wanted. Crouching Tiger has DTS 6Ch English on audio[0] and it is set as the default and Atmos is on [3]. I also decided to do a best audio option based on my own scale where object based sound gets a score of 10, DTS-HD MA gets a score of 20 and
AAC gets 50. I haven't fully fleshed this out.

The primary goal was to get the audio track that is set as default, which may not be [0] and use that for the file name. I also wanted to make sure my Atmos movies got tagged as such even if the Atmos track was not set as default.

Example where DTS is set as default but Atmos is available and where DTS-X is the default.

Crouching Tiger, Hidden Dragon (2000) DTS-HDMA 6Ch [Best Available TrueHD(Atmos) OB+8Ch]
The Huntsman - Winter's War (2016) DTS-HDMA(DTS-X) OB+8Ch

Image

It iterates through all the audio channels, scores them and based on that makes the string for the audio part of the file name. It's my first Groovy code so I apologize if its not the most efficient.

I fixed a few things and instead of making another post I changed it here:

Code: Select all

{
def sPath = "";
def mHDRCol = ["BT.709" : "NO", "BT.2020" : "YES"];
def mWebSrc = ["AMZN" : "AMZN", "NF" : "NF", "HBO" : "HBO"];
def hiResPath = "";
boolean isHDR = false;

if(self.video[0].bitdepth != null && self.bitdepth >= 10 && self.video[0].colourprimaries != null &&  mHDRCol.get(self.video[0].colourprimaries) == "YES") isHDR = true;

if(vf =~ /2160p/) hiResPath = '4k/' + video[0].Format.replace('AVC', 'x264').replace('HEVC', 'x265');
if(isHDR == true){ hiResPath += "(HDR)/" } else {hiResPath += "/"};

def VInfo = " - (FI " + vf + " ";
mWebSrc.any { k, v -> if(f.toString().contains(k))  VInfo += v + " "};

if(self.source =~ 'Remux') VInfo += fn.match(/BluRay/) else if(self.source != null) VInfo += self.source;

VInfo += " [" + video[0].Format.replace('AVC', 'x264').replace('HEVC', 'x265');
VInfo += " ";

if(isHDR == true) VInfo += " HDR " + self.bitdepth + "bit ";
if(isHDR == true && self.video[0].colourprimaries != null) VInfo += self.video[0].colourprimaries.replace(".","") + " ";
VInfo += "]";

sPath + hiResPath  + plex + VInfo;

}

{def ADef = 0;
def ADefScore = 100;
def ACurrent = 100;
def ABest = 100;
def ABestIndex;
def ACnt = audio.size;
def iCnt = 0;

//map of scoring, Codec + FormatProfile with '+' and '/' stripped
def mAudioScore =[
"DTS-HDXMACore" : 10, "DTS-HDMACore" : 20, "DTS-HDMAES MatrixCore" : 20, "DTSES DiscreteCore" : 25, "DTS" : 30,  "DTS-HDHRACore" : 25,
"TrueHDTrueHDAtmosTrueHD" : 10, "TrueHD" : 18, 
"AC3" : 30, "AC3+" : 20, "AAC" : 50, "AAC LCLC" : 45, "AAC LC-SBRHE-AACLC" : 40,
"MPA1L3Layer 3" : 60, "MPA1L2Layer 2" : 70,
"Vorbis" : 65,
"PCM" : 90, "Qclp" : 90, "161" : 90];

//map of Codec + FormatProfile made nice for file name
def mAFP = [ 
"DTS-HDXMACore" : "DTS-HDMA(DTS-X)", "DTS-HDMACore" : "DTS-HDMA", "DTS" : "DTS",  "DTS-HDMAES MatrixCore" : "DTS-HDMA",  "DTSES DiscreteCore" : "DTS-ES", "DTS-HDHRACore" : "DTS-HDHRA",
"TrueHDTrueHDAtmosTrueHD" : "TrueHD(Atmos)", "TrueHD" : "TrueHD", 
"AC3" : "AC3", "AC3+" : "DDP", 
"AAC LCLC" : "AAC", 
"MPA1L3Layer 3" : "MP3", "MPA1L2Layer 2" : "MP2",
"PCM" : "PCM", "Qclp" : "Qclp", "161" : "161"];

//map of Audio Channels(minus the slash) to pretty name
def mACH = [ 
"Object Based10" : "OB+10Ch", 
"Object Based8" : "OB+8Ch", "Object Based86" : "OB+8Ch", 
"Object Based6" : "OB+6Ch", 
"Object Based" : "OBCh", 
"9" : "9Ch", 
"8" : "8Ch", "86" : "8Ch",  "876" : "8Ch",
"7" : "7Ch", "76" : "7Ch",
"6" : "6Ch", 
"5" : "5Ch", 
"4" : "4Ch", "3" : "3Ch", "2" : "2Ch", "1" : "1Ch" ];

//Intermederia used to make finding missing strings from the maps
def CmACH = { if(mACH.get(it) != null) mACH.get(it) else " (mACH:" + it + " NOT FOUND)"};
def CmAFP = { if(mAFP.get(it) != null) mAFP.get(it) else " (mAFP:" + it + " NOT FOUND)"};
def CmAudioScore = { if(mAudioScore.get(it) != null) mAudioScore.get(it) else " (mAudioScore:" + it + " NOT FOUND)"};

//simple functions for creating audio text for file name
def GetFPSafe = {if(self.audio[it].FormatProfile != null) audio[it].FormatProfile.slash('').replace("+","") else ""};
//def GetAText = {mAFP.get( audio[it].codec + GetFPSafe(it)) + " " + mACH.get( audio[it].channels.slash('') ) };
def GetAText = {CmAFP( audio[it].codec + GetFPSafe(it)) + " " + CmACH( audio[it].channels.slash('') ) };

//Get index # that the default audio is using (only expecting 1 default audio)
audio.eachWithIndex{ a, idx -> if(call{a.default} == "Yes") ADef = idx};
//Get score for default audio
ADefScore = CmAudioScore(self.audio[ADef].codec + GetFPSafe(ADef))
//ADefScore = 50; //for your testing

//Check all audio streams, rank them against one another, get best score
for (iCnt = 0; iCnt < ACnt; iCnt++){

ACurrent = CmAudioScore(self.audio[iCnt].codec + GetFPSafe(iCnt))
if(ACurrent != null && ACurrent.toInteger() < ABest) ABestIndex = iCnt;
if(ACurrent != null && ACurrent.toInteger() < ABest) ABest = ACurrent.toInteger();
};

//end
if(ADefScore.toInteger() == ABest) GetAText(ABestIndex) + ")" else GetAText(ADef) + " [BA " + GetAText(ABestIndex) + "]" + ")";
}


And my little piece of code for HDR. As I learn more about HDR I will add more logic.

Code: Select all

{
def mHDRCol = ["BT.709" : "NO", "BT.2020" : "YES"];
if(bitdepth >= 10 &&  mHDRCol.get(self.video[0].colourprimaries) == "YES" ) '(HDR)/' else '(NonHDR)/';
}
Last edited by Cutha on 22 Aug 2017, 10:44, edited 4 times in total.

kim
Power User
Posts: 617
Joined: 15 May 2014, 16:17

Re: How about sharing our format expressions?

Post by kim » 21 Aug 2017, 14:38

I like your "score" idea, but it has problems:
e.g.
DTS-HDMA (mACH:86 NOT FOUND))
and extra
+ ")"
here is my audio format:

Code: Select all

{
def ChannelString = any{(0.0+audio.'ChannelPositionsString2'*.replaceAll(/Object\sBased\s\/|0.(?=\d.\d)/, '')*.split(' / ')*.collect{ it.split('/')*.toBigDecimal().sum() }*.max().max()).toString()}{channels};
def codecSubVersion = any{audio.any{ a -> call{a.FormatProfile} =~ 'HRA' } ? '.HRA' : null}{audio.any{ a -> call{a.FormatProfile} =~ 'MA / Core' } ? '.MA' : null}{audio.any{ a -> call{a.FormatProfile} =~ 'ES Matrix / Core' } ? '-ES' : null}{null};
def codecVersion = any{audio.Codec.join().match(/DTS-HD/)+codecSubVersion+'.'+audio.Codec.join().match(/TrueHD/)}{audio.Codec.join().match(/DTS-HD/)+codecSubVersion}{audio.Codec.join().match(/TrueHD/)}{audio.Codec.join().match(/DTS/)+codecSubVersion.replaceAll(/null/)}{ac};
(allOf{'.'+codecVersion.replaceAll(/null/)}
{if( ((ac == 'AAC'||ac == 'MP3') && (channels != '2.0' || ChannelString != channels) ) || ( (ac == 'AC3'||ac == 'DTS'||ac == 'TrueHD') && (channels != '5.1' || ChannelString != channels) ) ) return {any{ChannelString}{channels}}}
{aco.match(/Atmos/)}).join('.')
}

Cutha
Posts: 13
Joined: 21 Aug 2017, 09:16

Re: How about sharing our format expressions?

Post by Cutha » 21 Aug 2017, 14:56

I just ran it through some of my old collection to find the old codec stuff and then I ran it against my 4k collection and I found some surprises.

I knew I wouldn't capture all the possibilities off the start so I made it so it would be kind of easy to track down what is missing.

(mACH:86 NOT FOUND)) is because in the map for audio channels "mACH" I didn't have the "8/6", if you add "86" : "8Ch" to it then it should work. I updated it like this:

Code: Select all

//map of scoring, Codec + FormatProfile with + and / stripped
def mAudioScore =[ "DTS-HDXMACore" : 10, "DTS-HDMACore" : 20, "DTS" : 30, "TrueHDTrueHDAtmosTrueHD" : 10, "TrueHD" : 18, "AC3" : 40, "AAC" : 50, "AAC LCLC" : 50, "MPA1L3Layer 3" : 60,"MPA1L2Layer 2" : 70];
//map of Codec + FormatProfile made nice for file name
def mAFP = [ "DTS-HDXMACore" : "DTS-HDMA(DTS-X)", "DTS-HDMACore" : "DTS-HDMA", "DTS" : "DTS", "TrueHDTrueHDAtmosTrueHD" : "TrueHD(Atmos)", "TrueHD" : "TrueHD", "AC3" : "AC3", "AAC LCLC" : "AAC", "MPA1L3Layer 3" : "MP3", "MPA1L2Layer 2" : "MP2" ];
//map of Audio Channels(minus the slash) to pretty name
def mACH = [ "Object Based10" : "OB+10Ch", "Object Based8" : "OB+8Ch", "Object Based86" : "OB+8Ch", "Object Based6" : "OB+6Ch", "Object Based" : "OBCh", "9" : "9Ch", "8" : "8Ch", "86" : "8Ch", "7" : "7Ch", "6" : "6Ch", "5" : "5Ch", "4" : "4Ch", "3" : "3Ch", "2" : "2Ch", "1" : "1Ch" ];
For the HDR stuff I just found that Elysium, at least the version that I grabbed, doesn't have a .bitdepth field.

Video
ID : 1
Format : HEVC
Format/Info : High Efficiency Video Coding
Codec ID : V_MPEGH/ISO/HEVC
Duration : 1 h 49 min
Bit rate : 57.7 Mb/s
Width : 3 840 pixels
Height : 1 608 pixels
Display aspect ratio : 2.40:1
Frame rate mode : Constant
Frame rate : 23.976 (24000/1001) FPS
Bits/(Pixel*Frame) : 0.390
Stream size : 44.2 GiB (86%)
Language : English
Default : Yes
Forced : No


To prevent it from completely dying I did this:

Code: Select all

if(self.video[0].bitdepth != null && self.bitdepth == 10 && self.video[0].colourprimaries != null &&  mHDRCol.get(self.video[0].colourprimaries) == "YES") isHDR = "YES";

Cutha
Posts: 13
Joined: 21 Aug 2017, 09:16

Re: How about sharing our format expressions?

Post by Cutha » 21 Aug 2017, 15:05

kim wrote:
21 Aug 2017, 14:38
here is my audio format:
Yours works well with my Atmos and everything else but it does not identify the other object based format, DTS-X.

It displayed this:
.DTS-HD.MA.7.1

For this:
Format_Profile X / MA / Core
CodecID A_DTS
Codec DTS-HD
Codec/String DTS-HD
Codec/Family DTS
Duration 7210454.000000
BitRate_Mode VBR / VBR / CBR
BitRate_Mode/String Variable / Variable / Constant
BitRate 3840000 / 3840000 / 1509000
BitRate/String 3 840 kb/s / 3 840 kb/s / 1 509 kb/s
Channel(s) Object Based / 8 / 6
Channel(s)/String Object Based / 8 channels / 6 channels
ChannelPositions Object Based / Front: L C R, Side: L R, Back: L R, LFE / Front: L C R, Side: L R, LFE
ChannelPositions/String2 Object Based / 3/2/2.1 / 3/2/0.1
ChannelLayout Object Based / / C L R Ls Rs LFE

kim
Power User
Posts: 617
Joined: 15 May 2014, 16:17

Re: How about sharing our format expressions?

Post by kim » 21 Aug 2017, 17:24

that's because I don't any X-files:P
you can just add it like this:

Code: Select all

{
def ChannelString = any{(0.0+audio.'ChannelPositionsString2'*.replaceAll(/Object\sBased\s\/|0.(?=\d.\d)/, '')*.split(' / ')*.collect{ it.split('/')*.toBigDecimal().sum() }*.max().max()).toString()}{channels};
def codecSubVersion = any{audio.any{ a -> call{a.FormatProfile} =~ 'HRA' } ? '.HRA' : null}{audio.any{ a -> call{a.FormatProfile} =~ 'X / MA / Core' } ? '.X' : null}{audio.any{ a -> call{a.FormatProfile} =~ 'MA / Core' } ? '.MA' : null}{audio.any{ a -> call{a.FormatProfile} =~ 'ES Matrix / Core' } ? '-ES' : null}{null};
def codecVersion = any{audio.Codec.join().match(/DTS-HD/)+codecSubVersion+'.'+audio.Codec.join().match(/TrueHD/)}{audio.Codec.join().match(/DTS-HD/)+codecSubVersion}{audio.Codec.join().match(/TrueHD/)}{audio.Codec.join().match(/DTS/)+codecSubVersion.replaceAll(/null/)}{ac};
(allOf{'.'+codecVersion.replaceAll(/null/)}
{if( ((ac == 'AAC'||ac == 'MP3') && (channels != '2.0' || ChannelString != channels) ) || ( (ac == 'AC3'||ac == 'DTS'||ac == 'TrueHD') && (channels != '5.1' || ChannelString != channels) ) ) return {any{ChannelString}{channels}}}
{aco.match(/Atmos/)}).join('.')
}
result = .DTS-HD.X.7.1
feel free to edit it if you like it an other way ;)

e.g. like this:

Code: Select all

{
def ChannelString = any{(0.0+audio.'ChannelPositionsString2'*.replaceAll(/Object\sBased\s\/|0.(?=\d.\d)/, '')*.split(' / ')*.collect{ it.split('/')*.toBigDecimal().sum() }*.max().max()).toString()}{channels};
def codecSubVersion = any{audio.any{ a -> call{a.FormatProfile} =~ 'HRA' } ? '.HRA' : null}{audio.any{ a -> call{a.FormatProfile} =~ 'X / MA / Core' } ? '-X' : null}{audio.any{ a -> call{a.FormatProfile} =~ 'MA / Core' } ? '.MA' : null}{audio.any{ a -> call{a.FormatProfile} =~ 'ES Matrix / Core' } ? '-ES' : null}{null};
def codecVersion = any{audio.Codec.join().match(/DTS-HD/)+codecSubVersion+'.'+audio.Codec.join().match(/TrueHD/)}{audio.Codec.join().match(/DTS-HD/) && codecSubVersion == '-X' ? 'DTS-X' : null}{audio.Codec.join().match(/DTS-HD/)+codecSubVersion}{audio.Codec.join().match(/TrueHD/)}{audio.Codec.join().match(/DTS/)+codecSubVersion.replaceAll(/null/)}{ac};
(allOf{'.'+codecVersion.replaceAll(/null/)}
{if( ((ac == 'AAC'||ac == 'MP3') && (channels != '2.0' || ChannelString != channels) ) || ( (ac == 'AC3'||ac == 'DTS'||ac == 'TrueHD') && (channels != '5.1' || ChannelString != channels) ) ) return {any{ChannelString}{channels}}}
{aco.match(/Atmos/)}).join('.')
}
result = .DTS-X.7.1

BTW: what is the standard naming of DTS:X files ?

Cutha
Posts: 13
Joined: 21 Aug 2017, 09:16

Re: How about sharing our format expressions?

Post by Cutha » 22 Aug 2017, 00:37

kim wrote:
21 Aug 2017, 17:24
result = .DTS-X.7.1

BTW: what is the standard naming of DTS:X files ?
I only have a couple movies and they were both .DTS-X. so I assume thats what it is. I tried to google it but all I could find was stuff about WEBRip and that sort as far as naming goes.

kim
Power User
Posts: 617
Joined: 15 May 2014, 16:17

Re: How about sharing our format expressions?

Post by kim » 22 Aug 2017, 01:11

well then you can just copy/paste my format then ;)

Cutha
Posts: 13
Joined: 21 Aug 2017, 09:16

Re: How about sharing our format expressions?

Post by Cutha » 22 Aug 2017, 05:42

The sharing is great, the more knowledge the better. I am partial to knowing in the file name what the properties of the 'default' audio track and if there is a better track available. Because of that I check each audio track.

yodaspowart
Posts: 5
Joined: 02 Jun 2017, 01:04

Re: How about sharing our format expressions?

Post by yodaspowart » 13 Dec 2017, 19:36

Have a basic script that uses the groovy setup for 4K content going into its own folder for Movies 4K & TV Shows 4K based on 2160p in file name else it goes to regular folders:

Code: Select all

#!/bin/bash

export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
export LC_CTYPE="en_US.UTF-8"

TORRENT_PATH="$TR_TORRENT_DIR/$TR_TORRENT_NAME"
TORRENT_NAME="$TR_TORRENT_NAME"
TORRENT_LABEL="N/A"

# Subtitle language
SUBLANG=en
SKIP_EXTRACT=n
MUSIC=y

filebot -script /opt/filebot/scripts/amc.groovy \
    --output "$HOME/media" \
    -non-strict --encoding utf8 --log all --log-file amc-transmission.log --action copy --conflict override \
    --def artwork=false ut_kind=multi "ut_dir=$TORRENT_PATH" "ut_title=$TORRENT_NAME" subtitles=$SUBLANG \
    --def "movieFormat={fn =~ /2160p/ ? 'Movies 4K' : 'Movies'}/{n} ({y})/{n} {hd} {vc} {vf} ({y}){' CD'+pi}{'.'+lang}" \
    --def "seriesFormat={fn =~ /2160p/ ? 'TV Shows 4K' : 'TV Shows'}/{n}/{episode.special ? 'Special' : 'Season '+s.pad(2)}/{n} - {episode.special ? 'S00E'+special.pad(2) : s00e00} - {hd} - {vc} - {vf} - {t.replaceAll(/[\`´''ʻ]/, /'/).replaceAll(/[!?.]+$/).replacePart(', Part \$1')}{'.'+lang}" \
    extractFolder="$HOME/files/_extracted" music=$MUSIC skipExtract=$SKIP_EXTRACT &
Thinking of expanding this to Movies 3D, just need to figure out how to do an "OR".

User avatar
rednoah
The Source
Posts: 14363
Joined: 16 Nov 2011, 08:59
Location: Taipei
Contact:

Re: How about sharing our format expressions?

Post by rednoah » 13 Dec 2017, 20:35

Assuming that "3D" is part of the filename, this should do:

Code: Select all

{fn =~ /3D|2160p/ ? 'Movies 4K' : 'Movies'}
:idea: Please read the FAQ and How to Request Help.

yodaspowart
Posts: 5
Joined: 02 Jun 2017, 01:04

Re: How about sharing our format expressions?

Post by yodaspowart » 13 Dec 2017, 21:41

Thanks.
This is useful to know that the "OR" can be used like that if i needed to trigger variants for the Folder Movies 4K or Movies 3D.

Was thinking the "OR" could be used like:

IF filename has 3D it goes to folder named "Movies 3D" (1920x1080P)
IF filename has 2160p it goes to folder named "Movies 4K" (3840x2160P)
ELSE it goes to folder named "Movies" (< 1920x1080P)

Would it be this work?

Code: Select all

--def "movieFormat={fn =~ /2160p|4K|4k|UHD/ ? 'Movies 4K' : 'Movies'}{fn =~ /3D|3d|3dhsbs|H-SBS|3dhou|H-OU/ ? 'Movies 3D' : 'Movies'}/{n} ({y})/{n} {hd} {vc} {vf} ({y}){' CD'+pi}{'.'+lang}" \
Thanks rednoah

User avatar
rednoah
The Source
Posts: 14363
Joined: 16 Nov 2011, 08:59
Location: Taipei
Contact:

Re: How about sharing our format expressions?

Post by rednoah » 14 Dec 2017, 19:28

More like this:

Code: Select all

fn =~ /2160p/ ? 'Movies 4K' : fn =~ /3D/ ? 'Movies 3D' : 'Movies'
You can nest ternary expressions and have lots of if-then-else that way.
:idea: Please read the FAQ and How to Request Help.

yodaspowart
Posts: 5
Joined: 02 Jun 2017, 01:04

Re: How about sharing our format expressions?

Post by yodaspowart » 14 Dec 2017, 22:33

Thanks rednoah, this perfect for me!

My new script:

Code: Select all

#!/bin/bash

export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
export LC_CTYPE="en_US.UTF-8"

TORRENT_PATH="$TR_TORRENT_DIR/$TR_TORRENT_NAME"
TORRENT_NAME="$TR_TORRENT_NAME"
TORRENT_LABEL="N/A"

# Subtitle language
SUBLANG=en
SKIP_EXTRACT=n
MUSIC=y

filebot -script /opt/filebot/scripts/amc.groovy \
    --output "$HOME/media" \
	-non-strict --encoding utf8 --log all --log-file amc-transmission.log --action copy --conflict override \
	--def artwork=false ut_kind=multi "ut_dir=$TORRENT_PATH" "ut_title=$TORRENT_NAME" subtitles=$SUBLANG \
    --def "movieFormat={fn =~ /2160p|4K|4k|UHD/ ? 'Movies 4K' : fn =~ /3D|3d|3dhsbs|H-SBS|3dhou|H-OU/ ? 'Movies 3D' : 'Movies'}/{n} ({y})/{n} {hd} {vc} {vf} ({y}){' CD'+pi}{'.'+lang}" \
    --def "seriesFormat={fn =~ /2160p|4K|4k|UHD/ ? 'TV Shows 4K' : 'TV Shows'}/{n}/{episode.special ? 'Special' : 'Season '+s.pad(2)}/{n} - {episode.special ? 'S00E'+special.pad(2) : s00e00} - {hd} - {vc} - {vf} - {t.replaceAll(/[\`´‘’ʻ]/, /'/).replaceAll(/[!?.]+$/).replacePart(', Part \$1')}{'.'+lang}" \
	extractFolder="$HOME/files/_extracted" music=$MUSIC skipExtract=$SKIP_EXTRACT &

jebbe
Posts: 5
Joined: 25 Dec 2017, 04:54

Re: How about sharing our format expressions?

Post by jebbe » 25 Dec 2017, 05:03

I am absolutely no expert when it comes doing this, but am slowly learning and not trying to over complicate it for myself.

I mainly use FileBot for movies and nothing else. I've essentially used bits from already posted and kind of made it work for my own needs.

Code: Select all

W:\Movies ({vf})\{genre}\{primarytitle} ({y}){' ['+fn.replaceAll(/(?i)directors|theatrical|ultimate/,'$0 Cut').matchAll(/UNRATED|REMASTERED|EXTENDED|UNCUT|DIRECTORS.CUT|THEATRICAL.CUT|ULTIMATE.CUT|SPECIAL.EDITION/).join('][').upperInitial().lowerTrail()+']'} [{vf}] [{ac}{fn.match("-HD.MA.")+af}]/{primarytitle} ({y}) {vc}{" (CD$pi)"}{' '+lang}
This is what I currently have and is fine but I have all of my movies stored between two different drives labeled Y and W. I saw the snippet of code to tell Filebot to essentially use whichever drive has more free space to decide where to store said movies.

Code: Select all

{['C:', 'D:', 'E:'].collect{ (it+'/TV/'+n) as File }.sort{ a, b -> a.exists() <=> b.exists() ?: a.diskSpace <=> b.diskSpace }.last()}/{episode}
I've tried to make this work with my current set up but it comes out weird. I've changed the TV part to movies and what I had for movies but it doesn't seem to work right.

Code: Select all

{['Y:', 'W:'].collect{ (it+'/Movies ({vf})/'+n) as File }.sort{ a, b -> a.exists() <=> b.exists() ?: a.diskSpace <=> b.diskSpace }.last()}/{episode}\{genre}\{primarytitle} ({y}){' ['+fn.replaceAll(/(?i)directors|theatrical|ultimate/,'$0 Cut').matchAll(/UNRATED|REMASTERED|EXTENDED|UNCUT|DIRECTORS.CUT|THEATRICAL.CUT|ULTIMATE.CUT|SPECIAL.EDITION/).join('][').upperInitial().lowerTrail()+']'} [{vf}] [{ac}{fn.match("-HD.MA.")+af}]/{primarytitle} ({y}) {vc}{" (CD$pi)"}{' '+lang}
I attempted something like this, but it puts the ({vf}) as part of the movies folder title instead of using the command for the video size and it looks like that snippet has stuff for tv shows.

Anyone mind correcting me on this? I'd appreciate any help I can get to make this right. I also am curious about the drive size code, does it only scan the drives on the initial scan of the movie titles and move them into the same folder or does it take into account each movie as it fills up the drive?

jebbe
Posts: 5
Joined: 25 Dec 2017, 04:54

Re: How about sharing our format expressions?

Post by jebbe » 25 Dec 2017, 05:13

Literally after sharing this, I tinkered with it a bit more and discovered this.

Code: Select all

{['Y:', 'W:'].collect{ (it+'/Movies') as File }.sort{ a, b -> a.exists() <=> b.exists() ?: a.diskSpace <=> b.diskSpace }.last()} ({vf})\{genre}\{primarytitle} ({y}){' ['+fn.replaceAll(/(?i)directors|theatrical|ultimate/,'$0 Cut').matchAll(/UNRATED|REMASTERED|EXTENDED|UNCUT|DIRECTORS.CUT|THEATRICAL.CUT|ULTIMATE.CUT|SPECIAL.EDITION/).join('][').upperInitial().lowerTrail()+']'} [{vf}] [{ac}{fn.match("-HD.MA.")+af}]/{primarytitle} ({y}) {vc}{" (CD$pi)"}{' '+lang}
It seems to be working correctly as of right now. Does this look right?

Hercules40
Posts: 12
Joined: 15 Jun 2017, 00:46

Re: How about sharing our format expressions?

Post by Hercules40 » 31 Dec 2017, 18:48

I need some help. There are many titles that have question marks in them "?". How do I get the "?" removed from the title?

For example, Anime:

Code: Select all

G:\My Videos\Anime\{n.replace(':',' -').replaceAll(/[!?.]+$/).replaceAll(/[`´‘’ʻ]/, " -").replaceTrailingBrackets()}\{'Season '+s}\{n.replace(':',' -').replaceTrailingBrackets()} - {s+'x'}{e.pad(2)} - {t.replace(':',' -').replaceAll(/[!?.*]+$/).replaceAll(/[`´‘’ʻ]/, "'").lowerTrail().replacePart(', Part $1')}
ButI can't get it to work for:

"World End: What do you do at the end of the world? Are you busy? Will you save us?"

Thanks.

User avatar
rednoah
The Source
Posts: 14363
Joined: 16 Nov 2011, 08:59
Location: Taipei
Contact:

Re: How about sharing our format expressions?

Post by rednoah » 31 Dec 2017, 20:24

e.g. replace ? with nothing:

Code: Select all

t.replace('?', '')
:idea: Please read the FAQ and How to Request Help.

Hercules40
Posts: 12
Joined: 15 Jun 2017, 00:46

Re: How about sharing our format expressions?

Post by Hercules40 » 01 Jan 2018, 22:18

Yeah, I get that. And of course, instead of the title "t", I need to change the name "n". So, I tried this expression, but can anyone tell me why it doesn't work?

Code: Select all

\Anime\{n.replace(':',' -').replace('?','').replaceAll(/[!?.]+$/).replaceAll(/[`´‘’ʻ]/, " -").replaceTrailingBrackets()}\{'Season '+s}\{n.replace(':',' -').replaceTrailingBrackets()} - {s+'x'}{e.pad(2)} - {t.replace(':',' -').replace('?','').replaceAll(/[!?.*]+$/).replaceAll(/[`´‘’ʻ]/, "'").lowerTrail().replacePart(', Part $1')}

User avatar
rednoah
The Source
Posts: 14363
Joined: 16 Nov 2011, 08:59
Location: Taipei
Contact:

Re: How about sharing our format expressions?

Post by rednoah » 01 Jan 2018, 22:41

It works. Why would you think that it doesn't work? What results do you get?

Test Case:

Code: Select all

{'Is this a test?'.replace('?', '')}
:idea: Please read the FAQ and How to Request Help.

kim
Power User
Posts: 617
Joined: 15 May 2014, 16:17

Re: How about sharing our format expressions?

Post by kim » 01 Jan 2018, 23:55

Look at your output:
/Anime/World End - What Do You Do at the End of the World Are You Busy Will You Save Us/Season 1/World End - What Do You Do at the End of the World? Are You Busy? Will You Save Us? - 1x12 - The Happiest Girl in the World
this is easier to see like so
/Anime/ = folder
World End - What Do You Do at the End of the World Are You Busy Will You Save Us = n (folder)
/
Season 1 = folder
/
World End - What Do You Do at the End of the World? Are You Busy? Will You Save Us? = n (filename)
-
1x12 = s00e00 / sxe.pad(2)
-
The Happiest Girl in the World = t
for every n (folder or filename) of t you need to remove/replace e.g. '?' from the full path, you see ?

Code: Select all

{n.replace(':',' -').replaceAll(/[?]/).replaceAll(/[!?.]+$/).replaceAll(/[`´‘’ʻ]/, " -").replaceTrailingBrackets()}\{'Season '+s}\{n.replace(':',' -').replaceAll(/[?]/).replaceTrailingBrackets()} - {sxe.pad(2)} - {t.replace(':',' -').replace('?','').replaceAll(/[!?.*]+$/).replaceAll(/[`´‘’ʻ]/, "'").lowerTrail().replacePart(', Part $1')}

https://www.thetvdb.com/?tab=episode&se ... 3518&lid=7

btw:

Code: Select all

.replaceAll(/[!?.]+$/)
this only removes e.g. ? if this is the last char in n e.g.
before
World End - What Do You Do at the End of the World? Are You Busy? Will You Save Us?
after
World End - What Do You Do at the End of the World? Are You Busy? Will You Save Us
https://regexr.com/

ChefGregS
Posts: 67
Joined: 30 Mar 2016, 11:14

Re: How about sharing our format expressions?

Post by ChefGregS » 11 Mar 2018, 04:43

I keep this post updated as I fine-tune and update my formats. This latest update is on:
29 August 2018

Here are my formats for Movies and TV Shows. I currently use another app for renaming music files but if I ever change to Filebot for those I'll post mine for that as well.

MOVIES

Code: Select all

 {vf == /2160p/ ? 'L:/Movies 4K' : vf =~ /1080p|720p/ ? 'M:/Movies HD' : 'M:/Movies'}/{n.upperInitial().colon(' - ').replace('?', '!')} {' (' + fn.matchAll(/extended|uncensored|remastered|unrated|uncut|directors.cut|special.edition|redux/)*.upperInitial()*.lowerTrail().sort().join(', ').replaceAll(/[._]/, " ") + ')'} {any{' Part '+pi}{null}} ({y}) {fn.match(/3D/)}/{n.upperInitial().colon(' - ').replace('?', '!')} {' (' + fn.matchAll(/extended|uncensored|remastered|unrated|uncut|directors.cut|special.edition|redux/)*.upperInitial()*.lowerTrail().sort().join(', ').replaceAll(/[._]/, " ") + ')'} {any{' Part '+pi}{null}} [{y}, {any{csv('M:/replacecert1.csv').get(certification)}{certification}{"NR"} }, {runtime} Min] {[actors.take(3).join(', ')]} {[genres.take(3).join(', ')]} [{fn.match(/3D/)+', '}{"$vf, [email protected]$af"}]{subt} 
This takes this:
Nutty.Professor.II.The.Klumps.2000.1080p.HDDVD.x264.AC3-ETRG.mp4

and changes it to:
M:\Movies HD\Nutty Professor II -The Klumps (2000)/Nutty Professor II -The Klumps (2000) [PG-13, 106 Min] [Eddie Murphy, Janet Jackson, Larry Miller] [Fantasy, Comedy, Romance] [1080p, [email protected]].mp4

It takes it from wherever I have it stored and moves it to my Media hard drive and puts it in the correct MOVIE folder. I separate my movies based on resolution. Movies is anything under 720p. Movies HD is anything from 720-2160p. Movies 4K is any movie 2160 and over. It also creates a folder based on the movie title and year. You can easily change the names of the folders or drives in the first section of code.

It shows the rating and runtime then will list up to 3 of the actors, 3 of the genres and then gives information about if the movie is 3D, resolution, audio type and channels.

IF the information is included in the original file name it will also parse and then include in the new name the information such as UNRATED, UNCUT, DIRECTORS CUT, EXTENDED, REMASTERED, UNCENSORED, SPECIAL EDITION, REDUX. You can easily add to this list in the code if you have other version information.

At the end, notice the code {subt}....this will include the correct subtitle extension if Filebot determines the file being renamed is in fact a subtitle file. If the file is NOT a subtitle file this is ignored.

--

TV SERIES

Code: Select all

 Z:/TV Series/{n.upperInitial().colon(' - ').replace('?', '!') }/{ any{"Season $s"}{'Specials'} }/{n.upperInitial().colon(' - ').replace('?', '!') } {[genres.take(2).join(', ')]} - {s00e00} - {any{[airdate]}{null}} ({"$vf, [email protected]$af"}) - {t.colon(" - ").replace('?', '!')}{subt} 
This will take:
xpats.Bar.Rescue.S05E14.HDTV.XviD-AFG.avi

and create:
M:\TV Series\Bar Rescue\Season 5 [2016, 2017]\Bar Rescue - S05E14 - [2017-02-26] - I Know What You Did Last Summit.avi

The code moves the file(s) into the Media drive under the TV Series folder and then into the SHOW NAME folder. If there is a season associated with the file it puts the file(s) into the season folder. If however, the episode(s) are specials then they will be put into a Specials folder. The file name is the Show Name - season # and episode # - airdate of the episode - name of the episode if there is one.

When it comes to TV series I don't feel the need to have all the video and audio information in the file name. I don't really care. I have debated this, however, a few times as I have a few series where I had to piece together the seasons, thus some episodes in a season may be 720p and some may be 320p and some others 580p. IF I had the video info listed I could more easily either find new ones matching or re-encode myself. But, not really top of my list of things to work on atm. :)
Last edited by ChefGregS on 29 Aug 2018, 17:22, edited 2 times in total.

dpullen87
Posts: 1
Joined: 29 Aug 2018, 14:06

Re: How about sharing our format expressions?

Post by dpullen87 » 29 Aug 2018, 14:08

Hi DevXen,

I'd really appreciate it if you could send me your music script for MusicBrainz

Many Thanks,

alias
Posts: 1
Joined: 24 Sep 2018, 21:08

Re: How about sharing our format expressions?

Post by alias » 24 Sep 2018, 21:10

Thanks for share ChefGregS

venealis
Posts: 3
Joined: 28 Sep 2018, 13:37

Re: How about sharing our format expressions?

Post by venealis » 28 Sep 2018, 13:40

ChefGregS
If you would be willing to share your script for MusicBrainz with me as well that would be awesome.

Thanks.

Post Reply

Who is online

Users browsing this forum: No registered users and 4 guests