Skip to content

Instantly share code, notes, and snippets.

@meta-ks
Created May 12, 2025 05:11
Show Gist options
  • Save meta-ks/2d3270f73080e664e002a770a824a9df to your computer and use it in GitHub Desktop.
Save meta-ks/2d3270f73080e664e002a770a824a9df to your computer and use it in GitHub Desktop.
Extract teams meeting transcript from browser console. Works as on May, 2025
// Function to extract transcript
function extractTranscript() {
// Get all list cells that contain transcript entries
const listCells = document.querySelectorAll('.ms-List-cell');
let transcript = '';
// Iterate through each list cell
listCells.forEach((cell, index) => {
// Get speaker name if it exists
const speakerEl = cell.querySelector('[class*="itemDisplayName-"]');
const speaker = speakerEl ? speakerEl.textContent.trim() : '';
// Get timestamp if it exists
const timestampEl = cell.querySelector('[id^="Header-timestamp-"]');
const timestamp = timestampEl ? timestampEl.textContent.trim() : '';
// Get the text content
const textEl = cell.querySelector('[class*="entryText-"], [class*="eventText-"]');
const text = textEl ? textEl.textContent.trim() : '';
// Only add entries that have text content
if (text) {
// Format and add to transcript
if (speaker && timestamp) {
transcript += `[${timestamp}] ${speaker}: ${text}\n\n`;
} else if (speaker) {
transcript += `${speaker}: ${text}\n\n`;
} else {
transcript += `${text}\n\n`;
}
}
});
return transcript;
}// Function to set up a mutation observer to capture transcript entries
function setupTranscriptCapture() {
// Create a set to store unique transcript entries
const capturedEntries = new Set();
// Track transcript items by a unique identifier
const entryMap = new Map();
// Create a visual indicator to show capture is active
const indicator = document.createElement('div');
indicator.style.cssText = 'position:fixed;top:10px;right:10px;background:green;color:white;padding:10px;border-radius:5px;z-index:9999;';
indicator.textContent = 'Transcript capture active: 0 entries';
document.body.appendChild(indicator);
// Find the scrollable container with transcript items - using a more reliable selector
const scrollContainer = document.querySelector('#OneTranscript [data-is-scrollable="true"]');
if (!scrollContainer) {
console.error('Could not find transcript container');
return null;
}
// Create a function to extract and process entries
function processEntry(entry) {
const speakerEl = entry.querySelector('[class*="itemDisplayName-"]');
const speaker = speakerEl ? speakerEl.textContent.trim() : '';
const timestampEl = entry.querySelector('[id^="Header-timestamp-"]');
const timestamp = timestampEl ? timestampEl.textContent.trim() : '';
const textEl = entry.querySelector('[class*="entryText-"], [class*="eventText-"]');
const text = textEl ? textEl.textContent.trim() : '';
if (text) {
// Create a unique key for this entry
const key = `${timestamp}|${speaker}|${text}`;
// Store formatted entry text
let formattedEntry = '';
if (speaker && timestamp) {
formattedEntry = `[${timestamp}] ${speaker}: ${text}`;
} else if (speaker) {
formattedEntry = `${speaker}: ${text}`;
} else {
formattedEntry = text;
}
// Only add if we haven't seen this entry before
if (!entryMap.has(key)) {
entryMap.set(key, formattedEntry);
capturedEntries.add(formattedEntry);
// Update indicator
indicator.textContent = `Transcript capture active: ${capturedEntries.size} entries`;
indicator.style.background = 'green';
}
}
}
// Initial processing of visible entries
document.querySelectorAll('.ms-List-cell').forEach(cell => {
processEntry(cell);
});
// Set up mutation observer to catch new entries as they're loaded
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
// Look for added nodes that might be transcript entries
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
// If this is a list cell, process it
if (node.classList && node.classList.contains('ms-List-cell')) {
processEntry(node);
} else {
// Check for list cells inside this node
const cells = node.querySelectorAll('.ms-List-cell');
cells.forEach(cell => processEntry(cell));
}
}
});
});
// Briefly flash the indicator to show activity
indicator.style.background = 'blue';
setTimeout(() => {
indicator.style.background = 'green';
}, 200);
});
// Start observing
observer.observe(scrollContainer, {
childList: true,
subtree: true
});
// Add a button to get the collected transcript
const extractButton = document.createElement('button');
extractButton.textContent = 'Extract Complete Transcript';
extractButton.style.cssText = 'position:fixed;top:50px;right:10px;padding:10px;z-index:9999;background:#0078d4;color:white;border:none;border-radius:5px;cursor:pointer;';
extractButton.onclick = () => {
// Sort entries by timestamp if possible
const transcript = Array.from(capturedEntries).join('\n\n');
// Copy to clipboard
const textarea = document.createElement('textarea');
textarea.value = transcript;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
console.log('Full transcript extracted:');
console.log(transcript);
console.log('Transcript copied to clipboard!');
indicator.textContent = `Extracted ${capturedEntries.size} entries!`;
indicator.style.background = '#4CAF50';
setTimeout(() => {
indicator.style.background = 'green';
indicator.textContent = `Transcript capture active: ${capturedEntries.size} entries`;
}, 3000);
};
document.body.appendChild(extractButton);
// Return the cleanup function
return function cleanup() {
observer.disconnect();
document.body.removeChild(indicator);
document.body.removeChild(extractButton);
return Array.from(capturedEntries).join('\n\n');
};
}
// Function to ensure the transcript panel is open
function ensureTranscriptPanelOpen() {
return new Promise((resolve) => {
// Check if transcript panel is already visible
const transcriptPanel = document.querySelector('#OneTranscript');
if (transcriptPanel && window.getComputedStyle(transcriptPanel).display !== 'none') {
console.log('Transcript panel is already open');
return resolve(true);
}
// Try to find and click the Transcript button
// Look for button with "Transcript" text
const buttons = Array.from(document.querySelectorAll('span[class*="ms-Button-label"]'));
const transcriptButton = buttons.find(el => el.textContent.trim() === 'Transcript');
if (transcriptButton) {
console.log('Found Transcript button, clicking it...');
// Click the button (need to find the closest clickable parent)
const clickableParent = transcriptButton.closest('[data-is-focusable="true"]') ||
transcriptButton.closest('button') ||
transcriptButton.closest('[role="button"]');
if (clickableParent) {
clickableParent.click();
console.log('Clicked Transcript button');
// Wait for the transcript container to appear
const checkForContainer = () => {
const container = document.querySelector('#OneTranscript [data-is-scrollable="true"]');
if (container) {
console.log('Transcript container found');
resolve(true);
} else {
console.log('Waiting for transcript container to load...');
setTimeout(checkForContainer, 500); // Check again after 500ms
}
};
// Start checking
setTimeout(checkForContainer, 1000); // Give it 1 second before first check
return;
}
}
console.warn('Could not find or click Transcript button. Please open the transcript panel manually.');
resolve(false);
});
}
// Start everything in the correct sequence
(async function() {
// First, make sure the transcript panel is open
await ensureTranscriptPanelOpen();
// Then, start the transcript capture
const stopCapture = setupTranscriptCapture();
if (stopCapture) {
console.log('Scroll through the ENTIRE transcript to capture all entries.');
console.log('When finished, click the "Extract Complete Transcript" button or run stopCapture() to get the result.');
} else {
console.log('Failed to set up transcript capture. Please check if the transcript panel is open and try again.');
}
// Extract currently visible transcript
const transcript = extractTranscript();
if (transcript) {
console.log(transcript); // Display in console
// Create a temporary textarea to copy to clipboard
const textarea = document.createElement('textarea');
textarea.value = transcript;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
console.log('Transcript has been copied to clipboard!');
}
})();
@meta-ks
Copy link
Author

meta-ks commented May 12, 2025

Run the script in browser console. it will open the trancscript pane. Scroll manually [fell free to update and share]
It will remove dupplicates and save the whole transcript. Then click copy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment