2 Commits

Author SHA1 Message Date
Austin-Olacsi
966988e360 [feat] engines: add artstation engine (#5689) 2026-01-27 17:18:46 +01:00
Ivan Gabaldon
a9877aba3e [fix] themes: module resolution (#5694)
Vite should not pregen static paths on build, but rather compute the relative path at runtime.

- https://vite.dev/config/shared-options#base

Closes: https://github.com/searxng/searxng/issues/5561
2026-01-27 11:32:23 +01:00
5 changed files with 115 additions and 4 deletions

View File

@@ -34,7 +34,7 @@ const svg2svg_favicon_opts: Config = {
};
export default {
base: "/static/themes/simple/",
base: "./",
publicDir: "static/",
build: {

105
searx/engines/artstation.py Normal file
View File

@@ -0,0 +1,105 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Artstation (images)"""
import re
import typing as t
from json import dumps
from searx.result_types import EngineResults
from searx.network import post
from searx.enginelib import EngineCache
# Engine metadata
about = {
"website": 'https://www.artstation.com/',
"wikidata_id": 'Q65551500',
"official_api_documentation": None,
"use_official_api": False,
"require_api_key": False,
"results": 'JSON',
}
# Engine configuration
paging = True
categories = ['images']
results_per_page = 20
# Search URL
base_url = "https://www.artstation.com/api/v2/search/projects.json"
# Cache keys & expiration
CSRF_PUBLICKEY_CACHE = "public_csrf_token"
CSRF_PRIVATEKEY_CACHE = "private_csrf_token"
KEY_EXPIRATION_SECONDS = 3600
CACHE: EngineCache
def setup(engine_settings: dict[str, t.Any]) -> bool:
global CACHE # pylint: disable=global-statement
CACHE = EngineCache(engine_settings["name"])
return True
def fetch_csrf_tokens():
public_token: str | None = CACHE.get(CSRF_PUBLICKEY_CACHE)
private_token: str | None = CACHE.get(CSRF_PRIVATEKEY_CACHE)
if public_token and private_token:
return public_token, private_token
resp = post("https://www.artstation.com/api/v2/csrf_protection/token.json")
public_token = resp.json()["public_csrf_token"]
private_token = resp.cookies["PRIVATE-CSRF-TOKEN"]
CACHE.set(key=CSRF_PUBLICKEY_CACHE, value=public_token, expire=KEY_EXPIRATION_SECONDS)
CACHE.set(key=CSRF_PRIVATEKEY_CACHE, value=private_token, expire=KEY_EXPIRATION_SECONDS)
return public_token, private_token
def request(query, params):
public_token, private_token = fetch_csrf_tokens()
form_data = {
"query": query,
"page": params["pageno"],
"per_page": results_per_page,
"sorting": "relevance",
"pro_first": 1,
}
params["url"] = base_url
params["method"] = 'POST'
params['headers']['content-type'] = "application/json"
params['headers']['PUBLIC-CSRF-TOKEN'] = public_token
params["cookies"] = {"PRIVATE-CSRF-TOKEN": private_token}
params['data'] = dumps(form_data)
return params
def response(resp) -> EngineResults:
results = EngineResults()
search_res = resp.json()
for item in search_res["data"]:
thumb = item["smaller_square_cover_url"]
fullsize_image = re.sub(r'/\d{6,}/', '/', thumb).replace("smaller_square", "large")
results.add(
results.types.LegacyResult(
{
"template": 'images.html',
"title": item["title"],
"url": item["url"],
"author": f"{item['user']['username']} ({item['user']['full_name']})",
"img_src": fullsize_image,
"thumbnail_src": thumb,
}
)
)
return results

View File

@@ -475,6 +475,12 @@ engines:
shortcut: arc
timeout: 4.0
- name: artstation
engine: artstation
shortcut: as
categories: images
disabled: true
- name: arxiv
engine: arxiv
shortcut: arx

View File

@@ -1,3 +1,3 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["chunk/DBO1tjH7.min.js","chunk/13gvpunf.min.js","chunk/gZqIRpW1.min.js","chunk/DO33-10z.min.js","chunk/Cyk8UkF6.min.js","chunk/Q2W2fJLA.min.js","chunk/KPZlR0ib.min.js","chunk/Dzkb_VTO.min.js","chunk/u0MAGmsp.min.js"])))=>i.map(i=>d[i]);
var e=class{id;constructor(e){this.id=e,this.invoke()}async invoke(){try{console.debug(`[PLUGIN] ${this.id}: Running...`);let e=await this.run();if(!e)return;console.debug(`[PLUGIN] ${this.id}: Running post-exec...`),await this.post(e)}catch(e){console.error(`[PLUGIN] ${this.id}:`,e)}finally{console.debug(`[PLUGIN] ${this.id}: Done.`)}}};const t={index:`index`,results:`results`,preferences:`preferences`,unknown:`unknown`},n={closeDetail:void 0,scrollPageToSelected:void 0,selectImage:void 0,selectNext:void 0,selectPrevious:void 0};var r=()=>{let e=document.querySelector(`meta[name="endpoint"]`)?.getAttribute(`content`);return e&&e in t?e:t.unknown},i=()=>{let e=document.querySelector(`script[client_settings]`)?.getAttribute(`client_settings`);if(!e)return{};try{return JSON.parse(atob(e))}catch(e){return console.error(`Failed to load client_settings:`,e),{}}};const a=async(e,t,n)=>{let r=new AbortController,i=setTimeout(()=>r.abort(),n?.timeout??3e4),a=await fetch(t,{body:n?.body,method:e,signal:r.signal}).finally(()=>clearTimeout(i));if(!a.ok)throw Error(a.statusText);return a},o=(e,t,n,r)=>{if(typeof t!=`string`){t.addEventListener(e,n,r);return}document.addEventListener(e,e=>{for(let r of e.composedPath())if(r instanceof HTMLElement&&r.matches(t)){try{n.call(r,e)}catch(e){console.error(e)}break}},r)},s=(e,t)=>{for(let e of t?.on??[])if(!e)return;document.readyState===`loading`?o(`DOMContentLoaded`,document,e,{once:!0}):e()},c=r(),l=i(),u=(e,t)=>{d(t)&&e()};var d=e=>{switch(e.on){case`global`:return!0;case`endpoint`:return!!e.where.includes(c)}},f=`modulepreload`,p=function(e){return`/static/themes/simple/`+e},m={};const h=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}r=o(t.map(t=>{if(t=p(t,n),t in m)return;m[t]=!0;let r=t.endsWith(`.css`),i=r?`[rel="stylesheet"]`:``;if(n)for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${t}"]${i}`))return;let o=document.createElement(`link`);if(o.rel=r?`stylesheet`:f,r||(o.as=`script`),o.crossOrigin=``,o.href=t,a&&o.setAttribute(`nonce`,a),document.head.appendChild(o),r)return new Promise((e,n)=>{o.addEventListener(`load`,e),o.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})};s(()=>{document.documentElement.classList.remove(`no-js`),document.documentElement.classList.add(`js`),o(`click`,`.close`,function(){this.parentNode?.classList.add(`invisible`)}),o(`click`,`.searxng_init_map`,async function(e){e.preventDefault(),this.classList.remove(`searxng_init_map`),u(()=>h(async()=>{let{default:e}=await import(`./chunk/Dtr6y4WS.min.js`);return{default:e}},[]).then(({default:e})=>new e(this)),{on:`endpoint`,where:[t.results]})}),l.plugins?.includes(`infiniteScroll`)&&u(()=>h(async()=>{let{default:e}=await import(`./chunk/DBO1tjH7.min.js`);return{default:e}},__vite__mapDeps([0,1,2])).then(({default:e})=>new e),{on:`endpoint`,where:[t.results]}),l.plugins?.includes(`calculator`)&&u(()=>h(async()=>{let{default:e}=await import(`./chunk/DO33-10z.min.js`);return{default:e}},__vite__mapDeps([3,2,1])).then(({default:e})=>new e),{on:`endpoint`,where:[t.results]})}),s(()=>{h(()=>import(`./chunk/Cyk8UkF6.min.js`),__vite__mapDeps([4,1])),h(()=>import(`./chunk/Q2W2fJLA.min.js`),__vite__mapDeps([5,2,1])),l.autocomplete&&h(()=>import(`./chunk/KPZlR0ib.min.js`),__vite__mapDeps([6,1]))},{on:[c===t.index]}),s(()=>{h(()=>import(`./chunk/Cyk8UkF6.min.js`),__vite__mapDeps([4,1])),h(()=>import(`./chunk/Dzkb_VTO.min.js`),__vite__mapDeps([7,1])),h(()=>import(`./chunk/Q2W2fJLA.min.js`),__vite__mapDeps([5,2,1])),l.autocomplete&&h(()=>import(`./chunk/KPZlR0ib.min.js`),__vite__mapDeps([6,1]))},{on:[c===t.results]}),s(()=>{h(()=>import(`./chunk/u0MAGmsp.min.js`),__vite__mapDeps([8,1]))},{on:[c===t.preferences]});export{e as a,l as i,o as n,n as r,a as t};
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./chunk/DBO1tjH7.min.js","./chunk/13gvpunf.min.js","./chunk/gZqIRpW1.min.js","./chunk/DO33-10z.min.js","./chunk/Cyk8UkF6.min.js","./chunk/Q2W2fJLA.min.js","./chunk/KPZlR0ib.min.js","./chunk/Dzkb_VTO.min.js","./chunk/u0MAGmsp.min.js"])))=>i.map(i=>d[i]);
var e=class{id;constructor(e){this.id=e,this.invoke()}async invoke(){try{console.debug(`[PLUGIN] ${this.id}: Running...`);let e=await this.run();if(!e)return;console.debug(`[PLUGIN] ${this.id}: Running post-exec...`),await this.post(e)}catch(e){console.error(`[PLUGIN] ${this.id}:`,e)}finally{console.debug(`[PLUGIN] ${this.id}: Done.`)}}};const t={index:`index`,results:`results`,preferences:`preferences`,unknown:`unknown`},n={closeDetail:void 0,scrollPageToSelected:void 0,selectImage:void 0,selectNext:void 0,selectPrevious:void 0};var r=()=>{let e=document.querySelector(`meta[name="endpoint"]`)?.getAttribute(`content`);return e&&e in t?e:t.unknown},i=()=>{let e=document.querySelector(`script[client_settings]`)?.getAttribute(`client_settings`);if(!e)return{};try{return JSON.parse(atob(e))}catch(e){return console.error(`Failed to load client_settings:`,e),{}}};const a=async(e,t,n)=>{let r=new AbortController,i=setTimeout(()=>r.abort(),n?.timeout??3e4),a=await fetch(t,{body:n?.body,method:e,signal:r.signal}).finally(()=>clearTimeout(i));if(!a.ok)throw Error(a.statusText);return a},o=(e,t,n,r)=>{if(typeof t!=`string`){t.addEventListener(e,n,r);return}document.addEventListener(e,e=>{for(let r of e.composedPath())if(r instanceof HTMLElement&&r.matches(t)){try{n.call(r,e)}catch(e){console.error(e)}break}},r)},s=(e,t)=>{for(let e of t?.on??[])if(!e)return;document.readyState===`loading`?o(`DOMContentLoaded`,document,e,{once:!0}):e()},c=r(),l=i(),u=(e,t)=>{d(t)&&e()};var d=e=>{switch(e.on){case`global`:return!0;case`endpoint`:return!!e.where.includes(c)}},f=`modulepreload`,p=function(e,t){return new URL(e,t).href},m={};const h=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}r=o(t.map(t=>{if(t=p(t,n),t in m)return;m[t]=!0;let r=t.endsWith(`.css`),i=r?`[rel="stylesheet"]`:``;if(n)for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${t}"]${i}`))return;let o=document.createElement(`link`);if(o.rel=r?`stylesheet`:f,r||(o.as=`script`),o.crossOrigin=``,o.href=t,a&&o.setAttribute(`nonce`,a),document.head.appendChild(o),r)return new Promise((e,n)=>{o.addEventListener(`load`,e),o.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})};s(()=>{document.documentElement.classList.remove(`no-js`),document.documentElement.classList.add(`js`),o(`click`,`.close`,function(){this.parentNode?.classList.add(`invisible`)}),o(`click`,`.searxng_init_map`,async function(e){e.preventDefault(),this.classList.remove(`searxng_init_map`),u(()=>h(async()=>{let{default:e}=await import(`./chunk/Dtr6y4WS.min.js`);return{default:e}},[],import.meta.url).then(({default:e})=>new e(this)),{on:`endpoint`,where:[t.results]})}),l.plugins?.includes(`infiniteScroll`)&&u(()=>h(async()=>{let{default:e}=await import(`./chunk/DBO1tjH7.min.js`);return{default:e}},__vite__mapDeps([0,1,2]),import.meta.url).then(({default:e})=>new e),{on:`endpoint`,where:[t.results]}),l.plugins?.includes(`calculator`)&&u(()=>h(async()=>{let{default:e}=await import(`./chunk/DO33-10z.min.js`);return{default:e}},__vite__mapDeps([3,2,1]),import.meta.url).then(({default:e})=>new e),{on:`endpoint`,where:[t.results]})}),s(()=>{h(()=>import(`./chunk/Cyk8UkF6.min.js`),__vite__mapDeps([4,1]),import.meta.url),h(()=>import(`./chunk/Q2W2fJLA.min.js`),__vite__mapDeps([5,2,1]),import.meta.url),l.autocomplete&&h(()=>import(`./chunk/KPZlR0ib.min.js`),__vite__mapDeps([6,1]),import.meta.url)},{on:[c===t.index]}),s(()=>{h(()=>import(`./chunk/Cyk8UkF6.min.js`),__vite__mapDeps([4,1]),import.meta.url),h(()=>import(`./chunk/Dzkb_VTO.min.js`),__vite__mapDeps([7,1]),import.meta.url),h(()=>import(`./chunk/Q2W2fJLA.min.js`),__vite__mapDeps([5,2,1]),import.meta.url),l.autocomplete&&h(()=>import(`./chunk/KPZlR0ib.min.js`),__vite__mapDeps([6,1]),import.meta.url)},{on:[c===t.results]}),s(()=>{h(()=>import(`./chunk/u0MAGmsp.min.js`),__vite__mapDeps([8,1]),import.meta.url)},{on:[c===t.preferences]});export{e as a,l as i,o as n,n as r,a as t};
//# sourceMappingURL=sxng-core.min.js.map

File diff suppressed because one or more lines are too long