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)