var elem_container = null; var elem_text = null; var segments = []; var text_segments = []; var captionLineCount = 3; var allSegments = []; var lastIncompleteSegment = null; function formatTime(seconds) { const date = new Date(seconds * 1000); const hh = String(date.getUTCHours()).padStart(2, '0'); const mm = String(date.getUTCMinutes()).padStart(2, '0'); const ss = String(date.getUTCSeconds()).padStart(2, '0'); const mmm = String(date.getUTCMilliseconds()).padStart(3, '0'); return `${hh}:${mm}:${ss},${mmm}`; } function generateSRT() { return allSegments .map((seg, i) => { const start = formatTime(seg.start); const end = formatTime(seg.end); const text = seg.text.trim().replace(/[\r\n]+/g, ' '); return `${i + 1}\n${start} --> ${end}\n${text}`; }) .join('\n\n'); } function downloadSRT() { console.log("downloadSRT called"); console.log("Total segments for SRT:", allSegments.length); const srtBlob = new Blob([generateSRT()], { type: 'text/srt;charset=utf-8' }); const url = URL.createObjectURL(srtBlob); const a = document.createElement('a'); a.href = url; a.download = 'captions.srt'; a.style.display = 'none'; document.body.appendChild(a); a.click(); URL.revokeObjectURL(url); document.body.removeChild(a); } function initPopupElement() { if (document.getElementById('popupElement')) { return; } const popupContainer = document.createElement('div'); popupContainer.id = 'popupElement'; popupContainer.style.cssText = 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; color: black; padding: 16px; border-radius: 10px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5); display: none; text-align: center;'; const popupText = document.createElement('span'); popupText.textContent = 'Default Text'; popupText.className = 'popupText'; popupText.style.fontSize = '24px'; popupContainer.appendChild(popupText); const buttonContainer = document.createElement('div'); buttonContainer.style.marginTop = '8px'; const closePopupButton = document.createElement('button'); closePopupButton.textContent = 'Close'; closePopupButton.style.backgroundColor = '#65428A'; closePopupButton.style.color = 'white'; closePopupButton.style.border = 'none'; closePopupButton.style.padding = '8px 16px'; // Add padding for better click area closePopupButton.style.cursor = 'pointer'; closePopupButton.addEventListener('click', async () => { popupContainer.style.display = 'none'; await chrome.runtime.sendMessage({ action: 'toggleCaptureButtons', data: false }); }); buttonContainer.appendChild(closePopupButton); popupContainer.appendChild(buttonContainer); document.body.appendChild(popupContainer); } function showPopup(customText) { const popup = document.getElementById('popupElement'); const popupText = popup.querySelector('.popupText'); if (popup && popupText) { popupText.textContent = customText || 'Default Text'; // Set default text if custom text is not provided popup.style.display = 'block'; } } function init_element(lines = 3) { captionLineCount = Math.min(Math.max(parseInt(lines, 10) || 3, 1), 8); if (document.getElementById('transcription')) { return; } elem_container = document.createElement('div'); elem_container.id = "transcription"; elem_container.style.cssText = 'padding:0 24px;font-family:Arial,Helvetica,sans-serif;font-size:22px;font-weight:600;line-height:30px;position:fixed;top:85%;left:50%;transform:translate(-50%,-50%);width:min(80vw,900px);min-height:' + (captionLineCount * 30) + 'px;z-index:2147483647;color:white;text-align:center;letter-spacing:0.01em;text-shadow:0 0 2px #000,0 2px 4px rgba(0,0,0,0.95);cursor:move;'; for (var i = 0; i <= captionLineCount; i++) { elem_text = document.createElement('span'); elem_text.style.cssText = 'position:absolute;left:50%;transform:translateX(-50%);max-width:100%;padding:2px 14px;background:rgba(0,0,0,0.72);border-radius:6px;box-decoration-break:clone;-webkit-box-decoration-break:clone;'; elem_text.id = "t" + i; elem_container.appendChild(elem_text); if (i == captionLineCount) { elem_text.style.top = "-1000px" } } document.body.appendChild(elem_container); let x = 0; let y = 0; // Query the element const ele = elem_container; // Handle the mousedown event // that's triggered when user drags the element const mouseDownHandler = function (e) { // Get the current mouse position x = e.clientX; y = e.clientY; // Attach the listeners to `document` document.addEventListener('mousemove', mouseMoveHandler); document.addEventListener('mouseup', mouseUpHandler); }; const mouseMoveHandler = function (e) { // How far the mouse has been moved const dx = e.clientX - x; const dy = e.clientY - y; // Set the position of element ele.style.top = `${ele.offsetTop + dy}px`; ele.style.left = `${ele.offsetLeft + dx}px`; // Reassign the position of mouse x = e.clientX; y = e.clientY; }; const mouseUpHandler = function () { // Remove the handlers of `mousemove` and `mouseup` document.removeEventListener('mousemove', mouseMoveHandler); document.removeEventListener('mouseup', mouseUpHandler); }; ele.addEventListener('mousedown', mouseDownHandler); } function getStyle(el,styleProp) { var x = document.getElementById(el); if (x.currentStyle) var y = x.currentStyle[styleProp]; else if (window.getComputedStyle) var y = document.defaultView.getComputedStyle(x,null).getPropertyValue(styleProp); return y; } function get_lines(elem, line_height) { var divHeight = elem.offsetHeight; var lines = divHeight / line_height; var original_text = elem.textContent; var words = original_text.split(' '); var segments = []; var current_lines = 1; var segment = ''; var segment_len = 0; for (var i = 0; i < words.length; i++) { segment += words[i] + ' '; elem.textContent = segment; divHeight = elem.offsetHeight; if ((divHeight / line_height) > current_lines) { var line_segment = segment.substring(segment_len, segment.length - 1 - words[i].length - 1); segments.push(line_segment); segment_len += line_segment.length + 1; current_lines++; } } var line_segment = segment.substring(segment_len, segment.length - 1) segments.push(line_segment); elem.textContent = original_text; return segments; } function remove_element() { var elem = document.getElementById('transcription') for (var i = 0; i <= captionLineCount; i++) { document.getElementById("t" + i).remove(); } elem.remove() } chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { const { type, data } = request; const saveCaptions = data.saveCaptions; const captionLines = data.captionLines || captionLineCount; if (type === "STOP") { if (saveCaptions === true) { // If there is a last incomplete segment, push it to allSegments if (lastIncompleteSegment && lastIncompleteSegment.text && lastIncompleteSegment.text.trim() !== "") { // Apply same Python logic: check if transcript is empty OR start >= last end if (allSegments.length === 0 || parseFloat(lastIncompleteSegment.start) >= parseFloat(allSegments[allSegments.length - 1].end)) { allSegments.push({ start: lastIncompleteSegment.start, end: lastIncompleteSegment.end, text: lastIncompleteSegment.text }); console.log("Added final incomplete segment"); } } downloadSRT(); } remove_element(); sendResponse({data: "STOPPED"}); return true; } else if (type === "showWaitPopup"){ initPopupElement(); showPopup(`Estimated wait time ~ ${Math.round(data)} minutes`); sendResponse({data: "popup"}); return true; } init_element(captionLines); try { const message = JSON.parse(data.data); const segments = message["segments"]; if (saveCaptions === true) { segments.forEach(seg => { if (seg.completed === true && (allSegments.length === 0 || parseFloat(seg.start) >= parseFloat(allSegments[allSegments.length - 1].end))) { allSegments.push({ start: seg.start, end: seg.end, text: seg.text }); lastIncompleteSegment = null; } else if (seg.completed !== true) { lastIncompleteSegment = seg; } }); } var text = ''; for (var i = 0; i < segments.length; i++) { text += segments[i].text + ' '; } text = text.replace(/(\r\n|\n|\r)/gm, ""); var elem = document.getElementById('t' + captionLineCount); if (elem) { elem.textContent = text; var line_height_style = getStyle('t' + captionLineCount, 'line-height'); var line_height = parseInt(line_height_style.substring(0, line_height_style.length - 2)); var divHeight = elem.offsetHeight; var lines = divHeight / line_height; text_segments = []; text_segments = get_lines(elem, line_height); elem.textContent = ''; if (text_segments.length > captionLineCount - 1) { for (var i = 0; i < captionLineCount; i++) { document.getElementById('t' + i).textContent = text_segments[text_segments.length - captionLineCount + i]; } } else { for (var i = 0; i < captionLineCount; i++) { document.getElementById('t' + i).textContent = ''; } } if (text_segments.length <= captionLineCount - 1) { for (var i = 0; i < text_segments.length; i++) { document.getElementById('t' + i).textContent = text_segments[i]; } } else { for (var i = 0; i < captionLineCount; i++) { document.getElementById('t' + i).textContent = text_segments[text_segments.length - captionLineCount + i]; } } for (var i = 1; i < captionLineCount; i++) { var parent_elem = document.getElementById('t' + (i - 1)); var elem = document.getElementById('t' + i); if (parent_elem && elem) { elem.style.top = parent_elem.offsetHeight + parent_elem.offsetTop + 'px'; } } } } catch (error) { console.error("Error processing message:", error); } sendResponse({}); return true; });