// ==UserScript== // @name 豆包&即梦AI下载无水印图片视频 // @namespace http://tampermonkey.net/ // @version 3.5 // @description 实现豆包&即梦生成的图片视频免费无水印下载 // @author 微信11208596 // @license UNLICENSED // @match https://www.doubao.com/* // @match https://jimeng.jianying.com/ai-tool/* // @grant GM_download // @grant GM_xmlhttpRequest // @downloadURL none // ==/UserScript== (function () { 'use strict'; // 添加激活码相关功能 const ACTIVATION_KEY = 'doubao_activation_status'; const SECRET_KEY = 'db94xy20240322'; // 使用一个固定的密钥值 const VALID_DAYS = 30; // 激活码有效期(天) // 飞书多维表格配置 const FEISHU_CONFIG = { APP_ID: 'cli_a7317a5d6afd901c', APP_SECRET: 'cdGf1f5n5xY0tI6F07xKkcU1iPoFVdPD', BASE_ID: 'T1M4bzmLLarNLhs5jcEcwAcRn8Q', // 多维表格 base ID TABLE_ID: 'tbliBckxa87pskV8', // 数据表 ID API_URL: 'https://open.feishu.cn/open-apis', TOKEN: null }; // 修改获取访问令牌的函数 async function getFeishuAccessToken() { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal', headers: { 'Content-Type': 'application/json; charset=utf-8' }, data: JSON.stringify({ "app_id": FEISHU_CONFIG.APP_ID, "app_secret": FEISHU_CONFIG.APP_SECRET }), onload: function(response) { try { const data = JSON.parse(response.responseText); console.log('访问令牌响应:', data); if (data.code === 0 && data.tenant_access_token) { FEISHU_CONFIG.TOKEN = data.tenant_access_token; resolve(data.tenant_access_token); } else { console.error('获取访问令牌失败:', data); reject(new Error(`获取访问令牌失败: ${data.msg || '未知错误'}`)); } } catch (e) { console.error('解析响应失败:', e); reject(e); } }, onerror: function(error) { console.error('请求失败:', error); reject(error); } }); }); } // 修改生成激活码的函数 async function generateActivationCode() { try { if (!FEISHU_CONFIG.TOKEN) { await getFeishuAccessToken(); } // 生成随机部分 const randomPart = Math.random().toString(36).substring(2, 10); // 时间戳部分 const timestampPart = Date.now().toString(36); // 校验部分 const checkPart = generateCheckPart(randomPart + timestampPart); // 组合激活码 const code = `${randomPart}-${timestampPart}-${checkPart}`; // 计算过期时间 const expireTime = new Date(); expireTime.setDate(expireTime.getDate() + VALID_DAYS); // 创建新记录 const response = await fetch(`${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${FEISHU_CONFIG.BASE_ID}/tables/${FEISHU_CONFIG.TABLE_ID}/records`, { method: 'POST', headers: { 'Authorization': `Bearer ${FEISHU_CONFIG.TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ fields: { '激活码': code, '状态': '正常', '过期时间': expireTime.toISOString(), '创建时间': new Date().toISOString() } }) }); const data = await response.json(); if (data.code === 0) { return code; } else { console.error('创建激活码失败:', data); return null; } } catch (e) { console.error('生成激活码出错:', e); return null; } } // 生成校验部分 function generateCheckPart(str) { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32-bit integer } return Math.abs(hash).toString(36).substring(0, 4); } // 添加获取字段信息的函数 async function getTableFields() { const token = await getFeishuAccessToken(); return new Promise((resolve, reject) => { const url = `${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${FEISHU_CONFIG.BASE_ID}/tables/${FEISHU_CONFIG.TABLE_ID}/fields`; GM_xmlhttpRequest({ method: 'GET', url: url, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json; charset=utf-8' }, onload: function(response) { try { const data = JSON.parse(response.responseText); console.log('字段信息:', data); if (data.code === 0) { resolve(data.data.items); } else { reject(new Error(data.msg)); } } catch (e) { reject(e); } }, onerror: reject }); }); } // 修改验证激活码的函数 async function verifyActivationCode(deviceId, code) { try { // 获取访问令牌 const token = await getFeishuAccessToken(); console.log('获取到的访问令牌:', token); return new Promise((resolve, reject) => { const url = `${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${FEISHU_CONFIG.BASE_ID}/tables/${FEISHU_CONFIG.TABLE_ID}/records/search`; // 修改请求数据格式,只查询设备ID const requestData = { page_size: 10, field_names: ["设备ID", "状态", "过期时间"], filter: { conjunction: "and", conditions: [ { field_name: "设备ID", operator: "is", value: [deviceId] // 值必须是数组格式 } ] } }; console.log('请求数据:', JSON.stringify(requestData)); GM_xmlhttpRequest({ method: 'POST', url: url, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json; charset=utf-8' }, data: JSON.stringify(requestData), onload: function(response) { try { const data = JSON.parse(response.responseText); console.log('验证响应:', data); if (data.code !== 0) { reject(new Error(data.msg || '验证失败')); return; } // 检查记录是否存在 if (!data.data?.items || data.data.items.length === 0) { resolve(false); return; } const record = data.data.items[0].fields; // 只检查状态是否正常 if (record.状态 !== '正常') { resolve(false); return; } // 验证过期时间 const expireTime = new Date(record.过期时间); if (expireTime < new Date()) { resolve(false); return; } // 保存激活信息 localStorage.setItem('activation_code', code); localStorage.setItem(ACTIVATION_KEY, 'activated'); localStorage.setItem('expire_time', expireTime.toISOString()); resolve(true); } catch (e) { console.error('处理响应失败:', e); reject(e); } }, onerror: function(error) { console.error('请求失败:', error); reject(error); } }); }); } catch (e) { console.error('验证过程出错:', e); return false; } } // 修改更新激活记录的函数 async function updateActivationRecord(recordId, fields) { return new Promise((resolve, reject) => { if (!FEISHU_CONFIG.TOKEN) { reject(new Error('没有访问令牌')); return; } GM_xmlhttpRequest({ method: 'PUT', url: `${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${FEISHU_CONFIG.BASE_ID}/tables/${FEISHU_CONFIG.TABLE_ID}/records/${recordId}`, headers: { 'Authorization': `Bearer ${FEISHU_CONFIG.TOKEN}`, 'Content-Type': 'application/json' }, data: JSON.stringify({ fields }), onload: function(response) { const data = JSON.parse(response.responseText); if (data.code === 0) { resolve(data); } else { reject(new Error('更新记录失败')); } }, onerror: function(error) { reject(error); } }); }); } // 修改检查激活状态的函数 async function checkActivation() { const activationStatus = localStorage.getItem(ACTIVATION_KEY); const deviceId = localStorage.getItem('deviceId'); const activationCode = localStorage.getItem('activation_code'); const recordId = localStorage.getItem('record_id'); const expireTime = localStorage.getItem('expire_time'); if (!deviceId || !activationStatus || !activationCode || !recordId || !expireTime) { return false; } // 检查本地过期时间 if (new Date() > new Date(expireTime)) { localStorage.removeItem(ACTIVATION_KEY); localStorage.removeItem('activation_code'); localStorage.removeItem('record_id'); localStorage.removeItem('expire_time'); showFloatingTip('激活码已过期,请重新激活'); return false; } // 检查飞书表格中的状态 try { if (!FEISHU_CONFIG.TOKEN) { await getFeishuAccessToken(); } const response = await fetch(`${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${FEISHU_CONFIG.BASE_ID}/tables/${FEISHU_CONFIG.TABLE_ID}/records/${recordId}`, { method: 'GET', headers: { 'Authorization': `Bearer ${FEISHU_CONFIG.TOKEN}` } }); const data = await response.json(); if (data.code === 0) { const record = data.data.record.fields; const serverExpireTime = new Date(record.过期时间); const now = new Date(); if (record.状态 === '已禁用' || now > serverExpireTime) { localStorage.removeItem(ACTIVATION_KEY); localStorage.removeItem('activation_code'); localStorage.removeItem('record_id'); localStorage.removeItem('expire_time'); showFloatingTip('激活码已失效,请重新激活'); return false; } // 更新本地过期时间 localStorage.setItem('expire_time', serverExpireTime.toISOString()); return true; } } catch (e) { console.error('检查激活状态出错:', e); // 如果网络请求失败,暂时使用本地存储的状态 return activationStatus === 'activated'; } return false; } // 修改激活对话框样式 function createActivationDialog() { const overlay = document.createElement('div'); overlay.className = 'download-confirm-overlay'; const dialog = document.createElement('div'); dialog.className = 'download-confirm-dialog'; const deviceId = localStorage.getItem('deviceId') || Math.random().toString(36).substring(2) + Date.now().toString(36); if (!localStorage.getItem('deviceId')) { localStorage.setItem('deviceId', deviceId); } dialog.innerHTML = `

软件激活

请输入激活码以继续使用

请复制设备ID并联系微信(11208596)获取激活码
`; document.body.appendChild(overlay); document.body.appendChild(dialog); // 添加样式 const style = document.createElement('style'); style.textContent = ` .download-confirm-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.4); backdrop-filter: blur(8px); z-index: 9999; } .download-confirm-dialog { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 24px; border-radius: 12px; width: 90%; max-width: 360px; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); z-index: 10000; } .confirm-btn:hover { background: #0066DD !important; } .cancel-btn:hover { background: #eee !important; } .confirm-btn:active { transform: scale(0.98); } .cancel-btn:active { transform: scale(0.98); } `; document.head.appendChild(style); const confirmBtn = dialog.querySelector('.confirm-btn'); const cancelBtn = dialog.querySelector('.cancel-btn'); const activationInput = dialog.querySelector('#activationCode'); const deviceIdInput = dialog.querySelector('#deviceId'); // 复制设备ID功能 deviceIdInput.addEventListener('click', () => { deviceIdInput.select(); document.execCommand('copy'); showFloatingTip('设备ID已复制到剪贴板'); }); function closeDialog() { document.body.removeChild(overlay); document.body.removeChild(dialog); } confirmBtn.addEventListener('click', async () => { const code = activationInput.value.trim(); if (!code) { showFloatingTip('请输入激活码'); return; } confirmBtn.disabled = true; confirmBtn.textContent = '验证中...'; confirmBtn.style.opacity = '0.7'; try { const result = await verifyActivationCode(deviceId, code); if (result) { showFloatingTip('激活成功'); setTimeout(() => { closeDialog(); window.location.reload(); }, 1500); } else { showFloatingTip('激活失败'); confirmBtn.disabled = false; confirmBtn.textContent = '激活'; confirmBtn.style.opacity = '1'; } } catch (e) { console.error('验证过程出错:', e); showFloatingTip('验证出错'); confirmBtn.disabled = false; confirmBtn.textContent = '激活'; confirmBtn.style.opacity = '1'; } }); cancelBtn.addEventListener('click', closeDialog); } // 添加一个激活码生成工具函数(仅供开发使用) function generateActivationCodeForDevice(deviceId) { return generateActivationCode(deviceId); } // 显示浮动提示 function showFloatingTip(message) { const tip = document.createElement('div'); tip.className = 'floating-tip'; tip.innerHTML = `
i
${message} `; document.body.appendChild(tip); setTimeout(() => { tip.classList.add('show'); }, 100); setTimeout(() => { tip.classList.remove('show'); setTimeout(() => { document.body.removeChild(tip); }, 300); }, 3000); } // 修改查询激活码状态的函数 window.queryActivationCode = async function() { const code = document.getElementById('queryCode').value.trim(); const resultDiv = document.getElementById('queryResult'); if (!code) { showFloatingTip('请输入激活码'); return; } try { const response = await fetch(`${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${FEISHU_CONFIG.BASE_ID}/tables/${FEISHU_CONFIG.TABLE_ID}/records/query`, { method: 'POST', headers: { 'Authorization': `Bearer ${FEISHU_CONFIG.TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ filter: `CurrentValue.[激活码] = "${code}"` }) }); const data = await response.json(); if (!data.data.records || data.data.records.length === 0) { resultDiv.innerHTML = '
激活码不存在
'; return; } const record = data.data.records[0].fields; const expireTime = new Date(record.过期时间); const now = new Date(); const isExpired = now > expireTime; resultDiv.innerHTML = `
设备ID: ${record.设备ID || '未使用'}
到期时间: ${expireTime.toLocaleString()}
状态: ${record.状态 === '正常' ? (isExpired ? '已过期' : '有效') : '已禁用'}
${record.状态 === '正常' && !isExpired ? `
` : ''}
`; } catch (e) { resultDiv.innerHTML = '
查询失败,请稍后重试
'; console.error('查询失败:', e); } }; // 修改取消激活码的函数 window.deactivateCode = async function(code) { if (!confirm('确定要取消此激活码吗?取消后此激活码将无法继续使用。')) { return; } try { // 查询激活码记录 const response = await fetch(`${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${FEISHU_CONFIG.BASE_ID}/tables/${FEISHU_CONFIG.TABLE_ID}/records/query`, { method: 'POST', headers: { 'Authorization': `Bearer ${FEISHU_CONFIG.TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ filter: `CurrentValue.[激活码] = "${code}"` }) }); const data = await response.json(); if (!data.data.records || data.data.records.length === 0) { showFloatingTip('激活码不存在'); return; } // 更新状态为已禁用 await updateActivationRecord(data.data.records[0].record_id, { 状态: '已禁用', 禁用时间: new Date().toISOString() }); // 如果当前用户正在使用这个激活码,立即取消激活 const currentCode = localStorage.getItem('activation_code'); if (currentCode === code) { localStorage.removeItem(ACTIVATION_KEY); localStorage.removeItem('activation_code'); } showFloatingTip('激活码已成功取消'); // 更新查询结果显示 const resultDiv = document.getElementById('queryResult'); resultDiv.innerHTML = `
此激活码已被禁用,无法继续使用
`; // 强制刷新页面以确保状态更新 setTimeout(() => { window.location.reload(); }, 1500); } catch (e) { console.error('取消激活码失败:', e); showFloatingTip('取消激活码失败'); } }; // 修改定期检查机制 function startBlacklistCheck() { setInterval(async () => { const activationCode = localStorage.getItem('activation_code'); if (!activationCode) return; try { const response = await fetch(`${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${FEISHU_CONFIG.BASE_ID}/tables/${FEISHU_CONFIG.TABLE_ID}/records/query`, { method: 'POST', headers: { 'Authorization': `Bearer ${FEISHU_CONFIG.TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ filter: `CurrentValue.[激活码] = "${activationCode}"` }) }); const data = await response.json(); if (!data.data.records || data.data.records.length === 0 || data.data.records[0].fields.状态 === '已禁用' || new Date() > new Date(data.data.records[0].fields.过期时间)) { localStorage.removeItem(ACTIVATION_KEY); localStorage.removeItem('activation_code'); showFloatingTip('激活码已失效,请重新激活'); window.location.reload(); } } catch (e) { console.error('检查激活状态失败:', e); } }, 30000); // 每30秒检查一次 } // 在脚本初始化时启动检查 startBlacklistCheck(); // 修改下载验证函数 async function downloadWithActivationCheck(mediaUrl, mediaType, downloadFunction) { if (await checkActivation()) { // 创建下载确认对话框 createConfirmDialog(mediaUrl, mediaType, downloadFunction); } else { createActivationDialog(); } } // 修改确认对话框样式 const style = document.createElement('style'); style.textContent = ` @keyframes dialogShow { from { opacity: 0; transform: translate(-50%, -48%) scale(0.96); } to { opacity: 1; transform: translate(-50%, -50%) scale(1); } } @keyframes overlayShow { from { opacity: 0; } to { opacity: 1; } } .download-confirm-dialog { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(255, 255, 255, 0.8); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); padding: 28px 24px; border-radius: 14px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.05); z-index: 10000; min-width: 320px; max-width: 400px; text-align: center; font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Arial, sans-serif; animation: dialogShow 0.3s cubic-bezier(0.4, 0, 0.2, 1); } .download-confirm-dialog h3 { margin: 0 0 8px 0; color: #1d1d1f; font-size: 19px; font-weight: 600; letter-spacing: -0.022em; } .download-confirm-dialog p { margin: 0 0 20px 0; color: #86868b; font-size: 14px; line-height: 1.4; letter-spacing: -0.016em; } .download-confirm-dialog .input-container { margin: 20px 0; text-align: left; } .download-confirm-dialog label { display: block; margin-bottom: 8px; color: #1d1d1f; font-size: 13px; font-weight: 500; letter-spacing: -0.016em; } .download-confirm-dialog input { width: 100%; padding: 12px 16px; border: 1px solid rgba(0, 0, 0, 0.1); border-radius: 10px; font-size: 15px; color: #1d1d1f; background: rgba(255, 255, 255, 0.8); transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); box-sizing: border-box; } .download-confirm-dialog input:focus { outline: none; border-color: #0071e3; box-shadow: 0 0 0 4px rgba(0, 113, 227, 0.15); background: #ffffff; } .download-confirm-dialog .buttons { margin-top: 28px; display: flex; gap: 12px; justify-content: center; } .download-confirm-dialog button { min-width: 128px; padding: 12px 24px; border: none; border-radius: 10px; cursor: pointer; font-size: 14px; font-weight: 500; letter-spacing: -0.016em; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); } .download-confirm-dialog .confirm-btn { background: #0071e3; color: white; transform: scale(1); } .download-confirm-dialog .confirm-btn:hover { background: #0077ED; transform: scale(1.02); } .download-confirm-dialog .confirm-btn:active { transform: scale(0.98); } .download-confirm-dialog .confirm-btn:disabled { background: #999999; cursor: not-allowed; opacity: 0.7; transform: scale(1); } .download-confirm-dialog .cancel-btn { background: rgba(0, 0, 0, 0.05); color: #1d1d1f; backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); } .download-confirm-dialog .cancel-btn:hover { background: rgba(0, 0, 0, 0.1); } .download-confirm-dialog .cancel-btn:active { background: rgba(0, 0, 0, 0.15); } .download-confirm-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.4); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); z-index: 9999; animation: overlayShow 0.3s ease-out; } @media (prefers-color-scheme: dark) { .download-confirm-dialog { background: rgba(40, 40, 45, 0.8); } .download-confirm-dialog h3 { color: #ffffff; } .download-confirm-dialog p { color: #98989d; } .download-confirm-dialog label { color: #ffffff; } .download-confirm-dialog input { background: rgba(60, 60, 65, 0.8); border-color: rgba(255, 255, 255, 0.1); color: #ffffff; } .download-confirm-dialog input:focus { background: rgba(70, 70, 75, 0.8); } .download-confirm-dialog .cancel-btn { background: rgba(255, 255, 255, 0.1); color: #ffffff; } .download-confirm-dialog .cancel-btn:hover { background: rgba(255, 255, 255, 0.15); } } .download-confirm-dialog .tip { font-size: 12px; color: #86868b; margin-top: 6px; text-align: left; } .download-confirm-dialog .progress-text { margin-top: 12px; font-size: 13px; color: #1d1d1f; letter-spacing: -0.016em; } .download-confirm-dialog .success-icon { display: inline-block; width: 16px; height: 16px; border-radius: 50%; background: #00c853; position: relative; margin-right: 6px; transform: scale(0); transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .download-confirm-dialog .success-icon:after { content: ''; position: absolute; width: 8px; height: 4px; border: 2px solid white; border-top: 0; border-right: 0; transform: rotate(-45deg); top: 4px; left: 4px; } .download-confirm-dialog .success-icon.show { transform: scale(1); } @media (prefers-color-scheme: dark) { .download-confirm-dialog .tip { color: #98989d; } .download-confirm-dialog .progress-text { color: #ffffff; } } .floating-tip { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%) translateY(100px); background: rgba(0, 0, 0, 0.8); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); padding: 12px 20px; border-radius: 10px; color: white; font-size: 14px; z-index: 9999; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); opacity: 0; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Arial, sans-serif; display: flex; align-items: center; gap: 8px; pointer-events: none; } .floating-tip.show { transform: translateX(-50%) translateY(0); opacity: 1; } .floating-tip .icon { width: 18px; height: 18px; background: #fff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 12px; color: #000; } @media (prefers-color-scheme: dark) { .floating-tip { background: rgba(255, 255, 255, 0.9); color: #1d1d1f; } .floating-tip .icon { background: #1d1d1f; color: #fff; } } .usage-tip { position: fixed; top: 20px; left: 50%; transform: translateX(-50%) translateY(-100px); background: rgba(0, 0, 0, 0.9); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); padding: 16px 24px; border-radius: 12px; color: white; font-size: 15px; line-height: 1.4; z-index: 9999; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); opacity: 0; transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1); font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Arial, sans-serif; display: flex; align-items: center; gap: 12px; cursor: pointer; max-width: 90%; width: auto; border: 1px solid rgba(255, 255, 255, 0.1); } .usage-tip.show { transform: translateX(-50%) translateY(0); opacity: 1; } .usage-tip .icon { font-size: 24px; flex-shrink: 0; } .usage-tip .content { display: flex; flex-direction: column; gap: 4px; } .usage-tip .main-text { font-weight: 500; } .usage-tip .contact { font-size: 13px; color: rgba(255, 255, 255, 0.8); } @media (prefers-color-scheme: dark) { .usage-tip { background: rgba(255, 255, 255, 0.95); color: #1d1d1f; border: 1px solid rgba(0, 0, 0, 0.1); } .usage-tip .contact { color: rgba(0, 0, 0, 0.6); } } .remaining-time { background: rgba(0, 0, 0, 0.03); padding: 8px 12px; border-radius: 8px; text-align: center; } @media (prefers-color-scheme: dark) { .remaining-time { background: rgba(255, 255, 255, 0.05); color: #98989d; } } `; document.head.appendChild(style); // 获取当前网站域名 const currentDomain = window.location.hostname; // 添加创建下载确认对话框的函数 function createConfirmDialog(mediaUrl, mediaType, downloadFunction) { const overlay = document.createElement('div'); overlay.className = 'download-confirm-overlay'; const dialog = document.createElement('div'); dialog.className = 'download-confirm-dialog'; // 获取当前日期时间作为默认文件名的备选 const now = new Date(); const dateStr = `${now.getFullYear()}${(now.getMonth()+1).toString().padStart(2,'0')}${now.getDate().toString().padStart(2,'0')}`; // 获取提示词 let promptText = ''; let promptElement; if (mediaType === 'video') { promptElement = document.querySelector('span.lv-typography[class*="promptText-sTGKI"]'); } else { promptElement = document.querySelector('span.lv-typography[class*="promptText-"]') || document.querySelector('.message-text-aF_36u[data-testid="message_text_content"]'); } if (promptElement) { let text = promptElement.textContent.trim(); if (text.startsWith('帮我生成图片:')) { text = text.replace('帮我生成图片:', ''); } promptText = text .replace(/\s+/g, ' ') .replace(/^[""]-|[""]$/g, '') .replace(/[\\/:*?"<>|]/g, '_') .replace(/比例["\d+:\d+"]/, '') .trim() .substring(0, 100); } // 默认文件名使用提示词,如果没有提示词则使用日期 const defaultFileName = promptText || dateStr; dialog.innerHTML = `

下载${mediaType === 'video' ? '视频' : '图片'}

请确认下载信息

提示:右键点击${mediaType === 'video' ? '视频' : '图片'}即可下载,文件名将自动使用AI提示词
`; document.body.appendChild(overlay); document.body.appendChild(dialog); const confirmBtn = dialog.querySelector('.confirm-btn'); const cancelBtn = dialog.querySelector('.cancel-btn'); const fileNameInput = dialog.querySelector('#fileName'); const progressText = dialog.querySelector('.progress-text'); const statusText = dialog.querySelector('.status-text'); const successIcon = dialog.querySelector('.success-icon'); function closeDialog() { document.body.removeChild(overlay); document.body.removeChild(dialog); } function handleDownloadProgress(percent) { if (percent) { progressText.style.display = 'block'; statusText.textContent = `正在下载...${percent}%`; } } function handleDownloadSuccess() { confirmBtn.style.display = 'none'; progressText.style.display = 'block'; successIcon.classList.add('show'); statusText.textContent = '下载完成'; setTimeout(() => { closeDialog(); }, 1500); } function handleDownloadError(error) { progressText.style.display = 'block'; statusText.textContent = `下载失败: ${error}`; statusText.style.color = '#ff3b30'; confirmBtn.disabled = false; confirmBtn.textContent = '重试'; } confirmBtn.addEventListener('click', () => { confirmBtn.disabled = true; confirmBtn.textContent = '准备下载...'; const customFileName = fileNameInput.value.trim(); downloadFunction( mediaUrl, handleDownloadSuccess, customFileName, handleDownloadProgress, handleDownloadError ); }); cancelBtn.addEventListener('click', closeDialog); } // 处理视频URL,移除水印 function processVideoUrl(url) { try { if (url.includes('vlabvod.com')) { const urlObj = new URL(url); const paramsToRemove = [ 'lr', 'watermark', 'display_watermark_busi_user', 'cd', 'cs', 'ds', 'ft', 'btag', 'dy_q', 'feature_id' ]; paramsToRemove.forEach(param => { urlObj.searchParams.delete(param); }); if (urlObj.searchParams.has('br')) { const br = parseInt(urlObj.searchParams.get('br')); urlObj.searchParams.set('br', Math.max(br, 6000).toString()); urlObj.searchParams.set('bt', Math.max(br, 6000).toString()); } urlObj.searchParams.delete('l'); return urlObj.toString(); } return url; } catch (e) { console.error('处理视频URL时出错:', e); return url; } } // 获取真实视频URL async function getRealVideoUrl(videoElement) { let videoUrl = videoElement.src; if (!videoUrl) { const sourceElement = videoElement.querySelector('source'); if (sourceElement) { videoUrl = sourceElement.src; } } if (!videoUrl) { videoUrl = videoElement.getAttribute('data-src'); } return videoUrl; } // 获取文件扩展名 function getFileExtension(url) { const extension = url.split('?')[0].match(/\.(jpg|jpeg|png|gif|mp4|webm)$/i); return extension ? extension[0] : '.jpg'; } // 下载图片的函数 function downloadImage(imageUrl, onSuccess, customFileName, onProgress, onError) { const fileExtension = getFileExtension(imageUrl); const fileName = customFileName ? `${customFileName}${fileExtension}` : getFileNameFromUrl(imageUrl); GM_xmlhttpRequest({ method: 'GET', url: imageUrl, responseType: 'blob', headers: { 'Accept': 'image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', 'Referer': currentDomain.includes('doubao') ? 'https://www.doubao.com/' : 'https://jimeng.jianying.com/', 'Origin': currentDomain.includes('doubao') ? 'https://www.doubao.com' : 'https://jimeng.jianying.com', 'User-Agent': navigator.userAgent }, onprogress: function(progress) { if (progress.lengthComputable) { const percent = Math.round((progress.loaded / progress.total) * 100); if (onProgress) onProgress(percent); } }, onload: function(response) { if (response.status === 200) { const blob = response.response; const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); setTimeout(() => URL.revokeObjectURL(url), 100); if (onSuccess) onSuccess(); } else { if (onError) onError(`HTTP ${response.status}`); } }, onerror: function(error) { if (onError) onError(error.message || '网络错误'); } }); } // 下载视频的函数 function downloadVideo(videoUrl, onSuccess, customFileName, onProgress, onError) { const processedUrl = processVideoUrl(videoUrl); const fileExtension = '.mp4'; const fileName = customFileName ? `${customFileName}${fileExtension}` : getFileNameFromUrl(processedUrl); GM_xmlhttpRequest({ method: 'GET', url: processedUrl, responseType: 'blob', headers: { 'Accept': 'video/mp4,video/*;q=0.9,*/*;q=0.8', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', 'Range': 'bytes=0-', 'Referer': currentDomain.includes('doubao') ? 'https://www.doubao.com/' : 'https://jimeng.jianying.com/', 'Origin': currentDomain.includes('doubao') ? 'https://www.doubao.com' : 'https://jimeng.jianying.com', 'User-Agent': navigator.userAgent }, onprogress: function(progress) { if (progress.lengthComputable && onProgress) { const percent = Math.round((progress.loaded / progress.total) * 100); onProgress(percent); } }, onload: function(response) { if (response.status === 200 || response.status === 206) { const blob = response.response; const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = fileName; document.body.appendChild(link); link.click(); document.body.removeChild(link); setTimeout(() => URL.revokeObjectURL(url), 100); if (onSuccess) onSuccess(); } else { if (onError) onError(`HTTP ${response.status}`); } }, onerror: function(error) { if (onError) onError(error.message || '网络错误'); } }); } // 从 URL 中提取文件名 function getFileNameFromUrl(url) { url = url.split('?')[0]; const urlParts = url.split('/'); let fileName = urlParts[urlParts.length - 1]; if (fileName.includes('~')) { fileName = fileName.split('~')[0]; } if (!fileName.match(/\.(mp4|webm|jpg|jpeg|png)$/i)) { fileName += url.includes('video') ? '.mp4' : '.jpeg'; } return fileName; } // 修改右键菜单事件监听 document.addEventListener('contextmenu', async function (event) { const target = event.target; if (target.tagName.toLowerCase() === 'img') { event.preventDefault(); // 阻止默认右键菜单 const imageUrl = target.src; if (imageUrl) { downloadWithActivationCheck(imageUrl, 'image', (url, onSuccess, fileName, onProgress, onError) => { downloadImage(url, onSuccess, fileName, onProgress, onError); }); } } else if (target.tagName.toLowerCase() === 'video' || target.closest('video')) { event.preventDefault(); // 阻止默认右键菜单 const videoElement = target.tagName.toLowerCase() === 'video' ? target : target.closest('video'); if (videoElement) { const videoUrl = await getRealVideoUrl(videoElement); if (videoUrl) { downloadWithActivationCheck(videoUrl, 'video', (url, onSuccess, fileName, onProgress, onError) => { downloadVideo(url, onSuccess, fileName, onProgress, onError); }); } } } }, true); // 修改显示提示的函数 function showUsageTip() { // 检查上次显示时间 const lastShowTime = localStorage.getItem('last_tip_time'); const now = new Date(); if (lastShowTime) { const lastDate = new Date(lastShowTime); // 如果是同一天,不显示 if (lastDate.toDateString() === now.toDateString()) { return; } } // 创建提示元素 const tip = document.createElement('div'); tip.className = 'usage-tip'; tip.innerHTML = ` 💡
点击图片或视频,单击鼠标右键即可免费下载无水印的图片或视频 有问题联系微信:11208596
`; document.body.appendChild(tip); // 显示提示 setTimeout(() => { tip.classList.add('show'); }, 500); // 10秒后自动隐藏提示 setTimeout(() => { tip.classList.remove('show'); setTimeout(() => { document.body.removeChild(tip); }, 600); }, 10000); // 点击可以提前关闭提示 tip.addEventListener('click', () => { tip.classList.remove('show'); setTimeout(() => { document.body.removeChild(tip); }, 600); }); // 记录显示时间 localStorage.setItem('last_tip_time', now.toISOString()); } // 修改初始化提示的函数 function initUsageTip() { if (window.location.hostname.includes('doubao.com') || window.location.hostname.includes('jimeng.jianying.com')) { // 页面加载完成后显示提示 if (document.readyState === 'complete') { showUsageTip(); } else { window.addEventListener('load', showUsageTip); } // 监听页面可见性变化,但仍然遵循每天显示一次的规则 document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') { showUsageTip(); } }); } } // 初始化提示 initUsageTip(); // 修改测试函数 async function testFeishuAPI() { try { console.log('开始测试飞书API...'); // 1. 测试获取访问令牌 console.log('1. 测试获取访问令牌'); const tokenResponse = await fetch(`${FEISHU_CONFIG.API_URL}/auth/v3/tenant_access_token/internal`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ "app_id": FEISHU_CONFIG.APP_ID, "app_secret": FEISHU_CONFIG.APP_SECRET }) }); const tokenData = await tokenResponse.json(); console.log('访问令牌响应:', tokenData); if (tokenData.code === 0) { FEISHU_CONFIG.TOKEN = tokenData.tenant_access_token; // 2. 测试查询表格 console.log('2. 测试查询表格'); const tableResponse = await fetch(`${FEISHU_CONFIG.API_URL}/bitable/v1/apps/${FEISHU_CONFIG.BASE_ID}/tables/${FEISHU_CONFIG.TABLE_ID}/records?page_size=1`, { method: 'GET', headers: { 'Authorization': `Bearer ${FEISHU_CONFIG.TOKEN}`, 'Content-Type': 'application/json' } }); const tableData = await tableResponse.json(); console.log('表格数据响应:', tableData); if (tableData.code === 0) { return '测试成功,API正常工作'; } else { return `表格查询失败: ${tableData.msg}`; } } else { return `获取访问令牌失败: ${tokenData.msg}`; } } catch (e) { console.error('测试出错:', e); return `测试失败: ${e.message}`; } } // 修改测试面板的创建方式 function addTestPanel() { // 创建测试面板 const testPanel = document.createElement('div'); testPanel.innerHTML = `
设备ID: ${localStorage.getItem('deviceId') || '未生成'}
`; document.body.appendChild(testPanel); } // 修改验证按钮的事件处理 document.getElementById('verifyCodeBtn').addEventListener('click', async function() { const code = document.getElementById('testCode').value.trim(); if (!code) { showFloatingTip('请输入激活码'); return; } const deviceId = localStorage.getItem('deviceId'); if (!deviceId) { showFloatingTip('设备ID未生成'); return; } try { const result = await verifyActivationCode(deviceId, code); showFloatingTip(result ? '验证成功' : '验证失败'); } catch (e) { console.error('验证测试激活码失败:', e); showFloatingTip('验证出错'); } }); })();