About Multi-camera viewer optimized for RTSP streams
0

Configure Feed

Select the types of activity you want to include in your feed.

at master 3.5 kB View raw
1#!/usr/bin/env python3 2"""Prepare GitHub release assets without exceeding the per-asset size limit.""" 3 4from __future__ import annotations 5 6import argparse 7import glob 8import hashlib 9from pathlib import Path 10import shutil 11 12 13DEFAULT_MAX_ASSET_SIZE = 1900 * 1024 * 1024 14BUFFER_SIZE = 8 * 1024 * 1024 15 16 17def file_sha256(path: Path) -> str: 18 digest = hashlib.sha256() 19 with path.open("rb") as handle: 20 for chunk in iter(lambda: handle.read(BUFFER_SIZE), b""): 21 digest.update(chunk) 22 return digest.hexdigest() 23 24 25def split_file(source: Path, release_dir: Path, max_asset_size: int) -> list[Path]: 26 parts: list[Path] = [] 27 part_number = 1 28 29 with source.open("rb") as handle: 30 while True: 31 target = release_dir / f"{source.name}.part{part_number:02d}" 32 written = 0 33 34 with target.open("wb") as output: 35 while written < max_asset_size: 36 chunk = handle.read(min(BUFFER_SIZE, max_asset_size - written)) 37 if not chunk: 38 break 39 output.write(chunk) 40 written += len(chunk) 41 42 if written == 0: 43 target.unlink(missing_ok=True) 44 break 45 46 parts.append(target) 47 part_number += 1 48 49 return parts 50 51 52def find_sources(platform: str, include_appimage: bool) -> list[Path]: 53 patterns = [f"dist/*_{platform}_*.zip"] 54 if include_appimage: 55 patterns.append(f"dist/*_{platform}_*.AppImage") 56 57 return [Path(path) for pattern in patterns for path in glob.glob(pattern)] 58 59 60def prepare_release_assets(platform: str, include_appimage: bool, max_asset_size: int) -> None: 61 release_dir = Path("dist") / "release-assets" 62 release_dir.mkdir(parents=True, exist_ok=True) 63 64 sources = find_sources(platform, include_appimage) 65 if not sources: 66 raise SystemExit("No release assets found") 67 68 checksums: list[str] = [] 69 split_assets: list[str] = [] 70 71 for source in sources: 72 if source.stat().st_size < max_asset_size: 73 target = release_dir / source.name 74 shutil.copy2(source, target) 75 checksums.append(f"{file_sha256(target)} {target.name}") 76 continue 77 78 split_assets.append(source.name) 79 for part in split_file(source, release_dir, max_asset_size): 80 checksums.append(f"{file_sha256(part)} {part.name}") 81 82 checksum_path = release_dir / f"wildcam_{platform}_SHA256SUMS.txt" 83 checksum_path.write_text("\n".join(checksums) + "\n", encoding="utf-8") 84 85 if split_assets: 86 note_path = release_dir / f"wildcam_{platform}_split_assets.txt" 87 note_path.write_text( 88 "Some release assets exceeded GitHub's 2 GiB per-file limit and were split.\n" 89 "Reassemble a split asset with: cat <asset>.part* > <asset>\n" 90 "Split assets:\n" 91 + "\n".join(f"- {asset}" for asset in split_assets) 92 + "\n", 93 encoding="utf-8", 94 ) 95 96 97def parse_args() -> argparse.Namespace: 98 parser = argparse.ArgumentParser() 99 parser.add_argument("--platform", required=True) 100 parser.add_argument("--include-appimage", action="store_true") 101 parser.add_argument("--max-asset-size", type=int, default=DEFAULT_MAX_ASSET_SIZE) 102 return parser.parse_args() 103 104 105def main() -> None: 106 args = parse_args() 107 prepare_release_assets(args.platform, args.include_appimage, args.max_asset_size) 108 109 110if __name__ == "__main__": 111 main()