Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Timecode parsing for user input

Engaged ,
Sep 16, 2016 Sep 16, 2016

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;

}

TOPICS
Scripting
954
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Sep 16, 2016 Sep 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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Sep 16, 2016 Sep 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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Sep 16, 2016 Sep 16, 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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Sep 17, 2016 Sep 17, 2016

I'd stick to a colon (:) to separate fields. It's very common. Too permissive = too much ambiguity imo.

In particular, allowing the "." to separate fields is super ambiguous.

By the way, thanks for quoting that "10.5" is interpreted as "10:05" in the comp dialog. I was abused by the fact that the created comp was longer that 10 seconds.... Luckily, i had not tried that before your post!!!!

Xavier

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Sep 18, 2016 Sep 18, 2016

Well I'm trying to replicate the native behaviour. The current regex works the same as the AE timecode inputs, I was having conniptions trying to deal with a string like "12, 3 4" but then I tried it in a native timecode input and it failed there too, or more to the point it returned "0:00:12:03" without the 4. So a non-whitespace character followed by whitespace is valid, but whitespace on its own isn't. I could make my script able to deal with "12, 3 4", but it might be better to match the AE behaviour exactly.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Sep 18, 2016 Sep 18, 2016
LATEST

Oh I am SO stupid! All I needed to replicate the native behaviour, was to use the native built-in function:

currentFormatToTime() global function

currentFormatToTime(formattedTime, fps, isDuration)

Description

Converts a formatted string for a frame time value to a number of seconds, given a specified frame rate. For

example, if the formatted frame time value is 0:00:12 (the exact string format is determined by a project

setting), and the frame rate is 24 fps, the time would be 0.5 seconds (12/24). If the frame rate is 30 fps, the time

would be 0.4 seconds (12/30).

If the time is a duration, the frames are counted from 0. Otherwise, the frames are counted from the project’s

starting frame

Which has exactly the right behaviour when dealing with variably formated strings

currentFormatToTime("1..", 25)

Result: 60

currentFormatToTime("1, 2.3:4", 25)

Result: 3723.16

and timeToCurrentFormat() which does the reverse:

timeToCurrentFormat(60,25)

Result: 00:01:00:00

AAAAAGH! Next time I'll RTFM!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines