Compare commits

...

5 commits

Author SHA1 Message Date
2ecdb81f62
qutebrowser: Enable javascript for docker hub 2025-02-18 17:07:22 +01:00
53f2c49385
qutebrowser: Refactor redirects as importable class
The redirects 'plugin' is now a simple class which can be imported into
the configuration and is automatically active when instantiated. For
that to work it needs access to the qutebrowser python library but that
should hopefully be a given if wanting to have a qutebrowser plugin.

To make the default redirects active, simply import the class and
instantiate it:

```python
from fossredirect import Redirects
_ = Redirects()
```

This loads the defaults and activates them in qutebrowser. Try to go to
e.g. 'reddit.com' and it will automatically open in a libreddit
frontend.
To customize the redirects, provide a custom list of Services:

```python
from fossredirect import Redirects
_ = Redirects(services=[Service(source=["fromhere.com"], target=["tohere"])])
```

It works a little more flexibly now:
Redirects contains a list of Services.
Each service is a simple data container with the following:

```python
Service(
     source=["list.of", "hosts.to", "redirect.com"],
     target=["farside-redirect"]
)
```

The above redirects any of the source hosts to the far-side provided
target (in this case it would be
`farside.link/farside-redirect/<original-path>`).

However we can also specify 'custom' targets if farside does not have a
service that we want to redirect.

```python
Service(
     source=["list.of", "hosts.to", "redirect.com"],
     target=["my-redirected-host.org"],
     custom_targets=True
)
```

This directly rewrites the host to
`https://my-redirected-host.org/<original-path>`.

Lastly, we can have custom preprocess/postprocess functions which fix
some more involved redirect:

```python
Service(
     source=["list.of", "hosts.to", "redirect.com"],
     target=["my-redirected-host.org"],
     postprocess=lambda item: item
)
```

Be aware that the functions take 'QUrl' objects so you have to access
e.g. the actual host with `item.host()` before rewriting. Look at the
breezewiki rewrite function for an easy example.
2025-02-18 17:07:21 +01:00
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
5dfba029d8
jj: Add private commit uploading safety
Do not push commits prefixed with `private: ` to any git remote.
2025-02-18 17:07:20 +01:00
6a6dfe3a6e
jj: Add showing working copy log by default
By default, the log invocation now shows at least 5 ancestors of the
current working copy branch. This makes it much easier to get situated
to the current location for me - otherwise, if I just pushed my main
branch and thus there is the main bookmark just below my commit none of
the last couple changes are shown in the log. If there are fewer than 5
ancestors it will just show however many there were.
2025-02-18 17:07:16 +01:00
6 changed files with 211 additions and 213 deletions

View file

@ -3,16 +3,16 @@ from typing import cast
# pylint: disable=C0111
from qutebrowser.config.config import ConfigContainer # noqa: F401
from qutebrowser.config.configfiles import ConfigAPI # noqa: F401
from qutebrowser.config.configfiles import ConfigAPI
config: ConfigAPI = cast(ConfigAPI, config) # noqa: F821 pylint: disable=E0602,C0103
c: ConfigContainer = cast(ConfigContainer, c) # noqa: F821 pylint: disable=E0602,C0103
# Autogenerated config.py
# Documentation:
# qute://help/configuring.html
# qute://help/settings.html
# load additional settings configured via autoconfig.yml
_ = config.load_autoconfig()
@ -59,7 +59,7 @@ config.source("alias.py")
config.source("maps.py")
config.source("content.py")
config.source("searchengines.py")
config.source("redirects.py")
config.source("fossredirect.py")
# Tab-Bar
# have tab bar on the right, not on the top

View file

@ -32,6 +32,7 @@ js_whitelist = [
"*://news.ycombinator.com/*",
"*://old.reddit.com/*",
"*://translate.google.com/*",
"*://hub.docker.com/*",
"chrome://*/*",
"file://*",
"qute://*/*",

View file

@ -0,0 +1,203 @@
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
@dataclass
class Service:
source: list[str] = field(default_factory=lambda: [])
target: list[str] = field(default_factory=lambda: [])
custom_targets: bool = False
preprocess: Callable[[QUrl], QUrl] | None = None
postprocess: Callable[[QUrl], QUrl] | None = None
def __contains__(self, item: str):
for source in self.source:
if re.search(source, item):
return True
return False
def scribe_global_identity(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.
"""
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
def breezewiki_host_to_path(url: QUrl):
host = url.host()
if wiki := host[0 : host.find(".fandom.com")]:
url.setPath(f"/{wiki}{url.path()}")
return url
default_services = [
Service(source=["youtube.com"], target=["invidious"]),
Service(source=["stackoverflow.com"], target=["anonymousoverflow"]),
Service(source=["odysee.com"], target=["librarian"]),
Service(source=["reddit.com"], target=["redlib"]),
Service(source=["instagram.com"], target=["proxigram"]),
Service(source=["twitter.com"], target=["nitter"]),
Service(source=["imdb.com"], target=["libremdb"]),
Service(source=["tiktok.com"], target=["proxitok"]),
Service(source=["imgur.com"], target=["rimgo"]),
Service(
source=["medium.com"], target=["scribe"], postprocess=scribe_global_identity
),
Service(
source=["fandom.com"], target=["breezewiki"], preprocess=breezewiki_host_to_path
),
Service(source=["quora.com"], target=["quetre"]),
Service(source=["google.com"], target=["whoogle"]),
Service(source=["genius.com"], target=["dumb"]),
Service(source=["translate.google.com"], target=["lingva", "simplytranslate"]),
Service(source=["deepl.com"], target=["simplytranslate"]),
Service(source=["bandcamp.com"], target=["tent"]),
Service(
custom_targets=True,
source=["pinterest.com"],
target=[
"pain.thirtysix.pw",
"pt.bloat.cat",
"painterest.gitro.xyz",
],
),
Service(
custom_targets=True,
source=["tumblr.com"],
target=[
"pb.bloat.cat",
"tb.opnxng.com",
"priviblur.pussthecat.org",
"priviblur.thebunny.zone",
"priviblur.gitro.xyz",
"priviblur.canine.tools",
],
),
Service(
custom_targets=True,
source=["twitch.com"],
target=[
"safetwitch.drgns.space",
"safetwitch.projectsegfau.lt",
"stream.whateveritworks.org",
"safetwitch.datura.network",
"ttv.vern.cc",
"safetwitch.frontendfriendly.xyz",
"ttv.femboy.band",
"twitch.seitan-ayoub.lol",
"twitch.sudovanilla.org",
"safetwitch.r4fo.com",
"safetwitch.ducks.party",
"safetwitch.privacyredirect.com",
"st.ngn.tf",
"safetwitch.darkness.services",
"safetwitch.adminforge.de",
],
),
Service(
custom_targets=True,
source=["goodreads.com"],
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",
],
),
]
@dataclass
class Redirects:
services: list[Service] = field(default_factory=lambda: default_services)
selector: Callable[[list[str]], str] = lambda c: c[
random.randint(0, len(c) - 1)
] # selection algorithm
farside_service: str = (
"farside.link" # Contains url for farside-like redirector (e.g. 'fastsi.de')
)
def __post_init__(self):
interceptor.register(self.intercept)
def intercept(self, request: interceptor.Request) -> None:
# TODO: Implement config check (maybe 'privacy.redirect = False?')
# 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 url in self:
try:
request.redirect(self.get(url))
except RedirectException as e:
message.error(str(e))
def get(self, url: QUrl) -> QUrl:
service = self.get_service(url)
if not service:
return url
foss_host = self.selector(service.target)
if service.preprocess:
url = service.preprocess(url)
try:
if service.custom_targets:
url.setHost(foss_host)
else:
url.setHost(self.farside_service)
url.setPath(f"/{foss_host}{url.path()}")
except RedirectException as e:
message.error(str(e))
if service.postprocess:
url = service.postprocess(url)
return url
def get_service(self, url: QUrl) -> Service | None:
host = url.host()
for service in self.services:
if host in service:
return service
def __contains__(self, item: QUrl):
if self.get_service(item):
return True
return False
_ = Redirects()

View file

@ -1,207 +0,0 @@
import random
import re
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
type Service = dict[str, list[str]]
type Redirects = dict[str, Service]
redirects: Redirects = {
"youtube": {
"source": ["youtube.com"],
"farside": ["invidious"],
},
"stackoverflow": {
"source": ["stackoverflow.com", "askubuntu.com"],
"farside": ["anonymousoverflow"],
},
"lbry": {
"source": ["odysee.com"],
"farside": ["librarian"],
},
"reddit": {
"source": ["reddit.com"],
"farside": ["redlib"],
},
"instagram": {
"source": ["instagram.com"],
"farside": ["proxigram"],
},
"twitter": {
"source": ["twitter.com"],
"farside": ["nitter"],
},
"imdb": {
"source": ["imdb.com"],
"farside": ["libremdb"],
},
"translate": {
"source": ["translate.google.com"],
"farside": ["lingva"],
},
"tiktok": {
"source": ["tiktok.com"],
"farside": ["proxitok"],
},
"imgur": {
"source": ["imgur.com"],
"farside": ["rimgo"],
},
"medium": {
"source": ["medium.com"],
"farside": ["scribe"],
# "postprocess": fixScribePath
},
"fandom": {
"source": ["fandom.com"],
"farside": ["breezewiki"],
},
"quora": {
"source": ["quora.com"],
"farside": ["quetre"],
# "postprocess": lambda url: message.info(f"CALLING QUORA WITH {url}")
},
"google": {
"source": ["google.com"],
"target": [
"search.albony.xyz",
"search.garudalinux.org",
"search.dr460nf1r3.org",
"s.tokhmi.xyz",
"search.sethforprivacy.com",
"whoogle.dcs0.hu",
"gowogle.voring.me",
"whoogle.privacydev.net",
"wg.vern.cc",
"whoogle.hxvy0.gq",
"whoogle.hostux.net",
"whoogle.lunar.icu",
"wgl.frail.duckdns.org",
"whoogle.no-logs.com",
"whoogle.ftw.lol",
"whoogle-search--replitcomreside.repl.co",
"search.notrustverify.ch",
"whoogle.datura.network",
"whoogle.yepserver.xyz",
"search.nezumi.party",
],
},
"biblioreads": {
"source": ["goodreads.com"],
"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",
],
},
"safetwitch": {
"source": ["twitch.tv"],
"target": [
"safetwitch.drgns.space",
"safetwitch.projectsegfau.lt",
"stream.whateveritworks.org",
"safetwitch.datura.network",
"ttv.vern.cc",
"safetwitch.frontendfriendly.xyz",
"ttv.femboy.band",
"twitch.seitan-ayoub.lol",
"st.ggtyler.dev",
"safetwitch.lunar.icu",
"twitch.sudovanilla.com",
"safetwitch.r4fo.com",
"safetwitch.ducks.party",
"safetwitch.nogafam.fr",
"safetwitch.privacyredirect.com",
"st.ngn.tf",
],
},
}
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["farside" if "farside" in service else "target"])
)
try:
request.redirect(url)
except RedirectException as e:
message.error(str(e))
if "postprocess" in service and isinstance(service["postprocess"], Callable):
url = service["postprocess"](url)
def _farside_redirect(url: QUrl, service: str, use_fastside: bool = True) -> 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(
# TODO: Update to use typedefs/classes instead of this jumble
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)

View file

@ -9,7 +9,7 @@ key = "73BA40D5AFAF49C9"
[git]
sign-on-push = true
subprocess = true
private-commits = "description(glob:'wip:*')" # refuse to push WIP commits
private-commits = "description(glob:'wip:*') | description(glob:'private:*')" # refuse to push WIP commits
[ui]
diff-editor = ["nvim", "-c", "DiffEditor $left $right $output"]
@ -32,7 +32,7 @@ concat(
'''
[revsets]
log = "@ | ancestors(trunk()..(visible_heads() & mine()), 2) | trunk()"
log = "ancestors(@, 5) | ancestors(trunk()..(visible_heads() & mine()), 2) | trunk()"
[revset-aliases]
"bases" = "dev"

View file

@ -62,5 +62,6 @@ alias jb="jj log -r 'ancestors(heads(all()), 3)'"
alias jrb="jj rebase"
# remote work
alias jrv="jj git remote list"
alias jp="jj git push"