Skip to main content
Participant
July 7, 2026

Frame.io v4 completely broken in After Effects

  • July 7, 2026
  • 8 replies
  • 134 views

My Frame.io account was just upgraded to v4 and I’m now unable to use Frame.io inside After Effects.

I’m on Windows 11 and I’ve updated to the latest version of After Effects 2026 v26.3 and see the legacy Frame.io review window in the review workspace. This old legacy window no longer works with my v4 Frame.io account - that I understand. Unfortunately going to Window > Frame.io V4 does absolutely nothing. No new windows are opened and I’m completely dead in the water.

 

Things I’ve tried so far:
Uninstall AE 2026, reinstall without retaining any previous settings - same issue

Start AE 2026 in safe mode - same issue

Control Panel > Credential Manager > completely deleted anything related to Adobe or Frame.io, restarted windows, launched AE 2026 - same issue

    8 replies

    Participating Frequently
    September 4, 2026

    As a temporary solution, a little help, Gemini made me a script (😅) that works for my case:


    (function() {
        var comp = app.project.activeItem;
        if (!(comp instanceof CompItem)) {
            alert("Por favor, abra ou selecione a composição ativa antes de rodar o script.");
            return;
        }

        var csvFile = File.openDialog("Selecione o arquivo CSV do Frame.io", "*.csv");
        if (!csvFile) return;

        csvFile.open("r");
        var fileContent = csvFile.read(); 
        csvFile.close();

        var rows = parseCSVContent(fileContent);
        if (rows.length === 0) {
            alert("O arquivo CSV está vazio.");
            return;
        }

        var headers = rows[0];
        var commentIdx = findInArray(headers, "Comment");
        var timecodeInIdx = findInArray(headers, "Timecode In");
        var timecodeOutIdx = findInArray(headers, "Timecode Out");
        var frameIdx = findInArray(headers, "Frame");
        var durationIdx = findInArray(headers, "Duration");

        if (commentIdx === -1 || timecodeInIdx === -1 || timecodeOutIdx === -1) {
            alert("Não foi possível encontrar as colunas necessárias no CSV.");
            return;
        }

        app.beginUndoGroup("Gerar Legendas Formatadas no Rodapé");

        var textLayer = comp.layers.addText("Frame.io");
        textLayer.guideLayer = true;

        for (var i = 1; i <= comp.layers.length; i++) {
            comp.layers[i].selected = false;
        }
        textLayer.selected = true;

        var textProp = textLayer.property("Source Text");
        var textDocument = textProp.value;
        textDocument.justification = ParagraphJustification.CENTER_JUSTIFY;
        textProp.setValue(textDocument);

        textLayer.property("Position").setValue([960, 930]);

        var importCount = 0;
        var skippedCount = 0;
        var markerProp = textLayer.property("Marker");

        for (var j = 1; j < rows.length; j++) {
            var row = rows[j];
            if (row.length > Math.max(commentIdx, timecodeInIdx, timecodeOutIdx)) {
                var commentRaw = row[commentIdx];
                var tcInStr = row[timecodeInIdx];
                var tcOutStr = row[timecodeOutIdx];

                if (commentRaw && (tcInStr || (frameIdx !== -1 && row[frameIdx] !== ""))) {
                    var timeIn = 0;
                    var timeout = 0;

                    // Usa a contagem de frames do comp.frameDuration para alinhamento perfeito
                    if (frameIdx !== -1 && row[frameIdx] !== "" && !isNaN(parseFloat(row[frameIdx]))) {
                        timeIn = parseFloat(row[frameIdx]) * comp.frameDuration;
                        if (durationIdx !== -1 && row[durationIdx] !== "" && !isNaN(parseFloat(row[durationIdx]))) {
                            var durFrames = parseFloat(row[durationIdx]);
                            timeout = durFrames > 0 ? (timeIn + (durFrames * comp.frameDuration)) : timeIn;
                        } else {
                            timeout = timeIn;
                        }
                    } else {
                        timeIn = timecodeToSeconds(tcInStr, comp.frameRate);
                        timeout = timecodeToSeconds(tcOutStr, comp.frameRate);
                    }

                    var commentFormatted = wrapText(commentRaw, 60);
                    var markerVal = new MarkerValue(commentFormatted);

                    if (timeout > timeIn) {
                        markerVal.duration = timeout - timeIn;
                    } else {
                        markerVal.duration = 0;
                    }

                    // Verifica se já existe um marcador no mesmo tempo exato (ou dentro do mesmo frame)
                    var isOverwrite = false;
                    if (markerProp.numKeys > 0) {
                        var nearestIdx = markerProp.nearestKeyIndex(timeIn);
                        if (Math.abs(markerProp.keyTime(nearestIdx) - timeIn) < (comp.frameDuration / 2)) {
                            isOverwrite = true;
                        }
                    }

                    if (isOverwrite) {
                        skippedCount++;
                    } else {
                        importCount++;
                    }

                    markerProp.setValueAtTime(timeIn, markerVal);
                }
            }
        }

        // EXPRESSÃO DE DETECÇÃO INSTANTÂNEA AO ENCAIXAR A AGULHA (SNAP COM SHIFT)
        var expr = 
            "var m = thisLayer.marker;\n" +
            "var txt = \"\";\n" +
            "if (m.numKeys > 0) {\n" +
            "    var k = m.nearestKey(time);\n" +
            "    var halfFrame = thisComp.frameDuration / 2;\n" +
            "    if (k.duration > 0) {\n" +
            "        if (time >= (k.time - halfFrame) && time <= (k.time + k.duration)) {\n" +
            "            txt = k.comment;\n" +
            "        }\n" +
            "    } else {\n" +
            "        if (Math.abs(time - k.time) <= halfFrame) {\n" +
            "            txt = k.comment;\n" +
            "        }\n" +
            "    }\n" +
            "}\n" +
            "txt;";

        textProp.expression = expr;
        textLayer.locked = true;

        app.endUndoGroup();

        var msg = "Processo concluído!\n" + importCount + " marcadores criados na layer.";
        if (skippedCount > 0) {
            msg += "\n\nAviso: " + skippedCount + " comentário(s) sobreposto(s) no mesmo tempo foram ignorado(s)/sobrescritos.";
        }
        alert(msg);

        // ==========================================
        // Funções auxiliares
        // ==========================================

        function findInArray(arr, item) {
            for (var i = 0; i < arr.length; i++) {
                if (arr[i] === item) return i;
            }
            return -1;
        }

        function wrapText(str, maxChars) {
            var words = str.split(' ');
            var lines = [];
            var currentLine = '';

            for (var i = 0; i < words.length; i++) {
                var word = words[i];
                if (word.length > maxChars) {
                    if (currentLine.length > 0) {
                        lines.push(currentLine);
                        currentLine = '';
                    }
                    var start = 0;
                    while (start < word.length) {
                        lines.push(word.substring(start, start + maxChars));
                        start += maxChars;
                    }
                    continue;
                }

                if ((currentLine + (currentLine.length > 0 ? ' ' : '') + word).length <= maxChars) {
                    currentLine += (currentLine.length > 0 ? ' ' : '') + word;
                } else {
                    lines.push(currentLine);
                    currentLine = word;
                }
            }
            if (currentLine.length > 0) lines.push(currentLine);
            return lines.join('\n');
        }

        function timecodeToSeconds(tc, fps) {
            if (!tc) return 0;
            tc = String(tc).replace(/;/g, ":");
            var parts = tc.split(":");
            if (parts.length < 3) return 0;

            var hours = parseFloat(parts[0]) || 0;
            var minutes = parseFloat(parts[1]) || 0;
            var seconds = parseFloat(parts[2]) || 0;
            var frames = parts.length === 4 ? parseFloat(parts[3]) : 0;

            var nominalFps = Math.round(fps);
            var totalFrames = ((hours * 3600) + (minutes * 60) + seconds) * nominalFps + frames;

            return totalFrames / fps;
        }

        function parseCSVContent(text) {
            var lines = [];
            var row = [""];
            var i = 0;
            var inQuotes = false;

            for (var p = 0; p < text.length; p++) {
                var c = text.charAt(p);
                var nextC = text.charAt(p + 1);

                if (c === '"') {
                    if (inQuotes && nextC === '"') {
                        row[i] += '"';
                        p++; 
                    } else {
                        inQuotes = !inQuotes;
                    }
                } else if (c === ',' && !inQuotes) {
                    i++;
                    row[i] = "";
                } else if ((c === '\r' || c === '\n') && !inQuotes) {
                    if (c === '\r' && nextC === '\n') {
                        p++;
                    }
                    cleanRow(row);
                    lines.push(row);
                    row = [""];
                    i = 0;
                } else {
                    row[i] += c;
                }
            }
            if (row.length > 1 || row[0] !== "") {
                cleanRow(row);
                lines.push(row);
            }
            return lines;
        }

        function cleanRow(row) {
            for (var j = 0; j < row.length; j++) {
                row[j] = row[j].replace(/^\s+|\s+$/g, '');
            }
        }
    })();

    Participating Frequently
    September 4, 2026

    It brings that marked and commented guide layer within your comp that Frame.io used to provide.

    Participating Frequently
    September 4, 2026

    1) Open a simple text editor (as the Windows Notepad), paste this code, name and save it as a jsx file

    2) Install it into After Effects, so that you have the convenience of having it at the menu or simple run it and follow the instructions

     

    sskaz
    Inspiring
    July 8, 2026

    Frame.io v4 support might still be in beta: 

     

    Participant
    July 8, 2026

    That’s a pretty crappy way to roll out a new system. I have a corporate Adobe account and they don’t give us access to the beta versions, so this fancy new “upgrade” to v4 has crippled my workflow.

    sskaz
    Inspiring
    July 9, 2026

    It is crummy. Smells like someone in charge mandated that all the apps need to push out X version on Y date, regardless if things are ready or in sync with their other products.

    Participating Frequently
    July 8, 2026

    Same to me.