It is currently March 28th, 2024, 11:20 pm

RunCommand is using Measure values from the last update cycle

Get help with creating, editing & fixing problems with skins
kounger
Posts: 13
Joined: March 22nd, 2017, 12:14 am

RunCommand is using Measure values from the last update cycle

Post by kounger »

Hello, I was working on a music skin and at one point my skin looks for a random album cover image and then has to look for a music file that belongs to that cover. For that I need to have the option to get the content of a folder so I can iterate through each file. Unfortunately the io.popen function in Lua which gets a list of files from a folder doesn't work with Rainmeter. I looked for a workaround and wrote a powershell script that writes the content of a folder into a text file. I can then use that text file to find the music file I need.

Here's the problem: I run the powershell script using the RunCommand plugin. RunCommand has to be called with a bang so every time a new album cover is extracted the RunCommand measure is called and runs the powershell script that writes the list of files. For some reason RunCommand seems to lag behind and uses the Measure Values from the last update cycle. So in the end the skin shows the music file that matches the previous album cover which isn't right. What's also weird is that if you call RunCommand with "OnRefreshAction" it will work right for the first cycle. Using "OnUpdateAction" is no alternative though since this is too taxing on the CPU.

It would be nice if someone could help me with this problem. ;-)


Here are all parts of my skin needed to reproduce the problem:

The .ini file:

Code: Select all

[Rainmeter]
Update=1000

;OnUpdateAction=[!CommandMeasure "MeasureRunFolderContent" "Run"]
;OnRefreshAction=[!CommandMeasure MeasureRunFolderContent "Run"]

[Variables]
MusicPath=#@#Albums

[StringStyle]
FontFace=Courier New
FontSize=8
FontColor=255,255,255,255
StringAlign=LEFT
StringEffect=Border
Antialias=1

###Measures###
[MeasureRandomCover]
Measure=Plugin
Plugin=QuotePlugin
PathName=#MusicPath#
Subfolders=1
FileFilter=*.jpg;*.png;*.gif
UpdateDivider=5
OnChangeAction=[!UpdateMeasureGroup "Image"]

;This lua script will get the file path to the image [MeasureRandomCover] extracted.
[MeasureLuaGetImagePath]
Measure=Script
ScriptFile=#@#GetImagePath.lua
TableName=NoPath
Group=Image
UpdateDivider=-1
OnChangeAction=[!CommandMeasure MeasureRunFolderContent "Run"]
;DynamicVariables=1

[MeasureStringCoverFolder]
Measure=String
String=[MeasureLuaGetImagePath]
RegExpSubstitute=1
Substitute="\\[^\\]*$":""
Group=Image
UpdateDivider=-1
DynamicVariables=1

[MeasureStringCoverFolderParent]
Measure=String
String=[MeasureLuaGetImagePath]
RegExpSubstitute=1
Substitute="\\[^\\]*\\[^\\]*$":""
Group=Image
UpdateDivider=-1
DynamicVariables=1

;This measure calls a powershell script that gets the filepaths of all files inside [MeasureStringCoverFolder] and [MeasureStringCoverFolderParent]
;and writes them into the output file #@#\TempFileList.txt. 
[MeasureRunFolderContent]
Measure=Plugin
Plugin=RunCommand
Program=PowerShell.exe
Parameter=-NoProfile -ExecutionPolicy Bypass -Command "& '#@#\FolderContent.ps1' '[MeasureStringCoverFolder]' '[MeasureStringCoverFolderParent]' '#@#\TempFileList.txt'"
State=Hide
FinishAction=[!UpdateMeasure MeasureLuaFindMusicFile]
DynamicVariables=1

;This lua script will determine if the image from [MeasureStringImagePath] is possibly
;an album cover. It will then find the album folder with all music files that belongs to the 
;cover. It will return false if no album folder can be found or it returns the path to a music
;file from the album folder. This music file can then be used to extract more metadata about
;the album.
[MeasureLuaFindMusicFile]
Measure=Script
ScriptFile=#@#FindMusicFile.lua
TableName=NoPath
UpdateDivider=-1
;DynamicVariables=1

[MeasureStringFindMusicFile]
Measure=String
String=[MeasureLuaFindMusicFile]
DynamicVariables=1

###Meters###
[MeterImage]
Meter=Image
MeasureName=MeasureRandomCover
X=10
Y=10
H=100
W=100
AntiAlias=1 

[MeterImagePath]
Meter=STRING
MeasureName=MeasureLuaGetImagePath
MeterStyle=StringStyle  
ClipString=2
Text=Found Cover Path: %1
X=120
Y=10
W=200

[MeterFoundFile]
Meter=STRING
MeasureName=MeasureStringFindMusicFile
MeterStyle=StringStyle  
ClipString=2
Text=Found Music File: %1
X=120
Y=70
W=200
H=300
The powershell script:

Code: Select all

$inputFolderOne = $args[0]
$inputFolderTwo = $args[1]
$outputPath = $args[2]

try
{
    $stream = [System.IO.StreamWriter]::new( $outputPath )
    Get-ChildItem -LiteralPath "$inputFolderOne","$inputFolderTwo" | ForEach-Object { $stream.WriteLine($_.FullName) }
}
finally
{
    $stream.close()
    #See which values the PowerShell script used:
    #start cmd "/c echo $inputFolderOne && timeout 5"    
}
An image of the test skin. You can see that the found music file (just renamed text files in this skin, no actual music files) doesn't match the found cover image.
Image
You do not have the required permissions to view the files attached to this post.
kounger
Posts: 13
Joined: March 22nd, 2017, 12:14 am

Re: RunCommand is using Measure values from the last update cycle

Post by kounger »

Using OnChangeAction with [!CommandMeasure RunCommandMeasureName "Run"] just seems to be broken. I found a simple workaround for me though. Since OnRefreshAction=[!CommandMeasure RunCommandMeasureName "Run"] does what I want on the first update cycle I just use the [!Refresh] bang after x seconds to be able to correctly run the script again with the right values. There should be other workarounds too by saving results in variables to get around the one cycle delay without refreshing but this is a good enough solution in my case.

Code: Select all

[Rainmeter]
Update=1000
OnRefreshAction=[!CommandMeasure MeasureRunFolderContent "Run"]

[Variables]
MusicPath=#@#Albums
;Refresh every 10 seconds: (10+1)!!!
Counter=11

[StringStyle]
FontFace=Courier New
FontSize=8
FontColor=255,255,255,255
StringAlign=LEFT
StringEffect=Border
Antialias=1

###Measures###
[MeasureRandomCover]
Measure=Plugin
Plugin=QuotePlugin
PathName=#MusicPath#
Subfolders=1
FileFilter=*.jpg;*.png;*.gif
UpdateDivider=-1
OnChangeAction=[!Refresh]

;This lua script will get the file path to the image [MeasureRandomCover] extracted.
[MeasureLuaGetImagePath]
Measure=Script
ScriptFile=#@#GetImagePath.lua
TableName=NoPath
UpdateDivider=-1

[MeasureStringCoverFolder]
Measure=String
String=[MeasureLuaGetImagePath]
RegExpSubstitute=1
Substitute="\\[^\\]*$":""
UpdateDivider=-1
DynamicVariables=1

[MeasureStringCoverFolderParent]
Measure=String
String=[MeasureLuaGetImagePath]
RegExpSubstitute=1
Substitute="\\[^\\]*\\[^\\]*$":""
UpdateDivider=-1
DynamicVariables=1

;This measure calls a powershell script that gets the filepaths of all files inside [MeasureStringCoverFolder] and [MeasureStringCoverFolderParent]
;and writes them into the output file #@#\TempFileList.txt. 
[MeasureRunFolderContent]
Measure=Plugin
Plugin=RunCommand
Program=PowerShell.exe
Parameter=-NoProfile -ExecutionPolicy Bypass -Command "& '#@#\FolderContent.ps1' '[MeasureStringCoverFolder]' '[MeasureStringCoverFolderParent]' '#@#\TempFileList.txt'"
State=Hide
FinishAction=[!UpdateMeasureGroup "AfterRun"]
DynamicVariables=1

;This lua script will determine if the image from [MeasureStringImagePath] is possibly
;an album cover. It will then find the album folder with all music files that belongs to the 
;cover. It will return false if no album folder can be found or it returns the path to a music
;file from the album folder. This music file can then be used to extract more metadata about
;the album.
[MeasureLuaFindMusicFile]
Measure=Script
ScriptFile=#@#FindMusicFile.lua
TableName=NoPath
Group=AfterRun
UpdateDivider=-1

[MeasureStringFindMusicFile]
Measure=String
String=[MeasureLuaFindMusicFile]
DynamicVariables=-1
Group=AfterRun
UpdateDivider=-1

;Refreshes the skin after #Counter#-1 seconds
[MeasureRefreshTimer]
Measure=Calc
Formula=MeasureRefreshTimer+1
IfEqualValue=#Counter#
IfEqualAction=[!Refresh]
DynamicVariables=1

###Meters###
[MeterImage]
Meter=Image
MeasureName=MeasureRandomCover
X=10
Y=10
H=100
W=100
AntiAlias=1 

[MeterImagePath]
Meter=STRING
MeasureName=MeasureLuaGetImagePath
MeterStyle=StringStyle  
ClipString=2
Text=Found Cover Path: %1
X=120
Y=10
W=200

[MeterFoundFile]
Meter=STRING
MeasureName=MeasureStringFindMusicFile
MeterStyle=StringStyle  
ClipString=2
Text=Found Music File: %1
X=120
Y=70
W=200
H=300
User avatar
Brian
Developer
Posts: 2673
Joined: November 24th, 2011, 1:42 am
Location: Utah

Re: RunCommand is using Measure values from the last update cycle

Post by Brian »

This seems to be a timing issue.

When the quote plugin changes, you are updating the group "Image". This bang immediately updates all the measures in that group before continuing on the normal update cycle. So you would expect the update order to be: MeasureRandomCover, MeasureLuaGetImagePath, MeasureStringCoverFolder, MeasureStringCoverFolderParent....then: MeasureRunFolderContent, MeasureLuaFindMusicFile, and MeasureStringFindMusicFile.

However....
The first measure in the group also has a OnChangeAction..so this complicates the 'order' in which things are updated. Once the first measure in that group updates, it immediately runs the RunCommand measure before the rest of the 'Image' group is updated. Thus the RunCommand measure is using the previous section variables that have not been updated yet. The real update order of the skin is actually: MeasureRandomCover, MeasureLuaGetImagePath, MeasureRunFolderContent, MeasureFindMusicFile, MeasureStringCoverFolder, MeasureStringCoverFolderParent, MeasureStringFindMusicFile.

Basically, the RunCommand measure is ran before the other 'Image' group measures are updated.

But...there is another issue. In order for the RunCommand measure to use the 'current' section variables, it needs to be updated before running.

To fix your issue I would use OnUpdateAction instead of OnChangeAction and move it to the last measure in the 'Image' group (MeasureStringCoverFolderParent).

Code: Select all

[Rainmeter]
Update=1000

;OnUpdateAction=[!CommandMeasure "MeasureRunFolderContent" "Run"]
;OnRefreshAction=[!CommandMeasure MeasureRunFolderContent "Run"]

[Variables]
MusicPath=#@#Albums

[StringStyle]
FontFace=Courier New
FontSize=8
FontColor=255,255,255,255
StringAlign=LEFT
StringEffect=Border
Antialias=1

###Measures###
[MeasureRandomCover]
Measure=Plugin
Plugin=QuotePlugin
PathName=#MusicPath#
Subfolders=1
FileFilter=*.jpg;*.png;*.gif
UpdateDivider=5
OnChangeAction=[!UpdateMeasureGroup "Image"]

;This lua script will get the file path to the image [MeasureRandomCover] extracted.
[MeasureLuaGetImagePath]
Measure=Script
ScriptFile=#@#GetImagePath.lua
TableName=NoPath
Group=Image
UpdateDivider=-1
;DynamicVariables=1

[MeasureStringCoverFolder]
Measure=String
String=[MeasureLuaGetImagePath]
RegExpSubstitute=1
Substitute="\\[^\\]*$":""
Group=Image
UpdateDivider=-1
DynamicVariables=1

[MeasureStringCoverFolderParent]
Measure=String
String=[MeasureLuaGetImagePath]
RegExpSubstitute=1
Substitute="\\[^\\]*\\[^\\]*$":""
Group=Image
UpdateDivider=-1
DynamicVariables=1
OnUpdateAction=[!UpdateMeasure MeasureRunFolderContent][!CommandMeasure MeasureRunFolderContent "Run"]

;This measure calls a powershell script that gets the filepaths of all files inside [MeasureStringCoverFolder] and [MeasureStringCoverFolderParent]
;and writes them into the output file #@#\TempFileList.txt. 
[MeasureRunFolderContent]
Measure=Plugin
Plugin=RunCommand
Program=PowerShell.exe
Parameter=-NoProfile -ExecutionPolicy Bypass -Command "& '#@#\FolderContent.ps1' '[MeasureStringCoverFolder]' '[MeasureStringCoverFolderParent]' '#@#\TempFileList.txt'"
State=Hide
FinishAction=[!UpdateMeasure MeasureLuaFindMusicFile]
DynamicVariables=1

;This lua script will determine if the image from [MeasureStringImagePath] is possibly
;an album cover. It will then find the album folder with all music files that belongs to the 
;cover. It will return false if no album folder can be found or it returns the path to a music
;file from the album folder. This music file can then be used to extract more metadata about
;the album.
[MeasureLuaFindMusicFile]
Measure=Script
ScriptFile=#@#FindMusicFile.lua
TableName=NoPath
UpdateDivider=-1
;DynamicVariables=1

[MeasureStringFindMusicFile]
Measure=String
String=[MeasureLuaFindMusicFile]
DynamicVariables=1

###Meters###
[MeterImage]
Meter=Image
MeasureName=MeasureRandomCover
X=10
Y=10
H=100
W=100
AntiAlias=1 

[MeterImagePath]
Meter=STRING
MeasureName=MeasureLuaGetImagePath
MeterStyle=StringStyle  
ClipString=2
Text=Found Cover Path: %1
X=120
Y=10
W=200

[MeterFoundFile]
Meter=STRING
MeasureName=MeasureStringFindMusicFile
MeterStyle=StringStyle  
ClipString=2
Text=Found Music File: %1
X=120
Y=70
W=200
H=300
Hopefully that makes sense.

-Brian
kounger
Posts: 13
Joined: March 22nd, 2017, 12:14 am

Re: RunCommand is using Measure values from the last update cycle

Post by kounger »

Brian wrote:

Code: Select all

OnUpdateAction=[!UpdateMeasure MeasureRunFolderContent][!CommandMeasure MeasureRunFolderContent "Run"]
Thank you so much for your help Brian. My workaround from my previous post with the [!Refresh] bang already failed since I eventually need a countdown that mustn't be refreshed.

Then I tried to even out the one cycle delay by saving the values I need in string measures before these values update again. That seemed to work until I got to the point where I had to jump back to the QuoteMeasure whenever my lua script determined that the image this Measure extracted could not be a cover. At that point I think the RunCommand started to lag two cycles behind. :D

I actually tried to call the CommandMeasure bang from many positions inside the skin, also outside the Group "Image". I also wrote a lua script to call the bang but everything led to the same result so I put it back at the top inside QuoteMeasure which shouldn't be done after all.

I never used [!UpdateMeasure MeasureRunFolderContent] though. I thought only Meters have to be updated that way to get the new value from the measure. With the RunCommand Measure I falsely thought DynamicVariables=1 would take care of that.

Thankfully this part of my skin works now. Only my powershell script sometimes throws an exception with some folder paths. I will explain this io.popen workaround in the Lua Tips&Tricks thread once I have this figured out. I will also post the skin in the "Share Your Creations" Forum once it's finished.
User avatar
Brian
Developer
Posts: 2673
Joined: November 24th, 2011, 1:42 am
Location: Utah

Re: RunCommand is using Measure values from the last update cycle

Post by Brian »

kounger wrote:I never used [!UpdateMeasure MeasureRunFolderContent] though. I thought only Meters have to be updated that way to get the new value from the measure. With the RunCommand Measure I falsely thought DynamicVariables=1 would take care of that.
DynamicVariables=1 does take care of that, however, you may not see any changes until the next update cycle. !UpdateMeasure/Meter will tell Rainmeter to update the object *now* and not wait until the next update cycle - which is needed in this case since other measures are dependent on previous measures. It's all about timing.

-Brian
kounger
Posts: 13
Joined: March 22nd, 2017, 12:14 am

Re: RunCommand is using Measure values from the last update cycle

Post by kounger »

Ah okay. Thanks for clearing that up and helping me to understand how to work with RunCommand and UpdateDivider=-1. :thumbup:

I also mentioned my powershell script failing sometimes. To clear that up: That only happens when there is a single quote ' in the name. Cmd doesn't like that so before a path to a folder is given to the RunCommand measure every single quote has to be escaped with another single quote: Substitute="'":"''"

UPDATE:
In this post I explain the workaround in more detail:

https://forum.rainmeter.net/viewtopic.php?p=148989#p148989