// ==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 = `