diff --git a/feedmonkey.png b/feedmonkey.png
deleted file mode 100644
index f8f536d..0000000
Binary files a/feedmonkey.png and /dev/null differ
diff --git a/feedthemonkey b/feedthemonkey
deleted file mode 100755
index 87d08de..0000000
--- a/feedthemonkey
+++ /dev/null
@@ -1,261 +0,0 @@
-#!/usr/bin/env python2
-
-import sys, os, json, tempfile, urllib2, urllib, json
-from PyQt4 import QtGui, QtCore, QtWebKit, QtNetwork
-from threading import Thread
-
-settings = QtCore.QSettings("jabs.nu", "ttrssl")
-
-class MainWindow(QtGui.QMainWindow):
- def __init__(self):
- QtGui.QMainWindow.__init__(self)
- self.addAction(QtGui.QAction("Full Screen", self, checkable=True, toggled=lambda v: self.showFullScreen() if v else self.showNormal(), shortcut="F11"))
- self.history = self.get("history", [])
- self.restoreGeometry(QtCore.QByteArray.fromRawData(settings.value("geometry").toByteArray()))
- self.restoreState(QtCore.QByteArray.fromRawData(settings.value("state").toByteArray()))
-
- session_id = self.get("session_id")
- server_url = self.get("server_url")
- if not (session_id and server_url):
- session_id = sys.argv[2]
- server_url = sys.argv[1]
- self.put("session_id", session_id)
- self.put("session_id", server_url)
-
- self.tinyTinyRSS = TinyTinyRSS(self, server_url, session_id)
-
- self.content = Content(self)
- self.setCentralWidget(self.content)
-
- self.content.evaluateJavaScript("setArticle()")
- self.content.reload()
-
-
- def closeEvent(self, ev):
- settings.setValue("geometry", self.saveGeometry())
- settings.setValue("state", self.saveState())
- return QtGui.QMainWindow.closeEvent(self, ev)
-
- def put(self, key, value):
- "Persist an object somewhere under a given key"
- settings.setValue(key, json.dumps(value))
- settings.sync()
-
- def get(self, key, default=None):
- "Get the object stored under 'key' in persistent storage, or the default value"
- v = settings.value(key)
- return json.loads(unicode(v.toString())) if v.isValid() else default
-
- def setWindowTitle(self, t):
- super(QtGui.QMainWindow, self).setWindowTitle("Feed the Monkey" + t)
-
-
-
-class Content(QtGui.QWidget):
- def __init__(self, container):
- QtGui.QWidget.__init__(self)
-
- self.app = container
- self.index = 0
-
- self.wb = QtWebKit.QWebView(titleChanged=lambda t: container.setWindowTitle(t))
- #self.wb.setPage(WebPage(self.wb))
-
- self.wb.page().setLinkDelegationPolicy(QtWebKit.QWebPage.DelegateAllLinks)
- self.wb.linkClicked.connect(lambda url: self.openLink(url))
-
- self.setLayout(QtGui.QVBoxLayout(spacing=0))
- self.layout().setContentsMargins(0, 0, 0, 0)
- self.layout().addWidget(self.wb)
-
- self.do_close = QtGui.QShortcut("Ctrl+W", self, activated=lambda: container.close())
- self.do_show_next = QtGui.QShortcut("Space", self, activated=lambda: self.showNext())
- self.do_show_previous = QtGui.QShortcut("Backspace", self, activated=lambda: self.showPrevious())
- self.do_show_previous_k = QtGui.QShortcut("k", self, activated=lambda: self.showPrevious())
- self.do_show_next_j = QtGui.QShortcut("j", self, activated=lambda: self.showNext())
- self.do_open_current = QtGui.QShortcut("Return", self, activated=lambda: self.openCurrent())
- self.do_reload = QtGui.QShortcut("r", self, activated=lambda: self.reload())
-
- self.do_quit = QtGui.QShortcut("Ctrl+q", self, activated=lambda: container.close())
- self.zoomIn = QtGui.QShortcut("Ctrl++", self, activated=lambda: self.wb.setZoomFactor(self.wb.zoomFactor() + 0.2))
- self.zoomOut = QtGui.QShortcut("Ctrl+-", self, activated=lambda: self.wb.setZoomFactor(self.wb.zoomFactor() - 0.2))
- self.zoomOne = QtGui.QShortcut("Ctrl+0", self, activated=lambda: self.wb.setZoomFactor(1))
-
- self.wb.settings().setAttribute(QtWebKit.QWebSettings.PluginsEnabled, True)
- self.wb.settings().setIconDatabasePath(tempfile.mkdtemp())
- self.wb.setHtml(self.templateString())
-
- self.unread_articles = []
-
- def openLink(self, url):
- QtGui.QDesktopServices.openUrl(url)
-
- def reload(self):
- self.unread_articles = self.app.tinyTinyRSS.getUnreadFeeds()
- self.index = 0
- self.setUnreadCount()
- if len(self.unread_articles) > 0:
- self.showNext()
-
- def showNext(self):
-
- if len(self.unread_articles) > self.index:
- if self.index > 0:
- previous = self.unread_articles[self.index - 1]
- self.app.tinyTinyRSS.setArticleRead(previous["id"])
-
- next = self.unread_articles[self.index]
- self.setArticle(next)
- self.setUnreadCount()
- self.index += 1
- else:
- if self.index > 0:
- previous = self.unread_articles[self.index - 1]
- self.app.tinyTinyRSS.setArticleRead(previous["id"])
- self.setUnreadCount()
-
- def showPrevious(self):
- if self.index > 0:
- self.index -= 1
- previous = self.unread_articles[self.index]
- self.setArticle(previous)
- self.setUnreadCount()
-
- def openCurrent(self):
- current = self.unread_articles[self.index]
- url = QtCore.QUrl(current["link"])
- self.openLink(url)
-
- def setArticle(self, article):
- func = u"setArticle({});".format(json.dumps(article))
- self.evaluateJavaScript(func)
-
- def evaluateJavaScript(self, func):
- return self.wb.page().mainFrame().evaluateJavaScript(func)
-
- def setUnreadCount(self):
- length = len(self.unread_articles)
- unread = length - self.index
- self.app.setWindowTitle(" (" + str(unread) + "/" + str(length) + ")")
- if unread < 1:
- self.evaluateJavaScript("setArticle()")
-
- def templateString(self):
- return """
-
-
-
- ttrssl
-
-
-
-
-
-
-
-
- """
-
-
-
-class TinyTinyRSS:
- def __init__(self, app, server_url, session_id):
- self.app = app
- self.server_url = server_url
- self.session_id = session_id
-
- def doOperation(self, operation, options=None):
- url = self.server_url + "/api/"
- default_options = {'sid': self.session_id, 'op': operation}
- if options:
- options = dict(default_options.items() + options.items())
- else:
- options = default_options
- json_string = json.dumps(options)
- req = urllib2.Request(url)
- fd = urllib2.urlopen(req, json_string)
- body = ""
- while 1:
- data = fd.read(1024)
- if not len(data):
- break
- body += data
-
- return json.loads(body)["content"]
-
- def getUnreadFeeds(self):
- return self.doOperation("getHeadlines", {"show_excerpt":False, "view_mode":"unread", "show_content":True, "feed_id": -3})
-
- def setArticleRead(self, article_id):
- l = lambda: self.doOperation("updateArticle", {'article_ids':article_id, 'mode': 0, 'field': 2})
- t = Thread(target=l)
- t.start()
-
-
-if __name__ == "__main__":
- app = QtGui.QApplication(sys.argv)
- wb = MainWindow()
- wb.show()
- sys.exit(app.exec_())
\ No newline at end of file
diff --git a/feedthemonkey.desktop b/feedthemonkey.desktop
index 792da0d..f0746e0 100644
--- a/feedthemonkey.desktop
+++ b/feedthemonkey.desktop
@@ -3,7 +3,7 @@ Version=0.1.0
Comment=A desktop client for the TinyTinyRSS feed reader.
Exec=/usr/bin/feedthemonkey
GenericName=Feed Reader
-Icon=feedthemonkey
+Icon=feedthemonkey.py
Name=Feed the Monkey
NoDisplay=false
StartupNotify=true
diff --git a/feedthemonkey.py b/feedthemonkey.py
index 87d08de..cfed60f 100755
--- a/feedthemonkey.py
+++ b/feedthemonkey.py
@@ -9,28 +9,56 @@ settings = QtCore.QSettings("jabs.nu", "ttrssl")
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
+ self.setWindowIcon(QtGui.QIcon("feedmonkey"))
self.addAction(QtGui.QAction("Full Screen", self, checkable=True, toggled=lambda v: self.showFullScreen() if v else self.showNormal(), shortcut="F11"))
self.history = self.get("history", [])
self.restoreGeometry(QtCore.QByteArray.fromRawData(settings.value("geometry").toByteArray()))
self.restoreState(QtCore.QByteArray.fromRawData(settings.value("state").toByteArray()))
+ self.initUI()
+
session_id = self.get("session_id")
server_url = self.get("server_url")
- if not (session_id and server_url):
- session_id = sys.argv[2]
- server_url = sys.argv[1]
- self.put("session_id", session_id)
- self.put("session_id", server_url)
- self.tinyTinyRSS = TinyTinyRSS(self, server_url, session_id)
-
+ if not (session_id and server_url):
+ self.authenticate()
+ else:
+ self.initApp()
+
+ def initUI(self):
self.content = Content(self)
self.setCentralWidget(self.content)
+ menubar = self.menuBar()
+
+ reloadAction = QtGui.QAction("&Reload", self)
+ reloadAction.setStatusTip("Load new data")
+ reloadAction.setShortcut("r")
+ reloadAction.triggered.connect(self.content.reload)
+
+ logOutAction = QtGui.QAction("&Log Out", self)
+ logOutAction.setStatusTip("Log out from this entity")
+ logOutAction.triggered.connect(self.logOut)
+
+ exitAction = QtGui.QAction("&Exit", self)
+ exitAction.setShortcut("Ctrl+Q")
+ exitAction.setStatusTip("Exit Feed the Monkey")
+ exitAction.triggered.connect(self.close)
+
+ fileMenu = menubar.addMenu("&File")
+ fileMenu.addAction(reloadAction)
+ fileMenu.addAction(logOutAction)
+ fileMenu.addSeparator()
+ fileMenu.addAction(exitAction)
+
+ def initApp(self):
+ session_id = self.get("session_id")
+ server_url = self.get("server_url")
+ self.tinyTinyRSS = TinyTinyRSS(self, server_url, session_id)
+
self.content.evaluateJavaScript("setArticle()")
self.content.reload()
-
def closeEvent(self, ev):
settings.setValue("geometry", self.saveGeometry())
settings.setValue("state", self.saveState())
@@ -49,6 +77,36 @@ class MainWindow(QtGui.QMainWindow):
def setWindowTitle(self, t):
super(QtGui.QMainWindow, self).setWindowTitle("Feed the Monkey" + t)
+ def authenticate(self):
+
+ dialog = Login()
+
+ def callback():
+
+ server_url = str(dialog.textServerUrl.text())
+ user = str(dialog.textName.text())
+ password = str(dialog.textPass.text())
+
+ session_id = TinyTinyRSS.login(server_url, user, password)
+ if session_id:
+ self.put("session_id", session_id)
+ self.put("server_url", server_url)
+ self.initApp()
+ else:
+ self.authenticate()
+
+
+ dialog.accepted.connect(callback)
+
+ dialog.exec_()
+
+ def logOut(self):
+ self.tinyTinyRSS.logOut()
+ self.tinyTinyRSS = None
+ self.put("session_id", None)
+ self.put("server_url", None)
+ self.authenticate()
+
class Content(QtGui.QWidget):
@@ -223,8 +281,11 @@ class Content(QtGui.QWidget):
class TinyTinyRSS:
def __init__(self, app, server_url, session_id):
self.app = app
- self.server_url = server_url
- self.session_id = session_id
+ if server_url and session_id:
+ self.server_url = server_url
+ self.session_id = session_id
+ else:
+ self.app.authenticate()
def doOperation(self, operation, options=None):
url = self.server_url + "/api/"
@@ -253,6 +314,64 @@ class TinyTinyRSS:
t = Thread(target=l)
t.start()
+ def logOut(self):
+ self.doOperation("logout")
+
+ @classmethod
+ def login(self, server_url, user, password):
+ url = server_url + "/api/"
+ options = {"op": "login", "user": user, "password": password}
+ json_string = json.dumps(options)
+ req = urllib2.Request(url)
+ fd = urllib2.urlopen(req, json_string)
+ body = ""
+ while 1:
+ data = fd.read(1024)
+ if not len(data):
+ break
+ body += data
+
+ body = json.loads(body)["content"]
+
+ if body.has_key("error"):
+ msgBox = QtGui.QMessageBox()
+ msgBox.setText(body["error"])
+ msgBox.exec_()
+ return None
+
+ return body["session_id"]
+
+
+class Login(QtGui.QDialog):
+ def __init__(self):
+ QtGui.QDialog.__init__(self)
+ self.setWindowIcon(QtGui.QIcon("feedmonkey.png"))
+ self.setWindowTitle("Feed the Monkey - Login")
+
+ self.label = QtGui.QLabel(self)
+ self.label.setText("Please specify a server url, a username and a password.")
+
+ self.textServerUrl = QtGui.QLineEdit(self)
+ self.textServerUrl.setPlaceholderText("http://example.com/ttrss/")
+ self.textServerUrl.setText("http://")
+
+ self.textName = QtGui.QLineEdit(self)
+ self.textName.setPlaceholderText("username")
+
+ self.textPass = QtGui.QLineEdit(self)
+ self.textPass.setEchoMode(QtGui.QLineEdit.Password);
+ self.textPass.setPlaceholderText("password")
+
+ self.buttons = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok)
+ self.buttons.accepted.connect(self.accept)
+
+ layout = QtGui.QVBoxLayout(self)
+ layout.addWidget(self.label)
+ layout.addWidget(self.textServerUrl)
+ layout.addWidget(self.textName)
+ layout.addWidget(self.textPass)
+ layout.addWidget(self.buttons)
+
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
diff --git a/setup.py b/setup.py
index 9dffe9c..2eed3ff 100644
--- a/setup.py
+++ b/setup.py
@@ -11,7 +11,7 @@ setup(
url = "http://jabs.nu/feedthemonkey",
license = "BSD license",
packages = ['feedthemonkey'],
- scripts = ["feedthemonkey"],
+ scripts = ["feedthemonkey.py"],
data_files=[
('/usr/share/applications', ["feedthemonkey.desktop"]),
('/usr/share/pixmaps', ["feedthemonkey.xpm"])