// ==UserScript==
// @name AutoDL 自动刷新实例 - 防止回收
// @namespace http://tampermonkey.net/
// @version 1.0.0
// @description 自动刷新AutoDL平台长时间关机的实例,防止被平台回收。支持自定义天数阈值和批量操作。
// @author AutoDL助手
// @match https://www.autodl.com/*
// @icon https://www.autodl.com/favicon.ico
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_addStyle
// @grant GM_notification
// @grant GM_xmlhttpRequest
// @run-at document-end
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 添加样式
GM_addStyle(`
.autodl-refresh-panel {
position: fixed;
top: 20px;
right: 20px;
width: 350px;
background: white;
border: 2px solid #007bff;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10000;
font-family: 'Microsoft YaHei', Arial, sans-serif;
cursor: move;
user-select: none;
}
.autodl-refresh-panel.dragging {
opacity: 0.8;
box-shadow: 0 8px 24px rgba(0,0,0,0.25);
}
.autodl-refresh-header {
background: linear-gradient(135deg, #007bff, #0056b3);
color: white;
padding: 12px 15px;
border-radius: 8px 8px 0 0;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
cursor: move;
}
.autodl-refresh-close {
background: none;
border: none;
color: white;
font-size: 18px;
cursor: pointer;
padding: 0;
width: 20px;
height: 20px;
cursor: pointer;
}
.autodl-refresh-content {
padding: 15px;
cursor: default;
}
.autodl-refresh-input-group {
margin-bottom: 12px;
}
.autodl-refresh-input-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #333;
}
.autodl-refresh-input-group input {
width: 100%;
padding: 8px 10px;
border: 1px solid #ddd;
border-radius: 5px;
box-sizing: border-box;
font-size: 14px;
cursor: text;
}
.autodl-refresh-btn {
width: 100%;
padding: 10px;
background: linear-gradient(135deg, #28a745, #20c997);
color: white;
border: none;
border-radius: 5px;
font-size: 14px;
font-weight: bold;
cursor: pointer;
margin-bottom: 8px;
transition: all 0.3s ease;
}
.autodl-refresh-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
.autodl-refresh-btn:disabled {
background: #6c757d;
cursor: not-allowed;
transform: none;
}
.autodl-refresh-log {
max-height: 200px;
overflow-y: auto;
border: 1px solid #ddd;
border-radius: 5px;
padding: 10px;
background: #f8f9fa;
font-size: 12px;
line-height: 1.4;
}
.autodl-refresh-log-item {
margin-bottom: 5px;
padding: 3px 0;
}
.autodl-refresh-log-success {
color: #28a745;
}
.autodl-refresh-log-error {
color: #dc3545;
}
.autodl-refresh-log-warning {
color: #ffc107;
}
.autodl-refresh-log-info {
color: #17a2b8;
}
.autodl-refresh-log-debug {
color: #6c757d;
font-style: italic;
}
.autodl-refresh-status {
text-align: center;
padding: 8px;
margin: 8px 0;
border-radius: 5px;
font-weight: bold;
}
.autodl-refresh-status.running {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.autodl-refresh-status.completed {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.autodl-refresh-status.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.autodl-refresh-debug-info {
background: #e9ecef;
border: 1px solid #dee2e6;
border-radius: 5px;
padding: 8px;
margin: 8px 0;
font-size: 11px;
color: #495057;
}
.autodl-confirm-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 20000;
}
.autodl-confirm-content {
background: white;
border-radius: 10px;
padding: 25px;
max-width: 500px;
width: 90%;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
text-align: center;
}
.autodl-confirm-title {
font-size: 18px;
font-weight: bold;
color: #dc3545;
margin-bottom: 15px;
}
.autodl-confirm-message {
font-size: 14px;
line-height: 1.6;
color: #333;
margin-bottom: 20px;
text-align: left;
}
.autodl-confirm-instances {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 5px;
padding: 10px;
margin: 10px 0;
font-family: monospace;
font-size: 12px;
color: #495057;
}
.autodl-confirm-note {
font-size: 12px;
color: #6c757d;
font-style: italic;
margin-top: 10px;
}
.autodl-confirm-buttons {
display: flex;
gap: 15px;
justify-content: center;
margin-top: 20px;
}
.autodl-confirm-btn {
padding: 10px 25px;
border: none;
border-radius: 5px;
font-size: 14px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
}
.autodl-confirm-btn.confirm {
background: linear-gradient(135deg, #dc3545, #c82333);
color: white;
}
.autodl-confirm-btn.confirm:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(220, 53, 69, 0.3);
}
.autodl-confirm-btn.cancel {
background: linear-gradient(135deg, #6c757d, #5a6268);
color: white;
}
.autodl-confirm-btn.cancel:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(108, 117, 125, 0.3);
}
`);
// AutoDL API 基础URL
const BASE_URL = "https://www.autodl.com/api/v1";
// API 请求头
const HEADERS = {
"Content-Type": "application/json;charset=UTF-8",
"Accept": "*/*",
"appversion": "v5.95.2",
"Origin": "https://www.autodl.com",
"Referer": "https://www.autodl.com/login"
};
// 调试模式开关
const DEBUG_MODE = true;
// 调试日志函数
function debugLog(message, data = null) {
if (DEBUG_MODE) {
const timestamp = new Date().toLocaleTimeString();
console.log(`[AutoDL Debug ${timestamp}] ${message}`);
if (data) {
console.log('Debug Data:', data);
}
}
}
// SHA1 哈希函数
async function sha1Hash(text) {
try {
debugLog(`开始对密码进行SHA1加密...`);
const encoder = new TextEncoder();
const data = encoder.encode(text);
const hashBuffer = await crypto.subtle.digest('SHA-1', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hash = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
debugLog(`密码SHA1加密完成: ${hash.substring(0, 8)}...`);
return hash;
} catch (error) {
debugLog(`SHA1加密失败: ${error.message}`, error);
throw error;
}
}
// 登录并获取token
async function loginAndGetToken(phone, plaintextPassword) {
try {
debugLog(`开始登录流程,手机号: ${phone}`);
// 对明文密码进行SHA1加密
const encryptedPassword = await sha1Hash(plaintextPassword);
// 步骤1: 登录获取 ticket
addLog("正在进行登录...", "info");
debugLog(`发送登录请求到: ${BASE_URL}/new_login`);
const loginPayload = {
phone: phone,
password: encryptedPassword,
v_code: "",
phone_area: "+86",
picture_id: null
};
debugLog('登录请求参数:', loginPayload);
const loginResponse = await fetch(`${BASE_URL}/new_login`, {
method: 'POST',
headers: HEADERS,
body: JSON.stringify(loginPayload)
});
debugLog(`登录响应状态: ${loginResponse.status}`);
const loginData = await loginResponse.json();
debugLog('登录响应数据:', loginData);
if (loginData.code !== "Success") {
throw new Error(`登录失败: ${loginData.msg || '未知错误'}`);
}
const ticket = loginData.data.ticket;
addLog(`✅ 成功获取到 Ticket: ${ticket}`, "success");
debugLog(`获取到Ticket: ${ticket}`);
// 步骤2: 使用 ticket 换取 token
addLog("正在换取Token...", "info");
debugLog(`发送passport请求到: ${BASE_URL}/passport`);
const passportPayload = { ticket: ticket };
debugLog('Passport请求参数:', passportPayload);
const passportResponse = await fetch(`${BASE_URL}/passport`, {
method: 'POST',
headers: HEADERS,
body: JSON.stringify(passportPayload)
});
debugLog(`Passport响应状态: ${passportResponse.status}`);
const passportData = await passportResponse.json();
debugLog('Passport响应数据:', passportData);
if (passportData.code !== "Success") {
throw new Error(`换取 Token 失败: ${passportData.msg || '未知错误'}`);
}
const token = passportData.data.token;
addLog("🎉 成功获取到 Token!", "success");
debugLog(`获取到Token: ${token.substring(0, 20)}...`);
return token;
} catch (error) {
debugLog(`登录过程出错: ${error.message}`, error);
addLog(`❌ 登录失败: ${error.message}`, "error");
throw error;
}
}
// 获取所有计算实例的列表
async function getInstances(headers) {
try {
debugLog(`开始获取实例列表...`);
addLog("正在获取实例列表...", "info");
const response = await fetch(`${BASE_URL}/instance`, {
method: 'POST',
headers: headers,
body: JSON.stringify({})
});
debugLog(`获取实例响应状态: ${response.status}`);
const data = await response.json();
debugLog('实例列表响应数据:', data);
if (data.code === "Success") {
const instances = data.data.list;
debugLog(`成功获取到 ${instances.length} 个实例`);
addLog(`✅ 成功获取到 ${instances.length} 个实例`, "success");
return instances;
} else {
throw new Error(`获取实例列表失败: ${JSON.stringify(data)}`);
}
} catch (error) {
debugLog(`获取实例列表失败: ${error.message}`, error);
addLog(`❌ 获取实例列表失败: ${error.message}`, "error");
throw error;
}
}
// 开启指定的计算实例
async function powerOnInstance(headers, instanceUuid) {
try {
debugLog(`开始开机实例: ${instanceUuid}`);
addLog(`正在开机实例 ${instanceUuid}...`, "info");
const payload = {
instance_uuid: instanceUuid,
payload: "non_gpu"
};
debugLog('开机请求参数:', payload);
const response = await fetch(`${BASE_URL}/instance/power_on`, {
method: 'POST',
headers: headers,
body: JSON.stringify(payload)
});
debugLog(`开机响应状态: ${response.status}`);
const data = await response.json();
debugLog('开机响应数据:', data);
if (data.code === "Success") {
addLog(`实例 ${instanceUuid} 成功开机。`, "success");
debugLog(`实例 ${instanceUuid} 开机成功`);
} else {
throw new Error(`开机失败: ${JSON.stringify(data)}`);
}
} catch (error) {
debugLog(`开机失败 ${instanceUuid}: ${error.message}`, error);
addLog(`❌ 开机失败 ${instanceUuid}: ${error.message}`, "error");
throw error;
}
}
// 关闭指定的计算实例
async function powerOffInstance(headers, instanceUuid) {
try {
debugLog(`开始关机实例: ${instanceUuid}`);
addLog(`正在关机实例 ${instanceUuid}...`, "info");
const payload = {
instance_uuid: instanceUuid
};
debugLog('关机请求参数:', payload);
const response = await fetch(`${BASE_URL}/instance/power_off`, {
method: 'POST',
headers: headers,
body: JSON.stringify(payload)
});
debugLog(`关机响应状态: ${response.status}`);
const data = await response.json();
debugLog('关机响应数据:', data);
if (data.code === "Success") {
addLog(`实例 ${instanceUuid} 成功关机。`, "success");
debugLog(`实例 ${instanceUuid} 关机成功`);
} else {
throw new Error(`关机失败: ${JSON.stringify(data)}`);
}
} catch (error) {
debugLog(`关机失败 ${instanceUuid}: ${error.message}`, error);
addLog(`❌ 关机失败 ${instanceUuid}: ${error.message}`, "error");
throw error;
}
}
// 等待函数
function sleep(ms) {
debugLog(`等待 ${ms} 毫秒...`);
return new Promise(resolve => setTimeout(resolve, ms));
}
// 添加日志
function addLog(message, type = "info") {
const timestamp = new Date().toLocaleTimeString();
const logMessage = `[${timestamp}] ${message}`;
// 添加到控制面板
const logContainer = document.getElementById('autodl-refresh-log');
if (logContainer) {
const logItem = document.createElement('div');
logItem.className = `autodl-refresh-log-item autodl-refresh-log-${type}`;
logItem.textContent = logMessage;
logContainer.appendChild(logItem);
logContainer.scrollTop = logContainer.scrollHeight;
}
// 同时输出到控制台
console.log(`[AutoDL ${type.toUpperCase()}] ${message}`);
}
// 更新状态
function updateStatus(message, type = "info") {
debugLog(`更新状态: ${message} (${type})`);
const statusElement = document.getElementById('autodl-refresh-status');
if (statusElement) {
statusElement.textContent = message;
statusElement.className = `autodl-refresh-status ${type}`;
}
}
// 显示调试信息
function showDebugInfo() {
const debugContainer = document.getElementById('autodl-refresh-debug');
if (debugContainer) {
debugContainer.innerHTML = `
<div class="autodl-refresh-debug-info">
<strong>调试信息:</strong><br>
- 脚本版本: 1.0.0<br>
- 调试模式: ${DEBUG_MODE ? '开启' : '关闭'}<br>
- 当前时间: ${new Date().toLocaleString()}<br>
- 页面URL: ${window.location.href}<br>
- 用户代理: ${navigator.userAgent.substring(0, 50)}...
</div>
`;
}
}
// 显示确认弹框
function showConfirmModal(runningInstances, callback) {
const modal = document.createElement('div');
modal.className = 'autodl-confirm-modal';
const instanceList = runningInstances.map(inst => inst.uuid).join('\n');
modal.innerHTML = `
<div class="autodl-confirm-content">
<div class="autodl-confirm-title">⚠️ 发现无卡模式实例冲突</div>
<div class="autodl-confirm-message">
发现 ${runningInstances.length} 个无卡模式实例正在运行:
<div class="autodl-confirm-instances">${instanceList}</div>
是否关闭这些实例并执行其他符合条件的开关机操作?
</div>
<div class="autodl-confirm-note">
注:刚关闭的实例不会参与后续的开关机操作
</div>
<div class="autodl-confirm-buttons">
<button class="autodl-confirm-btn confirm">确认关闭并执行</button>
<button class="autodl-confirm-btn cancel">取消操作</button>
</div>
</div>
`;
document.body.appendChild(modal);
// 绑定按钮事件
modal.querySelector('.autodl-confirm-btn.confirm').addEventListener('click', () => {
modal.remove();
callback(true); // 用户确认
});
modal.querySelector('.autodl-confirm-btn.cancel').addEventListener('click', () => {
modal.remove();
callback(false); // 用户取消
});
// 点击背景关闭
modal.addEventListener('click', (e) => {
if (e.target === modal) {
modal.remove();
callback(false);
}
});
}
// 检查是否有无卡模式实例运行
function checkRunningNonGpuInstances(instances) {
// 这里需要根据实际的API响应结构调整
// 假设实例对象中有 start_mode 字段表示启动模式
return instances.filter(inst =>
inst.status === "running" &&
(inst.start_mode === "non_gpu" || inst.payload === "non_gpu")
);
}
// 主执行函数
async function executeAutoRefresh() {
debugLog('开始执行自动刷新流程');
console.log('🚀 ===== AutoDL 自动刷新开始 =====');
const phone = document.getElementById('autodl-phone').value.trim();
const password = document.getElementById('autodl-password').value.trim();
const daysThreshold = parseInt(document.getElementById('autodl-days').value.trim());
debugLog(`输入参数 - 手机号: ${phone}, 天数阈值: ${daysThreshold}`);
if (!phone || !password || isNaN(daysThreshold)) {
addLog("❌ 请填写完整的手机号、密码和天数阈值", "error");
console.error("❌ 参数验证失败");
return;
}
const startBtn = document.getElementById('autodl-start-btn');
startBtn.disabled = true;
startBtn.textContent = '执行中...';
try {
updateStatus("正在登录...", "running");
console.log('📱 开始登录流程...');
// 登录并获取 token
const token = await loginAndGetToken(phone, password);
// 使用 token 更新请求头
const authHeaders = { ...HEADERS, "Authorization": token };
debugLog('已更新请求头,包含Authorization token');
updateStatus("正在获取实例列表...", "running");
console.log('📋 开始获取实例列表...');
// 获取实例列表
const instances = await getInstances(authHeaders);
// 检查是否有无卡模式实例运行
const runningNonGpu = checkRunningNonGpuInstances(instances);
const excludedInstances = []; // 记录要排除的实例
if (runningNonGpu.length > 0) {
addLog(`⚠️ 发现 ${runningNonGpu.length} 个无卡模式实例正在运行`, "warning");
console.log('⚠️ 发现无卡模式实例冲突:', runningNonGpu);
// 显示确认弹框
return new Promise((resolve) => {
showConfirmModal(runningNonGpu, async (confirmed) => {
if (!confirmed) {
addLog("❌ 用户取消了操作", "warning");
updateStatus("操作已取消", "error");
startBtn.disabled = false;
startBtn.textContent = '开始执行';
resolve();
return;
}
// 用户确认,继续执行
addLog("✅ 用户确认关闭冲突实例", "success");
// 关闭冲突实例
for (const inst of runningNonGpu) {
try {
addLog(`正在关闭冲突实例: ${inst.uuid}`, "info");
await powerOffInstance(authHeaders, inst.uuid);
excludedInstances.push(inst.uuid);
await sleep(5000); // 等待5秒
} catch (error) {
addLog(`❌ 关闭冲突实例失败: ${inst.uuid}`, "error");
}
}
addLog(`✅ 已关闭 ${excludedInstances.length} 个冲突实例`, "success");
// 继续执行原有的开关机逻辑
await continueRefreshProcess(instances, excludedInstances, daysThreshold, authHeaders, startBtn);
resolve();
});
});
} else {
// 没有冲突,直接执行
await continueRefreshProcess(instances, excludedInstances, daysThreshold, authHeaders, startBtn);
}
} catch (error) {
console.error('❌ 程序运行出错:', error);
addLog(`❌ 程序运行出错: ${error.message}`, "error");
updateStatus("执行失败", "error");
startBtn.disabled = false;
startBtn.textContent = '开始执行';
}
}
// 继续执行刷新流程
async function continueRefreshProcess(instances, excludedInstances, daysThreshold, authHeaders, startBtn) {
try {
// 筛选关机时长 >= 阈值的实例(排除冲突实例)
console.log('🔍 开始筛选符合条件的实例...');
const uuidsToRefresh = [];
let totalShutdown = 0;
for (const inst of instances) {
// 排除冲突实例
if (excludedInstances.includes(inst.uuid)) {
debugLog(`跳过冲突实例: ${inst.uuid}`);
continue;
}
debugLog(`检查实例: ${inst.uuid}, 状态: ${inst.status}`);
if (inst.status === "shutdown") {
totalShutdown++;
const stoppedInfo = inst.stopped_at;
debugLog(`实例 ${inst.uuid} 关机信息:`, stoppedInfo);
if (stoppedInfo && stoppedInfo.Valid && stoppedInfo.Time) {
try {
const stoppedTime = new Date(stoppedInfo.Time);
const now = new Date();
const daysDiff = Math.floor((now - stoppedTime) / (1000 * 60 * 60 * 24));
debugLog(`实例 ${inst.uuid} 关机天数: ${daysDiff} 天`);
if (daysDiff >= daysThreshold) {
uuidsToRefresh.push(inst.uuid);
addLog(`📌 发现符合条件的实例: ${inst.uuid} (关机${daysDiff}天)`, "info");
}
} catch (error) {
debugLog(`解析实例 ${inst.uuid} 关机时间出错:${error.message}`, error);
addLog(`⚠️ 解析实例 ${inst.uuid} 关机时间出错:${error.message}`, "warning");
}
}
}
}
console.log(`📊 统计信息: 总实例 ${instances.length}, 关机实例 ${totalShutdown}, 冲突实例 ${excludedInstances.length}, 需要刷新 ${uuidsToRefresh.length}`);
if (uuidsToRefresh.length === 0) {
addLog(`🎉 没有找到关机超过${daysThreshold}天的实例,无需刷新。`, "success");
updateStatus("无需刷新", "completed");
console.log('✅ 无需刷新任何实例');
return;
}
addLog(`共找到 ${uuidsToRefresh.length} 个关机超过${daysThreshold}天的实例,即将执行"五开模式"...`, "info");
updateStatus(`正在处理 ${uuidsToRefresh.length} 个实例...`, "running");
console.log(`🔄 开始处理 ${uuidsToRefresh.length} 个实例...`);
// 执行开关机操作
let successCount = 0;
let errorCount = 0;
for (let i = 0; i < uuidsToRefresh.length; i++) {
const uuid = uuidsToRefresh[i];
try {
console.log(`\n🔄 处理实例 ${i + 1}/${uuidsToRefresh.length}: ${uuid}`);
addLog(`\n--- 正在处理实例 ${uuid} (${i + 1}/${uuidsToRefresh.length}) ---`, "info");
// 开机
await powerOnInstance(authHeaders, uuid);
addLog("等待 10 秒...", "info");
await sleep(10000);
// 关机
await powerOffInstance(authHeaders, uuid);
addLog("等待 10 秒...", "info");
await sleep(10000);
successCount++;
console.log(`✅ 实例 ${uuid} 处理完成`);
} catch (error) {
errorCount++;
console.error(`❌ 实例 ${uuid} 处理失败:`, error);
addLog(`❌ 操作实例 ${uuid} 时出现错误: ${error.message}`, "error");
}
}
console.log(`\n📈 执行结果统计: 成功 ${successCount}, 失败 ${errorCount}`);
addLog(`\n🎉 所有符合条件的实例已完成开关机操作。成功: ${successCount}, 失败: ${errorCount}`, "success");
updateStatus("执行完成", "completed");
// 发送通知
GM_notification({
text: `AutoDL自动刷新完成!处理了 ${uuidsToRefresh.length} 个实例,成功: ${successCount}, 失败: ${errorCount}`,
title: "AutoDL助手",
timeout: 5000
});
} catch (error) {
console.error('❌ 刷新流程出错:', error);
addLog(`❌ 刷新流程出错: ${error.message}`, "error");
updateStatus("执行失败", "error");
} finally {
startBtn.disabled = false;
startBtn.textContent = '开始执行';
console.log('🏁 ===== AutoDL 自动刷新结束 =====');
}
}
// 手动保存设置函数
function saveCredentials() {
const phone = document.getElementById('autodl-phone').value.trim();
const password = document.getElementById('autodl-password').value.trim();
const days = document.getElementById('autodl-days').value.trim();
if (!phone || !password || !days) {
addLog("❌ 请填写完整的手机号、密码和天数阈值", "error");
return;
}
GM_setValue('phone', phone);
GM_setValue('password', password);
GM_setValue('days', days);
addLog("✅ 账户信息已保存!下次使用时将自动加载", "success");
addLog("🔒 密码已加密存储在本地,请放心使用", "info");
// 更新按钮状态
const saveBtn = document.getElementById('autodl-save-btn');
saveBtn.textContent = '已保存';
saveBtn.style.background = 'linear-gradient(135deg, #28a745, #20c997)';
setTimeout(() => {
saveBtn.textContent = '保存设置';
saveBtn.style.background = 'linear-gradient(135deg, #17a2b8, #138496)';
}, 2000);
}
// 清除保存的账户信息
function clearCredentials() {
if (confirm('确定要清除保存的账户信息吗?这将删除您之前输入的手机号和密码。')) {
GM_deleteValue('phone');
GM_deleteValue('password');
GM_deleteValue('days');
addLog("✅ 账户信息已清除!", "success");
addLog("💡 请重新填写账户信息以继续使用。", "info");
// 清空输入框
document.getElementById('autodl-phone').value = '';
document.getElementById('autodl-password').value = '';
document.getElementById('autodl-days').value = '9';
}
}
// 测试连接函数
async function testConnection() {
const phone = document.getElementById('autodl-phone').value.trim();
const password = document.getElementById('autodl-password').value.trim();
const days = document.getElementById('autodl-days').value.trim();
if (!phone || !password || !days) {
addLog("❌ 请填写完整的手机号、密码和天数阈值", "error");
return;
}
try {
updateStatus("正在测试连接...", "running");
console.log('🔗 开始测试连接...');
// 尝试登录并获取实例列表
const token = await loginAndGetToken(phone, password);
const authHeaders = { ...HEADERS, "Authorization": token };
const instances = await getInstances(authHeaders);
addLog(`✅ 连接测试成功!可以正常登录和获取实例。`, "success");
updateStatus("连接成功", "completed");
GM_notification({
text: "AutoDL连接测试成功!",
title: "AutoDL助手",
timeout: 3000
});
} catch (error) {
addLog(`❌ 连接测试失败: ${error.message}`, "error");
updateStatus("连接失败", "error");
GM_notification({
text: `AutoDL连接测试失败: ${error.message}`,
title: "AutoDL助手",
timeout: 5000
});
}
}
// 创建控制面板
function createControlPanel() {
const panel = document.createElement('div');
panel.className = 'autodl-refresh-panel';
// 从存储中获取保存的值
const savedPhone = GM_getValue('phone', '');
const savedPassword = GM_getValue('password', '');
const savedDays = GM_getValue('days', '9');
panel.innerHTML = `
<div class="autodl-refresh-header">
<span>🤖 AutoDL 自动刷新</span>
<button class="autodl-refresh-close">×</button>
</div>
<div class="autodl-refresh-content">
<div class="autodl-refresh-input-group">
<label for="autodl-phone">手机号:</label>
<input type="text" id="autodl-phone" placeholder="请输入手机号" value="${savedPhone}">
</div>
<div class="autodl-refresh-input-group">
<label for="autodl-password">密码:</label>
<input type="password" id="autodl-password" placeholder="请输入密码" value="${savedPassword}">
</div>
<div class="autodl-refresh-input-group">
<label for="autodl-days">天数阈值:</label>
<input type="number" id="autodl-days" placeholder="如: 9" value="${savedDays}" min="1" max="365">
</div>
<div style="display: flex; gap: 8px; margin-bottom: 8px;">
<button id="autodl-start-btn" class="autodl-refresh-btn" style="flex: 1;">开始执行</button>
<button id="autodl-save-btn" class="autodl-refresh-btn" style="flex: 1; background: linear-gradient(135deg, #17a2b8, #138496);">保存设置</button>
</div>
<div style="display: flex; gap: 8px; margin-bottom: 8px;">
<button id="autodl-clear-btn" class="autodl-refresh-btn" style="flex: 1; background: linear-gradient(135deg, #dc3545, #c82333); font-size: 12px;">清除保存信息</button>
<button id="autodl-test-btn" class="autodl-refresh-btn" style="flex: 1; background: linear-gradient(135deg, #ffc107, #e0a800); font-size: 12px;">测试连接</button>
</div>
<div id="autodl-refresh-status" class="autodl-refresh-status">准备就绪</div>
<div class="autodl-refresh-log" id="autodl-refresh-log"></div>
<div id="autodl-refresh-debug"></div>
</div>
`;
document.body.appendChild(panel);
// 添加拖拽功能
let isDragging = false;
let currentX;
let currentY;
let initialX;
let initialY;
let xOffset = 0;
let yOffset = 0;
function dragStart(e) {
if (e.target.closest('.autodl-refresh-close') ||
e.target.closest('input') ||
e.target.closest('button') ||
e.target.closest('.autodl-refresh-log')) {
return;
}
initialX = e.clientX - xOffset;
initialY = e.clientY - yOffset;
if (e.target === panel || e.target.closest('.autodl-refresh-header')) {
isDragging = true;
panel.classList.add('dragging');
}
}
function dragEnd(e) {
initialX = currentX;
initialY = currentY;
isDragging = false;
panel.classList.remove('dragging');
}
function drag(e) {
if (isDragging) {
e.preventDefault();
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
xOffset = currentX;
yOffset = currentY;
setTranslate(currentX, currentY, panel);
}
}
function setTranslate(xPos, yPos, el) {
el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
}
panel.addEventListener('mousedown', dragStart);
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', dragEnd);
// 绑定按钮事件
document.getElementById('autodl-start-btn').addEventListener('click', executeAutoRefresh);
document.getElementById('autodl-save-btn').addEventListener('click', saveCredentials);
document.getElementById('autodl-clear-btn').addEventListener('click', clearCredentials);
document.getElementById('autodl-test-btn').addEventListener('click', testConnection);
document.querySelector('.autodl-refresh-close').addEventListener('click', () => {
panel.remove();
});
// 自动保存设置(当输入框失去焦点时)
const saveSettings = () => {
const phone = document.getElementById('autodl-phone').value.trim();
const password = document.getElementById('autodl-password').value.trim();
const days = document.getElementById('autodl-days').value.trim();
if (phone) GM_setValue('phone', phone);
if (password) GM_setValue('password', password);
if (days) GM_setValue('days', days);
addLog("✅ 设置已自动保存", "success");
};
// 绑定事件监听器
document.getElementById('autodl-phone').addEventListener('blur', saveSettings);
document.getElementById('autodl-password').addEventListener('blur', saveSettings);
document.getElementById('autodl-days').addEventListener('blur', saveSettings);
// 显示调试信息
showDebugInfo();
// 如果已有保存的账号密码,显示提示
if (savedPhone && savedPassword) {
addLog("✅ 已加载保存的账户信息", "success");
addLog(`📱 手机号: ${savedPhone}`, "info");
addLog("🔐 密码已保存,可直接执行", "info");
} else {
addLog("💡 首次使用,请填写账户信息并点击'保存设置'", "info");
}
}
// 等待页面加载完成后创建控制面板
function init() {
console.log('🚀 AutoDL 自动刷新插件正在初始化...');
// 检查是否在AutoDL网站
if (window.location.hostname.includes('autodl.com')) {
debugLog('检测到AutoDL网站,准备创建控制面板');
// 延迟创建面板,确保页面完全加载
setTimeout(() => {
createControlPanel();
addLog("🚀 AutoDL自动刷新插件已加载", "success");
addLog("💡 功能说明:自动刷新关机超过指定天数的实例,防止被平台回收", "info");
addLog("🔍 调试模式已开启,详细日志请查看浏览器控制台", "debug");
console.log('✅ AutoDL 自动刷新插件初始化完成');
}, 2000);
} else {
console.log('❌ 当前网站不是AutoDL,插件不会运行');
}
}
// 启动插件
init();
})();