You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

512 lines
19 KiB

import urwid
import yaml
import warnings
import subprocess
import os
from concurrent.futures import ThreadPoolExecutor
from API import Fetcher
from Cache import Cache
from Render import Render
import Utils
warnings.filterwarnings("ignore")
class LeftPane(urwid.ListBox):
def __init__(self, categories):
super().__init__(self)
items = [urwid.AttrMap(urwid.Columns([
(16, urwid.Text(category[4])), urwid.Text(category[1]), (5, urwid.Text(str(category[2])))]),
(category[0], category[1], category[2]), "reveal focus") for category in categories]
walker = urwid.SimpleListWalker(items)
self.body = walker
self.categoryPosition = 0
self.isCategoryView = True
self.currentCategory = ""
def processAttrMap(self, attrMap):
res = attrMap[None]
if res == "favorite":
return attrMap["attrs"]
else:
return res
def fill(self, items, is_category_view):
def getAttrs(item):
if is_category_view is True:
return (item[0], item[1], item[2])
else:
return (item[0], item[1])
if is_category_view is True:
items = [
urwid.AttrMap(
urwid.Columns(
[(16, urwid.Text(items[0][4])),
urwid.Text(items[0][1]),
(5, urwid.Text(str(items[0][2])))]),
{None: "favorite", "attrs": getAttrs(items[0])},
"reveal focus"),
*
[urwid.AttrMap(
urwid.Columns([(16, urwid.Text(item[4])),
urwid.Text(item[1]),
(5, urwid.Text(str(item[2])))]),
getAttrs(item),
"reveal focus") for item in items[1:]]]
else:
items = [urwid.AttrMap(urwid.Columns([
(16, urwid.Text(item[4])), urwid.Text(item[1]), (5, urwid.Text(str(item[2])))]),
getAttrs(item), "reveal focus") for item in items]
walker = urwid.SimpleListWalker(items)
self.body = walker
focus_widget, idx = self.get_focus()
if self.isCategoryView:
try:
self.currentCategory = focus_widget.attr_map[None][0]
except BaseException:
self.currentCategory = None
def findById(self, id):
idx = 0
for idx, item in zip(range(len(self.body)), self.body):
if item.attr_map[None][0] == id:
break
return idx
def setArticlesPaneTitle(self, text):
tui.rightBox.set_title(text)
def setCategoryArticles(self, attrMap):
itemId = attrMap[0]
name = attrMap[1]
tui.articles = tui.cache.getArticlesFromCategory(itemId, tui.show_read)
tui.articleView.fill(tui.articles, True)
self.setArticlesPaneTitle(name)
def setFeedArticles(self, attrMap):
itemId = attrMap[0]
tui.articles = tui.cache.getArticlesFromFeed(itemId, tui.show_read)
if tui.articles is not None:
tui.articleView.fill(tui.articles, False)
self.setArticlesPaneTitle(attrMap[1])
def keypress(self, size, key): # noqa
if key in ("j", "down"):
item_size = len(self.body)
try:
focus_widget, idx = self.get_focus()
if idx < item_size - 1:
idx = idx + 1
self.set_focus(idx)
focus_widget, idx = self.get_focus()
if self.isCategoryView:
self.currentCategory = self.processAttrMap(focus_widget.attr_map)[0]
self.setCategoryArticles(self.processAttrMap(focus_widget.attr_map))
else:
self.setFeedArticles(focus_widget.attr_map[None])
except BaseException:
pass
return
elif key in ("k", "up"):
try:
focus_widget, idx = self.get_focus()
if idx > 0:
idx = idx - 1
self.set_focus(idx)
focus_widget, idx = self.get_focus()
if self.isCategoryView:
self.currentCategory = self.processAttrMap(focus_widget.attr_map)[0]
self.setCategoryArticles(self.processAttrMap(focus_widget.attr_map))
else:
self.setFeedArticles(focus_widget.attr_map[None])
except BaseException:
pass
return
elif key in ("l", "right"):
self.stepInto()
return
elif key in ("h", "left"):
self.stepOut()
return
elif key in ("r"):
self.markAsRead()
return
return super().keypress(size, key)
def stepOut(self):
if not self.isCategoryView:
self.isCategoryView = True
self.fill(tui.categories, True)
tui.leftBox.set_title("Categories")
self.set_focus(self.categoryPosition)
focus_widget, idx = self.get_focus()
self.setCategoryArticles(self.processAttrMap(focus_widget.attr_map))
def stepInto(self):
try:
if self.isCategoryView and self.currentCategory != "Favorites":
self.isCategoryView = False
focus_widget, idx = self.get_focus()
self.categoryPosition = idx
categoryId = self.processAttrMap(focus_widget.attr_map)[0]
categoryName = self.processAttrMap(focus_widget.attr_map)[1]
feeds = tui.cache.getFeeds(categoryId, tui.show_read)
self.fill(feeds, False)
focus_widget, idx = self.get_focus()
self.setFeedArticles(focus_widget.attr_map[None])
tui.leftBox.set_title(categoryName)
except BaseException:
pass
def markAsRead(self):
try:
focus_widget, idx = self.get_focus()
del self.body[idx]
if idx > 0:
idx -= 1
self.set_focus(idx)
tui.cache.markStreamAsRead(self.processAttrMap(focus_widget.attr_map)[0])
tui.categories = tui.cache.getCategories(tui.show_read)
focus_widget, idx = self.get_focus()
if focus_widget is None and not self.isCategoryView:
self.stepOut()
return
if self.isCategoryView:
self.currentCategory = self.processAttrMap(focus_widget.attr_map)[0]
self.setCategoryArticles(self.processAttrMap(focus_widget.attr_map))
else:
self.setFeedArticles(focus_widget.attr_map[None])
except BaseException:
pass
class RightPane(urwid.ListBox):
def __init__(self, items):
super().__init__(self)
walker = urwid.SimpleListWalker([])
self.body = walker
self.isList = True
self.articlePosition = 0
self.article = None
self.chunkNumber = 0
def fill(self, articles, isCategoryView):
status = ""
def makeColumns(article):
title = article[1]
if isCategoryView:
title += " (" + article[-1] + ")"
cols = [
(2, urwid.Text(status)),
(16, urwid.Text(article[3])),
urwid.Text(title),
]
return cols
if tui.show_read:
status = "R"
items = [
urwid.AttrMap(
urwid.Columns(makeColumns(article)),
article[0],
"reveal focus") for article in articles]
walker = urwid.SimpleListWalker(items)
self.body = walker
def getArticle(self, articleId):
article = [item for item in tui.fetcher.articles[tui.feedView.currentCategory] if item["id"] == articleId][0]
return article
def setArticleTitle(self):
tui.rightBox.set_title(self.article.title + " (" + str(self.article.currentPageNumber) + "/" +
str(self.article.numberOfPages) + ")")
def keypress(self, size, key): # noqa
if key in ("j", "down"):
if not self.isList:
walker = urwid.SimpleListWalker([urwid.Text(self.article.scrollDown())])
self.body = walker
self.setArticleTitle()
item_size = len(self.body)
try:
focus_widget, idx = self.get_focus()
if idx < item_size - 1:
idx = idx + 1
self.set_focus(idx)
except BaseException:
pass
return
elif key in ("k", "up"):
if not self.isList:
walker = urwid.SimpleListWalker([urwid.Text(self.article.scrollUp())])
self.body = walker
self.setArticleTitle()
try:
focus_widget, idx = self.get_focus()
if idx > 0:
idx = idx - 1
self.set_focus(idx)
except BaseException:
pass
return
elif key in ("f"):
if self.isList is True:
article_widget, article_idx = self.get_focus()
articleId = article_widget.attr_map[None]
tui.cache.toggleArticleStarred(articleId)
tui.categories = tui.cache.getCategories(tui.show_read)
item_widget, item_idx = tui.feedView.get_focus()
if tui.feedView.isCategoryView:
tui.feedView.fill(tui.categories, tui.feedView.isCategoryView)
tui.feedView.set_focus(item_idx)
if item_idx == 0:
tui.feedView.setCategoryArticles(('Favorites', 'Favorites'))
elif key in ("r"):
if self.isList is True:
feeds = []
article_widget, article_idx = self.get_focus()
articleId = article_widget.attr_map[None]
tui.cache.toggleArticleStatus(articleId)
item_widget, item_idx = tui.feedView.get_focus()
itemAttrMap = item_widget.attr_map[None]
if tui.feedView.isCategoryView:
tui.feedView.setCategoryArticles(itemAttrMap)
else:
tui.feedView.setFeedArticles(itemAttrMap)
if article_idx > 0:
article_idx -= 1
tui.categories = tui.cache.getCategories(tui.show_read)
if tui.feedView.isCategoryView:
feeds = tui.categories
else:
feeds = tui.cache.getFeeds(tui.feedView.currentCategory, tui.show_read)
tui.feedView.fill(feeds, tui.feedView.isCategoryView)
new_idx = tui.feedView.findById(itemAttrMap[0])
try:
tui.feedView.set_focus(new_idx)
focus_widget, idx = tui.feedView.get_focus()
if tui.feedView.isCategoryView:
tui.feedView.currentCategory = tui.feedView.processAttrMap(focus_widget.attr_map)[0]
tui.feedView.setCategoryArticles(tui.feedView.processAttrMap(focus_widget.attr_map))
else:
tui.feedView.setFeedArticles(focus_widget.attr_map[None])
try:
self.set_focus(article_idx)
except BaseException:
pass
except BaseException:
pass
return
elif key in ("l", "right"):
if self.isList is True:
self.isList = False
self.chunkNumber = 0
try:
focus_widget, idx = self.get_focus()
self.articlePosition = idx
articleId = focus_widget.attr_map[None]
self.article = Render(*tui.cache.getArticle(articleId, tui.feedView.currentCategory == "Favorites"),
tui.cache.getArticleLinks(articleId))
walker = urwid.SimpleListWalker([urwid.Text(self.article.firstPage)])
self.body = walker
self.setArticleTitle()
except BaseException:
pass
return
elif key in ("c"):
if self.isList is False:
self.article.loadComments()
walker = urwid.SimpleListWalker([urwid.Text(self.article.firstPage)])
self.body = walker
return
elif key in ("h", "left"):
try:
if self.isList is False:
self.isList = True
self.fill(tui.articles, tui.feedView.isCategoryView)
focusFeed, idx = tui.feedView.get_focus()
tui.rightBox.set_title(tui.feedView.processAttrMap(focusFeed.attr_map)[1])
self.set_focus(self.articlePosition)
except BaseException:
pass
return
elif key == "m":
if self.isList is False and len(self.article.links) > 0:
top = Links(self.article.links)
tui.createOverlay(urwid.LineBox(top), len(self.article.links)+2)
return
elif key == "v":
if self.isList is False:
subprocess.Popen(['firefox', '-new-tab', self.article.url])
return
return super().keypress(size, key)
class Links(urwid.ListBox):
def __init__(self, links):
super().__init__(self)
items = [urwid.AttrMap(urwid.Columns([urwid.Text(link)]), (link), "reveal focus") for link in links]
walker = urwid.SimpleFocusListWalker(items)
self.body = walker
def parseLink(self, link):
ext = link.split(".")[-1]
if Utils.checkPic(ext.lower()):
os.system('nohup feh ' + link + ' </dev/null >/dev/null 2>&1 &')
elif Utils.checkStreamingVideo(link):
tui.destroyOverlay()
os.system(
'nohup mpv --ytdl-format="bestvideo[height<=720]+bestaudio/best[height<=720]" "' +
link +
'" </dev/null >/dev/null 2>&1 &')
else:
subprocess.Popen(['firefox', '-new-tab', link])
def keypress(self, size, key):
if key in ("j", "down"):
focus_widget, idx = self.get_focus()
item_size = len(self.body)
if idx < item_size - 1:
idx = idx + 1
self.set_focus(idx)
return
elif key in ("k", "up"):
focus_widget, idx = self.get_focus()
if idx > 0:
idx = idx - 1
self.set_focus(idx)
return
elif key in ("l", "right"):
focus_widget, idx = self.get_focus()
link = focus_widget.attr_map[None]
self.parseLink(link)
elif key in ("q", "esc"):
tui.destroyOverlay()
return
class TUI(urwid.Frame):
@classmethod
def create(cls):
tui = cls()
palette = [("linebox", "bold", "dark cyan", "standout"), ("text", "dark cyan", "dark cyan"),
("favorite", "dark green", "black"),
('header', 'white', 'black'), ('reveal focus', 'black', 'dark cyan', 'standout')]
loop = urwid.MainLoop(
tui,
palette,
event_loop=urwid.AsyncioEventLoop(),
unhandled_input=tui.unhandled_input,
)
tui.loop = loop
return tui
def __init__(self):
with open(os.path.expanduser('~') + '/.config/inomnibus/config.yml', 'r') as file:
config = yaml.safe_load(file)
URL = config["server"]["URL"]
token = config["server"]["token"]
self.show_read = 0
self.overlay = None
self.fetcher = Fetcher(URL, token)
self.cache = Cache(self.fetcher)
self.categories = self.cache.getCategories(self.show_read)
self.leftPaneItems = {}
self.activePane = False
self.executor = ThreadPoolExecutor(max_workers=1)
self.loop = None
self.feedView = LeftPane([])
self.articleView = RightPane([])
self.leftBox = urwid.LineBox(self.feedView, title="Categories")
self.rightBox = urwid.LineBox(self.articleView, title="Articles")
self.views = urwid.Columns(
[(56, self.leftBox), self.rightBox])
self.container = urwid.Pile([self.views])
self.body = self.container
self.articles = []
super().__init__(self.body)
def initialize_panes(self):
try:
self.feedView.fill(self.cache.getCategories(self.show_read), True)
try:
self.feedView.set_focus(1)
except BaseException:
pass
focus_widget, idx = self.feedView.get_focus()
item = self.feedView.processAttrMap(focus_widget.attr_map)[0]
name = self.feedView.processAttrMap(focus_widget.attr_map)[1]
self.articles = self.cache.getArticlesFromCategory(item, self.show_read)
self.articleView.fill(self.articles, True)
self.feedView.setArticlesPaneTitle(name)
except BaseException:
pass
def run(self):
self.initialize_panes()
self.loop.run()
self.executor.shutdown(wait=False)
def createOverlay(self, element, h):
self.overlay = urwid.Overlay(
element,
self.body, align="center", width=("relative", 50), valign="middle", height=h)
self.body = self.overlay
def destroyOverlay(self):
self.body = self.overlay.bottom_w
del(self.overlay)
def unhandled_input(self, key):
if key == "tab":
self.activePane = not self.activePane
self.views.set_focus(int(self.activePane))
if self.activePane:
tui.articleView.isList = True
elif key == "q":
raise urwid.ExitMainLoop()
elif key == "S":
olb = urwid.ListBox(urwid.SimpleListWalker([urwid.Text("")]))
overlay = urwid.Overlay(
urwid.LineBox(olb),
self.body, align="center", width=("relative", 50), valign="middle", height=3)
self.body = overlay
articles = tui.cache.refresh()
for text in articles:
olb.body = urwid.SimpleListWalker([urwid.Text(text)])
tui.loop.entering_idle()
self.body = overlay.bottom_w
self.initialize_panes()
elif key == "R":
self.show_read = int(not self.show_read)
focus_widget, idx = self.feedView.get_focus()
if self.feedView.isCategoryView:
self.feedView.setCategoryArticles(focus_widget.attr_map[None])
else:
self.feedView.setFeedArticles(focus_widget.attr_map[None])
self.categories = tui.cache.getCategories(self.show_read)
feeds = tui.cache.getFeeds(self.feedView.currentCategory, self.show_read)
if self.feedView.isCategoryView:
self.feedView.fill(self.categories, self.show_read)
else:
self.feedView.fill(feeds, self.show_read)
try:
self.feedView.set_focus(idx)
except BaseException:
pass
return
tui = TUI.create()
tui.run()