Adjusting a group of timecodes using Applescript

I frequently work with captioning files that are essentially text files with thousands of timecodes and caption codes seperated by tabs.

___they are formatted like this

01:33:04:12 9420 9420 94ae 94ae 9470 9470 57c8 45ce 2049 20c1 d3cb 45c4 20c8 49cd 20c8 49d3 20ce c1cd 452c 942f 942f

01:33:05:27 9420 9420 94ae 94ae 91d0 91d0 c845 20c7 c1d6 4520 cd45 20c1 ce20 c1ce d357 4552 9170 9170 4920 c449 c4ce a780 9229 9229 5420 5245 c14c 4cd9 20d5 cec4 4552 d354 c1ce c4ae 942f 942f

01:33:09:28 9420 9420 94ae 94ae 94d0 94d0 4954 a780 9229 9229 d320 c1d3 2049 4620 c845 2057 c1ce 54d3 20cd 4580 9470 9470 544f 20c4 49d3 434f d645 5220 d34f cd45 54c8 49ce c780 942f 942f

01:33:12:20 9420 9420 94ae 94ae 9470 9470 c1c2 4fd5 5420 c849 cd20 464f 5220 cdd9 d345 4c46 ae80 942f 942f

01:33:15:15 942c 942c

01:33:19:07 9420 9420 94ae 94ae 9470 9470 4920 ce45 d645 5220 c1d3 cb45 c420 464f 5220 54c8 49d3 ae80 942f 942f

01:33:21:20 9420 9420 94ae 94ae 9470 9470 d94f d520 434f d54c c420 c14c 57c1 d9d3 20d3 c1d9 20ce 4fae 942f 942f

01:33:25:13 9420 9420 94ae 94ae 9470 9470 54c8 4520 c445 d345 5254 a780 9229 9229 d320 c120 c249 c720 d04c c143 45ae 942f 942f

01:33:27:16 9420 9420 94ae 94ae 94d0 94d0 d045 52c8 c1d0 d320 c249 c720 45ce 4fd5 c7c8 9470 9470 464f 5220 45d6 45ce 20c1 20c7 4fc4 20ce 4f54 2054 4f20 4649 cec4 20d9 4fd5 ae80 942f 942f

01:33:32:26 9420 9420 94ae 94ae 9470 9470 a8e3 f279 e96e 6729 942f 942f

01:33:36:08 942c 942c


Sometimes I need to slide a group of timecodes to fix their syncronization with video. For example, say all of these timecodes are two and a half seconds too late. I would like to be able use applescript to say move all of these timecodes 75 frames earlier.

Right now all I can do is bring my text file into word, remove all of the spaces between the lines then bring it into excel and use this script to build a list of all of the time codes I want shifted.

I dont know where to go from here.

Ideally I could work with my original text file and applescript instead of going to word, then excel then to applescript.

Any help would be greatly apreciated.

thanks,

Ryan

Ryan;

Red Alert: The script below has a glitch: it doubles up the entries in a way I hadn’t caught before.

I think this does it. It’s not pretty because I didn’t optimize it, but I think it works. Note that I did not look after the case where the minute would change or the hour would, but that’s an easy addition. This solution assumes that your times are in hr:min:sec:hundreths

set TT to "01:33:04:12    9420 9420 94ae 94ae 9470 9470 57c8 45ce 2049 20c1 d3cb 45c4 20c8 49cd 20c8 49d3 20ce c1cd 452c 942f 942f

01:33:05:27    9420 9420 94ae 94ae 91d0 91d0 c845 20c7 c1d6 4520 cd45 20c1 ce20 c1ce d357 4552 9170 9170 4920 c449 c4ce a780 9229 9229 5420 5245 c14c 4cd9 20d5 cec4 4552 d354 c1ce c4ae 942f 942f

01:33:09:28    9420 9420 94ae 94ae 94d0 94d0 4954 a780 9229 9229 d320 c1d3 2049 4620 c845 2057 c1ce 54d3 20cd 4580 9470 9470 544f 20c4 49d3 434f d645 5220 d34f cd45 54c8 49ce c780 942f 942f

01:33:12:20    9420 9420 94ae 94ae 9470 9470 c1c2 4fd5 5420 c849 cd20 464f 5220 cdd9 d345 4c46 ae80 942f 942f

01:33:15:15    942c 942c

01:33:19:07    9420 9420 94ae 94ae 9470 9470 4920 ce45 d645 5220 c1d3 cb45 c420 464f 5220 54c8 49d3 ae80 942f 942f

01:33:21:20    9420 9420 94ae 94ae 9470 9470 d94f d520 434f d54c c420 c14c 57c1 d9d3 20d3 c1d9 20ce 4fae 942f 942f

01:33:25:13    9420 9420 94ae 94ae 9470 9470 54c8 4520 c445 d345 5254 a780 9229 9229 d320 c120 c249 c720 d04c c143 45ae 942f 942f

01:33:27:16    9420 9420 94ae 94ae 94d0 94d0 d045 52c8 c1d0 d320 c249 c720 45ce 4fd5 c7c8 9470 9470 464f 5220 45d6 45ce 20c1 20c7 4fc4 20ce 4f54 2054 4f20 4649 cec4 20d9 4fd5 ae80 942f 942f

01:33:32:26    9420 9420 94ae 94ae 9470 9470 a8e3 f279 e96e 6729 942f 942f

01:33:36:08    942c 942c"

copy TT to TC
set NT to ""

set howMuch to -2.5 -- assumed to be 2 seconds and 50/100ths

set CP to count paragraphs of TC
repeat with k from 1 to CP
	if length of paragraph k of TC > 0 then
		set p to paragraph k of TC
		set text item delimiters to space & space -- tab in real thing?
		set n to text item 1 of p -- the time block
		set Rem to text items 2 thru -1 of p -- the rest of it
		set text item delimiters to ":"
		set hrMin to text items 1 thru 3 of n
		set secHun to text items 3 thru 4 of n
		set {newSec, newHun} to setTime(secHun, howMuch)
		set newTime to (hrMin & newSec & newHun) as string
		set text item delimiters to ""
		set newLine to newTime & space & space & Rem & return
	end if
	set NT to NT & newLine & return
end repeat
NT

to setTime(secsHunds, amt)
	set {sec, hun} to secsHunds
	set aSec to amt div 1
	set frac to (amt - aSec) * 100 as integer
	set H to hun + frac
	if H < 0 then
		set aSec to aSec - 1
		set H to H + 100
	end if
	set s to (sec + aSec) as text
	set t to H as text
	if length of s = 1 then set s to "0" & s
	if length of t = 1 then set t to "0" & t
	return {s, t}
end setTime


By the way; I didn’t adjust for hour or minute changes, just for the seconds, because I wasn’t certain from your post what the units of any of the parts of xx:XX:ZZ:yy were.

Similar topic: sumSrtTime

Thanks, Querty; that was before my time and I hadn’t seen it. I’ve addressed it differently in the home version of my script, but I’m waiting to hear from the OP before I actually finish it and post it.

Here’s the fixed version still using a cascade of ifs rather than the “Garvey” approach. I’ll try that later given time.

set TC to "01:33:04:12    9420 9420 94ae 94ae 9470 9470 57c8 45ce 2049 20c1 d3cb 45c4 20c8 49cd 20c8 49d3 20ce c1cd 452c 942f 942f

01:33:05:27    9420 9420 94ae 94ae 91d0 91d0 c845 20c7 c1d6 4520 cd45 20c1 ce20 c1ce d357 4552 9170 9170 4920 c449 c4ce a780 9229 9229 5420 5245 c14c 4cd9 20d5 cec4 4552 d354 c1ce c4ae 942f 942f

01:33:09:28    9420 9420 94ae 94ae 94d0 94d0 4954 a780 9229 9229 d320 c1d3 2049 4620 c845 2057 c1ce 54d3 20cd 4580 9470 9470 544f 20c4 49d3 434f d645 5220 d34f cd45 54c8 49ce c780 942f 942f

01:33:12:20    9420 9420 94ae 94ae 9470 9470 c1c2 4fd5 5420 c849 cd20 464f 5220 cdd9 d345 4c46 ae80 942f 942f

01:33:15:15    942c 942c

01:33:19:07    9420 9420 94ae 94ae 9470 9470 4920 ce45 d645 5220 c1d3 cb45 c420 464f 5220 54c8 49d3 ae80 942f 942f

01:33:21:20    9420 9420 94ae 94ae 9470 9470 d94f d520 434f d54c c420 c14c 57c1 d9d3 20d3 c1d9 20ce 4fae 942f 942f

01:33:25:13    9420 9420 94ae 94ae 9470 9470 54c8 4520 c445 d345 5254 a780 9229 9229 d320 c120 c249 c720 d04c c143 45ae 942f 942f

01:33:27:16    9420 9420 94ae 94ae 94d0 94d0 d045 52c8 c1d0 d320 c249 c720 45ce 4fd5 c7c8 9470 9470 464f 5220 45d6 45ce 20c1 20c7 4fc4 20ce 4f54 2054 4f20 4649 cec4 20d9 4fd5 ae80 942f 942f

01:33:32:26    9420 9420 94ae 94ae 9470 9470 a8e3 f279 e96e 6729 942f 942f

01:33:36:08    942c 942c"

set NT to ""
set howMuch to -59.99 -- assumed to be seconds and 100ths
set CP to count paragraphs of TC
repeat with k from 1 to CP
	if length of paragraph k of TC > 0 then
		set p to paragraph k of TC
		set text item delimiters to space & space -- tab in real thing?
		set n to text item 1 of p -- the time block
		set Rem to text items 2 thru -1 of p -- save the rest of it
		-- break up the time block
		set text item delimiters to ":"
		tell n
			set hr to text item 1
			set min to text item 2
			set sec to text item 3
			set hun to text item 4
		end tell
		-- get new time and assemble with colons
		set newTime to setTime(hr, min, sec, hun, howMuch) as string
		set text item delimiters to ""
		-- build new paragraph with new time, add the spaces back, and the rest of the old string
		set newLine to newTime & space & space & Rem & return
		-- add it to the string
		set NT to NT & newLine & return
	end if
end repeat
NT

to setTime(hrs, mins, secs, huns, amt)
	set aSec to amt div 1
	set frac to (amt - aSec) * 100 as integer
	set H to huns + frac
	if H > 100 then
		set H to H - 100
		set aSec to aSec + 1
	end if
	if H < 0 then
		set aSec to aSec - 1
		set H to H + 100
	end if
	set s to (secs + aSec)
	if s > 60 then
		set s to s - 60
		set mins to mins + 1
	else if s < 0 then
		set s to s + 60
		set mins to mins - 1
	end if
	if mins > 60 then
		set mins to mins - 60
		set hrs to hrs + 1
	else if mins < 0 then
		set mins to mins + 60
		set hrs to hrs - 1
	end if
	if length of (H as text) = 1 then set H to "0" & H
	if length of (s as text) = 1 then set s to "0" & s
	if length of (mins as text) = 1 then set mins to "0" & mins
	if length of (hrs as text) = 1 then set mins to "0" & mins
	return {hrs, mins, s, H}
end setTime

Hi, Adam et al.

Video timecodes have 30 frames per second.

set txt to "01:33:04:12    9420 9420 94ae 94ae 9470 9470 57c8 45ce 2049 20c1 d3cb 45c4 20c8 49cd 20c8 49d3 20ce c1cd 452c 942f 942f

01:33:05:27    9420 9420 94ae 94ae 91d0 91d0 c845 20c7 c1d6 4520 cd45 20c1 ce20 c1ce d357 4552 9170 9170 4920 c449 c4ce a780 9229 9229 5420 5245 c14c 4cd9 20d5 cec4 4552 d354 c1ce c4ae 942f 942f

01:33:09:28    9420 9420 94ae 94ae 94d0 94d0 4954 a780 9229 9229 d320 c1d3 2049 4620 c845 2057 c1ce 54d3 20cd 4580 9470 9470 544f 20c4 49d3 434f d645 5220 d34f cd45 54c8 49ce c780 942f 942f

01:33:12:20    9420 9420 94ae 94ae 9470 9470 c1c2 4fd5 5420 c849 cd20 464f 5220 cdd9 d345 4c46 ae80 942f 942f

01:33:15:15    942c 942c

01:33:19:07    9420 9420 94ae 94ae 9470 9470 4920 ce45 d645 5220 c1d3 cb45 c420 464f 5220 54c8 49d3 ae80 942f 942f

01:33:21:20    9420 9420 94ae 94ae 9470 9470 d94f d520 434f d54c c420 c14c 57c1 d9d3 20d3 c1d9 20ce 4fae 942f 942f

01:33:25:13    9420 9420 94ae 94ae 9470 9470 54c8 4520 c445 d345 5254 a780 9229 9229 d320 c120 c249 c720 d04c c143 45ae 942f 942f

01:33:27:16    9420 9420 94ae 94ae 94d0 94d0 d045 52c8 c1d0 d320 c249 c720 45ce 4fd5 c7c8 9470 9470 464f 5220 45d6 45ce 20c1 20c7 4fc4 20ce 4f54 2054 4f20 4649 cec4 20d9 4fd5 ae80 942f 942f

01:33:32:26    9420 9420 94ae 94ae 9470 9470 a8e3 f279 e96e 6729 942f 942f

01:33:36:08    942c 942c"

addFrames(txt, -75) -- Subtract 75 frames from every time code in the text.

on addFrames(txt, f)
	set astid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to ":"
	
	set theLines to paragraphs of txt
	repeat with i from 1 to (count theLines)
		set thisLine to item i of theLines
		if ((count thisLine) > 0) then
			set TC to text 1 thru 11 of thisLine
			set {hr, min, sec, frm} to TC's text items
			set fSum to (hr * hours + min * minutes + sec) * 30 + frm + f
			tell (100000000 + fSum div 108000 * 1000000 + fSum mod 108000 div 1800 * 10000 + fSum mod 1800 div 30 * 100 + fSum mod 30) as string
				set newtc to {text 2 thru 3, text 4 thru 5, text 6 thru 7, text 8 thru 9}
			end tell
			set item i of theLines to (newtc as string) & text 12 thru -1 of thisLine
		end if
	end repeat
	set AppleScript's text item delimiters to return
	set newTxt to theLines as string
	set AppleScript's text item delimiters to astid
	
	return newTxt
end addFrames

I knew you’d have a neat way to do this Mr. G! :lol:

Yeah - and he beat me to it. :stuck_out_tongue:

When I noticed this topic a little earlier, Mr G. hadn’t posted a reply - although I suspected he might be taking a look at it. (Always know something’s brewing when the man goes quiet.) :wink:

Excellent stuff, Nigel. In spite of some (not too surprising) similarities of approach, my own effort is probably a little a bit slower - mainly because of some extra runtime calculations. (Just for fun, I also couldn’t resist a rather excessive one-liner, either - which no doubt will cause a groan or two in the Garvey household…)

In fact, I almost refrained from posting when I saw yours, but thought I’d throw in my effort anyway - if only to demonstrate a method using variable frame rates. I believe time-code can have a number of frame rates - although it was clear, from the calculation in Ryan’s post (2.5 seconds = 75 frames), that he was referring to 30 fps. I’m no expert on this, but the more common standards seem to include:

Even NTSC video, which runs at 29.97 fps, evidently drops a frame every now and then - to resynchronise between the time-code rate and a 30 fps playback rate (rather like the way leap seconds and leap years help to synchronise times and dates).

So, while there may not be much call for a frame rate calculation other than 30 fps, here’s my attempt, anyway…


set code_text to "01:33:04:12 9420 9420 94ae 94ae 9470 9470 57c8 45ce 2049 20c1 d3cb 45c4 20c8 49cd 20c8 49d3 20ce c1cd 452c 942f 942f

01:33:05:27 9420 9420 94ae 94ae 91d0 91d0 c845 20c7 c1d6 4520 cd45 20c1 ce20 c1ce d357 4552 9170 9170 4920 c449 c4ce a780 9229 9229 5420 5245 c14c 4cd9 20d5 cec4 4552 d354 c1ce c4ae 942f 942f

01:33:09:28 9420 9420 94ae 94ae 94d0 94d0 4954 a780 9229 9229 d320 c1d3 2049 4620 c845 2057 c1ce 54d3 20cd 4580 9470 9470 544f 20c4 49d3 434f d645 5220 d34f cd45 54c8 49ce c780 942f 942f

01:33:12:20 9420 9420 94ae 94ae 9470 9470 c1c2 4fd5 5420 c849 cd20 464f 5220 cdd9 d345 4c46 ae80 942f 942f

01:33:15:15 942c 942c

01:33:19:07 9420 9420 94ae 94ae 9470 9470 4920 ce45 d645 5220 c1d3 cb45 c420 464f 5220 54c8 49d3 ae80 942f 942f

01:33:21:20 9420 9420 94ae 94ae 9470 9470 d94f d520 434f d54c c420 c14c 57c1 d9d3 20d3 c1d9 20ce 4fae 942f 942f

01:33:25:13 9420 9420 94ae 94ae 9470 9470 54c8 4520 c445 d345 5254 a780 9229 9229 d320 c120 c249 c720 d04c c143 45ae 942f 942f

01:33:27:16 9420 9420 94ae 94ae 94d0 94d0 d045 52c8 c1d0 d320 c249 c720 45ce 4fd5 c7c8 9470 9470 464f 5220 45d6 45ce 20c1 20c7 4fc4 20ce 4f54 2054 4f20 4649 cec4 20d9 4fd5 ae80 942f 942f

01:33:32:26 9420 9420 94ae 94ae 9470 9470 a8e3 f279 e96e 6729 942f 942f

01:33:36:08 942c 942c"

----------------------------------------------------------------------------------------------------

|shift codes| of code_text by -75 at 30 -- of [text to be modified] by [+/- number of frames] at [frames per second]

to |shift codes| of t by s at r
	set {h, m, l, d, text item delimiters} to {r * hours, r * minutes, t's paragraphs, text item delimiters, ":"}
	repeat with i in l
		if (count i) > 12 then tell text items of i's text 1 thru 11 to tell (beginning * hours + (item 2) * minutes + (item 3)) * r + end + s to tell (100000000 + it div h * 1000000 + it mod h div m * 10000 + it mod m div r * 100 + it mod r) as string to set i's contents to ((get {text 2 thru 3, text 4 thru 5, text 6 thru 7, text 8 thru 9}) as string) & i's text 12 thru -1
	end repeat
	set text item delimiters to return
	set {l, text item delimiters} to {l as string, d}
	l
end |shift codes|


Edit: One or two minor performance tweaks (see discussion below).

Now that’s a one-liner, Kai. :o As you know, I enjoy them and view them as puzzles to be “decoded” (and I can decode this one).

The only problem I see with this thread is that we now have three solutions - one brute force and two elegant, but no word at all from the OP. Educational for me, for sure, (who hadn’t thought to use the frame rate as a basis) but I’m not sure useful for him/her. Ah well. :confused:

You know, when I saw Adam’s comment last night, I nearly posted a reply along the lines of: “I wouldn’t mind betting that, even as we speak, kai’s working on a one-line version that’ll appear between sometime 3 am and 5 am BST.” :wink:

Your approach, Mr. E ” may I call you k? ” is identical to mine, apart from the adjustments for the variable frame rate and “that line” in the middle. (It would take several lines to express the groans it caused in the Garvey household!)

I think that some of the very slight speed disadvantage is due to the fact that both count and text 1 thru 11 are applied to a reference rather than to a concrete value. And, of course, (item 1) and (item 4) could be rendered as beginning and end. So you’ll be delighted to know that some of ground can be made up by making the one-liner a little longer still! :slight_smile:

tell i's contents to if (count) > 12 then tell text items of text 1 thru 11 to tell (beginning * hours + (item 2) * minutes + (item 3)) * r + end + s to tell (100000000 + it div h * 1000000 + it mod h div m * 10000 + it mod m div r * 100 + it mod r) as string to set i's contents to ((get {text 2 thru 3, text 4 thru 5, text 6 thru 7, text 8 thru 9}) as string) & i's text 12 thru -1

Well. Mine’s obviously the best. :wink: But I’m sure OP’ll get back to us if necessary.

He says modestly. :lol:; Obvious?ly! :cool:

Of course, Mr. G ” or should I say N. I think we know each other well enough, now…

Spooky, for sure. Guess the time we’ve spent batting ideas back and forth had to tell, sooner or later. (No wonder we’re on first initial terms.) :wink:

Splendid! Glad to hear that I haven’t lost the knack! :stuck_out_tongue:

No worries. Losing the initial tell statement not only renders it slightly shorter than the original (now modified), but gains a further (minute) speed bump (here). :slight_smile:

Me too, Adam. Been a while since I worked closely with SMPTE time code - so I had to put myself through a brief refresher course…

Well of course! :wink:

Short slightly off-topic… to Adam Bell

Where AppleScript is concerned, I seem to be treading a very fine line between “hobby” and “mental illness.”

don’t worry you are not alone! thats so funny. :lol:

To get back on topic, I need to find a way to get the duration of a video file using applescript. But I cant figure out how. I know its in the “More Info:” section of the get info window of a video file. I want to access that info, preferably without actually opening the info window for each video file of dozens maybe hundreds.

What I need to do is take several video files and add their durations together to get total duration of the set. And the set will usually consist of 3 to 12 videos. Plus enough sets to set schedule video all day and night. I’m replacing a hardware video deck with an applescript, eventually.

Thanks,
and funny is good :wink:

Model: 1.83 GHz intel Core Duo
AppleScript: up to date
Browser: Firefox 1.5.0.4
Operating System: Mac OS X (10.4)