commit
4224c9c198
|
@ -0,0 +1,19 @@
|
||||||
|
# EditorConfig is awesome: http://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
# 4 space indentation
|
||||||
|
[*.py]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
# Matches the exact files either package.json or .travis.yml
|
||||||
|
[{package.json,.travis.yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
|
@ -57,6 +57,9 @@ HackerTray accepts its various options via the command line. Run `hackertray -h`
|
||||||
|
|
||||||
1. `-c`: Enables comments support. Clicking on links will also open the comments page on HN. Can be switched off via the UI, but the setting is not remembered.
|
1. `-c`: Enables comments support. Clicking on links will also open the comments page on HN. Can be switched off via the UI, but the setting is not remembered.
|
||||||
2. `--chrome PROFILE_PATH`: Specifying a profile path to a chrome directory will make HackerTray read the Chrome History file to mark links as read. Links are checked once every 5 minutes, which is when the History file is copied (to override the lock in case Chrome is open), searched using sqlite and deleted. This feature is still experimental.
|
2. `--chrome PROFILE_PATH`: Specifying a profile path to a chrome directory will make HackerTray read the Chrome History file to mark links as read. Links are checked once every 5 minutes, which is when the History file is copied (to override the lock in case Chrome is open), searched using sqlite and deleted. This feature is still experimental.
|
||||||
|
3. `--firefox PROFILE_PATH`: Specify path to a firefox profile directory. HackerTray will read your firefox history from this profile, and use it to mark links as read.
|
||||||
|
|
||||||
|
Note that the `--chrome` and `--firefox` options are independent, and can be used together. However, they cannot be specified multiple times (so reading from 2 chrome profiles is not possible).
|
||||||
|
|
||||||
###Google Chrome Profile Path
|
###Google Chrome Profile Path
|
||||||
|
|
||||||
|
@ -68,6 +71,9 @@ Where your Profile is stored depends on which version of chrome you are using:
|
||||||
|
|
||||||
Replace `Default` with `Profile 1`, `Profile 2` or so on if you use multiple profiles on Chrome. Note that the `--chrome` option accepts a `PROFILE_PATH`, not the History file itself. Also note that sometimes `~` might not be set, so you might need to use the complete path (such as `/home/nemo/.config/google-chrome/Default/`).
|
Replace `Default` with `Profile 1`, `Profile 2` or so on if you use multiple profiles on Chrome. Note that the `--chrome` option accepts a `PROFILE_PATH`, not the History file itself. Also note that sometimes `~` might not be set, so you might need to use the complete path (such as `/home/nemo/.config/google-chrome/Default/`).
|
||||||
|
|
||||||
|
###Firefox Profile Path
|
||||||
|
The default firefox profile path is `~/.mozilla/firefox/*.default`, where `*` denotes a random 8 digit string. You can also read `~/.mozilla/firefox/profiles.ini` to get a list of profiles.
|
||||||
|
|
||||||
##Features
|
##Features
|
||||||
1. Minimalist Approach to HN
|
1. Minimalist Approach to HN
|
||||||
2. Opens links in your default browser
|
2. Opens links in your default browser
|
||||||
|
|
|
@ -25,6 +25,7 @@ import signal
|
||||||
|
|
||||||
from hackernews import HackerNews
|
from hackernews import HackerNews
|
||||||
from chrome import Chrome
|
from chrome import Chrome
|
||||||
|
from firefox import Firefox
|
||||||
from version import Version
|
from version import Version
|
||||||
|
|
||||||
class HackerNewsApp:
|
class HackerNewsApp:
|
||||||
|
@ -89,7 +90,7 @@ class HackerNewsApp:
|
||||||
self.menu.show()
|
self.menu.show()
|
||||||
|
|
||||||
self.ind.set_menu(self.menu)
|
self.ind.set_menu(self.menu)
|
||||||
self.refresh(chrome_data_directory=args.chrome)
|
self.refresh(chrome_data_directory=args.chrome, firefox_data_directory=args.firefox)
|
||||||
|
|
||||||
def toggleComments(self, widget):
|
def toggleComments(self, widget):
|
||||||
"""Whether comments page is opened or not"""
|
"""Whether comments page is opened or not"""
|
||||||
|
@ -159,14 +160,19 @@ class HackerNewsApp:
|
||||||
self.menu.prepend(i)
|
self.menu.prepend(i)
|
||||||
i.show()
|
i.show()
|
||||||
|
|
||||||
def refresh(self, widget=None, no_timer=False, chrome_data_directory=None):
|
def refresh(self, widget=None, no_timer=False, chrome_data_directory=None, firefox_data_directory=None):
|
||||||
|
|
||||||
"""Refreshes the menu """
|
"""Refreshes the menu """
|
||||||
try:
|
try:
|
||||||
|
# Create an array of 20 false to denote matches in History
|
||||||
|
searchResults = [False]*20
|
||||||
data = list(reversed(HackerNews.getHomePage()[0:20]))
|
data = list(reversed(HackerNews.getHomePage()[0:20]))
|
||||||
if(chrome_data_directory):
|
|
||||||
urls = [item['url'] for item in data]
|
urls = [item['url'] for item in data]
|
||||||
searchResults = Chrome.search(urls, chrome_data_directory)
|
if(chrome_data_directory):
|
||||||
|
searchResults = self.mergeBoolArray(searchResults, Chrome.search(urls, chrome_data_directory))
|
||||||
|
|
||||||
|
if(firefox_data_directory):
|
||||||
|
searchResults = self.mergeBoolArray(searchResults, Firefox.search(urls, firefox_data_directory))
|
||||||
|
|
||||||
#Remove all the current stories
|
#Remove all the current stories
|
||||||
for i in self.menu.get_children():
|
for i in self.menu.get_children():
|
||||||
|
@ -175,10 +181,7 @@ class HackerNewsApp:
|
||||||
|
|
||||||
#Add back all the refreshed news
|
#Add back all the refreshed news
|
||||||
for index, item in enumerate(data):
|
for index, item in enumerate(data):
|
||||||
if(chrome_data_directory):
|
|
||||||
item['history'] = searchResults[index]
|
item['history'] = searchResults[index]
|
||||||
else:
|
|
||||||
item['history'] = False
|
|
||||||
self.addItem(item)
|
self.addItem(item)
|
||||||
# Catch network errors
|
# Catch network errors
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
|
@ -188,13 +191,18 @@ class HackerNewsApp:
|
||||||
if not no_timer:
|
if not no_timer:
|
||||||
gtk.timeout_add(10 * 30 * 1000, self.refresh, widget, no_timer, chrome_data_directory)
|
gtk.timeout_add(10 * 30 * 1000, self.refresh, widget, no_timer, chrome_data_directory)
|
||||||
|
|
||||||
|
# Merges two boolean arrays, using OR operation against each pair
|
||||||
|
def mergeBoolArray(self, original, patch):
|
||||||
|
for index, var in enumerate(original):
|
||||||
|
original[index] = original[index] or patch[index]
|
||||||
|
return original
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description='Hacker News in your System Tray')
|
parser = argparse.ArgumentParser(description='Hacker News in your System Tray')
|
||||||
parser.add_argument('-v','--version', action='version', version=Version.current())
|
parser.add_argument('-v','--version', action='version', version=Version.current())
|
||||||
parser.add_argument('-c','--comments', dest='comments',action='store_true', help="Load the HN comments link for the article as well")
|
parser.add_argument('-c','--comments', dest='comments',action='store_true', help="Load the HN comments link for the article as well")
|
||||||
parser.add_argument('--chrome', dest='chrome', help="Specify a Google Chrome Profile directory to use for matching chrome history")
|
parser.add_argument('--chrome', dest='chrome', help="Specify a Google Chrome Profile directory to use for matching chrome history")
|
||||||
|
parser.add_argument('--firefox', dest='firefox', help="Specify a Firefox Profile directory to use for matching firefox history")
|
||||||
parser.set_defaults(comments=False)
|
parser.set_defaults(comments=False)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
indicator = HackerNewsApp(args)
|
indicator = HackerNewsApp(args)
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
from __future__ import print_function
|
||||||
|
import sqlite3
|
||||||
|
import shutil
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class Firefox:
|
||||||
|
HISTORY_TMP_LOCATION = '/tmp/hackertray.firefox'
|
||||||
|
HISTORY_FILE_NAME = '/places.sqlite'
|
||||||
|
@staticmethod
|
||||||
|
def search(urls, config_folder_path):
|
||||||
|
Firefox.setup(config_folder_path)
|
||||||
|
conn = sqlite3.connect(Firefox.HISTORY_TMP_LOCATION)
|
||||||
|
db = conn.cursor()
|
||||||
|
result = []
|
||||||
|
for url in urls:
|
||||||
|
db_result = db.execute('SELECT url from moz_places WHERE url=:url',{"url":url})
|
||||||
|
if(db.fetchone() == None):
|
||||||
|
result.append(False)
|
||||||
|
else:
|
||||||
|
result.append(True)
|
||||||
|
os.remove(Firefox.HISTORY_TMP_LOCATION)
|
||||||
|
return result
|
||||||
|
@staticmethod
|
||||||
|
def setup(config_folder_path):
|
||||||
|
file_name = os.path.abspath(config_folder_path+Firefox.HISTORY_FILE_NAME)
|
||||||
|
if not os.path.isfile(file_name):
|
||||||
|
print("ERROR: ", "Could not find Firefox history file", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
shutil.copyfile(file_name, Firefox.HISTORY_TMP_LOCATION)
|
|
@ -0,0 +1,15 @@
|
||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
|
||||||
|
from hackertray import Firefox
|
||||||
|
|
||||||
|
class ChromeTest(unittest.TestCase):
|
||||||
|
def runTest(self):
|
||||||
|
config_folder_path = os.getcwd()+'/test/'
|
||||||
|
data = Firefox.search([
|
||||||
|
"http://www.hckrnews.com/",
|
||||||
|
"http://www.google.com/",
|
||||||
|
"http://wiki.ubuntu.com/",
|
||||||
|
"http://invalid_url/"],
|
||||||
|
config_folder_path)
|
||||||
|
self.assertTrue(data == [True,True,True,False])
|
Binary file not shown.
Loading…
Reference in New Issue