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/
|
# https://simonwillison.net/2023/Sep/30/cli-tools-python/
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import html
|
||||||
|
import http.server
|
||||||
|
import io
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
@ -144,12 +147,7 @@ def show(pattern, count):
|
||||||
db = database.Database.local()
|
db = database.Database.local()
|
||||||
feeds = db.load_all(feed_limit=count, pattern=pattern or "")
|
feeds = db.load_all(feed_limit=count, pattern=pattern or "")
|
||||||
|
|
||||||
def feed_sort_key(f: feed.Feed) -> int:
|
feeds.sort(key=feed.sort_key, reverse=True)
|
||||||
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)
|
|
||||||
for f in feeds:
|
for f in feeds:
|
||||||
click.echo(f"{f.title}")
|
click.echo(f"{f.title}")
|
||||||
if len(f.entries) > 0:
|
if len(f.entries) > 0:
|
||||||
|
|
@ -193,3 +191,44 @@ def unsubscribe(url):
|
||||||
if count == 0:
|
if count == 0:
|
||||||
click.echo(f"Not subscribed to feed {url}")
|
click.echo(f"Not subscribed to feed {url}")
|
||||||
return 1
|
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))
|
title = clean_text(str(title))
|
||||||
return Entry(id=id, inserted_at=insert_time, title=title, link=link)
|
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)
|
@dataclasses.dataclass(frozen=True)
|
||||||
class Feed:
|
class Feed:
|
||||||
|
|
@ -402,3 +423,10 @@ def merge_feeds(a: Feed, b: Feed) -> Feed:
|
||||||
link=source_feed.link,
|
link=source_feed.link,
|
||||||
entries=entries,
|
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