forked from solderpunk/AV-98
opnk is now basically working
This commit is contained in:
parent
654f03c53c
commit
323c088683
|
@ -2,7 +2,6 @@
|
|||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import tempfile
|
||||
import subprocess
|
||||
import textwrap
|
||||
import time
|
||||
|
@ -38,60 +37,6 @@ except ModuleNotFoundError:
|
|||
_DO_FEED = False
|
||||
|
||||
|
||||
less_version = 0
|
||||
if not shutil.which("less"):
|
||||
print("Please install the pager \"less\" to run Offpunk.")
|
||||
print("If you wish to use another pager, send me an email !")
|
||||
print("(I’m really curious to hear about people not having \"less\" on their system.)")
|
||||
sys.exit()
|
||||
output = run("less --version")
|
||||
# We get less Version (which is the only integer on the first line)
|
||||
words = output.split("\n")[0].split()
|
||||
less_version = 0
|
||||
for w in words:
|
||||
if w.isdigit():
|
||||
less_version = int(w)
|
||||
# restoring position only works for version of less > 572
|
||||
if less_version >= 572:
|
||||
_LESS_RESTORE_POSITION = True
|
||||
else:
|
||||
_LESS_RESTORE_POSITION = False
|
||||
#_DEFAULT_LESS = "less -EXFRfM -PMurl\ lines\ \%lt-\%lb/\%L\ \%Pb\%$ %s"
|
||||
# -E : quit when reaching end of file (to behave like "cat")
|
||||
# -F : quit if content fits the screen (behave like "cat")
|
||||
# -X : does not clear the screen
|
||||
# -R : interpret ANSI colors correctly
|
||||
# -f : suppress warning for some contents
|
||||
# -M : long prompt (to have info about where you are in the file)
|
||||
# -W : hilite the new first line after a page skip (space)
|
||||
# -i : ignore case in search
|
||||
# -S : do not wrap long lines. Wrapping is done by offpunk, longlines
|
||||
# are there on purpose (surch in asciiart)
|
||||
#--incsearch : incremental search starting rev581
|
||||
if less_version >= 581:
|
||||
less_base = "less --incsearch --save-marks -~ -XRfMWiS"
|
||||
elif less_version >= 572:
|
||||
less_base = "less --save-marks -XRfMWiS"
|
||||
else:
|
||||
less_base = "less -XRfMWiS"
|
||||
_DEFAULT_LESS = less_base + " \"+''\" %s"
|
||||
_DEFAULT_CAT = less_base + " -EF %s"
|
||||
def less_cmd(file, histfile=None,cat=False,grep=None):
|
||||
if histfile:
|
||||
env = {"LESSHISTFILE": histfile}
|
||||
else:
|
||||
env = {}
|
||||
if cat:
|
||||
cmd_str = _DEFAULT_CAT
|
||||
elif grep:
|
||||
grep_cmd = _GREP
|
||||
#case insensitive for lowercase search
|
||||
if grep.islower():
|
||||
grep_cmd += " -i"
|
||||
cmd_str = _DEFAULT_CAT + "|" + grep_cmd + " %s"%grep
|
||||
else:
|
||||
cmd_str = _DEFAULT_LESS
|
||||
run(cmd_str, parameter=file, direct_output=True, env=env)
|
||||
|
||||
try:
|
||||
from PIL import Image
|
||||
|
@ -211,7 +156,6 @@ class AbstractRenderer():
|
|||
self.title = None
|
||||
self.validity = True
|
||||
self.temp_files = {}
|
||||
self.less_histfile = {}
|
||||
self.center = center
|
||||
self.last_mode = "readable"
|
||||
|
||||
|
@ -592,31 +536,9 @@ class AbstractRenderer():
|
|||
def display(self,mode=None,window_title="",window_info=None,grep=None):
|
||||
if mode: self.last_mode = mode
|
||||
else: mode = self.last_mode
|
||||
wtitle = self._window_title(window_title,info=window_info)
|
||||
body = wtitle + "\n" + self.get_body(mode=mode)
|
||||
if not body:
|
||||
return False
|
||||
# We actually put the body in a tmpfile before giving it to less
|
||||
if mode not in self.temp_files:
|
||||
tmpf = tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False)
|
||||
self.temp_files[mode] = tmpf.name
|
||||
tmpf.write(body)
|
||||
tmpf.close()
|
||||
if mode not in self.less_histfile:
|
||||
firsttime = True
|
||||
tmpf = tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False)
|
||||
self.less_histfile[mode] = tmpf.name
|
||||
else:
|
||||
firsttime = False
|
||||
less_cmd(self.temp_files[mode], histfile=self.less_histfile[mode],cat=firsttime,grep=grep)
|
||||
#TODO to remove
|
||||
return True
|
||||
|
||||
def get_temp_file(self,mode=None):
|
||||
if not mode: mode = self.last_mode
|
||||
if mode in self.temp_files:
|
||||
return self.temp_files[mode]
|
||||
else:
|
||||
return None
|
||||
|
||||
# An instance of AbstractRenderer should have a self.render(body,width,mode) method.
|
||||
# 3 modes are used : readable (by default), full and links_only (the fastest, when
|
||||
|
|
85
offpunk.py
85
offpunk.py
|
@ -38,8 +38,8 @@ import uuid
|
|||
import webbrowser
|
||||
import base64
|
||||
import subprocess
|
||||
import ansirenderer
|
||||
import netcache
|
||||
import opnk
|
||||
from offutils import run,term_width
|
||||
try:
|
||||
import setproctitle
|
||||
|
@ -241,10 +241,7 @@ class GeminiClient(cmd.Cmd):
|
|||
# type sensitivie information.
|
||||
os.umask(0o077)
|
||||
|
||||
# This dictionary contains an url -> ansirenderer mapping. This allows
|
||||
# to reuse a renderer when visiting several times the same URL during
|
||||
# the same session
|
||||
self.rendererdic = {}
|
||||
self.opencache = opnk.opencache()
|
||||
self.no_cert_prompt = "\001\x1b[38;5;76m\002" + "ON" + "\001\x1b[38;5;255m\002" + "> " + "\001\x1b[0m\002"
|
||||
self.cert_prompt = "\001\x1b[38;5;202m\002" + "ON" + "\001\x1b[38;5;255m\002"
|
||||
self.offline_prompt = "\001\x1b[38;5;76m\002" + "OFF" + "\001\x1b[38;5;255m\002" + "> " + "\001\x1b[0m\001"
|
||||
|
@ -357,27 +354,8 @@ class GeminiClient(cmd.Cmd):
|
|||
|
||||
def get_renderer(self,url=None):
|
||||
# If launched without argument, we return the renderer for the current URL
|
||||
mode = None
|
||||
if not url: url = self.current_url
|
||||
findmode = url.split("##offpunk_mode=")
|
||||
if len(findmode) > 1:
|
||||
url = findmode[0]
|
||||
if findmode[1] in ["full"] or findmode[1].isnumeric():
|
||||
mode = findmode[1]
|
||||
# reuse existing renderer if any
|
||||
if url in self.rendererdic.keys():
|
||||
renderer = self.rendererdic[url]
|
||||
else:
|
||||
cache_path = netcache.get_cache_path(url)
|
||||
if cache_path:
|
||||
renderer = ansirenderer.renderer_from_file(cache_path,url)
|
||||
self.rendererdic[url] = renderer
|
||||
else:
|
||||
print("WARNING: no cache for requested renderer for %s" %url)
|
||||
return None
|
||||
if mode:
|
||||
renderer.set_mode(mode)
|
||||
return renderer
|
||||
return self.opencache.get_renderer(url)
|
||||
|
||||
def _go_to_url(self, url, update_hist=True, check_cache=True, handle=True,\
|
||||
name=None, mode=None,limit_size=False):
|
||||
|
@ -465,6 +443,7 @@ class GeminiClient(cmd.Cmd):
|
|||
handle=False,limit_size=True)
|
||||
is_rendered = False
|
||||
if display and netcache.is_cache_valid(url):
|
||||
#TODO : move title in ansirenderer
|
||||
title = renderer.get_url_title()
|
||||
nbr = len(renderer.get_links(mode=mode))
|
||||
if renderer.is_local():
|
||||
|
@ -474,8 +453,9 @@ class GeminiClient(cmd.Cmd):
|
|||
str_last = "last accessed on %s"\
|
||||
%time.ctime(netcache.cache_last_modified(url))
|
||||
title += " (%s links)"%nbr
|
||||
is_rendered = renderer.display(mode=mode,\
|
||||
window_title=title,window_info=str_last)
|
||||
#is_rendered = renderer.display(mode=mode,\
|
||||
# window_title=title,window_info=str_last)
|
||||
is_rendered = self.opencache.opnk(url,mode=mode)
|
||||
if display and is_rendered:
|
||||
self.page_index = 0
|
||||
# Update state (external files are not added to history)
|
||||
|
@ -484,38 +464,6 @@ class GeminiClient(cmd.Cmd):
|
|||
self.current_url = url
|
||||
if update_hist and not self.sync_only:
|
||||
self._update_history(url)
|
||||
elif display and not is_rendered :
|
||||
cmd_str = self._get_handler_cmd(ansirenderer.get_mime(url))
|
||||
try:
|
||||
run(cmd_str, netcache.get_cache_path(url), direct_output=True)
|
||||
except FileNotFoundError:
|
||||
print("Handler program %s not found!" % shlex.split(cmd_str)[0])
|
||||
print("You can use the ! command to specify another handler program or pipeline.")
|
||||
|
||||
|
||||
|
||||
|
||||
def _get_handler_cmd(self, mimetype):
|
||||
# Now look for a handler for this mimetype
|
||||
# Consider exact matches before wildcard matches
|
||||
exact_matches = []
|
||||
wildcard_matches = []
|
||||
for handled_mime, cmd_str in _MIME_HANDLERS.items():
|
||||
if "*" in handled_mime:
|
||||
wildcard_matches.append((handled_mime, cmd_str))
|
||||
else:
|
||||
exact_matches.append((handled_mime, cmd_str))
|
||||
for handled_mime, cmd_str in exact_matches + wildcard_matches:
|
||||
if fnmatch.fnmatch(mimetype, handled_mime):
|
||||
break
|
||||
else:
|
||||
# Use "xdg-open" as a last resort.
|
||||
if _HAS_XDGOPEN:
|
||||
cmd_str = "xdg-open %s"
|
||||
else:
|
||||
cmd_str = "echo \"Can’t find how to open \"%s"
|
||||
print("Please install xdg-open (usually from xdg-util package)")
|
||||
return cmd_str
|
||||
|
||||
@needs_gi
|
||||
def _show_lookup(self, offset=0, end=None, show_url=False):
|
||||
|
@ -716,19 +664,19 @@ class GeminiClient(cmd.Cmd):
|
|||
"""View or set handler commands for different MIME types."""
|
||||
if not line.strip():
|
||||
# Show all current handlers
|
||||
for mime in sorted(_MIME_HANDLERS.keys()):
|
||||
print("%s %s" % (mime, _MIME_HANDLERS[mime]))
|
||||
h = self.opencache.get_handlers()
|
||||
for mime in sorted(h.keys()):
|
||||
print("%s %s" % (mime, h[mime]))
|
||||
elif len(line.split()) == 1:
|
||||
mime = line.strip()
|
||||
if mime in _MIME_HANDLERS:
|
||||
print("%s %s" % (mime, _MIME_HANDLERS[mime]))
|
||||
h = self.opencache.get_handlers(mime=mime)
|
||||
if h:
|
||||
print("%s %s" % (mime, h))
|
||||
else:
|
||||
print("No handler set for MIME type %s" % mime)
|
||||
else:
|
||||
mime, handler = line.split(" ", 1)
|
||||
_MIME_HANDLERS[mime] = handler
|
||||
if "%s" not in handler:
|
||||
print("Are you sure you don't want to pass the filename to the handler?")
|
||||
self.opencache.set_handler(mime,handler)
|
||||
|
||||
def do_abbrevs(self, *args):
|
||||
"""Print all Offpunk command abbreviations."""
|
||||
|
@ -986,8 +934,6 @@ Marks are temporary until shutdown (not saved to disk)."""
|
|||
@needs_gi
|
||||
def do_info(self,line):
|
||||
"""Display information about current page."""
|
||||
print("current url: %s" %self.current_url)
|
||||
print("current renderer: %s" %self.rendererdic)
|
||||
renderer = self.get_renderer()
|
||||
url = self.current_url
|
||||
out = renderer.get_page_title() + "\n\n"
|
||||
|
@ -1176,8 +1122,7 @@ see "handler" command to set your handler."""
|
|||
if args[0] == "url":
|
||||
run("xdg-open %s", parameter=self.current_url, direct_output=True)
|
||||
else:
|
||||
cmd_str = self._get_handler_cmd(ansirenderer.get_mime(self.current_url))
|
||||
run(cmd_str, parameter=netcache.get_cache_path(self.current_url), direct_output=True)
|
||||
self.opencache.opnk(self.current_url,terminal=False)
|
||||
|
||||
@needs_gi
|
||||
def do_shell(self, line):
|
||||
|
|
189
opnk.py
189
opnk.py
|
@ -3,28 +3,199 @@
|
|||
#It will open any file or URL and display it nicely in less.
|
||||
#If not possible, it will fallback to xdg-open
|
||||
#URL are retrieved through netcache
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import argparse
|
||||
import netcache
|
||||
import ansirenderer
|
||||
import offutils
|
||||
import shutil
|
||||
from offutils import run,term_width
|
||||
|
||||
less_version = 0
|
||||
if not shutil.which("less"):
|
||||
print("Please install the pager \"less\" to run Offpunk.")
|
||||
print("If you wish to use another pager, send me an email !")
|
||||
print("(I’m really curious to hear about people not having \"less\" on their system.)")
|
||||
sys.exit()
|
||||
output = run("less --version")
|
||||
# We get less Version (which is the only integer on the first line)
|
||||
words = output.split("\n")[0].split()
|
||||
less_version = 0
|
||||
for w in words:
|
||||
if w.isdigit():
|
||||
less_version = int(w)
|
||||
# restoring position only works for version of less > 572
|
||||
if less_version >= 572:
|
||||
_LESS_RESTORE_POSITION = True
|
||||
else:
|
||||
_LESS_RESTORE_POSITION = False
|
||||
#_DEFAULT_LESS = "less -EXFRfM -PMurl\ lines\ \%lt-\%lb/\%L\ \%Pb\%$ %s"
|
||||
# -E : quit when reaching end of file (to behave like "cat")
|
||||
# -F : quit if content fits the screen (behave like "cat")
|
||||
# -X : does not clear the screen
|
||||
# -R : interpret ANSI colors correctly
|
||||
# -f : suppress warning for some contents
|
||||
# -M : long prompt (to have info about where you are in the file)
|
||||
# -W : hilite the new first line after a page skip (space)
|
||||
# -i : ignore case in search
|
||||
# -S : do not wrap long lines. Wrapping is done by offpunk, longlines
|
||||
# are there on purpose (surch in asciiart)
|
||||
#--incsearch : incremental search starting rev581
|
||||
if less_version >= 581:
|
||||
less_base = "less --incsearch --save-marks -~ -XRfMWiS"
|
||||
elif less_version >= 572:
|
||||
less_base = "less --save-marks -XRfMWiS"
|
||||
else:
|
||||
less_base = "less -XRfMWiS"
|
||||
_DEFAULT_LESS = less_base + " \"+''\" %s"
|
||||
_DEFAULT_CAT = less_base + " -EF %s"
|
||||
|
||||
def less_cmd(file, histfile=None,cat=False,grep=None):
|
||||
if histfile:
|
||||
env = {"LESSHISTFILE": histfile}
|
||||
else:
|
||||
env = {}
|
||||
if cat:
|
||||
cmd_str = _DEFAULT_CAT
|
||||
elif grep:
|
||||
grep_cmd = _GREP
|
||||
#case insensitive for lowercase search
|
||||
if grep.islower():
|
||||
grep_cmd += " -i"
|
||||
cmd_str = _DEFAULT_CAT + "|" + grep_cmd + " %s"%grep
|
||||
else:
|
||||
cmd_str = _DEFAULT_LESS
|
||||
run(cmd_str, parameter=file, direct_output=True, env=env)
|
||||
|
||||
class opencache():
|
||||
def __init__(self):
|
||||
self.temp_files = {}
|
||||
# This dictionary contains an url -> ansirenderer mapping. This allows
|
||||
# to reuse a renderer when visiting several times the same URL during
|
||||
# the same session
|
||||
self.rendererdic = {}
|
||||
self.less_histfile = {}
|
||||
self.mime_handlers = {}
|
||||
|
||||
def opnk(inpath,terminal=True):
|
||||
def _get_handler_cmd(self, mimetype):
|
||||
# Now look for a handler for this mimetype
|
||||
# Consider exact matches before wildcard matches
|
||||
exact_matches = []
|
||||
wildcard_matches = []
|
||||
for handled_mime, cmd_str in self.mime_handlers.items():
|
||||
if "*" in handled_mime:
|
||||
wildcard_matches.append((handled_mime, cmd_str))
|
||||
else:
|
||||
exact_matches.append((handled_mime, cmd_str))
|
||||
for handled_mime, cmd_str in exact_matches + wildcard_matches:
|
||||
if fnmatch.fnmatch(mimetype, handled_mime):
|
||||
break
|
||||
else:
|
||||
# Use "xdg-open" as a last resort.
|
||||
if _HAS_XDGOPEN:
|
||||
cmd_str = "xdg-open %s"
|
||||
else:
|
||||
cmd_str = "echo \"Can’t find how to open \"%s"
|
||||
print("Please install xdg-open (usually from xdg-util package)")
|
||||
return cmd_str
|
||||
|
||||
# Return the handler for a specific mimetype.
|
||||
# Return the whole dic if no specific mime provided
|
||||
def get_handlers(self,mime=None):
|
||||
if mime and mime in self.mime_handlers.keys():
|
||||
return self.mime_handlers[mime]
|
||||
elif mime:
|
||||
return None
|
||||
else:
|
||||
return self.mime_handlers
|
||||
|
||||
def set_handler(self,mime,handler):
|
||||
previous = None
|
||||
if mime in self.mime_handlers.keys():
|
||||
previous = self.mime_handlers[mime]
|
||||
self.mime_handlers[mime] = handler
|
||||
if "%s" not in handler:
|
||||
print("WARNING: this handler has no %%s, no filename will be provided to the command")
|
||||
if previous:
|
||||
print("Previous handler was %s"%previous)
|
||||
|
||||
def get_renderer(self,inpath,mode=None):
|
||||
renderer = None
|
||||
# We remove the ##offpunk_mode= from the URL
|
||||
# If mode is already set, we don’t use the part from the URL
|
||||
print("inpath : %s" %inpath)
|
||||
findmode = inpath.split("##offpunk_mode=")
|
||||
if len(findmode) > 1:
|
||||
inpath = findmode[0]
|
||||
if not mode:
|
||||
if findmode[1] in ["full"] or findmode[1].isnumeric():
|
||||
mode = findmode[1]
|
||||
path = netcache.get_cache_path(inpath)
|
||||
#TODO: check if inpath is URL?
|
||||
if path:
|
||||
if inpath not in self.rendererdic.keys():
|
||||
renderer = ansirenderer.renderer_from_file(path,inpath)
|
||||
if renderer:
|
||||
self.rendererdic[inpath] = renderer
|
||||
else:
|
||||
renderer = self.rendererdic[inpath]
|
||||
if renderer and mode:
|
||||
renderer.set_mode(mode)
|
||||
return renderer
|
||||
|
||||
def grep(self,inpath,searchterm):
|
||||
print("TODO: implement grep")
|
||||
#TODO
|
||||
|
||||
def opnk(self,inpath,mode=None,terminal=True):
|
||||
#Return True if inpath opened in Terminal
|
||||
# False otherwise
|
||||
#if terminal = False, we don’t try to open in the terminal,
|
||||
#we immediately fallback to xdg-open.
|
||||
#netcache currently provide the path if it’s a file.
|
||||
#may this should be migrated here.
|
||||
path = netcache.get_cache_path(inpath)
|
||||
|
||||
#TODO: migrate here ansirenderer display
|
||||
1. À partir du path, tenter le ansirenderer
|
||||
2. Sauver le rendu dans self.temp_files[mode] (donc le mode doit être passé à opnk)
|
||||
3. Sauver le renderer dans self.rendererdic
|
||||
3. Donner à less
|
||||
4. sinon, donner à xdg-open
|
||||
print("inpath opnk: %s" %inpath)
|
||||
renderer = self.get_renderer(inpath,mode=mode)
|
||||
# TODO: put the window title into ansirenderer itself
|
||||
#wtitle = self._window_title(window_title,info=window_info)
|
||||
if terminal and renderer:
|
||||
wtitle = "Test title\n"
|
||||
body = wtitle + "\n" + renderer.get_body(mode=mode)
|
||||
# We actually put the body in a tmpfile before giving it to less
|
||||
if mode not in self.temp_files:
|
||||
tmpf = tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False)
|
||||
self.temp_files[mode] = tmpf.name
|
||||
tmpf.write(body)
|
||||
tmpf.close()
|
||||
if mode not in self.less_histfile:
|
||||
firsttime = True
|
||||
tmpf = tempfile.NamedTemporaryFile("w", encoding="UTF-8", delete=False)
|
||||
self.less_histfile[mode] = tmpf.name
|
||||
else:
|
||||
firsttime = False
|
||||
grep=None
|
||||
less_cmd(self.temp_files[mode], histfile=self.less_histfile[mode],cat=firsttime,grep=grep)
|
||||
return True
|
||||
#maybe, we have no renderer. Or we want to skip it.
|
||||
else:
|
||||
print("We can’t renderer in Ansi, fallback to xdg-open")
|
||||
cmd_str = self._get_handler_cmd(ansirenderer.get_mime(inpath))
|
||||
try:
|
||||
run(cmd_str, netcache.get_cache_path(inpath), direct_output=True)
|
||||
except FileNotFoundError:
|
||||
print("Handler program %s not found!" % shlex.split(cmd_str)[0])
|
||||
print("You can use the ! command to specify another handler program or pipeline.")
|
||||
return False
|
||||
|
||||
|
||||
def get_temp_file(self,mode=None):
|
||||
if not mode: mode = self.last_mode
|
||||
if mode in self.temp_files:
|
||||
return self.temp_files[mode]
|
||||
else:
|
||||
return None
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
|
Loading…
Reference in New Issue