Untuk developer··4 menit baca

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.

Bigjpg API: perbesar gambar massal dengan Python

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 requests terpasang. 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

PanggilanFungsiCatatan
POST /api/img_uploadMengembalikan URL unggah bertanda tangan dan URL gambar publikUnggah berkas dengan PUT memakai Content-Type yang sama, lalu pakai fileurl sebagai input tugas
POST /api/task/Membuat tugas pembesaranMengembalikan tid, perkiraan minute, dan remaining_api_calls
GET /api/task/{ids}Status satu atau banyak tugasid dipisah koma, maksimal 100 per permintaan
POST /api/task/{ids}Mengulang tugas yang gagalHanya 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 — style harus art atau photo, noise -1 sampai 3, x2 1 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.

← Semua artikel