aboutsummaryrefslogtreecommitdiffstats
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,
}