pynex/nex.py

190 lines
5.5 KiB
Python
Raw Normal View History

2023-06-19 08:00:03 +00:00
import sys
import socket
import pytermgui as ptg
from typing import Tuple
import re
import logging
2023-06-19 08:00:03 +00:00
PORT = 1900
logger = logging.getLogger('nex')
logger.setLevel(logging.INFO)
logger.addHandler(logging.FileHandler('nex.log'))
2023-06-19 08:00:03 +00:00
2023-07-03 08:52:14 +00:00
def parse_url(url: str) -> Tuple[str, str]:
clean_url = url.replace('nex://', '')
split_url = clean_url.split('/', 1)
if len(split_url) == 1:
return clean_url, ''
return split_url[0], split_url[1]
2023-06-19 08:00:03 +00:00
class Page:
def __init__(self, url):
self.url = url
self.host, self.selector = parse_url(url)
self.raw_content = ''
self.content = self.raw_content
self.links = ['' for i in range(10)]
2023-06-19 08:00:03 +00:00
def request(self):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
try:
sock.connect((self.host, PORT))
except ConnectionError as e:
logger.error(e)
2023-07-03 08:52:14 +00:00
self.content = f'[error]Could not connect to {self.host}'
return
2023-06-19 08:00:03 +00:00
selector = self.selector + '\r\n'
sock.sendall(selector.encode('ascii'))
buf = bytearray()
while True:
data = sock.recv(4096)
if len(data) == 0:
break
buf.extend(data)
try:
self.raw_content = buf.decode('ascii')
except UnicodeDecodeError as e:
logger.error(e)
2023-07-03 08:52:14 +00:00
self.content = '[warning]Content could not be decoded'
return
self.content = self.raw_content
self._parse_links()
for i, l in enumerate(self.links):
2023-07-03 08:52:14 +00:00
self.content = re.sub(l, f'[bold primary]\[{i}][/] {l}',
self.content, 1)
@property
def title(self):
return f'nex://{self.url}'
2023-06-19 08:00:03 +00:00
def _parse_links(self):
pattern = re.compile(r'=>\s*([\w/:\.~-]*)\s*')
self.links = pattern.findall(self.raw_content)
2023-06-19 08:00:03 +00:00
2023-07-03 08:52:14 +00:00
landing_page = Page('')
landing_page.raw_content = 'Welcome to PyNex'
landing_page.content = landing_page.raw_content
2023-06-19 08:00:03 +00:00
2023-07-03 08:52:14 +00:00
class Session:
def __init__(self):
self.pages = {'': landing_page}
self.history = ['']
self.curr_index = 0
@property
def current_page(self):
return self.pages[self.history[self.curr_index]]
def prev(self):
if self.curr_index > 0:
self.curr_index -= 1
return self.current_page
def next(self):
if self.curr_index < len(self.history) - 1:
self.curr_index += 1
return self.current_page
def new_page(self, url):
self.curr_index += 1
if self.curr_index >= len(self.history):
self.history.append(url)
else:
self.history[self.curr_index] = url
page = Page(url)
page.request()
self.pages[url] = page
return self.current_page
2023-07-03 08:52:14 +00:00
class Browser(ptg.WindowManager):
def __init__(self, session=Session(), *args):
super().__init__()
2023-07-03 08:52:14 +00:00
self.layout = ptg.Layout()
self.session = session
2023-07-03 08:52:14 +00:00
self.page_container = ptg.Container(self.session.current_page.content)
self.body = ptg.Window(self.page_container,
overflow=ptg.Overflow.SCROLL,
vertical_align=ptg.VerticalAlignment.TOP)
self.footer = ptg.Window(
ptg.Splitter(
ptg.KeyboardButton("Back", self.back, bound='b'),
ptg.KeyboardButton("Forward", self.forward, bound='f'),
ptg.KeyboardButton("Reload", self.reload, bound='r'),
ptg.KeyboardButton("Quit", self.stop, bound='q')
),
box="EMPTY",
is_persistant=True
)
self._create_layout()
self._create_key_bindings()
self.update()
2023-07-03 08:52:14 +00:00
def _create_key_bindings(self):
self.bind('j', lambda *_: self.body.scroll(5))
self.bind(ptg.keys.DOWN, lambda *_: self.body.scroll(5))
self.bind('k', lambda *_: self.body.scroll(-5))
self.bind(ptg.keys.UP, lambda *_: self.body.scroll(-5))
self.bind('q', lambda *_: self.stop())
2023-07-03 08:52:14 +00:00
for i in range(10):
self.bind(f'{i}', lambda widget, key: self.follow_link(key))
2023-07-03 08:52:14 +00:00
def _create_layout(self):
self.layout.add_slot("Body")
self.layout.add_break()
self.layout.add_slot("Footer", height=1)
# self.add(self.header)
self.add(self.body)
self.add(self.footer)
2023-07-03 08:52:14 +00:00
def update(self):
self.page_container.set_widgets([self.session.current_page.content])
self.body.set_title(f'[bold tertiary]{self.session.current_page.title}', 1)
def back(self, *args):
self.session.prev()
self.update()
2023-07-03 08:52:14 +00:00
def forward(self, *args):
self.session.next()
self.update()
2023-07-03 08:52:14 +00:00
def reload(self, *args):
self.session.current_page.request()
self.update()
2023-07-03 08:52:14 +00:00
def go(self, url):
self.session.new_page(url)
self.update()
2023-07-03 08:52:14 +00:00
def follow_link(self, key):
index = int(key)
url = self.session.current_page.links[index]
if url.startswith('nex://'):
self.session.new_page(url)
else:
url = '/'.join([self.session.current_page.url.rstrip('/'), url])
self.session.new_page(url)
self.update()
2023-07-03 08:52:14 +00:00
def main(url=None):
with Browser() as browser:
if url is not None:
browser.go(url)
2023-06-19 08:00:03 +00:00
if __name__ == '__main__':
if len(sys.argv) > 1:
url = sys.argv[1]
2023-07-03 08:52:14 +00:00
main(url)