The most basic HTML UI
This commit is contained in:
parent
02232c9c3e
commit
fb9bfe0084
2 changed files with 73 additions and 6 deletions
51
cry/cli.py
51
cry/cli.py
|
|
@ -1,5 +1,8 @@
|
|||
# https://simonwillison.net/2023/Sep/30/cli-tools-python/
|
||||
import asyncio
|
||||
import html
|
||||
import http.server
|
||||
import io
|
||||
import logging
|
||||
|
||||
import click
|
||||
|
|
@ -144,12 +147,7 @@ def show(pattern, count):
|
|||
db = database.Database.local()
|
||||
feeds = db.load_all(feed_limit=count, pattern=pattern or "")
|
||||
|
||||
def feed_sort_key(f: feed.Feed) -> int:
|
||||
if len(f.entries) > 0:
|
||||
return max(e.inserted_at for e in f.entries)
|
||||
return -1
|
||||
|
||||
feeds.sort(key=feed_sort_key, reverse=True)
|
||||
feeds.sort(key=feed.sort_key, reverse=True)
|
||||
for f in feeds:
|
||||
click.echo(f"{f.title}")
|
||||
if len(f.entries) > 0:
|
||||
|
|
@ -193,3 +191,44 @@ def unsubscribe(url):
|
|||
if count == 0:
|
||||
click.echo(f"Not subscribed to feed {url}")
|
||||
return 1
|
||||
|
||||
|
||||
@cli.command("serve")
|
||||
def serve():
|
||||
class Handler(http.server.BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
db = database.Database.local()
|
||||
feeds = db.load_all(feed_limit=10)
|
||||
feeds.sort(key=feed.sort_key, reverse=True)
|
||||
|
||||
buffer = io.StringIO()
|
||||
buffer.write("<head>")
|
||||
buffer.write('<meta charset="utf-8"><title>Subscribed Feeds</title>')
|
||||
buffer.write("</head>")
|
||||
buffer.write("<h1>Feeds</h1>")
|
||||
for f in feeds:
|
||||
feed_title = html.escape(f.title)
|
||||
buffer.write(f'<h2><a href="{f.link}">{feed_title}</a></h2>')
|
||||
buffer.write(f"<div>")
|
||||
if len(f.entries) > 0:
|
||||
for entry in f.entries:
|
||||
title = html.escape(entry.title)
|
||||
buffer.write(
|
||||
f'<span>• <a href="{entry.link}">{title}</a> ({entry.time_ago()})</span> '
|
||||
)
|
||||
else:
|
||||
buffer.write("<i>No entries...</i>")
|
||||
buffer.write(f"</div>")
|
||||
buffer.flush()
|
||||
text = buffer.getvalue()
|
||||
response = text.encode("utf-8")
|
||||
|
||||
self.send_response(200)
|
||||
self.send_header("content-type", "text/html")
|
||||
self.send_header("content-length", str(len(response)))
|
||||
self.end_headers()
|
||||
self.wfile.write(response)
|
||||
|
||||
with http.server.HTTPServer(("", 8000), Handler) as server:
|
||||
click.echo("Serving at http://127.0.0.1:8000/")
|
||||
server.serve_forever()
|
||||
|
|
|
|||
28
cry/feed.py
28
cry/feed.py
|
|
@ -294,6 +294,27 @@ class Entry:
|
|||
title = clean_text(str(title))
|
||||
return Entry(id=id, inserted_at=insert_time, title=title, link=link)
|
||||
|
||||
def time_ago(self) -> str:
|
||||
inserted = self.inserted_at / 1000
|
||||
seconds = int(time.time()) - inserted
|
||||
if seconds <= 90:
|
||||
return f"{seconds}s"
|
||||
minutes = int(seconds / 60)
|
||||
if minutes <= 90:
|
||||
return f"{minutes}m"
|
||||
hours = int(minutes / 60)
|
||||
if hours < 24:
|
||||
return f"{hours}h"
|
||||
days = int(hours / 24)
|
||||
if days <= 7:
|
||||
return f"{days}d"
|
||||
weeks = int(days / 7)
|
||||
if weeks < 52:
|
||||
return f"{weeks}w"
|
||||
|
||||
years = int(weeks / 52)
|
||||
return f"{years}y"
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class Feed:
|
||||
|
|
@ -402,3 +423,10 @@ def merge_feeds(a: Feed, b: Feed) -> Feed:
|
|||
link=source_feed.link,
|
||||
entries=entries,
|
||||
)
|
||||
|
||||
|
||||
def sort_key(f: Feed) -> int:
|
||||
"""A sort key for sorting feeds by recency."""
|
||||
if len(f.entries) > 0:
|
||||
return max(e.inserted_at for e in f.entries)
|
||||
return -1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue