Bigjpg API: perbesar gambar massal dengan Python
Skrip siap jalan yang mengunggah gambar, membuat tugas pembesaran, memantau hasil, lalu mengunduhnya. Plus pengaturan konkurensi dan penanganan error agar pekerjaan 500 gambar tidak berhenti di tengah.
Untuk beberapa gambar, antarmuka web sudah cukup. Kalau folder Anda berisi lima ratus berkas, pakai API lebih enak: empat endpoint, tanpa browser, dan skripnya bisa dijalankan ulang sesuka hati. Versi di bawah ini yang biasa kami serahkan ke rekan kerja — unggah, buat, pantau, unduh, lengkap dengan penanganan kegagalan yang benar-benar kepakai.
Sebelum mulai
- Akses API hanya untuk akun berbayar. Kunci Anda ada di halaman API setelah masuk dengan paket yang masih aktif.
- Python 3 dengan
requeststerpasang. Tidak ada lagi. - Pembesaran berjalan asinkron di worker kami, jadi skrip membuat tugas lebih dulu, baru memantaunya. Pekerjaan 500 gambar memakan waktu selama tugas paling lambat di antrean Anda, bukan jumlah seluruh gambar.
Skripnya
import json
import os
import time
import requests
API = "https://bigjpg.com"
KEY = "YOUR_API_KEY"
STYLE = "art" # "art" untuk ilustrasi, "photo" untuk foto
NOISE = "1" # -1 tidak, 0 rendah, 1 sedang, 2 tinggi, 3 tertinggi
X2 = "2" # 1 = 2x, 2 = 4x, 3 = 8x, 4 = 16x
POLL_SECONDS = 8
MAX_RETRY = 1
HEADERS = {"X-API-KEY": KEY}
def upload(path):
"""Get a presigned URL, PUT the file, return its public URL."""
name = path.split("/")[-1]
r = requests.post(
API + "/api/img_upload",
headers=HEADERS,
data={"filename": name, "filetype": "image/jpeg"},
timeout=60,
)
info = r.json()
if info.get("status") != "ok":
raise RuntimeError("upload failed: " + str(info))
with open(path, "rb") as image:
r = requests.put(
info["url"],
data=image,
headers={"Content-Type": "image/jpeg"},
timeout=300,
)
if r.status_code != 200:
raise RuntimeError("upload failed with HTTP %s" % r.status_code)
return info["fileurl"]
def create(image_url):
"""Create an enlargement task, return its task id."""
payload = {
"style": STYLE,
"noise": NOISE,
"x2": X2,
"input": image_url,
}
r = requests.post(
API + "/api/task/",
headers=HEADERS,
data=json.dumps(payload),
timeout=60,
)
data = r.json()
if "tid" not in data:
raise RuntimeError("task failed: " + str(data))
return data["tid"]
def poll(tids):
"""Query up to 100 task ids at once. Returns task id -> result dict."""
r = requests.get(API + "/api/task/" + ",".join(tids), timeout=60)
return r.json()
def download(url, out):
"""Download a finished image, checking the transfer really succeeded."""
r = requests.get(url, timeout=300)
if r.status_code != 200:
raise RuntimeError("download failed with HTTP %s" % r.status_code)
with open(out, "wb") as f:
f.write(r.content)
return out
def main(paths):
pending = {}
attempts = {}
for path in paths:
try:
tid = create(upload(path))
except Exception as e:
print("skipped", path, e)
continue
pending[tid] = path
attempts[tid] = 0
print("queued", path, tid)
while pending:
for tid, info in poll(list(pending)[:100]).items():
if info.get("status") == "success":
out = os.path.splitext(pending[tid])[0] + "_bigjpg.jpg"
try:
download(info["url"], out)
except Exception as e:
print("download failed", pending[tid], e)
pending.pop(tid)
continue
print("done", pending.pop(tid), "->", out)
elif info.get("status") == "error":
# Coba sekali lagi, kalau gagal lepaskan berkas ini.
attempts[tid] += 1
if attempts[tid] <= MAX_RETRY:
requests.post(API + "/api/task/" + tid, headers=HEADERS, timeout=60)
print("retrying", pending[tid])
else:
print("giving up on", pending.pop(tid))
if pending:
time.sleep(POLL_SECONDS)
if __name__ == "__main__":
import sys
main(sys.argv[1:])
Apa yang dilakukan tiap panggilan
| Panggilan | Fungsi | Catatan |
|---|---|---|
| POST /api/img_upload | Mengembalikan URL unggah bertanda tangan dan URL gambar publik | Unggah berkas dengan PUT memakai Content-Type yang sama, lalu pakai fileurl sebagai input tugas |
| POST /api/task/ | Membuat tugas pembesaran | Mengembalikan tid, perkiraan minute, dan remaining_api_calls |
| GET /api/task/{ids} | Status satu atau banyak tugas | id dipisah koma, maksimal 100 per permintaan |
| POST /api/task/{ids} | Mengulang tugas yang gagal | Hanya tugas berstatus error yang kembali ke antrean |
Konkurensi dan sopan santun
- Pantau lebih jarang dari dugaan Anda. Pembesaran tidak instan; jeda 5–10 detik sudah cukup, dan 100 id dalam satu panggilan menjaga jumlah permintaan tetap rendah.
- Batasi unggahan. Dua sampai empat unggahan bersamaan adalah plafon yang wajar untuk satu mesin. Butuh throughput lebih besar, jalankan lebih banyak instans, bukan lebih banyak thread per instans.
- Ulangi dengan backoff. Error jaringan bukan kegagalan tugas. Ulangi panggilan HTTP sampai tiga kali dengan jeda yang makin panjang sebelum berkas dianggap rusak.
- Amati
remaining_api_calls. Setiap tugas yang terbentuk melaporkannya; hentikan loop di ambang yang rendah daripada mengetahui batasnya dari tumpukan error. - Catat pemetaannya. Simpan berkas berisi
tid, nama input, setelan, dan nama output. Kalau pekerjaan berjalan semalam, catatan itu satu-satunya cara tahu mana yang selesai.
Error yang pasti Anda temui
valid_api_key_required— header tidak ada atau kuncinya tidak dikenal. Periksa nama header dan pastikan paket kunci itu belum kedaluwarsa.requires_vip— akun gratis atau sudah kedaluwarsa. API untuk paket berbayar yang aktif.month_limit— kuota bulanan habis. Sisa kuota ikut dilaporkan tiap tugas, jadi Anda bisa berhenti sebelum kejadian.param_error—styleharus art atau photo,noise-1 sampai 3,x21 sampai 4.input_url_error— input harus URL gambar http atau https yang bisa diakses publik. URL privat bertanda tangan akan kedaluwarsa; unggah dulu, lalu kirim URL publik yang dikembalikan.
Biaya satu batch
Satu gambar dihitung satu kredit pada 2x, 4x, dan 8x; 16x dihitung dua. Jadi 500 gambar pada 4x berarti 500 kredit, dan pekerjaan yang sama pada 16x menjadi 1000. Karena 4x memang faktor yang tepat untuk sebagian besar foto, pilihan yang lebih murah biasanya juga yang lebih enak dilihat — alasannya ada di panduan faktor.
Referensi endpoint lengkap, termasuk parameter dan semua status error, ada di halaman API. Kalau ada bagian skrip yang tidak cocok dengan respons Anda, halaman itu versi yang kami jaga tetap baru.