From ce834a06cad26f24ae2b597ba0d8997540d9f5e4 Mon Sep 17 00:00:00 2001 From: "planetrenox@protonmail.com" Date: Thu, 15 Oct 2026 16:29:09 +0000 Subject: [PATCH] Feat: Ask Brave for 100 results and allow ?i= up to 40 on the main site free.* and ai.* stay at 20, since Openverse and Wikimedia return ~50 results together. Also note that production's Openverse requests are authenticated. Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- functions/[[path]].js | 11 ++++++----- functions/_utils/brave.js | 2 +- functions/_utils/openverse.js | 3 ++- index.html | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index fdc76a9..7dfd201 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Add `?i=` to serve a later search result instead of the first, e.g. when the fir https://direct-img.link/orange+cat?i=2 ``` -- `i` is `1`–`20`, default `1`. Invalid values serve `bad.webp` +- `i` is `1`–`40`, default `1`. Invalid values serve `bad.webp` - Leading zeros are ignored (`?i=02` = `?i=2`) - Only images that download count, so broken links and deleted photos are skipped and every `i` is a different image. `?i=2` is the 2nd working image - Higher `i` values take a little longer the first time, since the working images before it are downloaded to count them diff --git a/functions/[[path]].js b/functions/[[path]].js index 714b811..0253571 100644 --- a/functions/[[path]].js +++ b/functions/[[path]].js @@ -6,7 +6,8 @@ import { aiImage, parseAiSource, DEFAULT_AI_SOURCE } from "./_utils/ai.js"; import * as LIMITS from "../assets/rate-limit.js"; const TTL_SECONDS = 90 * 24 * 60 * 60; -const MAX_INDEX = 20; +// Highest ?i= per site: 40 on the main site (100 Brave results), 20 on free.* (~50 Openverse + Wikimedia results) and ai.* +const MAX_INDEX = { "": 40, free: 20, ai: 20 }; // How long a search may hold its query's lock; longer than the 20s image deadline plus the search itself const LOCK_SECONDS = 45; // How long a search's results are kept for the query's other ?i=, which then don't search again or count toward limits @@ -43,7 +44,7 @@ export async function onRequest(context) { const site = ["free", "ai"].find(s => url.hostname.startsWith(`${s}.`)) || ""; const free = site === "free", ai = site === "ai"; const label = { free: "Free ", ai: "AI " }[site] || ""; - const i = parseIndex(url.searchParams); + const i = parseIndex(url.searchParams, MAX_INDEX[site]); const src = free ? parseFreeSource(url.searchParams) : ai ? parseAiSource(url.searchParams) : null; const extra = [...url.searchParams.keys()].some(k => k !== "i" && !(site && k === "src")); if (!i || (site && !src) || extra) return asset("bad.webp"); @@ -278,10 +279,10 @@ COMMIT TRANSACTION;`; return job; } -// ?i=1..MAX_INDEX (default 1). Returns null if invalid. -function parseIndex(searchParams) { +// ?i=1..max (default 1). Returns null if invalid. +function parseIndex(searchParams, max) { const i = searchParams.get("i") || "1"; - return /^\d+$/.test(i) && +i >= 1 && +i <= MAX_INDEX ? +i : null; + return /^\d+$/.test(i) && +i >= 1 && +i <= max ? +i : null; } // Brave results, or Bing's if Brave has none or refuses (e.g. once its spending limit is hit). null if Bing fails too, diff --git a/functions/_utils/brave.js b/functions/_utils/brave.js index 0ee2292..737af54 100644 --- a/functions/_utils/brave.js +++ b/functions/_utils/brave.js @@ -1,7 +1,7 @@ // { urls } ([] if none found), or { error } if Brave refused or failed, e.g. once our rate or spending limit is hit export async function braveImageSearch(query, apiKey) { try { - const res = await fetch(`https://api.search.brave.com/res/v1/images/search?q=${encodeURIComponent(query)}&count=50&safesearch=off`, { + const res = await fetch(`https://api.search.brave.com/res/v1/images/search?q=${encodeURIComponent(query)}&count=100&safesearch=off`, { headers: { "Accept": "application/json", "X-Subscription-Token": apiKey }, }); if (!res.ok) return { error: `HTTP ${res.status}: ${(await res.text()).slice(0, 300)}` }; diff --git a/functions/_utils/openverse.js b/functions/_utils/openverse.js index 137073a..25f5198 100644 --- a/functions/_utils/openverse.js +++ b/functions/_utils/openverse.js @@ -2,7 +2,8 @@ const API = "https://api.openverse.org/v1"; // Uppercase so it can't collide with query cache keys, which are always lowercased const TOKEN_KEY = "OPENVERSE_TOKEN"; -// Tokens expire (~12h); client ID/secret are permanent, so fetch a new token whenever the cached one is gone +// Tokens expire (~12h); client ID/secret are permanent, so fetch a new token whenever the cached one is gone. +// Production has OPENVERSE_CLIENT_ID/SECRET set, so its requests are authenticated. async function getToken(env) { if (!env.OPENVERSE_CLIENT_ID || !env.OPENVERSE_CLIENT_SECRET) return null; const cached = await env.DIRECT_IMG_CACHE.get(TOKEN_KEY); diff --git a/index.html b/index.html index 6b7384b..0c5849d 100644 --- a/index.html +++ b/index.html @@ -75,7 +75,7 @@ Use images to complement your responses, powered by Brave.

Usage

Embed directly in your markdown files:

![orange cat](https://direct-img.link/orange+cat)
-

Not the image you wanted? Add ?i=2 (up to 20) to serve the next one:

+

Not the image you wanted? Add ?i=2 (up to 40) to serve the next one:

![orange cat](https://direct-img.link/orange+cat?i=2)

* Note: Literal dots (.) and slashes (/) are rejected to prevent bot abuse. Use %2E or %2F if your query requires them.