An efficient method for assigning numeric inputs into %output%

I had been working on a way to select/input cover size for an iTunes source script for quite some time; and with the new features of Mp3Tag v3.22+, I developed a method to do it (more or less).

Since this method can work with any number, and I was unable to find any topic (other than my own) regarding this subject, I've created this topic to explain how I did it, so other authors can benefit from it.

Until v3.22, there was only one field available for direct user input - [SearchBy]. With v3.22, the introduction of a settings schema now allowed adding string, boolean and integer values from outside of a script, and the ability to refer to them using ifVar or ifNotVar. For example, setting the number '5' in the schema, like

{
  "type": "number",
  "key": "testNumber",
  "title": "Something to enter a number",
  "description": "Enter a number",
  "default": 42
},

creates a input field with "42" pre-written (but this can be replaced with "5"). This field could then be called from the script, like

IfVar "testNumber" "5"
...
Say "5"
...
EndIf

in a simple manner. In this example, the number '5' is indirectly 'translated' from a 'Settings' value into a script value. With a minor tweak, it could even be stored in an output, like

IfVar "testNumber" "5"
   OutputTo "testNumber"
   Say "5"
EndIf

so that this value could now be called further down with sayOutput "testNumber".

But this only works for a single number. Typing any number other than '5' in the input field won't return any value in the script. For it to work with a number range, ifVar conditions must be set for each number in the range. For a range [0-9}, this would mean

IfVar "testNumber" "0"
   OutputTo "testNumber"
   Say "0"
EndIf
IfVar "testNumber" "1"
   OutputTo "testNumber"
   Say "1"
EndIf
IfVar "testNumber" "2"
   OutputTo "testNumber"
   Say "2"
EndIf
...
IfVar "testNumber" "9"
   OutputTo "testNumber"
   Say "9"
EndIf

So, 4 lines of code per digit x 10 possible numbers = 40 lines of code for a single-digit number to be translated. This can be further optimized, by replacing both outputTo "testNumber" and say "N" commands with set "testNumber" "N". So, 3 lines * 10 = 30 lines of code; which is perfectly doable.

With a 2-digit number range [00-99], it would take 3 * 100 = 300 lines of code; which is unpractical, but still doable. Bigger numbers (like a cover size in pixels, which range in the thousands) would be extremely impractical and very resource-heavy for a script to process.

An easier way to translate 2-digit numbers is to split them into 2 single-digit inputs in the schema, translate each one separately into the script with ifVar, and then join the 2 outputs back into a 2-digit number, like

OutputTo "fullNumber"
   Say "testNumber1"
   Say "testNumber2"

Translating the number "42" can now be done by inputting "4" into "testNumber1" and "2" into "testNumber2" in the schema, using 2 sets of ifVar for each digit, and ending with outputTo "fullNumber" to merge the 2 digits back into "42". This would take 3*10 * 2 (sets of ifVar) + 3 lines for outputting "fullNumber" = 63 lines of code. Which is far less than the 300 lines needed before, but is still quite a sum.

I've improved this concept, and managed to translate a 4-digit number in 23 lines of code. It requires use of Mp3Tag 3.23, JSON tools and %output% buffer referral. If you don't know how these tools works, you may not fully understand the following.

For a 2-digit number (like "42"), on the configuration schema side, simply create 2 number input fields; one for each digit of the number we want. That's all that is necessary from this end.

In the script, using json_foreach, it's possible to iterate over a JSON-formatted data array and perform actions for each element in that array. If our input data had an array with the single-digit numbers, it could be accessed and parsed into "testNumberN".

But normally, input data doesn't have those values, or it may not be JSON data at all.

What to do? Write our own JSON array!
How?

First, we clear the current buffer; this can be achieved with a regular expression (you may want to learn about those too) like

RegexpReplace "^.*" ""

Next, we write a JSON-formatted array into the buffer; for efficiency, I'm adding this step into the previous one, like

RegexpReplace "^.*" "{"array":[{"number":0},{"number":1},{"number":2},{"number":3},{"number":4},{"number":5},{"number":6},{"number":7},{"number":8},{"number":9}]}"

Next, we need to tell Mp3Tag to consider the text currently in the buffer as JSON data, which is done with

json "on" "current"

Now we are working in JSON "mode" and have a JSON array with [0-9]. Next we'll check its contents sequentially and use them as needed. Let's say

json_foreach "array"
   json_select "number"
   OutputTo "digit"
   SayRest
json_foreach_end

This reads -- for all elements in the array, select the "number" elements, and write them to "digit"--.
The output digit will be filled with each value in the array, step by step: "0", then "01", 012", "0123", "01234", "012345", "0123456", "01234567", "012345678", and "0123456789".

Now lets add an ifVar

json_foreach "array"
   json_select "number"
   OutputTo "digit"
   SayRest

   IfVar "testNumber1" "%digit%"
	   OutputTo "testNumber1"
	   SayRest
   EndIf

json_foreach_end

This is the same ifVar command as shown before; the only difference is that instead of comparing "testNumber1" to a number, it is now comparing with the current value of "digit" (which is being filled step-by-step with the values in the array, which are numbers). Going back to our number "42", which was split into "testNumber1" (4) and "testNumber2" (2), this loop now processes as follows

Loop 1: Select array value "0" / Output to "digit":"[empty]" / Write "0" into "digit" / IfVar "testNumber1" (4) is %digit%" (0) / false
Loop 2: Select array value "1" / Output to "digit":"0" / Write "1" into "digit" / IfVar "testNumber1" (4) is %digit%" (01) / false
Loop 3: Select array value "2" / Output to "digit":01 / Write "2" into "digit" / IfVar "testNumber1" (4) is %digit%" (012) / false
Loop 4: Select array value "3" / Output to "digit":"012" / Write "3" into "digit" / IfVar "testNumber1" (4) is %digit%" (0123) / false
...
Loop 10: Select array value "9" / Output to "digit":"012345678" / Write "9" into "digit" / IfVar "testNumber1" (4) is %digit%" (0123456789) / false

Our output "testNumber1" stayed empty because ifVar never matched "testNumber1" (4) with "digit", because the contents of "digit" were being appended to the end of the its current value.
Let's try again, but now we clear "digit" at the end of each loop:

json_foreach "array"
   json_select "number"
   OutputTo "digit"
   SayRest

   IfVar "testNumber1" "%digit%"
     OutputTo "testNumber1"
     SayRest
   EndIf

   Set "digit"
json_foreach_end

Running our (modified) loop again

Loop 1: Select array value "0" / Output to "digit":"[empty]" / Write "0" into "digit" / IfVar "testNumber1" (4) is %digit%" (0) / false / Clear "digit":"0"
Loop 2: Select array value "1" / Output to "digit":"[empty]" / Write "1" into "digit" / IfVar "testNumber1" (4) is %digit%" (1) / false / Clear "digit":"1"
Loop 3: Select array value "2" / Output to "digit":"[empty]" / Write "2" into "digit" / IfVar "testNumber1" (4) is %digit%" (2) / false / Clear "digit":"2"
Loop 4: Select array value "3" / Output to "digit":"[empty]" / Write "3" into "digit" / IfVar "testNumber1" (4) is %digit%" (3) / false / Clear "digit":"3"
Loop 5: Select array value "4" / Output to "digit":"[empty]" / Write "4" into "digit" / IfVar "testNumber1" (4) is %digit%" (4) / TRUE / OutputTo "testnumber1":"[empty]" / Write "4" into "testnumber1" / Clear "digit":"4"
Loop 6: ...
...
Loop 10: ...

Success! "4" has been written into "testNumber1". The rest of the loop will still run, but since there will be no match, it isn't important.

As we have done for "testNumber1", let's add "testNumber2" and check both at the same time

json_foreach "array"
   json_select "number"
   OutputTo "digit"
   SayRest

   IfVar "testNumber1" "%digit%"
      OutputTo "testNumber1"
      SayRest
   EndIf

   IfVar "testNumber2" "%digit%"
      OutputTo "testNumber2"
      SayRest
   EndIf

   Set "digit"
json_foreach_end

Now, after "testnumber1" (4), "testnumber2" (2) will be compared with the current value of "digit". When it matches, that value is written into its dedicated output "testNumber2". When it doesn't match, the loop keeps running until it does, then continues until the last array element has been checked.

Finally, after having both outputs (now set at "4" and "2"), we can do as before

OutputTo "fullNumber"
	Say "testNumber1" (4)
	Say "testNumber2" (2)

And we get our "fullNumber" (42)

The entire code from start to finish reads

RegexpReplace "^.*" "{"array":[{"number":0},{"number":1},{"number":2},{"number":3},{"number":4},{"number":5},{"number":6},{"number":7},{"number":8},{"number":9}]}"

json "on" "current"

json_foreach "array"
	json_select "number"
	OutputTo "digit"
	SayRest

	IfVar "testNumber1" "%digit%"
		OutputTo "testNumber1"
		SayRest
	EndIf

	IfVar "testNumber2" "%digit%"
		OutputTo "testNumber2"
		SayRest
	EndIf

	Set "digit"
json_foreach_end

OutputTo "fullNumber"
	Say "testNumber1"
	Say "testNumber2"

A 2-digit integer has indeed been translated from 2 user input fields from the Settings menu into a "fullNumber" output that can now be called as needed. And (after removing whitespaces) this was done in 19 lines of code.
Which is much less than 63, and much much less than 300.

If you've read my previous post regarding this theme, I had been asking for a "sayVar" command to directly write input fields into the current buffer. But this method is a better fit with a "setVar" command, so that

setVar "testNumber1" "testNumber1"

would translate the "testNumber1" value from the schema into a "testNumber1" output.

As I said before, I've worked this principle into my own WS script for iTunes, and managed to get a custom 4-digit number into a working value for setting cover size, and using only 23 lines of code.

If you are a fellow author and can use this method for your own needs please do so. Feedback is not necessary, but would be appreciated.

Thank you.

1 Like