Import Google Meet captions project and extension backups
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
class CaptionCapture {
|
||||
constructor() {
|
||||
this.isCapturing = false; // Flag to control capturing
|
||||
this.intervalId = null; // Store interval reference
|
||||
this.lastSpans = []; // Store last seen spans
|
||||
this.captionsLog = []; // Stores captions with timestamps
|
||||
this.mergedCpation = [];
|
||||
this.subtitleText= null;
|
||||
this.checkSubtitleInterval=100;
|
||||
}
|
||||
|
||||
// Function to detect and get the caption spans as an array
|
||||
detectCaptionAsArray() {
|
||||
let spans = document.querySelectorAll('div[jsname="tgaKEf"]');
|
||||
return Array.from(spans).map(span => span.innerText.trim()) || "";
|
||||
}
|
||||
|
||||
// Function to get absolute time in HH:MM:SS,mmm format (Real-Time)
|
||||
getCurrentTimestamp() {
|
||||
let now = new Date();
|
||||
let hours = String(now.getHours()).padStart(2, '0');
|
||||
let minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
let seconds = String(now.getSeconds()).padStart(2, '0');
|
||||
let millis = String(now.getMilliseconds()).padStart(3, '0');
|
||||
|
||||
return `${hours}:${minutes}:${seconds},${millis}`; // Absolute time format
|
||||
}
|
||||
|
||||
// Function to save new words with timestamp
|
||||
saveCaptionByWords(timestamp, newText) {
|
||||
this.captionsLog.push({ time: timestamp, text: newText });
|
||||
console.log(`${timestamp} --> ${newText}`); // Print in subtitle format
|
||||
}
|
||||
|
||||
formatTimestamp(ms) {
|
||||
const h = String(Math.floor(ms / 3600000)).padStart(2, "0");
|
||||
ms %= 3600000;
|
||||
const m = String(Math.floor(ms / 60000)).padStart(2, "0");
|
||||
ms %= 60000;
|
||||
const s = String(Math.floor(ms / 1000)).padStart(2, "0");
|
||||
const msStr = String(ms % 1000).padStart(3, "0");
|
||||
return `${h}:${m}:${s},${msStr}`;
|
||||
}
|
||||
|
||||
mergeCaptions(captions, gap = 1000) {
|
||||
if (!captions || captions.length === 0) return [];
|
||||
|
||||
const parseTime = (timestamp) => {
|
||||
const [h, m, sMs] = timestamp.split(":");
|
||||
const [s, ms] = sMs.split(",");
|
||||
return (
|
||||
parseInt(h) * 3600000 +
|
||||
parseInt(m) * 60000 +
|
||||
parseInt(s) * 1000 +
|
||||
parseInt(ms)
|
||||
);
|
||||
};
|
||||
|
||||
const merged = [];
|
||||
let current = {
|
||||
start: parseTime(captions[0].time),
|
||||
end: parseTime(captions[0].time),
|
||||
texts: [captions[0].text]
|
||||
};
|
||||
|
||||
for (let i = 1; i < captions.length; i++) {
|
||||
const entry = captions[i];
|
||||
const time = parseTime(entry.time);
|
||||
const timeDiff = time - current.end;
|
||||
|
||||
if (timeDiff > gap) {
|
||||
merged.push({
|
||||
start: this.formatTimestamp(current.start),
|
||||
end: this.formatTimestamp(current.end),
|
||||
text: current.texts.join(" ")
|
||||
});
|
||||
|
||||
current = {
|
||||
start: time,
|
||||
end: time,
|
||||
texts: [entry.text]
|
||||
};
|
||||
} else {
|
||||
current.end = time;
|
||||
current.texts.push(entry.text);
|
||||
}
|
||||
}
|
||||
|
||||
// Push the last group
|
||||
merged.push({
|
||||
start: this.formatTimestamp(current.start),
|
||||
end: this.formatTimestamp(current.end),
|
||||
text: current.texts.join(" ")
|
||||
});
|
||||
|
||||
this.mergedCpation=merged;
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
|
||||
printMergedSubtitle() {
|
||||
const merged = this.mergeCaptions(this.getCapturedCaptions(), 1000);
|
||||
if (merged.length === 0) {
|
||||
console.log("No subtitles to print.");
|
||||
return;
|
||||
}
|
||||
|
||||
const parseTime = (timestamp) => {
|
||||
const [h, m, sMs] = timestamp.split(":");
|
||||
const [s, ms] = sMs.split(",");
|
||||
return (
|
||||
parseInt(h) * 3600000 +
|
||||
parseInt(m) * 60000 +
|
||||
parseInt(s) * 1000 +
|
||||
parseInt(ms)
|
||||
);
|
||||
};
|
||||
|
||||
const baseTime = parseTime(merged[0].start); // time of first word
|
||||
|
||||
const toRelative = (absTime) => {
|
||||
const ms = parseTime(absTime) - baseTime;
|
||||
return this.formatTimestamp(ms);
|
||||
};
|
||||
|
||||
this.subtitleText = merged.map((item, index) =>
|
||||
`${index + 1}\n${toRelative(item.start)} --> ${toRelative(item.end)}\n${item.text}\n`
|
||||
).join("\n");
|
||||
|
||||
console.log(this.subtitleText);
|
||||
|
||||
return this.subtitleText;
|
||||
}
|
||||
|
||||
generateSrtFilename() {
|
||||
const now = new Date();
|
||||
const pad = (n) => String(n).padStart(2, '0');
|
||||
return `caption_${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}.srt`;
|
||||
}
|
||||
|
||||
downloadSrtFile() {
|
||||
if (!this.subtitleText) {
|
||||
console.warn("No subtitle content found. Please call printMergedSubtitle() first.");
|
||||
return;
|
||||
}
|
||||
|
||||
const filename = this.generateSrtFilename();
|
||||
const blob = new Blob([this.subtitleText], { type: "text/plain" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.click();
|
||||
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Function to start capturing captions
|
||||
startCapturing() {
|
||||
if (this.isCapturing) {
|
||||
console.log("Caption capturing is already running.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.isCapturing = true;
|
||||
console.log("Caption capturing started.");
|
||||
|
||||
this.intervalId = setInterval(() => {
|
||||
let currentSpans = this.detectCaptionAsArray();
|
||||
|
||||
let lastOldSpan = this.lastSpans[this.lastSpans.length - 1] || "";
|
||||
let lastNewSpan = currentSpans[currentSpans.length - 1] || "";
|
||||
|
||||
let isLastCaptionChanged = lastNewSpan !== lastOldSpan;
|
||||
|
||||
if (isLastCaptionChanged) {
|
||||
this.lastSpans = currentSpans; // Update stored spans
|
||||
let timestamp = this.getCurrentTimestamp();
|
||||
|
||||
// Get only the new part of the last span
|
||||
let newText = lastNewSpan.replace(lastOldSpan, "").trim();
|
||||
|
||||
if (newText) {
|
||||
console.log(newText)
|
||||
const wordCount = newText.trim().split(/\s+/).length;
|
||||
const BurstTextCount=80;
|
||||
if (wordCount <= (this.checkSubtitleInterval/100)*BurstTextCount) {
|
||||
this.saveCaptionByWords(timestamp, newText);
|
||||
} else {
|
||||
console.log(`Skipped burst text with ${wordCount} words at ${timestamp}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, this.checkSubtitleInterval); // Runs every 100ms for better precision
|
||||
}
|
||||
|
||||
// Function to stop capturing captions
|
||||
stopCapturing() {
|
||||
if (!this.isCapturing) {
|
||||
console.log("Caption capturing is not running.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.isCapturing = false;
|
||||
clearInterval(this.intervalId);
|
||||
console.log("Caption capturing stopped.");
|
||||
}
|
||||
|
||||
// Function to get the saved captions log
|
||||
getCapturedCaptions() {
|
||||
return this.captionsLog;
|
||||
}
|
||||
}
|
||||
|
||||
// Usage:
|
||||
const captionCapture = new CaptionCapture();
|
||||
|
||||
// Start capturing:
|
||||
captionCapture.startCapturing();
|
||||
|
||||
// Stop capturing:
|
||||
captionCapture.stopCapturing();
|
||||
|
||||
// Retrieve stored captions:
|
||||
console.log(captionCapture.getCapturedCaptions());
|
||||
|
||||
captionCapture.printMergedSubtitle();
|
||||
|
||||
captionCapture.downloadSrtFile();
|
||||
Reference in New Issue
Block a user