Make a usable Variable history "switcher" … just the last 5

I would like to save the last 5 (really, that's one more than I think I'll ever use) timestamps I'm copying from Lightroom Classic (Adobe's photograph database that also handles video). I currently copy the current timestamp (the playhead position) and save it as a Variable and then use it to return the playhead to that position. In between I use the system clipboard. I regularly need to return to the second-last saved timestamp position, and occasionally the third-last saved one.

I need to be able to identify them by order entered: most recent, second last, third last.

I save a new timestamp regularly — thirty to fifty in a work session.

The clipboard history switcher is the only datastore I know that lets one address by position in a queue: %PastClipboard%<Index>%.

The timestamps have a form that makes then easy to find. In my use, nothing else in the clipboard history will have the same form. The form is \d\d:\d\d:\d\d. That's minutes:seconds:frames. I've only seen 8-character strings — but I'm not using any videos longer than an hour.

Is there something in Keyboard Maestro already set up for this kind of storage and retrieval? Is there a "good practice" for setting it up?

Left to my own, I'd probably set up something like "they all rolled over and one fell out". Hoping for something a little more sophisticated.

Thanks.

I expect you could use a For Each action and a Number Range collection to iterate through the PastClipboard tokens looking for copied entries that match the desired format, there by extracting both the index of the entry and the text of the time.

Then you could use a Prompt With List action (unsorted) to ask which time slot you wanted (or perhaps multiple hot keys with the same or different keys for the five entries) to select the desired entry.

Then you could use the Set Clipboard to Past Clipboard action to move the entry to the front and paste it as desired (or if you only need the text to set the timestamp then you can just use the text without altering the clipboard or clipboard history.

3 Likes

Keyboard Maestro Variables can be addressed by array delimiters.

By default the delimiter is a comma ,

For a Global Variable called Lightroom_Timestamp that contains the text

one,two,three

you would access "two" by this syntax

Lightroom_Timestamp[2] or in a text field %Variable% Lightroom_Timestamp[2]%

Lightroom_Timestamp[0] gives you the total number of items stored (in this case 3)

You can also use a custom delimiter to separate the entries. For example you could use the symbol ★ This can be useful if your data entries might contain commas.

So the data in your variable array might look like one★two★three

And for a multiline array like:

one
two
three

you can access specific lines by using \n as the custom delimiter. So to get line 2 into a text field you would use %Variable%Lightroom_Timestamp[2]\n%

For example, you could have a "collecting macro" that builds your variable from the clipboard. Put in a Group that is only active in Lightroom Room Classic, the macro would be triggered each time the Clipboard changes, looks at the clipboard data and if it matches your criteria, appends it to your variable with your delimiter (or appends it to a new line in your variable, if you want to use lines as the delimiter. (Using the Append to Variable Action). As Lightroom_Timestamp is a Global Variable this data is stored between macro runs.

Or, to make this simpler and more robust, instead of being triggered on clipboard changing, you could just give your collecting macro a hotkey and press this each time you want to save an entry (I think this is the way I would do it).

Since this is just text we are saving, no need to limit it to 5 entries. You could limit it to say, 20.

Using [0] to check the total number of entries, if the entries are more than 20 you would remove entry 1 (oldest) entry.

You would have a second Macro that uses this data to restore the playhead. Just like the clipboard history switcher you can call back the data from any position in the array.

The most recent entry would be the total number of entries. Let's say you had collected 7 entries and you were using the delimiter ★

Lightroom_Timestamp[0]★ would return "7"

To get "7" in an easily usable from, set a Variable like this:
Set Local_Total to Lightroom_Timestamp[0]★ (which in this case sets Local_Total to 7)

Use this Variable like this to access the most recent entry in your array:

Lightroom_Timestamp[Local_Total]★

to get the entry before last use

Lightroom_Timestamp[Local_Total - 1]★

In a text field, this is written as %Variable%Lightroom_Timestamp[Local_Total - 1]★%

That should give you the tools to build your Macros.

EDIT - One practical thing to be away of. If you build your array by appending your data plus delimiter symbol each time you might have an array that looks like this:

one★two★three★

When getting the total entries, Lightroom_Timestamp[0]★ would return 4 as the answer rather than 3 as it would expect to see one★two★three to get the answer 3. So, in this case to access the most recent entry you would actually use %Variable%Lightroom_Timestamp[Local_Total - 1]★%

EDIT 2 In my original post I used all Local Variables as examples - but your "collecting" Variable would need to be a Global Variable to store the data between Macro runs. I've edited my original post to make this clearer.

.

3 Likes

I was hoping to get a dirty napkin with some arrows on it and you handed me a gold bar — thank you :blush:.

I didn't know "array variables" from sucking stones. 5★.

I think you've taught me enough not just to fish, but to make nets.

One thing you mention I knew I wasn't sure how to do (others are sure to follow as I knot):

A quick search of this forum brought up one solution: trim with RegEx search and replace, but also turned up setting array elements. Is there a direct (possibly in-built) way to remove an array element by index? I will have to be careful with the delimiter, as you address in your first "Edit" end-note.

As it turns out, for my use I think this will be academic. I need the timestamps for only as long as I am working on a video and I work on only one video at a time and there are never more than a hundred and rarely more than thirty timestamps for any one video. I don't need to save more than the last five — but since the size of the array variable is never going to cause any processing issues, it is simpler, I think, to not bother trimming it — I'll let it grow to include all timestamps for the video being edited, and then clear the array variable before starting on a next video. As is usual, this may provide an opportunity to change my workflow for the better.

My next/first decision is whether to keep the string on one line or to put each timestamp on its own line. I have so often slashed myself on the sharp edges included in \r and \n that I avoid them when I can. Would you say I am likely to regret not using a multiline array?

I will "hard-wire" the collecting of the timestamps into the array, as you suggest.

Nifty! Golden. Thank you.

2 Likes

You are very welcome. Happy New Year!

Yes, I think that is what I would do. It keeps the Macro simpler.

Yes. You do it like this. You can use the same technique to write directly to a particular slot in the array:

So, removing the oldest entry is as simple as:

2 Likes

You're in control, so you'll know you are using \n (or the %LineFeed% token) as your delimiter. And putting your items on separate lines make it really easy to use "Prompt With List" for those times you've forgotten whether the time code you want was 3 or 4 copies ago...

You can keep your "Prompt" ordered by Prepending new items to your variable and turning off "Sort Entries" in the action's options. Leave "Trim Lines" turned on and you won't get annoyed by the trailing blank line...

Prepending has the advantage that the most recent copy is item 1, the copy before that is item 2, etc -- matching how you think of things.

You could also look at a Typed String trigger for getting the code back into Lightroom:

...where typing eg ;l2; would insert the second item of your array.

3 Likes

So, based on the above, here's a "getter" that overrides ⌘C (so you might want to put it in a Group limited to Lightroom), appending copied timecodes to Global_timecodes for you to use later. It'll then remove the timecode from the System Clipboard -- delete the "Delete Current System Clipboard" action if you don't want that to happen. It'll only keep the 5 most recently copied codes -- you can change that number in the first action.

If what you copy isn't a timecode it goes to the System Clipboard as usual.

Collect Last n Time Codes.kmmacros (5.8 KB)

4 Likes

I went prospecting in unknown lands, the kind but aptly-first-named stranger @Zabobon handed me a gold bar, and while I was heading back to my bench to try to whack it into the tool I want you have presented not only a fully-fashioned bodkin of an elegance I am able to marvel at while working to understand, but cord, thimble, and a pouch to draw it together. It's magic. Thank you.

Questions likely anon — I'm sewing all this together — string triggers! — I didn't want my response to wait longer, or to leave the impression this is anything but useful, used, and valued. Chapeau!

2 Likes

Be interesting to see the working Macro when you're done! Glad to be of help.

That's a new one to me (i.e., assigning values to array entries.) Thanks for that revelation. In the past I probably did that using regex, which was much harder.

3 Likes

Yes, I think it was implemented after a request by @tiffle :grinning:

2 Likes

Indeed it was but I think you were the one to discover that you could also then delete array elements @Zabobon :+1:

2 Likes

I am going through this as "step-by-step" as I can in order to maximize my understanding and prevent cracks into which my whole process might disappear.

I tried taking normal strides. I tripped. I ended up undoing every one of them with my fingers crossed until I confirmed I hadn't broken anything.

In making smaller steps, I noticed something that is happening the first and second times I trigger the macro that I don't understand.

Would you or any wise counsel here be kind enough to teach me?

The trigger works: "typing" ⌘c while Lightroom Classic (there are several varieties of Lightroom; "Classic" describes the desktop non-cloud-based one; it is the only one I use) is the active program triggers the macro.

In the following, the actual timecodes don't matter. They are stand-ins for "the string saved that matches the RegEx used". (The RegEx "gate" works afaict and is not apposite here.)

I have removed all content in the variable "Global_timecodes" by selecting all via ⌘a while the computer focus is in the field shown on the right at "Keyboard Maestro ▹ Settings ▹ Preferences: Variables ▹ Global_timecodes".

The blinking insertion cursor shows.

First time the macro is triggered the content of the variable Global_timecodes shows:

00:20:22
00:20:22

I expect it to show only one entry.

Second time the macro is triggered the variable shows:

00:34:19
00:20:22
00:20:22
00:34:19

I expect it show only the two different timecodes

This pattern — timecode repeated as AA each on a line, then timecodes repeated as BAAB each on a line — happens every time.

The third and every subsequent time the macro behaves as expected: a new timecode is prepended, and when n is exceeded the least-recent timecode is deleted.

So on third triggering, with n set to "5":

00:59:13
00:34:19
00:20:22
00:20:22
00:34:19

and on, say, sixth triggering:

02:08:12
01:39:19
01:17:24
00:59:13
00:34:19

I understand that this is likely not going to make any difference in practice. Unfortunately, I don't know enough to rely on that. I am planning to execute this macro from another (rather than triggering it by "⌘c" and want to understand what the macro does before changing it in any way.

Thanks.

You tripped -- over my carelessly left lump of steaming stupidity...

In messing around with listing the copies in "most recent" and "least recent" order, this idiot left both the "Append to Variable" and the "Prepend to Variable" actions in. D'oh!

That's why you are getting two entries. Because I'm an absolute maroon...

Corrected version:

Collect Last n Time Codes (Corrected).kmmacros (5.4 KB)

Image

1 Like

First and foremost, thank you for fixing this (and explaining the fix).

Importantly: you are doing me the large and largely un-thankable kindness of not only sharing your expertise but also doing so in a way customized to my particular and not particularly fungible need AND doing it in a way that lets me gain tool-building expertise along with a specialized tool. Let me propose, with the expectation of your acceptance, the following "Maestro" agreement:

You and others provide information at the speed of kindness without the brake of triple-checking :blush:; I and others will voice our own follow-ups without fear of having our ignorance judged as stupidity; you won't castigate yourself for lumps in your kindness; I won't spend ninety minutes testing everything six times before asking my follow-up.

Deal?

I am finally making progress! My most recent lump, the result of which was a good bit of on-screen jitter and nonsense timecodes, was to have specified in multiple places the index of the array variable and forgotten to specify the separator. I have again slash-en'd myself — and lo! magic healed my boo-boos with knowledge shared, found, and re-found here.

Because we are not marooned :wink: .

1 Like