mirror of
https://github.com/wcbing/wcbing-apt-repo.git
synced 2025-12-28 18:18:31 +08:00
Compare commits
25 Commits
194866f306
...
e4d09a75af
| Author | SHA1 | Date | |
|---|---|---|---|
| e4d09a75af | |||
| ced865ae96 | |||
| 39d62d4790 | |||
| 612536ca51 | |||
| a775745284 | |||
| ced4424f7f | |||
| 8c5f96e24c | |||
| 88b986a35c | |||
| cdc21a78f2 | |||
| 3a1b8e0c96 | |||
| 51fad100c8 | |||
| 714b0ff576 | |||
| fd616b3ea4 | |||
| 30b2f2e08f | |||
| 190f50bc95 | |||
| 1678e35a07 | |||
| 16d46f3c82 | |||
| 204d14c4a0 | |||
| 399261f4ad | |||
| 5f6dac91cf | |||
| 2ba745fc2e | |||
| 43876bf6d3 | |||
| 33b5e1d84f | |||
| fc5254f4cb | |||
| 8f20346faa |
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,4 +1,6 @@
|
|||||||
data/github-local.json
|
data/github-local.json
|
||||||
data/deb.db
|
data/deb.db
|
||||||
deb/
|
*.deb
|
||||||
|
*Packages
|
||||||
|
*Release*
|
||||||
__pycache__/
|
__pycache__/
|
||||||
40
README.md
40
README.md
@ -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 | ✅ | ✅ |
|
||||||
|
|
||||||
## 自建仓库
|
## 自建仓库
|
||||||
|
|
||||||
|
|||||||
@ -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, arch, 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(
|
||||||
|
|||||||
@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
9
deb/dists/wcbing/apt-ftparchive.conf
Normal file
9
deb/dists/wcbing/apt-ftparchive.conf
Normal 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 仓库";
|
||||||
|
}
|
||||||
@ -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
7
get/eudic.sh
Normal 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
|
||||||
@ -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
|
||||||
|
|||||||
@ -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
31
get/wps-office.sh
Normal 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
|
||||||
@ -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
|
|
||||||
@ -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
21
run.sh
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user