Rename script to change multiple variables including 24 hour time

I use Opus to rename thousands of files but I have up to now mostly stuck with the built-in renaming options. I am trying to take a stab at a script that will change multiple parts of a filename in one go and could use some assistance.

I have thousands of files that have this naming structure

Title-Camera#-YYYY-Month-DD-hh-mm-ss-PM-notes.ext.ext

Example filenames:
PhotoXPS-cam#1-2019-Jun-13-05-47-16-PM.avi
TreeCam-cam#3-2019-Oct-13-08-20-27-AM-tree.avi.jpg

These are terrible because not only are they messy to look at they also fail to sort because of the spelled-out month and the 12 hour time.

Ideally I would like the above examples to rename to something like this:

PhotoXPS-cam#1-2019-Jun-13-05-47-16-PM.avi
-> PhotoXPS - 2019-06-13 174716.avi

TreeCam-cam#3-2019-Oct-13-08-20-27-AM-tree.avi.jpg
-> TreeCam - 2019-10-13 082027-tree.jpg

Notice that sometimes there are extra notes at the end of the files.
Also, note that sometimes there are extra extensions that I do not need to keep

I started with this:

function OnGetNewName(getNewNameData)
{

// Convert Variant strings to JScript String objects so we can use String methods.
	var strExtension = String(getNewNameData.newname_ext_m);
	var strNameOnly = String(getNewNameData.newname_stem_m);

// Replace cams with dash.
	strNameOnly = strNameOnly.replace (/-cam#[123]-/g,  " - ");

// Case-sensitive, replace months with numbers.
	strNameOnly = strNameOnly.replace(/Jan/g, "01");
	strNameOnly = strNameOnly.replace(/Feb/g, "02");
	strNameOnly = strNameOnly.replace(/Mar/g, "03");
	strNameOnly = strNameOnly.replace(/Apr/g, "04");
	strNameOnly = strNameOnly.replace(/May/g, "05");
	strNameOnly = strNameOnly.replace(/Jun/g, "06");
	strNameOnly = strNameOnly.replace(/Jul/g, "07");
	strNameOnly = strNameOnly.replace(/Aug/g, "08");
	strNameOnly = strNameOnly.replace(/Sep/g, "09");
	strNameOnly = strNameOnly.replace(/Oct/g, "10");
	strNameOnly = strNameOnly.replace(/Nov/g, "11");
	strNameOnly = strNameOnly.replace(/Dec/g, "12");



// Rejoin the name and extension and return the result.
	return strNameOnly + strExtension;

This gets me closer by outputting filenames like these

PhotoXPS-cam#1-2019-Jun-13-05-47-16-PM.avi
-> PhotoXPS - 2019-06-13-05-47-16-PM.avi

TreeCam-cam#3-2019-Oct-13-08-20-27-AM-tree.avi.jpg
-> TreeCam - 2019-10-13-08-20-27-AM-tree.avi.jpg

I then added:

// Replace Dash between date and time with " " .
	search = '-'
	var n = 4
	strNameOnly = (strNameOnly.replace(RegExp("^(?:.*?-){" + n + "}"), function(x){return x.replace(RegExp(search + "$"), " ")}))

// Replace Dash between time with "~" .
	search = '-'
	var n = 4
	strNameOnly = (strNameOnly.replace(RegExp("^(?:.*?-){" + n + "}"), function(x){return x.replace(RegExp(search + "$"), "~")}))
	strNameOnly = (strNameOnly.replace(RegExp("^(?:.*?-){" + n + "}"), function(x){return x.replace(RegExp(search + "$"), "~")}))

// Replace Dash before PM with " " .
	search = '-'
	var n = 4
	strNameOnly = (strNameOnly.replace(RegExp("^(?:.*?-){" + n + "}"), function(x){return x.replace(RegExp(search + "$"), " ")}))

This further improved the filenames to

PhotoXPS-cam#1-2019-Jun-13-05-47-16-PM.avi
-> PhotoXPS - 2019-06-13 05~47~16 PM.avi

TreeCam-cam#3-2019-Oct-13-08-20-27-AM-tree.avi.jpg
-> TreeCam - 2019-10-13 08~20~27 AM-tree.avi.jpg

( "~" was added to time so I had something unique to work with on the next step.)

This part of the script introduced my first issue, however; because searching for the 4th "-" can really screw up a filename that has already been renamed. I would prefer a way to search for the 4th "-" but exclude any "-" that has a letter next to it. That way it won't replace the dash at the end of the file name where I may have a note.

The final step I was trying to accomplish was converting AM/PM to 24 hour time.
I ran into this page 24-hour conversion in JS that has dozens of methods to accomplish this task.

The one I tried to insert into my script, unfortunately, breaks it.

// Convert a string like 10~05~23 PM to 24h format, returns like [22-05-23]
    var s = strNameOnly.match(/(\d+)~(\d+)~(\d+) (\w)+/g);
	var time = s.match(/\d{2}/g);
	if (time[0] === "12") time[0] = "00";
	if (s.indexOf("PM") > -1) time[0] = parseInt(time[0])+12;
	return time.join("");

I thought I could use the regex (/(\d+)~(\d+)~(\d+) (\w)+/g) to only edit the part of the filename with the time and then have it convert and place it all back together.

But alas my lack of skills in scripting either results in breaking the script or outputting a filename that only has a timestamp and nothing else.

Here is my script in its entirety.

function OnGetNewName(getNewNameData)
{

// Convert Variant strings to JScript String objects so we can use String methods.
	var strExtension = String(getNewNameData.newname_ext_m);
	var strNameOnly = String(getNewNameData.newname_stem_m);

// Replace cams with dash.
	strNameOnly = strNameOnly.replace (/-cam#[123]-/g,  " - ");

// Replace months with numbers.
	strNameOnly = strNameOnly.replace(/Jan/g, "01");
	strNameOnly = strNameOnly.replace(/Feb/g, "02");
	strNameOnly = strNameOnly.replace(/Mar/g, "03");
	strNameOnly = strNameOnly.replace(/Apr/g, "04");
	strNameOnly = strNameOnly.replace(/May/g, "05");
	strNameOnly = strNameOnly.replace(/Jun/g, "06");
	strNameOnly = strNameOnly.replace(/Jul/g, "07");
	strNameOnly = strNameOnly.replace(/Aug/g, "08");
	strNameOnly = strNameOnly.replace(/Sep/g, "09");
	strNameOnly = strNameOnly.replace(/Oct/g, "10");
	strNameOnly = strNameOnly.replace(/Nov/g, "11");
	strNameOnly = strNameOnly.replace(/Dec/g, "12");

// Replace Dash between date and time with " " .
	search = '-'
	var n = 4
	strNameOnly = (strNameOnly.replace(RegExp("^(?:.*?-){" + n + "}"), function(x){return x.replace(RegExp(search + "$"), " ")}))

// Replace Dash between time with "~" .
	search = '-'
	var n = 4
	strNameOnly = (strNameOnly.replace(RegExp("^(?:.*?-){" + n + "}"), function(x){return x.replace(RegExp(search + "$"), "~")}))
	strNameOnly = (strNameOnly.replace(RegExp("^(?:.*?-){" + n + "}"), function(x){return x.replace(RegExp(search + "$"), "~")}))

// Replace Dash before PM with " " .
	search = '-'
	var n = 4
	strNameOnly = (strNameOnly.replace(RegExp("^(?:.*?-){" + n + "}"), function(x){return x.replace(RegExp(search + "$"), " ")}))

// Convert a string like 10~05~23 PM to 24h format, returns like [22-05-23]
    var s = strNameOnly.match(/(\d+)~(\d+)~(\d+) (\w)+/g);
	var time = s.match(/\d{2}/g);
	if (time[0] === "12") time[0] = "00";
	if (s.indexOf("PM") > -1) time[0] = parseInt(time[0])+12;
	return time.join("");

// Rejoin the name and extension and return the result.
	return strNameOnly + strExtension;


}

Thanks for any help.

The script will always finish when it gets to return time.join(""); and return only the timestamp. But it would be difficult anyway to squeeze the 24h time back into strNameOnly.

Since your files are so well structured I'd split the original name into an array

arrNameOnly = strNameOnly.split('-');

and work from there. E.g. replacing the month:

arrNameOnly[3] = arrNameOnly[3].replace(/Jan/g, "01");
arrNameOnly[3] = arrNameOnly[3].replace(/Feb/g, "02");
...

In the end the parts get put together with the needed delimiters.

return arrNameOnly[0] +  ' - ' + arrNameOnly[2] + '-' + arrNameOnly[3] +  '-'  // + ... + ....
2 Likes

This was super helpful.

Here is my modified script.

function OnGetNewName(getNewNameData)
{

// Convert Variant strings to JScript String objects so we can use String methods.
	var strExtension = String(getNewNameData.newname_ext_m);
	var strNameOnly = String(getNewNameData.newname_stem_m);


// Split StrNameOnly by "-"
	arrNameOnly = strNameOnly.split('-');

// Replace cams with dash.
// arrNameOnly[2] = arrNameOnly[2].replace (/-cam#[123]-/g,  " - ");

// Replace months with numbers.
	arrNameOnly[3] = arrNameOnly[3].replace(/Jan/g, "01");
	arrNameOnly[3] = arrNameOnly[3].replace(/Feb/g, "02");
	arrNameOnly[3] = arrNameOnly[3].replace(/Mar/g, "03");
	arrNameOnly[3] = arrNameOnly[3].replace(/Apr/g, "04");
	arrNameOnly[3] = arrNameOnly[3].replace(/May/g, "05");
	arrNameOnly[3] = arrNameOnly[3].replace(/Jun/g, "06");
	arrNameOnly[3] = arrNameOnly[3].replace(/Jul/g, "07");
	arrNameOnly[3] = arrNameOnly[3].replace(/Aug/g, "08");
	arrNameOnly[3] = arrNameOnly[3].replace(/Sep/g, "09");
	arrNameOnly[3] = arrNameOnly[3].replace(/Oct/g, "10");
	arrNameOnly[3] = arrNameOnly[3].replace(/Nov/g, "11");
	arrNameOnly[3] = arrNameOnly[3].replace(/Dec/g, "12");


// Convert a time string to 24h format
    var s = arrNameOnly[5]+arrNameOnly[6]+arrNameOnly[7]+arrNameOnly[8];
	var time = s.match(/\d{2}/g);
	if (time[0] === "12") time[0] = "00";
	if (s.indexOf("PM") > -1) time[0] = parseInt(time[0])+12;


// Rejoin the name and extension and return the result.
	return arrNameOnly[2] + '-' + arrNameOnly[3] + '-' + arrNameOnly[4] + '.' + time.join("") +  ' - '  + arrNameOnly[0] +  ' - ' + arrNameOnly[9] + '-' + arrNameOnly[10] + '' + strExtension;

}


//       Name               Date             Time              Extras
//     0          1      [2      3    4]   [5   6    7    8]    9     10
//     TreeCam - cam#3 - 2019 - Oct - 13 - 08 - 20 - 27 - AM - tree - top.avi.jpg

This only leaves me with one issue and that is for the last sections 9 and 10. They leave an undifined if there is nothing in the source name there. How do I tell it to only add those sections if they already exist.

Current Name output:

PhotoXPS-cam#1-2019-Jun-13-05-47-16-PM.avi
-> 2019-06-13.174716 - PhotoXPS - undefined-undefined.avi

TreeCam-cam#3-2019-Oct-13-08-20-27-AM-tree-top.avi.jpg
-> 2019-10-13.082027 - TreeCam - tree-top.avi.jpg

Thanks Again.

Try replacing the return line with this:

	// Rejoin the name and extension and return the result.
	var newName;
	newName = arrNameOnly[2] + '-' + arrNameOnly[3] + '-' + arrNameOnly[4] + '.' + time.join("") +  ' - '  + arrNameOnly[0];
	if (arrNameOnly.length > 9) newName = newName + ' - ' + arrNameOnly[9];
	if (arrNameOnly.length > 10) newName = newName + '-' + arrNameOnly[10];
	return newName + '' + strExtension;
2 Likes

Perfect that did it.

For anyone else interested Here is the final Script:

function OnGetNewName(getNewNameData)
{

// Convert Variant strings to JScript String objects so we can use String methods.
	var strExtension = String(getNewNameData.newname_ext_m);
	var strNameOnly = String(getNewNameData.newname_stem_m);


// Split StrNameOnly by "-"
	arrNameOnly = strNameOnly.split('-');

// Replace months with numbers.
	arrNameOnly[3] = arrNameOnly[3].replace(/Jan/g, "01");
	arrNameOnly[3] = arrNameOnly[3].replace(/Feb/g, "02");
	arrNameOnly[3] = arrNameOnly[3].replace(/Mar/g, "03");
	arrNameOnly[3] = arrNameOnly[3].replace(/Apr/g, "04");
	arrNameOnly[3] = arrNameOnly[3].replace(/May/g, "05");
	arrNameOnly[3] = arrNameOnly[3].replace(/Jun/g, "06");
	arrNameOnly[3] = arrNameOnly[3].replace(/Jul/g, "07");
	arrNameOnly[3] = arrNameOnly[3].replace(/Aug/g, "08");
	arrNameOnly[3] = arrNameOnly[3].replace(/Sep/g, "09");
	arrNameOnly[3] = arrNameOnly[3].replace(/Oct/g, "10");
	arrNameOnly[3] = arrNameOnly[3].replace(/Nov/g, "11");
	arrNameOnly[3] = arrNameOnly[3].replace(/Dec/g, "12");


// Convert a time string to 24h format
    var s = arrNameOnly[5]+arrNameOnly[6]+arrNameOnly[7]+arrNameOnly[8];
	var time = s.match(/\d{2}/g);
	if (time[0] === "12") time[0] = "00";
	if (s.indexOf("PM") > -1) time[0] = parseInt(time[0])+12;


// Rejoin the name and extension and return the result.
	var newName;
	newName = arrNameOnly[2] + '-' + arrNameOnly[3] + '-' + arrNameOnly[4] + '.' + time.join("") +  ' - '  + arrNameOnly[0];
	if (arrNameOnly.length > 9) newName = newName + ' - ' + arrNameOnly[9];
	if (arrNameOnly.length > 10) newName = newName + '-' + arrNameOnly[10];
	return newName + '' + strExtension;
}

Results with...

PhotoXPS-cam#1-2019-Jun-13-05-47-16-PM.avi
-> 2019-06-13.174716 - PhotoXPS.avi

TreeCam-cam#3-2019-Oct-13-08-20-27-AM-tree-top.avi.jpg
-> 2019-10-13.082027 - TreeCam - tree-top.jpg

1 Like

One small update:

For some reason all times worked correctly except for 8pm and 9pm

2019-11-06%2021_59_11-Rename%20%5BCam%20Time%20Fix%5D

Notice how they both resulted in 12. Unfortunately, I have already run through almost 100k files with the script.

The good news is I have an updated script which should resolve the issue.

function OnGetNewName(getNewNameData)
{

// Convert Variant strings to JScript String objects so we can use String methods.
	var strExtension = String(getNewNameData.newname_ext_m);
	var strNameOnly = String(getNewNameData.newname_stem_m);


// Split StrNameOnly by "-"
	arrNameOnly = strNameOnly.split('-');

// Replace cams with dash.
// arrNameOnly[2] = arrNameOnly[2].replace (/-cam#[123]-/g,  " - ");

// Replace months with numbers.
	arrNameOnly[3] = arrNameOnly[3].replace(/Jan/g, "01");
	arrNameOnly[3] = arrNameOnly[3].replace(/Feb/g, "02");
	arrNameOnly[3] = arrNameOnly[3].replace(/Mar/g, "03");
	arrNameOnly[3] = arrNameOnly[3].replace(/Apr/g, "04");
	arrNameOnly[3] = arrNameOnly[3].replace(/May/g, "05");
	arrNameOnly[3] = arrNameOnly[3].replace(/Jun/g, "06");
	arrNameOnly[3] = arrNameOnly[3].replace(/Jul/g, "07");
	arrNameOnly[3] = arrNameOnly[3].replace(/Aug/g, "08");
	arrNameOnly[3] = arrNameOnly[3].replace(/Sep/g, "09");
	arrNameOnly[3] = arrNameOnly[3].replace(/Oct/g, "10");
	arrNameOnly[3] = arrNameOnly[3].replace(/Nov/g, "11");
	arrNameOnly[3] = arrNameOnly[3].replace(/Dec/g, "12");


// Convert a time string to 24h format
 	var s = arrNameOnly[5]+':'+arrNameOnly[6]+':'+arrNameOnly[7]+' '+arrNameOnly[8];
    var time = s.match(/(\d{2}):(\d{2}):(\d{2}) (\w)/);
    var hours = Number(time[1]);
	var zerohours = Number(time[1]);
    var minutes = time[2];
    var seconds = time[3];
    var meridian = time[4].toLowerCase();

    if (meridian == 'p' && hours < 12) {
      hours += 12;
    }
    else if (meridian == 'a' && hours == 12) {
      hours = '00'.slice(-2);
    }
    else if (meridian == 'a' && hours < 12) {
      hours = ('0'+ zerohours).slice(-2);
    }




// Rejoin the name and extension and return the result.
	var newName;
	newName = arrNameOnly[2] + '-' + arrNameOnly[3] + '-' + arrNameOnly[4] + '.' + hours + '' + minutes + '' + seconds +  ' - '  + arrNameOnly[0];
	if (arrNameOnly.length > 9) newName = newName + ' -' + arrNameOnly[9];
	if (arrNameOnly.length > 10) newName = newName + '-' + arrNameOnly[10];
	return newName + '' + strExtension;
}

2019-11-06%2022_37_51-Rename%20%5BCam%20Time%20Fix%202%5D%20_

As you can see the updated time conversion works with all hours now.
Thanks, everyone for the help.