feat: 新增多项功能并添加新工具

- 私人剪贴板新增限时直通URL生成功能
- doc_cloud_keeper新增标题复制按钮
- 新增贪蛇出动游戏工具
- 新增OSS中转直链工具
- 添加批量抽帧测试脚本
This commit is contained in:
yangxiangyuan
2026-08-11 10:22:31 +08:00
parent 91148fe064
commit 6dc79a2c12
28 changed files with 3169 additions and 6 deletions
@@ -0,0 +1,31 @@
@echo off
chcp 65001 >nul
setlocal
rem ==========================================
rem oss-browser 一键拉起协议注册脚本
rem 用法:把本文件放到与 oss-browser.exe 同一目录,双击运行
rem 作用:在当前用户注册表(HKCU)注册 ossbrowser:// 协议,无需管理员权限
rem ==========================================
set "EXE=%~dp0oss-browser.exe"
if not exist "%EXE%" (
echo.
echo [错误] 未在当前目录找到 oss-browser.exe
echo 请把本脚本放入解压后的 oss-browser-win32-x64 文件夹内(与 oss-browser.exe 同级)再运行。
echo.
pause
exit /b 1
)
reg add "HKCU\Software\Classes\ossbrowser" /ve /d "URL:ossbrowser Protocol" /f >nul
reg add "HKCU\Software\Classes\ossbrowser" /v "URL Protocol" /t REG_SZ /d "" /f >nul
reg add "HKCU\Software\Classes\ossbrowser\shell\open\command" /ve /d "\"%EXE%\" \"%%1\"" /f >nul
if errorlevel 1 (
echo [失败] 注册表写入出错。
pause
exit /b 1
)
echo.
echo [完成] 已注册 ossbrowser:// 协议,指向:
echo %EXE%
echo 现在可回到网页点击「打开 oss-browser」。首次拉起时浏览器会弹确认框,勾选“始终允许”后即为无感打开。
echo.
pause
@@ -0,0 +1,15 @@
{
"_comment": "OSS中转直链(oss_browser_link)的鉴权配置",
"enable_auth_comment": "true:启用 tool 外层鉴权;false:完全放开访问",
"enable_auth": true,
"max_age_days_comment": "鉴权 cookie 最大有效天数(1~365)",
"max_age_days": 30,
"cookieName_comment": "鉴权 Cookie 名称,默认格式 <tool>_gate",
"cookieName": "oss_browser_link_gate",
"requireGuid_comment": "true:强制 GUID 参数才能访问;false:允许仅凭 token 或已有会话访问",
"requireGuid": false,
"guidParam_comment": "URL 中的 GUID 参数名,建议使用小写 guid",
"guidParam": "guid",
"guidValue_comment": "GUID 参数值,建议使用高强度随机长串并定期更换(留空表示不启用 GUID)",
"guidValue": ""
}
+67
View File
@@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>OSS中转直链</title>
<link rel="stylesheet" href="oss_browser_link.css">
</head>
<body>
<div id="obl-root">
<header id="obl-header">
<h1>☁️ OSS中转直链</h1>
<p id="obl-sub">oss-browser 安装包 · 鉴权下载 · 一键拉起</p>
</header>
<section id="obl-file-card">
<div id="obl-file-icon">📦</div>
<div id="obl-file-info">
<div id="obl-file-name">oss-browser-win32-x64.zip</div>
<div id="obl-file-meta">加载中…</div>
</div>
<span id="obl-file-status" class="obl-status">检测中</span>
</section>
<section id="obl-link-row">
<input id="obl-link-input" readonly value="">
<button id="obl-copy-btn" class="obl-btn ghost">复制直链</button>
</section>
<section id="obl-actions">
<button id="obl-download-btn" class="obl-btn primary">⬇ 下载 oss-browser 安装包</button>
<button id="obl-launch-btn" class="obl-btn green">🚀 打开 oss-browser</button>
<button id="obl-reg-btn" class="obl-btn ghost">📄 下载一键注册脚本(首次)</button>
</section>
<section id="obl-steps">
<h2>使用说明</h2>
<ol>
<li><b>下载</b>:点击上方「下载」获取安装包(直链本身受 Toolbox 鉴权保护,未授权无法下载)。</li>
<li><b>解压</b>:将 zip 解压到一个固定目录(如 <code>D:\oss-browser</code>)。</li>
<li><b>注册协议(仅首次)</b>:下载「一键注册脚本」,放入解压目录(与 oss-browser.exe 同级),双击运行。脚本会在当前用户注册表注册 <code>ossbrowser://</code> 协议,无需管理员权限。</li>
<li><b>一键打开</b>:以后点击「打开 oss-browser」即可直接拉起本地程序(浏览器首次会弹确认框,勾选"始终允许"后不再打扰)。</li>
<li>若检测到未安装,会自动回退为下载安装包。</li>
<li>阿里OSS直链中转。DOC_CLOUD_KEEPER_OSS_ACCESS_KEY_ID是LTAI5t9fraTXxm-dafe-TwE31Ph2ne</li>
<li>DOC_CLOUD_KEEPER_OSS_ACCESS_KEY_SECRET是LBN3ntJfdwlnmCiA9QCed-dafe-Y7XlhxpvX</li>
<li>路径是oss://android-o1-images/trae-dafe-files-temp/</li>
</ol>
</section>
<div id="obl-toast" class="obl-hidden"></div>
<div id="obl-dialog" class="obl-hidden">
<div id="obl-dialog-box">
<div id="obl-dialog-head">
<span id="obl-dialog-title">交互详情</span>
<button id="obl-dialog-copy">复制</button>
<button id="obl-dialog-close">×</button>
</div>
<pre id="obl-dialog-body"></pre>
</div>
</div>
</div>
<script src="oss_browser_link.js"></script>
</body>
</html>
@@ -0,0 +1,122 @@
/* OSS中转直链 oss_browser_link 样式 */
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; }
body {
font-family: "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
background: linear-gradient(160deg, #10162e 0%, #1a2450 60%, #10162e 100%);
color: #e8ecff;
min-height: 100vh;
display: flex;
justify-content: center;
padding: 24px 16px calc(24px + env(safe-area-inset-bottom));
}
#obl-root { width: 100%; max-width: 640px; }
#obl-header h1 { font-size: 24px; letter-spacing: 1px; }
#obl-sub { color: #8f9ac8; font-size: 13px; margin-top: 6px; }
#obl-file-card {
display: flex; align-items: center; gap: 14px;
background: rgba(255,255,255,.06);
border: 1px solid rgba(255,255,255,.1);
border-radius: 14px;
padding: 18px;
margin-top: 20px;
}
#obl-file-icon { font-size: 40px; }
#obl-file-info { flex: 1; min-width: 0; }
#obl-file-name { font-weight: 600; font-size: 15px; word-break: break-all; }
#obl-file-meta { color: #8f9ac8; font-size: 12px; margin-top: 5px; }
.obl-status {
font-size: 12px; padding: 4px 10px; border-radius: 999px; white-space: nowrap;
background: rgba(255,255,255,.1); color: #aab4e0;
}
.obl-status.ok { background: rgba(46,204,113,.16); color: #4be08a; }
.obl-status.miss { background: rgba(255,120,120,.16); color: #ff8f8f; }
#obl-link-row { display: flex; gap: 8px; margin-top: 14px; }
#obl-link-input {
flex: 1; min-width: 0;
background: rgba(0,0,0,.3);
border: 1px solid rgba(255,255,255,.14);
border-radius: 10px;
color: #b9c3ef;
font-size: 12px;
padding: 10px 12px;
outline: none;
}
.obl-btn {
border: none; border-radius: 10px; cursor: pointer;
font-size: 14px; padding: 12px 16px; color: #fff;
transition: opacity .15s, transform .05s;
-webkit-tap-highlight-color: transparent;
}
.obl-btn:active { transform: scale(.97); }
.obl-btn:disabled { opacity: .5; cursor: not-allowed; }
.obl-btn.primary { background: linear-gradient(135deg, #3d7bff, #5a4dff); }
.obl-btn.green { background: linear-gradient(135deg, #20b26a, #17a05e); }
.obl-btn.ghost {
background: transparent; color: #aab4e0;
border: 1px solid rgba(255,255,255,.22);
}
#obl-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 18px; }
#obl-link-row .obl-btn { white-space: nowrap; }
#obl-steps {
margin-top: 22px;
background: rgba(255,255,255,.04);
border: 1px solid rgba(255,255,255,.08);
border-radius: 14px;
padding: 16px 18px;
}
#obl-steps h2 { font-size: 15px; margin-bottom: 10px; color: #cfd7ff; }
#obl-steps ol { padding-left: 20px; }
#obl-steps li { font-size: 13px; color: #9aa5d2; line-height: 1.9; }
#obl-steps code {
background: rgba(0,0,0,.35); padding: 1px 6px; border-radius: 5px;
font-size: 12px; color: #7fd0ff;
}
/* Toast */
#obl-toast {
position: fixed; left: 50%; top: 18%; transform: translateX(-50%);
background: #2d3a6e; color: #fff; font-size: 14px;
padding: 10px 18px; border-radius: 10px; z-index: 60;
box-shadow: 0 6px 24px rgba(0,0,0,.4);
max-width: 86vw;
}
#obl-toast.success { background: #1d9e58; }
#obl-toast.error { background: #d84a4a; }
/* 交互详情弹窗(双模式) */
#obl-dialog {
position: fixed; inset: 0; background: rgba(0,0,0,.55);
display: flex; align-items: center; justify-content: center;
z-index: 100; padding: 20px;
}
#obl-dialog-box {
width: 100%; max-width: 560px; max-height: 78vh;
background: #182047; border: 1px solid rgba(255,255,255,.14);
border-radius: 14px; display: flex; flex-direction: column;
}
#obl-dialog-head {
display: flex; align-items: center; gap: 10px;
padding: 12px 14px; border-bottom: 1px solid rgba(255,255,255,.1);
}
#obl-dialog-title { flex: 1; font-size: 14px; font-weight: 600; }
#obl-dialog-head button {
background: rgba(255,255,255,.1); border: none; color: #dfe5ff;
border-radius: 8px; padding: 6px 12px; cursor: pointer; font-size: 13px;
}
#obl-dialog-body {
padding: 12px 14px; overflow: auto; flex: 1;
font-size: 12px; line-height: 1.7; color: #b9c3ef; white-space: pre-wrap; word-break: break-all;
}
.obl-hidden { display: none !important; }
@media (min-width: 720px) {
#obl-actions { flex-direction: row; flex-wrap: wrap; }
#obl-actions .obl-btn { flex: 1; }
}
@@ -0,0 +1,236 @@
/* OSS中转直链 oss_browser_link 前端逻辑(双模式弹窗 / 交互详情 / 协议拉起) */
;(function () {
'use strict'
var CFG = { ui_mode: 'debug', public_url: '', launch_protocol: 'ossbrowser' }
function $ (id) { return document.getElementById(id) }
function bjNow () {
try { return new Date().toLocaleString('sv-SE', { timeZone: 'Asia/Shanghai' }) } catch (e) { return new Date().toLocaleString() }
}
/* ---------- Toast ---------- */
var toastTimer = null
function showToast (msg, kind) {
var el = $('obl-toast')
el.textContent = msg
el.className = kind || ''
clearTimeout(toastTimer)
toastTimer = setTimeout(function () { el.className = 'obl-hidden' }, 2000)
}
/* ---------- 交互详情弹窗(双模式) ---------- */
var dialogTimer = null
function showDialog (title, text, opts) {
opts = opts || {}
$('obl-dialog-title').textContent = title
$('obl-dialog-body').textContent = text
$('obl-dialog').classList.remove('obl-hidden')
clearTimeout(dialogTimer)
if (opts.error) return // 异常:停留窗口,不自动关闭
var delay = CFG.ui_mode === 'debug' ? 3000 : 2000
dialogTimer = setTimeout(closeDialog, delay)
}
function closeDialog () {
clearTimeout(dialogTimer)
$('obl-dialog').classList.add('obl-hidden')
}
function buildLog (rec) {
return [
'触发动作: ' + rec.action,
'时间: ' + rec.time,
'接口: ' + rec.method + ' ' + rec.url,
'请求参数: ' + JSON.stringify(rec.params || {}),
'状态: ' + rec.status,
'返回结果: ' + rec.result,
rec.error ? '错误信息: ' + rec.error : '',
'耗时: ' + rec.cost + 'ms'
].filter(Boolean).join('\n')
}
/* 包装一次与后台/系统交互的操作,按 ui_mode 分支提示 */
function runTracked (action, url, method, params, fn, opts) {
opts = opts || {}
var t0 = Date.now()
var rec = { action: action, time: bjNow(), url: url, method: method, params: params, status: '进行中', result: '', error: '', cost: 0 }
return Promise.resolve()
.then(fn)
.then(function (result) {
rec.cost = Date.now() - t0
rec.status = '成功'
rec.result = typeof result === 'string' ? result : JSON.stringify(result)
if (CFG.ui_mode === 'debug') showDialog('交互详情 · ' + action, buildLog(rec))
else if (!opts.silent) showToast(action + ' 成功', 'success')
return result
})
.catch(function (err) {
rec.cost = Date.now() - t0
rec.status = '失败'
rec.error = String((err && err.message) || err)
showDialog('交互详情 · ' + action, buildLog(rec), { error: true }) // 异常双模式均强提示
throw err
})
}
/* ---------- 初始化:拉配置 + 文件状态 ---------- */
function init () {
fetch('/api/oss_browser_link/config').then(function (r) { return r.json() }).then(function (j) {
if (j && j.ok && j.data) {
CFG.ui_mode = j.data.ui_mode || 'debug'
CFG.public_url = j.data.public_url || ''
CFG.launch_protocol = j.data.launch_protocol || 'ossbrowser'
}
}).catch(function () {})
$('obl-link-input').value = CFG.public_url || location.origin + '/download_files/oss-browser-win32-x64.zip'
refreshFileInfo()
bindEvents()
}
function fmtSize (n) {
if (!Number.isFinite(n)) return ''
if (n >= 1024 * 1024) return (n / 1024 / 1024).toFixed(2) + ' MB'
if (n >= 1024) return (n / 1024).toFixed(1) + ' KB'
return n + ' B'
}
function refreshFileInfo () {
var t0 = Date.now()
fetch('/api/oss_browser_link/info').then(function (r) { return r.json() }).then(function (j) {
var meta = $('obl-file-meta')
var st = $('obl-file-status')
var cost = Date.now() - t0
if (j && j.ok && j.data && j.data.available) {
meta.textContent = '大小 ' + fmtSize(j.data.size) + ' · 已中转至本站 · 鉴权保护'
st.textContent = '可下载'
st.className = 'obl-status ok'
$('obl-download-btn').disabled = false
} else {
meta.textContent = '尚未检测到中转文件 · 请等待管理员中转后刷新'
st.textContent = '未就绪'
st.className = 'obl-status miss'
$('obl-download-btn').disabled = true
}
if (CFG.ui_mode === 'debug') {
meta.textContent += ' · 检测耗时 ' + cost + 'ms'
}
}).catch(function () {
$('obl-file-meta').textContent = '文件状态检测失败'
$('obl-file-status').textContent = '异常'
$('obl-file-status').className = 'obl-status miss'
})
}
/* ---------- 按钮事件 ---------- */
function bindEvents () {
$('obl-copy-btn').addEventListener('click', function () {
var url = $('obl-link-input').value
var btn = this
btn.disabled = true; btn.textContent = '复制中…'
runTracked('复制直链', location.href, 'CLIPBOARD', { url: url }, function () {
if (navigator.clipboard && navigator.clipboard.writeText) return navigator.clipboard.writeText(url)
$('obl-link-input').select()
document.execCommand('copy')
return 'fallback copy ok'
}).then(function () {
showToast('直链已复制(对方需鉴权才能下载)', 'success')
}).catch(function () {})
btn.disabled = false; btn.textContent = '复制直链'
})
$('obl-download-btn').addEventListener('click', function () {
var btn = this
btn.disabled = true; btn.textContent = '准备下载…'
runTracked('下载安装包', '/download_files/oss-browser-win32-x64.zip', 'GET', {}, function () {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest()
xhr.open('GET', '/download_files/oss-browser-win32-x64.zip')
xhr.responseType = 'blob'
xhr.onload = function () {
if (xhr.status !== 200) return reject(new Error('HTTP ' + xhr.status))
var a = document.createElement('a')
a.href = URL.createObjectURL(xhr.response)
a.download = 'oss-browser-win32-x64.zip'
document.body.appendChild(a)
a.click()
setTimeout(function () { URL.revokeObjectURL(a.href); a.remove() }, 4000)
resolve('下载已触发,大小 ' + (xhr.response ? xhr.response.size : 0) + ' 字节')
}
xhr.onerror = function () { reject(new Error('网络错误')) }
xhr.send()
})
}).catch(function () {})
btn.disabled = false; btn.textContent = '⬇ 下载 oss-browser 安装包'
})
$('obl-launch-btn').addEventListener('click', function () {
launchOssBrowser()
})
$('obl-reg-btn').addEventListener('click', function () {
var btn = this
btn.disabled = true; btn.textContent = '下载中…'
runTracked('下载注册脚本', '/tools/oss_browser_link/assets/register_ossbrowser_launch.bat', 'GET', {}, function () {
var a = document.createElement('a')
a.href = '/tools/oss_browser_link/assets/register_ossbrowser_launch.bat'
a.download = 'register_ossbrowser_launch.bat'
document.body.appendChild(a)
a.click()
setTimeout(function () { a.remove() }, 2000)
return '脚本下载已触发(放入解压目录后双击运行)'
}).catch(function () {})
btn.disabled = false; btn.textContent = '📄 下载一键注册脚本(首次)'
})
$('obl-dialog-close').addEventListener('click', closeDialog)
$('obl-dialog-copy').addEventListener('click', function () {
var text = $('obl-dialog-body').textContent
if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text)
showToast('交互日志已复制', 'success')
})
}
/* ---------- 协议拉起:未装则回退下载 ---------- */
function launchOssBrowser () {
var proto = CFG.launch_protocol || 'ossbrowser'
var url = proto + '://launch?t=' + Date.now()
var btn = $('obl-launch-btn')
btn.disabled = true; btn.textContent = '正在打开…'
runTracked('打开 oss-browser', url, 'PROTOCOL', { protocol: proto }, function () {
return new Promise(function (resolve, reject) {
var done = false
var started = false
/* 拉起成功时浏览器通常会模糊/隐藏页面 */
window.addEventListener('blur', onBlur)
document.addEventListener('visibilitychange', onVis)
function onBlur () { started = true }
function onVis () { if (document.hidden) started = true }
try {
var f = document.createElement('iframe')
f.style.display = 'none'
f.src = url
document.body.appendChild(f)
setTimeout(function () { try { f.remove() } catch (e) {} }, 3000)
} catch (e) { location.href = url }
setTimeout(function () {
window.removeEventListener('blur', onBlur)
document.removeEventListener('visibilitychange', onVis)
if (done) return
done = true
if (started) resolve('已触发协议拉起')
else reject(new Error('未检测到已安装的 oss-browser(协议未注册)'))
}, 2500)
})
}).then(function () {
showToast('已尝试打开 oss-browser;若浏览器弹出确认框请勾选"始终允许"', 'success')
}).catch(function () {
/* 未安装:自动回退下载安装包 */
showToast('未检测到 oss-browser,已自动开始下载安装包', 'error')
setTimeout(function () { $('obl-download-btn').click() }, 600)
})
setTimeout(function () { btn.disabled = false; btn.textContent = '🚀 打开 oss-browser' }, 3200)
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init)
else init()
})()