From 1b2bda58d514316d85ae46502dd99933e97d7137 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Mon, 18 May 2026 13:29:53 +0530 Subject: [PATCH] fix: Improve drive search handling and bump manifest --- dist/drive/posts.js | 2 +- manifest.json | 660 +++++++++++++++++++-------------------- providers/drive/posts.ts | 244 ++++++++++----- 3 files changed, 503 insertions(+), 403 deletions(-) diff --git a/dist/drive/posts.js b/dist/drive/posts.js index 2b6d8f2..d61f85a 100644 --- a/dist/drive/posts.js +++ b/dist/drive/posts.js @@ -1 +1 @@ -"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__name=(target,value)=>__defProp(target,"name",{value:value,configurable:!0}),__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),__async=(__this,__arguments,generator)=>new Promise((resolve,reject)=>{var fulfilled=value=>{try{step(generator.next(value))}catch(e){reject(e)}},rejected=value=>{try{step(generator.throw(value))}catch(e){reject(e)}},step=x=>x.done?resolve(x.value):Promise.resolve(x.value).then(fulfilled,rejected);step((generator=generator.apply(__this,__arguments)).next())}),posts_exports={};__export(posts_exports,{getPosts:()=>getPosts,getSearchPosts:()=>getSearchPosts});var getPosts=__name(function(_0){return __async(this,arguments,function*({filter:filter,page:page,signal:signal,providerContext:providerContext}){const{getBaseUrl:getBaseUrl}=providerContext;return posts({url:`${(yield getBaseUrl("drive"))+filter}page/${page}/`,signal:signal,providerContext:providerContext})})},"getPosts"),getSearchPosts=__name(function(_0){return __async(this,arguments,function*({searchQuery:searchQuery,page:page,signal:signal,providerContext:providerContext}){const{getBaseUrl:getBaseUrl}=providerContext;return posts({url:`${yield getBaseUrl("drive")}page/${page}/?s=${searchQuery}`,signal:signal,providerContext:providerContext})})},"getSearchPosts");function posts(_0){return __async(this,arguments,function*({url:url,signal:signal,providerContext:providerContext}){try{console.log("Fetching URL:",url);const{cheerio:cheerio}=providerContext,res=yield fetch(url,{signal:signal}),data=yield res.text(),$=cheerio.load(data),catalog=[];return $(".poster-card").map((i,element)=>{const title=$(element).find(".poster-title").text(),link=$(element).parent().attr("href"),image=$(element).find(".poster-image img").attr("src");title&&link&&image&&catalog.push({title:title.replace("Download","").trim(),link:link,image:image})}),catalog}catch(err){return console.error("drive error ",err),[]}})}__name(posts,"posts"),exports.getPosts=getPosts,exports.getSearchPosts=getSearchPosts; \ No newline at end of file +"use strict";var __defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__name=(target,value)=>__defProp(target,"name",{value:value,configurable:!0}),__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to},__toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod),__async=(__this,__arguments,generator)=>new Promise((resolve,reject)=>{var fulfilled=value=>{try{step(generator.next(value))}catch(e){reject(e)}},rejected=value=>{try{step(generator.throw(value))}catch(e){reject(e)}},step=x=>x.done?resolve(x.value):Promise.resolve(x.value).then(fulfilled,rejected);step((generator=generator.apply(__this,__arguments)).next())}),posts_exports={};__export(posts_exports,{getPosts:()=>getPosts,getSearchPosts:()=>getSearchPosts});var getPosts=__name(function(_0){return __async(this,arguments,function*({filter:filter,page:page,signal:signal,providerContext:providerContext}){const{getBaseUrl:getBaseUrl}=providerContext;return posts({url:`${(yield getBaseUrl("drive"))+filter}page/${page}/`,signal:signal,providerContext:providerContext})})},"getPosts"),getSearchPosts=__name(function(_0){return __async(this,arguments,function*({searchQuery:searchQuery,page:page,signal:signal,providerContext:providerContext}){const{getBaseUrl:getBaseUrl}=providerContext,baseUrl=yield getBaseUrl("drive");return searchPosts({url:buildSearchUrl(baseUrl,searchQuery,page),baseUrl:baseUrl,signal:signal})})},"getSearchPosts");function posts(_0){return __async(this,arguments,function*({url:url,signal:signal,providerContext:providerContext}){try{console.log("Fetching URL:",url);const{cheerio:cheerio}=providerContext,res=yield fetch(url,{signal:signal}),data=yield res.text(),$=cheerio.load(data),catalog=[];return $(".poster-card").map((i,element)=>{const title=$(element).find(".poster-title").text(),link=$(element).parent().attr("href"),image=$(element).find(".poster-image img").attr("src");title&&link&&image&&catalog.push({title:title.replace("Download","").trim(),link:link,image:image})}),catalog}catch(err){return console.error("drive error ",err),[]}})}function searchPosts(_0){return __async(this,arguments,function*({url:url,baseUrl:baseUrl,signal:signal}){var _a,_b;try{console.log("Fetching drive search URL:",url);const res=yield fetch(url,{signal:signal});if(!res.ok)throw new Error(`drive search failed with status ${res.status}`);return null!=(_b=null==(_a=(yield res.json()).hits)?void 0:_a.map(hit=>{var _a2;const document=hit.document,title=null==(_a2=null==document?void 0:document.post_title)?void 0:_a2.trim(),link=(null==document?void 0:document.permalink)?normalizeUrl(baseUrl,document.permalink):"",image=(null==document?void 0:document.post_thumbnail)?normalizeUrl(baseUrl,document.post_thumbnail):"";return title&&link&&image?{title:title,link:link,image:image}:null}).filter(post=>null!==post))?_b:[]}catch(err){return console.error("drive search error ",err),[]}})}function buildSearchUrl(baseUrl,searchQuery,page){const separator=baseUrl.includes("?")?"&":"?";return`${trimTrailingSlash(baseUrl)}/search.php${separator}q=${encodeURIComponent(searchQuery)}&page=${page}`}function normalizeUrl(baseUrl,value){return value?/^https?:\/\//i.test(value)?value:value.startsWith("//")?`https:${value}`:value.startsWith("/")?`${trimTrailingSlash(baseUrl)}${value}`:`${trimTrailingSlash(baseUrl)}/${trimLeadingSlash(value)}`:""}function trimTrailingSlash(value){return value.replace(/\/+$/,"")}function trimLeadingSlash(value){return value.replace(/^\/+/,"")}__name(posts,"posts"),__name(searchPosts,"searchPosts"),__name(buildSearchUrl,"buildSearchUrl"),__name(normalizeUrl,"normalizeUrl"),__name(trimTrailingSlash,"trimTrailingSlash"),__name(trimLeadingSlash,"trimLeadingSlash"),exports.getPosts=getPosts,exports.getSearchPosts=getSearchPosts; \ No newline at end of file diff --git a/manifest.json b/manifest.json index 58b000c..fc96512 100644 --- a/manifest.json +++ b/manifest.json @@ -1,330 +1,330 @@ -[ - { - "display_name": "VegaMovies", - "value": "vega", - "version": "2.8", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "MultiStream", - "value": "autoEmbed", - "version": "1.6", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "MoviesDrive", - "value": "drive", - "version": "2.0", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "MultiMovies", - "value": "multi", - "version": "1.3", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "4khdHub", - "value": "4khdhub", - "version": "2.2", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "Cinewood", - "value": "1cinevood", - "version": "1.4", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "World4uFree", - "value": "world4u", - "version": "1.4", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "KatMoviesHd", - "value": "katmovies", - "version": "1.6", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "MoviesMod", - "value": "mod", - "version": "1.3", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "UHDMovies", - "value": "uhd", - "version": "1.2", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "ProtonMovies", - "value": "protonMovies", - "version": "1.4", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "CinemaLuxe", - "value": "cinemaLuxe", - "version": "1.7", - "icon": "", - "type": "global", - "disabled": true - }, - { - "display_name": "FilmyFly", - "value": "filmyfly", - "version": "1.4", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "MovieBox", - "value": "movieBox", - "version": "1.2", - "icon": "", - "type": "global", - "disabled": true - }, - { - "display_name": "Movies4U", - "value": "movies4u", - "version": "1.5", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "KmMovies", - "value": "kmMovies", - "version": "1.3", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "Zeefliz", - "value": "zeefliz", - "version": "1.5", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "KatMovieFix", - "value": "katMovieFix", - "version": "1.6", - "icon": "", - "type": "global", - "disabled": true - }, - { - "display_name": "Ringz", - "value": "ringz", - "version": "1.0", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "NetflixMirror", - "value": "netflixMirror", - "version": "1.7", - "icon": "", - "type": "global", - "disabled": true - }, - { - "display_name": "PrimeMirror", - "value": "primeMirror", - "version": "1.6", - "icon": "", - "type": "global", - "disabled": true - }, - { - "display_name": "HdHub4u", - "value": "hdhub4u", - "version": "1.9", - "icon": "", - "type": "global", - "disabled": false - }, - { - "display_name": "Ogomovies", - "value": "ogomovies", - "version": "1.0", - "icon": "", - "type": "india", - "disabled": true - }, - { - "display_name": "A.111477", - "value": "a111477", - "version": "1.2", - "icon": "", - "type": "english", - "disabled": true - }, - { - "display_name": "VadaPav", - "value": "vadapav", - "version": "1.0", - "icon": "", - "type": "global", - "disabled": true - }, - { - "display_name": "MoviesApi", - "value": "moviesApi", - "version": "1.0", - "icon": "", - "type": "english", - "disabled": true - }, - { - "display_name": "MoviezWap", - "value": "moviezwap", - "version": "1.0", - "icon": "", - "type": "india", - "disabled": false - }, - { - "display_name": "ShowBox", - "value": "showbox", - "version": "1.2", - "icon": "", - "type": "english", - "disabled": false - }, - { - "display_name": "RidoMovies", - "value": "ridoMovies", - "version": "1.0", - "icon": "", - "type": "english", - "disabled": false - }, - { - "display_name": "FlixHQ", - "value": "flixhq", - "version": "1.0", - "icon": "", - "type": "english", - "disabled": false - }, - { - "display_name": "Primewire", - "value": "primewire", - "version": "1.0", - "icon": "", - "type": "english", - "disabled": false - }, - { - "display_name": "HiAnime", - "value": "hiAnime", - "version": "1.1", - "icon": "", - "type": "english", - "disabled": true - }, - { - "display_name": "Animetsu", - "value": "animetsu", - "version": "1.1", - "icon": "", - "type": "english", - "disabled": true - }, - { - "display_name": "TokyoInsider", - "value": "tokyoInsider", - "version": "1.0", - "icon": "", - "type": "english", - "disabled": false - }, - { - "display_name": "KissKh", - "value": "kissKh", - "version": "1.2", - "icon": "", - "type": "english", - "disabled": false - }, - { - "display_name": "Dooflix", - "value": "dooflix", - "version": "1.4", - "icon": "", - "type": "india", - "disabled": true - }, - { - "display_name": "RogMovies", - "value": "luxMovies", - "version": "2.3", - "icon": "", - "type": "india", - "disabled": false - }, - { - "display_name": "TopMovies", - "value": "topmovies", - "version": "1.2", - "icon": "", - "type": "india", - "disabled": false - }, - { - "display_name": "GuardaHD", - "value": "guardahd", - "version": "1.6", - "icon": "", - "type": "italy", - "disabled": false - }, - { - "display_name": "SkyMovieHD", - "value": "skyMovieHD", - "version": "1.6", - "icon": "", - "type": "global", - "disabled": true - }, - { - "display_name": "Joya9tv", - "value": "Joya9tv", - "version": "1.5", - "icon": "", - "type": "india", - "disabled": false - } -] +[ + { + "display_name": "VegaMovies", + "value": "vega", + "version": "2.8", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "MultiStream", + "value": "autoEmbed", + "version": "1.6", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "MoviesDrive", + "value": "drive", + "version": "2.1", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "MultiMovies", + "value": "multi", + "version": "1.3", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "4khdHub", + "value": "4khdhub", + "version": "2.2", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "Cinewood", + "value": "1cinevood", + "version": "1.4", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "World4uFree", + "value": "world4u", + "version": "1.4", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "KatMoviesHd", + "value": "katmovies", + "version": "1.6", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "MoviesMod", + "value": "mod", + "version": "1.3", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "UHDMovies", + "value": "uhd", + "version": "1.2", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "ProtonMovies", + "value": "protonMovies", + "version": "1.4", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "CinemaLuxe", + "value": "cinemaLuxe", + "version": "1.7", + "icon": "", + "type": "global", + "disabled": true + }, + { + "display_name": "FilmyFly", + "value": "filmyfly", + "version": "1.4", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "MovieBox", + "value": "movieBox", + "version": "1.2", + "icon": "", + "type": "global", + "disabled": true + }, + { + "display_name": "Movies4U", + "value": "movies4u", + "version": "1.5", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "KmMovies", + "value": "kmMovies", + "version": "1.3", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "Zeefliz", + "value": "zeefliz", + "version": "1.5", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "KatMovieFix", + "value": "katMovieFix", + "version": "1.6", + "icon": "", + "type": "global", + "disabled": true + }, + { + "display_name": "Ringz", + "value": "ringz", + "version": "1.0", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "NetflixMirror", + "value": "netflixMirror", + "version": "1.7", + "icon": "", + "type": "global", + "disabled": true + }, + { + "display_name": "PrimeMirror", + "value": "primeMirror", + "version": "1.6", + "icon": "", + "type": "global", + "disabled": true + }, + { + "display_name": "HdHub4u", + "value": "hdhub4u", + "version": "1.9", + "icon": "", + "type": "global", + "disabled": false + }, + { + "display_name": "Ogomovies", + "value": "ogomovies", + "version": "1.0", + "icon": "", + "type": "india", + "disabled": true + }, + { + "display_name": "A.111477", + "value": "a111477", + "version": "1.2", + "icon": "", + "type": "english", + "disabled": true + }, + { + "display_name": "VadaPav", + "value": "vadapav", + "version": "1.0", + "icon": "", + "type": "global", + "disabled": true + }, + { + "display_name": "MoviesApi", + "value": "moviesApi", + "version": "1.0", + "icon": "", + "type": "english", + "disabled": true + }, + { + "display_name": "MoviezWap", + "value": "moviezwap", + "version": "1.0", + "icon": "", + "type": "india", + "disabled": false + }, + { + "display_name": "ShowBox", + "value": "showbox", + "version": "1.2", + "icon": "", + "type": "english", + "disabled": false + }, + { + "display_name": "RidoMovies", + "value": "ridoMovies", + "version": "1.0", + "icon": "", + "type": "english", + "disabled": false + }, + { + "display_name": "FlixHQ", + "value": "flixhq", + "version": "1.0", + "icon": "", + "type": "english", + "disabled": false + }, + { + "display_name": "Primewire", + "value": "primewire", + "version": "1.0", + "icon": "", + "type": "english", + "disabled": false + }, + { + "display_name": "HiAnime", + "value": "hiAnime", + "version": "1.1", + "icon": "", + "type": "english", + "disabled": true + }, + { + "display_name": "Animetsu", + "value": "animetsu", + "version": "1.1", + "icon": "", + "type": "english", + "disabled": true + }, + { + "display_name": "TokyoInsider", + "value": "tokyoInsider", + "version": "1.0", + "icon": "", + "type": "english", + "disabled": false + }, + { + "display_name": "KissKh", + "value": "kissKh", + "version": "1.2", + "icon": "", + "type": "english", + "disabled": false + }, + { + "display_name": "Dooflix", + "value": "dooflix", + "version": "1.4", + "icon": "", + "type": "india", + "disabled": true + }, + { + "display_name": "RogMovies", + "value": "luxMovies", + "version": "2.3", + "icon": "", + "type": "india", + "disabled": false + }, + { + "display_name": "TopMovies", + "value": "topmovies", + "version": "1.2", + "icon": "", + "type": "india", + "disabled": false + }, + { + "display_name": "GuardaHD", + "value": "guardahd", + "version": "1.6", + "icon": "", + "type": "italy", + "disabled": false + }, + { + "display_name": "SkyMovieHD", + "value": "skyMovieHD", + "version": "1.6", + "icon": "", + "type": "global", + "disabled": true + }, + { + "display_name": "Joya9tv", + "value": "Joya9tv", + "version": "1.5", + "icon": "", + "type": "india", + "disabled": false + } +] diff --git a/providers/drive/posts.ts b/providers/drive/posts.ts index 1ed9abb..6328094 100644 --- a/providers/drive/posts.ts +++ b/providers/drive/posts.ts @@ -1,72 +1,172 @@ -import { Post, ProviderContext } from "../types"; - -export const getPosts = async function ({ - filter, - page, - signal, - providerContext, -}: { - filter: string; - page: number; - providerValue: string; - signal: AbortSignal; - providerContext: ProviderContext; -}): Promise { - const { getBaseUrl } = providerContext; - const baseUrl = await getBaseUrl("drive"); - const url = `${baseUrl + filter}page/${page}/`; - return posts({ url, signal, providerContext }); -}; - -export const getSearchPosts = async function ({ - searchQuery, - page, - signal, - providerContext, -}: { - searchQuery: string; - page: number; - providerValue: string; - providerContext: ProviderContext; - signal: AbortSignal; -}): Promise { - const { getBaseUrl } = providerContext; - const baseUrl = await getBaseUrl("drive"); - const url = `${baseUrl}page/${page}/?s=${searchQuery}`; - return posts({ url, signal, providerContext }); -}; - -async function posts({ - url, - signal, - providerContext, -}: { - url: string; - signal: AbortSignal; - providerContext: ProviderContext; -}): Promise { - try { - console.log("Fetching URL:", url); - const { cheerio } = providerContext; - const res = await fetch(url, { signal }); - const data = await res.text(); - const $ = cheerio.load(data); - const catalog: Post[] = []; - $(".poster-card").map((i, element) => { - const title = $(element).find(".poster-title").text(); - const link = $(element).parent().attr("href"); - const image = $(element).find(".poster-image img").attr("src"); - if (title && link && image) { - catalog.push({ - title: title.replace("Download", "").trim(), - link: link, - image: image, - }); - } - }); - return catalog; - } catch (err) { - console.error("drive error ", err); - return []; - } -} +import { Post, ProviderContext } from "../types"; + +export const getPosts = async function ({ + filter, + page, + signal, + providerContext, +}: { + filter: string; + page: number; + providerValue: string; + signal: AbortSignal; + providerContext: ProviderContext; +}): Promise { + const { getBaseUrl } = providerContext; + const baseUrl = await getBaseUrl("drive"); + const url = `${baseUrl + filter}page/${page}/`; + return posts({ url, signal, providerContext }); +}; + +export const getSearchPosts = async function ({ + searchQuery, + page, + signal, + providerContext, +}: { + searchQuery: string; + page: number; + providerValue: string; + providerContext: ProviderContext; + signal: AbortSignal; +}): Promise { + const { getBaseUrl } = providerContext; + const baseUrl = await getBaseUrl("drive"); + const url = buildSearchUrl(baseUrl, searchQuery, page); + return searchPosts({ + url, + baseUrl, + signal, + }); +}; + +async function posts({ + url, + signal, + providerContext, +}: { + url: string; + signal: AbortSignal; + providerContext: ProviderContext; +}): Promise { + try { + console.log("Fetching URL:", url); + const { cheerio } = providerContext; + const res = await fetch(url, { signal }); + const data = await res.text(); + const $ = cheerio.load(data); + const catalog: Post[] = []; + $(".poster-card").map((i, element) => { + const title = $(element).find(".poster-title").text(); + const link = $(element).parent().attr("href"); + const image = $(element).find(".poster-image img").attr("src"); + if (title && link && image) { + catalog.push({ + title: title.replace("Download", "").trim(), + link: link, + image: image, + }); + } + }); + return catalog; + } catch (err) { + console.error("drive error ", err); + return []; + } +} + +async function searchPosts({ + url, + baseUrl, + signal, +}: { + url: string; + baseUrl: string; + signal: AbortSignal; +}): Promise { + try { + console.log("Fetching drive search URL:", url); + const res = await fetch(url, { signal }); + + if (!res.ok) { + throw new Error(`drive search failed with status ${res.status}`); + } + + const data = (await res.json()) as { + hits?: Array<{ + document?: { + permalink?: string; + post_thumbnail?: string; + post_title?: string; + }; + }>; + }; + + return ( + data.hits + ?.map((hit) => { + const document = hit.document; + const title = document?.post_title?.trim(); + const link = document?.permalink + ? normalizeUrl(baseUrl, document.permalink) + : ""; + const image = document?.post_thumbnail + ? normalizeUrl(baseUrl, document.post_thumbnail) + : ""; + + if (!title || !link || !image) { + return null; + } + + return { + title, + link, + image, + }; + }) + .filter((post): post is Post => post !== null) ?? [] + ); + } catch (err) { + console.error("drive search error ", err); + return []; + } +} + +function buildSearchUrl( + baseUrl: string, + searchQuery: string, + page: number, +): string { + const separator = baseUrl.includes("?") ? "&" : "?"; + return `${trimTrailingSlash(baseUrl)}/search.php${separator}q=${encodeURIComponent( + searchQuery, + )}&page=${page}`; +} + +function normalizeUrl(baseUrl: string, value: string): string { + if (!value) { + return ""; + } + + if (/^https?:\/\//i.test(value)) { + return value; + } + + if (value.startsWith("//")) { + return `https:${value}`; + } + + if (value.startsWith("/")) { + return `${trimTrailingSlash(baseUrl)}${value}`; + } + + return `${trimTrailingSlash(baseUrl)}/${trimLeadingSlash(value)}`; +} + +function trimTrailingSlash(value: string): string { + return value.replace(/\/+$/, ""); +} + +function trimLeadingSlash(value: string): string { + return value.replace(/^\/+/, ""); +}