-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathMakeTimestamps.jsx
More file actions
397 lines (327 loc) · 15.6 KB
/
Copy pathMakeTimestamps.jsx
File metadata and controls
397 lines (327 loc) · 15.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
// Purpose: Lets you select any number of graphics clips on the timeline, and it will automatically generate YouTube chapter timestamps from the text within them.
// > Also lets you choose to include timestamps of certain color markers.
//
// How To use: 1. Select any graphics clips on the timeline that contain the chapter titles.
// 2. Run the script. It will show an alert box with the timestamps, which you can copy by focusing the dialog and pressing Ctrl+C.
//
// Author Repo: https://github.com/ThioJoe/Adobe-Apps-Scripts-And-Tools
//
//
// ======================== USER SETTINGS =============================
// Whether to add an "Intro" timestamp for 0:00
var addIntro = true
// Whether to show the exact timecode for the timestamps. If false, will show rounded down to the nearest second
var exactTimecode = false
// In addition to selected clips, you can get the timestamps of markers of chosen colors set below. See below for color indexes.
// Put a comma separated list of the numbers between the brackets. Leave empty to not include any markers.
var markerColorsInclude = [ 4 ]
// Text to show for matched markers without a name (if applicable)
var coloredMarkerText = "[MARKER]"
// Marker color must be represented as the index of the color in the marker panel
// 0 = Green
// 1 = Red
// 2 = Purple
// 3 = Orange
// 4 = Yellow
// 5 = White
// 6 = Blue
// 7 = Cyan
// You can also set colors of markers that will be printed as a separate list at the end of the timestamps (comma separated list of colors above)
var markersColorsForSeparateTimestamps = [ 3 ]
// Text to show in the heading for the separate timestamps
var alternateTimestampsText = "Ads @:"
var altTimestampsExactTimecode = true // Whether to show the exact timecode for the separate timestamps
var showCommentsForAltTimestamps = true // If the alt timestamps have text comments, they'll be shown next to them, otherwise only the timecodes will be shown
// --- Fine Tuning Prferences ---
// Whether to use a semicolon within dropframe timecodes, or always use a colon
var useSemicolonInDropframe = false // If true, will use semicolon in dropframe timecodes, otherwise will always use colon
// Whether to always include the full HH:MM:SS:frame timecode for exact timecode. Otherwise hours will be excluded if there are no hours in the timecode,
// and minutes will be shown without leading 0 if there are no hours and no minutes greater than 10 in any of the timecode results
var alwaysFullTimecode = false
// ====================================================================
// ====================================================================
// ---------------------- Include ThioUtils.jsx ----------------------
function getCurrentScriptDirectory() { return (new File($.fileName)).parent; }
function joinPath() { return Array.prototype.slice.call(arguments).join('/'); }
function relativeToFullPath(relativePath) { return joinPath(getCurrentScriptDirectory(), relativePath); }
try { eval("#include '" + relativeToFullPath("ThioUtils.jsx") + "'"); }
catch(e) {
try { var e1=e; eval("#include '" + relativeToFullPath("includes/ThioUtils.jsx") + "'"); } // Check Utils folder
catch(e) { var e2=e; try { eval("#include '" + relativeToFullPath("../ThioUtils.jsx") + "'"); } // Check parent directory
catch (e) { var e3=e; try { eval("#include '" + relativeToFullPath("../includes/ThioUtils.jsx") + "'"); } // Check parent includes folder
catch (e) { var e4=e; alert("Could not find ThioUtils.jsx in current dir, includes folder, or parent dir." + "\n\nAll Attempt Errors: \n"+e1+"\n"+e2+"\n"+e2+"\n"+e3+"\n"+e4); } // Return optional here, if you're within a main() function
}}}
// ---------------------------------------------------------------
function GetSortedArrayFromDictionary(dict) {
var items = []
for (var key in dict) {
items.push([key, dict[key]]);
}
// Sort the array based on the second element
items.sort(function(first, second) {
return first[1] - second[1];
});
return items
}
function sortTimeObjectNestedArray(timeObjArray) {
// Sort the array based on the second element
timeObjArray.sort(function(first, second) {
return first[1].seconds - second[1].seconds;
});
return timeObjArray
}
function MakeTimeCodeMMSS(timestampsArray, noLabels) {
var finalString = ""
for (var i = 0; i < timestampsArray.length; i++) {
var key = timestampsArray[i][0]
var timeSeconds = timestampsArray[i][1]
// Try to round down unless the number is very close to the next whole number
var roundingThreshold = 0.85
if (timeSeconds % 1 > roundingThreshold) {
timeSeconds = Math.ceil(timeSeconds)
} else {
timeSeconds = Math.floor(timeSeconds)
}
// Convert to timecode of HH:MM:SS. Ignore hours if there are none. For minutes, don't show leading 0 unless there are hours. For seconds, always show leading 0.
var hours = Math.floor(timeSeconds / 3600)
var minutes = Math.floor((timeSeconds % 3600) / 60)
var seconds = Math.floor(timeSeconds % 60)
var timecode = ""
// Don't show hours if there are none
if (hours > 0) {
timecode += hours + ":"
}
// If there are hours, show minutes with leading 0. If there are no hours, don't show leading 0, but still show 0 if there are no minutes
if (minutes > 0) {
if (hours > 0) {
// var minuteStr = minutes.toString()
var paddedMinutes = ("0" + minutes.toString()).slice(-2);
timecode += paddedMinutes + ":"
} else {
timecode += minutes + ":"
}
} else if (hours > 0) {
timecode += "00:"
} else {
timecode += "0:"
}
// Always show seconds with leading 0
timecode += ("0" + seconds.toString()).slice(-2);
// Remove the unique index from the end of the key
var cleanedKey;
var separator = "";
if (noLabels) {
cleanedKey = ""
} else {
cleanedKey = key.replace(/~~\d+~~$/, "")
if (cleanedKey !== "") {
separator = " - "
}
}
// Add to the final string
finalString += "\n" + timecode + separator + cleanedKey
}
return finalString;
}
function getMaxOfPart(timeObjArray, partIndex) {
var max = 0
// partIndex: 0 = hours, 1 = minutes, 2 = seconds
for (var i = 0; i < timeObjArray.length; i++) {
// The timecode generated by premiere should always have an hours part
var timeObj = timeObjArray[i][1]
var timecodeStr = ThioUtils.getTimecodeString_FromTimeObject(timeObj)
if (timecodeStr.indexOf(":") !== -1) {
var timecodeParts = timecodeStr.split(":")
} else if (timecodeStr.indexOf(";") !== -1) {
var timecodeParts = timecodeStr.split(";")
}
// Check if the first part (hours) is greater than 0
if (timecodeParts.length > partIndex - 1 && parseInt(timecodeParts[partIndex]) > 0) {
var partValue = parseInt(timecodeParts[partIndex])
if (partValue > max) {
max = partValue
}
}
}
return max
}
function makeTimeCodeAsIs(timeObjArray, noLabels) {
var finalString = ""
for (var i = 0; i < timeObjArray.length; i++) {
var key = timeObjArray[i][0]
var timeObj = timeObjArray[i][1]
var timecode = ThioUtils.getTimecodeString_FromTimeObject(timeObj)
var cleanedKey;
if (noLabels) {
cleanedKey = ""
} else {
cleanedKey = key.replace(/~~\d+~~$/, "")
}
// Determine if it's ; or : based on the timecode format, split on the correct character
var frameChar;
if (timecode.indexOf(":") !== -1) {
var timecodeParts = timecode.split(":")
frameChar = ":"
} else if (timecode.indexOf(";") !== -1) {
var timecodeParts = timecode.split(";")
if (useSemicolonInDropframe) {
frameChar = ";"
} else {
frameChar = ":"
}
}
var includeHoursPart = getMaxOfPart(timeObjArray, 0) > 0
var hasAnyMinutesOver10 = getMaxOfPart(timeObjArray, 1) > 9
// Reassemble the timecode
var reassembledTimecode = ""
for (var j = 0; j < timecodeParts.length; j++) {
// If the first part is hours, only add it if there's any results that have non-zero hours
if (j === 0 && includeHoursPart == false && alwaysFullTimecode !== true) {
continue; // Skip the hours part if it's 0
}
var timecodePartToAdd = timecodeParts[j]
// If it's the minute part, remove any leading zero if there's no hours and there's no minute part greater than 10 in the array
if (j === 1) {
if (Number(timecodePartToAdd) < 10 && !includeHoursPart && hasAnyMinutesOver10 === false && alwaysFullTimecode !== true) {
timecodePartToAdd = timecodePartToAdd.replace(/^0/, ""); // Remove leading zero if it's less than 10 and no hours
}
}
reassembledTimecode += timecodePartToAdd
// Add the frame character if it's not the last part
if (j < timecodeParts.length - 1) {
reassembledTimecode += frameChar;
}
}
var separator = "";
if (noLabels) {
separator = ""
} else {
if (cleanedKey !== "") {
separator = " - "
}
}
finalString += "\n" + reassembledTimecode + separator + cleanedKey
}
return finalString;
}
function MakeTimestamps(addIntro, includeMarkerColors, coloredMarkerText, markersOnly, noLabels, exactTimecode) {
var activeSequence = app.project.activeSequence
var selectedVanillaClipObjects = []
if (typeof markersOnly === 'undefined' || markersOnly === null || !markersOnly) {
selectedVanillaClipObjects = activeSequence.getSelection()
}
if (typeof noLabels === 'undefined' || noLabels === null || !noLabels) {
noLabels = false
} else {
noLabels = true
}
if (typeof exactTimecode === 'undefined' || exactTimecode === null || !exactTimecode) {
exactTimecode = false
} else {
exactTimecode = true
}
var ui = 0 // "Unique Index" for making sure the keys are unique, append to key, will be removed later
// Function to create a unique suffix. Doing this since if we go above 9 there will be multiple digits and can't simply remove the last character
function uni(key) {
return "~~" + ui + "~~"
}
// Dictionary of text in the titles and their start times
var timestamps = {}
var timestamps_timeObjs = []
if (addIntro) {
timestamps["Intro" + uni()] = 0
var tempArray = []
tempArray[0] = "Intro" + uni()
tempArray[1] = ThioUtils.secondsToTimeObject(0)
timestamps_timeObjs.push(tempArray)
ui++
}
// For adding timestamps based on markers of certain colors
if (includeMarkerColors.length > 0) {
// Get the markers
var markers = activeSequence.markers
for (var i = 0; i < markers.numMarkers; i++) {
var marker = markers[i];
var markerTime = marker.start.seconds;
var markerTimeObject = ThioUtils.getTimecodeString_FromTimeObject(marker.start)
var markerName;
if (marker.name === "") {
markerName = coloredMarkerText;
} else {
markerName = marker.name;
}
// Get the color of the marker, see if it matches any of the chosen included colors
var markerColor = marker.getColorByIndex(i);
for (var j = 0; j < includeMarkerColors.length; j++) {
if (markerColor === includeMarkerColors[j]) {
timestamps[markerName + uni()] = markerTime;
tempArray = []
tempArray[0] = markerName + uni()
tempArray[1] = marker.start
timestamps_timeObjs.push(tempArray)
ui++;
}
}
}
}
// For adding timestamps based on text in the selected graphics clips
for (var i = 0; i < selectedVanillaClipObjects.length; i++) {
var vanillaClip = selectedVanillaClipObjects[i];
var startTime = vanillaClip.start.seconds
// Go through the components (effects) of the clip to find the text
for (var j = 0; j < vanillaClip.components.length; j++) {
var component = vanillaClip.components[j]
if (component.matchName === "AE.ADBE Text") {
var textContent = component.instanceName
// Strip any newlines. NOTE: For some reason extendscript will replace \n with \r in strings silently, so we actually need to replace \r
var textContentCleaned = textContent.replace(/\r\n|\r|\n/g, " ");
timestamps[textContentCleaned + uni()] = startTime
tempArray = []
tempArray[0] = textContentCleaned + uni()
tempArray[1] = vanillaClip.start
timestamps_timeObjs.push(tempArray)
ui++
}
}
}
var timestampsSorted = {}
var timestampsFullSorted = {}
// Sort the dictionary by time
timestampsSorted = GetSortedArrayFromDictionary(timestamps)
timestampsFullSorted = sortTimeObjectNestedArray(timestamps_timeObjs)
if (!exactTimecode) {
var finalString = MakeTimeCodeMMSS(timestampsSorted, noLabels)
} else {
var finalString = makeTimeCodeAsIs(timestampsFullSorted, noLabels)
}
return finalString
}
var finalStringToPrint
finalStringToPrint = MakeTimestamps(true, markerColorsInclude, coloredMarkerText, null, null, exactTimecode)
// For the secondary set of timestamps, if any
if (markersColorsForSeparateTimestamps.length > 0) {
var additionalString = MakeTimestamps(false, markersColorsForSeparateTimestamps, "", true, !showCommentsForAltTimestamps, altTimestampsExactTimecode)
if (additionalString !== "") {
finalStringToPrint += "\n\n" + alternateTimestampsText + additionalString
}
}
// Trim the final string to remove any leading or trailing whitespace. There's no trim function in extendscript, so using regex
finalStringToPrint = finalStringToPrint.replace(/^\s+|\s+$/g, "");
// Any final replacements or fixes, such as replacing directional quotes with standard quotes
finalStringToPrint = finalStringToPrint.replace(/[“”]/g, '"').replace(/[‘’]/g, "'");
// if isThioUtilsLoaded() === true {
if (ThioUtils.isThioUtilsLibLoaded() === true) {
var stringAndMessage = finalStringToPrint + "\n\n" + "---------------------------\nCopy to clipboard?";
var choice = confirm(stringAndMessage, false, "Timestamps");
if (choice === true){
var copyResult = ThioUtils.copyToClipboard(finalStringToPrint);
if (copyResult === false){
var fallbackMessage = "\n" + finalStringToPrint + "\n\n" + "---------------------------\nFailed to copy to clipboard. You can try again by focusing this dialog box and pressing Ctrl+C";
alert(fallbackMessage)
}
}
} else {
// Shows the final string in an alert box, which you can copy by focusing the dialog and pressing Ctrl+C
var stringAndMessage = "\n" + finalStringToPrint + "\n\n" + "---------------------------\nCopy the text by focusing this dialog box and pressing Ctrl+C";
alert(stringAndMessage)
}