Skip to content

Extension API

WarpDL extensions are JavaScript modules executed using the Goja runtime. Extensions transform URLs before downloads begin, enabling support for custom hosting services and URL schemes.

Every extension requires two files:

  1. manifest.json - Extension metadata
  2. main.js (or custom entrypoint) - JavaScript code with extract() function
<platform-config-dir>/warpdl/extstore/
└── <extension-hash>/
├── manifest.json
└── main.js

Platform-specific paths:

  • Linux: ~/.config/warpdl/extstore/
  • macOS: ~/Library/Application Support/warpdl/extstore/
  • Windows: %AppData%/warpdl/extstore/ (e.g., C:\Users\<username>\AppData\Roaming\warpdl\extstore\)

The manifest defines extension metadata and URL matching patterns.

{
"name": "my-extension",
"version": "1.0.0",
"matches": ["^https://example\\.com/.*"],
"description": "Transform example.com URLs",
"entrypoint": "main.js"
}
Field Required Type Description
name Yes string Extension display name
version Yes string Semantic version (e.g., “1.0.0”)
matches Yes string[] Array of regex patterns for URL matching
description No string Human-readable description
entrypoint No string Main JavaScript file (default: “main.js”)
assets No string[] Additional files to include (e.g., helper scripts)
auth No object OAuth2 configuration for extensions that call an API — see Authenticated Extensions

The matches field contains JavaScript regex patterns. WarpDL tests each pattern against download URLs using RegExp.MatchString().

{
"matches": [
"^https://drive\\.google\\.com/.*",
"^https://.*\\.googleapis\\.com/.*"
]
}

Every extension must define an extract(url) function. This is the only entry point WarpDL calls.

main.js
function extract(url) {
// Transform the URL and return the direct download link
return transformedUrl;
}

Parameters:

  • url (string): The original URL to transform

Returns:

  • string: Transformed URL for downloading
  • Return "end" to signal interaction failure/cancellation

Example:

function extract(url) {
// Force HTTPS
if (url.startsWith("http://")) {
return url.replace("http://", "https://");
}
// Extract direct link from hosting service
let match = url.match(/example\.com\/file\/(\w+)/);
if (match) {
return "https://cdn.example.com/dl/" + match[1];
}
return url;
}

WarpDL provides several built-in functions for extensions:

Print values to stdout (for debugging).

print("Processing URL:", url);
print("Match found:", fileId);

Read user input from stdin. Optionally process input with a callback function.

// Simple input
let choice = input("Select quality (1-3): ");
// With callback
input("Enter filename: ", function(filename) {
print("You entered:", filename);
return filename;
});

Import additional JavaScript modules from the extension directory.

// Import helper.js from extension directory
let helper = require("./helper.js");

Note: Modules use CommonJS format (module.exports).

Make HTTP requests (limited to 1MB response size).

let response = request({
method: "GET",
url: "https://api.example.com/file-info",
headers: {
"User-Agent": "WarpDL Extension",
"Authorization": "Bearer token"
},
body: "" // Optional request body
});
print("Status:", response.status_code);
print("Body:", response.body);
print("Content-Length:", response.content_length);
// Access response headers
let contentType = response.headers.get("Content-Type");

Request Options:

  • method (string): HTTP method (GET, POST, etc.)
  • url (string): Request URL
  • headers (object): Request headers (key-value pairs)
  • body (string): Request body

Response Object:

  • status_code (number): HTTP status code
  • body (string): Response body (max 1MB)
  • content_length (number): Content-Length header value
  • headers (Headers): Response headers object

Headers Object Methods:

  • get(name) - Get header value
  • has(name) - Check if header exists
  • set(name, value) - Set header value
  • append(name, value) - Append to header value
  • delete(name) - Remove header
  • keys() - Get all header names
  • values() - Get all header values
  • entries() - Get [name, value] pairs
  • forEach(callback) - Iterate over headers
  • getSetCookies() - Get Set-Cookie header values

Extensions that call an API on the user’s behalf declare an auth block in their manifest. WarpDL then drives the OAuth2 login, stores the credential, and exposes token access to the extension’s JavaScript.

{
"name": "my-api-extension",
"version": "1.0.0",
"description": "Transform api.example.com URLs",
"matches": ["^https://api\\.example\\.com/.*"],
"entrypoint": "main.js",
"auth": {
"type": "oauth2",
"client_id": "1234.apps.example.com",
"scopes": ["files.read"],
"authorize_url": "https://api.example.com/oauth/authorize",
"token_url": "https://api.example.com/oauth/token",
"device_url": "https://api.example.com/oauth/device/code",
"revoke_url": "https://api.example.com/oauth/revoke",
"pkce": "S256",
"extra_auth_params": { "access_type": "offline" }
}
}
Field Required Description
type yes Must be "oauth2"
client_id yes OAuth2 client ID
scopes yes At least one scope; also the default requested at login
authorize_url yes Authorization endpoint (https:// only)
token_url yes Token endpoint (https:// only)
device_url no Enables the device-code flow
revoke_url no Used on logout to revoke the token server-side
client_secret no Only for providers that require it on /token for public clients (e.g. Google “Desktop app”); it ships inside the extension and is not secret
pkce no "S256" (default) or "plain"
extra_auth_params no Extra query parameters for the authorization URL

A device_url lets users log in with --device (no browser callback). Without it, login uses a loopback PKCE redirect.

These globals are only defined for extensions whose manifest declares auth:

Returns a valid access token (string), triggering or refreshing the login flow when needed.

var token = getAccessToken(); // default account, manifest scopes
var token = getAccessToken({ scopes: ["files.read"] });
var token = getAccessToken({ account: "work" });

Requested scopes must be a subset of the manifest’s scopes. options.account selects a named credential; omit it for the default account.

Calls request(req) with Authorization: Bearer <token> added to the headers, and retries once with a fresh token if the response is 401.

var response = fetchWithAuth({
method: "GET",
url: "https://api.example.com/me"
}, { scopes: ["files.read"] });

Drops the cached access token so the next getAccessToken refreshes or re-authenticates. The refresh credential is kept.

invalidateToken({ account: "work" });

Returns the account labels this extension has stored credentials for.

listAccounts(); // e.g. ["default", "work"]

Users authenticate once per plugin (and per account label) with the auth command:

Terminal window
warpdl auth login my-api-extension # loopback PKCE flow, opens a browser
warpdl auth login my-api-extension --as work # store under the "work" label
warpdl auth login my-api-extension --device # device-code flow (requires device_url)
warpdl auth login my-api-extension --no-browser # print the URL instead of opening a browser
warpdl auth login my-api-extension --scopes files.read
warpdl auth list # stored credentials, expiry included
warpdl auth status my-api-extension --as work # exit 0 valid, 1 missing, 2 expired
warpdl auth logout my-api-extension --as work

Credentials are encrypted in tokens.gob in the WarpDL config directory (the encryption key lives in the OS keyring where available) and are separate from the extension code. If a download needs a token that isn’t stored yet, the daemon surfaces an authentication error and the CLI hints at warpdl auth login <plugin-id>. Login waits up to 5 minutes for the browser step to complete.

This extension transforms Google Drive sharing URLs into direct download links:

manifest.json:

{
"name": "gdrive-extractor",
"version": "1.0.0",
"matches": ["^https://drive\\.google\\.com/file/d/.*"],
"description": "Extract direct download links from Google Drive"
}

main.js:

function extract(url) {
// Extract file ID from sharing URL
let match = url.match(/\/file\/d\/([a-zA-Z0-9_-]+)/);
if (!match) {
print("Error: Invalid Google Drive URL");
return "end"; // Signal failure
}
let fileId = match[1];
// Construct direct download URL
let directUrl = "https://drive.google.com/uc?export=download&id=" + fileId;
print("Extracted file ID:", fileId);
return directUrl;
}
Terminal window
warpdl ext install /path/to/extension/
Terminal window
# Show active extensions only
warpdl ext list
# Show all extensions (including inactive)
warpdl ext list --show-all
# or
warpdl ext list -a
Terminal window
warpdl ext activate <extension-id|name>
warpdl ext deactivate <extension-id|name>
Terminal window
warpdl ext uninstall <extension-id|name>
  • Execution: Synchronous only (no async/await, Promises)
  • Network: HTTP requests limited to 1MB response size
  • File System: No direct file system access (use require() for local modules)
  • Isolation: Each extension runs in a separate JavaScript runtime
  • URL Transformation: Extensions can only transform URLs via extract() - no download hooks or progress callbacks

If extract() returns "end", WarpDL treats it as an interaction failure and stops processing:

function extract(url) {
let apiResponse = request({
method: "GET",
url: "https://api.example.com/resolve?url=" + url
});
if (apiResponse.status_code !== 200) {
print("API error:", apiResponse.status_code);
return "end"; // Stop processing
}
return apiResponse.body;
}

Throwing errors or runtime exceptions will also terminate extension execution and display the error to the user.