Introducing Offpunk
This commit is contained in:
parent
3d66f99c95
commit
700cba7184
80
README.md
80
README.md
|
@ -1,7 +1,49 @@
|
|||
# AV-98
|
||||
# OFFPUNK
|
||||
|
||||
A command-line, text-based and offline Gemini browser.
|
||||
|
||||
Focused on Gemini first but with some Gopher/web support available or projected, the goal of Offpunk is to be able to synchronise your content once (a day, a week, a month) and then browse it while staying disconnected.
|
||||
|
||||
Offpunk is a fork of the original [AV-98](https://tildegit.org/solderpunk/AV-98) by Solderpunk and was originally only called AV-98-offline as an experimental branch.
|
||||
|
||||
## Lightning introduction
|
||||
|
||||
You use the `go` command to visit a URL, e.g. `go gemini.circumlunar.space`. If xsel is installed, go will automatically fetch the URL from your clipboard.
|
||||
|
||||
Links in Gemini documents are assigned numerical indices. Just type an index to
|
||||
follow that link.
|
||||
|
||||
If a Gemini document is too long to fit on your screen, use the `less` command
|
||||
to pipe it to the `less` pager.
|
||||
|
||||
Use `add` to add a capsule to your bookmarks and `bm` to show your bookmarks (which are stored in a file in you .config).
|
||||
|
||||
Use `offline` to only browse cached content and `online` to go back online.
|
||||
|
||||
Use the `help` command to learn about additional commands.
|
||||
|
||||
When launched with the "--sync" option, offpunk will run non-interactively and fetch content from your bookmarks and content tentatively accessed while offline. New content found in your bookmarks will be added to your tour.
|
||||
|
||||
With "--sync", one could specify a "--cache validity" in seconds. This option will not refresh content if a cache exists and is less than the specified amount of seconds old.
|
||||
|
||||
For example, running
|
||||
|
||||
`offpunk.py --sync --cache-validity 43200`
|
||||
|
||||
will refresh your bookmarks if those are at least 12h old.
|
||||
|
||||
|
||||
|
||||
## TODO
|
||||
|
||||
Known issues in the code:
|
||||
* WONTFIX: Sync is slow if you have bookmarks with lot of links that change very often.
|
||||
* FIXME0: Certificates error are not handled in --sync
|
||||
* FIXME1: consider root file is always index.gmi
|
||||
* FIXME2: offline web browser use os.system because it’s the only one that understands the ">> file.txt"
|
||||
|
||||
* TODO: Update blackbox to reflect cache hits.
|
||||
|
||||
## AV-98-offline
|
||||
This is a fork of the original [AV-98](https://tildegit.org/solderpunk/AV-98) by Solderpunk.
|
||||
|
||||
This fork is an experiment by Ploum (=> gemini://rawtext.club/~ploum ) to add offline capabilities to AV-98, including a persistent "tour" option.
|
||||
|
||||
|
@ -18,27 +60,10 @@ New ressources discovered in your bookmarks while --sync will be added to your t
|
|||
|
||||
At the moment, caching only work for gemini:// ressources. gopher:// is not implemented and http(s):// ressources are sent to an "offline browser" (by default, None, nothing is done). It could be useful to, for example, send the http:// links to a text file in order to visit them while online.
|
||||
|
||||
Known issues in the code:
|
||||
* WONTFIX: Sync is slow if you have bookmarks with lot of links that change very often.
|
||||
* WONTFIX: Certificates error are not handled in --sync
|
||||
* FIXME1: consider root file is always index.gmi
|
||||
* FIXME2: offline web browser use os.system because it’s the only one that understands the ">> file.txt"
|
||||
|
||||
* TODO: Update blackbox to reflect cache hits.
|
||||
|
||||
## Original description
|
||||
|
||||
AV-98 is an experimental client for the
|
||||
[Gemini protocol](https://gemini.circumlunar.space). It is derived from the
|
||||
[gopher client VF-1](https://github.com/solderpunk/VF-1) by the same author.
|
||||
AV-98 is "experimental" in the sense that it may occasionally extend or deviate
|
||||
from the official Gemini specification for the purposes of, well,
|
||||
experimentation. Despite this, it is expected to be stable enough for regular
|
||||
daily use at the same time.
|
||||
|
||||
## Dependencies
|
||||
|
||||
AV-98 has no "strict dependencies", i.e. it will run and work without anything
|
||||
Offpunk has no "strict dependencies", i.e. it will run and work without anything
|
||||
else beyond the Python standard library. However, it will "opportunistically
|
||||
import" a few other libraries if they are available to offer an improved
|
||||
experience.
|
||||
|
@ -48,6 +73,7 @@ experience.
|
|||
* The [cryptography library](https://pypi.org/project/cryptography/) will
|
||||
provide a better and slightly more secure experience when using the default
|
||||
TOFU certificate validation mode and is highly recommended.
|
||||
* [Python magic](https://github.com/ahupp/python-magic/) is useful to determine the MIME type of cached object. If not present, the file extension will be used but some capsules provide wrong extension or no extension at all.
|
||||
|
||||
## Features
|
||||
|
||||
|
@ -61,18 +87,6 @@ experience.
|
|||
* IPv6 support
|
||||
* Supports any character encoding recognised by Python
|
||||
|
||||
## Lightning introduction
|
||||
|
||||
You use the `go` command to visit a URL, e.g. `go gemini.circumlunar.space`.
|
||||
|
||||
Links in Gemini documents are assigned numerical indices. Just type an index to
|
||||
follow that link.
|
||||
|
||||
If a Gemini document is too long to fit on your screen, use the `less` command
|
||||
to pipe it to the `less` pager.
|
||||
|
||||
Use the `help` command to learn about additional commands.
|
||||
|
||||
## RC files
|
||||
|
||||
You can use an RC file to automatically run any sequence of valid AV-98
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
# AV-98 Gemini client
|
||||
# Dervied from VF-1 (https://github.com/solderpunk/VF-1),
|
||||
# Offpunk Offline Gemini client
|
||||
# Derived from AV-98 by Solderpunk,
|
||||
# (C) 2021 Ploum <offpunk@ploum.eu>
|
||||
# (C) 2019, 2020 Solderpunk <solderpunk@sdf.org>
|
||||
# With contributions from:
|
||||
# - danceka <hannu.hartikainen@gmail.com>
|
||||
|
@ -66,7 +67,7 @@ _MAX_REDIRECTS = 5
|
|||
_MAX_CACHE_SIZE = 10
|
||||
_MAX_CACHE_AGE_SECS = 180
|
||||
# TODO : use XDG spec for cache
|
||||
_CACHE_PATH = "~/.cache/av98/"
|
||||
_CACHE_PATH = "~/.cache/offpunk/"
|
||||
|
||||
# Command abbreviations
|
||||
_ABBREVS = {
|
||||
|
@ -309,7 +310,7 @@ class GeminiClient(cmd.Cmd):
|
|||
|
||||
# Find config directory
|
||||
## Look for something pre-existing
|
||||
for confdir in ("~/.av98/", "~/.config/av98/"):
|
||||
for confdir in ("~/.offpunk/", "~/.config/offpunk/"):
|
||||
confdir = os.path.expanduser(confdir)
|
||||
if os.path.exists(confdir):
|
||||
self.config_dir = confdir
|
||||
|
@ -317,15 +318,15 @@ class GeminiClient(cmd.Cmd):
|
|||
## Otherwise, make one in .config if it exists
|
||||
else:
|
||||
if os.path.exists(os.path.expanduser("~/.config/")):
|
||||
self.config_dir = os.path.expanduser("~/.config/av98/")
|
||||
self.config_dir = os.path.expanduser("~/.config/offpunk/")
|
||||
else:
|
||||
self.config_dir = os.path.expanduser("~/.av98/")
|
||||
self.config_dir = os.path.expanduser("~/.offpunk/")
|
||||
print("Creating config directory {}".format(self.config_dir))
|
||||
os.makedirs(self.config_dir)
|
||||
|
||||
self.no_cert_prompt = "\x1b[38;5;76m" + "AV-98" + "\x1b[38;5;255m" + "> " + "\x1b[0m"
|
||||
self.cert_prompt = "\x1b[38;5;202m" + "AV-98" + "\x1b[38;5;255m"
|
||||
self.offline_prompt = "\x1b[38;5;76m" + "OFF-98" + "\x1b[38;5;255m" + "> " + "\x1b[0m"
|
||||
self.no_cert_prompt = "\x1b[38;5;76m" + "ON" + "\x1b[38;5;255m" + "> " + "\x1b[0m"
|
||||
self.cert_prompt = "\x1b[38;5;202m" + "ON" + "\x1b[38;5;255m"
|
||||
self.offline_prompt = "\x1b[38;5;76m" + "OFF" + "\x1b[38;5;255m" + "> " + "\x1b[0m"
|
||||
self.prompt = self.no_cert_prompt
|
||||
self.gi = None
|
||||
self.history = []
|
||||
|
@ -401,7 +402,7 @@ class GeminiClient(cmd.Cmd):
|
|||
first_seen date, last_seen date, count integer)""")
|
||||
|
||||
def _go_to_gi(self, gi, update_hist=True, check_cache=True, handle=True):
|
||||
"""This method might be considered "the heart of AV-98".
|
||||
"""This method might be considered "the heart of Offpunk".
|
||||
Everything involved in fetching a gemini resource happens here:
|
||||
sending the request over the network, parsing the response if
|
||||
its a menu, storing the response in a temporary file, choosing
|
||||
|
@ -427,7 +428,7 @@ class GeminiClient(cmd.Cmd):
|
|||
return
|
||||
elif gi.scheme == "gopher" and not self.options.get("gopher_proxy", None)\
|
||||
and not self.sync_only:
|
||||
print("""AV-98 does not speak Gopher natively.
|
||||
print("""Offpunk does not speak Gopher natively.
|
||||
However, you can use `set gopher_proxy hostname:port` to tell it about a
|
||||
Gopher-to-Gemini proxy (such as a running Agena instance), in which case
|
||||
you'll be able to transparently follow links to Gopherspace!""")
|
||||
|
@ -1384,7 +1385,7 @@ you'll be able to transparently follow links to Gopherspace!""")
|
|||
print("Are you sure you don't want to pass the filename to the handler?")
|
||||
|
||||
def do_abbrevs(self, *args):
|
||||
"""Print all AV-98 command abbreviations."""
|
||||
"""Print all Offpunk command abbreviations."""
|
||||
header = "Command Abbreviations:"
|
||||
self.stdout.write("\n{}\n".format(str(header)))
|
||||
if self.ruler:
|
||||
|
@ -1394,19 +1395,20 @@ you'll be able to transparently follow links to Gopherspace!""")
|
|||
self.stdout.write("\n")
|
||||
|
||||
def do_offline(self, *args):
|
||||
"""Use AV-98 offline by only accessing cached content"""
|
||||
"""Use Offpunk offline by only accessing cached content"""
|
||||
if self.offline_only:
|
||||
print("Offline and undisturbed.")
|
||||
else:
|
||||
self.offline_only = True
|
||||
self.prompt = self.offline_prompt
|
||||
print("AV-98 is now offline and will only access cached content")
|
||||
print("Offpunk is now offline and will only access cached content")
|
||||
|
||||
def do_online(self, *args):
|
||||
"""Use Offpunk online with a direct connection"""
|
||||
if self.offline_only:
|
||||
self.offline_only = False
|
||||
self.prompt = self.no_cert_prompt
|
||||
print("AV-98 is online and will access the network")
|
||||
print("Offpunk is online and will access the network")
|
||||
else:
|
||||
print("Already online. Try offline.")
|
||||
|
||||
|
@ -1593,7 +1595,7 @@ Think of it like marks in vi: 'mark a'='ma' and 'go a'=''a'."""
|
|||
|
||||
def do_version(self, line):
|
||||
"""Display version information."""
|
||||
print("AV-98 " + _VERSION)
|
||||
print("Offpunk " + _VERSION)
|
||||
|
||||
### Stuff that modifies the lookup table
|
||||
def do_ls(self, line):
|
||||
|
@ -1726,7 +1728,7 @@ Use 'ls -l' to see URLs."""
|
|||
print("File %s already exists!" % filename)
|
||||
else:
|
||||
# Don't use _get_active_tmpfile() here, because we want to save the
|
||||
# "source code" of menus, not the rendered view - this way AV-98
|
||||
# "source code" of menus, not the rendered view - this way Offpunk
|
||||
# can navigate to it later.
|
||||
shutil.copyfile(self.tmp_filename, filename)
|
||||
print("Saved to %s" % filename)
|
||||
|
@ -1818,7 +1820,7 @@ current gemini browsing session."""
|
|||
|
||||
### The end!
|
||||
def do_quit(self, *args):
|
||||
"""Exit AV-98."""
|
||||
"""Exit Offpunk."""
|
||||
# Close TOFU DB
|
||||
self.db_conn.commit()
|
||||
self.db_conn.close()
|
||||
|
@ -1835,7 +1837,7 @@ current gemini browsing session."""
|
|||
if os.path.exists(certfile):
|
||||
os.remove(certfile)
|
||||
print()
|
||||
print("Thank you for flying AV-98!")
|
||||
print("You can close your screen!")
|
||||
sys.exit()
|
||||
|
||||
do_exit = do_quit
|
||||
|
@ -1862,14 +1864,14 @@ def main():
|
|||
|
||||
# Handle --version
|
||||
if args.version:
|
||||
print("AV-98 " + _VERSION)
|
||||
print("Offpunk " + _VERSION)
|
||||
sys.exit()
|
||||
|
||||
# Instantiate client
|
||||
gc = GeminiClient(restricted=args.restricted,synconly=args.sync)
|
||||
|
||||
# Process config file
|
||||
rcfile = os.path.join(gc.config_dir, "av98rc")
|
||||
rcfile = os.path.join(gc.config_dir, "offpunkrc")
|
||||
if os.path.exists(rcfile):
|
||||
print("Using config %s" % rcfile)
|
||||
with open(rcfile, "r") as fp:
|
||||
|
@ -1887,7 +1889,7 @@ def main():
|
|||
|
||||
# Say hi
|
||||
if not args.sync:
|
||||
print("Welcome to AV-98!")
|
||||
print("Welcome to Offpunk!")
|
||||
if args.restricted:
|
||||
print("Restricted mode engaged!")
|
||||
print("Enjoy your patrol through Geminispace...")
|
16
setup.py
16
setup.py
|
@ -1,12 +1,12 @@
|
|||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='AV-98',
|
||||
version='1.0.4dev',
|
||||
description="Command line Gemini client.",
|
||||
author="Solderpunk",
|
||||
author_email="solderpunk@sdf.org",
|
||||
url='https://tildegit.org/solderpunk/AV-98/',
|
||||
name='offpunk',
|
||||
version='0.1',
|
||||
description="Offline Command line Gemini client forked from AV-98.",
|
||||
author="Ploum",
|
||||
author_email="offpunk@ploum.eu",
|
||||
url='https://tildegit.org/ploum/AV-98-offline/',
|
||||
classifiers=[
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Programming Language :: Python :: 3 :: Only',
|
||||
|
@ -15,9 +15,9 @@ setup(
|
|||
'Environment :: Console',
|
||||
'Development Status :: 4 - Beta',
|
||||
],
|
||||
py_modules = ["av98"],
|
||||
py_modules = ["offpunk"],
|
||||
entry_points={
|
||||
"console_scripts": ["av98=av98:main"]
|
||||
"console_scripts": ["offpunk=offpunk:main"]
|
||||
},
|
||||
install_requires=[],
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue