Compare commits

...

25 Commits

Author SHA1 Message Date
194866f306 update wps302 worker url 2025-11-12 21:47:50 +08:00
a28355f2c4 Update README 2025-11-10 10:49:26 +08:00
adb9c1863d update apt-ftparchive release 2025-11-09 22:37:43 +08:00
89143cdacd refactor: list page 2025-11-06 15:45:37 +08:00
7d1a73332c style: update 2025-11-05 11:08:32 +08:00
3448e12357 add eudic 2025-11-04 15:17:41 +08:00
f36e90c12a add remotedesktopmanager repo 2025-11-03 20:32:49 +08:00
a067cc55e2 fix: 修正 Packages 字段提取 2025-11-03 20:30:07 +08:00
4ff28c8476 add wezterm repo 2025-11-03 20:21:06 +08:00
e729997857 fix: 修改 Packages 分割方式 2025-11-03 20:16:26 +08:00
0157d85a2b add freedownloadmanager repo 2025-10-15 10:36:00 +08:00
79f78687ed update mihomo 2025-10-15 10:29:22 +08:00
edaace8a6e fix sunloginclient 2025-10-15 10:28:50 +08:00
702bbcaa5c style: update merge-apt-repo.py 2025-10-09 17:35:51 +08:00
f0bd23de24 add yesplaymusic 2025-10-09 17:19:26 +08:00
eff521bfc0 revert: remove debiancn repo, restore wps-office 2025-10-06 19:14:47 +08:00
a0c387dc00 add simplenote 2025-10-06 18:37:38 +08:00
4d15fd95c1 add fooyin 2025-10-06 18:33:51 +08:00
424f2d43b7 add spotify-client repo 2025-10-06 18:30:35 +08:00
2e17e9dd8a add anydesk repo 2025-10-06 18:29:33 +08:00
bdd43d63b5 add chrome-remote-desktop repo 2025-10-06 18:25:07 +08:00
c3b2823a9d add winboat 2025-09-29 18:30:08 +08:00
5698f8fb27 add splayer 2025-09-27 09:54:04 +08:00
e880333d90 update qqmusic 2025-09-26 10:22:47 +08:00
d70f685d5f refactor: save packages info to file 2025-09-25 10:04:18 +08:00
14 changed files with 279 additions and 136 deletions

4
.gitignore vendored
View File

@ -1,4 +1,6 @@
data/github-local.json data/github-local.json
data/deb.db data/deb.db
deb/ *.deb
*Packages
*Release*
__pycache__/ __pycache__/

View File

@ -1,29 +1,30 @@
# wcbing APT 软件源/仓库 # wcbing APT 软件源/仓库
适用于 Debian-based 发行版Debian/Ubuntu/Mint/...)的 APT 软件源/仓库,收集一些国内常用软件的二进制包。 适用于 Debian-based 发行版Debian/Ubuntu/Mint/...)的 APT 软件源/仓库,收集一些常用软件的二进制包。
本仓库仅提供链接重定向功能,致力于在不改动 APT 的情况下实现类似 WinGet、Homebrew Cask 等仅提供仓库索引的效果。 ## 特色功能
- 本仓库仅提供链接重定向功能,在不改动 APT 的情况下实现了类似 WinGet、Homebrew Cask 等仅提供仓库索引的效果,确保了来源的安全可靠并规避了分发二进制文件可能导致的版权风险。
- 添加国内常用软件,更适合中国用户使用。
- 支持 AppStream实验性可以与你喜欢的图形软件商店如 GNOME Software、KDE Plasma Discover结合使用。
## 使用现有仓库 ## 使用仓库
[现有仓库实例](https://packages.wcbing.top/deb/)查看使用方法。 点击 [现有仓库实例](https://packages.wcbing.top/deb/) 查看,里面有使用方法和[仓库内容列表](https://packages.wcbing.top/deb/list/)
## 收录的软件说明 ## 收录的软件说明
- 现主要服务 x86_64 用户,同时实验性支持 arm64。 - 现主要服务 x86_64 用户,同时实验性支持 arm64。
如有需要请参考最后一节自行建立仓库。 如有需要请参考最后一节自行建立仓库。
- 主要收录官方打包发布的文件,不接受第三方自行打包的商业软件 - 非开源、商业软件只收录官方打包发布的文件,不接受第三方自行打包的。
- 有固定的更新地址,如官网、官方仓库或 Github Releases。 - 有固定的更新地址,如官网、官方仓库或 Github Releases。
- 不收录打包的 wine 应用、Android 应用。 - 不收录打包的 wine 应用、Android 应用。
- 不收录图标、主题、字体等包,以后可能单独建一个相关仓库。 - 不收录图标、主题、字体等包,这些单独建一个相关仓库。
## 已收录软件 ## 部分已收录软件
> 部分应用长时间不更新,可能不会如下面一样编写脚本,最终请以 apt 查询到的为准。 ### 部分自行收集软件
### 自行收集
| 软件名 | 包名 | amd64 | arm64 | | 软件名 | 包名 | amd64 | arm64 |
| ----- | ---- | ----- | ----- | | ----- | ---- | ----- | ----- |
@ -31,6 +32,7 @@
| [QQ音乐](https://y.qq.com/download/download.html) | qqmusic | ✅ | | | [QQ音乐](https://y.qq.com/download/download.html) | qqmusic | ✅ | |
| [腾讯会议](https://meeting.tencent.com/download/) | wemeet | ✅ | ✅ | | [腾讯会议](https://meeting.tencent.com/download/) | wemeet | ✅ | ✅ |
| [腾讯文档](https://docs.qq.com/home/download) | tdappdesktop | ✅ | ✅ | | [腾讯文档](https://docs.qq.com/home/download) | tdappdesktop | ✅ | ✅ |
| [WPS Office](https://linux.wps.cn/) | wps-office | ✅ | |
| [百度网盘](https://pan.baidu.com/download) | baidunetdisk | ✅ | | | [百度网盘](https://pan.baidu.com/download) | baidunetdisk | ✅ | |
| [钉钉](https://www.dingtalk.com/download/) | com.alibabainc.dingtalk | ✅ | ✅ | | [钉钉](https://www.dingtalk.com/download/) | com.alibabainc.dingtalk | ✅ | ✅ |
| [飞书](https://www.feishu.cn/download) | bytedance-feishu-stable | ✅ | ✅ | | [飞书](https://www.feishu.cn/download) | bytedance-feishu-stable | ✅ | ✅ |
@ -42,12 +44,12 @@
| [ToDesk](https://www.todesk.com/linux.html) | todesk | ✅ | | | [ToDesk](https://www.todesk.com/linux.html) | todesk | ✅ | |
| [微信](https://linux.weixin.qq.com/) | wechat | ✅ | ✅ | | [微信](https://linux.weixin.qq.com/) | wechat | ✅ | ✅ |
| [kali-undercover](https://www.kali.org/docs/introduction/kali-undercover/) | kali-undercover | ✅ | ✅ | | [kali-undercover](https://www.kali.org/docs/introduction/kali-undercover/) | kali-undercover | ✅ | ✅ |
| [欧路词典](https://www.eudic.net/v4/en/app/download) | eudic | ✅ | |
### Github Releses ### Github Releses
> 因服务器资源有限,本仓库可能无法收录部分大文件。 > 也可投稿至星火商店的 [Github Releases 更新配置仓库](https://gitee.com/spark-building-service/github),其和本部分内容是同源的。
> 有收录需求也可投稿至星火商店的 [Github Releases 更新配置仓库](https://gitee.com/spark-building-service/github),其和本部分内容是同源的。
| 软件名 | 包名 | amd64 | arm64 | | 软件名 | 包名 | amd64 | arm64 |
| ----- | ---- | ----- | ----- | | ----- | ---- | ----- | ----- |
@ -72,6 +74,11 @@
| [Joplin](https://github.com/laurent22/joplin) | joplin | ✅ | | | [Joplin](https://github.com/laurent22/joplin) | joplin | ✅ | |
| [Tiny RDM](https://github.com/tiny-craft/tiny-rdm) | tinyrdm | ✅ | | | [Tiny RDM](https://github.com/tiny-craft/tiny-rdm) | tinyrdm | ✅ | |
| [MQTTX](https://github.com/emqx/MQTTX) | mqttx | ✅ | ✅ | | [MQTTX](https://github.com/emqx/MQTTX) | mqttx | ✅ | ✅ |
| [SPlayer](https://github.com/imsyy/SPlayer) | splayer | ✅ | |
| [WinBoat](https://github.com/TibixDev/winboat) | winboat | ✅ | |
| [fooyin](https://github.com/fooyin/fooyin) | fooyin | ✅ | |
| [Simplenote](https://github.com/Automattic/simplenote-electron) | simplenote | ✅ | ✅ |
| [YesPlayMusic](https://github.com/qier222/YesPlayMusic) | yesplaymusic | ✅ | ✅ |
#### Gitee #### Gitee
@ -80,13 +87,14 @@
| [星火应用商店](https://gitee.com/spark-store-project/spark-store) | spark-store | ✅ | ✅ | | [星火应用商店](https://gitee.com/spark-store-project/spark-store) | spark-store | ✅ | ✅ |
### 合并自现有 repo ### 合并自现有官方仓库
| 软件仓库 | 包名 | amd64 | arm64 | | 软件仓库 | 包名 | amd64 | arm64 |
| ------ | ---- | ----- | ----- | | ------ | ---- | ----- | ----- |
| [Mozilla Firefox](https://support.mozilla.org/zh-CN/kb/install-firefox-linux) | firefox<br />firefox_beta<br />firefox_devedition<br />firefox_nightly<br />firefox_esr<br />mozillavpn | ✅ | ✅ | | [Mozilla Firefox](https://support.mozilla.org/zh-CN/kb/install-firefox-linux) | firefox<br />firefox_beta<br />firefox_devedition<br />firefox_nightly<br />firefox_esr<br />mozillavpn | ✅ | ✅ |
| Google Chrome | google-chrome-stable<br />google-chrome-beta<br />google-chrome-unstable | ✅ | | | Google Chrome | google-chrome-stable<br />google-chrome-beta<br />google-chrome-unstable | ✅ | |
| Google Earth | google-earth-pro-stable<br />google-earth-ec-stable | ✅ | | | Google Earth | google-earth-pro-stable<br />google-earth-ec-stable | ✅ | |
| Chrome Remote Desktop | chrome-remote-desktop | ✅ | |
| Microsoft Edge | microsoft-edge-stable<br />microsoft-edge-beta<br />microsoft-edge-dev | ✅ | | | Microsoft Edge | microsoft-edge-stable<br />microsoft-edge-beta<br />microsoft-edge-dev | ✅ | |
| Opera | opera-stable<br />opera-beta<br />opera-developer | ✅ | | | Opera | opera-stable<br />opera-beta<br />opera-developer | ✅ | |
| Visual Studio Code | code<br />code-insiders<br />code-exploration | ✅ | ✅ | | Visual Studio Code | code<br />code-insiders<br />code-exploration | ✅ | ✅ |
@ -104,8 +112,12 @@
| [lazydocker: wcbing 打包](https://github.com/wcbing-build/lazydocker-debs) | lazydocker | ✅ | ✅ | | [lazydocker: wcbing 打包](https://github.com/wcbing-build/lazydocker-debs) | lazydocker | ✅ | ✅ |
| [lazygit: wcbing 打包](https://github.com/wcbing-build/lazygit-debs) | lazygit | ✅ | ✅ | | [lazygit: wcbing 打包](https://github.com/wcbing-build/lazygit-debs) | lazygit | ✅ | ✅ |
| [NextTrace](https://github.com/nxtrace/nexttrace-debs) | nexttrace | ✅ | ✅ | | [NextTrace](https://github.com/nxtrace/nexttrace-debs) | nexttrace | ✅ | ✅ |
| [Debian 中文社区软件源](https://github.com/debiancn/repo)[镜像](https://help.mirrors.cernet.edu.cn/debiancn/) | anydesk<br />marktext<br />wps-office<br />[更多](https://github.com/debiancn/repo) | ✅ | |
| [Gitea](https://gitlab.com/packaging/gitea)[镜像](https://mirrors.ustc.edu.cn/help/packaging-gitea.html) | gitea | ✅ | ✅ | | [Gitea](https://gitlab.com/packaging/gitea)[镜像](https://mirrors.ustc.edu.cn/help/packaging-gitea.html) | gitea | ✅ | ✅ |
| [AnyDesk](https://deb.anydesk.com/howto.html) | anydesk | ✅ | ✅ |
| [Spotify](https://www.spotify.com/sg-zh/download/linux/) | spotify-client | ✅ | |
| [Free Download Manager](https://www.freedownloadmanager.org/zh/download-fdm-for-linux.htm) | freedownloadmanager | ✅ | |
| [WezTerm](https://wezterm.org/install/linux.html#using-the-apt-repo) | wezterm<br />wezterm-nightly | ✅ | ✅ |
| [Remote Desktop Manager](https://docs.devolutions.net/rdm/installation/client/?tab=linux) | remotedesktopmanager | ✅ | ✅ |
## 自建仓库 ## 自建仓库

View File

@ -4,11 +4,13 @@ import os
import sqlite3 import sqlite3
import sys import sys
import logging import logging
import re
from threading import Lock from threading import Lock
BASE_DIR = "deb" DEB_BASE_DIR = "deb"
PACKAGES_DIR = "packages"
DB_DIR = "data" DB_DIR = "data"
USER_AGENT = "Debian APT-HTTP/1.3 (2.6.1)" # from Debian 12 USER_AGENT = "Debian APT-HTTP/1.3 (3.0.3)" # from Debian 13
version_lock = Lock() version_lock = Lock()
@ -19,43 +21,79 @@ logging.basicConfig(
) )
def download(url: str) -> None: def download(url: str, file_path: str) -> bool:
"""Download file using curl with APT User-Agent.""" """Download file using curl with APT User-Agent."""
file_path = os.path.join(BASE_DIR, url.split("?")[0]) curl_process = subprocess.run(
os.makedirs(os.path.dirname(file_path), exist_ok=True) ["curl", "-H", f"User-Agent: {USER_AGENT}", "-fsLo", file_path, url]
subprocess.run(["curl", "-H", f"User-Agent: {USER_AGENT}", "-fsLo", file_path, url]) )
if curl_process.returncode or not os.path.exists(file_path):
logging.error(f"Failed to download {url}")
return False
return True
def scan(name, arch, url, file_path) -> bool:
scan_process = subprocess.run(
["apt-ftparchive", "packages", file_path], capture_output=True
)
package = scan_process.stdout.decode()
package = re.sub(
r"^(Filename: ).*", f"\\1{url}", package, flags=re.MULTILINE
) # 替换 Filename 开头的行
package_file_path = os.path.join(PACKAGES_DIR, arch, f"{name}.package")
try:
with open(package_file_path, "w") as f:
f.write(package)
return True
except IOError as e:
logging.error(f"Failed to write package file for {name}: {e}")
return False
def check_download(name: str, version: str, url: str, arch: str) -> None: def check_download(name: str, version: str, url: str, arch: str) -> None:
"""Check and handle package download/update.""" """Check and handle package download/update."""
logging.info("%s:%s = %s", name, arch, version) logging.info("%s:%s = %s", name, arch, version)
db_path = os.path.join("data", f"{BASE_DIR}.db") file_path = os.path.join(DEB_BASE_DIR, arch, f"{name}_{version}_{arch}.deb")
local_version = None
db_path = os.path.join(DB_DIR, f"{DEB_BASE_DIR}.db")
# get local version # get local version
with version_lock, sqlite3.connect(db_path) as conn: with version_lock, sqlite3.connect(db_path) as conn:
res = conn.execute( res = conn.execute(
f"SELECT version, url FROM '{arch}' WHERE name = ?", (name,) f"SELECT version FROM '{arch}' WHERE name = ?", (name,)
).fetchone() ).fetchone()
if res: if res:
local_version, local_url = res local_version = res[0]
if local_version != version: if local_version == version:
print(f"Update: {name}:{arch} ({local_version} -> {version})") return
download(url)
# update database # download and scan
with version_lock, sqlite3.connect(db_path) as conn: logging.info(f"Downloading {name}:{arch} ({version})")
conn.execute( os.makedirs(os.path.join(DEB_BASE_DIR, arch), exist_ok=True)
f"UPDATE '{arch}' SET version = ?, url = ? WHERE name = ?", if not download(url, file_path):
(version, url, name), return
) logging.info(f"Downloaded {name}:{arch} ({version})")
conn.commit() os.makedirs(os.path.join(PACKAGES_DIR, arch), exist_ok=True)
# remove old version if not scan(name, arch, url, file_path):
if local_url != url: # 防止固定下载链接 return
old_file_path = os.path.join(BASE_DIR, local_url.split("?")[0])
if os.path.exists(old_file_path): if res:
os.remove(old_file_path) print(f"Update: {name}:{arch} ({local_version} -> {version})")
# update database
with version_lock, sqlite3.connect(db_path) as conn:
conn.execute(
f"UPDATE '{arch}' SET version = ?, url = ? WHERE name = ?",
(version, url, name),
)
conn.commit()
# remove old version
old_file_path = os.path.join(DEB_BASE_DIR, f"{name}_{local_version}_{arch}.deb")
if os.path.exists(old_file_path):
os.remove(old_file_path)
else: else:
print(f"AddNew: {name}:{arch} ({version})") print(f"AddNew: {name}:{arch} ({version})")
download(url)
# update database # update database
with version_lock, sqlite3.connect(db_path) as conn: with version_lock, sqlite3.connect(db_path) as conn:
conn.execute( conn.execute(

View File

@ -9,7 +9,7 @@
"mihomo": { "mihomo": {
"repo": "MetaCubeX/mihomo", "repo": "MetaCubeX/mihomo",
"file_list": { "file_list": {
"amd64": "mihomo-linux-amd64-compatible-{releases_tag}.deb", "amd64": "mihomo-linux-amd64-{releases_tag}.deb",
"arm64": "mihomo-linux-arm64-{releases_tag}.deb" "arm64": "mihomo-linux-arm64-{releases_tag}.deb"
} }
}, },
@ -138,5 +138,37 @@
"amd64": "MQTTX_{version}_amd64.deb", "amd64": "MQTTX_{version}_amd64.deb",
"arm64": "MQTTX_{version}_arm64.deb" "arm64": "MQTTX_{version}_arm64.deb"
} }
},
"splayer": {
"repo": "imsyy/SPlayer",
"file_list": {
"amd64": "splayer-{version}-amd64.deb"
}
},
"winboat": {
"repo": "TibixDev/winboat",
"file_list": {
"amd64": "winboat-{version}-amd64.deb"
}
},
"fooyin": {
"repo": "fooyin/fooyin",
"file_list": {
"amd64": "fooyin_{version}-trixie_amd64.deb"
}
},
"simplenote": {
"repo": "Automattic/simplenote-electron",
"file_list": {
"amd64": "Simplenote-linux-{version}-amd64.deb",
"arm64": "Simplenote-linux-{version}-arm64.deb"
}
},
"yesplaymusic": {
"repo": "qier222/YesPlayMusic",
"file_list": {
"amd64": "yesplaymusic_{version}_amd64.deb",
"arm64": "yesplaymusic_{version}_arm64.deb"
}
} }
} }

View File

@ -11,6 +11,12 @@
"amd64": "dists/stable/main/binary-amd64/Packages.gz" "amd64": "dists/stable/main/binary-amd64/Packages.gz"
} }
}, },
"chrome-remote-desktop": {
"repo": "https://dl.google.com/linux/chrome-remote-desktop/deb/",
"path": {
"amd64": "dists/stable/main/binary-amd64/Packages.gz"
}
},
"termius": { "termius": {
"repo": "https://deb.termius.com/", "repo": "https://deb.termius.com/",
"path": { "path": {
@ -111,12 +117,6 @@
"mix": "Packages" "mix": "Packages"
} }
}, },
"debiancn": {
"repo": "https://mirrors.cernet.edu.cn/debiancn/",
"path": {
"amd64": "dists/bookworm/main/binary-amd64/Packages.gz"
}
},
"gitea": { "gitea": {
"repo": "https://mirrors.ustc.edu.cn/packaging-gitea/", "repo": "https://mirrors.ustc.edu.cn/packaging-gitea/",
"path": { "path": {
@ -135,5 +135,37 @@
"path": { "path": {
"mix": "Packages" "mix": "Packages"
} }
} },
"anydesk": {
"repo": "https://deb.anydesk.com/",
"path": {
"amd64": "dists/all/main/binary-amd64/Packages",
"arm64": "dists/all/main/binary-arm64/Packages"
}
},
"spotify-client": {
"repo": "https://repository.spotify.com/",
"path": {
"amd64": "dists/stable/non-free/binary-amd64/Packages"
}
},
"freedownloadmanager": {
"repo": "https://debrepo.freedownloadmanager.org/",
"path": {
"amd64": "dists/jammy/main/binary-amd64/Packages.gz"
}
},
"wezterm": {
"repo": "https://apt.fury.io/wez/",
"path": {
"mix": "Packages"
}
},
"remotedesktopmanager": {
"repo": "https://dl.cloudsmith.io/public/devolutions/rdm/deb/debian/",
"path": {
"amd64": "dists/trixie/main/binary-amd64/Packages.gz",
"arm64": "dists/trixie/main/binary-arm64/Packages.gz"
}
}
} }

View File

@ -0,0 +1,9 @@
APT::FTPArchive::Release {
Origin "wcbing APT Repo";
Label "wcbing APT Repo";
Suite "wcbing";
Codename "wcbing";
Architectures "amd64 arm64";
Components "main";
Description "wcbing APT Repo || wcbing 的 APT 仓库";
}

View File

@ -7,7 +7,7 @@ import re
from concurrent.futures import ThreadPoolExecutor, wait from concurrent.futures import ThreadPoolExecutor, wait
from check_downloader import check_download from check_downloader import check_download
github_info_list = {} git_repo_list = {}
CONFIG = {"data_dir": "data", "proxy": "", "thread": 5} CONFIG = {"data_dir": "data", "proxy": "", "thread": 5}
@ -31,11 +31,11 @@ if __name__ == "__main__":
# read all repo info 读取所有仓库配置 # read all repo info 读取所有仓库配置
with open(os.path.join(CONFIG["data_dir"], "github.json"), "r") as f: with open(os.path.join(CONFIG["data_dir"], "github.json"), "r") as f:
github_info_list = json.load(f) git_repo_list = json.load(f)
tasks = [] tasks = []
with ThreadPoolExecutor(max_workers=CONFIG["thread"]) as executor: with ThreadPoolExecutor(max_workers=CONFIG["thread"]) as executor:
for name, repo in github_info_list.items(): for name, repo in git_repo_list.items():
if "site" in repo: if "site" in repo:
repo_url = os.path.join(repo["site"], repo['repo']) repo_url = os.path.join(repo["site"], repo['repo'])
else: else:

7
get/eudic.sh Normal file
View File

@ -0,0 +1,7 @@
WEB_CONTENT=$(curl -s https://www.eudic.net/v4/en/app/download)
AMD64_URL=$(echo $WEB_CONTENT | grep -o 'https://[^"]*\.deb[^"]*')
# https://www.eudic.net/download/eudic.deb?v=2025-08-25
VERSION=$(echo $AMD64_URL | cut -d '=' -f 2)
./check_downloader.py eudic "$VERSION" "$AMD64_URL" amd64

View File

@ -1,6 +1,6 @@
WEB_CONTENT=$(curl -s "https://y.qq.com/download/download.html") WEB_CONTENT=$(curl -s "https://y.qq.com/download/download.html")
VERSION=$(echo $WEB_CONTENT | grep -o "Linux <span class=\"product_list__version\">最新版:[0-9\.]*" | cut -d ':' -f 2) VERSION=$(echo $WEB_CONTENT | grep -o "Linux <span class=\"product_list__version\">最新版:[0-9\.]*" | cut -d ':' -f 2)
AMD64_URL=$(echo $WEB_CONTENT | grep -o "https://[0-9a-z/\._]*amd64\.deb" | head -n 1) AMD64_URL=$(echo $WEB_CONTENT | grep -o "https://[^\"]*amd64\.deb[^\"]*" | head -n 1)
./check_downloader.py qqmusic $VERSION $AMD64_URL amd64 ./check_downloader.py qqmusic $VERSION $AMD64_URL amd64

View File

@ -1,6 +1,6 @@
JSON=$(curl -fs "https://client-webapi.oray.com/softwares/SUNLOGIN_X_LINUX?x64=1") JSON=$(curl -fs "https://client-webapi.oray.com/softwares/SUNLOGIN_X_LINUX?x64=1")
VERSION=$(echo "$JSON" | jq -r '.versionno') VERSION=$(printf "%s" "$JSON" | jq -r '.versionno')
AMD64_URL=$(echo $JSON | jq -r '.downloadurl') AMD64_URL=$(printf "%s" "$JSON" | jq -r '.downloadurl')
./check_downloader.py sunloginclient $VERSION $AMD64_URL amd64 ./check_downloader.py sunloginclient $VERSION $AMD64_URL amd64

31
get/wps-office.sh Normal file
View File

@ -0,0 +1,31 @@
# decrypt() {
# url=$1
# pathname="/$(echo $url | cut -d '/' -f 4-)"
# secrity_key="7f8faaaa468174dc1c9cd62e5f218a5b"
# timestamp10=$(date '+%s')
# md5hash=$(echo -n "${secrity_key}${pathname}${timestamp10}" | md5sum | cut -d " " -f 1 )
# url="$url?t=${timestamp10}&k=${md5hash}"
# echo $url
# }
# WPS 官网 JS 代码大致逻辑如下:
# function downLoad(url) {
# var urlObj=new URL(url);
# var uri=urlObj.pathname;
# var secrityKey="7f8faaaa468174dc1c9cd62e5f218a5b";
# var timestamp10=Math.floor(new Date().getTime() / 1000);
# var md5hash=CryptoJS.MD5(secrityKey + uri + timestamp10);
# url += '?t=' + timestamp10 + '&k=' + md5hash
# console.log(url);
# }
WEB_CONTENT=$(curl -fs https://linux.wps.cn/)
VERSION=$(echo $WEB_CONTENT | grep -o "<p class=\"banner_txt\">[0-9.]*</p>" | sed 's/<p class=\"banner_txt\">\(.*\)<\/p>/\1/')
AMD64_ORI_URL=$(echo $WEB_CONTENT | grep -o "https://[0-9a-zA-Z_\/\.\-]*amd64\.deb" | head -n 1)
# AMD64_URL=$(decrypt $AMD64_ORI_URL)
# 使用 CloudFlare Workers 动态生成重定向链接,其基本逻辑如上方 JS 代码所示。
# 这样 Packages 中固定链接也可重定向至官网,不给官方白嫖流量的机会。
AMD64_URL="https://wps302.wcbing.top/$AMD64_ORI_URL"
./check_downloader.py wps-office $VERSION $AMD64_URL amd64

View File

@ -1,27 +0,0 @@
decrypt() {
url=$1
pathname="/$(echo $url | cut -d '/' -f 4-)"
secrity_key="7f8faaaa468174dc1c9cd62e5f218a5b"
timestamp10=$(date '+%s')
md5hash=$(echo -n "${secrity_key}${pathname}${timestamp10}" | md5sum | cut -d " " -f 1 )
url="$url?t=${timestamp10}&k=${md5hash}"
echo $url
# # js
# function downLoad(url) {
# var urlObj=new URL(url);
# var uri=urlObj.pathname;
# var secrityKey="7f8faaaa468174dc1c9cd62e5f218a5b";
# var timestamp10=Math.floor(new Date().getTime() / 1000);
# var md5hash=CryptoJS.MD5(secrityKey + uri + timestamp10);
# url += '?t=' + timestamp10 + '&k=' + md5hash
# console.log(url);
# }
}
WEB_CONTENT=$(curl -fs https://linux.wps.cn/)
VERSION=$(echo $WEB_CONTENT | grep -o "<p class=\"banner_txt\">[0-9.]*</p>" | sed 's/<p class=\"banner_txt\">\(.*\)<\/p>/\1/')
X64_ORI_URL=$(echo $WEB_CONTENT | grep -o "https://[0-9a-zA-Z_\/\.\-]*amd64\.deb" | head -n 1)
X64_URL=$(decrypt $X64_ORI_URL)
./check_downloader.py wps-office $VERSION $X64_URL

View File

@ -13,27 +13,37 @@ import sys
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from threading import Lock from threading import Lock
import apt_pkg import apt_pkg
from apt_pkg import version_compare
apt_pkg.init() # 初始化 apt_pkg apt_pkg.init() # 初始化 apt_pkg
package_version = {arch: {} for arch in ["all", "amd64", "i386", "arm64"]} USER_AGENT = "Debian APT-HTTP/1.3 (3.0.3)" # from Debian 13
package_info = {arch: {} for arch in ["all", "amd64", "i386", "arm64"]} arch_List = ["amd64", "arm64", "all", "i386"]
lock = {arch: Lock() for arch in ["all", "amd64", "i386", "arm64"]} lock = {arch: Lock() for arch in arch_List}
packages = {arch: {} for arch in arch_List} # 存放用于生成 Packages 的内容
USER_AGENT = "Debian APT-HTTP/1.3 (2.6.1)" # from Debian 12 """ packages format:
{
""" "arch": {
repo info json format: "package1": {
"repo_name": { "version": "1.0.0",
"repo": repo url, end with "/" "url": "https://example.com/package1.deb",
"xxx_path": { "package": ""
"arch": repo Packages file path of "arch", start with no "/" }
} }
} }
""" """
def read_repo_list(repo_list_file: str) -> dict: def read_repo_list(repo_list_file: str) -> dict:
"""
repo info json format:
"repo_name": {
"repo": repo url, end with "/" is better
"path": {
"arch": repo Packages file path of "arch", don't start with "/"
}
}
"""
try: try:
with open(repo_list_file, "r") as f: with open(repo_list_file, "r") as f:
return json.load(f) return json.load(f)
@ -46,7 +56,7 @@ def get_remote_packages(repo_url: str, file_path: str) -> bytes:
""" """
get the packages file content from remote repo get the packages file content from remote repo
""" """
file_url = repo_url + file_path file_url = os.path.join(repo_url, file_path)
try: try:
response = requests.get( response = requests.get(
file_url, timeout=10, headers={"User-Agent": USER_AGENT} file_url, timeout=10, headers={"User-Agent": USER_AGENT}
@ -67,38 +77,46 @@ def get_remote_packages(repo_url: str, file_path: str) -> bytes:
else: # Packages else: # Packages
content = response.content content = response.content
# complete the two newlines if the ending is less than two newlines
# 结尾不足两个换行符的话,补全两个换行符
if not content.endswith(b"\n\n"):
content += b"\n"
return content.replace(b"Filename: ", f"Filename: {repo_url}".encode()) return content.replace(b"Filename: ", f"Filename: {repo_url}".encode())
except Exception as e: except Exception as e:
logging.error(f"Error fetching packages: {e}") logging.error(f"Error fetching packages: {e}")
return b"" return b""
def get_latest(deb_packages: bytes): def split_latest(packages_file_content: bytes):
""" """
split the information of each packet, deduplication and store the latest in infoList split the information of each packet, deduplication and store the latest in infoList
将每个包的信息分割开去重并将最新的存放到 infoList 将每个包的信息分割开去重并将最新的存放到 infoList
""" """
deb_packages = re.sub(rb"^Package: ", b"{{start}}Package: ", deb_packages, flags=re.MULTILINE) # Remove trailing empty lines first
info_list = deb_packages.split(b"{{start}}")[1:] packages_file_content = packages_file_content.rstrip(b"\n\r\t ")
find_name = re.compile(rb"Package: (.+)") # split on two or more consecutive blank lines
find_arch = re.compile(rb"Architecture: (.+)") package_list = [
find_version = re.compile(rb"Version: (.+)") part + b"\n\n"
for part in re.split(rb"(?:\r?\n){2,}", packages_file_content)
if part.strip()
]
for v in info_list: find_name = re.compile(rb"Package:[ ]*(.+)")
find_arch = re.compile(rb"Architecture:[ ]*(.+)")
find_url = re.compile(rb"Filename:[ ]*(.+)")
find_version = re.compile(rb"Version:[ ]*(.+)")
for package in package_list:
name = "unknown"
try: try:
name = find_name.search(v).group(1).decode() name = find_name.search(package).group(1).decode()
arch = find_arch.search(v).group(1).decode() arch = find_arch.search(package).group(1).decode()
tmp_version = find_version.search(v).group(1).decode() url = find_url.search(package).group(1).decode()
tmp_version = find_version.search(package).group(1).decode()
with lock[arch]: with lock[arch]:
# 使用 apt_pkg 进行版本比较 # 使用 apt_pkg 进行版本比较
if name not in package_version[arch] or apt_pkg.version_compare(tmp_version, package_version[arch][name]) > 0: if (
package_version[arch][name] = tmp_version name not in packages[arch]
package_info[arch][name] = v or version_compare(tmp_version, packages[arch][name]["version"]) > 0
):
packages[arch][name] = {"version": tmp_version, "url": url, "package": package}
except Exception as e: except Exception as e:
logging.error(f"Error processing package {name}: {e}") logging.error(f"Error processing package {name}: {e}")
return return
@ -110,7 +128,7 @@ def process_repo(r: dict):
""" """
try: try:
for path in r["path"].values(): for path in r["path"].values():
get_latest(get_remote_packages(r["repo"], path)) split_latest(get_remote_packages(r["repo"], path))
except Exception as e: except Exception as e:
logging.error(f"Error processing repo {r.get('name', 'unknown')}: {e}") logging.error(f"Error processing repo {r.get('name', 'unknown')}: {e}")
@ -136,7 +154,7 @@ if __name__ == "__main__":
# 处理本地 repo # 处理本地 repo
if args.local: if args.local:
with open(args.local) as f: with open(args.local) as f:
get_latest(f.read().encode()) split_latest(f.read().encode())
# 读取 repo_list 配置 # 读取 repo_list 配置
repo_list = read_repo_list(args.repo) repo_list = read_repo_list(args.repo)
@ -151,7 +169,13 @@ if __name__ == "__main__":
for arch in ["amd64", "arm64"]: for arch in ["amd64", "arm64"]:
os.makedirs(f"deb/dists/wcbing/main/binary-{arch}/", exist_ok=True) os.makedirs(f"deb/dists/wcbing/main/binary-{arch}/", exist_ok=True)
with open(f"deb/dists/wcbing/main/binary-{arch}/Packages", "+wb") as f: with open(f"deb/dists/wcbing/main/binary-{arch}/Packages", "+wb") as f:
for i in package_info[arch].values(): for i in packages[arch].values():
f.write(i) f.write(i["package"])
for i in package_info["all"].values(): for i in packages["all"].values():
f.write(i) f.write(i["package"])
# 输出 packages.json用于展示仓库内容
for arch in arch_List:
for i in packages[arch].values():
i.pop("package")
json.dump(packages, open("deb/list/packages.json", "w"), indent=4)

21
run.sh
View File

@ -4,31 +4,14 @@
./get-github-releases.py ./get-github-releases.py
find get -type f -name "*.sh" -exec sh {} \; find get -type f -name "*.sh" -exec sh {} \;
# generate the html
./gen-list-html.py
# generate the Packages file
## generate the local Packages file
cd deb
apt-ftparchive packages . > tmpPackages
sed -i "s|\./\(https\?\):/|\1://|g" tmpPackages
cd ..
## merge the Packages file from local package ## merge the Packages file from local package
cat $(find packages -name "*.package") >> deb/tmpPackages cat $(find packages -name "*.package") > deb/tmpPackages
## merge the Packages files from third-party repositories ## merge the Packages files from third-party repositories
./merge-apt-repo.py --local deb/tmpPackages ./merge-apt-repo.py --local deb/tmpPackages
# generate the Release file # generate the Release file
cd deb/dists/wcbing && \ cd deb/dists/wcbing && \
echo 'Origin: wcbing APT Repo apt-ftparchive release -c apt-ftparchive.conf . > Release && \
Label: wcbing
Suite: wcbing
Codename: wcbing
Architectures: amd64 arm64
Components: main
Description: wcbing APT Repo || wcbing 的 APT 仓库' > Release && \
apt-ftparchive release . >> Release && \
gpg --yes --detach-sign -a -o Release.gpg Release && \ gpg --yes --detach-sign -a -o Release.gpg Release && \
gpg --yes --clearsign -o InRelease Release gpg --yes --clearsign -o InRelease Release