dotfiles/qutebrowser/config/redirects.py
Marty Oehme 77ead6a618
qutebrowser: Refactor redirects plugin
Make more extensive using of dataclasses for typing and simpler future
refactors.
2025-02-18 17:07:21 +01:00

168 lines
5.6 KiB
Python

import random
import re
from dataclasses import dataclass, field
from typing import Callable
from urllib import parse
from qutebrowser.api import interceptor
from qutebrowser.extensions.interceptors import QUrl, RedirectException
from qutebrowser.utils import message
def fixScribePath(url: QUrl):
"""Fix external medium blog to scribe translation.
Some paths from medium will go through a 'global identity'
path which messes up the actual url path we want to go
to and puts it in queries. This puts it back on the path.
"""
# double unquoting necessary!
# I suppose we double-wrap it earlier somewhere?
# unquoted = parse.unquote(
# url.path(options=QUrl.ComponentFormattingOption.FullyEncoded)
# )
path = parse.unquote(f"{url.path()}{url.query()}", encoding="ascii")
url.setQuery(None)
new_path = re.sub(r"m/global-identity-2redirectUrl=", "", path)
url.setPath(
parse.quote(new_path),
mode=QUrl.ParsingMode.StrictMode,
)
return url
# TODO: Once implemented, delete
# type Service = dict[str, list[str]]
type Redirects = dict[str, Service]
@dataclass
class Service:
source: list[str] = field(default_factory=lambda: [])
target: list[str] = field(default_factory=lambda: [])
custom_targets: bool = False
postprocess: Callable[[], str] | None = None
# T = TypeVar("T")
# @dataclass
# class Redirects:
# services: list[Service]
# selector: Callable[[list[str]], str] = lambda c: c[random.randint(0, len(c) - 1)] #TODO: should implement the pick random sel algo from below
# farside_service: str = "farside.link" # "fastsi.de" # TODO: should contain the url for the farside redirector used
redirects: Redirects = {
"youtube": Service(source=["youtube.com"], target=["invidious"]),
"stackoverflow": Service(
source=["stackoverflow.com"], target=["anonymousoverflow"]
),
"lbry": Service(source=["odysee.com"], target=["librarian"]),
"reddit": Service(source=["reddit.com"], target=["redlib"]),
"instagram": Service(source=["instagram.com"], target=["proxigram"]),
"twitter": Service(source=["twitter.com"], target=["nitter"]),
"imdb": Service(source=["imdb.com"], target=["libremdb"]),
"translate": Service(source=["translate.google.com"], target=["lingva"]),
"tiktok": Service(source=["tiktok.com"], target=["proxitok"]),
"imgur": Service(source=["imgur.com"], target=["rimgo"]),
"medium": Service(
source=["medium.com"], target=["scribe"], postprocess=fixScribePath
),
"fandom": Service(source=["fandom.com"], target=["breezewiki"]),
"quora": Service(source=["quora.com"], target=["quetre"]),
"google": Service(source=["google.com"], target=["whoogle"]),
"twitch": Service(
source=["twitch.com"],
custom_targets=True,
target=[
"https://safetwitch.drgns.space/",
"https://safetwitch.projectsegfau.lt/",
"https://stream.whateveritworks.org",
"https://safetwitch.datura.network",
"https://ttv.vern.cc",
"https://safetwitch.frontendfriendly.xyz/",
"https://ttv.femboy.band",
"https://twitch.seitan-ayoub.lol",
"https://www.ggtyler.dev/other/frontends",
"https://lunar.icu",
"https://twitch.sudovanilla.org",
"https://safetwitch.r4fo.com",
"https://safetwitch.ducks.party",
"https://nogafam.fr",
"https://safetwitch.privacyredirect.com/",
"https://st.ngn.tf/",
"https://safetwitch.darkness.services",
"https://4o1x5.dev/privacy-policy/",
"https://safetwitch.adminforge.de",
],
),
"goodreads": Service(
source=["goodreads.com"],
custom_targets=True,
target=[
"biblioreads.eu.org",
"biblioreads.vercel.app",
"biblioreads.mooo.com",
"bl.vern.cc",
"biblioreads.lunar.icu",
"read.whateveritworks.org",
"biblioreads.privacyfucking.rocks",
"read.seitan-ayoub.lol",
"read.freedit.eu",
"biblioreads.ducks.party",
"biblioreads.snine.nl",
"biblioreads.privacyredirect.com",
"reads.nezumi.party",
"br.bloat.cat",
"read.canine.tools",
],
),
}
def rewrite(request: interceptor.Request) -> None:
# if config.get(name="content.oss_redirects") is False:
# return
if (
request.resource_type != interceptor.ResourceType.main_frame
or request.request_url.scheme() in {"data", "blob"}
):
return
url = request.request_url
if service := _should_be_redirected(url.host()):
url = _farside_redirect(url, _pick_random(service.target))
try:
request.redirect(url)
except RedirectException as e:
message.error(str(e))
if service.postprocess:
url = service.postprocess()
def _farside_redirect(url: QUrl, service: str, use_fastside: bool = False) -> QUrl:
try:
url.setHost("fastside.link" if use_fastside else "farside.link")
url.setPath(f"/{service}{url.path()}")
except RedirectException as e:
message.error(str(e))
return url
def _pick_random[T](choices: list[T]) -> T:
return choices[random.randint(0, len(choices) - 1)]
def _should_be_redirected(
host: str,
redirects: Redirects = redirects,
) -> Service | None:
for service in redirects.values():
for source in service.source:
if re.search(source, host):
return service
return None
interceptor.register(rewrite)