Skip to content

Releases: Mofan2020/SuperDownloadManager

v2.2.4

v2.2.4 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 31 Oct 06:22

v2.2.4

这一版修的是下载过程中文件内容错乱的问题。

下载的文件内容错乱

下载完成的文件可能大小正常但内容是错的。7z、APK、DMG 这几种格式打开都会报「数据已损坏」。

  • 动态分块时同一个块只会被一个连接下载,不会出现两个连接往同一块里写、互相覆盖的情况。
  • 下载器会核对服务器实际发来的字节数。跟你请求的不一致时会报错,不会把不对的数据当成下载好的。
  • 合并文件之前会核对每一块的长度。块缺失或者长度不对时会报错,不会拼出一个大小对但内容错的文件。
  • 取消下载或者下载失败后残留的临时分片文件会被彻底清掉,不会被下一次下载当成「已经下好的内容」。

速度显示

  • 续传的任务刚启动时,速度曲线不再飙到几万 MB/s。那个数字是假的,是把盘上已有的字节算成了这一瞬间的传输量。
  • 详情页每个分片的「耗时」显示的是这个分片实际下载了多久。之前显示的是「距上次写入多久」,已完成的分片那个数字会一直涨。

详情页分片列表

  • 每个分片多一列「速率」,一眼能看出哪个分片拖后腿。
  • 卡住的分片(下载中但长时间没进数据)整行标红。
  • 进度条上悬停能看到每个分片的状态、字节数、耗时和速率。

v2.2.3

v2.2.3 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 29 Oct 13:44

v2.2.3

修了你反馈的三个问题。

继续下载

失败之后再点「继续下载」,现在真的从断点接着下,不会从头重来。

  • 服务器每次请求都换 ETag 的情况,不再被误判成「文件变了」。
  • 文件确实变了的话,点一次「继续下载」就会自动清掉旧进度重新下,不用再点第二次。详情面板会写明「服务器上的文件已变化,已重新开始下载」。
  • 任务刚失败、还在收尾的时候点「继续下载」,会等收尾完成再启动,不会「点了没反应」。
  • 盘上残留的分片文件(取消过的下载、Windows 上删不掉的那些)不会再被当成「已经下好的字节」,新下载的内容不会被带歪。

链接失效

链接失效后可以填新链接继续。SDM 会先确认新链接拿到的文件和原来那个是同一个文件:

  • 是同一个文件 → 接着下,已经下过的部分不重下。
  • 不是 → 拒绝并提示换一个,已经下好的字节一个不动。
  • 没法确认 → 清掉重新下,界面上说明进度没了。

判断靠逐字节比对。ETag 和文件大小都不靠谱:很多 CDN 每次请求都换 ETag,大小一样的文件更是到处都有。

「换链接」也进了右键菜单,失败、暂停、等待新链接的任务都能用。

详情页

分段明细跟着列表一起刷新了,不用关掉详情页再重开。刷新时滚动位置会保住。

Windows 上的坏文件

动态分段切出来的新分片,在 Windows 上可能把上一轮下载残留的字节当成「已经下好的」,新内容接在旧内容后面写,合并出来的文件长度看着正常、内容是乱的。现在从零写,不会再混进去。

v2.2.2

v2.2.2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 28 Oct 13:35

v2.2.2

五处修复,全是用户 2026-10-28 反馈的。

详情面板里的分片表一打开就没了

点进任务详情菜单,分片显示大概零点几秒内就消失了。

根因:分片明细挂在 task 对象上,而列表轮询每 1.5 秒把
State.tasks 整个换掉(列表接口不带分片明细,省内存 + 省序列化)——
挂在 task 上的 chunks 被这一换抹掉。

现在分片明细单独存 State.taskChunks[taskId],跟 taskErrors、
speedHistory 一个路子。任务移除时一起清掉。

辅助文件不再堆在下载目录

.sdm.json(完成后的元信息)、.sdm.session.json(下载中的会话)、
.sdm.partial-N(每个分片一个临时文件)以前都躺在目标文件旁边。
一个 16 分片的下载会在用户的下载文件夹里留下 19 个文件,而且
.sdm.partial-* 这种名字用户完全看不懂——只会以为软件下坏了。

现在统一搬到 SDM 自己的地盘:

~/Library/Application Support/SuperDownloadManager/downloads/
%LOCALAPPDATA%\SuperDownloadManager\downloads\        (Windows)
~/.local/share/SuperDownloadManager/downloads/           (Linux)

下载目录里从此只剩用户要的那一个文件。

文件名的 key 里带目标绝对路径的短哈希——同名文件放在不同目录是
不同文件,它们的 sidecar 不能混(sidecar 是用来比对 etag /
content_length 判断「还是不是同一个文件」的,混了会导致把新内容
续写进旧文件的空洞里)。

升级前下的东西还在老位置:读的时候会把旧的搬过来,所以正在下的
下载不会凭空消失,续传也用不上。

重名自动加编号,格式跟 macOS 一致

movie.mkv 已存在 → 下成 movie 1.mkv,再下成 movie 2.mkv。

即在文件名的末尾(扩展名之前)加一个空格,再加编号,编号表示
这是第几个同名文件。跟 Finder / Safari / 邮件客户端的处理方式一致,
用户不用适应第二套规则。以前是 movie (1).mkv(第三种风格)。

动态分段:把长尾治了

用户观察到的现象是「下载到 96% 的时候速度掉得很低,比刚开始低很多」。
这是长尾——下载末尾的活只剩一两条连接在爬。

根因有三个,叠在一起:

  1. 分片数封顶 = 目标分片数 = 连接数。 空闲 worker 想去切最大的
    未完成分片,切不动 → worker 直接退出,那条连接就永久没了。
  2. 最小可拆大小固定不变。 剩下的字节不够再拆一半时,一片都拆不动。
  3. 「找活干」占着连接名额。 名额只在真正传输时才该占——不然
    governor 限的是「已经分好片的数量」,不是「同时在传的连接数」。

修法:

  • 分片数上限改成 max_chunks(连接数 × 4 或 32,取大的)——目标分片数
    是连接数,不是分片数的硬上限。多出来的分片就是给空闲连接准备的
    活干。
  • 最小可拆大小随剩余量自适应:按「剩余总量 / 目标分片数」算出
    平均每片该多大,夹在硬地板(64KB)和 min_chunk_size 之间。剩得越少
    切得越细。
  • worker 拆不动时不退出,改成等新分片(20ms 一轮)。
  • 「找活干」不占连接名额。

顺带修了一个死设置:设置页的「初始连接数」和「分片上限」以前
界面能改、引擎压根不读,用户调完发现什么都没变——只能得出「这程序
没有动态分段能力」的结论。现在真的接到引擎上了,而且分片上限被强制
不低于连接数 × 4(否则长尾立刻回来)。

验证方式

尾部速度用结构性指标测,不用速度比值——比值受机器负载影响,
标不准阈值就是一条会随机红的 flaky 测试。

第一层:结构性的事实(完全不看时间,零波动)

  • 没人 worker 在活没干完的时候提前下班
  • worker 确实等过新分片
  • 分片数突破了「目标分片数」= 连接数

第二层:差分比较(跟机器快慢无关)

同一个测试里跑两遍——一遍是现在的实现,一遍是「把分片数封顶改回
目标分片数」的旧行为——同一台机器、同一时刻,然后比较。

机器快慢在两边同时生效,抵消掉了。所以断言的是「现在的实现明显
好于旧行为」,而不是「现在的实现达到某个绝对值」。

这是 flaky 测试的根治办法:跟一个已知的坏实现比。
最初用的是绝对阈值(「尾段平均并发 ÷ 峰值并发 ≥ 0.70」),
结果 v2.2.2 的第一次 CI 就红了——CI runner 上峰值并发被算成 9
(本地是 8),比值 0.65 刚好掉到阈值下面。慢服务器下更差,
实测能掉到 0.66、连接数掉到 9 条。绝对阈值标不准就是随机红。

实测差距:

快服务器   好 13 条 / 0.85   vs  坏 9 条 / 0.41
慢服务器   好 10 条 / 0.80   vs  坏 7 条 / 0.21

变异测试(每个修复单独撤掉,测试必须变红):

撤掉的修复 变红的测试
分片数上限改回 target test_chunk_layout_exceeds_target、两条差分测试
worker 拆不动就下班 test_no_worker_exits_while_work_remains、test_workers_wait_for_new_chunks_instead_of_idling_out
最小可拆大小固定(不自适应) test_chunk_layout_exceeds_target、两条差分测试
「找活干」时占住连接名额 两条差分测试

v2.2.1

修剪贴板监控——macOS 和 Windows 上它从来就没工作过。用户 2026-10-28
实测发现的:两个平台都复现。

macOS:AppleScript 写错了,读取每轮都失败

代码写的是 osascript -e "pbpaste"。但 pbpaste 是 shell 命令,
不是 AppleScript 命令——AppleScript 解释器把它当成一个变量名,报

0:7: execution error: 变量"pbpaste"没有定义。 (-2753)

退出码非 0 → 读取函数返回 (None, -1) → 每一轮都失败。也就是说
「剪贴板链接自动识别」这个功能从上线到现在一次都没工作过。

改成直接调 /usr/bin/pbpaste:输出纯文本,剪贴板里是文件时输出空、
退出码 0(不会误报),而且不用每 0.5 秒起一个 AppleScript 解释器。
osascript -e "the clipboard" 留作兜底(那是 AppleScript 里读剪贴板的
正确写法,the clipboard 是 Standard Additions 的属性)。

Windows:句柄被截断,读出来是空的

GetClipboardData 返回的是 HANDLE(64 位)。代码没声明 restype,
而 ctypes 在没声明时默认按 c_int 返回——64 位 Windows 上句柄被截成
32 位
。低 32 位最高位是 1 的时候(现代 Windows 的句柄值经常落在
这个区间),ctypes.c_void_p(handle) 会把这个负数补成
0xFFFFFFFFxxxxxxxx 的错指针,GlobalLock 拿不到数据 → 读出来是空的。

现在给所有 Win32 调用都声明了 argtypes / restype,
GetClipboardData 和 GlobalLock 的 restype 是 wintypes.HANDLE。

开关打开后线程压根没起

监听线程之前是「启动那一刻设置已经开着才起」。于是:

  • 用户从设置页把开关打开 → 设置写对了,线程没跑 → 没反应
  • 用户从 Windows 托盘把开关打开 → 同上,而且托盘那条路连
    start() 都不调

现在线程无条件常驻,「开不开」由它每轮自己读设置决定
(_capture_enabled 本来就是这么设计的,只是从来没人保证线程在跑)。
任何入口改开关,下一轮就生效。

三个入口统一走一个出口

菜单栏、托盘、设置页之前各写各的设置键——写的是同一个键倒没错,但
只有设置页那条会推 settings_updated,菜单栏和托盘勾完之后,
设置页上的开关还是老样子,用户以为没生效。现在三个入口都走
GuiBridge.set_clipboard_monitor():写设置 + 推事件 + 状态一致。

读失败不再静默

之前读失败只有一条 debug 级日志,而 Windows 用户默认日志级别是
warning——那条根本看不到,「剪贴板监控用不了」在用户那边是一个完全
无从查起的问题。现在失败是 warning 级(30 秒节流,不刷屏)。

设置页补了一条说明

「剪贴板链接自动识别」旁边加了一句:只有扩展名能识别成文件类型
(zip / dmg / mp4 …)的链接才会提示,聊天里的普通链接不会打扰你。
这是 Swift v1.0.7 起就有的过滤规则,但从来没在界面上说过。


v2.2.0

浏览器下载捕获——Chrome / Edge / Firefox / Safari 四个浏览器的下载
可以交给本程序接管。

之前这套东西其实写好了(契约 §4 那一整套:协议、原生宿主、回环通道、
四个扩展目录都在仓库里),但用户没法用:扩展只躺在源码仓库里,
应用里没有任何地方告诉用户「扩展在哪、装没装、token 是多少」——
连 Safari 那份连配对入口都没有。这一版把缺的那一段补齐。

设置页新增「浏览器接管」板块

打开「设置 → 浏览器接管」,能看到:

  • 配对 token:可复制、可重新生成(重新生成 = 旧 token 立刻作废,
    拿着旧 token 的扩展再也派不了单)
  • 每个浏览器的扩展目录:点「打开扩展目录」直接在访达/资源管理器里打开
  • 每个浏览器装没装:应用自己扫扩展目录、查 pluginkit 得到的真实状态
  • 每个浏览器的安装入口:点「打开扩展管理页」直接跳到浏览器的扩展页

扩展文件现在跟着应用一起打包(macOS 在 Contents/Resources/extensions/,
Windows 在 onefile 的 sys._MEIPASS/extensions/),不用再去源码仓库里找。

Safari:走官方的「未签名的临时扩展」路径

本项目没有 Apple Developer Program 开发者签名,所以 Safari 扩展
不签名,走 Apple 官方给未签名扩展留的路子(macOS 17 / Safari 17 起):

设置 → 高级 → 「显示网页开发者功能」→ 设置 → 开发者 → 「允许未签名的扩展」
→ 设置 → 开发者 → 「添加临时扩展…」→ 选扩展目录 → 设置 → 扩展里勾选启用。

不需要 Xcode,不需要开发者账号。

平台的硬限制如实标在界面和文档里:退出 Safari 会重置「允许未签名的扩展」,
临时扩展最长 24 小时。Safari 也只能手动派单——它没有
downloads.onDeterminingFilename 那种「下载开始前拦一道」的钩子,
硬做只能等下载开始后再取消,那时候文件已经在路上,用户会丢文件。

Safari 还必须手动粘一次 token:Safari 没有 Chrome 那种原生消息宿主,
只能走回环 HTTP 通道——而那个端口带着 Access-Control-Allow-Origin: *,
不能在上面开 /pair(那等于把 token 贴给任何网页,等于打开
「谁都能往下载器塞任务」的门)。扩展的选项页和弹窗里都加了 token 输入框。

Chrome / Edge:把扩展 ID 钉死

宿主清单里的 allowed_origins 必须跟扩展的 origin 精确一致,否则
Chrome 直接拒绝拉起宿主。而「加载已解压的扩展程序」装出来的扩展,
ID 默认按目录路径算哈希——换个机器、换个目录就变了,清单永远对不上。

所以 Chrome / Edge 的 manifest 里写死了一把公钥(key 字段),扩展 ID
由公钥算出来,恒定为 bangcjehjdodcpfekmnkfmbeffcclpih(Chrome 和 Edge 同一个)。
Firefox 不需要这一步:它的扩展 ID 是 manifest 里
browser_specific_settings.gecko.id 自己定的。

升级扩展时不要换这把公钥——一换 ID 就变了,所有装好的宿主清单全部失效。
有一条测试专门盯着「manifest 里的 key 算出来的 ID」和「代码里写死的 ID」
是否一致。

其他

  • Safari 扩展补了选项页(原来只有弹窗),版本号四个扩展统一到 2.2.0
  • 扩展 manifest 加了 options_ui,Chrome/Firefox 也能从扩展详情页进选项页

v2.1.3

修一个真正的架构 bug(下载任务根本跑不起来),再加上自动分类、全局 UA,
以及去掉手动分片数。

下载一直卡住、0 字节 —— 根因是事件循环被抹掉了

上一版把「卡在 0 KB/s」当成网络问题修了一轮(探测超时、重试预算)。
这次拿到进程内的 Python 栈才看清:下载任务压根没跑起来。

GuiBridge.set_window() 里有这么一段:从当前线程猜一个正在跑的
asyncio 循环,猜不到就把 _loop 置成 None。而窗口是在主线程里建的,
主线程上根本没有 asyncio 循环(引擎的循环在 sdm-engine 线程里)——
于是 set_window 把 set_loop() 刚设好的引擎循环抹成了 None。

_loop 是 None 之后,桥接层所有调用退化成 asyncio.run(coro):
一次性循环。engine.start() 在这个一次性循环里把下载任务
create_task 出去,asyncio.run 一返回就拆循环,任务在跑第一行之前
就被取消。结果就是:任务躺在列表里永远不动、0 字节、连一个 socket
都没建过(lsof 上看不到任何对外连接)。暂停/继续/取消也都打在错误的
循环上,所以怎么点都没反应。添加框里的「解析」能成功,是因为它自己
开循环同步取结果,不依赖引擎循环。

修法:

  • set_window() 不再碰 _loop——引擎循环只有一个来源:set_loop()
  • main() 里 set_loop() 挪到 set_window() 之后
  • 桥接层加了兜底:真的没设上循环时,自己起一个常驻的后台循环,
    绝不再退化成一次性的 asyncio.run
  • 连接池等待加了超时上限(pool=None 的意思是「等连接池空位时永不
    超时」,池满了就永久挂起)

自动分类

  • 分类下拉第一个选项是「自动」并且默认选中。早先前端把第一个选项
    (视频)当成分类发给后端,后端再覆盖掉引擎按 URL 猜出来的正确分类
    ——用户加一个 .dmg,列表里却归在「视频」。现在「自动」传的是 null,
    由引擎判断
  • 按服务器给的文件名判分类:URL 常常是无扩展名的 CDN 路径
    (/download/12345),真正的文件名在 Content-Disposition 里。
    探测成功后按真实文件名回填分类(.dmg → 程序),保存目录也跟着换
  • 用户手动改过分类就不再自动改

全局 UA

  • 设置页新增「全局 User-Agent」:留空 = 用程序内置的默认值,
    随时可以改,改完立刻对后续所有下载生效
  • 优先级:站点规则里的 UA → 全局 UA → 内置默认 UA
  • 清空输入框是「恢复默认」,不是「发一个空的 UA」

去掉手动分片数

添加对话框里那个默认 8 的分片数输入框没有了。分片本来就是引擎动态
规划的(起始连接数按文件大小和实测速度自己长),挂一个数字框只会
让人以为那个数字决定实际分片数。

其它

  • 探测失败的日志从 notice 提到 warning——用户把日志级别调到 warning
    时,那恰恰是排查「卡在连接中」最关键的一条线索
  • 引擎探测之后会按真实文件名修正任务的分类(用户没手动选过时)

v2.1.2

修两件事:下载一直卡在 0 KB/s,以及下载列表的界面错位。

「0 KB/s」那条查出来是三件事叠在一起:探测探了两次、探测用的超时是
分片那个 60 秒的看门狗、重试预算只算退避不算请求本身的时间。三条
一起,服务器慢的时候就是几分钟的静默——界面全程显示 0 KB/s,
还没有报错。界面错位那条更严重:列表的 grid 模板压根没生效。

界面

  • 下载列表的列布局修好了:表头和数据行用的是同一套 grid 模板
    (--row-cols,名字列是 1fr),但这个变量被写在了列表容器上,
    而它里面的 var(--w-*) 只定义在行和表头上——CSS 自定义属性是在
    声明它的那个元素上做替换的,容器上找不到 --w-name,整个模板
    就成了无效值,grid-template-columns 退回 none,所有列竖着
    叠起来
    (实测一行 281px 高,八个格子竖排)。现在模板写在用它的
    元素上,列宽和窄屏自适应一起恢复
  • 表头和数据行不再差 36px:列选择器按钮原来跟表头并排(flex),
    把表头挤窄了 36px,名字之后的每一列都整体错开。现在按钮浮在表头
    右端,表头拿回满宽
  • 数字列右对齐:表头的「大小 / 速度 / 剩余时间」是右对齐的,行里
    的数字却是左对齐的,标签和数字永远对不上
  • 连接阶段显示「连接中」:从任务开跑到收到第一个字节之间不再显示
    「0 KB/s」——那段时间确实一个字节都没拿到,但「还没开始」和「卡死
    了」是两回事
  • 顺手修掉 i18n 里 7 处乱码文案(「SHA-256:(0)」这类被二次编码
    过的字符串)

引擎

  • 探测不再探两次:引擎探一次拿真实文件名和总长度,下载流程又探
    一次——两个 RTT 的纯等待。现在探一次的结果直接交给流程层
  • 探测有自己的短超时:探测以前借用分片的 60 秒 read 超时(那是给
    慢连接用的空闲看门狗),服务器不回响应头时一次探测就静默 60 秒,
    重试几轮就是好几分钟。现在探测用 15 秒的独立超时
  • 重试预算真的约束总时间:预算以前只统计退避的 sleep,不统计请求
    本身耗掉的时间——服务器不响应时预算永远花不出去。现在请求耗时也
    计入预算,最长二十几秒就会给出明确的失败
  • 探测失败给一句人话:以前界面上显示的是 ReadTimeout 这种英文类名,
    现在是「连接服务器超时 / 连不上服务器 / 网络错误」

验证

  • 引擎侧新增 tests/test_connect_phase.py(探测预算、不重复探测、连接
    态生命周期),前端新增 tests/test_connecting_js.mjs 和
    tests/test_column_alignment_js.mjs
  • 列对齐用无头浏览器量像素验的(Chromium + WebKit 都跑过):修复
    前一行 281px 高、八格竖排;修复后行高 56px,表头和数据行的 grid
    轨道完全一致

已知限制

  • 仍不支持 BT / 磁力
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.1.1

修三个用户实际撞上的问题:改设置后页面总跳回顶部、开启开机自启会立刻多出一个进程、
解析链接一直在报「所有重试都失败」。顺带修好进度条最后一段经常少报一截的问题。

界面

  • 改设置不再跳回页面顶部:改任何设置都会重画整个设置页,而重画会把滚动位置一起
    冲掉——用户改个设置就被弹回顶部。现在重画前记下位置、重画后放回去
  • 任务被删掉导致列表重画时,设置页的滚动位置同样不再丢

macOS

  • 开启开机自启不再立刻多出一个 SDM 进程:之前写完 plist 还顺手跑了一次
    launchctl load,而 load 不只是「注册」,它当场就把任务跑起来了。自启的意思是
    「下次登录时启动」,不是「现在启动」。现在只写 plist,登录时 launchd 自己会扫

引擎

  • 磁力链接这类地址立刻失败并说清楚:磁力链接(magnet: 开头)以前会被当成网络
    错误重试 5 次,最后报一句「所有重试都失败」——跟真实原因完全对不上。现在直接告诉你
    「这是磁力链接(BT),SDM 只下载 HTTP(S) 的普通文件,不支持 BT/磁力」
  • 不可重试的失败不再重试:404(文件不存在)、416(服务器不认这个 Range)这类重试
    多少次结果都一样,现在一次就报错,不再白等 7.5 秒
  • 两处错误分类对齐:新建任务对话框和任务列表现在用同一份分类。磁力链接在两边都
    归到「客户端错误」,不再在任务列表里显示成「网络连接中断,将自动重试」还标着可
    重试;404 归到「文件不存在」;429(被限流)归到「可重试」
  • 进度条最后一段不再少报:下载快结束时进度会停在 98.97% 再也不动(2 MB 的文件
    最终报成 2031616,正好少一个 64 KB 块)。根因是进度合并窗口到期时发的是上一个
    窗口攒的旧值,把最后那次上报直接抹掉了
  • 续传时进度不再倒退一次:续传的汇总基数没算盘上已有的字节,第一次上报会比实际
    少,进度条会往回跳一下,紧接着算出来的速度也是个巨大的假值

已知限制

  • 仍不支持 BT / 磁力...
Read more

v2.2.1

v2.2.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 28 Oct 11:14

v2.2.1

修剪贴板监控——macOS 和 Windows 上它从来就没工作过。用户 2026-10-28
实测发现的:两个平台都复现。

macOS:AppleScript 写错了,读取每轮都失败

代码写的是 osascript -e "pbpaste"。但 pbpaste 是 shell 命令,
不是 AppleScript 命令——AppleScript 解释器把它当成一个变量名,报

0:7: execution error: 变量"pbpaste"没有定义。 (-2753)

退出码非 0 → 读取函数返回 (None, -1) → 每一轮都失败。也就是说
「剪贴板链接自动识别」这个功能从上线到现在一次都没工作过。

改成直接调 /usr/bin/pbpaste:输出纯文本,剪贴板里是文件时输出空、
退出码 0(不会误报),而且不用每 0.5 秒起一个 AppleScript 解释器。
osascript -e "the clipboard" 留作兜底(那是 AppleScript 里读剪贴板的
正确写法,the clipboard 是 Standard Additions 的属性)。

Windows:句柄被截断,读出来是空的

GetClipboardData 返回的是 HANDLE(64 位)。代码没声明 restype,
而 ctypes 在没声明时默认按 c_int 返回——64 位 Windows 上句柄被截成
32 位
。低 32 位最高位是 1 的时候(现代 Windows 的句柄值经常落在
这个区间),ctypes.c_void_p(handle) 会把这个负数补成
0xFFFFFFFFxxxxxxxx 的错指针,GlobalLock 拿不到数据 → 读出来是空的。

现在给所有 Win32 调用都声明了 argtypes / restype,
GetClipboardData 和 GlobalLock 的 restype 是 wintypes.HANDLE。

开关打开后线程压根没起

监听线程之前是「启动那一刻设置已经开着才起」。于是:

  • 用户从设置页把开关打开 → 设置写对了,线程没跑 → 没反应
  • 用户从 Windows 托盘把开关打开 → 同上,而且托盘那条路连
    start() 都不调

现在线程无条件常驻,「开不开」由它每轮自己读设置决定
(_capture_enabled 本来就是这么设计的,只是从来没人保证线程在跑)。
任何入口改开关,下一轮就生效。

三个入口统一走一个出口

菜单栏、托盘、设置页之前各写各的设置键——写的是同一个键倒没错,但
只有设置页那条会推 settings_updated,菜单栏和托盘勾完之后,
设置页上的开关还是老样子,用户以为没生效。现在三个入口都走
GuiBridge.set_clipboard_monitor():写设置 + 推事件 + 状态一致。

读失败不再静默

之前读失败只有一条 debug 级日志,而 Windows 用户默认日志级别是
warning——那条根本看不到,「剪贴板监控用不了」在用户那边是一个完全
无从查起的问题。现在失败是 warning 级(30 秒节流,不刷屏)。

设置页补了一条说明

「剪贴板链接自动识别」旁边加了一句:只有扩展名能识别成文件类型
(zip / dmg / mp4 …)的链接才会提示,聊天里的普通链接不会打扰你。
这是 Swift v1.0.7 起就有的过滤规则,但从来没在界面上说过。


v2.2.0

浏览器下载捕获——Chrome / Edge / Firefox / Safari 四个浏览器的下载
可以交给本程序接管。

之前这套东西其实写好了(契约 §4 那一整套:协议、原生宿主、回环通道、
四个扩展目录都在仓库里),但用户没法用:扩展只躺在源码仓库里,
应用里没有任何地方告诉用户「扩展在哪、装没装、token 是多少」——
连 Safari 那份连配对入口都没有。这一版把缺的那一段补齐。

设置页新增「浏览器接管」板块

打开「设置 → 浏览器接管」,能看到:

  • 配对 token:可复制、可重新生成(重新生成 = 旧 token 立刻作废,
    拿着旧 token 的扩展再也派不了单)
  • 每个浏览器的扩展目录:点「打开扩展目录」直接在访达/资源管理器里打开
  • 每个浏览器装没装:应用自己扫扩展目录、查 pluginkit 得到的真实状态
  • 每个浏览器的安装入口:点「打开扩展管理页」直接跳到浏览器的扩展页

扩展文件现在跟着应用一起打包(macOS 在 Contents/Resources/extensions/,
Windows 在 onefile 的 sys._MEIPASS/extensions/),不用再去源码仓库里找。

Safari:走官方的「未签名的临时扩展」路径

本项目没有 Apple Developer Program 开发者签名,所以 Safari 扩展
不签名,走 Apple 官方给未签名扩展留的路子(macOS 17 / Safari 17 起):

设置 → 高级 → 「显示网页开发者功能」→ 设置 → 开发者 → 「允许未签名的扩展」
→ 设置 → 开发者 → 「添加临时扩展…」→ 选扩展目录 → 设置 → 扩展里勾选启用。

不需要 Xcode,不需要开发者账号。

平台的硬限制如实标在界面和文档里:退出 Safari 会重置「允许未签名的扩展」,
临时扩展最长 24 小时。Safari 也只能手动派单——它没有
downloads.onDeterminingFilename 那种「下载开始前拦一道」的钩子,
硬做只能等下载开始后再取消,那时候文件已经在路上,用户会丢文件。

Safari 还必须手动粘一次 token:Safari 没有 Chrome 那种原生消息宿主,
只能走回环 HTTP 通道——而那个端口带着 Access-Control-Allow-Origin: *,
不能在上面开 /pair(那等于把 token 贴给任何网页,等于打开
「谁都能往下载器塞任务」的门)。扩展的选项页和弹窗里都加了 token 输入框。

Chrome / Edge:把扩展 ID 钉死

宿主清单里的 allowed_origins 必须跟扩展的 origin 精确一致,否则
Chrome 直接拒绝拉起宿主。而「加载已解压的扩展程序」装出来的扩展,
ID 默认按目录路径算哈希——换个机器、换个目录就变了,清单永远对不上。

所以 Chrome / Edge 的 manifest 里写死了一把公钥(key 字段),扩展 ID
由公钥算出来,恒定为 bangcjehjdodcpfekmnkfmbeffcclpih(Chrome 和 Edge 同一个)。
Firefox 不需要这一步:它的扩展 ID 是 manifest 里
browser_specific_settings.gecko.id 自己定的。

升级扩展时不要换这把公钥——一换 ID 就变了,所有装好的宿主清单全部失效。
有一条测试专门盯着「manifest 里的 key 算出来的 ID」和「代码里写死的 ID」
是否一致。

其他

  • Safari 扩展补了选项页(原来只有弹窗),版本号四个扩展统一到 2.2.0
  • 扩展 manifest 加了 options_ui,Chrome/Firefox 也能从扩展详情页进选项页

v2.1.3

修一个真正的架构 bug(下载任务根本跑不起来),再加上自动分类、全局 UA,
以及去掉手动分片数。

下载一直卡住、0 字节 —— 根因是事件循环被抹掉了

上一版把「卡在 0 KB/s」当成网络问题修了一轮(探测超时、重试预算)。
这次拿到进程内的 Python 栈才看清:下载任务压根没跑起来。

GuiBridge.set_window() 里有这么一段:从当前线程猜一个正在跑的
asyncio 循环,猜不到就把 _loop 置成 None。而窗口是在主线程里建的,
主线程上根本没有 asyncio 循环(引擎的循环在 sdm-engine 线程里)——
于是 set_window 把 set_loop() 刚设好的引擎循环抹成了 None。

_loop 是 None 之后,桥接层所有调用退化成 asyncio.run(coro):
一次性循环。engine.start() 在这个一次性循环里把下载任务
create_task 出去,asyncio.run 一返回就拆循环,任务在跑第一行之前
就被取消。结果就是:任务躺在列表里永远不动、0 字节、连一个 socket
都没建过(lsof 上看不到任何对外连接)。暂停/继续/取消也都打在错误的
循环上,所以怎么点都没反应。添加框里的「解析」能成功,是因为它自己
开循环同步取结果,不依赖引擎循环。

修法:

  • set_window() 不再碰 _loop——引擎循环只有一个来源:set_loop()
  • main() 里 set_loop() 挪到 set_window() 之后
  • 桥接层加了兜底:真的没设上循环时,自己起一个常驻的后台循环,
    绝不再退化成一次性的 asyncio.run
  • 连接池等待加了超时上限(pool=None 的意思是「等连接池空位时永不
    超时」,池满了就永久挂起)

自动分类

  • 分类下拉第一个选项是「自动」并且默认选中。早先前端把第一个选项
    (视频)当成分类发给后端,后端再覆盖掉引擎按 URL 猜出来的正确分类
    ——用户加一个 .dmg,列表里却归在「视频」。现在「自动」传的是 null,
    由引擎判断
  • 按服务器给的文件名判分类:URL 常常是无扩展名的 CDN 路径
    (/download/12345),真正的文件名在 Content-Disposition 里。
    探测成功后按真实文件名回填分类(.dmg → 程序),保存目录也跟着换
  • 用户手动改过分类就不再自动改

全局 UA

  • 设置页新增「全局 User-Agent」:留空 = 用程序内置的默认值,
    随时可以改,改完立刻对后续所有下载生效
  • 优先级:站点规则里的 UA → 全局 UA → 内置默认 UA
  • 清空输入框是「恢复默认」,不是「发一个空的 UA」

去掉手动分片数

添加对话框里那个默认 8 的分片数输入框没有了。分片本来就是引擎动态
规划的(起始连接数按文件大小和实测速度自己长),挂一个数字框只会
让人以为那个数字决定实际分片数。

其它

  • 探测失败的日志从 notice 提到 warning——用户把日志级别调到 warning
    时,那恰恰是排查「卡在连接中」最关键的一条线索
  • 引擎探测之后会按真实文件名修正任务的分类(用户没手动选过时)

v2.1.2

修两件事:下载一直卡在 0 KB/s,以及下载列表的界面错位。

「0 KB/s」那条查出来是三件事叠在一起:探测探了两次、探测用的超时是
分片那个 60 秒的看门狗、重试预算只算退避不算请求本身的时间。三条
一起,服务器慢的时候就是几分钟的静默——界面全程显示 0 KB/s,
还没有报错。界面错位那条更严重:列表的 grid 模板压根没生效。

界面

  • 下载列表的列布局修好了:表头和数据行用的是同一套 grid 模板
    (--row-cols,名字列是 1fr),但这个变量被写在了列表容器上,
    而它里面的 var(--w-*) 只定义在行和表头上——CSS 自定义属性是在
    声明它的那个元素上做替换的,容器上找不到 --w-name,整个模板
    就成了无效值,grid-template-columns 退回 none,所有列竖着
    叠起来
    (实测一行 281px 高,八个格子竖排)。现在模板写在用它的
    元素上,列宽和窄屏自适应一起恢复
  • 表头和数据行不再差 36px:列选择器按钮原来跟表头并排(flex),
    把表头挤窄了 36px,名字之后的每一列都整体错开。现在按钮浮在表头
    右端,表头拿回满宽
  • 数字列右对齐:表头的「大小 / 速度 / 剩余时间」是右对齐的,行里
    的数字却是左对齐的,标签和数字永远对不上
  • 连接阶段显示「连接中」:从任务开跑到收到第一个字节之间不再显示
    「0 KB/s」——那段时间确实一个字节都没拿到,但「还没开始」和「卡死
    了」是两回事
  • 顺手修掉 i18n 里 7 处乱码文案(「SHA-256:(0)」这类被二次编码
    过的字符串)

引擎

  • 探测不再探两次:引擎探一次拿真实文件名和总长度,下载流程又探
    一次——两个 RTT 的纯等待。现在探一次的结果直接交给流程层
  • 探测有自己的短超时:探测以前借用分片的 60 秒 read 超时(那是给
    慢连接用的空闲看门狗),服务器不回响应头时一次探测就静默 60 秒,
    重试几轮就是好几分钟。现在探测用 15 秒的独立超时
  • 重试预算真的约束总时间:预算以前只统计退避的 sleep,不统计请求
    本身耗掉的时间——服务器不响应时预算永远花不出去。现在请求耗时也
    计入预算,最长二十几秒就会给出明确的失败
  • 探测失败给一句人话:以前界面上显示的是 ReadTimeout 这种英文类名,
    现在是「连接服务器超时 / 连不上服务器 / 网络错误」

验证

  • 引擎侧新增 tests/test_connect_phase.py(探测预算、不重复探测、连接
    态生命周期),前端新增 tests/test_connecting_js.mjs 和
    tests/test_column_alignment_js.mjs
  • 列对齐用无头浏览器量像素验的(Chromium + WebKit 都跑过):修复
    前一行 281px 高、八格竖排;修复后行高 56px,表头和数据行的 grid
    轨道完全一致

已知限制

  • 仍不支持 BT / 磁力
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.1.1

修三个用户实际撞上的问题:改设置后页面总跳回顶部、开启开机自启会立刻多出一个进程、
解析链接一直在报「所有重试都失败」。顺带修好进度条最后一段经常少报一截的问题。

界面

  • 改设置不再跳回页面顶部:改任何设置都会重画整个设置页,而重画会把滚动位置一起
    冲掉——用户改个设置就被弹回顶部。现在重画前记下位置、重画后放回去
  • 任务被删掉导致列表重画时,设置页的滚动位置同样不再丢

macOS

  • 开启开机自启不再立刻多出一个 SDM 进程:之前写完 plist 还顺手跑了一次
    launchctl load,而 load 不只是「注册」,它当场就把任务跑起来了。自启的意思是
    「下次登录时启动」,不是「现在启动」。现在只写 plist,登录时 launchd 自己会扫

引擎

  • 磁力链接这类地址立刻失败并说清楚:磁力链接(magnet: 开头)以前会被当成网络
    错误重试 5 次,最后报一句「所有重试都失败」——跟真实原因完全对不上。现在直接告诉你
    「这是磁力链接(BT),SDM 只下载 HTTP(S) 的普通文件,不支持 BT/磁力」
  • 不可重试的失败不再重试:404(文件不存在)、416(服务器不认这个 Range)这类重试
    多少次结果都一样,现在一次就报错,不再白等 7.5 秒
  • 两处错误分类对齐:新建任务对话框和任务列表现在用同一份分类。磁力链接在两边都
    归到「客户端错误」,不再在任务列表里显示成「网络连接中断,将自动重试」还标着可
    重试;404 归到「文件不存在」;429(被限流)归到「可重试」
  • 进度条最后一段不再少报:下载快结束时进度会停在 98.97% 再也不动(2 MB 的文件
    最终报成 2031616,正好少一个 64 KB 块)。根因是进度合并窗口到期时发的是上一个
    窗口攒的旧值,把最后那次上报直接抹掉了
  • 续传时进度不再倒退一次:续传的汇总基数没算盘上已有的字节,第一次上报会比实际
    少,进度条会往回跳一下,紧接着算出来的速度也是个巨大的假值

已知限制

  • 仍不支持 BT / 磁力(要做磁力得整个塞一个 BT 客户端进来,不在当前范围内);现在遇到
    这类地址会立刻说清楚原因,而不是重试到天荒地老
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.1.0

这一版按 DeepSeek 规格做了整轮补齐,重点在引擎和错误处理——这两块是真正
的壁垒,其余是体验放大器。

引擎

  • 动态分段:分片布局在下载过程中会长大,不再是开跑前一次性切死。从 2 条
    连接起步,有实打实的进度才逐级放开到设定上限;单个分片跑到一半会被拆成两半,
    拆出来的另一半另开连接继续拉
  • 无 Range 自动回退:服务器对 Range 请求返回 200 而不是 206 时,自动退化成
    单流下载,不再把整份文件当成分片数据写坏
  • 续传校验:会话文件里记了 ETag / Last-Modified / Content-Length,资源变了
    就不再接着旧进度写,任务转成「资源已变更」而不是悄悄写出一个坏文件
  • 会话落盘:分片布局写成 <文件名>.sdm.session.json,崩溃或被杀之后能按原
    布局接着下
  • 进度报增量:文件级进度是所有分片增量之和。修好了进度虚高 2.5 倍的 bug
    (2 MB 的文件一度报成 5 MB)
  • 代理:支持 http / https 原生代理,SOCKS4 / SOCKS5 走 httpx-socks
  • 校验和:可选 md5 / sha1 / sha256,完成后自动算出校验值
  • 完成后行为:打开文件、打开文件夹、跑脚本、休眠,可按任务设置
  • 磁盘空间预检:开跑前检查剩余空间,不够就直接失败,不下到一半才发现
  • 名字冲突:默认保留两者(foo (1).zip),覆盖要走「移到废纸篓」,不会
    静默删掉用户已有文件
  • 任务队列:同时活跃的任务数有上限(默认 5),超出的排队,可置顶置底
  • 重试与改选项:失败任务可重试,代理 / 校验算法 / 完成后行为可以中途改

错误处理

  • 12 类结构化错误分类:网络、磁盘、权限、空间不足、认证、链接过期、服务器、
    限流、协议、不支持、用户取消、未知
  • 每类错误都带能不能自动重试和用户该做什么的中文说明
  • HTTP 状态码自动归类,403 / 410 / 451 归到「链接失效」,任务转「等待新链接」
  • 尊重 Retry-After,重试用指数退避 + 抖动,最多 5 次
  • 错误详情可以复制出来,粘到反馈里能看懂

浏览器接管

  • Native Messaging 宿主:Chrome / Edge / Firefox / Safari 扩展通过原生消息
    把下载派给应用,自动带上页面的 Cookie、User-Agent、Referer
  • 配对令牌:扩展第一次连接要跟应用配对,令牌存在本地,回环 HTTP 通道强制
    校验
  • Chrome / Edge 扩展重写:取消浏览器自带的下载,接管文件名、按分类分目录
  • Firefox 修好:以前把文件名当 URL 发出去,压根下不动
  • Safari 改成纯手动:右键菜单 + 工具栏按钮,不做自动拦截

界面

  • 底部状态栏:全局速度、限速、活跃任务数
  • 分类列 + 列显隐选择器
  • 新建任务对话框重写:多行 URL、保存目录、分类、代理、请求头键值行、分块数、
    开始时机(立即 / 排队 / 定时),带解析状态和重新解析
  • 详情面板:分片可视化 + 最近 60 秒的速度曲线
  • 浅色 / 深色主题切换,跟随系统
  • 设置里补齐 7 个新键(并发任务数、主题、代理、配对令牌、初始连接数、分片上限等)

macOS / Windows 系统集成

  • 菜单栏状态行:实时显示「下载中 N 个 · 速度」,1.5 秒刷一次;带暂停全部、
    继续全部、剪贴板监控勾选
  • Dock 图标显示整体下载进度
  • AppleScript 词典:可在 Script Editor 里 add download、pause all downloads、
    resume all downloads、get download list
  • Windows 托盘图标 + 与 macOS 一致的菜单
  • Windows URL Scheme 注册表项

打包

  • 新增 sdmd-native-host 产物,浏览器按清单拉起它
  • macOS 首次启动自动装原生消息宿主清单,Windows 解包后自动装
  • AppleScript 词典打进 .app,Info.plist 里写 OSAScriptingDefinition

已知限制

  • Safari 只做手动接管,不做自动拦截
  • 不支持 BT / 磁力
  • 没有 App Intents 原生集成
  • SOCKS 代理需要额外装 httpx-socks
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.0.6

  • 下载列表可以按文件名、大小、速度、状态排序,未知大小的任务排在最后
  • 速度显示改成平滑曲线,不再忽快忽慢;任务结束或暂停时速度归零
  • 下载完成后自动写进历史记录
  • 日志每天自动清理一次,改动日志设置时也会立即清理
  • 设置里可以直接打开 macOS 的 Console.app 看日志
  • 下载完成和失败时发系统通知;应用在前台时也弹横幅;点通知会把主窗口拉到前面
  • 站点规则对探测请求也生效,需要 Referer 或特定 UA 的站点不再一启动就失败
  • 命令行和后台守护支持用环境变量指定 socket 路径
  • 后台守护必须由系统服务启动,手动运行会给出提示而不是挂在前台
  • 剪贴板候选弹窗可以随时停止监听;已经在下载队列里的链接不再重复提示
  • 关闭自动分类后,所有下载落进下载目录根目录
  • 关掉主窗口后应用继续在菜单栏待着,可从菜单栏唤回
  • 关闭窗口时按设置隐藏 Dock 图标,从菜单栏唤回时恢复
  • 启动时自动同步开机自启状态
  • 带宽限制输入框的单位改成 KB/s,之前填的数值实际是按字节算的
  • 支持 `superdo...
Read more

v2.2.0

v2.2.0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 27 Oct 12:57

v2.2.0

浏览器下载捕获——Chrome / Edge / Firefox / Safari 四个浏览器的下载
可以交给本程序接管。

之前这套东西其实写好了(契约 §4 那一整套:协议、原生宿主、回环通道、
四个扩展目录都在仓库里),但用户没法用:扩展只躺在源码仓库里,
应用里没有任何地方告诉用户「扩展在哪、装没装、token 是多少」——
连 Safari 那份连配对入口都没有。这一版把缺的那一段补齐。

设置页新增「浏览器接管」板块

打开「设置 → 浏览器接管」,能看到:

  • 配对 token:可复制、可重新生成(重新生成 = 旧 token 立刻作废,
    拿着旧 token 的扩展再也派不了单)
  • 每个浏览器的扩展目录:点「打开扩展目录」直接在访达/资源管理器里打开
  • 每个浏览器装没装:应用自己扫扩展目录、查 pluginkit 得到的真实状态
  • 每个浏览器的安装入口:点「打开扩展管理页」直接跳到浏览器的扩展页

扩展文件现在跟着应用一起打包(macOS 在 Contents/Resources/extensions/,
Windows 在 onefile 的 sys._MEIPASS/extensions/),不用再去源码仓库里找。

Safari:走官方的「未签名的临时扩展」路径

本项目没有 Apple Developer Program 开发者签名,所以 Safari 扩展
不签名,走 Apple 官方给未签名扩展留的路子(macOS 17 / Safari 17 起):

设置 → 高级 → 「显示网页开发者功能」→ 设置 → 开发者 → 「允许未签名的扩展」
→ 设置 → 开发者 → 「添加临时扩展…」→ 选扩展目录 → 设置 → 扩展里勾选启用。

不需要 Xcode,不需要开发者账号。

平台的硬限制如实标在界面和文档里:退出 Safari 会重置「允许未签名的扩展」,
临时扩展最长 24 小时。Safari 也只能手动派单——它没有
downloads.onDeterminingFilename 那种「下载开始前拦一道」的钩子,
硬做只能等下载开始后再取消,那时候文件已经在路上,用户会丢文件。

Safari 还必须手动粘一次 token:Safari 没有 Chrome 那种原生消息宿主,
只能走回环 HTTP 通道——而那个端口带着 Access-Control-Allow-Origin: *,
不能在上面开 /pair(那等于把 token 贴给任何网页,等于打开
「谁都能往下载器塞任务」的门)。扩展的选项页和弹窗里都加了 token 输入框。

Chrome / Edge:把扩展 ID 钉死

宿主清单里的 allowed_origins 必须跟扩展的 origin 精确一致,否则
Chrome 直接拒绝拉起宿主。而「加载已解压的扩展程序」装出来的扩展,
ID 默认按目录路径算哈希——换个机器、换个目录就变了,清单永远对不上。

所以 Chrome / Edge 的 manifest 里写死了一把公钥(key 字段),扩展 ID
由公钥算出来,恒定为 bangcjehjdodcpfekmnkfmbeffcclpih(Chrome 和 Edge 同一个)。
Firefox 不需要这一步:它的扩展 ID 是 manifest 里
browser_specific_settings.gecko.id 自己定的。

升级扩展时不要换这把公钥——一换 ID 就变了,所有装好的宿主清单全部失效。
有一条测试专门盯着「manifest 里的 key 算出来的 ID」和「代码里写死的 ID」
是否一致。

其他

  • Safari 扩展补了选项页(原来只有弹窗),版本号四个扩展统一到 2.2.0
  • 扩展 manifest 加了 options_ui,Chrome/Firefox 也能从扩展详情页进选项页

v2.1.3

修一个真正的架构 bug(下载任务根本跑不起来),再加上自动分类、全局 UA,
以及去掉手动分片数。

下载一直卡住、0 字节 —— 根因是事件循环被抹掉了

上一版把「卡在 0 KB/s」当成网络问题修了一轮(探测超时、重试预算)。
这次拿到进程内的 Python 栈才看清:下载任务压根没跑起来。

GuiBridge.set_window() 里有这么一段:从当前线程猜一个正在跑的
asyncio 循环,猜不到就把 _loop 置成 None。而窗口是在主线程里建的,
主线程上根本没有 asyncio 循环(引擎的循环在 sdm-engine 线程里)——
于是 set_window 把 set_loop() 刚设好的引擎循环抹成了 None。

_loop 是 None 之后,桥接层所有调用退化成 asyncio.run(coro):
一次性循环。engine.start() 在这个一次性循环里把下载任务
create_task 出去,asyncio.run 一返回就拆循环,任务在跑第一行之前
就被取消。结果就是:任务躺在列表里永远不动、0 字节、连一个 socket
都没建过(lsof 上看不到任何对外连接)。暂停/继续/取消也都打在错误的
循环上,所以怎么点都没反应。添加框里的「解析」能成功,是因为它自己
开循环同步取结果,不依赖引擎循环。

修法:

  • set_window() 不再碰 _loop——引擎循环只有一个来源:set_loop()
  • main() 里 set_loop() 挪到 set_window() 之后
  • 桥接层加了兜底:真的没设上循环时,自己起一个常驻的后台循环,
    绝不再退化成一次性的 asyncio.run
  • 连接池等待加了超时上限(pool=None 的意思是「等连接池空位时永不
    超时」,池满了就永久挂起)

自动分类

  • 分类下拉第一个选项是「自动」并且默认选中。早先前端把第一个选项
    (视频)当成分类发给后端,后端再覆盖掉引擎按 URL 猜出来的正确分类
    ——用户加一个 .dmg,列表里却归在「视频」。现在「自动」传的是 null,
    由引擎判断
  • 按服务器给的文件名判分类:URL 常常是无扩展名的 CDN 路径
    (/download/12345),真正的文件名在 Content-Disposition 里。
    探测成功后按真实文件名回填分类(.dmg → 程序),保存目录也跟着换
  • 用户手动改过分类就不再自动改

全局 UA

  • 设置页新增「全局 User-Agent」:留空 = 用程序内置的默认值,
    随时可以改,改完立刻对后续所有下载生效
  • 优先级:站点规则里的 UA → 全局 UA → 内置默认 UA
  • 清空输入框是「恢复默认」,不是「发一个空的 UA」

去掉手动分片数

添加对话框里那个默认 8 的分片数输入框没有了。分片本来就是引擎动态
规划的(起始连接数按文件大小和实测速度自己长),挂一个数字框只会
让人以为那个数字决定实际分片数。

其它

  • 探测失败的日志从 notice 提到 warning——用户把日志级别调到 warning
    时,那恰恰是排查「卡在连接中」最关键的一条线索
  • 引擎探测之后会按真实文件名修正任务的分类(用户没手动选过时)

v2.1.2

修两件事:下载一直卡在 0 KB/s,以及下载列表的界面错位。

「0 KB/s」那条查出来是三件事叠在一起:探测探了两次、探测用的超时是
分片那个 60 秒的看门狗、重试预算只算退避不算请求本身的时间。三条
一起,服务器慢的时候就是几分钟的静默——界面全程显示 0 KB/s,
还没有报错。界面错位那条更严重:列表的 grid 模板压根没生效。

界面

  • 下载列表的列布局修好了:表头和数据行用的是同一套 grid 模板
    (--row-cols,名字列是 1fr),但这个变量被写在了列表容器上,
    而它里面的 var(--w-*) 只定义在行和表头上——CSS 自定义属性是在
    声明它的那个元素上做替换的,容器上找不到 --w-name,整个模板
    就成了无效值,grid-template-columns 退回 none,所有列竖着
    叠起来
    (实测一行 281px 高,八个格子竖排)。现在模板写在用它的
    元素上,列宽和窄屏自适应一起恢复
  • 表头和数据行不再差 36px:列选择器按钮原来跟表头并排(flex),
    把表头挤窄了 36px,名字之后的每一列都整体错开。现在按钮浮在表头
    右端,表头拿回满宽
  • 数字列右对齐:表头的「大小 / 速度 / 剩余时间」是右对齐的,行里
    的数字却是左对齐的,标签和数字永远对不上
  • 连接阶段显示「连接中」:从任务开跑到收到第一个字节之间不再显示
    「0 KB/s」——那段时间确实一个字节都没拿到,但「还没开始」和「卡死
    了」是两回事
  • 顺手修掉 i18n 里 7 处乱码文案(「SHA-256:(0)」这类被二次编码
    过的字符串)

引擎

  • 探测不再探两次:引擎探一次拿真实文件名和总长度,下载流程又探
    一次——两个 RTT 的纯等待。现在探一次的结果直接交给流程层
  • 探测有自己的短超时:探测以前借用分片的 60 秒 read 超时(那是给
    慢连接用的空闲看门狗),服务器不回响应头时一次探测就静默 60 秒,
    重试几轮就是好几分钟。现在探测用 15 秒的独立超时
  • 重试预算真的约束总时间:预算以前只统计退避的 sleep,不统计请求
    本身耗掉的时间——服务器不响应时预算永远花不出去。现在请求耗时也
    计入预算,最长二十几秒就会给出明确的失败
  • 探测失败给一句人话:以前界面上显示的是 ReadTimeout 这种英文类名,
    现在是「连接服务器超时 / 连不上服务器 / 网络错误」

验证

  • 引擎侧新增 tests/test_connect_phase.py(探测预算、不重复探测、连接
    态生命周期),前端新增 tests/test_connecting_js.mjs 和
    tests/test_column_alignment_js.mjs
  • 列对齐用无头浏览器量像素验的(Chromium + WebKit 都跑过):修复
    前一行 281px 高、八格竖排;修复后行高 56px,表头和数据行的 grid
    轨道完全一致

已知限制

  • 仍不支持 BT / 磁力
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.1.1

修三个用户实际撞上的问题:改设置后页面总跳回顶部、开启开机自启会立刻多出一个进程、
解析链接一直在报「所有重试都失败」。顺带修好进度条最后一段经常少报一截的问题。

界面

  • 改设置不再跳回页面顶部:改任何设置都会重画整个设置页,而重画会把滚动位置一起
    冲掉——用户改个设置就被弹回顶部。现在重画前记下位置、重画后放回去
  • 任务被删掉导致列表重画时,设置页的滚动位置同样不再丢

macOS

  • 开启开机自启不再立刻多出一个 SDM 进程:之前写完 plist 还顺手跑了一次
    launchctl load,而 load 不只是「注册」,它当场就把任务跑起来了。自启的意思是
    「下次登录时启动」,不是「现在启动」。现在只写 plist,登录时 launchd 自己会扫

引擎

  • 磁力链接这类地址立刻失败并说清楚:磁力链接(magnet: 开头)以前会被当成网络
    错误重试 5 次,最后报一句「所有重试都失败」——跟真实原因完全对不上。现在直接告诉你
    「这是磁力链接(BT),SDM 只下载 HTTP(S) 的普通文件,不支持 BT/磁力」
  • 不可重试的失败不再重试:404(文件不存在)、416(服务器不认这个 Range)这类重试
    多少次结果都一样,现在一次就报错,不再白等 7.5 秒
  • 两处错误分类对齐:新建任务对话框和任务列表现在用同一份分类。磁力链接在两边都
    归到「客户端错误」,不再在任务列表里显示成「网络连接中断,将自动重试」还标着可
    重试;404 归到「文件不存在」;429(被限流)归到「可重试」
  • 进度条最后一段不再少报:下载快结束时进度会停在 98.97% 再也不动(2 MB 的文件
    最终报成 2031616,正好少一个 64 KB 块)。根因是进度合并窗口到期时发的是上一个
    窗口攒的旧值,把最后那次上报直接抹掉了
  • 续传时进度不再倒退一次:续传的汇总基数没算盘上已有的字节,第一次上报会比实际
    少,进度条会往回跳一下,紧接着算出来的速度也是个巨大的假值

已知限制

  • 仍不支持 BT / 磁力(要做磁力得整个塞一个 BT 客户端进来,不在当前范围内);现在遇到
    这类地址会立刻说清楚原因,而不是重试到天荒地老
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.1.0

这一版按 DeepSeek 规格做了整轮补齐,重点在引擎和错误处理——这两块是真正
的壁垒,其余是体验放大器。

引擎

  • 动态分段:分片布局在下载过程中会长大,不再是开跑前一次性切死。从 2 条
    连接起步,有实打实的进度才逐级放开到设定上限;单个分片跑到一半会被拆成两半,
    拆出来的另一半另开连接继续拉
  • 无 Range 自动回退:服务器对 Range 请求返回 200 而不是 206 时,自动退化成
    单流下载,不再把整份文件当成分片数据写坏
  • 续传校验:会话文件里记了 ETag / Last-Modified / Content-Length,资源变了
    就不再接着旧进度写,任务转成「资源已变更」而不是悄悄写出一个坏文件
  • 会话落盘:分片布局写成 <文件名>.sdm.session.json,崩溃或被杀之后能按原
    布局接着下
  • 进度报增量:文件级进度是所有分片增量之和。修好了进度虚高 2.5 倍的 bug
    (2 MB 的文件一度报成 5 MB)
  • 代理:支持 http / https 原生代理,SOCKS4 / SOCKS5 走 httpx-socks
  • 校验和:可选 md5 / sha1 / sha256,完成后自动算出校验值
  • 完成后行为:打开文件、打开文件夹、跑脚本、休眠,可按任务设置
  • 磁盘空间预检:开跑前检查剩余空间,不够就直接失败,不下到一半才发现
  • 名字冲突:默认保留两者(foo (1).zip),覆盖要走「移到废纸篓」,不会
    静默删掉用户已有文件
  • 任务队列:同时活跃的任务数有上限(默认 5),超出的排队,可置顶置底
  • 重试与改选项:失败任务可重试,代理 / 校验算法 / 完成后行为可以中途改

错误处理

  • 12 类结构化错误分类:网络、磁盘、权限、空间不足、认证、链接过期、服务器、
    限流、协议、不支持、用户取消、未知
  • 每类错误都带能不能自动重试和用户该做什么的中文说明
  • HTTP 状态码自动归类,403 / 410 / 451 归到「链接失效」,任务转「等待新链接」
  • 尊重 Retry-After,重试用指数退避 + 抖动,最多 5 次
  • 错误详情可以复制出来,粘到反馈里能看懂

浏览器接管

  • Native Messaging 宿主:Chrome / Edge / Firefox / Safari 扩展通过原生消息
    把下载派给应用,自动带上页面的 Cookie、User-Agent、Referer
  • 配对令牌:扩展第一次连接要跟应用配对,令牌存在本地,回环 HTTP 通道强制
    校验
  • Chrome / Edge 扩展重写:取消浏览器自带的下载,接管文件名、按分类分目录
  • Firefox 修好:以前把文件名当 URL 发出去,压根下不动
  • Safari 改成纯手动:右键菜单 + 工具栏按钮,不做自动拦截

界面

  • 底部状态栏:全局速度、限速、活跃任务数
  • 分类列 + 列显隐选择器
  • 新建任务对话框重写:多行 URL、保存目录、分类、代理、请求头键值行、分块数、
    开始时机(立即 / 排队 / 定时),带解析状态和重新解析
  • 详情面板:分片可视化 + 最近 60 秒的速度曲线
  • 浅色 / 深色主题切换,跟随系统
  • 设置里补齐 7 个新键(并发任务数、主题、代理、配对令牌、初始连接数、分片上限等)

macOS / Windows 系统集成

  • 菜单栏状态行:实时显示「下载中 N 个 · 速度」,1.5 秒刷一次;带暂停全部、
    继续全部、剪贴板监控勾选
  • Dock 图标显示整体下载进度
  • AppleScript 词典:可在 Script Editor 里 add download、pause all downloads、
    resume all downloads、get download list
  • Windows 托盘图标 + 与 macOS 一致的菜单
  • Windows URL Scheme 注册表项

打包

  • 新增 sdmd-native-host 产物,浏览器按清单拉起它
  • macOS 首次启动自动装原生消息宿主清单,Windows 解包后自动装
  • AppleScript 词典打进 .app,Info.plist 里写 OSAScriptingDefinition

已知限制

  • Safari 只做手动接管,不做自动拦截
  • 不支持 BT / 磁力
  • 没有 App Intents 原生集成
  • SOCKS 代理需要额外装 httpx-socks
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.0.6

  • 下载列表可以按文件名、大小、速度、状态排序,未知大小的任务排在最后
  • 速度显示改成平滑曲线,不再忽快忽慢;任务结束或暂停时速度归零
  • 下载完成后自动写进历史记录
  • 日志每天自动清理一次,改动日志设置时也会立即清理
  • 设置里可以直接打开 macOS 的 Console.app 看日志
  • 下载完成和失败时发系统通知;应用在前台时也弹横幅;点通知会把主窗口拉到前面
  • 站点规则对探测请求也生效,需要 Referer 或特定 UA 的站点不再一启动就失败
  • 命令行和后台守护支持用环境变量指定 socket 路径
  • 后台守护必须由系统服务启动,手动运行会给出提示而不是挂在前台
  • 剪贴板候选弹窗可以随时停止监听;已经在下载队列里的链接不再重复提示
  • 关闭自动分类后,所有下载落进下载目录根目录
  • 关掉主窗口后应用继续在菜单栏待着,可从菜单栏唤回
  • 关闭窗口时按设置隐藏 Dock 图标,从菜单栏唤回时恢复
  • 启动时自动同步开机自启状态
  • 带宽限制输入框的单位改成 KB/s,之前填的数值实际是按字节算的
  • 支持 superdownloadmanager:// 链接唤起下载

v2.0.5

  • 修好分片计数:详情页的「已完成分片 / 总片数」以前一直是 0/0,现在正确显示
  • 最后一个分片完成后状态不再停留在未完成
  • 已完成的任务也能查看分片明细
  • 修好 socket 路径过长时 daemon 起不来:路径超过系统上限自动改用短路径
  • 打包时校验应用图标,没打进 bundle 直接让构建失败

v2.0.4

  • 详情页分片表可以展开看全部,默认仍只列前 64 个
  • 换任务时自动收起

v2.0.3

  • 修好应用图标:之前打包没传图标参数,访达和 Dock 里显示的是打包工具的默认图标
  • macOS 菜单栏常驻图标,菜单项跟桌面版一致:打开主窗口、加 URL、在访达显示下载目录、关于、设置、退出
  • Windows 也用同一个图标

v2.0.2

  • 一个包就够:命令行和后台守护都装在 app 里,首次启动自动装好
  • macOS 把 sdm 软链到 PATH 并注册开机自启
  • Windows 首次运行时释放命令行和守护进程,加进 PATH 并注册计划任务
  • macOS DMG 生成(build.py --mac)

v2.0.1

Python 重写版。跨 macOS、Windows、Linux,界面换成液态玻璃。

  • 引擎改用 httpx 真流式传输,字节到达即落盘
  • 分片下载默认 32 段,上限 128
  • 断点续传按已落盘字节继续
  • 链接过期(403/410/451)时任务转「等待新链接」,换新 URL 后从已完成分段续传
  • 全局限速、单任务限速、时间段限速规则
  • 自动文件分类,无扩展名文件按 magic byte 识别
  • 站点规则支持 Referer、User-Agent、Cookie、Basic Auth
  • 下载历史、计划任务、站点规则、日志、通知
  • 剪贴板候选确认、浏览器扩展派单
  • sdm 命令行,每个命令返回 JSON
  • 后台 daemon,开机自启
  • 窗口自由缩放,布局重排不裁切
  • 详情页显示真实分片状态
  • macOS DMG 生成(build.py --mac)

Windows 22H2 与 macOS 12+。

v2.1.3

Choose a tag to compare

@github-actions github-actions released this 27 Oct 11:46

v2.1.3

修一个真正的架构 bug(下载任务根本跑不起来),再加上自动分类、全局 UA,
以及去掉手动分片数。

下载一直卡住、0 字节 —— 根因是事件循环被抹掉了

上一版把「卡在 0 KB/s」当成网络问题修了一轮(探测超时、重试预算)。
这次拿到进程内的 Python 栈才看清:下载任务压根没跑起来。

GuiBridge.set_window() 里有这么一段:从当前线程猜一个正在跑的
asyncio 循环,猜不到就把 _loop 置成 None。而窗口是在主线程里建的,
主线程上根本没有 asyncio 循环(引擎的循环在 sdm-engine 线程里)——
于是 set_window 把 set_loop() 刚设好的引擎循环抹成了 None。

_loop 是 None 之后,桥接层所有调用退化成 asyncio.run(coro):
一次性循环。engine.start() 在这个一次性循环里把下载任务
create_task 出去,asyncio.run 一返回就拆循环,任务在跑第一行之前
就被取消。结果就是:任务躺在列表里永远不动、0 字节、连一个 socket
都没建过(lsof 上看不到任何对外连接)。暂停/继续/取消也都打在错误的
循环上,所以怎么点都没反应。添加框里的「解析」能成功,是因为它自己
开循环同步取结果,不依赖引擎循环。

修法:

  • set_window() 不再碰 _loop——引擎循环只有一个来源:set_loop()
  • main() 里 set_loop() 挪到 set_window() 之后
  • 桥接层加了兜底:真的没设上循环时,自己起一个常驻的后台循环,
    绝不再退化成一次性的 asyncio.run
  • 连接池等待加了超时上限(pool=None 的意思是「等连接池空位时永不
    超时」,池满了就永久挂起)

自动分类

  • 分类下拉第一个选项是「自动」并且默认选中。早先前端把第一个选项
    (视频)当成分类发给后端,后端再覆盖掉引擎按 URL 猜出来的正确分类
    ——用户加一个 .dmg,列表里却归在「视频」。现在「自动」传的是 null,
    由引擎判断
  • 按服务器给的文件名判分类:URL 常常是无扩展名的 CDN 路径
    (/download/12345),真正的文件名在 Content-Disposition 里。
    探测成功后按真实文件名回填分类(.dmg → 程序),保存目录也跟着换
  • 用户手动改过分类就不再自动改

全局 UA

  • 设置页新增「全局 User-Agent」:留空 = 用程序内置的默认值,
    随时可以改,改完立刻对后续所有下载生效
  • 优先级:站点规则里的 UA → 全局 UA → 内置默认 UA
  • 清空输入框是「恢复默认」,不是「发一个空的 UA」

去掉手动分片数

添加对话框里那个默认 8 的分片数输入框没有了。分片本来就是引擎动态
规划的(起始连接数按文件大小和实测速度自己长),挂一个数字框只会
让人以为那个数字决定实际分片数。

其它

  • 探测失败的日志从 notice 提到 warning——用户把日志级别调到 warning
    时,那恰恰是排查「卡在连接中」最关键的一条线索
  • 引擎探测之后会按真实文件名修正任务的分类(用户没手动选过时)

v2.1.2

修两件事:下载一直卡在 0 KB/s,以及下载列表的界面错位。

「0 KB/s」那条查出来是三件事叠在一起:探测探了两次、探测用的超时是
分片那个 60 秒的看门狗、重试预算只算退避不算请求本身的时间。三条
一起,服务器慢的时候就是几分钟的静默——界面全程显示 0 KB/s,
还没有报错。界面错位那条更严重:列表的 grid 模板压根没生效。

界面

  • 下载列表的列布局修好了:表头和数据行用的是同一套 grid 模板
    (--row-cols,名字列是 1fr),但这个变量被写在了列表容器上,
    而它里面的 var(--w-*) 只定义在行和表头上——CSS 自定义属性是在
    声明它的那个元素上做替换的,容器上找不到 --w-name,整个模板
    就成了无效值,grid-template-columns 退回 none,所有列竖着
    叠起来
    (实测一行 281px 高,八个格子竖排)。现在模板写在用它的
    元素上,列宽和窄屏自适应一起恢复
  • 表头和数据行不再差 36px:列选择器按钮原来跟表头并排(flex),
    把表头挤窄了 36px,名字之后的每一列都整体错开。现在按钮浮在表头
    右端,表头拿回满宽
  • 数字列右对齐:表头的「大小 / 速度 / 剩余时间」是右对齐的,行里
    的数字却是左对齐的,标签和数字永远对不上
  • 连接阶段显示「连接中」:从任务开跑到收到第一个字节之间不再显示
    「0 KB/s」——那段时间确实一个字节都没拿到,但「还没开始」和「卡死
    了」是两回事
  • 顺手修掉 i18n 里 7 处乱码文案(「SHA-256:(0)」这类被二次编码
    过的字符串)

引擎

  • 探测不再探两次:引擎探一次拿真实文件名和总长度,下载流程又探
    一次——两个 RTT 的纯等待。现在探一次的结果直接交给流程层
  • 探测有自己的短超时:探测以前借用分片的 60 秒 read 超时(那是给
    慢连接用的空闲看门狗),服务器不回响应头时一次探测就静默 60 秒,
    重试几轮就是好几分钟。现在探测用 15 秒的独立超时
  • 重试预算真的约束总时间:预算以前只统计退避的 sleep,不统计请求
    本身耗掉的时间——服务器不响应时预算永远花不出去。现在请求耗时也
    计入预算,最长二十几秒就会给出明确的失败
  • 探测失败给一句人话:以前界面上显示的是 ReadTimeout 这种英文类名,
    现在是「连接服务器超时 / 连不上服务器 / 网络错误」

验证

  • 引擎侧新增 tests/test_connect_phase.py(探测预算、不重复探测、连接
    态生命周期),前端新增 tests/test_connecting_js.mjs 和
    tests/test_column_alignment_js.mjs
  • 列对齐用无头浏览器量像素验的(Chromium + WebKit 都跑过):修复
    前一行 281px 高、八格竖排;修复后行高 56px,表头和数据行的 grid
    轨道完全一致

已知限制

  • 仍不支持 BT / 磁力
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.1.1

修三个用户实际撞上的问题:改设置后页面总跳回顶部、开启开机自启会立刻多出一个进程、
解析链接一直在报「所有重试都失败」。顺带修好进度条最后一段经常少报一截的问题。

界面

  • 改设置不再跳回页面顶部:改任何设置都会重画整个设置页,而重画会把滚动位置一起
    冲掉——用户改个设置就被弹回顶部。现在重画前记下位置、重画后放回去
  • 任务被删掉导致列表重画时,设置页的滚动位置同样不再丢

macOS

  • 开启开机自启不再立刻多出一个 SDM 进程:之前写完 plist 还顺手跑了一次
    launchctl load,而 load 不只是「注册」,它当场就把任务跑起来了。自启的意思是
    「下次登录时启动」,不是「现在启动」。现在只写 plist,登录时 launchd 自己会扫

引擎

  • 磁力链接这类地址立刻失败并说清楚:磁力链接(magnet: 开头)以前会被当成网络
    错误重试 5 次,最后报一句「所有重试都失败」——跟真实原因完全对不上。现在直接告诉你
    「这是磁力链接(BT),SDM 只下载 HTTP(S) 的普通文件,不支持 BT/磁力」
  • 不可重试的失败不再重试:404(文件不存在)、416(服务器不认这个 Range)这类重试
    多少次结果都一样,现在一次就报错,不再白等 7.5 秒
  • 两处错误分类对齐:新建任务对话框和任务列表现在用同一份分类。磁力链接在两边都
    归到「客户端错误」,不再在任务列表里显示成「网络连接中断,将自动重试」还标着可
    重试;404 归到「文件不存在」;429(被限流)归到「可重试」
  • 进度条最后一段不再少报:下载快结束时进度会停在 98.97% 再也不动(2 MB 的文件
    最终报成 2031616,正好少一个 64 KB 块)。根因是进度合并窗口到期时发的是上一个
    窗口攒的旧值,把最后那次上报直接抹掉了
  • 续传时进度不再倒退一次:续传的汇总基数没算盘上已有的字节,第一次上报会比实际
    少,进度条会往回跳一下,紧接着算出来的速度也是个巨大的假值

已知限制

  • 仍不支持 BT / 磁力(要做磁力得整个塞一个 BT 客户端进来,不在当前范围内);现在遇到
    这类地址会立刻说清楚原因,而不是重试到天荒地老
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.1.0

这一版按 DeepSeek 规格做了整轮补齐,重点在引擎和错误处理——这两块是真正
的壁垒,其余是体验放大器。

引擎

  • 动态分段:分片布局在下载过程中会长大,不再是开跑前一次性切死。从 2 条
    连接起步,有实打实的进度才逐级放开到设定上限;单个分片跑到一半会被拆成两半,
    拆出来的另一半另开连接继续拉
  • 无 Range 自动回退:服务器对 Range 请求返回 200 而不是 206 时,自动退化成
    单流下载,不再把整份文件当成分片数据写坏
  • 续传校验:会话文件里记了 ETag / Last-Modified / Content-Length,资源变了
    就不再接着旧进度写,任务转成「资源已变更」而不是悄悄写出一个坏文件
  • 会话落盘:分片布局写成 <文件名>.sdm.session.json,崩溃或被杀之后能按原
    布局接着下
  • 进度报增量:文件级进度是所有分片增量之和。修好了进度虚高 2.5 倍的 bug
    (2 MB 的文件一度报成 5 MB)
  • 代理:支持 http / https 原生代理,SOCKS4 / SOCKS5 走 httpx-socks
  • 校验和:可选 md5 / sha1 / sha256,完成后自动算出校验值
  • 完成后行为:打开文件、打开文件夹、跑脚本、休眠,可按任务设置
  • 磁盘空间预检:开跑前检查剩余空间,不够就直接失败,不下到一半才发现
  • 名字冲突:默认保留两者(foo (1).zip),覆盖要走「移到废纸篓」,不会
    静默删掉用户已有文件
  • 任务队列:同时活跃的任务数有上限(默认 5),超出的排队,可置顶置底
  • 重试与改选项:失败任务可重试,代理 / 校验算法 / 完成后行为可以中途改

错误处理

  • 12 类结构化错误分类:网络、磁盘、权限、空间不足、认证、链接过期、服务器、
    限流、协议、不支持、用户取消、未知
  • 每类错误都带能不能自动重试和用户该做什么的中文说明
  • HTTP 状态码自动归类,403 / 410 / 451 归到「链接失效」,任务转「等待新链接」
  • 尊重 Retry-After,重试用指数退避 + 抖动,最多 5 次
  • 错误详情可以复制出来,粘到反馈里能看懂

浏览器接管

  • Native Messaging 宿主:Chrome / Edge / Firefox / Safari 扩展通过原生消息
    把下载派给应用,自动带上页面的 Cookie、User-Agent、Referer
  • 配对令牌:扩展第一次连接要跟应用配对,令牌存在本地,回环 HTTP 通道强制
    校验
  • Chrome / Edge 扩展重写:取消浏览器自带的下载,接管文件名、按分类分目录
  • Firefox 修好:以前把文件名当 URL 发出去,压根下不动
  • Safari 改成纯手动:右键菜单 + 工具栏按钮,不做自动拦截

界面

  • 底部状态栏:全局速度、限速、活跃任务数
  • 分类列 + 列显隐选择器
  • 新建任务对话框重写:多行 URL、保存目录、分类、代理、请求头键值行、分块数、
    开始时机(立即 / 排队 / 定时),带解析状态和重新解析
  • 详情面板:分片可视化 + 最近 60 秒的速度曲线
  • 浅色 / 深色主题切换,跟随系统
  • 设置里补齐 7 个新键(并发任务数、主题、代理、配对令牌、初始连接数、分片上限等)

macOS / Windows 系统集成

  • 菜单栏状态行:实时显示「下载中 N 个 · 速度」,1.5 秒刷一次;带暂停全部、
    继续全部、剪贴板监控勾选
  • Dock 图标显示整体下载进度
  • AppleScript 词典:可在 Script Editor 里 add download、pause all downloads、
    resume all downloads、get download list
  • Windows 托盘图标 + 与 macOS 一致的菜单
  • Windows URL Scheme 注册表项

打包

  • 新增 sdmd-native-host 产物,浏览器按清单拉起它
  • macOS 首次启动自动装原生消息宿主清单,Windows 解包后自动装
  • AppleScript 词典打进 .app,Info.plist 里写 OSAScriptingDefinition

已知限制

  • Safari 只做手动接管,不做自动拦截
  • 不支持 BT / 磁力
  • 没有 App Intents 原生集成
  • SOCKS 代理需要额外装 httpx-socks
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.0.6

  • 下载列表可以按文件名、大小、速度、状态排序,未知大小的任务排在最后
  • 速度显示改成平滑曲线,不再忽快忽慢;任务结束或暂停时速度归零
  • 下载完成后自动写进历史记录
  • 日志每天自动清理一次,改动日志设置时也会立即清理
  • 设置里可以直接打开 macOS 的 Console.app 看日志
  • 下载完成和失败时发系统通知;应用在前台时也弹横幅;点通知会把主窗口拉到前面
  • 站点规则对探测请求也生效,需要 Referer 或特定 UA 的站点不再一启动就失败
  • 命令行和后台守护支持用环境变量指定 socket 路径
  • 后台守护必须由系统服务启动,手动运行会给出提示而不是挂在前台
  • 剪贴板候选弹窗可以随时停止监听;已经在下载队列里的链接不再重复提示
  • 关闭自动分类后,所有下载落进下载目录根目录
  • 关掉主窗口后应用继续在菜单栏待着,可从菜单栏唤回
  • 关闭窗口时按设置隐藏 Dock 图标,从菜单栏唤回时恢复
  • 启动时自动同步开机自启状态
  • 带宽限制输入框的单位改成 KB/s,之前填的数值实际是按字节算的
  • 支持 superdownloadmanager:// 链接唤起下载

v2.0.5

  • 修好分片计数:详情页的「已完成分片 / 总片数」以前一直是 0/0,现在正确显示
  • 最后一个分片完成后状态不再停留在未完成
  • 已完成的任务也能查看分片明细
  • 修好 socket 路径过长时 daemon 起不来:路径超过系统上限自动改用短路径
  • 打包时校验应用图标,没打进 bundle 直接让构建失败

v2.0.4

  • 详情页分片表可以展开看全部,默认仍只列前 64 个
  • 换任务时自动收起

v2.0.3

  • 修好应用图标:之前打包没传图标参数,访达和 Dock 里显示的是打包工具的默认图标
  • macOS 菜单栏常驻图标,菜单项跟桌面版一致:打开主窗口、加 URL、在访达显示下载目录、关于、设置、退出
  • Windows 也用同一个图标

v2.0.2

  • 一个包就够:命令行和后台守护都装在 app 里,首次启动自动装好
  • macOS 把 sdm 软链到 PATH 并注册开机自启
  • Windows 首次运行时释放命令行和守护进程,加进 PATH 并注册计划任务
  • macOS DMG 生成(build.py --mac)

v2.0.1

Python 重写版。跨 macOS、Windows、Linux,界面换成液态玻璃。

  • 引擎改用 httpx 真流式传输,字节到达即落盘
  • 分片下载默认 32 段,上限 128
  • 断点续传按已落盘字节继续
  • 链接过期(403/410/451)时任务转「等待新链接」,换新 URL 后从已完成分段续传
  • 全局限速、单任务限速、时间段限速规则
  • 自动文件分类,无扩展名文件按 magic byte 识别
  • 站点规则支持 Referer、User-Agent、Cookie、Basic Auth
  • 下载历史、计划任务、站点规则、日志、通知
  • 剪贴板候选确认、浏览器扩展派单
  • sdm 命令行,每个命令返回 JSON
  • 后台 daemon,开机自启
  • 窗口自由缩放,布局重排不裁切
  • 详情页显示真实分片状态
  • macOS DMG 生成(build.py --mac)

Windows 22H2 与 macOS 12+。

v2.1.2

v2.1.2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 26 Oct 15:35

v2.1.2

修两件事:下载一直卡在 0 KB/s,以及下载列表的界面错位。

「0 KB/s」那条查出来是三件事叠在一起:探测探了两次、探测用的超时是
分片那个 60 秒的看门狗、重试预算只算退避不算请求本身的时间。三条
一起,服务器慢的时候就是几分钟的静默——界面全程显示 0 KB/s,
还没有报错。界面错位那条更严重:列表的 grid 模板压根没生效。

界面

  • 下载列表的列布局修好了:表头和数据行用的是同一套 grid 模板
    (--row-cols,名字列是 1fr),但这个变量被写在了列表容器上,
    而它里面的 var(--w-*) 只定义在行和表头上——CSS 自定义属性是在
    声明它的那个元素上做替换的,容器上找不到 --w-name,整个模板
    就成了无效值,grid-template-columns 退回 none,所有列竖着
    叠起来
    (实测一行 281px 高,八个格子竖排)。现在模板写在用它的
    元素上,列宽和窄屏自适应一起恢复
  • 表头和数据行不再差 36px:列选择器按钮原来跟表头并排(flex),
    把表头挤窄了 36px,名字之后的每一列都整体错开。现在按钮浮在表头
    右端,表头拿回满宽
  • 数字列右对齐:表头的「大小 / 速度 / 剩余时间」是右对齐的,行里
    的数字却是左对齐的,标签和数字永远对不上
  • 连接阶段显示「连接中」:从任务开跑到收到第一个字节之间不再显示
    「0 KB/s」——那段时间确实一个字节都没拿到,但「还没开始」和「卡死
    了」是两回事
  • 顺手修掉 i18n 里 7 处乱码文案(「SHA-256:(0)」这类被二次编码
    过的字符串)

引擎

  • 探测不再探两次:引擎探一次拿真实文件名和总长度,下载流程又探
    一次——两个 RTT 的纯等待。现在探一次的结果直接交给流程层
  • 探测有自己的短超时:探测以前借用分片的 60 秒 read 超时(那是给
    慢连接用的空闲看门狗),服务器不回响应头时一次探测就静默 60 秒,
    重试几轮就是好几分钟。现在探测用 15 秒的独立超时
  • 重试预算真的约束总时间:预算以前只统计退避的 sleep,不统计请求
    本身耗掉的时间——服务器不响应时预算永远花不出去。现在请求耗时也
    计入预算,最长二十几秒就会给出明确的失败
  • 探测失败给一句人话:以前界面上显示的是 ReadTimeout 这种英文类名,
    现在是「连接服务器超时 / 连不上服务器 / 网络错误」

验证

  • 引擎侧新增 tests/test_connect_phase.py(探测预算、不重复探测、连接
    态生命周期),前端新增 tests/test_connecting_js.mjs 和
    tests/test_column_alignment_js.mjs
  • 列对齐用无头浏览器量像素验的(Chromium + WebKit 都跑过):修复
    前一行 281px 高、八格竖排;修复后行高 56px,表头和数据行的 grid
    轨道完全一致

已知限制

  • 仍不支持 BT / 磁力
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.1.1

修三个用户实际撞上的问题:改设置后页面总跳回顶部、开启开机自启会立刻多出一个进程、
解析链接一直在报「所有重试都失败」。顺带修好进度条最后一段经常少报一截的问题。

界面

  • 改设置不再跳回页面顶部:改任何设置都会重画整个设置页,而重画会把滚动位置一起
    冲掉——用户改个设置就被弹回顶部。现在重画前记下位置、重画后放回去
  • 任务被删掉导致列表重画时,设置页的滚动位置同样不再丢

macOS

  • 开启开机自启不再立刻多出一个 SDM 进程:之前写完 plist 还顺手跑了一次
    launchctl load,而 load 不只是「注册」,它当场就把任务跑起来了。自启的意思是
    「下次登录时启动」,不是「现在启动」。现在只写 plist,登录时 launchd 自己会扫

引擎

  • 磁力链接这类地址立刻失败并说清楚:磁力链接(magnet: 开头)以前会被当成网络
    错误重试 5 次,最后报一句「所有重试都失败」——跟真实原因完全对不上。现在直接告诉你
    「这是磁力链接(BT),SDM 只下载 HTTP(S) 的普通文件,不支持 BT/磁力」
  • 不可重试的失败不再重试:404(文件不存在)、416(服务器不认这个 Range)这类重试
    多少次结果都一样,现在一次就报错,不再白等 7.5 秒
  • 两处错误分类对齐:新建任务对话框和任务列表现在用同一份分类。磁力链接在两边都
    归到「客户端错误」,不再在任务列表里显示成「网络连接中断,将自动重试」还标着可
    重试;404 归到「文件不存在」;429(被限流)归到「可重试」
  • 进度条最后一段不再少报:下载快结束时进度会停在 98.97% 再也不动(2 MB 的文件
    最终报成 2031616,正好少一个 64 KB 块)。根因是进度合并窗口到期时发的是上一个
    窗口攒的旧值,把最后那次上报直接抹掉了
  • 续传时进度不再倒退一次:续传的汇总基数没算盘上已有的字节,第一次上报会比实际
    少,进度条会往回跳一下,紧接着算出来的速度也是个巨大的假值

已知限制

  • 仍不支持 BT / 磁力(要做磁力得整个塞一个 BT 客户端进来,不在当前范围内);现在遇到
    这类地址会立刻说清楚原因,而不是重试到天荒地老
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.1.0

这一版按 DeepSeek 规格做了整轮补齐,重点在引擎和错误处理——这两块是真正
的壁垒,其余是体验放大器。

引擎

  • 动态分段:分片布局在下载过程中会长大,不再是开跑前一次性切死。从 2 条
    连接起步,有实打实的进度才逐级放开到设定上限;单个分片跑到一半会被拆成两半,
    拆出来的另一半另开连接继续拉
  • 无 Range 自动回退:服务器对 Range 请求返回 200 而不是 206 时,自动退化成
    单流下载,不再把整份文件当成分片数据写坏
  • 续传校验:会话文件里记了 ETag / Last-Modified / Content-Length,资源变了
    就不再接着旧进度写,任务转成「资源已变更」而不是悄悄写出一个坏文件
  • 会话落盘:分片布局写成 <文件名>.sdm.session.json,崩溃或被杀之后能按原
    布局接着下
  • 进度报增量:文件级进度是所有分片增量之和。修好了进度虚高 2.5 倍的 bug
    (2 MB 的文件一度报成 5 MB)
  • 代理:支持 http / https 原生代理,SOCKS4 / SOCKS5 走 httpx-socks
  • 校验和:可选 md5 / sha1 / sha256,完成后自动算出校验值
  • 完成后行为:打开文件、打开文件夹、跑脚本、休眠,可按任务设置
  • 磁盘空间预检:开跑前检查剩余空间,不够就直接失败,不下到一半才发现
  • 名字冲突:默认保留两者(foo (1).zip),覆盖要走「移到废纸篓」,不会
    静默删掉用户已有文件
  • 任务队列:同时活跃的任务数有上限(默认 5),超出的排队,可置顶置底
  • 重试与改选项:失败任务可重试,代理 / 校验算法 / 完成后行为可以中途改

错误处理

  • 12 类结构化错误分类:网络、磁盘、权限、空间不足、认证、链接过期、服务器、
    限流、协议、不支持、用户取消、未知
  • 每类错误都带能不能自动重试和用户该做什么的中文说明
  • HTTP 状态码自动归类,403 / 410 / 451 归到「链接失效」,任务转「等待新链接」
  • 尊重 Retry-After,重试用指数退避 + 抖动,最多 5 次
  • 错误详情可以复制出来,粘到反馈里能看懂

浏览器接管

  • Native Messaging 宿主:Chrome / Edge / Firefox / Safari 扩展通过原生消息
    把下载派给应用,自动带上页面的 Cookie、User-Agent、Referer
  • 配对令牌:扩展第一次连接要跟应用配对,令牌存在本地,回环 HTTP 通道强制
    校验
  • Chrome / Edge 扩展重写:取消浏览器自带的下载,接管文件名、按分类分目录
  • Firefox 修好:以前把文件名当 URL 发出去,压根下不动
  • Safari 改成纯手动:右键菜单 + 工具栏按钮,不做自动拦截

界面

  • 底部状态栏:全局速度、限速、活跃任务数
  • 分类列 + 列显隐选择器
  • 新建任务对话框重写:多行 URL、保存目录、分类、代理、请求头键值行、分块数、
    开始时机(立即 / 排队 / 定时),带解析状态和重新解析
  • 详情面板:分片可视化 + 最近 60 秒的速度曲线
  • 浅色 / 深色主题切换,跟随系统
  • 设置里补齐 7 个新键(并发任务数、主题、代理、配对令牌、初始连接数、分片上限等)

macOS / Windows 系统集成

  • 菜单栏状态行:实时显示「下载中 N 个 · 速度」,1.5 秒刷一次;带暂停全部、
    继续全部、剪贴板监控勾选
  • Dock 图标显示整体下载进度
  • AppleScript 词典:可在 Script Editor 里 add download、pause all downloads、
    resume all downloads、get download list
  • Windows 托盘图标 + 与 macOS 一致的菜单
  • Windows URL Scheme 注册表项

打包

  • 新增 sdmd-native-host 产物,浏览器按清单拉起它
  • macOS 首次启动自动装原生消息宿主清单,Windows 解包后自动装
  • AppleScript 词典打进 .app,Info.plist 里写 OSAScriptingDefinition

已知限制

  • Safari 只做手动接管,不做自动拦截
  • 不支持 BT / 磁力
  • 没有 App Intents 原生集成
  • SOCKS 代理需要额外装 httpx-socks
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.0.6

  • 下载列表可以按文件名、大小、速度、状态排序,未知大小的任务排在最后
  • 速度显示改成平滑曲线,不再忽快忽慢;任务结束或暂停时速度归零
  • 下载完成后自动写进历史记录
  • 日志每天自动清理一次,改动日志设置时也会立即清理
  • 设置里可以直接打开 macOS 的 Console.app 看日志
  • 下载完成和失败时发系统通知;应用在前台时也弹横幅;点通知会把主窗口拉到前面
  • 站点规则对探测请求也生效,需要 Referer 或特定 UA 的站点不再一启动就失败
  • 命令行和后台守护支持用环境变量指定 socket 路径
  • 后台守护必须由系统服务启动,手动运行会给出提示而不是挂在前台
  • 剪贴板候选弹窗可以随时停止监听;已经在下载队列里的链接不再重复提示
  • 关闭自动分类后,所有下载落进下载目录根目录
  • 关掉主窗口后应用继续在菜单栏待着,可从菜单栏唤回
  • 关闭窗口时按设置隐藏 Dock 图标,从菜单栏唤回时恢复
  • 启动时自动同步开机自启状态
  • 带宽限制输入框的单位改成 KB/s,之前填的数值实际是按字节算的
  • 支持 superdownloadmanager:// 链接唤起下载

v2.0.5

  • 修好分片计数:详情页的「已完成分片 / 总片数」以前一直是 0/0,现在正确显示
  • 最后一个分片完成后状态不再停留在未完成
  • 已完成的任务也能查看分片明细
  • 修好 socket 路径过长时 daemon 起不来:路径超过系统上限自动改用短路径
  • 打包时校验应用图标,没打进 bundle 直接让构建失败

v2.0.4

  • 详情页分片表可以展开看全部,默认仍只列前 64 个
  • 换任务时自动收起

v2.0.3

  • 修好应用图标:之前打包没传图标参数,访达和 Dock 里显示的是打包工具的默认图标
  • macOS 菜单栏常驻图标,菜单项跟桌面版一致:打开主窗口、加 URL、在访达显示下载目录、关于、设置、退出
  • Windows 也用同一个图标

v2.0.2

  • 一个包就够:命令行和后台守护都装在 app 里,首次启动自动装好
  • macOS 把 sdm 软链到 PATH 并注册开机自启
  • Windows 首次运行时释放命令行和守护进程,加进 PATH 并注册计划任务
  • macOS DMG 生成(build.py --mac)

v2.0.1

Python 重写版。跨 macOS、Windows、Linux,界面换成液态玻璃。

  • 引擎改用 httpx 真流式传输,字节到达即落盘
  • 分片下载默认 32 段,上限 128
  • 断点续传按已落盘字节继续
  • 链接过期(403/410/451)时任务转「等待新链接」,换新 URL 后从已完成分段续传
  • 全局限速、单任务限速、时间段限速规则
  • 自动文件分类,无扩展名文件按 magic byte 识别
  • 站点规则支持 Referer、User-Agent、Cookie、Basic Auth
  • 下载历史、计划任务、站点规则、日志、通知
  • 剪贴板候选确认、浏览器扩展派单
  • sdm 命令行,每个命令返回 JSON
  • 后台 daemon,开机自启
  • 窗口自由缩放,布局重排不裁切
  • 详情页显示真实分片状态
  • macOS DMG 生成(build.py --mac)

Windows 22H2 与 macOS 12+。

v2.1.1

v2.1.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 26 Oct 13:10

v2.1.1

修三个用户实际撞上的问题:改设置后页面总跳回顶部、开启开机自启会立刻多出一个进程、
解析链接一直在报「所有重试都失败」。顺带修好进度条最后一段经常少报一截的问题。

界面

  • 改设置不再跳回页面顶部:改任何设置都会重画整个设置页,而重画会把滚动位置一起
    冲掉——用户改个设置就被弹回顶部。现在重画前记下位置、重画后放回去
  • 任务被删掉导致列表重画时,设置页的滚动位置同样不再丢

macOS

  • 开启开机自启不再立刻多出一个 SDM 进程:之前写完 plist 还顺手跑了一次
    launchctl load,而 load 不只是「注册」,它当场就把任务跑起来了。自启的意思是
    「下次登录时启动」,不是「现在启动」。现在只写 plist,登录时 launchd 自己会扫

引擎

  • 磁力链接这类地址立刻失败并说清楚:磁力链接(magnet: 开头)以前会被当成网络
    错误重试 5 次,最后报一句「所有重试都失败」——跟真实原因完全对不上。现在直接告诉你
    「这是磁力链接(BT),SDM 只下载 HTTP(S) 的普通文件,不支持 BT/磁力」
  • 不可重试的失败不再重试:404(文件不存在)、416(服务器不认这个 Range)这类重试
    多少次结果都一样,现在一次就报错,不再白等 7.5 秒
  • 两处错误分类对齐:新建任务对话框和任务列表现在用同一份分类。磁力链接在两边都
    归到「客户端错误」,不再在任务列表里显示成「网络连接中断,将自动重试」还标着可
    重试;404 归到「文件不存在」;429(被限流)归到「可重试」
  • 进度条最后一段不再少报:下载快结束时进度会停在 98.97% 再也不动(2 MB 的文件
    最终报成 2031616,正好少一个 64 KB 块)。根因是进度合并窗口到期时发的是上一个
    窗口攒的旧值,把最后那次上报直接抹掉了
  • 续传时进度不再倒退一次:续传的汇总基数没算盘上已有的字节,第一次上报会比实际
    少,进度条会往回跳一下,紧接着算出来的速度也是个巨大的假值

已知限制

  • 仍不支持 BT / 磁力(要做磁力得整个塞一个 BT 客户端进来,不在当前范围内);现在遇到
    这类地址会立刻说清楚原因,而不是重试到天荒地老
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.1.0

这一版按 DeepSeek 规格做了整轮补齐,重点在引擎和错误处理——这两块是真正
的壁垒,其余是体验放大器。

引擎

  • 动态分段:分片布局在下载过程中会长大,不再是开跑前一次性切死。从 2 条
    连接起步,有实打实的进度才逐级放开到设定上限;单个分片跑到一半会被拆成两半,
    拆出来的另一半另开连接继续拉
  • 无 Range 自动回退:服务器对 Range 请求返回 200 而不是 206 时,自动退化成
    单流下载,不再把整份文件当成分片数据写坏
  • 续传校验:会话文件里记了 ETag / Last-Modified / Content-Length,资源变了
    就不再接着旧进度写,任务转成「资源已变更」而不是悄悄写出一个坏文件
  • 会话落盘:分片布局写成 <文件名>.sdm.session.json,崩溃或被杀之后能按原
    布局接着下
  • 进度报增量:文件级进度是所有分片增量之和。修好了进度虚高 2.5 倍的 bug
    (2 MB 的文件一度报成 5 MB)
  • 代理:支持 http / https 原生代理,SOCKS4 / SOCKS5 走 httpx-socks
  • 校验和:可选 md5 / sha1 / sha256,完成后自动算出校验值
  • 完成后行为:打开文件、打开文件夹、跑脚本、休眠,可按任务设置
  • 磁盘空间预检:开跑前检查剩余空间,不够就直接失败,不下到一半才发现
  • 名字冲突:默认保留两者(foo (1).zip),覆盖要走「移到废纸篓」,不会
    静默删掉用户已有文件
  • 任务队列:同时活跃的任务数有上限(默认 5),超出的排队,可置顶置底
  • 重试与改选项:失败任务可重试,代理 / 校验算法 / 完成后行为可以中途改

错误处理

  • 12 类结构化错误分类:网络、磁盘、权限、空间不足、认证、链接过期、服务器、
    限流、协议、不支持、用户取消、未知
  • 每类错误都带能不能自动重试和用户该做什么的中文说明
  • HTTP 状态码自动归类,403 / 410 / 451 归到「链接失效」,任务转「等待新链接」
  • 尊重 Retry-After,重试用指数退避 + 抖动,最多 5 次
  • 错误详情可以复制出来,粘到反馈里能看懂

浏览器接管

  • Native Messaging 宿主:Chrome / Edge / Firefox / Safari 扩展通过原生消息
    把下载派给应用,自动带上页面的 Cookie、User-Agent、Referer
  • 配对令牌:扩展第一次连接要跟应用配对,令牌存在本地,回环 HTTP 通道强制
    校验
  • Chrome / Edge 扩展重写:取消浏览器自带的下载,接管文件名、按分类分目录
  • Firefox 修好:以前把文件名当 URL 发出去,压根下不动
  • Safari 改成纯手动:右键菜单 + 工具栏按钮,不做自动拦截

界面

  • 底部状态栏:全局速度、限速、活跃任务数
  • 分类列 + 列显隐选择器
  • 新建任务对话框重写:多行 URL、保存目录、分类、代理、请求头键值行、分块数、
    开始时机(立即 / 排队 / 定时),带解析状态和重新解析
  • 详情面板:分片可视化 + 最近 60 秒的速度曲线
  • 浅色 / 深色主题切换,跟随系统
  • 设置里补齐 7 个新键(并发任务数、主题、代理、配对令牌、初始连接数、分片上限等)

macOS / Windows 系统集成

  • 菜单栏状态行:实时显示「下载中 N 个 · 速度」,1.5 秒刷一次;带暂停全部、
    继续全部、剪贴板监控勾选
  • Dock 图标显示整体下载进度
  • AppleScript 词典:可在 Script Editor 里 add download、pause all downloads、
    resume all downloads、get download list
  • Windows 托盘图标 + 与 macOS 一致的菜单
  • Windows URL Scheme 注册表项

打包

  • 新增 sdmd-native-host 产物,浏览器按清单拉起它
  • macOS 首次启动自动装原生消息宿主清单,Windows 解包后自动装
  • AppleScript 词典打进 .app,Info.plist 里写 OSAScriptingDefinition

已知限制

  • Safari 只做手动接管,不做自动拦截
  • 不支持 BT / 磁力
  • 没有 App Intents 原生集成
  • SOCKS 代理需要额外装 httpx-socks
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.0.6

  • 下载列表可以按文件名、大小、速度、状态排序,未知大小的任务排在最后
  • 速度显示改成平滑曲线,不再忽快忽慢;任务结束或暂停时速度归零
  • 下载完成后自动写进历史记录
  • 日志每天自动清理一次,改动日志设置时也会立即清理
  • 设置里可以直接打开 macOS 的 Console.app 看日志
  • 下载完成和失败时发系统通知;应用在前台时也弹横幅;点通知会把主窗口拉到前面
  • 站点规则对探测请求也生效,需要 Referer 或特定 UA 的站点不再一启动就失败
  • 命令行和后台守护支持用环境变量指定 socket 路径
  • 后台守护必须由系统服务启动,手动运行会给出提示而不是挂在前台
  • 剪贴板候选弹窗可以随时停止监听;已经在下载队列里的链接不再重复提示
  • 关闭自动分类后,所有下载落进下载目录根目录
  • 关掉主窗口后应用继续在菜单栏待着,可从菜单栏唤回
  • 关闭窗口时按设置隐藏 Dock 图标,从菜单栏唤回时恢复
  • 启动时自动同步开机自启状态
  • 带宽限制输入框的单位改成 KB/s,之前填的数值实际是按字节算的
  • 支持 superdownloadmanager:// 链接唤起下载

v2.0.5

  • 修好分片计数:详情页的「已完成分片 / 总片数」以前一直是 0/0,现在正确显示
  • 最后一个分片完成后状态不再停留在未完成
  • 已完成的任务也能查看分片明细
  • 修好 socket 路径过长时 daemon 起不来:路径超过系统上限自动改用短路径
  • 打包时校验应用图标,没打进 bundle 直接让构建失败

v2.0.4

  • 详情页分片表可以展开看全部,默认仍只列前 64 个
  • 换任务时自动收起

v2.0.3

  • 修好应用图标:之前打包没传图标参数,访达和 Dock 里显示的是打包工具的默认图标
  • macOS 菜单栏常驻图标,菜单项跟桌面版一致:打开主窗口、加 URL、在访达显示下载目录、关于、设置、退出
  • Windows 也用同一个图标

v2.0.2

  • 一个包就够:命令行和后台守护都装在 app 里,首次启动自动装好
  • macOS 把 sdm 软链到 PATH 并注册开机自启
  • Windows 首次运行时释放命令行和守护进程,加进 PATH 并注册计划任务
  • macOS DMG 生成(build.py --mac)

v2.0.1

Python 重写版。跨 macOS、Windows、Linux,界面换成液态玻璃。

  • 引擎改用 httpx 真流式传输,字节到达即落盘
  • 分片下载默认 32 段,上限 128
  • 断点续传按已落盘字节继续
  • 链接过期(403/410/451)时任务转「等待新链接」,换新 URL 后从已完成分段续传
  • 全局限速、单任务限速、时间段限速规则
  • 自动文件分类,无扩展名文件按 magic byte 识别
  • 站点规则支持 Referer、User-Agent、Cookie、Basic Auth
  • 下载历史、计划任务、站点规则、日志、通知
  • 剪贴板候选确认、浏览器扩展派单
  • sdm 命令行,每个命令返回 JSON
  • 后台 daemon,开机自启
  • 窗口自由缩放,布局重排不裁切
  • 详情页显示真实分片状态
  • macOS DMG 生成(build.py --mac)

Windows 22H2 与 macOS 12+。

v2.1.0

v2.1.0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 25 Oct 15:48

v2.1.0

引擎

  • 动态分段:分片布局在下载过程中会长大,不再是开跑前一次性切死。从 2 条
    连接起步,有实打实的进度才逐级放开到设定上限;单个分片跑到一半会被拆成两半,
    拆出来的另一半另开连接继续拉
  • 无 Range 自动回退:服务器对 Range 请求返回 200 而不是 206 时,自动退化成
    单流下载,不再把整份文件当成分片数据写坏
  • 续传校验:会话文件里记了 ETag / Last-Modified / Content-Length,资源变了
    就不再接着旧进度写,任务转成「资源已变更」而不是悄悄写出一个坏文件
  • 会话落盘:分片布局写成 <文件名>.sdm.session.json,崩溃或被杀之后能按原
    布局接着下
  • 进度报增量:文件级进度是所有分片增量之和。修好了进度虚高 2.5 倍的 bug
    (2 MB 的文件一度报成 5 MB)
  • 代理:支持 http / https 原生代理,SOCKS4 / SOCKS5 走 httpx-socks
  • 校验和:可选 md5 / sha1 / sha256,完成后自动算出校验值
  • 完成后行为:打开文件、打开文件夹、跑脚本、休眠,可按任务设置
  • 磁盘空间预检:开跑前检查剩余空间,不够就直接失败,不下到一半才发现
  • 名字冲突:默认保留两者(foo (1).zip),覆盖要走「移到废纸篓」,不会
    静默删掉用户已有文件
  • 任务队列:同时活跃的任务数有上限(默认 5),超出的排队,可置顶置底
  • 重试与改选项:失败任务可重试,代理 / 校验算法 / 完成后行为可以中途改

错误处理

  • 12 类结构化错误分类:网络、磁盘、权限、空间不足、认证、链接过期、服务器、
    限流、协议、不支持、用户取消、未知
  • 每类错误都带能不能自动重试和用户该做什么的中文说明
  • HTTP 状态码自动归类,403 / 410 / 451 归到「链接失效」,任务转「等待新链接」
  • 尊重 Retry-After,重试用指数退避 + 抖动,最多 5 次
  • 错误详情可以复制出来,粘到反馈里能看懂

浏览器接管

  • Native Messaging 宿主:Chrome / Edge / Firefox / Safari 扩展通过原生消息
    把下载派给应用,自动带上页面的 Cookie、User-Agent、Referer
  • 配对令牌:扩展第一次连接要跟应用配对,令牌存在本地,回环 HTTP 通道强制
    校验
  • Chrome / Edge 扩展重写:取消浏览器自带的下载,接管文件名、按分类分目录
  • Firefox 修好:以前把文件名当 URL 发出去,压根下不动
  • Safari 改成纯手动:右键菜单 + 工具栏按钮,不做自动拦截

界面

  • 底部状态栏:全局速度、限速、活跃任务数
  • 分类列 + 列显隐选择器
  • 新建任务对话框重写:多行 URL、保存目录、分类、代理、请求头键值行、分块数、
    开始时机(立即 / 排队 / 定时),带解析状态和重新解析
  • 详情面板:分片可视化 + 最近 60 秒的速度曲线
  • 浅色 / 深色主题切换,跟随系统
  • 设置里补齐 7 个新键(并发任务数、主题、代理、配对令牌、初始连接数、分片上限等)

macOS / Windows 系统集成

  • 菜单栏状态行:实时显示「下载中 N 个 · 速度」,1.5 秒刷一次;带暂停全部、
    继续全部、剪贴板监控勾选
  • Dock 图标显示整体下载进度
  • AppleScript 词典:可在 Script Editor 里 add download、pause all downloads、
    resume all downloads、get download list
  • Windows 托盘图标 + 与 macOS 一致的菜单
  • Windows URL Scheme 注册表项

打包

  • 新增 sdmd-native-host 产物,浏览器按清单拉起它
  • macOS 首次启动自动装原生消息宿主清单,Windows 解包后自动装
  • AppleScript 词典打进 .app,Info.plist 里写 OSAScriptingDefinition

已知限制

  • Safari 只做手动接管,不做自动拦截
  • 不支持 BT / 磁力
  • 没有 App Intents 原生集成
  • SOCKS 代理需要额外装 httpx-socks
  • 本版仍未经过大规模手测,按惯例发 Pre-release

v2.0.6

  • 下载列表可以按文件名、大小、速度、状态排序,未知大小的任务排在最后
  • 速度显示改成平滑曲线,不再忽快忽慢;任务结束或暂停时速度归零
  • 下载完成后自动写进历史记录
  • 日志每天自动清理一次,改动日志设置时也会立即清理
  • 设置里可以直接打开 macOS 的 Console.app 看日志
  • 下载完成和失败时发系统通知;应用在前台时也弹横幅;点通知会把主窗口拉到前面
  • 站点规则对探测请求也生效,需要 Referer 或特定 UA 的站点不再一启动就失败
  • 命令行和后台守护支持用环境变量指定 socket 路径
  • 后台守护必须由系统服务启动,手动运行会给出提示而不是挂在前台
  • 剪贴板候选弹窗可以随时停止监听;已经在下载队列里的链接不再重复提示
  • 关闭自动分类后,所有下载落进下载目录根目录
  • 关掉主窗口后应用继续在菜单栏待着,可从菜单栏唤回
  • 关闭窗口时按设置隐藏 Dock 图标,从菜单栏唤回时恢复
  • 启动时自动同步开机自启状态
  • 带宽限制输入框的单位改成 KB/s,之前填的数值实际是按字节算的
  • 支持 superdownloadmanager:// 链接唤起下载

v2.0.5

  • 修好分片计数:详情页的「已完成分片 / 总片数」以前一直是 0/0,现在正确显示
  • 最后一个分片完成后状态不再停留在未完成
  • 已完成的任务也能查看分片明细
  • 修好 socket 路径过长时 daemon 起不来:路径超过系统上限自动改用短路径
  • 打包时校验应用图标,没打进 bundle 直接让构建失败

v2.0.4

  • 详情页分片表可以展开看全部,默认仍只列前 64 个
  • 换任务时自动收起

v2.0.3

  • 修好应用图标:之前打包没传图标参数,访达和 Dock 里显示的是打包工具的默认图标
  • macOS 菜单栏常驻图标,菜单项跟桌面版一致:打开主窗口、加 URL、在访达显示下载目录、关于、设置、退出
  • Windows 也用同一个图标

v2.0.2

  • 一个包就够:命令行和后台守护都装在 app 里,首次启动自动装好
  • macOS 把 sdm 软链到 PATH 并注册开机自启
  • Windows 首次运行时释放命令行和守护进程,加进 PATH 并注册计划任务
  • macOS DMG 生成(build.py --mac)

v2.0.1

Python 重写版。跨 macOS、Windows、Linux,界面换成液态玻璃。

  • 引擎改用 httpx 真流式传输,字节到达即落盘
  • 分片下载默认 32 段,上限 128
  • 断点续传按已落盘字节继续
  • 链接过期(403/410/451)时任务转「等待新链接」,换新 URL 后从已完成分段续传
  • 全局限速、单任务限速、时间段限速规则
  • 自动文件分类,无扩展名文件按 magic byte 识别
  • 站点规则支持 Referer、User-Agent、Cookie、Basic Auth
  • 下载历史、计划任务、站点规则、日志、通知
  • 剪贴板候选确认、浏览器扩展派单
  • sdm 命令行,每个命令返回 JSON
  • 后台 daemon,开机自启
  • 窗口自由缩放,布局重排不裁切
  • 详情页显示真实分片状态
  • macOS DMG 生成(build.py --mac)

Windows 22H2 与 macOS 12+。

v2.0.6

v2.0.6 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 25 Oct 13:29

v2.0.6

  • 下载列表可以按文件名、大小、速度、状态排序,未知大小的任务排在最后
  • 速度显示改成平滑曲线,不再忽快忽慢;任务结束或暂停时速度归零
  • 下载完成后自动写进历史记录
  • 日志每天自动清理一次,改动日志设置时也会立即清理
  • 设置里可以直接打开 macOS 的 Console.app 看日志
  • 下载完成和失败时发系统通知;应用在前台时也弹横幅;点通知会把主窗口拉到前面
  • 站点规则对探测请求也生效,需要 Referer 或特定 UA 的站点不再一启动就失败
  • 命令行和后台守护支持用环境变量指定 socket 路径
  • 后台守护必须由系统服务启动,手动运行会给出提示而不是挂在前台
  • 剪贴板候选弹窗可以随时停止监听;已经在下载队列里的链接不再重复提示
  • 关闭自动分类后,所有下载落进下载目录根目录
  • 关掉主窗口后应用继续在菜单栏待着,可从菜单栏唤回
  • 关闭窗口时按设置隐藏 Dock 图标,从菜单栏唤回时恢复
  • 启动时自动同步开机自启状态
  • 带宽限制输入框的单位改成 KB/s,之前填的数值实际是按字节算的
  • 支持 superdownloadmanager:// 链接唤起下载

v2.0.5

  • 修好分片计数:详情页的「已完成分片 / 总片数」以前一直是 0/0,现在正确显示
  • 最后一个分片完成后状态不再停留在未完成
  • 已完成的任务也能查看分片明细
  • 修好 socket 路径过长时 daemon 起不来:路径超过系统上限自动改用短路径
  • 打包时校验应用图标,没打进 bundle 直接让构建失败

v2.0.4

  • 详情页分片表可以展开看全部,默认仍只列前 64 个
  • 换任务时自动收起

v2.0.3

  • 修好应用图标:之前打包没传图标参数,访达和 Dock 里显示的是打包工具的默认图标
  • macOS 菜单栏常驻图标,菜单项跟桌面版一致:打开主窗口、加 URL、在访达显示下载目录、关于、设置、退出
  • Windows 也用同一个图标

v2.0.2

  • 一个包就够:命令行和后台守护都装在 app 里,首次启动自动装好
  • macOS 把 sdm 软链到 PATH 并注册开机自启
  • Windows 首次运行时释放命令行和守护进程,加进 PATH 并注册计划任务
  • macOS DMG 生成(build.py --mac)

v2.0.1

Python 重写版。跨 macOS、Windows、Linux,界面换成液态玻璃。

  • 引擎改用 httpx 真流式传输,字节到达即落盘
  • 分片下载默认 32 段,上限 128
  • 断点续传按已落盘字节继续
  • 链接过期(403/410/451)时任务转「等待新链接」,换新 URL 后从已完成分段续传
  • 全局限速、单任务限速、时间段限速规则
  • 自动文件分类,无扩展名文件按 magic byte 识别
  • 站点规则支持 Referer、User-Agent、Cookie、Basic Auth
  • 下载历史、计划任务、站点规则、日志、通知
  • 剪贴板候选确认、浏览器扩展派单
  • sdm 命令行,每个命令返回 JSON
  • 后台 daemon,开机自启
  • 窗口自由缩放,布局重排不裁切
  • 详情页显示真实分片状态
  • macOS DMG 生成(build.py --mac)

Windows 22H2 与 macOS 12+。