|
| 1 | +"""Generate hitomi-manifest.json for client-side download.""" |
| 2 | +import sys |
| 3 | +import json |
| 4 | +import hashlib |
| 5 | +import sqlite3 |
| 6 | +import argparse |
| 7 | +from pathlib import Path |
| 8 | +from datetime import datetime, timezone |
| 9 | +from contextlib import closing |
| 10 | + |
| 11 | + |
| 12 | +SOURCES = [ |
| 13 | + { |
| 14 | + "id": "github", |
| 15 | + "priority": 10, |
| 16 | + "url": "https://github.com/jasoneri/ComicGUISpider/releases/download/preset/hitomi.db" |
| 17 | + }, |
| 18 | + { |
| 19 | + "id": "gitee", |
| 20 | + "priority": 20, |
| 21 | + "url": "https://gitee.com/json_eri/ComicGUISpider/releases/download/preset/hitomi.db" |
| 22 | + }, |
| 23 | +] |
| 24 | + |
| 25 | + |
| 26 | +def generate(db_path, output_path): |
| 27 | + db_file = Path(db_path) |
| 28 | + if not db_file.exists(): |
| 29 | + print(f"[ERROR] {db_path} not found") |
| 30 | + return False |
| 31 | + |
| 32 | + # sha256 |
| 33 | + h = hashlib.sha256() |
| 34 | + with open(db_file, 'rb') as f: |
| 35 | + for chunk in iter(lambda: f.read(8192), b''): |
| 36 | + h.update(chunk) |
| 37 | + sha256 = h.hexdigest() |
| 38 | + size = db_file.stat().st_size |
| 39 | + |
| 40 | + # stats |
| 41 | + table_count = 0 |
| 42 | + row_count = 0 |
| 43 | + with closing(sqlite3.connect(db_path)) as conn: |
| 44 | + cursor = conn.cursor() |
| 45 | + tables = cursor.execute( |
| 46 | + "SELECT name FROM sqlite_master WHERE type='table' AND name != 'language' AND name NOT LIKE 'sqlite_%'" |
| 47 | + ).fetchall() |
| 48 | + table_count = len(tables) |
| 49 | + for (t,) in tables: |
| 50 | + count = cursor.execute(f"SELECT COUNT(*) FROM `{t}`").fetchone()[0] |
| 51 | + row_count += count |
| 52 | + |
| 53 | + now = datetime.now(timezone.utc) |
| 54 | + manifest = { |
| 55 | + "schema_version": 1, |
| 56 | + "version": now.strftime("%Y.%m.%d.%H%M"), |
| 57 | + "generated_at": now.isoformat(), |
| 58 | + "file": { |
| 59 | + "name": "hitomi.db", |
| 60 | + "size": size, |
| 61 | + "sha256": sha256, |
| 62 | + "table_count": table_count, |
| 63 | + "row_count": row_count, |
| 64 | + }, |
| 65 | + "sources": SOURCES, |
| 66 | + } |
| 67 | + |
| 68 | + out = Path(output_path) |
| 69 | + out.parent.mkdir(parents=True, exist_ok=True) |
| 70 | + with open(out, 'w', encoding='utf-8') as f: |
| 71 | + json.dump(manifest, f, indent=2, ensure_ascii=False) |
| 72 | + |
| 73 | + print(f"[OK] manifest written to {out}") |
| 74 | + print(f" version: {manifest['version']}") |
| 75 | + print(f" sha256: {sha256[:16]}...") |
| 76 | + print(f" size: {size} bytes") |
| 77 | + print(f" tables: {table_count}, rows: {row_count}") |
| 78 | + return True |
| 79 | + |
| 80 | + |
| 81 | +if __name__ == "__main__": |
| 82 | + parser = argparse.ArgumentParser(description="Generate hitomi-manifest.json") |
| 83 | + parser.add_argument("db_path", help="Path to hitomi.db") |
| 84 | + parser.add_argument("--output", "-o", default="hitomi-manifest.json", help="Output manifest path") |
| 85 | + args = parser.parse_args() |
| 86 | + sys.exit(0 if generate(args.db_path, args.output) else 1) |
0 commit comments