Skip to content

Instantly share code, notes, and snippets.

@yumin9822
Last active July 1, 2025 07:46
Show Gist options
  • Save yumin9822/5f4fda9cdb6a167464f771c7929ae934 to your computer and use it in GitHub Desktop.
Save yumin9822/5f4fda9cdb6a167464f771c7929ae934 to your computer and use it in GitHub Desktop.
qbittorrent
#!/bin/bash
# qBittorrent-nox 统一安装/升降级脚本
# 自动检测系统状态并进行相应的安装或升降级操作
# 检查是否为root用户
if [ "$EUID" -ne 0 ]; then
echo "请以root用户运行此脚本"
exit 1
fi
# 检查CPU架构并映射到对应的二进制文件名
arch_raw=`uname -m`
case $arch_raw in
"x86_64")
arch="x86_64"
;;
"i386"|"i686")
arch="x86"
;;
"aarch64"|"arm64")
arch="aarch64"
;;
"armv7l"|"armv7")
arch="armv7"
;;
"arm")
arch="armhf"
;;
"riscv64")
arch="riscv64"
;;
*)
echo "CPU架构 $arch_raw 不支持,退出脚本"
echo "支持的架构: x86_64, i386/i686, aarch64/arm64, armv7l/armv7, arm, riscv64"
exit 1
;;
esac
echo "检测到CPU架构: $arch_raw -> 使用二进制: $arch"
# 检测当前安装的qBittorrent版本
echo ""
echo "检测当前系统qBittorrent状态..."
qb_binary_path="/usr/bin/qbittorrent-nox"
current_version=""
is_installed=false
if [ -f "$qb_binary_path" ]; then
# 尝试获取当前版本
current_version=$($qb_binary_path --version 2>/dev/null | grep -i "qbittorrent" | sed 's/.*[qQ][bB]ittorrent[^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/')
if [ -n "$current_version" ]; then
echo "检测到已安装的qBittorrent版本: $current_version"
is_installed=true
else
echo "检测到qBittorrent二进制文件,但无法获取版本信息"
current_version="unknown"
is_installed=true
fi
else
echo "未检测到已安装的qBittorrent"
is_installed=false
fi
# 检查systemd服务是否存在
service_exists=false
if [ -f "/etc/systemd/system/qbittorrent-nox.service" ]; then
service_exists=true
fi
# 根据检测结果确定操作模式
if [ "$is_installed" = true ]; then
echo ""
echo "=========================================="
echo "系统检测到已安装的qBittorrent"
echo "当前版本: $current_version"
echo "将进入升降级模式"
echo "=========================================="
operation_mode="upgrade"
else
echo ""
echo "=========================================="
echo "系统未检测到qBittorrent安装"
echo "将进入全新安装模式"
echo "=========================================="
operation_mode="install"
fi
# 如果是全新安装,需要配置端口和密码
if [ "$operation_mode" = "install" ]; then
echo ""
echo "配置qBittorrent参数..."
echo "Please set qBittorrent server port:"
read -t 30 -p "(Default: 8080):" QB_PORT
if [ "$QB_PORT" = "" ]; then
QB_PORT="8080"
fi
echo "qBittorrent server port is: $QB_PORT"
echo "Please set qBittorrent web admin password:"
read -t 30 -p "(Default: qbadmin):" password
if [ "$password" = "" ]; then
password="qbadmin"
fi
echo "qBittorrent web admin password is: $password"
fi
# 版本选择方式
echo ""
echo "请选择版本安装方式:"
echo "1) 从预定义版本中选择"
echo "2) 手动输入qBittorrent和libtorrent版本"
echo "3) 安装最新版本 (自动从GitHub获取)"
read -p "请输入选择 (1, 2, or 3): " install_method
case $install_method in
1)
# 预定义版本选择
echo "请选择版本 (qBittorrent版本 - libtorrent版本):"
options=("qBittorrent 4.3.9 - libtorrent-v1.2.15" "qBittorrent 4.3.9 - libtorrent-v2.0.5" "qBittorrent 4.4.5 - libtorrent-v1.2.18" "qBittorrent 4.4.5 - libtorrent-v2.0.8" "qBittorrent 4.5.5 - libtorrent-v1.2.19" "qBittorrent 4.5.5 - libtorrent-v2.0.9" "qBittorrent 4.6.3 - libtorrent-v1.2.19" "qBittorrent 4.6.3 - libtorrent-v2.0.9" "qBittorrent 4.6.7 - libtorrent-v2.0.10")
select opt in "${options[@]}"
do
case $opt in
"qBittorrent 4.3.9 - libtorrent-v1.2.15")
qBver=4.3.9 && libver=1.2.15; break
;;
"qBittorrent 4.3.9 - libtorrent-v2.0.5")
qBver=4.3.9 && libver=2.0.5; break
;;
"qBittorrent 4.4.5 - libtorrent-v1.2.18")
qBver=4.4.5 && libver=1.2.18; break
;;
"qBittorrent 4.4.5 - libtorrent-v2.0.8")
qBver=4.4.5 && libver=2.0.8; break
;;
"qBittorrent 4.5.5 - libtorrent-v1.2.19")
qBver=4.5.5 && libver=1.2.19; break
;;
"qBittorrent 4.5.5 - libtorrent-v2.0.9")
qBver=4.5.5 && libver=2.0.9; break
;;
"qBittorrent 4.6.3 - libtorrent-v1.2.19")
qBver=4.6.3 && libver=1.2.19; break
;;
"qBittorrent 4.6.3 - libtorrent-v2.0.9")
qBver=4.6.3 && libver=2.0.9; break
;;
"qBittorrent 4.6.7 - libtorrent-v2.0.10")
qBver=4.6.7 && libver=2.0.10; break
;;
*) echo "请选择有效的版本";
esac
done
;;
2)
# 手动输入版本
echo ""
echo "手动版本输入模式"
echo "你可以在此处查看所有可用版本:"
echo "https://github.com/userdocs/qbittorrent-nox-static/releases"
echo ""
echo "常见qBittorrent版本: 4.3.9, 4.4.5, 4.5.5, 4.6.3, 4.6.7, 4.6.8, 5.1.1"
read -p "请输入qBittorrent版本 (例如: 4.6.7): " qBver
if [ -z "$qBver" ]; then
echo "错误: qBittorrent版本不能为空"
exit 1
fi
echo "常见libtorrent版本: 1.2.15, 1.2.18, 1.2.19, 2.0.5, 2.0.8, 2.0.9, 2.0.10, 2.0.11"
read -p "请输入libtorrent版本 (例如: 2.0.10): " libver
if [ -z "$libver" ]; then
echo "错误: libtorrent版本不能为空"
exit 1
fi
echo "选择的版本: qBittorrent $qBver - libtorrent v$libver"
read -p "确认${operation_mode}? (y/N): " confirm
if [[ ! $confirm =~ ^[Yy]$ ]]; then
echo "操作已取消"
exit 0
fi
;;
3)
# 获取最新版本
echo ""
echo "从GitHub获取最新版本信息..."
# 检查是否有必要的工具
if ! command -v wget > /dev/null && ! command -v curl > /dev/null; then
echo "错误: wget和curl都不可用,请安装其中一个"
exit 1
fi
# 尝试获取版本信息
if command -v wget > /dev/null; then
version_json=$(wget -qO- "https://github.com/userdocs/qbittorrent-nox-static/releases/latest/download/dependency-version.json" 2>/dev/null)
else
version_json=$(curl -s "https://github.com/userdocs/qbittorrent-nox-static/releases/latest/download/dependency-version.json" 2>/dev/null)
fi
if [ -z "$version_json" ]; then
echo "错误: 无法从GitHub获取版本信息"
echo "请检查网络连接或稍后重试"
exit 1
fi
# 解析JSON获取版本号
qBver=$(echo "$version_json" | grep '"qbittorrent"' | sed 's/.*"qbittorrent"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
libver=$(echo "$version_json" | grep '"libtorrent_2_0"' | sed 's/.*"libtorrent_2_0"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
if [ -z "$qBver" ] || [ -z "$libver" ]; then
echo "错误: 无法解析版本信息"
echo "原始响应: $version_json"
exit 1
fi
echo "找到的最新版本:"
echo "- qBittorrent: $qBver"
echo "- libtorrent: $libver"
# 检查是否需要升降级
if [ "$operation_mode" = "upgrade" ] && [ "$current_version" = "$qBver" ]; then
echo ""
echo "当前版本已经是最新版本 ($current_version)"
read -p "是否强制重新安装? (y/N): " force_install
if [[ ! $force_install =~ ^[Yy]$ ]]; then
echo "操作已取消"
exit 0
fi
fi
echo ""
read -p "确认${operation_mode}到最新版本? (y/N): " confirm
if [[ ! $confirm =~ ^[Yy]$ ]]; then
echo "操作已取消"
exit 0
fi
;;
*)
echo "无效选择,退出"
exit 1
;;
esac
# 检查版本是否相同(仅升降级模式)
if [ "$operation_mode" = "upgrade" ] && [ "$current_version" = "$qBver" ] && [ "$install_method" != "3" ]; then
echo ""
echo "警告: 目标版本 ($qBver) 与当前版本 ($current_version) 相同"
read -p "是否继续? (y/N): " continue_same
if [[ ! $continue_same =~ ^[Yy]$ ]]; then
echo "操作已取消"
exit 0
fi
fi
echo ""
echo "开始${operation_mode}: qBittorrent $qBver with libtorrent v$libver..."
# 构建下载URL
download_url="https://github.com/userdocs/qbittorrent-nox-static/releases/download/release-${qBver}_v${libver}/${arch}-qbittorrent-nox"
echo "下载URL: $download_url"
# 下载二进制文件到临时位置
temp_binary="/tmp/qbittorrent-nox-${qBver}"
echo "下载中..."
if ! wget "$download_url" -O "$temp_binary"; then
echo "错误: 下载qBittorrent二进制文件失败"
echo "请检查版本组合是否存在: https://github.com/userdocs/qbittorrent-nox-static/releases"
exit 1
fi
chmod +x "$temp_binary"
# 验证下载的二进制文件
echo "验证下载的二进制文件..."
downloaded_version=$($temp_binary --version 2>/dev/null | grep -i "qbittorrent" | sed 's/.*[qQ][bB]ittorrent[^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/')
if [ -z "$downloaded_version" ]; then
echo "警告: 无法验证下载的二进制文件版本"
else
echo "下载的版本: $downloaded_version"
if [ "$downloaded_version" != "$qBver" ]; then
echo "警告: 下载的版本 ($downloaded_version) 与预期版本 ($qBver) 不匹配"
read -p "是否继续安装? (y/N): " continue_mismatch
if [[ ! $continue_mismatch =~ ^[Yy]$ ]]; then
echo "操作已取消"
rm -f "$temp_binary"
exit 0
fi
fi
fi
# 停止qBittorrent服务
echo ""
echo "停止qBittorrent服务..."
systemctl stop qbittorrent-nox.service 2>/dev/null || true
# 等待进程完全停止
echo "等待进程完全停止..."
sleep 3
# 强制杀死残留进程
if pgrep -i -f qbittorrent > /dev/null; then
echo "强制终止残留的qBittorrent进程..."
pkill -f qbittorrent
sleep 2
fi
# 备份当前版本(仅升降级模式)
backup_name=""
if [ "$operation_mode" = "upgrade" ] && [ -f "$qb_binary_path" ]; then
if [ -n "$current_version" ] && [ "$current_version" != "unknown" ]; then
backup_name="/usr/bin/qbittorrent-nox-v${current_version}"
else
backup_name="/usr/bin/qbittorrent-nox-backup-$(date +%Y%m%d-%H%M%S)"
fi
echo "备份当前版本到: $backup_name"
mv "$qb_binary_path" "$backup_name"
if [ $? -eq 0 ]; then
echo "备份成功"
else
echo "错误: 备份失败"
rm -f "$temp_binary"
exit 1
fi
elif [ "$operation_mode" = "install" ] && [ -f "$qb_binary_path" ]; then
echo "备份现有二进制文件..."
mv "$qb_binary_path" "/usr/bin/qbittorrent-nox-old"
echo "旧二进制文件备份为: /usr/bin/qbittorrent-nox-old"
fi
# 安装新版本
echo "安装新版本..."
mv "$temp_binary" "$qb_binary_path"
if [ $? -eq 0 ]; then
echo "新版本安装成功"
else
echo "错误: 安装新版本失败"
# 尝试恢复备份
if [ -n "$backup_name" ] && [ -f "$backup_name" ]; then
echo "尝试恢复备份..."
mv "$backup_name" "$qb_binary_path"
fi
exit 1
fi
# 创建或更新systemd服务文件(全新安装或服务文件不存在时)
if [ "$operation_mode" = "install" ] || [ "$service_exists" = false ]; then
echo "创建systemd服务..."
cat <<-EOF >/etc/systemd/system/qbittorrent-nox.service
[Unit]
Description=qBittorrent-nox
After=network.target
[Service]
User=root
Type=forking
RemainAfterExit=yes
ExecStart=/usr/bin/qbittorrent-nox -d --webui-port="${QB_PORT:-8080}"
[Install]
WantedBy=multi-user.target
EOF
# 重载systemd配置
systemctl daemon-reload
systemctl enable qbittorrent-nox
fi
# 仅在全新安装时配置qBittorrent(升降级模式下保持原有配置不变)
if [ "$operation_mode" = "install" ]; then
echo "配置qBittorrent..."
# 创建配置目录
mkdir -p /root/.config/qBittorrent
# 检查是否已存在配置文件
if [ -f "/root/.config/qBittorrent/qBittorrent.conf" ]; then
echo "检测到现有配置文件,保持原有配置不变"
else
echo "创建新的配置文件..."
# 下载密码生成工具并生成加密密码
wget "https://fastly.jsdelivr.net/gh/yumin9822/Seedbox-Components@main/Torrent%20Clients/qBittorrent/${arch}/qb_password_gen" -O /tmp/qb_password_gen && chmod +x /tmp/qb_password_gen
PBKDF2password=$(/tmp/qb_password_gen $password)
# 创建配置文件
cat <<-EOF >/root/.config/qBittorrent/qBittorrent.conf
[BitTorrent]
Session\CheckingMemUsageSize=512
Session\ValidateHTTPSTrackerCertificate=false
[LegalNotice]
Accepted=true
[Network]
Cookies=@Invalid()
[Preferences]
Connection\PortRangeMin=45000
Bittorrent\MaxConnecs=1024
Bittorrent\MaxConnecsPerTorrent=16
Bittorrent\MaxUploads=-1
Bittorrent\MaxUploadsPerTorrent=-1
WebUI\CSRFProtection=false
Queueing\QueueingEnabled=false
WebUI\LocalHostAuth=false
WebUI\Password_PBKDF2="@ByteArray($PBKDF2password)"
WebUI\Port=${QB_PORT}
WebUI\Username=admin
EOF
# 清理临时文件
rm -f /tmp/qb_password_gen
fi
else
echo "升降级模式:保持现有配置文件不变"
fi
# 启动qBittorrent服务
echo "启动qBittorrent服务..."
systemctl start qbittorrent-nox.service
# 检查服务状态
sleep 3
if systemctl is-active --quiet qbittorrent-nox.service; then
echo "qBittorrent服务启动成功"
else
echo "警告: qBittorrent服务启动可能失败"
echo "请检查服务状态: systemctl status qbittorrent-nox"
fi
# 验证安装/升降级结果
echo ""
echo "验证操作结果..."
new_version=$($qb_binary_path --version 2>/dev/null | grep -i "qbittorrent" | sed 's/.*[qQ][bB]ittorrent[^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/')
# 获取公网IP(仅全新安装时)
if [ "$operation_mode" = "install" ]; then
IPADDRESS=$(wget -4qO- ifconfig.me/ip 2>/dev/null) || IPADDRESS=$(wget -4qO- ip.sb 2>/dev/null) || IPADDRESS="YOUR_SERVER_IP"
fi
echo "======================================"
if [ "$operation_mode" = "upgrade" ]; then
echo "qBittorrent升降级完成!"
echo "- 原版本: $current_version"
echo "- 新版本: $new_version"
echo "- libtorrent: v$libver"
if [ -n "$backup_name" ]; then
echo "- 备份位置: $backup_name"
fi
else
echo "qBittorrent安装完成!"
echo "- qBittorrent: $new_version"
echo "- libtorrent: v$libver"
echo "- Web界面: http://${IPADDRESS}:${QB_PORT}"
echo "- 用户名: admin"
echo "- 密码: ${password}"
fi
echo ""
echo "服务管理命令:"
echo "- 启动服务: systemctl start qbittorrent-nox"
echo "- 停止服务: systemctl stop qbittorrent-nox"
echo "- 查看状态: systemctl status qbittorrent-nox"
echo "- 查看日志: journalctl -u qbittorrent-nox -f"
echo ""
if [ "$operation_mode" = "upgrade" ] && [ -n "$backup_name" ]; then
echo "如果升降级后出现问题,可以恢复备份:"
echo " systemctl stop qbittorrent-nox"
echo " mv $backup_name $qb_binary_path"
echo " systemctl start qbittorrent-nox"
echo ""
fi
if [ "$operation_mode" = "install" ]; then
echo "如果无法从外网访问Web界面,请尝试:"
echo " iptables -I INPUT -p tcp --dport ${QB_PORT} -j ACCEPT"
echo " # 或者关闭防火墙 (请谨慎操作):"
echo " # iptables -F && iptables-save > /etc/iptables/rules.v4"
echo " # ufw disable"
fi
echo "======================================"
// ==UserScript==
// @name qbittorrent 状态栏显示数量和大小
// @name:en show counts and sizes of selected for qBittorrent
// @namespace localhost
// @version 2024-01-31
// @description 在 webUI 状态栏显示选中种子信息
// @description:en show counts and sizes of selected torrents.
// @author flashlab
// @match http://127.0.0.1:8080/
// @match *:*/*
// @icon https://www.qbittorrent.org/favicon.ico
// @license MIT
// @grant unsafeWindow
// @run-at document-end
// @downloadURL https://update.greasyfork.org/scripts/484895/qbittorrent%20%E7%8A%B6%E6%80%81%E6%A0%8F%E6%98%BE%E7%A4%BA%E6%95%B0%E9%87%8F%E5%92%8C%E5%A4%A7%E5%B0%8F.user.js
// @updateURL https://update.greasyfork.org/scripts/484895/qbittorrent%20%E7%8A%B6%E6%80%81%E6%A0%8F%E6%98%BE%E7%A4%BA%E6%95%B0%E9%87%8F%E5%92%8C%E5%A4%A7%E5%B0%8F.meta.js
// ==/UserScript==
/* globals torrentsTable */
(function() {
'use strict';
window.addEventListener('load', function () {
const states= ['size', 'total_size', 'unique_size', 'uploaded'];
var statusKey = states[0];
// update info
function updateSelected() {
const statusTd = document.getElementById("selectedStatus");
if (!statusTd) return;
const selectedRows = torrentsTable.selectedRowsIds();
const rows = torrentsTable.getFilteredAndSortedRows();
var counts = 0;
var sizes;
var readableSize;
if (statusKey.startsWith("unique")) {
const rawSizes = selectedRows.map(hash => rows[hash].full_data.size);
sizes = [...new Set(rawSizes)].reduce((sum, size) => {
++counts;
return (sum + size)
}, 0)
} else {
counts = selectedRows.length;
sizes = selectedRows.reduce(
(sum, hash) => rows[hash].full_data[statusKey] + sum, 0
)
}
readableSize = unsafeWindow.qBittorrent.Misc.friendlyUnit(sizes);
statusTd.textContent = `${readableSize} ${statusKey} of ${counts}`
statusTd.title = `${sizes} bytes`
}
(function main() {
let statusTd = document.getElementById("selectedStatus");
if (statusTd) return;
const footerow = document.querySelector("#desktopFooter > table > tbody > tr");
const bar = document.createElement("td");
bar.className = "statusBarSeparator";
footerow.insertAdjacentElement("afterbegin", bar);
const info = document.createElement("td");
info.id = "selectedStatus";
info.textContent = "点击切换显示方式"
info.style.cursor = "pointer";
statusTd = footerow.insertAdjacentElement("afterbegin", info);
// switch event
statusTd.onclick = (() => {
let i = 0;
return function(){
i = ++i%states.length;
statusKey = states[i];
updateSelected();
};
})();
if (typeof torrentsTable == 'undefined' || !torrentsTable.onSelectedRowChanged || torrentsTable.customOnSelectedRowChanged) return
torrentsTable.customOnSelectedRowChanged = torrentsTable.onSelectedRowChanged;
torrentsTable.onSelectedRowChanged = () => {
torrentsTable.customOnSelectedRowChanged();
updateSelected()
}
torrentsTable.customSelectAll = torrentsTable.selectAll;
torrentsTable.selectAll = () => {
torrentsTable.customSelectAll();
updateSelected()
}
})();
})
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment