How can I use a variable from one include() script in another include() script?

Running FileBot from the console, Groovy scripting, shell scripts, etc
Post Reply
rv23
Posts: 64
Joined: 19 Jul 2020, 15:05

How can I use a variable from one include() script in another include() script?

Post by rv23 »

I am using filebot's include() method to source several "libraries", which are groovy scripts (not classes) in a groovy script executed using filebot --script <script>.

I have several libraries, and multiple methods within those libraries.
in my main script I use include() to source the "library" scripts.
include('lib/detect')

I use alot of regex, some of which is duplicated within those libraries, and I would like to use a single variable in one library and reference that variable in both the main script and other libraries.

I have only been able to figure out how to declare a variable within a specific library for use with that specific library (but I can't figure out how to use it in other library files) using groovy.transform.field

for example
lib\detect.groovy

Code: Select all

@Field String airDateOrderMatcherRegex = /(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])/
and I can use that in a method (within lib\detect.groovy) like so

Code: Select all

return filename =~ /${airDateOrderMatcherRegex}/
Any ideas on how (if) this can be done?

I have been unsuccessful in using import within the main script or library script to import local groovy scripts/classes
I have tried creating a lib\constants.groovy class (which I can't figure out how to import) with the variable declared public, but can't figure out how to reference/use it in the lib\detect.groovy method (even using include()), I just get the "No such property" error ..

Code: Select all

No such property: airDateOrderMatcherRegex for class: lib.Script7
User avatar
rednoah
The Source
Posts: 22923
Joined: 16 Nov 2011, 08:59
Location: Taipei
Contact:

Re: How can I use a variable from one include() script in another include() script?

Post by rednoah »

I have no idea how the Groovy intrinsics work out if you load scripts together like this.


The thing that works for sure is global functions:
* https://github.com/filebot/scripts/blob ... .groovy#L5
* https://github.com/filebot/scripts/blob ... roovy#L611


So I'd use global functions for shared code instead of global variables.
:idea: Please read the FAQ and How to Request Help.
kim
Power User
Posts: 1251
Joined: 15 May 2014, 16:17

Re: How can I use a variable from one include() script in another include() script?

Post by kim »

Including a groovy script in another groovy:
https://stackoverflow.com/questions/913 ... her-groovy


for global var. to work it needs to like this (outside any function)

Code: Select all

airDateOrderMatcherRegex = /(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])/
e.g. this do not work (at least for me)

Code: Select all

def airDateOrderMatcherRegex = /(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])/
rv23
Posts: 64
Joined: 19 Jul 2020, 15:05

Re: How can I use a variable from one include() script in another include() script?

Post by rv23 »

@kim -
Thanks for the SO link, I'll poke at that a bit and search for some other groovy alternatives to the native import statement.

Your idea is good and mirrors what I have found out when using filebot --script to run groovy scripts, however that seems to only work for variables you declare in the script directly executed by filebot. If defined in a script that is included in the main script using the filebot include() method, these doesn't work for anything outside that script (so you can't use them in main or another included script)

There is also the @Field groovy transformation which can be used with defined variables to make then "global" as an FYI - https://groovy-lang.org/structure.html#_variables

@rednoah
I wouldn't say it's entirely a groovy thing, as the import statement behaves different between a groovy script run directly by the groovy interpreter and a groovy script run by filebot -script

For example, this works with a script called directly by the groovy interpreter (groovy test_mainscript.groovy)
test_mainscript.groovy

Code: Select all

import lib.Constants
// Adding it as a Static variable to a class and calling it directly works
println("Main Sript")
println("Main Script referencing lib variable airDateOrderMatcherRegexStatic:[${Constants.airDateOrderMatcherRegexStatic}]")
// Adding it as a public variable to a class and instantiating it works
Constants myConstants = new Constants()
println("Main Script referencing lib myConstants.property airDateOrderMatcherRegexPublic:[${myConstants.airDateOrderMatcherRegexPublic}]")
lib/Constants.groovy

Code: Select all

package lib
class Constants {
  static String airDateOrderMatcherRegexStatic = /(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])/
  public String airDateOrderMatcherRegexPublic = /(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])/
  def airDateOrderMatcherRegexDef = /(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])/
}
However trying to run it using filebot -script produces an error when it tries to import the local groovy class (I just assumed it wasn't possible to import local files, which is why there is the include() method)

Code: Select all

startup failed:
Script1.groovy: 2: unable to resolve class lib.Constants
 @ line 2, column 1.
   import lib.Constants
   ^

1 error

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Script1.groovy: 2: unable to resolve class lib.Constants
 @ line 2, column 1.
   import lib.Constants
   ^

1 error

        at net.filebot.cli.ScriptShell.evaluate(Unknown Source)
        at net.filebot.cli.ScriptShell.runScript(Unknown Source)
        at net.filebot.cli.ArgumentProcessor.runScript(Unknown Source)
        at net.filebot.cli.ArgumentProcessor.run(Unknown Source)
        at net.filebot.Main.main(Unknown Source)

Error (o_O)
Switching to using the include(), however I can't seem to instantiate the Constants class, nor access any of it's variables/constants, with a groovy script like this ..
filebot -script test_filebot_mainscript.groovy

Code: Select all

include('lib/Constants')
// Produces error
// No such property: airDateOrderMatcherRegexStatic for class: Script1
println("Main Script referencing lib variable airDateOrderMatcherRegexStatic:[${airDateOrderMatcherRegexStatic}]")
// Produces Error
// No such property: Constants for class: Script1
println("Main Sript")
println("Main Script referencing lib variable airDateOrderMatcherRegexStatic:[${Constants.airDateOrderMatcherRegexStatic}]")
// Produces Error
// unable to resolve class Constants
Constants myConstants = new Constants()
println("Main Script referencing lib myConstants.property airDateOrderMatcherRegexPublic:[${myConstants.airDateOrderMatcherRegexPublic}]")
// Produces Error
// No such property: airDateOrderMatcherRegexDef for class: Script1
println("Main Script referencing lib variable airDateOrderMatcherRegexDef:[${airDateOrderMatcherRegexDef}]")
and run using

Code: Select all

filebot -script test_filebot_mainscript.groovy
I will poke at this a bit more and post if I can find any workarounds. Thanks for the suggestions.
kim
Power User
Posts: 1251
Joined: 15 May 2014, 16:17

Re: How can I use a variable from one include() script in another include() script?

Post by kim »

main:

Code: Select all

include ('Constants')

println Constants()

Constants:

Code: Select all

def Constants() {
  String airDateOrderMatcherRegexStatic = /(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])/
  String airDateOrderMatcherRegexPublic = /(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])/
  def airDateOrderMatcherRegexDef = /(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])/
  return [airDateOrderMatcherRegexStatic, airDateOrderMatcherRegexPublic, airDateOrderMatcherRegexDef]
}
output:

Code: Select all

[(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=]), (?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=]), (?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])]
Done ?(?????)?
OR

Code: Select all

include ('Constants')

println Constants().airDateOrderMatcherRegexStatic

Code: Select all

def Constants() {
  String airDateOrderMatcherRegexStatic = /(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])/
  String airDateOrderMatcherRegexPublic = /(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])/
  def airDateOrderMatcherRegexDef = /(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])/
  return ['airDateOrderMatcherRegexStatic': airDateOrderMatcherRegexStatic, 'airDateOrderMatcherRegexPublic': airDateOrderMatcherRegexPublic, 'airDateOrderMatcherRegexDef': airDateOrderMatcherRegexDef]
}
output:

Code: Select all

(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])
Done ?(?????)?
User avatar
rednoah
The Source
Posts: 22923
Joined: 16 Nov 2011, 08:59
Location: Taipei
Contact:

Re: How can I use a variable from one include() script in another include() script?

Post by rednoah »

rv23 wrote: 04 Jul 2021, 15:48 I wouldn't say it's entirely a groovy thing, as the import statement behaves different between a groovy script run directly by the groovy interpreter and a groovy script run by filebot -script
Packages won't work because you're relying on the file system file structure. That fundamentally doesn't work for FileBot scripting because most script sources are not file system based.
:idea: Please read the FAQ and How to Request Help.
rv23
Posts: 64
Joined: 19 Jul 2020, 15:05

Re: How can I use a variable from one include() script in another include() script?

Post by rv23 »

@Kim - Your spot on, it looks like the best available option is to use a method to return the values, either as a single method/value or single method returning map.

I lack the necessary expertise and knowledge to say for certain what *might* work, I can say that I didn't find an option that worked with filebot -script AND maintained current functionality with using the include() method within the main script. Tho I did find a way that seems to work that will allow me to instantiate a class (something I have wanted to do for a data type instead of using a list of maps).

So I'll recap what I observed (for other's that might be interested, or have the necessary knowledge to make something work)
1. https://code-maven.com/groovy-import-fu ... other-file
While this works in the main script, it also seems to interfere with filebot's include() method and Calling a library method doesn't work/has issues and seems to *only* work with classes (will get StackOverflowError trying to use constants for example).

Code: Select all

def globals = new GroovyScriptEngine( '.' ).with {
  loadScriptByName( 'lib/ConstantsClassStatic.groovy' )
}
this.metaClass.mixin(globals)
println("Main Script referencing lib declaration airDateOrderMatcherRegexStatic:[${airDateOrderMatcherRegexStatic}]")
include('lib/test7')
println getConstants() // Broken/Doesn't work, you get an error trying to run getConstants() from test7.groovy
2. https://stackoverflow.com/questions/485 ... 15#4859715
Try to use an interface called applicationConstants as an alternative to class (it works in native groovy using import), doesn't work with filebot include()

Code: Select all

include('lib/ConstantsInterface')
this.mixin(applicationConstants) // Error: No such property: applicationConstants for class: Script1
println applicationConstants.airDateOrderMatcherRegexStatic
3. https://code-maven.com/groovy-import-fu ... other-file
Constants using @FIELD only work in the main script, and you can run methods from include() files, but I couldn't get the method from the include() to see the @Field constant.
def is required for tools, doesn't work if left undeclared.

Code: Select all

GroovyShell shell = new GroovyShell()
def tools = shell.parse(new File('lib/ConstantsDec.groovy'))
println tools.getProperties()
//[class:class lib.ConstantsDec, airDateOrderMatcherRegexStringField:(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=]), binding:groovy.lang.Binding@3f0846c6]
//(?i)\b(?<![\.\[(])((S\d{1,2}|\d{1,2})(?>\.|\s)?([ExS]\d{1,4})[_]?(?>v\d{1,2})?)\b(?![\.\])=])
println tools.airDateOrderMatcherRegexStringField // Works in MAIN Script only
include('lib/test6')
println getConstants() //No such property: tools for class: lib.Script2
4. https://stackoverflow.com/questions/256 ... oesnt-work
evaluate works in the main script, , and you can run methods from include() files, but I couldn't get the method from the include() to see the constant.

Code: Select all

//constantsDec = evaluate(new File("lib/ConstantsDec.groovy"))
println("Main Script referencing lib declaration airDateOrderMatcherRegexNoDec:[${airDateOrderMatcherRegexNoDec}]") // Works in MAIN script only!
include('lib/test4')
println getConstants() // No such property: airDateOrderMatcherRegexNoDec for class: lib.Script2
User avatar
rednoah
The Source
Posts: 22923
Joined: 16 Nov 2011, 08:59
Location: Taipei
Contact:

Re: How can I use a variable from one include() script in another include() script?

Post by rednoah »

1.
You should be able to use custom classes / objects / methods / fields with this pattern.

lib/hello.groovy

Code: Select all

class Hello {

	def msg = "Hello World"

	def say() {
		println msg
	}
}

return new Hello()
main.groovy

Code: Select all

def hello = runScript('lib/hello')

println hello.msg
hello.say()
e.g.

Code: Select all

$ filebot -script main.groovy
Hello World
Hello World



2.
However, if I just wanted to pass in global variables, then I might just use --def name=value command-line arguments, and possibly supply those via @file syntax for easy editing and reuse.

e.g.

Code: Select all

filebot -script "g:println a" --def a="Hello World"
Hello World
:idea: Please read the FAQ and How to Request Help.
devster
Posts: 417
Joined: 06 Jun 2017, 22:56

Re: How can I use a variable from one include() script in another include() script?

Post by devster »

I found that the following also seems to work (apparently):

partials/stdPart.groovy

Code: Select all

import java.util.regex.Pattern

class Hello {

    Map replaceMap = [
        (~/[`´‘’ʻ""“”]/): "'",
        '|': ' - ',
        ':': '\u2236',
        '?': '\uFE56', // '﹖' Small Question Mark
        '*': '\u204E', // '⁎' low asterisk
        (~/[*\p{Zs}]+/): ' ',
        (~/\b[IiVvXx]+\b/): { String it -> it.upper() },
        (~/\b[0-9](?i:th|nd|rd)\b/): { String it -> it.lower() }
    ]
}

String.metaClass.stdReplace { Map replacer ->
    String tmpStd = delegate
    replacer.each { k1, v ->
        [k1].flatten().each { k2 ->
            pattern = k2.class == Pattern ? k2 : Pattern.quote(k2)
            if (v.class == Closure) {
                tmpStd = tmpStd.replaceAll(pattern, (Closure) v)
            } else {
                tmpStd = tmpStd.replaceAll(pattern, v)
            }
        }
    }
    if (movie) {
        return tmpStd.replaceTrailingBrackets()
    } else if (anime || episode) {
        return tmpStd
    }
    return tmpStd
}

return new Hello()
and then:

main.groovy

Code: Select all

{
	def h = include('/filebot-scripts/partials/stdPart.groovy')
	
	assert '"A|B? xX 3RD"'.stdReplace(h.replaceMap) == '\'A - B﹖ XX 3rd\''
}
I only work in black and sometimes very, very dark grey. (Batman)
Post Reply