Skip to main content
stib
Inspiring
September 16, 2016
Question

Timecode parsing for user input

  • September 16, 2016
  • 1 reply
  • 893 views

I have a script that allows users to enter timecode as a string, and I wanted it to perform the same as the Timecode inputs in the rest of the program: if they type "1..." I want it to register as 1:00:00:00 without them having to type a correctly formatted string.

This. Took. Ages. I descended deep into the regex underworld, but I'm happy to say that I managed to placate the JS gods with my mad skillz on the lyre and returned victorious. So for any other young traveller preparing to take the journey, here's what I worked out. If you have suggestions, please, let me know.

function parseTimeString(theString, comp) {

    comp = app.project.activeItem; //need an active comp in order to get the frameDuration

    if (comp) {

        theString = "" + theString;

        //allows user to lazily enter timecode, eg. to enter 1 minute you type 1.. rather than 0:1:0:0

        //this took ages to work out..

        var hrsStr = theString.match(/(\d+)\D\d*\D\d*\D\d*$/); //matches  "0:1:2:3" and "0..." and returns '0'

        var minStr = theString.match(/(\d+)\D\d*\D\d*$/); //matches "0:1:2:3" , "1,2.3" and "1.." and returns 1

        var secStr = theString.match(/(\d+)\D\d*$/); //and so on..

        var frmStr = theString.match(/(\d+)$/);

        //convert the strings to time values

        var hrs = hrsStr

            ? parseInt(hrsStr)

            : 0;

        var min = minStr

            ? parseInt(minStr)

            : 0;

        var sec = secStr

            ? parseInt(secStr)

            : 0;

        var frm = frmStr

            ? parseInt(frmStr)

            : 0;

        //return the result as time, measured in seconds

        return 3600 * hrs + 60 * min + sec + comp.frameDuration * frm;

    }

    return false;

}

This topic has been closed for replies.

1 reply

UQg
Legend
September 16, 2016

One tip, for base 10 integers, always use parseInt(x, 10) or you will get some surprises, eg: parseInt('019")

And by the way, in the AE, non integer entries are allowed in the comp duration dialog!!

Also make the function return, in some situations, false instead of a number can be a source of mistake imo.

Another possibility without regexp:

function parseTimeCode(str, comp){

    str = String(str);

  

    if (!(comp instanceof CompItem)) throw "comp is not a comp";

  

    var seconds = 0;

    var splitStr = str.split(":").reverse();

    var n, N=splitStr.length;

    var x;

    if (N>4) throw "Too many ':' !!! : Only hh:mm:ss:ff supported";

    for (n=0; n<N; n++){

        x = +splitStr;

        if (isNaN(x)) throw "Invalid  number input : " + splitStr;

        splitStr = x;

        };

  

    seconds += splitStr[0]* comp.frameDuration;

    if (N>1) seconds += splitStr[1];

    if (N>2) seconds += splitStr[2]*60;

    if (N>3) seconds += splitStr[3]*3600;

  

    return seconds;

    };

try{myTime = parseTimeCode("10.5 : 019", app.project.activeItem)}catch(e){/*alert(e);*/};

Xavier

stib
stibAuthor
Inspiring
September 16, 2016

Thanks for the tip about the radix parameter.

I'm going for a more permissive user input, so it allows the user to use any sort of separator, hence the regex (but it's mostly because I don't really know much about JS string methods).

I think you'll find the timecode inputs in AE work in the same way, that is any of the characters : , . ; and interestingly + get interpreted as a separator, so if you type in "10.5 : 019" it will be interpreted as 0:10:05:19. It means you can use shorthand like "1.." to mean one minute. I'm aiming to mimic that.

So I could use your algorithm but with this as the split function:

var splitStr = str.split(/\D/).reverse();

That mimics the native AE behaviour, but it actually turns out to be more permissive - you could use any non-numeric character as a separator. Having \D+ also means spaces I could make it aware of arithmetic expressions, like sliders are, but that's probably function creep.

Reversing the array is a great way to deal with the possibility of variable length input. I was originally checking the result of parseInt() on each term to see if it returned a number and that's when I learned that

NaN === NaN // = false

It's been a learning curve.

stib
stibAuthor
Inspiring
September 17, 2016

Turns out

var splitStr = str.split(/\D/).reverse(); 

doesn't cope with the example 10.5 : 019 with the colon with spaces on either side, because it acts like three characters. So it's a bit too permissive. I tried using

var splitStr = str.split(/\s*\D\s*/).reverse(); 

to cope with any additional whitespace.

But then I realise that while string.split() works with your example it fails with the more common example 10.. meaning 10 minutes. String.split() doesn't return an empty match after the last separator so "10..".split(".") returns ['10', ''], not ['10','','']. So it looks like it's back to the regex.

I am sooo overthinking this fairly trivial bit of UI.