初始提交:短剧库源码(含转码优化 ultrafast)
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
cd /d "%~dp0.."
|
||||
if not exist dist mkdir dist
|
||||
set CGO_ENABLED=0
|
||||
set GOOS=windows
|
||||
set GOARCH=amd64
|
||||
go build -trimpath -ldflags="-s -w" -o dist\juku_windows_amd64.exe .
|
||||
if errorlevel 1 exit /b 1
|
||||
echo 已生成 dist\juku_windows_amd64.exe
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
||||
mkdir -p dist
|
||||
|
||||
echo "Building macOS arm64..."
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -trimpath -ldflags="-s -w" -o dist/juku_darwin_arm64 .
|
||||
|
||||
echo "Building macOS amd64..."
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o dist/juku_darwin_amd64 .
|
||||
|
||||
echo "Building Linux amd64..."
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o dist/juku_linux_amd64 .
|
||||
|
||||
echo "Building Windows amd64..."
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o dist/juku_windows_amd64.exe .
|
||||
|
||||
echo "Done. Files are in dist/."
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
project_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
echo "请先安装 Docker。" >&2
|
||||
exit 1
|
||||
fi
|
||||
exec docker build --pull -t "${JUKU_IMAGE:-juku:local}" "$@" "$project_dir"
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
project_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
if ! command -v docker >/dev/null 2>&1 || ! docker compose version >/dev/null 2>&1; then
|
||||
echo "请先安装 Docker 及 Compose v2。" >&2
|
||||
exit 1
|
||||
fi
|
||||
exec docker compose --project-directory "$project_dir" -f "$project_dir/compose.yaml" up -d --build "$@"
|
||||
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env python3
|
||||
"""把新版 短剧库.zip 与本地源码对比,展示变更并更新本地旧代码。
|
||||
|
||||
用法:
|
||||
python scripts/update_from_zip.py # 从默认地址下载最新包并预览变更
|
||||
python scripts/update_from_zip.py --apply # 下载并真正更新本地代码
|
||||
python scripts/update_from_zip.py 某个路径/短剧库.zip # 用本地压缩包
|
||||
python scripts/update_from_zip.py --url https://... # 指定其他下载地址
|
||||
|
||||
行为:
|
||||
1. 读取压缩包(自动处理中文文件名 GBK/UTF-8、自动剥离压缩包内公共顶层目录)
|
||||
2. 与本地源码逐文件比对(忽略 .git、data/、日志、压缩包等运行时文件)
|
||||
3. 列出 新增 / 修改 / 删除 三类变更;加 --apply 后写入本地
|
||||
4. 更新完成后用 git status / git diff 自查,再提交
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
DEFAULT_URL = "https://www.252035.xyz/短剧库.zip"
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
IGNORE_DIRS = {".git", "data", "__pycache__", "dist", "短剧下载"}
|
||||
IGNORE_SUFFIX = {".log", ".zip", ".tar", ".pyc"}
|
||||
IGNORE_NAMES = {".DS_Store", "Thumbs.db"}
|
||||
PROTECT = {"scripts/update_from_zip.py"} # 更新工具自身,永远不删
|
||||
|
||||
|
||||
def download(url: str) -> Path:
|
||||
quoted = urllib.parse.quote(url, safe=":/?&=%")
|
||||
print(f"下载最新压缩包:{url}")
|
||||
request = urllib.request.Request(quoted, headers={"User-Agent": "juku-update/1.0"})
|
||||
with urllib.request.urlopen(request, timeout=60) as resp:
|
||||
total = int(resp.headers.get("Content-Length") or 0)
|
||||
target = Path(tempfile.gettempdir()) / "短剧库-update.zip"
|
||||
done = 0
|
||||
with target.open("wb") as f:
|
||||
while True:
|
||||
chunk = resp.read(1 << 16)
|
||||
if not chunk:
|
||||
break
|
||||
f.write(chunk)
|
||||
done += len(chunk)
|
||||
if total:
|
||||
print(f"\r {done}/{total} 字节", end="", flush=True)
|
||||
print(f"\n 已保存到 {target}")
|
||||
return target
|
||||
|
||||
|
||||
def norm_name(info: zipfile.ZipInfo) -> str:
|
||||
"""修正 zip 内中文文件名。
|
||||
|
||||
打包工具有两种常见错误:用 UTF-8 编码但不打 0x800 标志(跨平台工具),
|
||||
或用 GBK 编码且不打标志(中文 Windows 右键压缩)。依次尝试恢复:
|
||||
UTF-8 优先(其字节序列极少与 GBK 混淆),失败再试 GBK。
|
||||
"""
|
||||
name = info.filename
|
||||
if not info.flag_bits & 0x800:
|
||||
try:
|
||||
raw = name.encode("cp437")
|
||||
except UnicodeEncodeError:
|
||||
return name.replace("\\", "/")
|
||||
for encoding in ("utf-8", "gbk"):
|
||||
try:
|
||||
name = raw.decode(encoding)
|
||||
break
|
||||
except UnicodeDecodeError:
|
||||
continue
|
||||
return name.replace("\\", "/")
|
||||
|
||||
|
||||
def ignored(rel: str) -> bool:
|
||||
parts = rel.split("/")
|
||||
return (
|
||||
any(p in IGNORE_DIRS for p in parts[:-1])
|
||||
or Path(rel).name in IGNORE_NAMES
|
||||
or Path(rel).suffix in IGNORE_SUFFIX
|
||||
)
|
||||
|
||||
|
||||
def common_prefix(names: list[str]) -> str:
|
||||
"""所有条目共享同一顶层目录时返回该前缀(如 '短剧库/'),否则返回空串。"""
|
||||
if not names:
|
||||
return ""
|
||||
tops = {n.split("/", 1)[0] for n in names}
|
||||
if len(tops) == 1 and all("/" in n for n in names):
|
||||
return next(iter(tops)) + "/"
|
||||
return ""
|
||||
|
||||
|
||||
def md5_of(path: Path) -> str:
|
||||
h = hashlib.md5()
|
||||
with path.open("rb") as f:
|
||||
for chunk in iter(lambda: f.read(1 << 16), b""):
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="用新版 zip 更新本地短剧库源码")
|
||||
parser.add_argument("zipfile", nargs="?", default=None,
|
||||
help="本地压缩包路径;省略则从 --url 地址下载最新包")
|
||||
parser.add_argument("--url", default=DEFAULT_URL, help=f"新版下载地址(默认 {DEFAULT_URL})")
|
||||
parser.add_argument("--apply", action="store_true", help="真正写入本地(默认只预览)")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.zipfile:
|
||||
zip_path = Path(args.zipfile)
|
||||
if not zip_path.exists():
|
||||
print(f"找不到压缩包:{args.zipfile}")
|
||||
return 1
|
||||
else:
|
||||
zip_path = download(args.url)
|
||||
|
||||
with zipfile.ZipFile(zip_path) as zf:
|
||||
infos = [i for i in zf.infolist() if not i.is_dir()]
|
||||
names = [norm_name(i) for i in infos]
|
||||
# 先丢弃 macOS 压缩附带的资源文件,避免干扰顶层目录识别
|
||||
pairs = [
|
||||
(info, name)
|
||||
for info, name in zip(infos, names)
|
||||
if "__MACOSX" not in name.split("/") and not Path(name).name.startswith("._")
|
||||
]
|
||||
prefix = common_prefix([name for _, name in pairs])
|
||||
if prefix:
|
||||
print(f"检测到压缩包顶层目录:{prefix}(比对时已忽略)")
|
||||
incoming: dict[str, bytes] = {}
|
||||
for info, name in pairs:
|
||||
rel = name[len(prefix):] if prefix else name
|
||||
if rel and not ignored(rel):
|
||||
incoming[rel] = zf.read(info)
|
||||
|
||||
added, modified, unchanged = [], [], []
|
||||
for rel, blob in sorted(incoming.items()):
|
||||
local = REPO_ROOT / rel
|
||||
if not local.exists():
|
||||
added.append(rel)
|
||||
elif md5_of(local) != hashlib.md5(blob).hexdigest():
|
||||
modified.append(rel)
|
||||
else:
|
||||
unchanged.append(rel)
|
||||
|
||||
local_files: set[str] = set()
|
||||
for path in REPO_ROOT.rglob("*"):
|
||||
if not path.is_file():
|
||||
continue
|
||||
rel = path.relative_to(REPO_ROOT).as_posix()
|
||||
if ignored(rel) or rel in IGNORE_NAMES:
|
||||
continue
|
||||
local_files.add(rel)
|
||||
deleted = sorted(local_files - set(incoming) - PROTECT)
|
||||
|
||||
print(f"\n对比完成:压缩包 {len(incoming)} 个文件,本地 {len(local_files)} 个文件")
|
||||
print(f" 新增 {len(added)} 个")
|
||||
for rel in added:
|
||||
print(f" + {rel}")
|
||||
print(f" 修改 {len(modified)} 个")
|
||||
for rel in modified:
|
||||
print(f" ~ {rel}")
|
||||
print(f" 删除 {len(deleted)} 个")
|
||||
for rel in deleted:
|
||||
print(f" - {rel}")
|
||||
print(f" 未变化 {len(unchanged)} 个")
|
||||
|
||||
if not (added or modified or deleted):
|
||||
print("本地代码与压缩包一致,无需更新。")
|
||||
return 0
|
||||
if not args.apply:
|
||||
print("\n当前为预览模式。确认无误后追加 --apply 执行更新,然后 git add -A && git commit。")
|
||||
return 0
|
||||
|
||||
for rel in added + modified:
|
||||
target = REPO_ROOT / rel
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
target.write_bytes(incoming[rel])
|
||||
print(f"已写入 {rel}")
|
||||
for rel in deleted:
|
||||
(REPO_ROOT / rel).unlink(missing_ok=True)
|
||||
print(f"已删除 {rel}")
|
||||
print("\n更新完成。请执行 git status / git diff 复核后提交。")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,11 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
title 短剧库
|
||||
cd /d "%~dp0.."
|
||||
if not exist "dist\juku_windows_amd64.exe" (
|
||||
echo 找不到下载器,请先编译或获取包含 dist 的完整版本。
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
"dist\juku_windows_amd64.exe" %*
|
||||
if errorlevel 1 pause
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
case "$(uname -m)" in
|
||||
arm64) binary="./dist/juku_darwin_arm64" ;;
|
||||
x86_64) binary="./dist/juku_darwin_amd64" ;;
|
||||
*) echo "不支持的 macOS 架构,请使用 go run ."; exit 1 ;;
|
||||
esac
|
||||
if [ ! -x "$binary" ]; then
|
||||
echo "找不到可执行程序,请先在项目目录运行 ./scripts/build.sh。"
|
||||
exit 1
|
||||
fi
|
||||
exec "$binary" "$@"
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
if [ ! -x "./dist/juku_linux_amd64" ]; then
|
||||
echo "找不到 Linux x64 程序,请先运行 ./scripts/build.sh。"
|
||||
exit 1
|
||||
fi
|
||||
exec ./dist/juku_linux_amd64 "$@"
|
||||
Reference in New Issue
Block a user