跳转至

本机软件库

维护「重装系统程序清单」的操作手册。

流程:放安装包到 packages\ → 改 programs.json → 跑 python programs_server.py(起服务器)→ 本机点「运行」,局域网另有只读页面可点「本地下载」。


1. 目录结构

Text Only
program-list\
├── programs.json                  # 唯一数据源,手工编辑这个
├── programs_server.py             # 服务器 + 静态生成器(零依赖)
├── programs.html                  # --generate 产出的静态页,勿手改
├── README.md
├── icons\                         # 自动生成的图标缓存,可随时整个删掉
│   └── <sha1 前16位>.png
└── packages\                      # 安装包都放这里
    ├── python-3.15.2-amd64.exe
    ├── node-v26.13.0-x64.msi
    └── ...

路径一律用相对路径,从 programs.json 所在目录算起:

  • 安装包 → "file": "packages/python-3.15.2-amd64.exe"
  • 程序找文件 = json 所在目录 / file 字段,与盘符无关,整个文件夹搬到哪台机器都能用

2. 新增一个软件

第 1 步:安装包丢进 packages\

保持官方原文件名,别加空格和全角符号。

第 2 步:programs.json 的 programs 数组末尾加一条

JSON
{
  "name": "7-Zip",
  "name_cn": "7-Zip 压缩工具",
  "file": "packages/7z2600-x64.exe",
  "version": "26.00",
  "platform": "windows-x64",
  "category": "系统工具",
  "source_site": "7-zip.org",
  "page_url": "https://www.7-zip.org/download.html",
  "size_bytes": 1591816,
  "note": "",
  "updated_at": 1794465524627
}

上一项记得补逗号。updated_at 填当前**毫秒级**时间戳(Date.now() 风格,13 位),命令行取:date +%s%3N。

第 3 步:启动服务器

Bash
cd /d D:\Backup\program-list
python programs_server.py
Text Only
1
2
3
4
5
服务已启动 http://127.0.0.1:63515/
  局域网:http://10.1.25.35:63515/  (只读,不显示运行按钮)
  目录:D:\Backup\program-list
  运行按钮仅在本机 127.0.0.1 / localhost 打开时展示
  仅当 programs.json 变更时才重新生成,其余请求走缓存;Ctrl+C 退出

浏览器会自动打开(--no-open 可关掉)。改完 JSON 存盘,按 F5 就会看到新内容,不用重启服务。

本机打开时每张卡片右侧有蓝色「运行」按钮,点了就用系统默认程序打开对应的文件——.exe 直接执行,.msi 交给 Windows Installer,.zip 交给解压软件;本地包存在时按钮区还有「本地下载」,点它即以附件形式下载该文件。


3. 字段说明

meta(只保留两项)

字段 说明
title 页面大标题(当前为「本机软件库」)
timestamp 清单最后更新时刻,毫秒级时间戳(Date.now(),13 位),页面顶部「更新」显示它(格式化为 YYYY-MM-DD)

程序数、总体积、分类列表全部由程序在渲染时从 programs 数组和磁盘文件算出,不写回 JSON。

programs[]

字段 必填 说明
name ✅ 英文/原名,灰色小字,参与搜索
name_cn ✅ 中文名,卡片主标题;留空则回退用 name
file ✅ 相对路径 packages/xxx.exe,必须与磁盘完全一致。没有本地文件填 ""
version 建议 蓝色徽章
platform ✅ windows-x64 / macos-arm64 / macos-x64 / android
category ✅ 决定筛选 chip,取值见下
source_site 建议 来源网站,显示在「来源」
page_url ✅ 官方下载页,「官网下载」按钮;无来源填 "",按钮自动隐藏
size_bytes 可选 本地无文件时的兜底体积
note 可选 一行备注,留空不显示;当前全部为空
updated_at ✅ 该条最后修改时刻,毫秒级时间戳,页面显示为「更新 2026-11-12」

已废弃:download_url(直链)字段已从 JSON 全部移除。需要分发安装包时直接点卡片上的文件路径下载,不再记录外链直链。

category 取值(新增尽量复用):

Text Only
运行环境 · 开发工具 · 效率工具 · 设计工具 · AI 工具
系统工具 · 安全 · 远程工具 · 网络 · 办公协作 · 浏览器

file 为空串:不显示文件标签,也没有「本地下载」和「运行」按钮,只剩「官网下载」(如 Google Chrome)。


4. 图标

卡片左侧的图标是**自动从安装包里提取的**,不用手工准备:

  • 用 Windows 自带的 PrivateExtractIconsW 取 256px 图标,再自己编码成 PNG(zlib + struct,标准库,不需要 Pillow)
  • 缓存到 icons\<sha1(file)前16位>.png,浏览器直接按 /icons/xxx.png 请求(静态页则走相对路径)
  • 增量:只有图标不存在、或源文件比图标新时才重新提取。24 个包首次约 0.3 秒,之后零开销

取不到图标的会显示首字母块,这属于正常情况:

情况 说明
.msi / .zip 不是 PE 文件,本身没有图标资源(Node.js、RBTray 就是这种)
file 为空 本地没包(如 Chrome)
非 Windows 系统 提取依赖 Win32 API,直接全部回退成字母块

整个 icons\ 删掉也没关系,下次生成会自动重建。旧条目删掉后残留的图标不会自动清理(无害,手动删即可)。


5. 「本地下载」按钮

卡片右侧按钮区第一个就是「本地下载」,指向本地安装包:

模式 href
服务器 /download?file=packages/xxx.exe(新标签页,附件流式回传)
--generate 静态页 packages/xxx.exe(相对路径,file:// 下就是本地文件)

只有 file 指向的文件**存在**时才出现这个按钮;不存在则不渲染(避免点了 404)。左侧的文件路径标签只是展示信息,不可点。

服务端 GET /download 的处理:

  1. 复用安装包的同一套校验(必须 resolve 到 packages\ 内、文件必须存在),穿越路径返回 404
  2. Content-Type: application/octet-stream + Content-Length,256 KB 分块流式发送,大文件不占内存
  3. Content-Disposition: attachment,中文文件名用 RFC 5987 的 filename*=UTF-8'' 编码,下载下来名字不会乱码
  4. 不限制后缀——.zip / .apk / .dmg 也能下

所以想让某个包能被局域网下载,只要它躺在 packages\ 里并在 JSON 里写了正确的 file 就行。


6. 数组顺序 = 安装顺序

programs 下标即页面序号 1..N,不另设 install_order。重装时从上往下点「运行」即可。


7. 服务器模式(默认)

行为 说明
端口 默认自动挑选空闲端口;想固定用 --port 8080(被占用时会自动换)
监听 默认 0.0.0.0,局域网其它机器可访问(只读);--host 127.0.0.1 则仅本机
刷新 只有 programs.json 变了才重新渲染:每次请求只 stat 一次文件(mtime_ns + size),指纹没变直接返回内存里的缓存 HTML。本机和局域网页面按钮不同,各存一份
打开浏览器 启动 0.6 秒后自动打开;--no-open 关闭

运行按钮(仅本机)

服务端不是靠前端隐藏来控制的,GET / 时就按客户端来源决定渲不渲染:

  • 客户端 IP 必须是 127.0.0.1 / ::1
  • 且 Host 头必须是 127.0.0.1 / localhost(所以用本机局域网 IP 打开自己也是只读)
  • 两个条件都满足才输出「运行」按钮

POST /install 会**再校验一次**同样的条件,远程请求直接 403 {"ok":false,"message":"仅允许本机运行"}。绕过前端、直接 curl 也打不进来。

其余规则:

  • 只对 file 指向**存在**文件、且后缀为 .exe / .msi / .bat / .cmd 的条目显示
  • 一律用系统默认程序打开(os.startfile):.exe 直接执行、.msi 走 Windows Installer、.zip 走解压软件
  • 启动方式就是 os.startfile(等价双击),不做任何窗口监听、轮询或置前逻辑
  • 不限制后缀,packages\ 里放什么都能点「运行」
  • 装系统类软件会弹 UAC,需要你点「是」
  • 结果通过页面底部 toast 提示(「已启动 xxx」/「文件不存在」等)

安全约束(写在代码里,别绕过)

  • 运行和下载都只允许 packages\ 内的文件,../ 路径穿越直接拒绝(404)
  • 运行能力绑定本机回环 + Host 白名单,局域网访问天生只读
  • 局域网可下载 = 任何同网段的人都能拉走 packages\ 里的安装包,这是功能不是漏洞;不想这样就用 --host 127.0.0.1

静态模式

Bash
python programs_server.py --generate

生成 programs.html,没有运行按钮(POST /install 是服务端接口),「本地下载」的 href 是相对路径,双击本地打开页面后也能点它下载安装包。


8. 页面是什么样

  • 顶部:程序数 / 总体积 / 更新日期
  • 搜索框 + 分类 chip 筛选
  • 卡片:左侧**图标**(左上角小圆标是序号)、中文名、英文名、版本徽章、平台、分类标签、相对路径、来源、体积、更新日期
  • 左侧:文件路径标签(纯展示)
  • 右侧按钮,从左到右:本地下载 → 官网下载 → 运行(最后一个是蓝色主按钮,仅本机 + 服务器模式)

刻意不做的东西(别再加回来):快捷键监听、打印按钮、复制本地路径、复制直链、直链按钮、状态标记(本地就绪 / 需在线下载)。


9. 命令行

Bash
1
2
3
4
5
6
7
python programs_server.py                    # 起服务器(默认,自动端口,0.0.0.0,自动开浏览器)
python programs_server.py --port 8080        # 指定端口
python programs_server.py --host 127.0.0.1   # 只监听本机,局域网访问不到
python programs_server.py --no-open          # 起服务器但不自动开浏览器
python programs_server.py --generate         # 只生成静态 programs.html
python programs_server.py --generate -o 清单.html
python programs_server.py -i a.json          # 换数据源

10. 排错

现象 处理
体积显示不对 / 走到兜底 size_bytes file 与磁盘文件名不一致,或不在 packages\ 下。ls packages 对一下
点「运行」提示「文件不存在」 file 路径写错,或文件被移走
点「运行」后没反应 看服务端终端日志;UAC 弹窗可能被最小化或挡在其它窗口后
程序起来了但窗口在后台 已知行为,不处理。服务是后台进程,安装器窗口可能停在任务栏,点一下即可
服务启动后浏览器打不开 用终端里打印的 http://127.0.0.1:<端口>/ 手动访问,端口每次可能不同
局域网访问不了 本机防火墙拦了该端口;或启动时用了 --host 127.0.0.1
局域网页面没有「运行」按钮 正常设计:运行只在本机 127.0.0.1 / localhost 打开时出现
点文件路径报 404 file 路径写错或文件被移走,刷新页面后该标签会变回不可点
图标显示成首字母块 .msi / .zip 没有图标资源,属正常;.exe 也这样说明提取失败,删掉 icons\ 里对应文件重新刷新即可
图标全空 / 裂图 静态页被单独拷走而没带 icons\ 目录;把整个文件夹一起搬
下载的中文文件名乱码 用了很老的浏览器;接口已带 filename*=UTF-8'',Chrome/Edge/Firefox 都正常
端口固定失败 --port 指定的端口被占用时会自动换一个,看启动输出确认实际端口
中文乱码 JSON 存成 GBK 了,另存为 UTF-8 无 BOM
「JSON 解析失败」 漏逗号或末尾多逗号,用 python -m json.tool programs.json 查
改了 JSON 页面没变 看终端是「重新生成」还是「缓存」;若显示缓存说明文件没真正写入(编辑器可能用了临时文件替换,试试重新保存)。报错会返回 500 页面并打印原因
分类 chip 顺序怪 chip 按 category 首次出现顺序排,调整首条位置

11. 约束

  • 单文件自包含:CSS/JS 全内联,无 CDN、无外链字体
  • 只改 programs.json,programs.html 是产物
  • 零第三方依赖,Python 3.9+ 可跑
  • 全流程相对路径(含 icons/),目录整体搬家不用改任何配置