about summary refs log tree commit diff stats
path: root/src/assets/javascripts
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-01-29 17:19:04 +0100
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-01-29 17:19:04 +0100
commit0cfcd80f3a715a77821aa83e0c89245ec2f53eec (patch)
treeaec2aea5c24c15f287bc05e695b60d45a26e6dc8 /src/assets/javascripts
parentchore(merge): Merge remote-tracking branch 'origin/master' (diff)
parentAdded Structables, and made it the default https://codeberg.org/LibRedirect/b... (diff)
downloadlibredirect-0cfcd80f3a715a77821aa83e0c89245ec2f53eec.zip
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'src/assets/javascripts')
-rw-r--r--src/assets/javascripts/services.js188
-rw-r--r--src/assets/javascripts/utils.js20
2 files changed, 165 insertions, 43 deletions
diff --git a/src/assets/javascripts/services.js b/src/assets/javascripts/services.js
index 7213380f..961759c2 100644
--- a/src/assets/javascripts/services.js
+++ b/src/assets/javascripts/services.js
@@ -54,9 +54,10 @@ function regexArray(service, url, config, options, frontend) {
  * @param {URL} url
  * @param {string} frontend
  * @param {string} randomInstance
+ * @param {string} type
  * @returns {undefined|string}
  */
-function rewrite(url, originUrl, frontend, randomInstance) {
+function rewrite(url, originUrl, frontend, randomInstance, type) {
   switch (frontend) {
     case "hyperpipe":
       for (const key of [...url.searchParams.keys()]) if (key !== "q") url.searchParams.delete(key)
@@ -99,6 +100,7 @@ function rewrite(url, originUrl, frontend, randomInstance) {
       if (/\/@[a-z]+\//.exec(url.pathname)) return randomInstance
       return `${randomInstance}${url.pathname}${url.search}`
     }
+    case "small":
     case "libMedium":
     case "scribe": {
       const regex = url.hostname.match(/^(link|cdn-images-\d+|.*)\.medium\.com/)
@@ -220,9 +222,9 @@ function rewrite(url, originUrl, frontend, randomInstance) {
       return `${randomInstance}${url.pathname}${url.search}`
     case "redlib":
     case "libreddit": {
-      const subdomain = url.hostname.match(/^(?:(?:external-)?preview|i)(?=\.redd\.it)/)
+      const subdomain = url.hostname.match(/^(?:((?:external-)?preview|i)\.)?redd\.it/)
       if (!subdomain) return `${randomInstance}${url.pathname}${url.search}`
-      switch (subdomain[0]) {
+      switch (subdomain[1]) {
         case "preview":
           return `${randomInstance}/preview/pre${url.pathname}${url.search}`
         case "external-preview":
@@ -230,7 +232,7 @@ function rewrite(url, originUrl, frontend, randomInstance) {
         case "i":
           return `${randomInstance}/img${url.pathname}`
       }
-      return randomInstance
+      return `${randomInstance}/comments${url.pathname}`
     }
     case "teddit":
       if (/^(?:(?:external-)?preview|i)\.redd\.it/.test(url.hostname)) {
@@ -238,6 +240,7 @@ function rewrite(url, originUrl, frontend, randomInstance) {
         else return `${randomInstance}${url.pathname}${url.search}&teddit_proxy=${url.hostname}`
       }
       return `${randomInstance}${url.pathname}${url.search}`
+    case "troddit":
     case "eddrit":
       if (/^(?:(?:external-)?preview|i)\.redd\.it/.test(url.hostname)) return randomInstance
       return `${randomInstance}${url.pathname}${url.search}`
@@ -267,8 +270,11 @@ function rewrite(url, originUrl, frontend, randomInstance) {
         // https://stackexchange.com or https://superuser.com
         return `${randomInstance}${url.pathname}${url.search}`
       }
-      const regex = url.href.match(/https?:\/{2}(?:([a-zA-Z0-9-]+)\.)?stackexchange\.com\//)
+      const regex = url.href.match(/https?:\/{2}(?:([a-zA-Z0-9-]+)\.(meta\.)?)?stackexchange\.com\//)
       if (regex && regex.length > 1) {
+        if (regex[2]) {
+          return `${randomInstance}/exchange/${url.hostname}${url.pathname}${url.search}`
+        }
         const subdomain = regex[1]
         return `${randomInstance}/exchange/${subdomain}${url.pathname}${url.search}`
       }
@@ -282,6 +288,15 @@ function rewrite(url, originUrl, frontend, randomInstance) {
     }
     case "biblioReads":
       return `${randomInstance}${url.pathname}${url.search}`
+    case "wikimore": {
+      let hostSplit = url.host.split(".")
+      // wikiless doesn't have mobile view support yet
+      if (hostSplit[0] != "wikipedia" && hostSplit[0] != "www") {
+        const lang = url.hostname.split(".")[0]
+        return `${randomInstance}/wiki/${lang}${url.pathname}${url.search}${url.hash}`
+      }
+      return `${randomInstance}${url.pathname}${url.search}${url.hash}`
+    }
     case "wikiless": {
       let hostSplit = url.host.split(".")
       // wikiless doesn't have mobile view support yet
@@ -292,6 +307,7 @@ function rewrite(url, originUrl, frontend, randomInstance) {
       }
       return `${randomInstance}${url.pathname}${url.search}${url.hash}`
     }
+    case "offtiktok":
     case "proxiTok":
       if (url.pathname.startsWith("/email")) return randomInstance
       return `${randomInstance}${url.pathname}${url.search}`
@@ -353,7 +369,10 @@ function rewrite(url, originUrl, frontend, randomInstance) {
       }
     }
     case "binternet":
-      if (url.hostname == "i.pinimg.com") return `${randomInstance}/image_proxy.php?url=${url.href}`
+      if (url.hostname == "i.pinimg.com") return `${randomInstance}/image_proxy.php?url=${encodeURIComponent(url.href)}`
+      return `${randomInstance}${url.pathname}${url.search}`
+    case "painterest":
+      if (url.hostname == "i.pinimg.com") return `${randomInstance}/_/proxy?url=${encodeURIComponent(url.href)}`
       return `${randomInstance}${url.pathname}${url.search}`
     case "laboratory": {
       let path = url.pathname
@@ -378,11 +397,24 @@ function rewrite(url, originUrl, frontend, randomInstance) {
       }
       return `${randomInstance}${url.pathname}${url.search}`
     }
+    case "vixipy": {
+      const regex = /\/[a-z]{1,3}\/(.*)/.exec(url.pathname)
+      if (regex) {
+        let path = regex[1]
+        if (path.startsWith("tags/")) path = path.replace(/tags/, "tag")
+        return `${randomInstance}/${path}${url.search}`
+      }
+      return `${randomInstance}${url.pathname}${url.search}`
+    }
     case "invidious": {
+      // tracker
       url.searchParams.delete("si")
+
+      if (type == "sub_frame") url.searchParams.append("autoplay", "0")
+
       if (url.hostname == "youtu.be" || (url.hostname.endsWith("youtube.com") && url.pathname.startsWith("/live"))) {
         const watch = url.pathname.substring(url.pathname.lastIndexOf("/") + 1)
-        return `${randomInstance}/watch?v=${watch}${url.search.replace("?", "&")}`
+        return `${randomInstance}/watch?v=${watch}&${url.search.substring(1)}`
       }
       if (url.hostname.endsWith("youtube.com") && url.pathname.startsWith("/redirect?")) return url.href
       return `${randomInstance}${url.pathname}${url.search}`
@@ -501,6 +533,7 @@ function rewrite(url, originUrl, frontend, randomInstance) {
       }
       return `${randomInstance}${url.pathname}${url.search}`
     }
+    case "ultimateTab":
     case "freetar":
       if (url.pathname.startsWith("/search.php")) {
         url.searchParams.set("search_term", url.searchParams.get("value"))
@@ -519,16 +552,16 @@ function rewrite(url, originUrl, frontend, randomInstance) {
       return `${randomInstance}${url.pathname}${url.search}`
     }
     case "skunkyArt": {
-      if (url.pathname.startsWith("/search")) return `${randomInstance}${url.pathname}${url.search}&scope=all`
+      if (url.pathname.startsWith("/search")) return `${randomInstance}${url.pathname}${url.search}&type=all`
 
-      const artReg = /^\/.*?\/art\/(.*)\/?/.exec(url.pathname)
-      if (artReg) return `${randomInstance}/post/art/${artReg[1]}${url.search}`
+      const artReg = /^\/(.*?)\/art\/(.*)\/?/.exec(url.pathname)
+      if (artReg) return `${randomInstance}/post/${artReg[1]}/${artReg[2]}${url.search}`
 
       const userReg = /^\/([^\/]+)$/.exec(url.pathname)
-      if (userReg) return `${randomInstance}/user/${userReg[1]}${url.search}`
+      if (userReg) return `${randomInstance}/group_user?q=${userReg[1]}&type=about`
 
-      const galleryReg = /^\/.*?\/gallery(\/$|$)$/.exec(url.pathname)
-      if (galleryReg) return `${randomInstance}/user/${userReg[1]}?a=gallery`
+      const galleryReg = /^\/(.*?)\/gallery(\/$|$)$/.exec(url.pathname)
+      if (galleryReg) return `${randomInstance}/group_user?q=${galleryReg[1]}&type=gallery`
 
       return `${randomInstance}${url.pathname}${url.search}`
     }
@@ -550,6 +583,43 @@ function rewrite(url, originUrl, frontend, randomInstance) {
       const accountReg = /^\/([^\/]+)\/?$/.exec(url.pathname)
       if (accountReg) return `${randomInstance}/account${url.pathname}${url.search}`
 
+    case "duckDuckGoAiChat":
+      return "https://duckduckgo.com/?q=DuckDuckGo+AI+Chat&ia=chat&duckai=1"
+
+    case "soundcloak":
+      if (url.pathname.startsWith("/feed") || url.pathname.startsWith("/stream")) {
+        // this feature requires authentication and is unsupported, so just redirect to main page
+        return randomInstance
+      }
+
+      if (url.pathname.startsWith("/search")) {
+        if (!url.search) {
+          return randomInstance
+        }
+
+        let type = ""
+        if (url.pathname.startsWith("/search/sounds")) {
+          type = "tracks"
+        } else if (url.pathname.startsWith("/search/people")) {
+          type = "users"
+        } else if (url.pathname.startsWith("/search/albums") || url.pathname.startsWith("/search/sets")) {
+          type = "playlists"
+        }
+
+        if (type) {
+          type = "&type=" + type
+        } else {
+          return randomInstance // fallback for unsupported search types (searching for anything for example)
+        }
+
+        return `${randomInstance}/search${url.search}${type}`
+      }
+
+      if (url.host == "on.soundcloud.com") {
+        return `${randomInstance}/on${url.pathname}`
+      }
+
+      return `${randomInstance}${url.pathname}${url.search}`
     case "piped":
     case "pipedMaterial":
     case "cloudtube":
@@ -627,7 +697,7 @@ function redirect(url, type, originUrl, documentUrl, incognito, forceRedirection
   }
   if (!frontend) return
 
-  return rewrite(url, originUrl, frontend, randomInstance)
+  return rewrite(url, originUrl, frontend, randomInstance, type)
 }
 
 /**
@@ -647,30 +717,19 @@ async function redirectAsync(url, type, originUrl, documentUrl, incognito, force
 /**
  * @param {URL} url
  */
-function computeService(url) {
-  return new Promise(async resolve => {
-    const config = await utils.getConfig()
-    const options = await utils.getOptions()
-    for (const service in config.services) {
-      if (regexArray(service, url, config, options)) {
-        resolve(service)
-        return
-      } else {
-        for (const frontend in config.services[service].frontends) {
-          if (all(service, frontend, options, config).findIndex(instance => url.href.startsWith(instance)) >= 0) {
-            return resolve(service)
-          }
-        }
-      }
-    }
-    resolve()
-  })
-}
-export function computeFrontend(url) {
+async function computeServiceFrontend(url) {
+  const config = await utils.getConfig()
+  const options = await utils.getOptions()
   for (const service in config.services) {
-    for (const frontend in config.services[service].frontends) {
-      if (all(service, frontend, options, config).findIndex(instance => url.href.startsWith(instance)) >= 0) {
-        return { service, frontend }
+    if (regexArray(service, url, config, options)) {
+      return { service, frontend: null }
+    } else {
+      for (const frontend in config.services[service].frontends) {
+        const instances = all(service, frontend, options, config)
+        const i = instances.findIndex(instance => url.href.startsWith(instance))
+        if (i >= 0) {
+          return { service, frontend }
+        }
       }
     }
   }
@@ -765,6 +824,39 @@ async function reverse(url) {
         return `${config.services[service].url}/${url.search.slice(1)}`
       case "goodreads":
         return `https://goodreads.com${url.pathname}${url.search}`
+      case "soundcloud":
+        if (frontend == "soundcloak") {
+          if (url.pathname.includes("/_/")) {
+            // soundcloak-specific pages
+            return `${config.services[service].url}${url.pathname.split("/_/")[0]}`
+          }
+
+          if (url.pathname == "/search") {
+            let type = url.searchParams.get("type")
+            switch (type) {
+              case "playlists":
+                type = "sets"
+                break
+              case "tracks":
+                type = "sounds"
+                break
+              case "users":
+                type = "people"
+                break
+              default:
+                type = ""
+            }
+
+            url.searchParams.delete("type")
+            if (!type) {
+              return `${config.services[service].url}/search?${url.searchParams.toString()}`
+            } else {
+              return `${config.services[service].url}/search/${type}?${url.searchParams.toString()}`
+            }
+          }
+
+          return `${config.services[service].url}${url.pathname}`
+        }
       default:
         return
     }
@@ -783,9 +875,12 @@ const defaultInstances = {
   poketube: ["https://poketube.fun"],
   proxiTok: ["https://proxitok.pabloferreiro.es"],
   redlib: ["https://libreddit.vhack.eu"],
+  offtiktok: ["https://www.offtiktok.com"],
   eddrit: ["https://eddrit.com"],
+  troddit: ["https://www.troddit.com"],
   scribe: ["https://scribe.rip"],
   libMedium: ["https://md.vern.cc"],
+  small: ["https://small.bloat.cat"],
   quetre: ["https://quetre.iket.me"],
   libremdb: ["https://libremdb.iket.me"],
   simplyTranslate: ["https://simplytranslate.org"],
@@ -811,27 +906,36 @@ const defaultInstances = {
   wolfreeAlpha: ["https://gqq.gitlab.io", "https://uqq.gitlab.io"],
   laboratory: ["https://lab.vern.cc"],
   binternet: ["https://bn.bloat.cat"],
+  painterest: ["https://pt.bloat.cat"],
   pixivFe: ["https://pixivfe.exozy.me"],
   liteXiv: ["https://litexiv.exozy.me"],
+  vixipy: ["https://vx.maid.zone"],
   indestructables: ["https://indestructables.private.coffee"],
   destructables: ["https://ds.vern.cc"],
+  structables: ["https://structables.private.coffee"],
   safetwitch: ["https://safetwitch.drgns.space"],
   twineo: ["https://twineo.exozy.me"],
   proxigram: ["https://ig.opnxng.com"],
-  tuboYoutube: ["https://tubo.migalmoreno.com"],
-  tuboSoundcloud: ["https://tubo.migalmoreno.com"],
+  tuboYoutube: ["https://tubo.media"],
+  tuboSoundcloud: ["https://tubo.media"],
   tekstoLibre: ["https://davilarek.github.io/TekstoLibre"],
   skyview: ["https://skyview.social"],
   priviblur: ["https://pb.bloat.cat"],
   nitter: ["https://nitter.privacydev.net"],
   pasted: ["https://pasted.drakeerv.com"],
+  pasty: ["https://pasty.lus.pm"],
   freetar: ["https://freetar.de"],
+  ultimateTab: ["https://ultimate-tab.com"],
   ratAintTieba: ["https://rat.fis.land"],
   shoelace: ["https://shoelace.mint.lgbt"],
   skunkyArt: ["https://skunky.bloat.cat"],
-  ytify: ["https://ytify.netlify.app"],
+  ytify: ["https://ytify.us.kg"],
   nerdsForNerds: ["https://nn.vern.cc"],
+  ducksForDucks: ["https://ducksforducks.private.coffee"],
   koub: ["https://koub.clovius.club"],
+  soundcloak: ["https://soundcloak.fly.dev"],
+  gocook: ["https://cook.adminforge.de"],
+  wikimore: ["https://wikimore.private.coffee"],
 }
 
 async function getDefaults() {
@@ -942,6 +1046,7 @@ async function copyRaw(url) {
  * @param {URL} url
  */
 function isException(url) {
+  if (!options) return false
   if (!options.exceptions) return false
   let exceptions = options.exceptions
   if (exceptions && url) {
@@ -968,12 +1073,11 @@ function isException(url) {
 export default {
   redirect,
   redirectAsync,
-  computeService,
+  computeServiceFrontend,
   reverse,
   initDefaults,
   processUpdate,
   copyRaw,
   switchInstance,
   isException,
-  computeFrontend,
 }
diff --git a/src/assets/javascripts/utils.js b/src/assets/javascripts/utils.js
index e5b8ba46..f360a15b 100644
--- a/src/assets/javascripts/utils.js
+++ b/src/assets/javascripts/utils.js
@@ -31,7 +31,8 @@ function protocolHost(url) {
   if (url.pathname == "/TekstoLibre/" && url.host.endsWith("github.io"))
     return `${url.protocol}//${url.host}${url.pathname.slice(0, -1)}`
 
-  return `${url.protocol}//${url.host}${url.pathname}`
+  const pathname = url.pathname != "/" ? url.pathname : ""
+  return `${url.protocol}//${url.host}${pathname}`
 }
 
 /**
@@ -221,6 +222,22 @@ export function randomInstances(clearnet, n) {
   }
   return instances
 }
+
+async function autoPickInstance(clearnet, url) {
+  if (url) {
+    const i = clearnet.findIndex(instance => url.href.startsWith(instance))
+    if (i >= 0) clearnet.splice(i, 1)
+  }
+  const random = randomInstances(clearnet, 5)
+  const pings = await Promise.all([
+    ...random.map(async instance => {
+      return [instance, await ping(instance)]
+    }),
+  ])
+  pings.sort((a, b) => a[1] - b[1])
+  return pings[0][0]
+}
+
 export function style(options, window) {
   const vars = cssVariables(options, window)
   return `--text: ${vars.text};
@@ -276,4 +293,5 @@ export default {
   convertMapCentre,
   randomInstances,
   style,
+  autoPickInstance,
 }