× Attention! These bookmarklets are outdated. Install the Chrome extension instead. Please.

Bookmarklets to Extend and Improve Originator UX

DoubleExport SRT
(now with FNs, italics, positioning!)

Check out Import SRT


/* ATTENTION: NEW BETA VERSION
EXPORTS SOURCE COLUMN AS WELL
	USAGE
Drag the button above to your Bookmarks Bar in Chrome.
Click the button when in an Originator task.

	DISCLAIMER
This script is provided as a convenience tool. 
The author will not be held liable for its potential misuse.
The user agrees to use the script responsibly and treat
SRT files obtained with its help as Netflix confidential
material as described in the FNs, NDA,

As with other confidential assets the SRT file must be deleted 
when you finish working on the project.

The source code for this tool is provided below.
Please make sure to review it.

(c) Dan Biktashev katzurki@gmail.com
May 24 2020 v2.0
*/


var our_clq = document.location.href.toString().split("=")[1].split(":")[2]
var clq_pattern = new RegExp('^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$','i');

if (!our_clq || !clq_pattern.test(our_clq)) {
    alert("You must be in an Originator task!");
    throw new Error
}

if (!clq_pattern.test(our_clq)) {
    alert("The CLQ is invalid: " + our_clq + "\n" + "Please report me to the developer.");
    throw new Error
}

var lsJson = localStorage["clq:origination:" + our_clq]
if (!lsJson) {
    alert("Timed text events not found in localStorage\nfor CLQ: " + our_clq + "\n" + "If the CLQ is correct and" + "\n" + "\"Save to local storage\" is enabled in Settings," + "\n" + "save the task and try again.");
    throw new Error
}
var json_obj = JSON.parse(lsJson)
var src = json_obj["events"]
var fps_ = json_obj["meta"]["fps"];
var fps = fps_.split("_")[1] / 100
//From {"fps":"FPS_2500"}
var proposed_fps = prompt("Framerate set to: " + fps + " fps.\n\nEnter new value like 2997, 2500 or 23976023976023978:");
if (proposed_fps) {
    var int_fps = proposed_fps.substring(0, 2);
    var decimal_fps = proposed_fps.substring(2, proposed_fps.length);
    fps_ = "FPS" + "_" + int_fps+decimal_fps
    fps = (int_fps + "." + decimal_fps)*1;
}

alert("Proceeding with FPS " + fps + ". File will be named _"+fps_)
//if(fps >= 23.97 && fps <= 24) fps = 24 //To take care of dropframes
var frms = 1000 / fps
download(srtName(fps_), array2srt(src))

function srtName(suffix = "") {
    var s = document
        .getElementsByClassName(
            "cpe-page-menu-label")[0]
        .innerText
    var srtName = (s.replace(/[ ]/g
                , '_')
            .replace(/[^a-z0-9_]/gi
                , '') + "_" + suffix + ".srt"
        ) //This gets rid of all punctuation, spaces and non-English letters
        .trim() //resulting in a name like 14545_El_Burrito_A_Breaking_Fat_Movie_FPS_2500.srt
    if (!srtName) srtName = our_clq +
        "_" + suffix +
        ".srt" //Fallback measure. Useful for debugging later
    return srtName
}

function frames2tcf(framenumber, framerate) { //frames to 00:01:02:24 format
var seconds_float = framenumber / framerate
var seconds_int = Math.floor(seconds_float)
var seconds_frac = seconds_float - seconds_int
var frames = Math.round(seconds_frac * framerate) + ''
var date = new Date(0);
date.setSeconds(seconds_int);
var timeString = date.toISOString().substr(11, 8)
if(frames.length == 1) frames = "0"+frames
var timecodef = timeString + ":" + frames
return timecodef
}

function frames2timecode(frames) { //frames to 00:01:02,000 format
    var milliseconds = Math.round(
        frames * frms)
    var srt_timecode = TimeConversion(
        milliseconds)
    return srt_timecode
}

function array2srt(events_object) {
    var ordered_events = []
    for (var id in events_object) {
        try{ var type_fn = events_object[id]["type"]
            if(type_fn === "fn") {events_object[id]["txt"]+="<b></b>"; type_fn = undefined; delete(type_fn)}
    } catch(e) {}
            ordered_events.push([
            events_object[id][
            "start"
            ]
            , events_object[id][
            "end"
            ], events_object[id]
            ["txt"],
            events_object[id]
            ["styles"],
            events_object[id]
            ["rgn"]
            ])
    }
    ordered_events.sort(function (a
        , b) {
        return a[0] - b[0];
    }); //Array sorted by in_cues, sequentially
    var index = 0
    var srt_txt = ''
    for (event of ordered_events) {
        index++
        var start = frames2timecode(
            event[0])
        var end = frames2timecode(event[
            1])
        var content = event[2]
        try { if( typeof event[3][0]["type"] !== "undefined" ) { if(event[3][0]["type"] == "italic" ) 
            { content = italicize(content, event[3]) } 
        } } catch (e) {}
        
        try {
        if( typeof event[4] !== "undefined" ) { if (event[4] == "top" ) {
            content = "{\\an8}" + content
        } } } catch (e) {}

        try{ if(event["type"] == "fn") {content += '<b></b>'} } catch(e) {}
        console.log(content)
        var current_event = index +
            "\n" + start + " --> " +
            end +
            "\n" + content + "\n"
        srt_txt += current_event + "\n"
    }
    return srt_txt
}


function italicize(content, italics_array){
position_offset = 0
for(var italic of italics_array){
    var position_from = italic["from"]+position_offset; position_offset += 3
    content = [content.slice(0, position_from), "<i>", content.slice(position_from)].join('')
    var position_to = italic["to"]+position_offset; position_offset += 4
    content = [content.slice(0, position_to), "</i>", content.slice(position_to)].join('')
}
return content
}

function download(filename, text) {
    var element = document
        .createElement('a');
    element.setAttribute('href'
        , 'data:text/plain;charset=utf-8,' +
        encodeURIComponent(text));
    element.setAttribute('download'
        , filename);
    element.style.display = 'none';
    document.body.appendChild(element);
    element.click();
    document.body.removeChild(element);
}

function TimeConversion(duration) {
    let time = parseDuration(duration)
    return formatTimeHMSS(time)
}

function parseDuration(duration) {
    let remain = duration
    let hours = Math.floor(remain / (
        1000 * 60 * 60))
    remain = remain % (1000 * 60 * 60)
    let minutes = Math.floor(remain / (
        1000 * 60))
    remain = remain % (1000 * 60)
    let seconds = Math.floor(remain / (
        1000))
    remain = remain % (1000)
    let milliseconds = remain
    return {
        hours
        , minutes
        , seconds
        , milliseconds
    }
}

function formatTimeHMSS(o) {
    let hours = o.hours.toString()
    if (hours.length === 1) hours =
        '0' + hours
    let minutes = o.minutes.toString()
    if (minutes.length === 1) minutes =
        '0' + minutes
    let seconds = o.seconds.toString()
    if (seconds.length === 1) seconds =
        '0' + seconds
    let milliseconds = o.milliseconds
        .toString()
    if (milliseconds.length === 1)
        milliseconds = '00' +
        milliseconds
    if (milliseconds.length === 2)
        milliseconds = '0' +
        milliseconds
    return hours + ":" + minutes + ":" +
        //Example: 00:01:02,999 -- note that the SRT spec calls for a comma, not a period!
        seconds + "," +
        milliseconds
}   

CHANGELOG


v1.0. Initial beta release
v1.0.1 Now the SRT file is saved as a proper file
v2.0 Everything rewritten in vanilla JS - no more jQuery. Speed improvements. Additional checks.  
WIP (these are works in progress and not guaranteed to work or work well):

FlixFont+
Save MP4
Save SRT
AutoQC