qutebrowser: Refactor gemini userscript
Now possibly a little less clunky (though still using soon-to-be deprecated modules), and able to correctly surf gemini/non-gemini pages as a userscript or as a stand-alone script (simply calling it from command line). Works as before for tabbed pages (the clunky rename/symlink way). Can now also be used as the default way to surf to links so I switched out my qutebrowser f/F link hinting for this script. Will simply open links if staying on http but open gemini version as local file if moving on to gemini. Could probably be rewritten as an actual plugin to interject itself in link opening to be a little more elegant (similar to the redirect code I have running to move to open source web frontends).
This commit is contained in:
parent
f09a75820e
commit
f7304b8941
3 changed files with 78 additions and 33 deletions
|
@ -1,4 +1,4 @@
|
||||||
c.aliases["gem"] = "hint links userscript qute-gemini"
|
c.aliases["gem"] = "spawn --userscript qute-gemini "
|
||||||
|
|
||||||
# Use q for quitting a tab (mimicks vim buffer) - qa is used for exiting
|
# Use q for quitting a tab (mimicks vim buffer) - qa is used for exiting
|
||||||
c.aliases["q"] = "tab-close"
|
c.aliases["q"] = "tab-close"
|
||||||
|
|
|
@ -6,6 +6,10 @@ lleader = ","
|
||||||
|
|
||||||
## CHANGED DEFAULTS
|
## CHANGED DEFAULTS
|
||||||
|
|
||||||
|
# the full power of qutebrowser at your fingertips for any gemini page!
|
||||||
|
config.bind("f", "hint links userscript qute-gemini")
|
||||||
|
config.bind("F", "hint links userscript qute-gemini-tab")
|
||||||
|
|
||||||
# rebind moving tabs to free for download
|
# rebind moving tabs to free for download
|
||||||
config.bind("gG", "tab-give")
|
config.bind("gG", "tab-give")
|
||||||
# switch binds for scroll-marks and quick-/book-marks
|
# switch binds for scroll-marks and quick-/book-marks
|
||||||
|
|
|
@ -6,6 +6,20 @@
|
||||||
# SPDX-FileCopyrightText: 2020 petedussin
|
# SPDX-FileCopyrightText: 2020 petedussin
|
||||||
# SPDX-FileCopyrightText: 2020-2021 Sotiris Papatheodorou
|
# SPDX-FileCopyrightText: 2020-2021 Sotiris Papatheodorou
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
# 2022-2023 Marty Oehme (added stand-alone script capability)
|
||||||
|
|
||||||
|
# Use it as a qutebrowser userscript to open gemini pages:
|
||||||
|
# Put this file in qutebrowser userscript folder and call command
|
||||||
|
# `:spawn --userscript qute-gemini "gemini://my-gemini-url.org"`
|
||||||
|
# or
|
||||||
|
# `:hint links userscript qute-gemini` to open from selected link
|
||||||
|
# Rename file to `qute-gemini-tab` (or create symlink) to open
|
||||||
|
# any gemini url as a new tab.
|
||||||
|
|
||||||
|
# Since the script also opens normal URLs you can even replace your
|
||||||
|
# normal link hint mapping with it (usually f or F for tabbed) and
|
||||||
|
# continue surfing like normal, only that you can now also access
|
||||||
|
# any gemini pages as if they were part of the normal http protocol.
|
||||||
|
|
||||||
import cgi
|
import cgi
|
||||||
import html
|
import html
|
||||||
|
@ -41,7 +55,8 @@ CSS
|
||||||
_status_code_desc = {
|
_status_code_desc = {
|
||||||
"1": "Gemini status code 1 Input. This is not implemented in qute-gemini.",
|
"1": "Gemini status code 1 Input. This is not implemented in qute-gemini.",
|
||||||
"10": "Gemini status code 10 Input. This is not implemented in qute-gemini.",
|
"10": "Gemini status code 10 Input. This is not implemented in qute-gemini.",
|
||||||
"11": "Gemini status code 11 Sensitive Input. This is not implemented in qute-gemini.",
|
"11": """Gemini status code 11 Sensitive Input. This is not implemented
|
||||||
|
in qute-gemini.""",
|
||||||
"3": "Gemini status code 3 Redirect. Stopped after "
|
"3": "Gemini status code 3 Redirect. Stopped after "
|
||||||
+ str(_max_redirects)
|
+ str(_max_redirects)
|
||||||
+ " redirects.",
|
+ " redirects.",
|
||||||
|
@ -53,28 +68,41 @@ _status_code_desc = {
|
||||||
+ " redirects.",
|
+ " redirects.",
|
||||||
"4": "Gemini status code 4 Temporary Failure. Server message: META",
|
"4": "Gemini status code 4 Temporary Failure. Server message: META",
|
||||||
"40": "Gemini status code 40 Temporary Failure. Server message: META",
|
"40": "Gemini status code 40 Temporary Failure. Server message: META",
|
||||||
"41": "Gemini status code 41 Server Unavailable. The server is unavailable due to overload or maintenance. Server message: META",
|
"41": """Gemini status code 41 Server Unavailable.
|
||||||
"42": "Gemini status code 42 CGI Error. A CGI process, or similar system for generating dynamic content, died unexpectedly or timed out. Server message: META",
|
The server is unavailable due to overload or maintenance. Server message: META""",
|
||||||
"43": "Gemini status code 43 Proxy Error. A proxy request failed because the server was unable to successfully complete a transaction with the remote host. Server message: META",
|
"42": """Gemini status code 42 CGI Error.
|
||||||
"44": "Gemini status code 44 Slow Down. Rate limiting is in effect. Please wait META seconds before making another request to this server.",
|
A CGI process, or similar system for generating dynamic content,
|
||||||
|
died unexpectedly or timed out. Server message: META""",
|
||||||
|
"43": """Gemini status code 43 Proxy Error.
|
||||||
|
A proxy request failed because the server was unable to successfully
|
||||||
|
complete a transaction with the remote host. Server message: META""",
|
||||||
|
"44": """Gemini status code 44 Slow Down. Rate limiting is in effect.
|
||||||
|
Please wait META seconds before making another request to this server.""",
|
||||||
"5": "Gemini status code 5 Permanent Failure. Server message: META",
|
"5": "Gemini status code 5 Permanent Failure. Server message: META",
|
||||||
"50": "Gemini status code 50 Permanent Failure. Server message: META",
|
"50": "Gemini status code 50 Permanent Failure. Server message: META",
|
||||||
"51": "Gemini status code 51 Not Found. he requested resource could not be found but may be available in the future. Server message: META",
|
"51": """Gemini status code 51 Not Found. The requested resource could
|
||||||
"52": "Gemini status code 52 Gone. The resource requested is no longer available and will not be available again. Server message: META",
|
not be found but may be available in the future. Server message: META""",
|
||||||
"53": "Gemini status code 53 Proxy Request Refused. The request was for a resource at a domain not served by the server and the server does not accept proxy requests. Server message: META",
|
"52": """Gemini status code 52 Gone. The resource requested is no longer
|
||||||
"59": "Gemini status code 59 Bad Request. The server was unable to parse the client's request, presumably due to a malformed request. Server message: META",
|
available and will not be available again. Server message: META""",
|
||||||
"6": "Gemini status code 6 Client Certificate Required. This is not implemented in qute-gemini.",
|
"53": """Gemini status code 53 Proxy Request Refused. The request was for
|
||||||
|
a resource at a domain not served by the server and the server does
|
||||||
|
not accept proxy requests. Server message: META""",
|
||||||
|
"59": """Gemini status code 59 Bad Request. The server was unable to
|
||||||
|
parse the client's request, presumably due to a malformed request.
|
||||||
|
Server message: META""",
|
||||||
|
"6": """Gemini status code 6 Client Certificate Required.
|
||||||
|
This is not implemented in qute-gemini.""",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def qute_url() -> str:
|
def qute_url() -> str:
|
||||||
"""Get the URL passed to the script by qutebrowser."""
|
"""Get the URL passed to the script by qutebrowser."""
|
||||||
return os.environ["QUTE_URL"]
|
return os.environ.get("QUTE_URL", "")
|
||||||
|
|
||||||
|
|
||||||
def qute_fifo() -> str:
|
def qute_fifo() -> str:
|
||||||
"""Get the FIFO or file to write qutebrowser commands to."""
|
"""Get the FIFO or file to write qutebrowser commands to."""
|
||||||
return os.environ["QUTE_FIFO"]
|
return os.environ.get("QUTE_FIFO", "")
|
||||||
|
|
||||||
|
|
||||||
def html_href(url: str, description: str) -> str:
|
def html_href(url: str, description: str) -> str:
|
||||||
|
@ -118,7 +146,7 @@ def gemini_fetch_url(url: str) -> Tuple[str, str, str, str, str]:
|
||||||
url = "gemini://" + url
|
url = "gemini://" + url
|
||||||
parsed_url = urllib.parse.urlparse(url)
|
parsed_url = urllib.parse.urlparse(url)
|
||||||
if parsed_url.scheme != "gemini":
|
if parsed_url.scheme != "gemini":
|
||||||
return "", "Received non-gemini:// URL: " + url
|
return "", "Received non-gemini:// URL: " + url, "59", "", "Non-gemini URL"
|
||||||
if parsed_url.port is not None:
|
if parsed_url.port is not None:
|
||||||
useport = parsed_url.port
|
useport = parsed_url.port
|
||||||
else:
|
else:
|
||||||
|
@ -214,15 +242,15 @@ def gemtext_to_html(
|
||||||
pass
|
pass
|
||||||
# Link
|
# Link
|
||||||
elif line.startswith("=>"):
|
elif line.startswith("=>"):
|
||||||
l = line[2:].split(None, 1)
|
ln = line[2:].split(None, 1)
|
||||||
# Use the URL itself as the description if there is none
|
# Use the URL itself as the description if there is none
|
||||||
if len(l) == 1:
|
if len(ln) == 1:
|
||||||
l.append(l[0])
|
ln.append(ln[0])
|
||||||
# Encode the link description
|
# Encode the link description
|
||||||
l[1] = html.escape(l[1])
|
ln[1] = html.escape(ln[1])
|
||||||
# Resolve relative URLs
|
# Resolve relative URLs
|
||||||
l[0] = gemini_absolutise_url(url, l[0])
|
ln[0] = gemini_absolutise_url(url, ln[0])
|
||||||
lines.append("\t\t<p>" + html_href(l[0], l[1]) + "</p>")
|
lines.append("\t\t<p>" + html_href(ln[0], ln[1]) + "</p>")
|
||||||
# Preformated toggle
|
# Preformated toggle
|
||||||
elif line.startswith("```"):
|
elif line.startswith("```"):
|
||||||
if in_pre:
|
if in_pre:
|
||||||
|
@ -316,7 +344,7 @@ def qute_error_page(url: str, description: str) -> str:
|
||||||
return "data:text/html;charset=UTF-8," + urllib.parse.quote(html_page)
|
return "data:text/html;charset=UTF-8," + urllib.parse.quote(html_page)
|
||||||
|
|
||||||
|
|
||||||
def open_gemini(url: str, open_args: str) -> None:
|
def open_gemini(url: str) -> str:
|
||||||
"""Open Gemini URL in qutebrowser."""
|
"""Open Gemini URL in qutebrowser."""
|
||||||
# Get the Gemini content
|
# Get the Gemini content
|
||||||
content, content_url, status, meta, error_msg = gemini_fetch_url(url)
|
content, content_url, status, meta, error_msg = gemini_fetch_url(url)
|
||||||
|
@ -328,18 +356,30 @@ def open_gemini(url: str, open_args: str) -> None:
|
||||||
tmpf = tempfile.NamedTemporaryFile("w", suffix=".html", delete=False)
|
tmpf = tempfile.NamedTemporaryFile("w", suffix=".html", delete=False)
|
||||||
tmp_filename = tmpf.name
|
tmp_filename = tmpf.name
|
||||||
tmpf.close()
|
tmpf.close()
|
||||||
|
if not tmp_filename:
|
||||||
|
return ""
|
||||||
with open(tmp_filename, "w") as f:
|
with open(tmp_filename, "w") as f:
|
||||||
f.write(gemtext_to_html(content, content_url, url, status, meta))
|
f.write(gemtext_to_html(content, content_url, url, status, meta))
|
||||||
open_url = " file://" + tmp_filename
|
open_url = " file://" + tmp_filename
|
||||||
# Open the HTML file in qutebrowser
|
# Open the HTML file in qutebrowser
|
||||||
with open(qute_fifo(), "w") as qfifo:
|
return open_url
|
||||||
qfifo.write("open " + open_args + open_url)
|
|
||||||
|
|
||||||
|
|
||||||
def open_other(url: str, open_args: str) -> None:
|
def open_url(url: str, open_args: str) -> None:
|
||||||
"""Open non-Gemini URL in qutebrowser."""
|
parsed_url = urllib.parse.urlparse(url)
|
||||||
with open(qute_fifo(), "w") as qfifo:
|
if parsed_url.scheme == "gemini":
|
||||||
qfifo.write("open " + open_args + " " + url)
|
to_open = open_gemini(url)
|
||||||
|
else:
|
||||||
|
to_open = url
|
||||||
|
if not to_open:
|
||||||
|
return
|
||||||
|
|
||||||
|
fifo = qute_fifo()
|
||||||
|
if fifo and fifo != "":
|
||||||
|
with open(fifo, "w") as qfifo:
|
||||||
|
qfifo.write(f"open {open_args} {to_open}")
|
||||||
|
return
|
||||||
|
os.system(f"xdg-open {to_open}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -348,10 +388,11 @@ if __name__ == "__main__":
|
||||||
open_args = "-t"
|
open_args = "-t"
|
||||||
else:
|
else:
|
||||||
open_args = ""
|
open_args = ""
|
||||||
# Select how to open the URL depending on its scheme
|
|
||||||
url = qute_url()
|
# Take url to open as argument or from qutebrowser url
|
||||||
parsed_url = urllib.parse.urlparse(url)
|
if len(sys.argv) > 1:
|
||||||
if parsed_url.scheme == "gemini":
|
url = sys.argv[1]
|
||||||
open_gemini(url, open_args)
|
|
||||||
else:
|
else:
|
||||||
open_other(url, open_args)
|
url = qute_url()
|
||||||
|
# Select how to open the URL depending on its scheme
|
||||||
|
open_url(url, open_args)
|
||||||
|
|
Loading…
Reference in a new issue