// ==UserScript==
// @name 憨憨主页种子推送到QB
// @author Orange7
// @license GPL-2.0
// @namespace http://tampermonkey.net/
// @version 0.6
// @description 将种子推送到qBittorrent下载
// @icon https://www.qbittorrent.org/favicon.svg
// @match https://hhanclub.top/torrents.php*
// @match https://hhanclub.top/rescue.php*
// @match https://hhan.club/torrents.php*
// @match https://hhan.club/rescue.php*
// @grant GM_xmlhttpRequest
// @grant GM_log
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @grant GM_listValues
// @grant GM_registerMenuCommand
// @grant unsafeWindow
// @grant window.close
// @grant window.focus
// @grant window.onurlchange
// @run-at document-start
// @connect *
// ==/UserScript==
(function() {
'use strict';
// 添加全局CSS样式
GM_addStyle(`
.torrent-manage .flex.flex-col a[href^="download.php"] {
display: flex !important;
justify-content: center !important;
align-items: center !important;
text-align: center !important;
}
`);
// QB配置
const DEFAULT_CONFIG = {
address: 'http://localhost:8080',
username: 'admin',
password: 'adminadmin',
savePath: '',
autoStart: true,
tags: '',
category: '',
useRawMode: false, // 添加 raw 模式开关
upLimit: 0 // 添加上传速度限制参数
};
// 获取配置
function getConfig() {
return {
address: GM_getValue('qb_address', DEFAULT_CONFIG.address),
username: GM_getValue('qb_username', DEFAULT_CONFIG.username),
password: GM_getValue('qb_password', DEFAULT_CONFIG.password),
savePath: GM_getValue('qb_savepath', DEFAULT_CONFIG.savePath),
autoStart: GM_getValue('qb_autostart', DEFAULT_CONFIG.autoStart),
tags: GM_getValue('qb_tags', DEFAULT_CONFIG.tags),
category: GM_getValue('qb_category', DEFAULT_CONFIG.category),
useRawMode: GM_getValue('qb_userawmode', DEFAULT_CONFIG.useRawMode),
upLimit: GM_getValue('qb_uplimit', DEFAULT_CONFIG.upLimit)
};
}
// QB登录
function qbLogin() {
const config = getConfig();
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'POST',
url: `${config.address}/api/v2/auth/login`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: `username=${encodeURIComponent(config.username)}&password=${encodeURIComponent(config.password)}`,
onload: function(response) {
if (response.status === 200 && response.responseText === 'Ok.') {
resolve();
} else {
reject('登录失败');
}
},
onerror: function() {
reject('连接失败');
}
});
});
}
// 添加种子到QB
function addTorrent(torrentUrl) {
const config = getConfig();
return qbLogin().then(() => {
if (!config.useRawMode) {
// 使用 URLs 模式
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'POST',
url: `${config.address}/api/v2/torrents/add`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: `urls=${encodeURIComponent(torrentUrl)}&savepath=${encodeURIComponent(config.savePath)}&paused=${!config.autoStart}&tags=${encodeURIComponent(config.tags)}&category=${encodeURIComponent(config.category)}&upLimit=${config.upLimit * 1024}`,
onload: function(response) {
if (response.status === 200 && response.responseText === 'Ok.') {
resolve('添加成功');
} else {
reject('添加失败');
}
},
onerror: function() {
reject('连接失败');
}
});
});
} else {
// 使用 RAW 模式
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url: torrentUrl,
responseType: 'blob',
onload: function(response) {
if (response.status === 200) {
const torrentBlob = new Blob([response.response], { type: 'application/x-bittorrent' });
const formData = new FormData();
formData.append('torrents', torrentBlob, 'downloaded.torrent');
formData.append('savepath', config.savePath);
formData.append('paused', !config.autoStart);
formData.append('tags', config.tags);
formData.append('category', config.category);
formData.append('upLimit', config.upLimit * 1024);
GM_xmlhttpRequest({
method: 'POST',
url: `${config.address}/api/v2/torrents/add`,
headers: {
},
data: formData,
onload: function (response) {
if (response.status === 200) {
console.log('种子上传成功', response);
resolve('添加成功');
} else {
console.error('种子上传失败', response);
reject('添加失败');
}
},
onerror: function (err) {
console.error('上传请求失败', err);
reject('连接失败');
},
});
} else {
reject('下载种子失败');
}
},
onerror: function() {
reject('下载种子失败');
}
});
});
}
});
}
// 创建配置界面
function createConfigUI() {
const config = getConfig();
const div = document.createElement('div');
div.innerHTML = `
<div id="qb-config" style="position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#fff;padding:30px;border-radius:8px;box-shadow:0 2px 10px rgba(0,0,0,0.1);z-index:9999;display:none;min-width:320px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;">
<h3 style="margin:0 0 20px 0;color:#333;font-size:18px;text-align:center;padding-bottom:10px;border-bottom:1px solid #eee;">qBittorrent配置</h3>
<div style="margin-bottom:15px;">
<label style="display:block;margin-bottom:5px;color:#666;font-size:14px;">地址</label>
<input type="text" id="qb-address" value="${config.address}" style="width:100%;padding:8px;border:1px solid #ddd;border-radius:4px;box-sizing:border-box;margin-top:3px;">
</div>
<div style="margin-bottom:15px;">
<label style="display:block;margin-bottom:5px;color:#666;font-size:14px;">用户名</label>
<input type="text" id="qb-username" value="${config.username}" style="width:100%;padding:8px;border:1px solid #ddd;border-radius:4px;box-sizing:border-box;margin-top:3px;">
</div>
<div style="margin-bottom:15px;">
<label style="display:block;margin-bottom:5px;color:#666;font-size:14px;">密码</label>
<input type="password" id="qb-password" value="${config.password}" style="width:100%;padding:8px;border:1px solid #ddd;border-radius:4px;box-sizing:border-box;margin-top:3px;">
</div>
<div style="margin-bottom:15px;">
<label style="display:block;margin-bottom:5px;color:#666;font-size:14px;">保存路径(不填取默认)</label>
<input type="text" id="qb-savepath" value="${config.savePath}" style="width:100%;padding:8px;border:1px solid #ddd;border-radius:4px;box-sizing:border-box;margin-top:3px;">
</div>
<div style="margin-bottom:15px;">
<label style="display:block;margin-bottom:5px;color:#666;font-size:14px;">分类</label>
<input type="text" id="qb-category" value="${config.category || ''}" style="width:100%;padding:8px;border:1px solid #ddd;border-radius:4px;box-sizing:border-box;margin-top:3px;">
</div>
<div style="margin-bottom:15px;">
<label style="display:block;margin-bottom:5px;color:#666;font-size:14px;">标签 (多个用,分隔)</label>
<input type="text" id="qb-tags" value="${config.tags}" style="width:100%;padding:8px;border:1px solid #ddd;border-radius:4px;box-sizing:border-box;margin-top:3px;">
</div>
<div style="margin-bottom:15px;">
<label style="display:block;color:#666;font-size:14px;">
<input type="checkbox" id="qb-autostart" ${config.autoStart ? 'checked' : ''} style="margin-right:5px;">
自动开始下载
</label>
</div>
<div style="margin-bottom:15px;">
<label style="display:block;color:#666;font-size:14px;">
<input type="checkbox" id="qb-userawmode" ${config.useRawMode ? 'checked' : ''} style="margin-right:5px;">
使用RAW模式 (先下载种子再上传)
</label>
</div>
<div style="margin-bottom:15px;">
<label style="display:block;margin-bottom:5px;color:#666;font-size:14px;">上传速度限制 (KB/s,0为无限制)</label>
<input type="number" id="qb-uplimit" value="${config.upLimit}" style="width:100%;padding:8px;border:1px solid #ddd;border-radius:4px;box-sizing:border-box;margin-top:3px;">
</div>
<div style="display:flex;justify-content:flex-end;gap:10px;margin-top:20px;">
<button id="qb-cancel" style="padding:8px 16px;border:none;border-radius:4px;cursor:pointer;font-size:14px;background-color:#f5f5f5;color:#666;">取消</button>
<button id="qb-save" style="padding:8px 16px;border:none;border-radius:4px;cursor:pointer;font-size:14px;background-color:#54F7A4;color:white;">保存</button>
</div>
</div>
`;
document.body.appendChild(div);
// 保存配置
document.getElementById('qb-save').addEventListener('click', () => {
GM_setValue('qb_address', document.getElementById('qb-address').value);
GM_setValue('qb_username', document.getElementById('qb-username').value);
GM_setValue('qb_password', document.getElementById('qb-password').value);
GM_setValue('qb_savepath', document.getElementById('qb-savepath').value);
GM_setValue('qb_category', document.getElementById('qb-category').value); // 保存分类
GM_setValue('qb_autostart', document.getElementById('qb-autostart').checked);
GM_setValue('qb_tags', document.getElementById('qb-tags').value);
GM_setValue('qb_userawmode', document.getElementById('qb-userawmode').checked);
GM_setValue('qb_uplimit', parseInt(document.getElementById('qb-uplimit').value) || 0);
document.getElementById('qb-config').style.display = 'none';
});
document.getElementById('qb-cancel').addEventListener('click', () => {
document.getElementById('qb-config').style.display = 'none';
});
}
// 主函数
function main() {
// 创建配置界面
createConfigUI();
// 注册配置菜单
GM_registerMenuCommand('QB配置', () => {
document.getElementById('qb-config').style.display = 'block';
});
// 查找所有下载按钮
const torrentDivs = document.querySelectorAll('div.torrent-manage');
torrentDivs.forEach(div => {
const downloadLink = div.querySelector('a[href^="download.php"]');
if (!downloadLink) return;
// 创建QB按钮
const qbButton = document.createElement('a');
qbButton.href = 'javascript:void(0);';
qbButton.className = 'xl:px-[5px] text-[14px] bg-[#F29D38] !text-[#FFFFFF] rounded-[5px]';
qbButton.textContent = '推送到QB';
qbButton.style.marginTop = '5px';
// 添加点击事件
qbButton.addEventListener('click', () => {
const torrentUrl = new URL(downloadLink.href, window.location.href).href;
addTorrent(torrentUrl)
.then(msg => alert(msg))
.catch(err => alert('错误: ' + err));
});
// 添加按钮到页面
const buttonContainer = div.querySelector('.flex.flex-col');
if (buttonContainer) {
buttonContainer.appendChild(qbButton);
}
});
}
// 当DOM加载完成后执行
if (document.readyState === "loading") {
document.addEventListener('DOMContentLoaded', main);
} else {
main();
}
})();