user_metrics.py
| #!/bin/env python3 | |
| import requests | |
| import os | |
| from typing import Literal | |
| from tqdm import tqdm | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| USER_ID = int(os.environ["DANBOORU_USER_ID"]) | |
| def api_request(kind: Literal["get", "put"], endpoint: str, *, params: dict = {}, headers: dict = {}, json: dict = {}, data: dict = {}) -> dict | list: | |
| url = f"https://danbooru.donmai.us/{endpoint}.json" | |
| r = requests.request(kind, url, | |
| auth=(os.environ["DANBOORU_LOGIN"], os.environ["DANBOORU_API_KEY"]), | |
| params=params if len(params) > 0 else None, | |
| headers={ "User-Agent": f"user #{USER_ID}", **headers }, | |
| json=json if len(json) > 0 else None, | |
| data=data if len(data) > 0 else None | |
| ) | |
| return r.json() | |
| def api_get(endpoint: str, *, params: dict = {}, headers: dict = {}, json: dict = {}, data: dict = {}) -> dict | list: | |
| return api_request("get", endpoint, params=params, headers=headers, json=json, data=data) | |
| print(f"Fetching metrics for user #{USER_ID}") | |
| params: dict[str, str | int] = { | |
| "limit": 1000, | |
| "only": "id,status,media_asset_count,media_assets[id,file_ext,file_size,image_width,image_height,post[uploader_id]]", | |
| "search[uploader_id]": USER_ID | |
| } | |
| def format_bytes(n: int) -> str: | |
| if n < 1024: | |
| return f"{n} bytes" | |
| elif n < 1024*1024: | |
| return f"{n / 1024:.3f} KiB" | |
| elif n < 1024*1024*1024: | |
| return f"{n / 1024 / 1024:.3f} MiB" | |
| elif n < 1024*1024*1024*1024: | |
| return f"{n / 1024 / 1024 / 1024:.3f} GiB" | |
| else: | |
| return f"{n / 1024 / 1024 / 1024 / 1024:.3f} TiB" | |
| def format_pixels(n: int) -> str: | |
| if n < 1e3: | |
| return f"{n}" | |
| elif n < 1e6: | |
| return f"{n / 1e3:.3f} KP" | |
| elif n < 1e9: | |
| return f"{n / 1e6:.3f} MP" | |
| elif n < 1e12: | |
| return f"{n / 1e9:.3f} GP" | |
| else: | |
| return f"{n / 1e12:.3f} TP" | |
| class Stats: | |
| pixels: int | |
| files: dict[str, int] | |
| bytes: int | |
| def __init__(self): | |
| self.pixels = 0 | |
| self.files = {} | |
| self.bytes = 0 | |
| def add_pixels(self, w: int, h: int): | |
| self.pixels += w * h | |
| def add_ext(self, ext: str): | |
| if ext not in self.files: | |
| self.files[ext] = 1 | |
| else: | |
| self.files[ext] += 1 | |
| def add_bytes(self, n: int): | |
| self.bytes += n | |
| def update(self, w: int, h: int, ext: str, size: int): | |
| self.add_pixels(w, h) | |
| self.add_ext(ext) | |
| self.add_bytes(size) | |
| def update_asset(self, asset: dict): | |
| self.update(asset["image_width"], asset["image_height"], asset["file_ext"], asset["file_size"]) | |
| def dump(self, title: str): | |
| print(f"{title}:") | |
| print(" ", self.pixels, f"pixels ({format_pixels(self.pixels)})") | |
| print(" ", self.bytes, f"bytes ({format_bytes(self.bytes)})") | |
| print(" File formats:") | |
| for ext, n in sorted(self.files.items(), key=lambda kv: kv[1], reverse=True): | |
| print(f" {ext}: {n}") | |
| print() | |
| total_uploads = 0 | |
| total_assets = 0 | |
| statuses = { | |
| "pending": 0, | |
| "processing": 0, | |
| "completed": 0, | |
| "error": 0 | |
| } | |
| self_upload = Stats() | |
| other_upload = Stats() | |
| not_uploaded = Stats() | |
| total = Stats() | |
| all_assets = set() | |
| with tqdm() as progress: | |
| cur = 0 | |
| while True: | |
| params["page"] = f"a{cur}" | |
| res = api_get("uploads", params=params) | |
| if len(res) == 0: | |
| break | |
| total_uploads += len(res) | |
| for upload in res: | |
| cur = max(cur, upload["id"]) | |
| total_assets += upload["media_asset_count"] | |
| statuses[upload["status"]] += 1 | |
| try: | |
| for asset in upload["media_assets"]: | |
| if asset["id"] not in all_assets: | |
| all_assets.add(asset["id"]) | |
| else: | |
| continue | |
| if "post" in asset: | |
| if asset["post"]["uploader_id"] == USER_ID: | |
| self_upload.update_asset(asset) | |
| total.update_asset(asset) | |
| else: | |
| other_upload.update_asset(asset) | |
| else: | |
| not_uploaded.update_asset(asset) | |
| total.update_asset(asset) | |
| except KeyError: | |
| print(f"Exploded on https://danbooru.donmai.us/uploads/{upload["id"]}.json") | |
| raise | |
| progress.update() | |
| progress.display() | |
| print() | |
| print(total_assets, "assets,", len(all_assets), "unique assets") | |
| print(total_uploads, "uploads") | |
| for ext, n in sorted(statuses.items(), key=lambda kv: kv[1], reverse=True): | |
| print(f" {ext}: {n}") | |
| print() | |
| self_upload.dump("Uploaded") | |
| other_upload.dump("Uploaded by someone else") | |
| not_uploaded.dump("Not uploaded") | |
| total.dump("Total") |
参与讨论