diff options
-rw-r--r-- | browser.py | 125 | ||||
-rw-r--r-- | gemini.py | 112 |
2 files changed, 119 insertions, 118 deletions
@@ -1,118 +1,8 @@ import sys -import socket -import ssl from PySide2 import QtCore, QtWidgets -def htmlescape(text): - return text.replace('<', '<').replace('>', '>') - -def gem2html(gem): - html = [] - state = 'text' - blanklines = 0 - for line in gem.split('\n'): - if line.startswith('```'): - if state == 'pre': - newstate = 'text' - blanklines = 0 - else: - newstate = 'pre' - elif state == 'pre': - newstate = 'pre' - elif line.startswith('=>'): - newstate = 'links' - elif line.startswith('* '): - newstate = 'list' - else: - newstate = 'text' - - if state != 'pre': - if len(line.strip()) == 0: - blanklines += 1 - if blanklines > 1: - html.append('<br/>') - continue - blanklines = 0 - - if state != newstate: - if state in ('links', 'list'): - html.append('</ul>') - elif state == 'pre': - html.append('</pre>') - - if newstate in ('links', 'list'): - html.append('<ul>') - elif newstate == 'pre': - html.append('<pre>') - state = newstate - - if line.startswith('```'): - pass - elif state == 'links': - tokens = line.split(None, 2) - if len(tokens) == 3: - _, url, text = tokens - html.append('<li><a href="{url}">{text}</a></li>'.format(url=url, text=text)) - else: - _, url = tokens - html.append('<li><a href="{url}">{url}</a></li>'.format(url=url)) - elif state == 'list': - html.append('<li>{}</li>'.format(line[2:])) - elif state == 'pre': - html.append(line) - else: - if line.startswith('###'): - html.append('<h3>{}</h3>'.format(line[3:].lstrip())) - elif line.startswith('##'): - html.append('<h2>{}</h2>'.format(line[2:].lstrip())) - elif line.startswith('#'): - html.append('<h1>{}</h1>'.format(line[1:].lstrip())) - else: - html.append('<p>{}</p>'.format(htmlescape(line))) - return '\n'.join(html) - -def absolute_url(base, url): - """ - modifies `url` in place - """ - if not url.scheme(): - url.setScheme(base.scheme()) - if not url.host(): - url.setHost(base.host()) - if not url.path().startswith('/'): - url.setPath(base.path().rsplit('/', 1)[0] + '/' + url.path()) - if url.port() == -1: - url.setPort(1965) - return url - -def gem_get(url): - if len(url.path()) == 0: - url.setPath('/') - return { - 'status': '32', - 'meta': url.toDisplayString(), - } - context = ssl.create_default_context() - context.check_hostname = False - context.verify_mode = ssl.CERT_NONE - with socket.create_connection((url.host(), url.port())) as sock: - with context.wrap_socket(sock, server_hostname=url.host()) as ssock: - ssock.sendall('gemini://{}{}\r\n'.format(url.host(), url.path()).encode('utf8')) - fp = ssock.makefile(mode='rb') - header = fp.readline(1027) - status, meta = header.decode('utf8').split(None, 1) - if status[0] != '2': - return { - 'status': status, - 'meta': meta.strip(), - } - body = fp.read() - return { - 'status': status, - 'meta': meta.strip(), - 'body': body.decode('utf8'), -} +import gemini class GViewport(QtWidgets.QTextBrowser): @@ -128,9 +18,8 @@ class GViewport(QtWidgets.QTextBrowser): def mouseMoveEvent(self, event): cur = self.cursorForPosition(event.localPos().toPoint()) hover_url = cur.charFormat().anchorHref() - hover_url = absolute_url(self._current_url, QtCore.QUrl(hover_url)) + hover_url = gemini.absolute_url(self._current_url, QtCore.QUrl(hover_url)) if hover_url != self._hover_url: - print(hover_url) self._hover_url = hover_url self.hoverUrlChanged.emit(self._hover_url.toString()) return super().mouseMoveEvent(event) @@ -147,9 +36,9 @@ class GViewport(QtWidgets.QTextBrowser): url.setPort(1965) if not url.path().startswith('/'): url.setPath(self._current_url.path().rsplit('/', 1)[0] + '/' + url.path()) - gem = gem_get(absolute_url(self._current_url, url)) + gem = gemini.get(gemini.absolute_url(self._current_url, url)) if 'body' in gem: - html = gem2html(gem['body']) + html = gemini.gem2html(gem['body']) else: html = '<h1>{} {}</h1>'.format(gem['status'], gem['meta']) self._current_url = url @@ -158,13 +47,13 @@ class GViewport(QtWidgets.QTextBrowser): def setSource(self, url): if url.scheme() != 'gemini': return - gem = gem_get(absolute_url(self._current_url, url)) + gem = gemini.get(gemini.absolute_url(self._current_url, url)) while gem['status'][0] == '3': url = QtCore.QUrl(gem['meta']) if url.port() == 1965: url.setPort(-1) print('redirect: {}'.format(url)) - gem = gem_get(absolute_url(self._current_url, url)) + gem = gemini.get(gemini.absolute_url(self._current_url, url)) self._last_redirect = (url, gem) if url.port() == 1965: url.setPort(-1) @@ -185,7 +74,7 @@ class GUrlBar(QtWidgets.QLineEdit): class GBrowser(QtWidgets.QMainWindow): def __init__(self): - super(GBrowser, self).__init__() + QtWidgets.QMainWindow.__init__(self) self.initUI() def initUI(self): diff --git a/gemini.py b/gemini.py new file mode 100644 index 0000000..c0d09c1 --- /dev/null +++ b/gemini.py @@ -0,0 +1,112 @@ +import socket +import ssl + +def htmlescape(text): + return text.replace('<', '<').replace('>', '>') + +def gem2html(gem): + html = [] + state = 'text' + blanklines = 0 + for line in gem.split('\n'): + if line.startswith('```'): + if state == 'pre': + newstate = 'text' + blanklines = 0 + else: + newstate = 'pre' + elif state == 'pre': + newstate = 'pre' + elif line.startswith('=>'): + newstate = 'links' + elif line.startswith('* '): + newstate = 'list' + else: + newstate = 'text' + + if state != 'pre': + if len(line.strip()) == 0: + blanklines += 1 + if blanklines > 1: + html.append('<br/>') + continue + blanklines = 0 + + if state != newstate: + if state in ('links', 'list'): + html.append('</ul>') + elif state == 'pre': + html.append('</pre>') + + if newstate in ('links', 'list'): + html.append('<ul>') + elif newstate == 'pre': + html.append('<pre>') + state = newstate + + if line.startswith('```'): + pass + elif state == 'links': + tokens = line.split(None, 2) + if len(tokens) == 3: + _, url, text = tokens + html.append('<li><a href="{url}">{text}</a></li>'.format(url=url, text=text)) + else: + _, url = tokens + html.append('<li><a href="{url}">{url}</a></li>'.format(url=url)) + elif state == 'list': + html.append('<li>{}</li>'.format(line[2:])) + elif state == 'pre': + html.append(line) + else: + if line.startswith('###'): + html.append('<h3>{}</h3>'.format(line[3:].lstrip())) + elif line.startswith('##'): + html.append('<h2>{}</h2>'.format(line[2:].lstrip())) + elif line.startswith('#'): + html.append('<h1>{}</h1>'.format(line[1:].lstrip())) + else: + html.append('<p>{}</p>'.format(htmlescape(line))) + return '\n'.join(html) + +def absolute_url(base, url): + """ + modifies `url` in place + """ + if not url.scheme(): + url.setScheme(base.scheme()) + if not url.host(): + url.setHost(base.host()) + if not url.path().startswith('/'): + url.setPath(base.path().rsplit('/', 1)[0] + '/' + url.path()) + if url.port() == -1: + url.setPort(1965) + return url + +def get(url): + if len(url.path()) == 0: + url.setPath('/') + return { + 'status': '32', + 'meta': url.toDisplayString(), + } + context = ssl.create_default_context() + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + with socket.create_connection((url.host(), url.port())) as sock: + with context.wrap_socket(sock, server_hostname=url.host()) as ssock: + ssock.sendall('gemini://{}{}\r\n'.format(url.host(), url.path()).encode('utf8')) + fp = ssock.makefile(mode='rb') + header = fp.readline(1027) + status, meta = header.decode('utf8').split(None, 1) + if status[0] != '2': + return { + 'status': status, + 'meta': meta.strip(), + } + body = fp.read() + return { + 'status': status, + 'meta': meta.strip(), + 'body': body.decode('utf8'), + } |