Node.js

Simple JavaScript (Node.js) script to upload a capture zip file. It retrieves an authentication token using the client_id and client_secret found in your Developer Dashboard.

Read the Quick Start Guide and the Upload Captures pages to learn more.

⚠️ To run this you need Node.js 18+ (for native fetch and FormData support). Install dependencies if needed: npm install node-fetch

import fs from "fs";
import path from "path";
import fetch from "node-fetch"; // Node 18+ users can omit this import

const CLIENT_ID = "insert-it-here";
const CLIENT_SECRET = "insert-it-here";

const AUTH_ENDPOINT = "https://signin.teleport.varjo.com/oauth2/token";

const API_BASE = "https://teleport.varjo.com";

async function main() {
  const filename = process.argv[2];
  if (!filename) {
    console.error("Usage: node upload_capture.js <file>");
    process.exit(1);
  }

  const filePath = path.resolve(filename);
  const bytesize = fs.statSync(filePath).size;
  console.log(`Uploading capture of ${bytesize} bytes...`);

  // Step 1: Authenticate
  const authResponse = await fetch(AUTH_ENDPOINT, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "client_credentials",
      client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET,
      scope: "openid profile email",
    }),
  });

  if (!authResponse.ok) {
    console.error("Authentication failed:", authResponse.status);
    process.exit(1);
  }

  const { access_token } = await authResponse.json();

  // Step 2: Create capture
  const created = await fetch(`${API_BASE}/api/v1/captures`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${access_token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: path.basename(filePath),
      bytesize,
      input_data_format: "bulk-images",
    }),
  });

  if (!created.ok) {
    console.error("Failed to create capture:", created.status);
    process.exit(1);
  }

  const { eid, num_parts, chunk_size } = await created.json();
  console.log(`Uploading ${num_parts} parts...`);

  // Step 3: Upload file parts
  const fd = fs.openSync(filePath, "r");
  const parts = [];

  for (let part_no = 1; part_no <= num_parts; part_no++) {
    const urlResp = await fetch(
      `${API_BASE}/api/v1/captures/${eid}/create-upload-url/${part_no}`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${access_token}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ eid, bytesize }),
      }
    );

    if (!urlResp.ok) {
      console.error(`Failed to get upload URL for part ${part_no}`);
      process.exit(1);
    }

    const { upload_url } = await urlResp.json();

    // Read a chunk from the file
    const buffer = Buffer.alloc(chunk_size);
    const bytesRead = fs.readSync(fd, buffer, 0, chunk_size, null);
    const chunk = buffer.subarray(0, bytesRead);

    console.log(`Uploading part ${part_no}...`);
    const putResp = await fetch(upload_url, {
      method: "PUT",
      body: chunk,
    });

    if (!putResp.ok) {
      console.error(`Failed to upload part ${part_no}:`, putResp.status);
      process.exit(1);
    }

    const etag = putResp.headers.get("etag").replace(/"/g, "");
    parts.push({ number: part_no, etag });
  }

  fs.closeSync(fd);

  // Step 4: Complete upload
  const completed = await fetch(`${API_BASE}/api/v1/captures/${eid}/uploaded`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${access_token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ eid, parts }),
  });

  if (!completed.ok) {
    console.error("Failed to finalize upload:", completed.status);
    process.exit(1);
  }

  const { state } = await completed.json();
  console.log("Upload done!");
  console.log("Capture state:", state);
}

main().catch((err) => {
  console.error("Error:", err);
  process.exit(1);
});