Dialog/button for batch timeshifting (picture) metadata with syntax check

This button will show a dialog (if you have selected files) to enter a timeshift string and picking a reference date (https://www.gpsoft.com.au/help/opus12/index.html#!Documents/Time_Shifting.htm).
It does syntax checking for the timeshift string. If it is valid, it will apply the timeshift with the selected reference date with the SetAttr command to the selected files.

image

  • If you dont want the help text feel free to edit the dialogsize in the buttons resources.
  • The syntax check for the value for year is arbitrary (3000, was just the next millenial from now)
  • Leading 0s are allowed

Installation
Edit your menu (via context menu "customize") and copy the code below and paste into your menu (contextmenu "paste")

<?xml version="1.0"?>
<button backcol="none" display="both" label_pos="right" textcol="none">
	<label>Time Shift</label>
	<icon1>#setdate</icon1>
	<function type="script">
		<instruction>@disablenosel</instruction>
		<instruction>@script JScript</instruction>
		<instruction>function OnClick(clickData)</instruction>
		<instruction>{</instruction>
		<instruction>	ShowDialog(clickData, &quot;&quot;, -1);</instruction>
		<instruction>}</instruction>
		<instruction />
		<instruction>//Show the dialog and do syntax check</instruction>
		<instruction>//Is an extra function because dialog can be closed with wrong data and needs to be shown again</instruction>
		<instruction>function ShowDialog(clickData, pickedTimeshift, pickedSourceDate)</instruction>
		<instruction>{</instruction>
		<instruction>	var dlg = DOpus.Dlg;</instruction>
		<instruction>	dlg.window = clickData.func.sourcetab;</instruction>
		<instruction>	dlg.template = &quot;timeshiftdialog&quot;;</instruction>
		<instruction>	dlg.detach = true;</instruction>
		<instruction>	dlg.Create();</instruction>
		<instruction>	</instruction>
		<instruction>	if(pickedTimeshift != &quot;&quot;)</instruction>
		<instruction>		dlg.Control(&quot;editTimeShift&quot;).value = pickedTimeshift;</instruction>
		<instruction>	dlg.Control(&quot;comboSourceDate&quot;).value = pickedSourceDate == -1 ? 2 : pickedSourceDate; // Default is datetaken</instruction>
		<instruction />
		<instruction>	dlg.Show();</instruction>
		<instruction />
		<instruction>	while (true) </instruction>
		<instruction>	{</instruction>
		<instruction>	    var msg = dlg.GetMsg();</instruction>
		<instruction>	    if (!msg.result) </instruction>
		<instruction>			break;</instruction>
		<instruction />
		<instruction>		var timeShift = String(dlg.Control(&quot;editTimeShift&quot;).value);</instruction>
		<instruction>		if(TestTimeshiftString(timeShift))</instruction>
		<instruction>			dlg.Control(&quot;staticSyntax&quot;).bg = &quot;0,255,0&quot;;</instruction>
		<instruction />
		<instruction>		else</instruction>
		<instruction>			dlg.Control(&quot;staticSyntax&quot;).bg = &quot;255,0,0&quot;;</instruction>
		<instruction>		</instruction>
		<instruction>		if (msg.event == &quot;click&quot;)</instruction>
		<instruction>		{</instruction>
		<instruction>			if(msg.control == &quot;buttonApply&quot;)</instruction>
		<instruction>			{</instruction>
		<instruction>				if(TestTimeshiftString(timeShift))</instruction>
		<instruction>					dlg.EndDlg(0);</instruction>
		<instruction>			}				</instruction>
		<instruction>			if(msg.control == &quot;buttonDocs&quot;) </instruction>
		<instruction>				DOpus.NewCommand.RunCommand(&quot;https://www.gpsoft.com.au/help/opus12/index.html#!Documents/Time_Shifting.htm&quot;);</instruction>
		<instruction>			if(msg.control == &quot;buttonCancel&quot;)</instruction>
		<instruction>				dlg.EndDlg(1);</instruction>
		<instruction>		}</instruction>
		<instruction>	}</instruction>
		<instruction>	</instruction>
		<instruction>	if(dlg.result == 0)</instruction>
		<instruction>	{</instruction>
		<instruction>		var sourceDateIndex = dlg.Control(&quot;comboSourceDate&quot;).value;</instruction>
		<instruction>		var timeShift = String(dlg.Control(&quot;editTimeShift&quot;).value);</instruction>
		<instruction />
		<instruction>		if(!TestTimeshiftString(timeShift)) //</instruction>
		<instruction>		{</instruction>
		<instruction>			var errorDlg = clickData.func.Dlg;</instruction>
		<instruction>			errorDlg.message = &quot;The timeshift string you entered is wrong&quot;;</instruction>
		<instruction>			errorDlg.buttons = &quot;OK&quot;;</instruction>
		<instruction>			errorDlg.title = &quot;Wrong timeshift string&quot;;</instruction>
		<instruction>			errorDlg.Show();</instruction>
		<instruction>			ShowDialog(clickData);</instruction>
		<instruction>		}</instruction>
		<instruction>		else if(sourceDateIndex == -1)</instruction>
		<instruction>		{</instruction>
		<instruction>			var errorDlg = clickData.func.Dlg;</instruction>
		<instruction>			errorDlg.message = &quot;Pick date for timeshift origin&quot;;</instruction>
		<instruction>			errorDlg.buttons = &quot;OK&quot;;</instruction>
		<instruction>			errorDlg.title = &quot;Incomplete data&quot;;</instruction>
		<instruction>			errorDlg.Show();</instruction>
		<instruction>			if(TestTimeshiftString(timeShift))</instruction>
		<instruction>				ShowDialog(clickData, timeShift, -1);</instruction>
		<instruction>			else</instruction>
		<instruction>				ShowDialog(clickData, &quot;&quot;, -1);</instruction>
		<instruction>		}</instruction>
		<instruction>		else//all correct</instruction>
		<instruction>		{</instruction>
		<instruction>			var dates = [&quot;createdate&quot;, &quot;datedigitized&quot;, &quot;datetaken&quot;, &quot;lastmodifieddate&quot;];</instruction>
		<instruction>			ShiftTime(clickData, dates[sourceDateIndex], timeShift);</instruction>
		<instruction>		}</instruction>
		<instruction>	}</instruction>
		<instruction>	else if(dlg.result == 1)</instruction>
		<instruction>		return;</instruction>
		<instruction>}</instruction>
		<instruction />
		<instruction>// Test for syntax</instruction>
		<instruction>function TestTimeshiftString(timeShift)</instruction>
		<instruction>{</instruction>
		<instruction>	var regex = /^((\+|\-)?((0{0,3}[0-9]|0{0,2}[1-9][0-9]|0?[1-9][0-9]{2}|[12][0-9]{3}|3000):(0?[1-9]|1[0-2]):(0?[1-9]|[12][0-9]|3[01])|(0?[1-9]|1[0-2]):(0?[1-9]|[12][0-9]|3[01])|(0?[1-9]|[12][0-9]|3[01])) )?(\+|\-)?((0?[0-9]|1[0-9]|2[0-3]):(0?[0-9]|[1-5][0-9]):(0?[0-9]|[1-5][0-9])|(0?[0-9]|1[0-9]|2[0-3]):(0?[0-9]|[1-5][0-9])|(0?[0-9]|1[0-9]|2[0-3]))$/;</instruction>
		<instruction>	var matches = timeShift.match(regex);</instruction>
		<instruction>	return matches != null;</instruction>
		<instruction>}</instruction>
		<instruction />
		<instruction>//Apply Timeshift to selected files</instruction>
		<instruction>function ShiftTime(clickData, sourceDate, timeShift)</instruction>
		<instruction>{</instruction>
		<instruction>	var selectedFiles = clickData.func.sourcetab.selected_files;</instruction>
		<instruction>	var cmd = DOpus.Create.Command;</instruction>
		<instruction>	cmd.AddLine(&quot;SetAttr META \&quot;&quot; + sourceDate + &quot;:&quot; + timeShift + &quot;\&quot;&quot;);</instruction>
		<instruction>	Log(&quot;SetAttr META \&quot;&quot; + sourceDate + &quot;:&quot; + timeShift + &quot;\&quot;&quot;);</instruction>
		<instruction>	cmd.SetModifier(&quot;nodeselect&quot;);</instruction>
		<instruction>	cmd.AddFiles(selectedFiles);</instruction>
		<instruction>	cmd.Run();</instruction>
		<instruction>}</instruction>
		<instruction />
		<instruction>function Log(msg)</instruction>
		<instruction>{</instruction>
		<instruction>	DOpus.output(String(msg));</instruction>
		<instruction>}</instruction>
		<instruction>==SCRIPT RESOURCES</instruction>
		<instruction>&lt;resources&gt;</instruction>
		<instruction>	&lt;resource name=&quot;timeshiftdialog&quot; type=&quot;dialog&quot;&gt;</instruction>
		<instruction>		&lt;dialog fontsize=&quot;8&quot; height=&quot;276&quot; lang=&quot;english&quot; title=&quot;Timeshift selected files&quot; width=&quot;306&quot;&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;8&quot; name=&quot;static1&quot; title=&quot;Timeshift string&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;56&quot; x=&quot;6&quot; y=&quot;6&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;12&quot; name=&quot;editTimeShift&quot; tip=&quot;&amp;lt;date-shift&amp;gt; &amp;lt;time-shift&amp;gt; or &amp;lt;time-shift&amp;gt;&quot; type=&quot;edit&quot; width=&quot;174&quot; x=&quot;66&quot; y=&quot;6&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;8&quot; name=&quot;static2&quot; title=&quot;Date source&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;48&quot; x=&quot;6&quot; y=&quot;24&quot; /&gt;</instruction>
		<instruction>			&lt;control height=&quot;40&quot; name=&quot;comboSourceDate&quot; sort=&quot;yes&quot; type=&quot;combo&quot; width=&quot;228&quot; x=&quot;66&quot; y=&quot;24&quot;&gt;</instruction>
		<instruction>				&lt;contents&gt;</instruction>
		<instruction>					&lt;item text=&quot;createdate&quot; /&gt;</instruction>
		<instruction>					&lt;item text=&quot;datedigitized&quot; /&gt;</instruction>
		<instruction>					&lt;item text=&quot;datetaken&quot; /&gt;</instruction>
		<instruction>					&lt;item text=&quot;lastmodifieddate&quot; /&gt;</instruction>
		<instruction>				&lt;/contents&gt;</instruction>
		<instruction>			&lt;/control&gt;</instruction>
		<instruction>			&lt;control height=&quot;204&quot; name=&quot;group1&quot; title=&quot;Help&quot; type=&quot;group&quot; width=&quot;294&quot; x=&quot;6&quot; y=&quot;66&quot; /&gt;</instruction>
		<instruction>			&lt;control default=&quot;yes&quot; height=&quot;14&quot; name=&quot;buttonApply&quot; title=&quot;Apply&quot; type=&quot;button&quot; width=&quot;50&quot; x=&quot;6&quot; y=&quot;42&quot; /&gt;</instruction>
		<instruction>			&lt;control height=&quot;14&quot; name=&quot;buttonDocs&quot; title=&quot;Bring me to the docs&quot; type=&quot;button&quot; width=&quot;90&quot; x=&quot;108&quot; y=&quot;42&quot; /&gt;</instruction>
		<instruction>			&lt;control height=&quot;14&quot; name=&quot;buttonCancel&quot; title=&quot;Cancel&quot; type=&quot;button&quot; width=&quot;50&quot; x=&quot;246&quot; y=&quot;42&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;8&quot; name=&quot;static3&quot; title=&quot;The format of the time shift string is either:&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;150&quot; x=&quot;12&quot; y=&quot;78&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;8&quot; name=&quot;st&quot; title=&quot;&amp;lt;date-shift&amp;gt; &amp;lt;time-shift&amp;gt; or &amp;lt;time-shift&amp;gt;&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;144&quot; x=&quot;12&quot; y=&quot;90&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;12&quot; name=&quot;static5&quot; title=&quot;The &amp;lt;date-shift&amp;gt; portion has three allowable formats:&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;186&quot; x=&quot;12&quot; y=&quot;108&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;12&quot; name=&quot;static6&quot; title=&quot;[+-]Y:M:D - year:month:day, with an optional positive or negative modifier&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;255&quot; x=&quot;18&quot; y=&quot;120&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;12&quot; name=&quot;static7&quot; title=&quot;[+-]M:D - month:day, with an optional positive or negative modifier&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;231&quot; x=&quot;18&quot; y=&quot;132&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;12&quot; name=&quot;static8&quot; title=&quot;[+-]D - day, with an optional positive or negative modifier&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;201&quot; x=&quot;18&quot; y=&quot;144&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;12&quot; name=&quot;static9&quot; title=&quot;[+-]H:M:S - hours:minutes:seconds, with an optional positive or negative modifier&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;282&quot; x=&quot;18&quot; y=&quot;174&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;12&quot; name=&quot;static10&quot; title=&quot;[+-]H:M - hours:minutes, with an optional positive or negative modifier&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;246&quot; x=&quot;18&quot; y=&quot;186&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;12&quot; name=&quot;static11&quot; title=&quot;[+-]H - hours, with an optional positive or negative modifier&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;210&quot; x=&quot;18&quot; y=&quot;198&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;12&quot; name=&quot;static12&quot; title=&quot;The &amp;lt;time-shift&amp;gt; portion has three allowable formats as well:&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;213&quot; x=&quot;12&quot; y=&quot;162&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;8&quot; name=&quot;static13&quot; title=&quot;Here are a few other examples:&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;132&quot; x=&quot;12&quot; y=&quot;222&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;8&quot; name=&quot;static14&quot; title=&quot;-1:0:0 0 - subtract one year from the timestamps&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;174&quot; x=&quot;18&quot; y=&quot;234&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;8&quot; name=&quot;static15&quot; title=&quot;+8:30 - add eight and a half hours to the timestamp&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;180&quot; x=&quot;18&quot; y=&quot;246&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;8&quot; name=&quot;static16&quot; title=&quot;+3 -0:45 - add three days, subtract 45 minutes from the timestamp&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;228&quot; x=&quot;18&quot; y=&quot;258&quot; /&gt;</instruction>
		<instruction>			&lt;control halign=&quot;left&quot; height=&quot;8&quot; name=&quot;staticSyntax&quot; title=&quot;Syntax Check&quot; type=&quot;static&quot; valign=&quot;top&quot; width=&quot;48&quot; x=&quot;246&quot; y=&quot;6&quot; /&gt;</instruction>
		<instruction>		&lt;/dialog&gt;</instruction>
		<instruction>	&lt;/resource&gt;</instruction>
		<instruction>&lt;/resources&gt;</instruction>
	</function>
</button>

3 Likes

Hi, Felix.
I really like this idea, thank you for your post.

It's making me wonder what button might be most useful given my use pattern. At the moment, I'm thinking two buttons:

  • shift by x years (letting me input x)
  • change all dates (file, modified, taken, digitized) to x (letting me input x with a picker)

Will let the idea sit for now. Thanks again for the stimulation.

1 Like

Hi playful,
nice to see that the button is usefull for others too :slight_smile:

Im not sure if I got your ideas right because at leaste the "shift by x years" is possible already with this button (+X:0:0).
If I get your second idea right (modifying all dates at once), this is not possible and needs several runs for each date to modify. I encountered problems when trying to apply datetaken to media files from my smartphone (and thus not only images but also videos) and then the SetAttr command failed with a vacuous error message. This button maybe also needs a check for the type of the metadata so that specific date attributes will be only applied to matching files in the selection.