吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 943|回复: 30
上一主题 下一主题
收起左侧

[其他原创] 油猴脚本,一键导出腾讯会议转写内容

[复制链接]
跳转到指定楼层
楼主
lucool 发表于 2026-5-12 09:08 回帖奖励
本帖最后由 lucool 于 2026-5-12 19:40 编辑

想把腾讯视频会议转写的文字稿导出,在网上找到一个油猴脚本,无法使用,然后放到deepseek里面让其修改,经过几次反复,修改成功。下面是代码,复制到油猴里面就可以用了。代码里面有修改前代码的github地址。

[JavaScript] 纯文本查看 复制代码
// ==UserScript==
// @name         腾讯会议转写纪要导出神器 (Tencent Meeting Transcript Exporter) 修复版
// @namespace    https://github.com/awesome-tampermonkey
// @version      1.1.2
// @description  一键导出腾讯会议录制视频和纯文字转写的内容和纪要,支持Markdown、HTML、TXT格式导出和复制
// @author       东哥说AI
// @match        https://meeting.tencent.com/cw/*
// @match        https://meeting.tencent.com/ct/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const CONFIG = {
        BUTTON_STYLE: `
            position: fixed;
            top: 20px;
            right: 20px;
            z-index: 10000;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            border-radius: 8px;
            padding: 12px 20px;
            font-size: 14px;
            font-weight: 600;
            cursor: pointer;
            box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
            transition: all 0.3s ease;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        `,
        MODAL_STYLE: `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.7);
            z-index: 10001;
            display: flex;
            justify-content: center;
            align-items: center;
        `,
        MODAL_CONTENT_STYLE: `
            background: white;
            border-radius: 12px;
            padding: 30px;
            max-width: 500px;
            width: 90%;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
        `
    };

    const utils = {
        getPageType() {
            const url = window.location.href;
            if (url.includes('/cw/')) return 'recording';
            if (url.includes('/ct/')) return 'transcript';
            return 'unknown';
        },

        getMeetingTitle() {
            // 尝试多种方式获取会议标题
            const selectors = [
                '.meeting-main-subject .subject',
                '.meeting-subject',
                '.meeting-title',
                '.style_header-info__6mQkP', // 新增选择器
                'h1', // 通用标题
            ];
            
            for (const sel of selectors) {
                const el = document.querySelector(sel);
                if (el) {
                    const text = el.textContent.trim();
                    // 如果只获取到部分内容(比如"返回"等),继续查找
                    if (text && text.length > 3 && !text.startsWith('返回')) {
                        return text;
                    }
                }
            }
            
            // 如果以上都失败,从URL或其他地方获取
            const pageTitle = document.title;
            if (pageTitle && pageTitle !== '腾讯会议') {
                return pageTitle;
            }
            
            return '腾讯会议转写';
        },
        
        getRecordingTime() {
            // 尝试从页面中提取录制时间
            const selectors = [
                '.meeting-begin-time-in-date',
                '.meeting-time',
                '[class*="time"]',
            ];
            
            for (const sel of selectors) {
                const el = document.querySelector(sel);
                if (el) {
                    const text = el.textContent.trim();
                    // 匹配时间格式
                    if (/\d{4}[\/\-]\d{2}[\/\-]\d{2}/.test(text)) {
                        return text.match(/\d{4}[\/\-]\d{2}[\/\-]\d{2}[\s\d\:]*/)[0];
                    }
                }
            }
            
            return new Date().toLocaleString('zh-CN');
        },
        
        getMeetingKeywords() {
            const keywords = [];
            const topicElements = document.querySelectorAll('.topicTag .topicText');
            topicElements.forEach(element => {
                const keyword = element.textContent.trim();
                if (keyword) {
                    keywords.push(keyword);
                }
            });
            return keywords;
        },

        getCurrentTabType() {
            const pageType = this.getPageType();
            if (pageType === 'transcript') return 'both';

            const activeTab = document.querySelector('.met-tabs__tabitem.is-active .tab');
            if (!activeTab) return 'transcript';

            const tabText = activeTab.textContent.trim();
            if (tabText.includes('纪要')) return 'summary';
            return 'transcript';
        },

        // 修复后的转写内容获取方法
        async getTranscriptContent() {
            const pageType = this.getPageType();
            
            // 查找虚拟滚动容器
            const scrollContainer = document.querySelector('.minutes-module-list');
            
            if (!scrollContainer) {
                return this.getTranscriptContentFallback();
            }
            
            // 检查是否需要滚动
            if (scrollContainer.scrollHeight <= scrollContainer.clientHeight + 100) {
                return this.getTranscriptContentFallback();
            }
            
            const originalScrollTop = scrollContainer.scrollTop;
            let allContent = new Map();
            let lastScrollTop = -1;
            let stuckCounter = 0;
            
            try {
                // 先滚动到顶部
                scrollContainer.scrollTop = 0;
                await this.sleep(300);
                
                // 持续滚动直到底部
                while (scrollContainer.scrollTop !== lastScrollTop && stuckCounter < 3) {
                    lastScrollTop = scrollContainer.scrollTop;
                    
                    // 收集当前可见的内容
                    const paragraphs = document.querySelectorAll('.paragraph-module_detail-page-style__Lhz8l');
                    
                    paragraphs.forEach(para => {
                        const pid = para.getAttribute('data-pid');
                        if (!pid || allContent.has(pid)) return;
                        
                        const speakerElement = para.querySelector('.paragraph-module_speaker-name__afSbd');
                        const timeElement = para.querySelector('.paragraph-module_p-start-time__QAWWl');
                        const textElement = para.querySelector('.paragraph-module_sentences__zK2oL');
                        
                        if (textElement && textElement.textContent.trim()) {
                            allContent.set(pid, {
                                pid: parseInt(pid),
                                time: timeElement ? timeElement.textContent.trim() : '',
                                speaker: speakerElement ? speakerElement.textContent.trim() : '未知发言人',
                                text: textElement.textContent.trim()
                            });
                        }
                    });
                    
                    // 向下滚动
                    const newScrollTop = scrollContainer.scrollTop + scrollContainer.clientHeight * 0.8;
                    scrollContainer.scrollTop = newScrollTop;
                    
                    // 等待渲染
                    await this.sleep(150);
                    
                    // 检查是否卡住
                    if (scrollContainer.scrollTop === lastScrollTop) {
                        stuckCounter++;
                        // 尝试多滚动一点
                        scrollContainer.scrollTop += 50;
                        await this.sleep(100);
                    } else {
                        stuckCounter = 0;
                    }
                }
                
                // 恢复原始滚动位置
                scrollContainer.scrollTop = originalScrollTop;
                
                // 按pid排序
                return Array.from(allContent.values()).sort((a, b) => a.pid - b.pid);
                
            } catch (error) {
                console.error('获取转写内容失败:', error);
                scrollContainer.scrollTop = originalScrollTop;
                return this.getTranscriptContentFallback();
            }
        },
        
        getTranscriptContentFallback() {
            const paragraphs = document.querySelectorAll('.paragraph-module_detail-page-style__Lhz8l');
            let content = [];
            
            paragraphs.forEach(para => {
                const speakerElement = para.querySelector('.paragraph-module_speaker-name__afSbd');
                const timeElement = para.querySelector('.paragraph-module_p-start-time__QAWWl');
                const textElement = para.querySelector('.paragraph-module_sentences__zK2oL');
                
                if (textElement && textElement.textContent.trim()) {
                    content.push({
                        time: timeElement ? timeElement.textContent.trim() : '',
                        speaker: speakerElement ? speakerElement.textContent.trim() : '未知发言人',
                        text: textElement.textContent.trim()
                    });
                }
            });
            
            return content;
        },

        sleep(ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        },

        getSummaryContent() {
            const pageType = this.getPageType();
            
            if (pageType === 'transcript') {
                const summaryContainer = document.querySelector('.aisummary-container .summary-content-wrap');
                if (!summaryContainer) return null;

                const elements = summaryContainer.querySelectorAll('h4, p, li');
                return Array.from(elements).map(el => ({
                    type: el.tagName.toLowerCase(),
                    text: el.textContent.trim()
                })).filter(item => item.text);
            }

            const summaryContainer = document.querySelector('.summary-content-wrap');
            if (!summaryContainer) return null;

            const elements = summaryContainer.querySelectorAll('h4, p, li, div[contenteditable="true"]');
            return Array.from(elements).map(el => ({
                type: el.tagName.toLowerCase(),
                text: el.textContent.trim()
            })).filter(item => item.text);
        },

        formatAsMarkdown(data, type) {
            const title = this.getMeetingTitle();
            const recordingTime = this.getRecordingTime();
            const keywords = this.getMeetingKeywords();
            const exportTime = new Date().toLocaleString('zh-CN');
            
            let markdown = `# ${title}\n\n`;
            markdown += `**录制时间**: ${recordingTime}\n`;
            markdown += `**导出时间**: ${exportTime}\n`;
            markdown += `**内容类型**: ${type === 'transcript' ? '转写内容' : '会议纪要'}\n`;
            
            if (keywords.length > 0) {
                markdown += `**会议关键词**: ${keywords.join('、')}\n`;
            }
            markdown += `\n---\n\n`;
            
            if (type === 'transcript' && Array.isArray(data)) {
                markdown += `## 转写内容\n\n`;
                data.forEach(item => {
                    markdown += `### ${item.speaker}${item.time ? ` (${item.time})` : ''}\n\n`;
                    markdown += `${item.text}\n\n---\n\n`;
                });
            } else if (type === 'summary' && Array.isArray(data)) {
                markdown += `## 会议纪要\n\n`;
                data.forEach(item => {
                    if (item.type === 'h4') {
                        markdown += `### ${item.text}\n\n`;
                    } else if (item.type === 'li') {
                        markdown += `- ${item.text}\n`;
                    } else {
                        markdown += `${item.text}\n\n`;
                    }
                });
            }
            
            return markdown;
        },

        formatAsHTML(data, type) {
            const title = this.getMeetingTitle();
            const recordingTime = this.getRecordingTime();
            const keywords = this.getMeetingKeywords();
            const exportTime = new Date().toLocaleString('zh-CN');
            
            let html = `<!DOCTYPE html>\n<html lang="zh-CN">\n<head>\n`;
            html += `    <meta charset="UTF-8">\n`;
            html += `    <meta name="viewport" content="width=device-width, initial-scale=1.0">\n`;
            html += `    <title>${title}</title>\n`;
            html += `    <style>\n`;
            html += `        body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; max-width: 800px; margin: 0 auto; padding: 20px; }\n`;
            html += `        .header { border-bottom: 2px solid #eee; padding-bottom: 20px; margin-bottom: 30px; }\n`;
            html += `        .transcript-item { margin-bottom: 20px; padding: 15px; background: #f8f9fa; border-radius: 8px; }\n`;
            html += `        .speaker { font-weight: bold; color: #2c3e50; }\n`;
            html += `        .time { color: #7f8c8d; font-size: 0.9em; }\n`;
            html += `        .content { margin-top: 8px; }\n`;
            html += `        .keywords { margin: 10px 0; }\n`;
            html += `        .keyword-tag { display: inline-block; background: #e3f2fd; color: #1976d2; padding: 4px 8px; margin: 2px; border-radius: 4px; font-size: 0.9em; }\n`;
            html += `    </style>\n</head>\n<body>\n`;
            
            html += `    <div class="header">\n`;
            html += `        <h1>${title}</h1>\n`;
            html += `        <p><strong>录制时间</strong>: ${recordingTime}</p>\n`;
            html += `        <p><strong>导出时间</strong>: ${exportTime}</p>\n`;
            html += `        <p><strong>内容类型</strong>: ${type === 'transcript' ? '转写内容' : '会议纪要'}</p>\n`;
            if (keywords.length > 0) {
                html += `        <div class="keywords">\n`;
                html += `            <strong>会议关键词</strong>: `;
                keywords.forEach(keyword => {
                    html += `<span class="keyword-tag">${keyword}</span>`;
                });
                html += `\n        </div>\n`;
            }
            html += `    </div>\n`;
            
            if (type === 'transcript' && Array.isArray(data)) {
                html += `    <h2>转写内容</h2>\n`;
                data.forEach(item => {
                    html += `    <div class="transcript-item">\n`;
                    html += `        <div class="speaker">${item.speaker}${item.time ? ` <span class="time">(${item.time})</span>` : ''}</div>\n`;
                    html += `        <div class="content">${item.text}</div>\n`;
                    html += `    </div>\n`;
                });
            } else if (type === 'summary' && Array.isArray(data)) {
                html += `    <h2>会议纪要</h2>\n`;
                data.forEach(item => {
                    if (item.type === 'h4') {
                        html += `    <h3>${item.text}</h3>\n`;
                    } else if (item.type === 'li') {
                        html += `    <li>${item.text}</li>\n`;
                    } else {
                        html += `    <p>${item.text}</p>\n`;
                    }
                });
            }
            
            html += `</body>\n</html>`;
            return html;
        },

        formatAsTXT(data, type) {
            const title = this.getMeetingTitle();
            const recordingTime = this.getRecordingTime();
            const keywords = this.getMeetingKeywords();
            const exportTime = new Date().toLocaleString('zh-CN');
            
            let txt = `${title}\n`;
            txt += `${'='.repeat(Math.min(title.length, 50))}\n\n`;
            txt += `录制时间: ${recordingTime}\n`;
            txt += `导出时间: ${exportTime}\n`;
            txt += `内容类型: ${type === 'transcript' ? '转写内容' : '会议纪要'}\n`;
            
            if (keywords.length > 0) {
                txt += `会议关键词: ${keywords.join('、')}\n`;
            }
            txt += `\n`;
            
            if (type === 'transcript' && Array.isArray(data)) {
                txt += `转写内容\n${'-'.repeat(10)}\n\n`;
                data.forEach(item => {
                    txt += `${item.speaker}${item.time ? ` (${item.time})` : ''}\n`;
                    txt += `${item.text}\n\n`;
                });
            } else if (type === 'summary' && Array.isArray(data)) {
                txt += `会议纪要\n${'-'.repeat(10)}\n\n`;
                data.forEach(item => {
                    if (item.type === 'h4') {
                        txt += `\n${item.text}\n${'-'.repeat(Math.min(item.text.length, 30))}\n`;
                    } else if (item.type === 'li') {
                        txt += `&#8226; ${item.text}\n`;
                    } else {
                        txt += `${item.text}\n\n`;
                    }
                });
            }
            
            return txt;
        },

        downloadFile(content, filename, type) {
            const mimeTypes = {
                'md': 'text/markdown',
                'html': 'text/html',
                'txt': 'text/plain'
            };
            
            const blob = new Blob(['\ufeff' + content], { type: mimeTypes[type] || 'text/plain' }); // 添加BOM解决中文乱码
            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);
        },

        async copyToClipboard(text) {
            try {
                await navigator.clipboard.writeText(text);
                return true;
            } catch (err) {
                const textArea = document.createElement('textarea');
                textArea.value = text;
                textArea.style.position = 'fixed';
                textArea.style.opacity = '0';
                document.body.appendChild(textArea);
                textArea.select();
                const success = document.execCommand('copy');
                document.body.removeChild(textArea);
                return success;
            }
        },

        showMessage(message, type = 'success') {
            const existingMsg = document.querySelector('.tm-exporter-message');
            if (existingMsg) existingMsg.remove();
            
            const messageDiv = document.createElement('div');
            messageDiv.className = 'tm-exporter-message';
            const colors = {
                'success': '#10b981',
                'error': '#ef4444',
                'info': '#3b82f6',
                'warning': '#f59e0b'
            };
            messageDiv.style.cssText = `
                position: fixed;
                top: 80px;
                right: 20px;
                z-index: 10002;
                padding: 12px 20px;
                border-radius: 6px;
                color: white;
                font-weight: 600;
                font-size: 14px;
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
                transition: all 0.3s ease;
                background: ${colors[type] || colors.success};
                max-width: 400px;
                word-wrap: break-word;
            `;
            messageDiv.textContent = message;
            document.body.appendChild(messageDiv);
            
            setTimeout(() => {
                messageDiv.style.opacity = '0';
                messageDiv.style.transform = 'translateX(100%)';
                setTimeout(() => {
                    if (messageDiv.parentNode) {
                        messageDiv.parentNode.removeChild(messageDiv);
                    }
                }, 300);
            }, 3000);
        }
    };

    class TencentMeetingExporter {
        constructor() {
            this.init();
        }

        init() {
            this.createExportButton();
        }

        createExportButton() {
            // 移除已存在的按钮
            const existingBtn = document.querySelector('.tm-exporter-btn');
            if (existingBtn) existingBtn.remove();
            
            const button = document.createElement('button');
            button.className = 'tm-exporter-btn';
            button.textContent = '&#128221; 导出转写/纪要';
            button.style.cssText = CONFIG.BUTTON_STYLE;
            
            button.addEventListener('mouseenter', () => {
                button.style.transform = 'translateY(-2px)';
                button.style.boxShadow = '0 6px 20px rgba(102, 126, 234, 0.6)';
            });
            
            button.addEventListener('mouseleave', () => {
                button.style.transform = 'translateY(0)';
                button.style.boxShadow = '0 4px 15px rgba(102, 126, 234, 0.4)';
            });
            
            button.addEventListener('click', () => {
                this.showExportModal();
            });
            
            document.body.appendChild(button);
        }

        showExportModal() {
            const currentTab = utils.getCurrentTabType();
            let modalHTML;

            if (currentTab === 'both') {
                modalHTML = `
                    <h2 style="margin: 0 0 20px 0; color: #2c3e50; font-size: 24px;">导出转写/纪要</h2>
                    <p style="margin: 0 0 25px 0; color: #7f8c8d; line-height: 1.5;">选择要导出的内容类型和格式</p>
                    <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; margin-bottom: 25px;">
                        <button id="export-transcript-md" style="padding: 15px; border: 2px solid #3498db; background: white; color: #3498db; border-radius: 8px; cursor: pointer; font-weight: 600; transition: all 0.3s ease;">&#128196; 导出转写(MD)</button>
                        <button id="export-summary-md" style="padding: 15px; border: 2px solid #e74c3c; background: white; color: #e74c3c; border-radius: 8px; cursor: pointer; font-weight: 600; transition: all 0.3s ease;">&#128203; 导出纪要(MD)</button>
                        <button id="export-transcript-html" style="padding: 15px; border: 2px solid #f39c12; background: white; color: #f39c12; border-radius: 8px; cursor: pointer; font-weight: 600; transition: all 0.3s ease;">&#127760; 导出转写(HTML)</button>
                        <button id="export-summary-html" style="padding: 15px; border: 2px solid #9b59b6; background: white; color: #9b59b6; border-radius: 8px; cursor: pointer; font-weight: 600; transition: all 0.3s ease;">&#127760; 导出纪要(HTML)</button>
                        <button id="export-transcript-txt" style="padding: 15px; border: 2px solid #16a085; background: white; color: #16a085; border-radius: 8px; cursor: pointer; font-weight: 600; transition: all 0.3s ease;">&#128221; 导出转写(TXT)</button>
                        <button id="copy-transcript-md" style="padding: 15px; border: 2px solid #8e44ad; background: white; color: #8e44ad; border-radius: 8px; cursor: pointer; font-weight: 600; transition: all 0.3s ease;">&#128203; 复制转写(MD)</button>
                    </div>
                    <div style="display: flex; justify-content: flex-end; gap: 10px;">
                        <button id="modal-close" style="padding: 10px 20px; border: 1px solid #bdc3c7; background: white; color: #7f8c8d; border-radius: 6px; cursor: pointer;">取消</button>
                    </div>
                `;
            } else {
                const tabName = currentTab === 'transcript' ? '转写内容' : '会议纪要';
                modalHTML = `
                    <h2 style="margin: 0 0 20px 0; color: #2c3e50; font-size: 24px;">导出${tabName}</h2>
                    <p style="margin: 0 0 25px 0; color: #7f8c8d; line-height: 1.5;">选择导出格式,文件名将自动使用会议标题</p>
                    <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; margin-bottom: 25px;">
                        <button id="export-md" style="padding: 15px; border: 2px solid #3498db; background: white; color: #3498db; border-radius: 8px; cursor: pointer; font-weight: 600; transition: all 0.3s ease;">&#128196; Markdown</button>
                        <button id="export-html" style="padding: 15px; border: 2px solid #e74c3c; background: white; color: #e74c3c; border-radius: 8px; cursor: pointer; font-weight: 600; transition: all 0.3s ease;">&#127760; HTML</button>
                        <button id="export-txt" style="padding: 15px; border: 2px solid #f39c12; background: white; color: #f39c12; border-radius: 8px; cursor: pointer; font-weight: 600; transition: all 0.3s ease;">&#128221; TXT</button>
                        <button id="copy-md" style="padding: 15px; border: 2px solid #9b59b6; background: white; color: #9b59b6; border-radius: 8px; cursor: pointer; font-weight: 600; transition: all 0.3s ease;">&#128203; 复制Markdown</button>
                    </div>
                    <div style="display: flex; justify-content: flex-end; gap: 10px;">
                        <button id="modal-close" style="padding: 10px 20px; border: 1px solid #bdc3c7; background: white; color: #7f8c8d; border-radius: 6px; cursor: pointer;">取消</button>
                    </div>
                `;
            }

            const modal = document.createElement('div');
            modal.style.cssText = CONFIG.MODAL_STYLE;
            
            const modalContent = document.createElement('div');
            modalContent.style.cssText = CONFIG.MODAL_CONTENT_STYLE;
            modalContent.innerHTML = modalHTML;
            
            // 添加按钮悬停效果
            const buttons = modalContent.querySelectorAll('button[id^="export-"], button[id^="copy-"]');
            buttons.forEach(btn => {
                btn.addEventListener('mouseenter', () => {
                    btn.style.background = btn.style.color;
                    btn.style.color = 'white';
                });
                btn.addEventListener('mouseleave', () => {
                    btn.style.background = 'white';
                    btn.style.color = btn.style.borderColor;
                });
            });

            // 绑定事件
            if (currentTab === 'both') {
                modalContent.querySelector('#export-transcript-md')?.addEventListener('click', () => this.exportContent('md', 'transcript'));
                modalContent.querySelector('#export-summary-md')?.addEventListener('click', () => this.exportContent('md', 'summary'));
                modalContent.querySelector('#export-transcript-html')?.addEventListener('click', () => this.exportContent('html', 'transcript'));
                modalContent.querySelector('#export-summary-html')?.addEventListener('click', () => this.exportContent('html', 'summary'));
                modalContent.querySelector('#export-transcript-txt')?.addEventListener('click', () => this.exportContent('txt', 'transcript'));
                modalContent.querySelector('#copy-transcript-md')?.addEventListener('click', () => this.copyContent('transcript'));
            } else {
                modalContent.querySelector('#export-md')?.addEventListener('click', () => this.exportContent('md'));
                modalContent.querySelector('#export-html')?.addEventListener('click', () => this.exportContent('html'));
                modalContent.querySelector('#export-txt')?.addEventListener('click', () => this.exportContent('txt'));
                modalContent.querySelector('#copy-md')?.addEventListener('click', () => this.copyContent());
            }
            modalContent.querySelector('#modal-close').addEventListener('click', () => this.closeModal(modal));
            
            modal.addEventListener('click', (e) => {
                if (e.target === modal) {
                    this.closeModal(modal);
                }
            });
            
            modal.appendChild(modalContent);
            document.body.appendChild(modal);
        }

        async exportContent(format, contentType = null) {
            try {
                const currentTab = utils.getCurrentTabType();
                const exportType = contentType || (currentTab === 'both' ? 'transcript' : currentTab);
                let data;

                if (exportType === 'transcript') {
                    utils.showMessage('正在获取转写内容,请稍候...', 'info');
                    data = await utils.getTranscriptContent();
                    
                    if (!data || data.length === 0) {
                        utils.showMessage('未找到转写内容,请确保页面已加载完成', 'error');
                        return;
                    }
                    
                    utils.showMessage(`获取成功!共找到 ${data.length} 条转写记录`, 'success');
                } else {
                    data = utils.getSummaryContent();
                    if (!data || data.length === 0) {
                        utils.showMessage('未找到纪要内容,请确保页面已加载完成', 'error');
                        return;
                    }
                }
                
                const title = utils.getMeetingTitle();
                const recordingTime = utils.getRecordingTime();
                
                let timestamp;
                if (recordingTime) {
                    timestamp = recordingTime.replace(/[^\d]/g, '').slice(0, 14);
                    if (timestamp.length >= 12) {
                        timestamp = timestamp.slice(0, 8) + '_' + timestamp.slice(8);
                    }
                } else {
                    const now = new Date();
                    timestamp = now.getFullYear().toString() + 
                        (now.getMonth() + 1).toString().padStart(2, '0') + 
                        now.getDate().toString().padStart(2, '0') + '_' +
                        now.getHours().toString().padStart(2, '0') + 
                        now.getMinutes().toString().padStart(2, '0') + 
                        now.getSeconds().toString().padStart(2, '0');
                }
                
                const tabName = exportType === 'transcript' ? '转写' : '纪要';
                // 清理文件名中的特殊字符
                const safeTitle = title.replace(/[\\/:*?"<>|]/g, '_');
                const filename = `${safeTitle}_${tabName}_${timestamp}.${format}`;

                let content;
                switch (format) {
                    case 'md':
                        content = utils.formatAsMarkdown(data, exportType);
                        break;
                    case 'html':
                        content = utils.formatAsHTML(data, exportType);
                        break;
                    case 'txt':
                        content = utils.formatAsTXT(data, exportType);
                        break;
                }
                
                utils.downloadFile(content, filename, format);
                utils.showMessage(`${format.toUpperCase()}文件导出成功!`);
                
                const modal = document.querySelector('div[style*="position: fixed"][style*="z-index: 10001"]');
                if (modal) this.closeModal(modal);
                
            } catch (error) {
                console.error('导出失败:', error);
                utils.showMessage('导出失败:' + error.message, 'error');
            }
        }

        async copyContent(contentType = null) {
            try {
                const currentTab = utils.getCurrentTabType();
                const exportType = contentType || (currentTab === 'both' ? 'transcript' : currentTab);
                let data;

                if (exportType === 'transcript') {
                    utils.showMessage('正在获取转写内容,请稍候...', 'info');
                    data = await utils.getTranscriptContent();
                    
                    if (!data || data.length === 0) {
                        utils.showMessage('未找到转写内容,请确保页面已加载完成', 'error');
                        return;
                    }
                    
                    utils.showMessage(`获取成功!共找到 ${data.length} 条转写记录`, 'success');
                } else {
                    data = utils.getSummaryContent();
                    if (!data || data.length === 0) {
                        utils.showMessage('未找到纪要内容,请确保页面已加载完成', 'error');
                        return;
                    }
                }

                const content = utils.formatAsMarkdown(data, exportType);
                const success = await utils.copyToClipboard(content);

                if (success) {
                    utils.showMessage('Markdown内容已复制到剪贴板!');
                } else {
                    utils.showMessage('复制失败,请重试', 'error');
                }

                const modal = document.querySelector('div[style*="position: fixed"][style*="z-index: 10001"]');
                if (modal) this.closeModal(modal);
                
            } catch (error) {
                console.error('复制失败:', error);
                utils.showMessage('复制失败:' + error.message, 'error');
            }
        }

        closeModal(modal) {
            modal.style.opacity = '0';
            setTimeout(() => {
                if (modal.parentNode) {
                    modal.parentNode.removeChild(modal);
                }
            }, 300);
        }
    }

    // 页面加载完成后初始化
    function init() {
        // 先检查页面是否包含转写内容
        const checkInterval = setInterval(() => {
            const hasContent = document.querySelector('.minutes-module-list');
            if (hasContent) {
                clearInterval(checkInterval);
                new TencentMeetingExporter();
            }
        }, 500);
        
        // 最多等待10秒
        setTimeout(() => {
            clearInterval(checkInterval);
            if (!document.querySelector('.tm-exporter-btn')) {
                new TencentMeetingExporter();
            }
        }, 10000);
    }

    init();

})();

免费评分

参与人数 3吾爱币 +2 热心值 +2 收起 理由
kenanxwl + 1 谢谢@Thanks!
shengruqing + 1 我很赞同!
yanfei007 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
 楼主| lucool 发表于 2026-5-12 12:44 |楼主
SherlockProel 发表于 2026-5-12 09:54
我在网页加入会议,它提示我要下载腾讯会议哈哈哈哈哈哈哈哈,没找到网页参会的入口

https://meeting.tencent.com/,这个可以网页登陆,让你下载,你不下载就行。
推荐
 楼主| lucool 发表于 2026-5-12 09:30 |楼主
补充一下,必须要在网页版打开腾讯会议才能用!
沙发
 楼主| lucool 发表于 2026-5-12 09:12 |楼主
修改前代码的地址 https://github.com/donggeai/Awesome-Tampermonkey-Scripts/tree/master/TencentMeetingTranscriptExporter
3#
cuimen 发表于 2026-5-12 09:19
感谢分享, 研究学习下
4#
haagyp 发表于 2026-5-12 09:29
谢谢分享,学习一下
6#
Ziye2021 发表于 2026-5-12 09:33
谢谢分享,学习一下
7#
WORSONG178 发表于 2026-5-12 09:33
不知道老版本的插件能不能用这个功能。
8#
andytang866 发表于 2026-5-12 09:33
用户脚本的名称无效
名称在哪,怎么改啊,大佬
9#
andytang866 发表于 2026-5-12 09:44
lucool 发表于 2026-5-12 09:30
补充一下,必须要在网页版打开腾讯会议才能用!

我复制到油猴里,提示“用户脚本的名称无效”
10#
SherlockProel 发表于 2026-5-12 09:45
很6啊,是不是管理员不开权限也能导出
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-5-13 06:02

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表