0

Tự động hóa dựng video trên Premiere Pro bằng AI: ExtendScript, CEP/UXP và MCP từ góc nhìn developer

Phần lớn thời gian hậu kỳ một video nói chuyện (talking head) dành cho các thao tác lặp lại: cắt đoạn im lặng, gắn phụ đề, chèn nhạc, đánh marker rồi xuất thêm bản 9:16. Các thao tác này có quy tắc rõ ràng, nên về lý thuyết có thể giao cho máy. Vấn đề nằm ở chỗ một mô hình ngôn ngữ như Claude hay Grok chỉ sinh ra văn bản và lời gọi công cụ, nó không tự bấm được nút nào trong Premiere Pro.

Để AI "dựng" được video, cần ba lớp: một API scripting bên trong Premiere, một nơi chạy code đó (panel hoặc plugin), và một giao thức để trợ lý AI gọi các hàm này.

Các lớp scripting của Premiere Pro

ExtendScript

ExtendScript là phương ngữ JavaScript của Adobe, dựa trên chuẩn ECMAScript 3. Bạn không có let, const, arrow function hay đối tượng JSON có sẵn, nên các dự án thường nhúng thêm polyfill như json2.js để trao đổi dữ liệu có cấu trúc.

Premiere cung cấp một DOM cho ExtendScript, bắt đầu từ app.project. Từ đó bạn truy cập sequences, activeSequence, videoTracks và audioTracks, từng clip trên track (TrackItem), marker, project item và app.encoder để gửi job sang Adobe Media Encoder. Tài liệu cộng đồng duy trì tại ppro-scripting.docsforadobe.dev là nơi tra cứu chữ ký hàm tiện nhất. Để viết và debug, bạn có thể dùng VS Code với extension ExtendScript Debugger của Adobe.

Ngoài DOM chính thức còn có QE DOM, bật bằng app.enableQE(). Cộng đồng hay dùng QE DOM cho các thao tác như razor (cắt clip tại một điểm), nhưng đây là API không có tài liệu chính thức và có thể thay đổi giữa các bản Premiere.

CEP

CEP (Common Extensibility Platform) cho phép làm panel bằng HTML, CSS và JavaScript, chạy trên Chromium kèm Node.js bên trong Premiere. Panel gọi ExtendScript qua CSInterface.evalScript(code, callback), và kết quả trả về callback luôn ở dạng chuỗi. Tài liệu scripting của Premiere khuyến nghị chạy ExtendScript từ panel CEP thay vì truyền script qua dòng lệnh.

UXP

UXP (Unified Extensibility Platform) là nền tảng plugin mới, dùng engine JavaScript hiện đại và có Premiere DOM API riêng. Theo trang UXP của Premiere, plugin UXP đã ở trạng thái phát hành chính thức (GA), kèm hỗ trợ Hybrid plugin để gọi thư viện C++. Trong thông báo tháng 9/2026, Adobe nêu lộ trình cho Premiere như sau:

  • Từ tháng 12/2027, Adobe Marketplace ngừng nhận plugin CEP mới cho Premiere.
  • Từ tháng 12/2028, CEP bị tắt mặc định, người dùng vẫn có thể bật lại.
  • Từ tháng 12/2029, các bản Premiere mới không còn đi kèm CEP.

Adobe cũng ghi rằng ExtendScript không bị ảnh hưởng bởi đợt chuyển đổi này. Với dự án mới, bạn nên đánh giá UXP ngay từ đầu. Với dự án đang chạy CEP và ExtendScript, bạn vẫn còn thời gian nhưng nên tách logic nghiệp vụ khỏi lớp gọi API để sau này chuyển sang UXP dễ hơn.

MCP: cầu nối giữa trợ lý AI và Premiere

MCP (Model Context Protocol) là chuẩn mở do Anthropic công bố cuối năm 2024. Ứng dụng chủ (host) như Claude Desktop kết nối tới các MCP server. Mỗi server khai báo một danh sách tool, gồm tên, mô tả và JSON Schema cho tham số. Khi cần, mô hình sinh lời gọi tool, host chuyển lời gọi đó tới server và trả kết quả về cho mô hình. MCP server chạy local thường giao tiếp với host qua stdio. Chi tiết đặc tả có tại modelcontextprotocol.io.

Với Premiere, một kiến trúc thường gặp trông như sau:

Trợ lý AI (Claude, Grok...)
   │  lời gọi tool (JSON)
   ▼
MCP server (Node.js hoặc Python, chạy trên máy dựng)
   │  WebSocket / HTTP qua localhost
   ▼
Panel CEP hoặc plugin UXP bên trong Premiere
   │  evalScript (ExtendScript) hoặc Premiere DOM API (UXP)
   ▼
Project, sequence, timeline

Cách thiết kế tool quyết định độ an toàn của cả hệ thống. Một tool kiểu run_script(code) cho phép AI chạy code tùy ý, rất linh hoạt nhưng khó kiểm soát và khó debug. Bộ tool nhỏ, rõ nghĩa như get_timeline, import_files, create_subclips, add_caption_track và queue_export dễ kiểm thử hơn, và cho phép bạn đặt điều kiện xác nhận trước các thao tác có tính phá hủy.

Đọc trạng thái timeline

Mọi quy trình tự động nên bắt đầu bằng thao tác chỉ đọc. Đoạn ExtendScript dưới đây liệt kê clip trên các track video của sequence đang mở:

// ExtendScript (ES3) — chạy qua evalScript từ panel CEP hoặc ExtendScript Debugger
(function () {
    var seq = app.project.activeSequence;
    if (!seq) {
        return "NO_ACTIVE_SEQUENCE";
    }
    var lines = [];
    for (var t = 0; t < seq.videoTracks.numTracks; t++) {
        var track = seq.videoTracks[t];
        for (var c = 0; c < track.clips.numItems; c++) {
            var clip = track.clips[c];
            lines.push("V" + (t + 1) + " | " + clip.name +
                " | start=" + clip.start.seconds.toFixed(2) +
                " | end=" + clip.end.seconds.toFixed(2));
        }
    }
    return lines.join("\n");
})();

Các thuộc tính start, end, inPoint, outPoint của TrackItem là đối tượng Time. Một Time có .seconds (số) và .ticks (chuỗi), với 254.016.000.000 tick mỗi giây. Một số hàm nhận tham số thời gian ở dạng tick, vì vậy hàm quy đổi giây sang tick sẽ được dùng nhiều.

Quy trình thực tế: từ footage thô đến bản xuất

Bước 1: Phân tích nguồn bằng ffmpeg

API scripting của Premiere không cung cấp hàm đọc mức âm thanh hay waveform của clip. Vì vậy, việc đo đoạn im lặng nên làm trực tiếp trên file nguồn bằng ffmpeg:

# Ghi các đoạn im lặng (dưới -35 dB, dài từ 0,7 giây) ra log
ffmpeg -hide_banner -i C017.mp4 -af silencedetect=noise=-35dB:d=0.7 -f null - 2> C017_silence.log

# Lấy thời lượng file
ffprobe -v error -show_entries format=duration -of csv=p=0 C017.mp4

# Trích 1 khung hình mỗi 5 giây để gửi cho mô hình có khả năng đọc ảnh
ffmpeg -i C017.mp4 -vf fps=1/5 -q:v 3 frames/C017_%04d.jpg

Ngưỡng -35dB và độ dài 0.7 giây chỉ là điểm xuất phát; bạn nên thử trên vài file mẫu trước khi chạy cả lô.

Sau đó, MCP server đổi log thành danh sách đoạn cần giữ. Phần đệm (pad) giữ lại một chút khoảng lặng để câu nói không bị cụt:

// Node.js: chuyển log silencedetect thành danh sách [start, end] cần giữ (giây)
const fs = require("fs");

function keepSegments(logPath, duration, pad = 0.15) {
  const log = fs.readFileSync(logPath, "utf8");
  const starts = [...log.matchAll(/silence_start: (-?[\d.]+)/g)].map((m) => Number(m[1]));
  const ends = [...log.matchAll(/silence_end: ([\d.]+)/g)].map((m) => Number(m[1]));
  const keep = [];
  let cursor = 0;
  starts.forEach((s, i) => {
    const e = ends[i] ?? duration;
    if (s - cursor > 0.05) keep.push([cursor, Math.min(s + pad, duration)]);
    cursor = Math.max(e - pad, 0);
  });
  if (duration - cursor > 0.05) keep.push([cursor, duration]);
  return keep;
}

Bước 2: Cắt đoạn im lặng mà không đụng vào sequence gốc

Thay vì razor trực tiếp trên timeline đang dùng, bạn có thể tạo subclip cho từng đoạn cần giữ rồi dựng một sequence mới từ các subclip đó. Cách này dùng hai hàm có trong DOM chính thức là ProjectItem.createSubClip() và Project.createNewSequenceFromClips():

// ExtendScript: dựng sequence mới từ danh sách đoạn cần giữ
var TICKS_PER_SECOND = 254016000000;

function secToTicks(s) {
    return String(Math.round(s * TICKS_PER_SECOND));
}

function findItemByName(parent, name) {
    for (var i = 0; i < parent.children.numItems; i++) {
        var item = parent.children[i];
        if (item.name === name) return item;
        if (item.type === ProjectItemType.BIN) {
            var found = findItemByName(item, name);
            if (found) return found;
        }
    }
    return null;
}

var keep = [[0.0, 4.35], [4.95, 12.9], [13.45, 30.0]]; // kết quả từ bước 1
var source = findItemByName(app.project.rootItem, "C017.mp4");
var parts = [];
for (var i = 0; source && i < keep.length; i++) {
    var sub = source.createSubClip("C017_part_" + (i + 1),
        secToTicks(keep[i][0]), secToTicks(keep[i][1]), 0, 1, 1);
    if (sub) parts.push(sub);
}
if (parts.length) {
    app.project.createNewSequenceFromClips("C017_clean", parts, app.project.rootItem);
}

Một lưu ý khi đọc tài liệu: đơn vị thời gian của các hàm chèn clip không thống nhất. Track.insertClip() ghi tham số là tick, Sequence.overwriteClip() ghi là giây, còn ví dụ của chính tài liệu lại truyền start.seconds. Trước khi đưa vào pipeline, bạn nên viết test nhỏ trên một project bản sao để xác nhận hành vi trên phiên bản Premiere đang dùng.

Bước 3: Gắn phụ đề từ file SRT

Với từng video riêng lẻ, tính năng Speech to Text có sẵn trong bảng Text của Premiere là lựa chọn nhanh nhất. Danh sách ngôn ngữ thay đổi theo bản cập nhật, nên bạn cần kiểm tra tiếng Việt có trong bản mình dùng. Khi làm cả lô và đã có sẵn file SRT, bạn có thể nhập file rồi tạo caption track bằng script:

// ExtendScript: nhập SRT và tạo caption track trên sequence đang mở
var seq = app.project.activeSequence;
app.project.importFiles(["D:/Project/Phu_de/C017.srt"], true, app.project.rootItem, false);
var srt = findItemByName(app.project.rootItem, "C017.srt");
if (seq && srt) {
    seq.createCaptionTrack(srt, 0, Sequence.CAPTION_FORMAT_SUBTITLE);
}

createCaptionTrack(projectItem, startAtTime, captionFormat) nhận độ lệch tính bằng giây và trả về true nếu thành công. Sau đó, bạn nên áp một caption style đã lưu để cả series dùng chung kiểu chữ.

Bước 4: Nhạc nền và mức âm lượng

Đây là phần khó tự động hóa nhất. Âm lượng của clip nằm trong component Volume và được chỉnh qua TrackItem.components, nhưng giá trị đọc ghi qua script không trùng với con số dB hiển thị trên giao diện. Bạn cần đo và lập bảng quy đổi trên bản thử trước khi dùng. Hai hướng thực dụng hơn là:

  • Trộn sẵn nhạc nền với giọng nói bằng ffmpeg, ví dụ dùng filter sidechaincompress để nhạc tự nhỏ lại khi có tiếng nói, rồi đưa file đã trộn vào timeline.
  • Để AI dựng phần hình và phụ đề, còn người dựng dùng tính năng ducking trong bảng Essential Sound để hoàn thiện phần âm thanh.

Bước 5: Marker, chapters và xuất file

Marker giúp đánh dấu điểm nhấn và chapters cho YouTube. Việc xuất file có thể đẩy vào hàng đợi của Adobe Media Encoder với preset .epr đã chuẩn bị sẵn:

// ExtendScript: thêm marker và gửi sequence sang Adobe Media Encoder
var seq = app.project.activeSequence;
var m = seq.markers.createMarker(12.5); // thời điểm tính bằng giây
m.name = "Hook";
m.comments = "Câu mở đầu";

var jobId = app.encoder.encodeSequence(
    seq,
    "D:/Exports/C017_16x9.mp4",
    "D:/Presets/YouTube_1080p.epr",
    0, // 0 = toàn bộ sequence, 1 = từ In đến Out, 2 = work area
    1  // xóa job khỏi hàng đợi khi xong
);
app.encoder.startBatch();

encodeSequence() trả về job ID dạng chuỗi, hoặc 0 nếu thất bại.

Vòng lặp an toàn: đọc, sửa, đọc lại

Một agent dựng video đáng tin cậy không coi việc tool trả về "thành công" là xong việc. Sau mỗi thay đổi, agent nên đọc lại timeline (bằng tool như get_timeline ở trên) và so với yêu cầu ban đầu. Nếu kết quả không khớp, agent hoàn tác hoặc dừng lại và báo lỗi. Một vài quy tắc nên đặt cứng trong MCP server thay vì chỉ ghi trong prompt:

  • Luôn dựng ra sequence mới, không sửa sequence người dùng đang mở nếu chưa được đồng ý.
  • Hỏi lại trước khi xóa project item hoặc ghi đè file xuất.
  • Ghi log mọi lời gọi tool kèm tham số để dễ tái hiện lỗi.
  • Chạy thử trên bản sao project trước khi áp dụng cho project thật.

Một ví dụ triển khai: CoolMe AI Premiere Automation

CoolMe AI Premiere Automation là một extension đi theo hướng trên. Theo trang sản phẩm, công cụ chạy trên Windows 10/11, CoolMe khuyến nghị Premiere Pro 2023 trở lên, và người dùng ra lệnh bằng tiếng Việt qua Grok Bot hoặc Claude. Bài Edit video bằng AI trên Premiere Pro trên blog CoolMe mô tả kiến trúc theo tài liệu kỹ thuật của bản 1.0.2: panel xây dựng trên CEP, phía trợ lý AI kết nối qua MCP, và một bridge chuyển lệnh giữa hai bên.

Theo bài viết, mỗi lệnh đi theo vòng lặp đọc trạng thái, thực hiện thay đổi rồi đọc lại timeline. Nếu kết quả không khớp, tài liệu hướng dẫn AI hoàn tác và báo lỗi. Công cụ dùng ffmpeg để trích khung hình gửi cho Grok phân tích và đo đoạn im lặng trên file. Khi dựng mới, công cụ tạo sequence mới, đồng thời hỏi lại trước khi xóa mục trong project, trước khi xuất file khi chưa có đường dẫn và thiết lập xuất, và trước khi sửa sequence đang mở. CoolMe cũng ghi rõ giới hạn của bản hiện tại: Premiere không cho phần mềm bên ngoài đọc trực tiếp mức âm thanh, việc tự hạ nhạc nền dưới giọng nói và chỉnh màu theo kế hoạch vẫn đang được hoàn thiện, còn phụ đề .ass hoặc .ssa có thể bị Premiere báo không hỗ trợ, nên SRT là lựa chọn an toàn nhất.

Giới hạn và mẹo khi tự xây dựng

  • ExtendScript là ES3. Bạn nên viết code đơn giản, tránh cú pháp hiện đại và nhúng polyfill JSON nếu cần trao đổi dữ liệu có cấu trúc.
  • evalScript luôn trả về chuỗi. Bạn nên quy ước định dạng trả về (ví dụ JSON hoặc mã lỗi cố định) để MCP server parse ổn định.
  • Đường dẫn Windows trong chuỗi ExtendScript nên dùng dấu / hoặc escape \\ để tránh lỗi khó phát hiện.
  • Script chạy lâu sẽ chặn giao diện Premiere. Bạn nên chia việc thành nhiều lời gọi nhỏ thay vì một script dài.
  • Hành vi API có thể khác giữa các bản Premiere. Bạn nên ghi rõ phiên bản đã kiểm thử và chạy lại bộ test sau mỗi lần cập nhật.
  • AI xử lý tốt phần lặp lại, nhưng người dựng vẫn cần duyệt phụ đề (tên riêng, con số), điểm cắt ở chỗ chuyển câu và khung hình của bản dọc trước khi đăng.

Nếu muốn thử một bản đã đóng gói sẵn thay vì tự xây bridge, bạn có thể xem CoolMe AI Premiere Automation, hiện có bản dùng thử miễn phí 2 ngày.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.