| <!DOCTYPE html> |
| <html lang="vi"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| <title>Viet AutoSub Editor</title> |
| <link rel="preconnect" href="https://fonts.googleapis.com" /> |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Bangers&family=Pacifico&family=Dancing+Script:wght@400;700&family=Bebas+Neue&family=Lobster&family=Permanent+Marker&family=Playfair+Display:wght@700;900&display=swap" rel="stylesheet" /> |
| <link rel="stylesheet" href="static/styles.css" /> |
| <script src="static/cvnss4.0-converter.js"></script> |
| </head> |
| <body> |
|
|
| |
| <div class="offline-banner" id="offlineBanner" hidden> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="offline-banner-icon"> |
| <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/> |
| </svg> |
| <span id="offlineBannerText">Đang chạy offline — Chức năng AI (auto sub, xuất MP4) cần kết nối server HF Space.</span> |
| <button class="offline-banner-close" id="offlineBannerClose" title="Đóng">×</button> |
| </div> |
|
|
| |
| <nav class="topbar"> |
| <div class="topbar-inner"> |
| <div class="logo-group"> |
| <svg class="logo-icon" viewBox="0 0 32 32" fill="none" aria-label="Viet AutoSub"> |
| <rect x="2" y="6" width="28" height="20" rx="4" stroke="currentColor" stroke-width="2"/> |
| <rect x="6" y="20" width="20" height="4" rx="1.5" fill="currentColor" opacity="0.25"/> |
| <rect x="8" y="21" width="7" height="2" rx="1" fill="currentColor"/> |
| <rect x="17" y="21" width="5" height="2" rx="1" fill="currentColor" opacity="0.6"/> |
| <circle cx="16" cy="13" r="4" stroke="currentColor" stroke-width="1.5"/> |
| <polygon points="14.5,11.5 18.5,13 14.5,14.5" fill="currentColor"/> |
| </svg> |
| <span class="logo-text">Viet AutoSub</span> |
| </div> |
| <div class="topbar-right"> |
| <span class="badge badge-env" id="badgeEnv"> |
| <span class="pulse-dot" id="pulseDot"></span> |
| <span id="badgeEnvText">Đang kiểm tra...</span> |
| </span> |
| <span class="badge badge-model" id="modelBadge">whisper-small</span> |
| </div> |
| </div> |
| </nav> |
|
|
| |
| <main class="main"> |
|
|
| |
| <div class="steps"> |
| <div class="step active" data-step="1"> |
| <div class="step-num">1</div> |
| <div class="step-label">Upload video</div> |
| </div> |
| <div class="step-line"></div> |
| <div class="step" data-step="2"> |
| <div class="step-num">2</div> |
| <div class="step-label">Auto sub tiếng Việt</div> |
| </div> |
| <div class="step-line"></div> |
| <div class="step" data-step="3"> |
| <div class="step-num">3</div> |
| <div class="step-label">Chỉnh sửa subtitle</div> |
| </div> |
| <div class="step-line"></div> |
| <div class="step" data-step="4"> |
| <div class="step-num">4</div> |
| <div class="step-label">Xuất SRT / MP4</div> |
| </div> |
| </div> |
|
|
| |
| <section class="panel upload-panel" id="uploadPanel"> |
| <div class="drop-zone" id="dropZone"> |
| <svg class="drop-icon" viewBox="0 0 48 48" fill="none"> |
| <rect x="4" y="8" width="40" height="32" rx="6" stroke="currentColor" stroke-width="2" stroke-dasharray="4 3"/> |
| <path d="M24 18v12M18 24l6-6 6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> |
| </svg> |
| <p class="drop-title">Kéo thả video vào đây</p> |
| <p class="drop-hint">hoặc click để chọn file — MP4, MOV, MKV, AVI, WebM ≤ 250 MB</p> |
| <input id="videoFile" type="file" accept="video/*" hidden /> |
| </div> |
| <div class="file-info" id="fileInfo" hidden> |
| <div class="file-meta"> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="file-icon"><path d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V7.414A2 2 0 0017.414 6L14 2.586A2 2 0 0012.586 2H4zm8 1.414L15.586 8H13a1 1 0 01-1-1V4.414zM4 5h6v2a3 3 0 003 3h2v5a1 1 0 01-1 1H4a1 1 0 01-1-1V5a1 1 0 011-1z"/></svg> |
| <span id="fileName">video.mp4</span> |
| <span class="file-size" id="fileSize">0 MB</span> |
| </div> |
| <button class="btn btn-ghost btn-sm" id="btnClearFile">Đổi file</button> |
| </div> |
| </section> |
|
|
| |
| <div class="grid-two"> |
|
|
| |
| <section class="panel video-panel"> |
| <div class="panel-head"> |
| <h2 class="panel-title"> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-sm"><path d="M6.672 1.911a1 1 0 10-1.932.518l.259.966a1 1 0 001.932-.518l-.26-.966zM2.429 4.74a1 1 0 10-.517 1.932l.966.259a1 1 0 00.517-1.932l-.966-.26zm8.814-.569a1 1 0 00-1.415-1.414l-.707.707a1 1 0 101.415 1.415l.707-.708zm-7.071 7.072l.707-.707A1 1 0 003.465 9.12l-.708.707a1 1 0 001.415 1.415zm3.2-5.171a1 1 0 00-1.3 1.3l4 10a1 1 0 001.823.075l1.38-2.759 3.018 3.02a1 1 0 001.414-1.415l-3.019-3.02 2.76-1.379a1 1 0 00-.076-1.822l-10-4z"/></svg> |
| Xem trước |
| </h2> |
| </div> |
| <div class="video-wrap" id="videoWrap"> |
| <video id="preview" controls playsinline></video> |
| <div class="video-placeholder" id="videoPlaceholder"> |
| <svg viewBox="0 0 64 64" fill="none" class="placeholder-icon"> |
| <rect x="8" y="14" width="48" height="36" rx="6" stroke="currentColor" stroke-width="2"/> |
| <polygon points="26,24 42,32 26,40" fill="currentColor" opacity="0.3"/> |
| </svg> |
| <span>Chưa có video</span> |
| </div> |
| |
| <div class="sub-preview-overlay" id="subPreviewOverlay" hidden> |
| <span class="sub-preview-text" id="subPreviewText">Phụ đề mẫu — Xem trước Karaoke</span> |
| </div> |
| </div> |
| </section> |
|
|
| |
| <section class="panel action-panel"> |
| <div class="panel-head"> |
| <h2 class="panel-title"> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-sm"><path fill-rule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"/></svg> |
| Điều khiển |
| </h2> |
| </div> |
|
|
| <div class="action-stack"> |
| |
| <div class="mode-selector"> |
| <label class="mode-label">Chế độ nhận diện</label> |
| <div class="mode-toggle" id="modeToggle"> |
| <button class="mode-btn active" data-mode="music" id="modeMusic"> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-btn"><path d="M18 3a1 1 0 00-1.196-.98l-10 2A1 1 0 006 5v9.114A4.369 4.369 0 005 14c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V7.82l8-1.6v5.894A4.37 4.37 0 0015 12c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V3z"/></svg> |
| Lời bài hát |
| </button> |
| <button class="mode-btn" data-mode="speech" id="modeSpeech"> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-btn"><path fill-rule="evenodd" d="M7 4a3 3 0 016 0v4a3 3 0 11-6 0V4zm4 10.93A7.001 7.001 0 0017 8a1 1 0 10-2 0A5 5 0 015 8a1 1 0 00-2 0 7.001 7.001 0 006 6.93V17H6a1 1 0 100 2h8a1 1 0 100-2h-3v-2.07z" clip-rule="evenodd"/></svg> |
| Giọng nói |
| </button> |
| </div> |
| <p class="mode-hint" id="modeHint">Tối ưu cho Vietsub lời bài hát, nhận diện toàn bộ lyrics khớp timeline.</p> |
| </div> |
|
|
| |
| <button id="btnTranscribe" class="btn btn-primary btn-lg btn-full"> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-btn"><path fill-rule="evenodd" d="M7 4a3 3 0 016 0v4a3 3 0 11-6 0V4zm4 10.93A7.001 7.001 0 0017 8a1 1 0 10-2 0A5 5 0 015 8a1 1 0 00-2 0 7.001 7.001 0 006 6.93V17H6a1 1 0 100 2h8a1 1 0 100-2h-3v-2.07z" clip-rule="evenodd"/></svg> |
| Auto sub tiếng Việt |
| </button> |
|
|
| |
| <div class="coverage-bar" id="coverageBar" hidden> |
| <div class="coverage-header"> |
| <span class="coverage-label">Phủ sóng timeline</span> |
| <span class="coverage-pct" id="coveragePct">0%</span> |
| </div> |
| <div class="coverage-track"> |
| <div class="coverage-fill" id="coverageFill"></div> |
| </div> |
| </div> |
|
|
| |
| <div class="progress-wrap" id="progressWrap" hidden> |
| <div class="progress-bar"> |
| <div class="progress-fill" id="progressFill"></div> |
| </div> |
| <span class="progress-text" id="progressText">Đang xử lý...</span> |
| </div> |
|
|
| |
| <div id="status" class="status-box status-idle"> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="status-icon"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/></svg> |
| <span id="statusText">Sẵn sàng. Hãy upload video để bắt đầu.</span> |
| </div> |
|
|
| <hr class="divider" /> |
|
|
| |
| <div class="karaoke-style-panel"> |
| <h3 class="ks-title"> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-sm"><path d="M18 3a1 1 0 00-1.196-.98l-10 2A1 1 0 006 5v9.114A4.369 4.369 0 005 14c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V7.82l8-1.6v5.894A4.37 4.37 0 0015 12c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V3z"/></svg> |
| Kiểu Karaoke |
| </h3> |
|
|
| |
| <div class="ks-group"> |
| <label class="ks-label" for="ksFont">Font phụ đề</label> |
| <select class="ks-select" id="ksFont"> |
| <option value="Bangers" style="font-family:'Bangers'">Bangers</option> |
| <option value="Bebas Neue" style="font-family:'Bebas Neue'">Bebas Neue</option> |
| <option value="Lobster" style="font-family:'Lobster'">Lobster</option> |
| <option value="Permanent Marker" style="font-family:'Permanent Marker'">Permanent Marker</option> |
| <option value="Pacifico" style="font-family:'Pacifico'">Pacifico</option> |
| <option value="Dancing Script" style="font-family:'Dancing Script'">Dancing Script</option> |
| <option value="Playfair Display" style="font-family:'Playfair Display'">Playfair Display</option> |
| </select> |
| </div> |
|
|
| |
| <div class="ks-group ks-color-row"> |
| <div class="ks-color-item"> |
| <label class="ks-label" for="ksColor">Màu chữ</label> |
| <div class="ks-color-wrap"> |
| <input type="color" id="ksColor" value="#FFFFFF" class="ks-color-input" /> |
| <span class="ks-color-hex" id="ksColorHex">#FFFFFF</span> |
| </div> |
| </div> |
| <div class="ks-color-item"> |
| <label class="ks-label" for="ksHighlight">Màu highlight</label> |
| <div class="ks-color-wrap"> |
| <input type="color" id="ksHighlight" value="#FFD700" class="ks-color-input" /> |
| <span class="ks-color-hex" id="ksHighlightHex">#FFD700</span> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="ks-group ks-color-row"> |
| <div class="ks-color-item"> |
| <label class="ks-label" for="ksOutline">Màu viền</label> |
| <div class="ks-color-wrap"> |
| <input type="color" id="ksOutline" value="#000000" class="ks-color-input" /> |
| <span class="ks-color-hex" id="ksOutlineHex">#000000</span> |
| </div> |
| </div> |
| <div class="ks-color-item"> |
| <label class="ks-label" for="ksOutlineWidth">Độ dày viền</label> |
| <div class="ks-slider-wrap"> |
| <input type="range" id="ksOutlineWidth" min="0" max="6" step="1" value="2" class="ks-range" /> |
| <span class="ks-range-val" id="ksOutlineWidthVal">2px</span> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="ks-group"> |
| <label class="ks-label" for="ksSize">Cỡ chữ</label> |
| <div class="ks-slider-wrap"> |
| <input type="range" id="ksSize" min="50" max="200" step="5" value="100" class="ks-range" /> |
| <span class="ks-range-val" id="ksSizeVal">100%</span> |
| </div> |
| </div> |
|
|
| |
| <div class="ks-group"> |
| <label class="ks-label" for="ksPosition">Vị trí dọc</label> |
| <div class="ks-slider-wrap"> |
| <input type="range" id="ksPosition" min="0" max="100" step="1" value="90" class="ks-range" /> |
| <span class="ks-range-val" id="ksPositionVal">90%</span> |
| </div> |
| <p class="ks-hint">0% = trên cùng, 100% = dưới cùng</p> |
| </div> |
|
|
| |
| <div class="ks-group ks-toggle-row"> |
| <label class="ks-label" for="ksKaraokeMode">Chế độ Karaoke từng từ</label> |
| <label class="ks-switch"> |
| <input type="checkbox" id="ksKaraokeMode" /> |
| <span class="ks-switch-slider"></span> |
| </label> |
| </div> |
| <p class="ks-hint ks-hint-karaoke" id="ksKaraokeHint">Bật để highlight từng từ theo nhạc khi xuất MP4.</p> |
|
|
| |
| <button id="btnPreviewStyle" class="btn btn-outline btn-sm btn-full"> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-btn"><path d="M10 12a2 2 0 100-4 2 2 0 000 4z"/><path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd"/></svg> |
| Xem trước phụ đề |
| </button> |
| </div> |
|
|
| <hr class="divider" /> |
|
|
| |
| <div class="btn-row"> |
| <button id="btnAddRow" class="btn btn-outline" disabled> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-btn"><path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd"/></svg> |
| Thêm dòng |
| </button> |
| </div> |
|
|
| <hr class="divider" /> |
|
|
| |
| <div class="export-group"> |
| <h3 class="export-title">Xuất file</h3> |
| <div class="btn-row"> |
| <button id="btnExportSrt" class="btn btn-outline" disabled> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-btn"><path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd"/></svg> |
| Xuất .SRT |
| </button> |
| <button id="btnExportMp4" class="btn btn-success" disabled> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-btn"><path d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z"/></svg> |
| Xuất .MP4 burn sub |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div class="download-group" id="downloadGroup" hidden> |
| <a id="downloadSrt" class="dl-link dl-srt" href="#" download> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-btn"><path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd"/></svg> |
| Tải .SRT |
| </a> |
| <a id="downloadMp4" class="dl-link dl-mp4" href="#" download> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-btn"><path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd"/></svg> |
| Tải .MP4 |
| </a> |
| </div> |
| </div> |
| </section> |
| </div> |
|
|
| |
| <section class="panel table-panel"> |
| <div class="panel-head"> |
| <h2 class="panel-title"> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-sm"><path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4z" clip-rule="evenodd"/></svg> |
| Bảng Subtitle |
| </h2> |
| <div class="table-meta"> |
| <button class="btn btn-cvnss btn-sm" id="btnToggleCvnss" title="Chuyển đổi Tiếng Việt ↔ CVNSS4.0"> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-btn"><path fill-rule="evenodd" d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" clip-rule="evenodd"/></svg> |
| <span id="cvnssToggleLabel">CVNSS4.0</span> |
| </button> |
| <span class="seg-count" id="segmentCount">0 dòng</span> |
| <button class="btn btn-ghost btn-sm" id="btnCollapseTable" title="Thu gọn"> |
| <svg viewBox="0 0 20 20" fill="currentColor" class="icon-xs"><path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/></svg> |
| </button> |
| </div> |
| </div> |
| <div class="table-scroll" id="tableScroll"> |
| <table> |
| <thead> |
| <tr> |
| <th class="col-idx">#</th> |
| <th class="col-time">Bắt đầu</th> |
| <th class="col-time">Kết thúc</th> |
| <th class="col-text">Nội dung</th> |
| <th class="col-act">Thao tác</th> |
| </tr> |
| </thead> |
| <tbody id="subtitleBody"> |
| <tr class="empty-row"> |
| <td colspan="5"> |
| <div class="empty-state"> |
| <svg viewBox="0 0 48 48" fill="none" class="empty-icon"> |
| <rect x="6" y="10" width="36" height="28" rx="4" stroke="currentColor" stroke-width="1.5"/> |
| <line x1="12" y1="20" x2="36" y2="20" stroke="currentColor" stroke-width="1.5" opacity="0.3"/> |
| <line x1="12" y1="26" x2="30" y2="26" stroke="currentColor" stroke-width="1.5" opacity="0.3"/> |
| <line x1="12" y1="32" x2="24" y2="32" stroke="currentColor" stroke-width="1.5" opacity="0.3"/> |
| </svg> |
| <p>Chưa có subtitle. Upload video rồi bấm <strong>Auto sub tiếng Việt</strong> để bắt đầu.</p> |
| </div> |
| </td> |
| </tr> |
| </tbody> |
| </table> |
| </div> |
| </section> |
|
|
| </main> |
|
|
| |
| <footer class="footer"> |
| <span>Viet AutoSub Editor, 2026 — Ứng dụng này được sự tài trợ bởi CVNSS4.0 và Thầy Trần Tư Bình. Hoàn toàn miễn phí. Ủng hộ và học CVNSS4.0 <a href="https://chuvnsongsong.com/" target="_blank" rel="noopener noreferrer" class="footer-link">tại đây</a></span> |
| </footer> |
|
|
| <script src="static/app.js"></script> |
| </body> |
| </html> |
|
|