├── Chrome History.alfredworkflow
├── README.md
├── chrome_history_sample.jpeg
├── chrome_history_usage.jpeg
└── src
└── chrome_history_workflow.py
/Chrome History.alfredworkflow:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iandrea57/alfred-chromehistory/d22957483d11443ff8455798d1a421cde2134115/Chrome History.alfredworkflow
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # alfred-chromehistory
2 | Chrome History to search chrome history with keyword and open url in browser.
3 |
4 | ## Usage
5 | 1. Trigger Alfred (normally ⌥Spac)
6 | 2. Type hi and then enter history's keywords (part of url or title)
7 | 3. Multiple keywords split with white space
8 |
9 | ## Sample
10 | 
11 | 
12 |
--------------------------------------------------------------------------------
/chrome_history_sample.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iandrea57/alfred-chromehistory/d22957483d11443ff8455798d1a421cde2134115/chrome_history_sample.jpeg
--------------------------------------------------------------------------------
/chrome_history_usage.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iandrea57/alfred-chromehistory/d22957483d11443ff8455798d1a421cde2134115/chrome_history_usage.jpeg
--------------------------------------------------------------------------------
/src/chrome_history_workflow.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | '''
5 | chrome history v0.1
6 |
7 | Author: iandrea57
8 | Host: https://github.com/iandrea57
9 | '''
10 |
11 | import itertools
12 | import sqlite3
13 | import datetime
14 | import cgi
15 | import os
16 | import sys
17 | reload(sys)
18 | sys.setdefaultencoding("utf-8")
19 |
20 | DEBUG_MODE = False #debug mode
21 | HISTORY_DB_PATH = os.path.expanduser('~') + '/Library/Application Support/Google/Chrome/Default/History' #you can command `chrome://version/` in chrome to find the path
22 |
23 | class History:
24 | '''
25 | This is chrome history class, field from table: urls
26 | contains field: id, url, title, visit_count, last_visit_time
27 | '''
28 |
29 | id = 0
30 | url = ''
31 | title = ''
32 | visit_count = 0
33 | last_visit_time = None
34 | def __init__(self, id, url, title, visit_count, last_visit_time):
35 | self.id = id
36 | if url is not None:
37 | self.url = url
38 | if title is not None:
39 | self.title = title
40 | self.visit_count = visit_count
41 | self.last_visit_time = datetime.datetime.fromtimestamp(last_visit_time/1000000-11644473600)
42 |
43 | def __str__(self):
44 | '''
45 | in DEBUG_MODE: return all field value
46 | normally: return workflow_xml item format info
47 | '''
48 | if DEBUG_MODE:
49 | return 'id=%d, url=%s, title=%s, visit_count=%d, last_visit_time=%s' % (self.id, self.url, self.title, self.visit_count, self.last_visit_time)
50 | else:
51 | return '- ' + cgi.escape(self.title) + '' + cgi.escape(str(self.last_visit_time) + ' ' + self.url) + 'icon.png
'
52 |
53 |
54 | def backup_db(path):
55 | '''
56 | backup chrome's History database file daily (named 'History.bak.%y-%m-%d', previous backup file will be removed)
57 | '''
58 | time_day_str = datetime.datetime.now().strftime('%Y-%m-%d')
59 | backup_suffix = '.bak'
60 | backup_path = path + backup_suffix + "." + time_day_str
61 | if not os.path.exists(backup_path):
62 | os.system('rm ' + path.replace(' ', '\\ ') + backup_suffix + '.*')
63 | os.system('cp -a ' + path.replace(' ', '\\ ') + ' ' + backup_path.replace(' ', '\\ '))
64 | return backup_path
65 |
66 |
67 | def get_conn(path):
68 | '''
69 | get sqlite3 database connect
70 | '''
71 | conn = None
72 | if path is not None and os.path.exists(path) and os.path.isfile(path):
73 | conn = sqlite3.connect(path)
74 | conn.text_factory = str
75 | if DEBUG_MODE:
76 | print 'sqlite connect path', path
77 | return conn
78 |
79 |
80 | def get_cursor(conn):
81 | '''
82 | get sqlite3 database cursor
83 | '''
84 | if conn is not None:
85 | return conn.cursor()
86 | else:
87 | return None
88 |
89 |
90 | def fetchall(conn, sql, data=None):
91 | '''
92 | fetchall data from sqlite3 database by sql and param
93 | '''
94 | result = None
95 | if sql is not None and sql != '':
96 | cursor = get_cursor(conn)
97 | if cursor is not None:
98 | if DEBUG_MODE:
99 | print('execute sql:[{}], param:[{}]'.format(sql, data))
100 | if data is not None:
101 | cursor.execute(sql, data)
102 | else:
103 | cursor.execute(sql)
104 | result = cursor.fetchall()
105 | cursor.close()
106 | return result
107 |
108 |
109 | def build_history_list(result):
110 | '''
111 | build chrome history data list from sqlite3 query result arrays
112 | '''
113 | history_list = []
114 | if result is not None and len(result) > 0:
115 | for row in result:
116 | history_list.append(History(row[0], row[1], row[2], row[3], row[4]))
117 | return history_list
118 |
119 |
120 | def print_history_list(history_list):
121 | '''
122 | print chrome history list
123 | in DEBUG_MODE: print all field value
124 | normally: print workflow_xml item format info
125 | '''
126 | if DEBUG_MODE:
127 | for history in history_list:
128 | print history
129 | else:
130 | workflow_xml = ''
131 | for history in history_list:
132 | workflow_xml += str(history)
133 | workflow_xml += ''
134 | print workflow_xml
135 |
136 |
137 | def split_keywords(keywords, title_param_max_count):
138 | '''
139 | split keywords for param ${title}, ${url}
140 | '''
141 | dim_keywords = []
142 | for i in range(title_param_max_count+1):
143 | for split_tuple in itertools.combinations(keywords, i):
144 | dim_keywords.append([split_tuple, tuple(set(keywords).difference(set(split_tuple)))])
145 | if DEBUG_MODE:
146 | print 'dim_keywords:', dim_keywords
147 | return dim_keywords
148 |
149 |
150 | def query_from_db(conn, keywords):
151 | '''
152 | query chrome history list from sqlite3 by keywords
153 | '''
154 | like_keywords = []
155 | for keyword in keywords:
156 | like_keywords.append('%' + keyword + '%')
157 | dim_keywords = split_keywords(like_keywords, 2)
158 | flat_keywords = []
159 | sql = 'select id, url, title, visit_count, last_visit_time from urls where '
160 | for i in range(len(dim_keywords)):
161 | if i > 0:
162 | sql += ' or '
163 | sub_sql = ''
164 | title_keywords = dim_keywords[i][0]
165 | url_keywords = dim_keywords[i][1]
166 | flat_keywords += title_keywords + url_keywords
167 | if len(title_keywords) > 0:
168 | for url_keyword in title_keywords:
169 | if sub_sql != '':
170 | sub_sql += ' and '
171 | sub_sql += 'title like ?'
172 | if len(url_keywords) > 0:
173 | for url_keyword in url_keywords:
174 | if sub_sql != '':
175 | sub_sql += ' and '
176 | sub_sql += 'url like ?'
177 | sql += sub_sql
178 | sql += ' order by visit_count desc, last_visit_time desc, id desc limit 50'
179 | result = fetchall(conn, sql, tuple(flat_keywords))
180 | return result
181 |
182 |
183 | def query(content):
184 | '''
185 | query chrome history list by content and print result
186 | '''
187 | keywords = content.split()
188 | if DEBUG_MODE:
189 | print 'keywords:', keywords
190 | path = backup_db(HISTORY_DB_PATH)
191 | conn = get_conn(path)
192 | result = query_from_db(conn, keywords)
193 | history_list = build_history_list(result)
194 | print_history_list(history_list)
195 |
196 |
197 | if __name__ == '__main__':
198 | if DEBUG_MODE:
199 | query(u'dev bee')
200 | else:
201 | query(u'{query}')
--------------------------------------------------------------------------------