If I use the simple @filepath.groovy format the partial works as intended:
Code: Select all
$ cat partialTest.groovy
@/path-to-filebot-scripts/partials/audio.groovy
$ cat audio.groovy
{ // audio map, some of these are probably not needed anymore
    def mCFP = [
        "FLAC": "FLAC",
        "PCM": "PCM",
        "MPEG Audio Layer 3": "MP3",
        "AAC LC": "AAC LC",
        "AAC LC SBR": "HE-AAC", // HE-AACv1
        "AAC LC SBR PS": "HE-AACv2",
        "AC-3 Dep": "E-AC-3+Dep",
        "AC-3 Blu-ray Disc Dep": "E-AC-3+Dep",
        "E-AC-3 Blu-ray Disc Dep": "E-AC-3+Dep",
        "E-AC-3 Dep": "E-AC-3+Dep",
        "E-AC-3 JOC": "E-AC-3 JOC",
        "DTS XBR": "DTS-HD HRA", // needs review
        "DTS ES": "DTS-ES Matrix",
        "DTS ES XBR": "DTS-HD HRA",
        "DTS ES XXCH XBR": "DTS-HD HRA", // needs review
        "DTS ES XXCH": "DTS-ES Discrete",
        "DTS ES XXCH XLL": "DTS-HD MA", // needs review
        "DTS XLL": "DTS-HD MA",
        /* "DTS XLL X": "DTS\u02D0X", // IPA triangular colon */
        "DTS XLL X": "DTS-X",
        "MLP FBA": "TrueHD",
        "MLP FBA 16-ch": "TrueHD",
        "DTS 96/24": "DTS 96-24", // needs review
    ]
        audio.collect { au ->
          /* Format seems to be consistently defined and identical to Format/String
             Format_Profile and Format_AdditionalFeatures instead
             seem to be usually mutually exclusive
             Format_Commercial (and _If_Any variant) seem to be defined
             mainly for Dolby/DTS formats */
          String _ac = any
                        { allOf
                            { any { au["Format/String"] } { au["Format"] } }
                            { au["Format_Profile"] }
                            { au["Format_AdditionalFeatures"] }
                          .collect{ it.tokenize() }.flatten().unique().join(" ") }
                        { au["Format_Commercial"] }
          /* original _aco_ binding uses "Codec_Profile", "Format_Profile", "Format_Commercial" */
          String _aco = any { au["Codec_Profile"] } { au["Format_Profile"] } { au["Format_Commercial"] }
          /* def atmos = (_aco =~ /(?i:atmos)/) ? "Atmos" : null */
          Boolean fAtmos = any { au.FormatCommercial =~ /(?i)atmos/ } { false }
          Boolean oAtmos = any { au.NumberOfDynamicObjects } { false }
          String isAtmos = (fAtmos || oAtmos) ? "Atmos" : null
          /* _channels_ uses "ChannelPositions/String2", "Channel(s)_Original", "Channel(s)"
             compared to _af_ which uses "Channel(s)_Original", "Channel(s)"
             local _channels uses the same variables as {channels} but calculates
             the result for each audio stream */
          String    _channels = any
                                  { au["ChannelPositions/String2"] }
                                  { au["Channel(s)_Original"] }
                                  { au["Channel(s)"] }
          String    _ch
          /* _channels can contain no numbers */
          Object    splitCh = _channels =~ /^(?i)object.based$/ ? "Object Based" :
                              _channels.tokenize("\\/\\.")
                              /* the below may be needed for 3/2/0.2.1/3/2/0.1 files */
                              // _channels.tokenize("\\/").take(3)*.tokenize("\\.")
                              //          .flatten()*.toInteger()
          String    chSimple = any { au["Channel(s)"] } { au["Channel(s)/String"].replaceAll("channels", "") }
          switch (splitCh) {
            case { it instanceof String }:
              _ch = allOf { splitCh } { chSimple + "ch" }.join(" ")
              break
            case { it.size > 4 }:
              def wide = splitCh.takeRight(1)
              Double main = splitCh.take(4)*.toDouble().inject(0, { a, b -> a + b })
              Double sub = Double.parseDouble("0." + wide.last())
              _ch = (main + sub).toBigDecimal().setScale(1, java.math.RoundingMode.HALF_UP).toString()
              break
            case { it.size > 1 }:
              /* original logic is _mostly_ unchanged if format is like 3/2/0.1 */
              Double sub = Double.parseDouble(splitCh.takeRight(2).join("."))
              _ch = splitCh.take(2)*.toDouble().plus(sub).inject(0, { a, b -> a + b })
                           .toBigDecimal().setScale(1, java.math.RoundingMode.HALF_UP).toString()
              break
            default:
              _ch = splitCh.first().toDouble()
          }
          /* UNUSED - possible fix for mistakes in ChannelPositions/String2 */
          String _channelPositions = any{au["ChannelPositions"]}{null}
          String channelParse
          if ( chSimple.toInteger() != _ch.tokenize(".")*.toInteger().sum() &&
               _channelPositions != null ) {
            List   channelsPos = _channelPositions.tokenize(",")
            String mainFix = channelsPos.take(3).inject(0) { acc, p ->
              Integer parsedCh = p.tokenize(":").takeRight(1).first().trim().tokenize(" ").size()
              acc + parsedCh
            }
            String subFix = channelsPos.takeRight(1).first().trim().tokenize(" ").size()
            channelParse = "${mainFix}.${subFix}"
          }
          String _chFix
          if (channelParse != null && Float.parseFloat(_ch).compareTo(Float.parseFloat(channelParse)) != 0)  {
              _chFix = channelParse
          } else {
              _chFix = _ch
          }
            // { allOf{ _chFix }{ au["NumberOfDynamicObjects"] + "obj" }.join("+") }
        String _lang = any { au["Language"] } { video.first()["Language"] }
          def stream = allOf
            { allOf { _ch } { au["NumberOfDynamicObjects"].concat("obj") }.join("+") }
            { allOf { mCFP.get(_ac, _ac) } {isAtmos/* atmos */}.join("+") }
            /* { allOf{ mCFP.get(combined, _aco) }{atmos}.join("+") } /* bit risky keeping _aco as default */
            { net.filebot.Language.findLanguage(_lang).ISO3.upperInitial() }
            /* _cf_ not being used > "Codec/Extensions", "Format" */
          def ret = [:]
          /* this is done to retain stream order */
          ret.id = any{ au["StreamKindId"] }{ au["StreamKindPos"] }{ au["ID"] }
          ret.data = stream
          return ret
        }.toSorted{ it.id }.collect{ it.data }*.join(" ").join(", ")
}
However if I use the { include 'path-to-file.groovy' } expression I get either:
Code: Select all
Expression yields empty value: startup failed:
/path-to-filebot-scripts/partials/audio.groovy: 1: Ambiguous expression could be either a parameterless closure expression or an isolated open code block;
   solution: Add an explicit closure parameter list, e.g. {it -> ...}, or force it to be treated as an open block by giving it a label, e.g. L:{...} @ line 1, column 1.
   { // audio map, some of these are probably not needed anymore
   ^
1 error
Code: Select all
Expression yields empty value: Cannot invoke method tokenize() on null object

 FileBot format code can be split into multiple files that are combined at "compile time" by FileBot, while Groovy code can run other Groovy code by merit of being a scripting language, i.e. FileBot format code can include format code from other files, groovy code can include groovy code from other files, but groovy code cannot include FileBot format code from other files.
 FileBot format code can be split into multiple files that are combined at "compile time" by FileBot, while Groovy code can run other Groovy code by merit of being a scripting language, i.e. FileBot format code can include format code from other files, groovy code can include groovy code from other files, but groovy code cannot include FileBot format code from other files.
 Please read the
 Please read the