// ==UserScript== // @name rm在线观看加密TXT文件 // @namespace http://tampermonkey.net/ // @version 1.2 // @description 在下载确认页面添加在线观看按钮,直接解密并显示TXT内容 // @author YourName // @match *://*/* // @grant GM_xmlhttpRequest // ==/UserScript== (function() { 'use strict'; // ==================== 脚本启用提示 ==================== console.log('%c🎬 在线观看加密TXT文件脚本已启用', 'color: #00ff00; font-size: 16px; font-weight: bold;'); // 页面角标提示 function addPageIndicator() { const indicator = document.createElement('div'); indicator.id = 'crypto-script-indicator'; indicator.innerHTML = '🎬 加密TXT在线观看脚本已启动'; indicator.style.cssText = ` position: fixed; bottom: 20px; right: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 6px 12px; border-radius: 16px; font-size: 11px; font-family: Arial, sans-serif; z-index: 999999; box-shadow: 0 2px 8px rgba(0,0,0,0.2); cursor: pointer; opacity: 0.85; `; indicator.onclick = () => indicator.remove(); document.body.appendChild(indicator); setTimeout(() => indicator.remove(), 5000); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', addPageIndicator); } else { addPageIndicator(); } // ==================== 核心加密解密模块 ==================== const FIXED_KEY = "multi-mode-encryption-2024"; function base64ToArray(base64) { const binaryString = atob(base64); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes; } function isEncryptedText(text) { if (!text || text.length < 50) return false; try { const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/; const cleanText = text.replace(/\s/g, ''); const isBase64 = base64Regex.test(cleanText); const hasInvalidChars = /[^A-Za-z0-9+/=\s]/.test(text); const lengthValid = cleanText.length % 4 === 0; const hasChineseChars = /[\u4e00-\u9fa5]/.test(text); return isBase64 && !hasInvalidChars && lengthValid && !hasChineseChars; } catch { return false; } } async function decrypt(encryptedBase64, password = FIXED_KEY) { try { const cleanBase64 = encryptedBase64.replace(/\s/g, ''); const combined = base64ToArray(cleanBase64); const salt = combined.slice(0, 16); const iv = combined.slice(16, 28); const encrypted = combined.slice(28); const encoder = new TextEncoder(); const passwordKey = await crypto.subtle.importKey( 'raw', encoder.encode(password), { name: 'PBKDF2' }, false, ['deriveKey'] ); const key = await crypto.subtle.deriveKey( { name: 'PBKDF2', salt: salt, iterations: 100000, hash: 'SHA-256' }, passwordKey, { name: 'AES-GCM', length: 256 }, false, ['decrypt'] ); const decrypted = await crypto.subtle.decrypt( { name: 'AES-GCM', iv: iv }, key, encrypted ); return new TextDecoder().decode(decrypted); } catch (error) { console.error('解密错误:', error); throw new Error('解密失败:文件可能已损坏或不是有效的加密文件'); } } // ==================== 获取下载URL ==================== function getDownloadUrl() { const confirmBtn = document.querySelector('.btn2 button[onclick*="location.href"], button[onclick*="location.href"]'); if (confirmBtn) { const onclickAttr = confirmBtn.getAttribute('onclick'); const match = onclickAttr.match(/location\.href='([^']+)'/); if (match && match[1]) { return match[1]; } } return null; } // ==================== 获取并显示文件内容 ==================== async function fetchAndDisplayFile() { const downloadUrl = getDownloadUrl(); if (!downloadUrl) { showError('无法获取下载链接'); return; } showFullscreenView('loading', '正在获取文件内容,请稍候...'); try { GM_xmlhttpRequest({ method: 'GET', url: downloadUrl, responseType: 'text', onload: async function(response) { if (response.status === 200) { const content = response.responseText; const contentType = response.responseHeaders?.toLowerCase() || ''; const isTextFile = contentType.includes('text/plain') || downloadUrl.toLowerCase().endsWith('.txt') || !content.match(/[\x00-\x08\x0b\x0c\x0e-\x1f]/); if (!isTextFile) { showError('该文件不是文本文件(.txt),无法在线观看'); return; } if (!isEncryptedText(content)) { showFullscreenView('text', content, '⚠️ 该文件不是加密内容,以下是原始内容:'); return; } updateFullscreenProgress('正在解密内容,请稍候...'); try { const decrypted = await decrypt(content); showFullscreenView('text', decrypted, '✅ 解密成功!以下是文件内容:'); } catch (decryptError) { showError(`解密失败:${decryptError.message}`); } } else { showError(`获取文件失败:HTTP ${response.status}`); } }, onerror: function(error) { showError('网络请求失败,请检查网络连接'); }, ontimeout: function() { showError('请求超时,请稍后重试'); }, timeout: 30000 }); } catch (error) { showError(`发生错误:${error.message}`); } } // ==================== 全屏显示界面 ==================== function showFullscreenView(type, content, title = '') { const existingView = document.getElementById('onlineViewer'); if (existingView) existingView.remove(); const viewer = document.createElement('div'); viewer.id = 'onlineViewer'; viewer.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.95); z-index: 999999; display: flex; flex-direction: column; font-family: 'Microsoft YaHei', 'SimHei', 'PingFang SC', monospace; `; const header = document.createElement('div'); header.style.cssText = ` background: #2d2d2d; color: white; padding: 15px 20px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #444; flex-shrink: 0; `; const titleSpan = document.createElement('span'); titleSpan.style.cssText = ` font-size: 18px; font-weight: bold; `; titleSpan.textContent = title || (type === 'loading' ? '加载中' : '在线观看'); const closeBtn = document.createElement('button'); closeBtn.textContent = '✕ 关闭'; closeBtn.style.cssText = ` background: #e74c3c; color: white; border: none; padding: 8px 20px; border-radius: 5px; cursor: pointer; font-size: 14px; font-weight: bold; `; closeBtn.onmouseover = () => closeBtn.style.background = '#c0392b'; closeBtn.onmouseout = () => closeBtn.style.background = '#e74c3c'; closeBtn.onclick = () => viewer.remove(); header.appendChild(titleSpan); header.appendChild(closeBtn); const contentArea = document.createElement('div'); contentArea.className = 'content-area'; contentArea.style.cssText = ` flex: 1; overflow: auto; padding: 20px; background: #1e1e1e; `; if (type === 'loading') { contentArea.innerHTML = `
${content}
`; } else if (type === 'text') { const pre = document.createElement('pre'); pre.textContent = content; pre.style.cssText = ` margin: 0; padding: 15px; background: #252526; color: #d4d4d4; border-radius: 8px; font-family: 'Consolas', 'Monaco', 'Courier New', monospace; font-size: 14px; line-height: 1.6; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word; `; contentArea.appendChild(pre); const actions = document.createElement('div'); actions.style.cssText = ` margin-top: 15px; display: flex; gap: 10px; justify-content: flex-end; `; const copyBtn = createActionButton('📋 复制全部内容', async () => { try { await navigator.clipboard.writeText(content); showToast('已复制到剪贴板'); } catch (err) { showToast('复制失败'); } }); const downloadBtn = createActionButton('💾 下载为TXT', () => { const blob = new Blob([content], { type: 'text/plain;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `decrypted_${Date.now()}.txt`; a.click(); URL.revokeObjectURL(url); showToast('开始下载'); }); actions.appendChild(copyBtn); actions.appendChild(downloadBtn); contentArea.appendChild(actions); } viewer.appendChild(header); viewer.appendChild(contentArea); document.body.appendChild(viewer); } function updateFullscreenProgress(message) { const contentArea = document.querySelector('#onlineViewer .content-area'); if (contentArea) { contentArea.innerHTML = `
${message}
`; } } function createActionButton(text, onClick) { const btn = document.createElement('button'); btn.textContent = text; btn.style.cssText = ` background: #3498db; color: white; border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer; font-size: 14px; `; btn.onmouseover = () => btn.style.background = '#2980b9'; btn.onmouseout = () => btn.style.background = '#3498db'; btn.onclick = onClick; return btn; } function showError(message) { const existingView = document.getElementById('onlineViewer'); if (existingView) existingView.remove(); const toast = document.createElement('div'); toast.textContent = `❌ ${message}`; toast.style.cssText = ` position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: #e74c3c; color: white; padding: 12px 24px; border-radius: 8px; z-index: 1000000; font-size: 14px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); animation: fadeOut 3s ease forwards; `; const style = document.createElement('style'); style.textContent = ` @keyframes fadeOut { 0% { opacity: 1; } 70% { opacity: 1; } 100% { opacity: 0; visibility: hidden; } } `; document.head.appendChild(style); document.body.appendChild(toast); setTimeout(() => toast.remove(), 3000); } function showToast(message) { const toast = document.createElement('div'); toast.textContent = `✅ ${message}`; toast.style.cssText = ` position: fixed; bottom: 20px; right: 20px; background: #27ae60; color: white; padding: 10px 20px; border-radius: 5px; z-index: 1000000; font-size: 14px; animation: fadeOut 2s ease forwards; `; document.body.appendChild(toast); setTimeout(() => toast.remove(), 2000); } // ==================== 添加在线观看按钮(带边框样式)==================== function addOnlineViewButton() { // 查找确定按钮 let confirmBtn = document.querySelector('.btn2 button[onclick*="location.href"]'); if (!confirmBtn) { confirmBtn = document.querySelector('button[onclick*="location.href"]'); } if (!confirmBtn) { return; } if (document.getElementById('onlineViewBtn')) { return; } // 找到 popBottom 容器 const popBottom = confirmBtn.closest('.popBottom'); if (!popBottom) { return; } // 找到取消按钮所在的 span const cancelSpan = popBottom.querySelector('.bt2'); // 创建新的 span 用于在线观看按钮 const newSpan = document.createElement('span'); newSpan.className = 'btn-online'; newSpan.style.marginLeft = '15px'; // 创建在线观看按钮 - 带边框样式 const onlineBtn = document.createElement('button'); onlineBtn.id = 'onlineViewBtn'; onlineBtn.textContent = '🎬 在线观看'; onlineBtn.type = 'button'; // 漂亮的边框样式 onlineBtn.style.cssText = ` padding: 6px 16px; border: 1px solid #ff6600; border-radius: 20px; background: linear-gradient(135deg, #fff 0%, #fff5e6 100%); color: #ff6600; cursor: pointer; font-size: 14px; font-weight: bold; transition: all 0.3s ease; box-shadow: 0 1px 3px rgba(0,0,0,0.1); `; // 悬停效果 onlineBtn.onmouseover = () => { onlineBtn.style.background = 'linear-gradient(135deg, #ff6600 0%, #ff8833 100%)'; onlineBtn.style.color = '#fff'; onlineBtn.style.borderColor = '#ff6600'; onlineBtn.style.transform = 'translateY(-1px)'; onlineBtn.style.boxShadow = '0 3px 8px rgba(255,102,0,0.3)'; }; onlineBtn.onmouseout = () => { onlineBtn.style.background = 'linear-gradient(135deg, #fff 0%, #fff5e6 100%)'; onlineBtn.style.color = '#ff6600'; onlineBtn.style.borderColor = '#ff6600'; onlineBtn.style.transform = 'translateY(0)'; onlineBtn.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)'; }; // 点击事件 onlineBtn.onclick = (e) => { e.preventDefault(); e.stopPropagation(); fetchAndDisplayFile(); }; // 将按钮放入新span const innerSpan = document.createElement('span'); innerSpan.appendChild(onlineBtn); newSpan.appendChild(innerSpan); // 插入到取消按钮之前 if (cancelSpan) { popBottom.insertBefore(newSpan, cancelSpan); } else { popBottom.appendChild(newSpan); } // 给取消按钮也加点左边距 if (cancelSpan && !cancelSpan.style.marginLeft) { cancelSpan.style.marginLeft = '15px'; } console.log('✅ 在线观看按钮已添加,带边框样式'); } // 监听页面变化,动态添加按钮 function init() { addOnlineViewButton(); const observer = new MutationObserver(function(mutations) { if (!document.getElementById('onlineViewBtn')) { addOnlineViewButton(); } }); observer.observe(document.body, { childList: true, subtree: true }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();