Collects transcript entries and replaces download button
// ==UserScript==
// @name Transcript Collector
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Collects transcript entries and replaces download button
// @author You
// @license MIT
// @match https://audiototext.com/*
// @grant GM_download
// @grant GM_setClipboard
// ==/UserScript==
(function() {
'use strict';
// Function to extract transcript data
function extractTranscriptData() {
const transcriptItems = document.querySelectorAll('.group.relative.rounded-lg.sm\\:rounded-xl.shadow-sm.border');
const formattedData = [];
const plainText = [];
transcriptItems.forEach(item => {
// Extract name
const nameElement = item.querySelector('.inline-flex.items-center.rounded-full');
const name = nameElement ? nameElement.textContent.trim() : 'Unknown';
// Extract timestamp
const timestampElement = item.querySelector('.text-xs.font-mono');
const timestamp = timestampElement ? timestampElement.textContent.trim() : '';
// Extract text
const textElement = item.querySelector('p');
const text = textElement ? textElement.textContent.trim() : '';
// Add formatted line
if (name && timestamp && text) {
formattedData.push(`[${name}] [${timestamp}] ${text}`);
plainText.push(text);
}
});
return {
formatted: formattedData.join('\n'),
plainText: plainText.join('\n'),
raw: formattedData
};
}
// Function to create and trigger download
function downloadTranscript(format = 'formatted') {
const data = extractTranscriptData();
let content = '';
let filename = '';
if (format === 'formatted') {
content = data.formatted;
filename = 'transcript_formatted.txt';
} else if (format === 'plain') {
content = data.plainText;
filename = 'transcript_plain.txt';
} else if (format === 'json') {
content = JSON.stringify(data.raw, null, 2);
filename = 'transcript.json';
}
// Create download
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// Function to create dropdown menu
function createDownloadMenu() {
const menu = document.createElement('div');
menu.style.position = 'absolute';
menu.style.top = '100%';
menu.style.right = '0';
menu.style.marginTop = '0.5rem';
menu.style.backgroundColor = 'white';
menu.style.border = '1px solid #e2e8f0';
menu.style.borderRadius = '0.5rem';
menu.style.boxShadow = '0 10px 15px -3px rgba(0, 0, 0, 0.1)';
menu.style.zIndex = '50';
menu.style.minWidth = '150px';
menu.style.display = 'none';
const options = [
{ label: 'Formatted (Name + Timestamp + Text)', format: 'formatted' },
{ label: 'Plain Text Only', format: 'plain' },
{ label: 'JSON Format', format: 'json' },
{ label: 'Copy to Clipboard (Formatted)', format: 'copy_formatted' },
{ label: 'Copy to Clipboard (Plain)', format: 'copy_plain' }
];
options.forEach(opt => {
const option = document.createElement('div');
option.textContent = opt.label;
option.style.padding = '0.5rem 1rem';
option.style.cursor = 'pointer';
option.style.fontSize = '0.875rem';
option.style.color = '#374151';
option.style.transition = 'all 0.2s';
option.addEventListener('mouseenter', () => {
option.style.backgroundColor = '#f3f4f6';
});
option.addEventListener('mouseleave', () => {
option.style.backgroundColor = 'white';
});
option.addEventListener('click', () => {
if (opt.format === 'copy_formatted') {
const data = extractTranscriptData();
GM_setClipboard(data.formatted);
showNotification('Copied formatted transcript to clipboard!');
} else if (opt.format === 'copy_plain') {
const data = extractTranscriptData();
GM_setClipboard(data.plainText);
showNotification('Copied plain text to clipboard!');
} else {
downloadTranscript(opt.format);
showNotification(`Downloading ${opt.format}...`);
}
menu.style.display = 'none';
});
menu.appendChild(option);
});
return menu;
}
// Function to show notification
function showNotification(message) {
const notification = document.createElement('div');
notification.textContent = message;
notification.style.position = 'fixed';
notification.style.bottom = '20px';
notification.style.right = '20px';
notification.style.backgroundColor = '#10b981';
notification.style.color = 'white';
notification.style.padding = '12px 20px';
notification.style.borderRadius = '8px';
notification.style.fontSize = '14px';
notification.style.zIndex = '10000';
notification.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
notification.style.animation = 'fadeInOut 2s ease-in-out';
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 2000);
}
// Add animation styles
const style = document.createElement('style');
style.textContent = `
@keyframes fadeInOut {
0% { opacity: 0; transform: translateY(10px); }
15% { opacity: 1; transform: translateY(0); }
85% { opacity: 1; transform: translateY(0); }
100% { opacity: 0; transform: translateY(10px); }
}
`;
document.head.appendChild(style);
// Function to replace download button
function replaceDownloadButton() {
const downloadButton = document.querySelector('[title="Download"]');
if (!downloadButton) return false;
const buttonContainer = downloadButton.closest('div[title="Download"]');
if (!buttonContainer) return false;
// Store original button HTML if needed
const originalHTML = buttonContainer.outerHTML;
// Create new button with dropdown
const wrapper = document.createElement('div');
wrapper.style.position = 'relative';
wrapper.style.display = 'inline-flex';
const newButton = buttonContainer.cloneNode(true);
const svg = newButton.querySelector('svg');
const span = newButton.querySelector('span');
if (svg) svg.outerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-file-down w-3.5 h-3.5 text-gray-600 group-hover:text-primary transition-colors"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M12 18v-6"/><path d="M9 15l3-3 3 3"/></svg>`;
if (span) span.textContent = 'Export';
newButton.classList.add('group');
newButton.style.cursor = 'pointer';
const menu = createDownloadMenu();
wrapper.appendChild(newButton);
wrapper.appendChild(menu);
// Toggle menu on click
newButton.addEventListener('click', (e) => {
e.stopPropagation();
const isVisible = menu.style.display === 'block';
// Close all other menus
document.querySelectorAll('.transcript-menu').forEach(m => {
if (m !== menu) m.style.display = 'none';
});
menu.style.display = isVisible ? 'none' : 'block';
});
// Close menu when clicking outside
document.addEventListener('click', function closeMenu(e) {
if (!wrapper.contains(e.target)) {
menu.style.display = 'none';
}
});
// Replace the original button
buttonContainer.parentNode.replaceChild(wrapper, buttonContainer);
wrapper.className = 'transcript-menu-container';
return true;
}
// Function to add a floating button for quick access
function addFloatingButton() {
const floatingBtn = document.createElement('button');
floatingBtn.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
<span>Export Transcript</span>
`;
floatingBtn.style.position = 'fixed';
floatingBtn.style.bottom = '20px';
floatingBtn.style.right = '20px';
floatingBtn.style.backgroundColor = '#3b82f6';
floatingBtn.style.color = 'white';
floatingBtn.style.border = 'none';
floatingBtn.style.borderRadius = '8px';
floatingBtn.style.padding = '10px 16px';
floatingBtn.style.cursor = 'pointer';
floatingBtn.style.zIndex = '9999';
floatingBtn.style.display = 'flex';
floatingBtn.style.alignItems = 'center';
floatingBtn.style.gap = '8px';
floatingBtn.style.fontSize = '14px';
floatingBtn.style.fontWeight = '500';
floatingBtn.style.boxShadow = '0 2px 8px rgba(0,0,0,0.15)';
floatingBtn.style.transition = 'all 0.2s';
floatingBtn.addEventListener('mouseenter', () => {
floatingBtn.style.backgroundColor = '#2563eb';
floatingBtn.style.transform = 'scale(1.05)';
});
floatingBtn.addEventListener('mouseleave', () => {
floatingBtn.style.backgroundColor = '#3b82f6';
floatingBtn.style.transform = 'scale(1)';
});
let menuVisible = false;
let floatingMenu = null;
floatingBtn.addEventListener('click', () => {
if (!floatingMenu) {
floatingMenu = createDownloadMenu();
floatingMenu.style.position = 'fixed';
floatingMenu.style.bottom = '70px';
floatingMenu.style.right = '20px';
floatingMenu.style.display = 'block';
document.body.appendChild(floatingMenu);
// Remove menu when clicking outside
const removeMenu = (e) => {
if (!floatingBtn.contains(e.target) && !floatingMenu.contains(e.target)) {
floatingMenu.remove();
floatingMenu = null;
document.removeEventListener('click', removeMenu);
}
};
setTimeout(() => document.addEventListener('click', removeMenu), 100);
} else {
floatingMenu.remove();
floatingMenu = null;
}
});
document.body.appendChild(floatingBtn);
}
// Wait for elements to load and replace button
function init() {
const checkInterval = setInterval(() => {
if (replaceDownloadButton()) {
clearInterval(checkInterval);
addFloatingButton();
console.log('Transcript collector loaded successfully');
}
}, 1000);
// Stop checking after 30 seconds
setTimeout(() => clearInterval(checkInterval), 30000);
}
// Start the script
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();