├── .gitignore ├── LICENSE ├── README.md ├── excluded_urls.txt.sample ├── requirements.txt └── session_buddy_tool.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | excluded_urls.txt 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Martin Wišo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Session Buddy Tool 2 | =========== 3 | 4 | A simple tool for managing [Session Buddy Chrome Extension][0] data 5 | 6 | Features: 7 | * export to JSON 8 | * clear saved sessions 9 | * merge multiple sessions into one 10 | 11 | 12 | ## Requirements 13 | * python 14 | * cjson 15 | * sqllite3 16 | 17 | ## Setup 18 | 19 | Install requiremtents: 20 | ``` 21 | $ pip install -r requirements.txt 22 | ``` 23 | 24 | ## Usage 25 | ``` 26 | $ ./session_buddy_tool.py -h 27 | usage: session_buddy_tool.py [-h] -a {export,merge,clean} [-e EXCLUDE] 28 | [-p PROFILE] 29 | 30 | optional arguments: 31 | -h, --help show this help message and exit 32 | -a {export,merge,clean}, --action {export,merge,clean} 33 | Action: export, merge, clean 34 | -e EXCLUDE, --exclude EXCLUDE 35 | Path to file with excluded urls 36 | -p PROFILE, --profile PROFILE 37 | Path to Chrome profile 38 | 39 | ``` 40 | 41 | [1]: https://chrome.google.com/webstore/detail/session-buddy/edacconmaakjimmfgnblocblbcdcpbko -------------------------------------------------------------------------------- /excluded_urls.txt.sample: -------------------------------------------------------------------------------- 1 | https://www.google.com/ 2 | https://github.com/tgrk/ 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cjson 2 | sqlite3 3 | -------------------------------------------------------------------------------- /session_buddy_tool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Session Buddy Chrome Extension tool 4 | # 5 | # TODO: 6 | # * update existing item with new 1links collection 7 | # * check if exists in GetPocket and insert if not? 8 | # * find extension db path? what about cross-platform shit? 9 | 10 | import sys 11 | import traceback 12 | import argparse 13 | import cjson 14 | import sqlite3 15 | import os 16 | from os.path import expanduser 17 | 18 | # 19 | # Helpers 20 | # 21 | def load_exclude_file(path): 22 | excluded = [] 23 | if os.path.isfile(path): 24 | with open(path) as f: 25 | excluded = f.readlines() 26 | else: 27 | print "File with excluded urls can not be found!" 28 | return excluded 29 | 30 | def extract_links(row, full): 31 | tabs = cjson.decode(row[1]) 32 | row_id = None 33 | for key in tabs[0].keys(): 34 | obj = tabs[0][key] 35 | if key == "id": 36 | row_id = obj 37 | elif key == "tabs": 38 | items = [] 39 | for i in obj: 40 | if full: 41 | items.append(i) 42 | else: 43 | items.append({"title": i["title"], "url": i["url"]}) 44 | return {"id": row_id, "items": items} 45 | 46 | #TODO: maybe use sets to speed things up? 47 | def remove_duplicates(items): 48 | seen = [] 49 | unique = [] 50 | for item in items: 51 | if not item["url"] in seen: 52 | seen.append(item["url"]) 53 | unique.append(item) 54 | return unique 55 | 56 | def filter_excluded(items): 57 | filtered = [] 58 | for item in items: 59 | found = 0 60 | for url in excluded_urls: 61 | if item["url"].startswith(url): 62 | found += 1 63 | if found == 0: 64 | filtered.append(item) 65 | return filtered 66 | 67 | def build_tabs(id, top, width, heighttabs): 68 | return {"alwaysOnTop":false, 69 | "focused":true, 70 | "height":height, 71 | "id":id, 72 | "incognito":false, 73 | "left":0, 74 | "state":"normal", 75 | "tabs":tabs, 76 | "top":top, 77 | "type":"normal", 78 | "width":width 79 | } 80 | 81 | def get_saved_sessions(conn, table, full): 82 | sessions = [] 83 | try: 84 | cur = conn.cursor() 85 | cur.execute("SELECT id, windows FROM %s;" % table) 86 | for row in cur.fetchall(): 87 | item = extract_links(row, full) 88 | sessions += item["items"] 89 | 90 | except sqlite3.Error, e: 91 | print "Get sessions error: %s" % e.args[0] 92 | return sessions 93 | 94 | def insert_row(conn, table, row_id, items): 95 | try: 96 | cur = conn.cursor() 97 | cur.execute("INSERT INTO %s VALUES();" % table) 98 | return True 99 | except sqlite3.Error, e: 100 | print "Add merged sessions error: %s" % e.args[0] 101 | return False 102 | 103 | def delete_row(conn, table, row_id): 104 | try: 105 | cur = conn.cursor() 106 | cur.execute("DELETE * FROM %s WHERE id=?;" % table, row_id) 107 | return True 108 | except sqlite3.Error, e: 109 | print "Delete error: %s" % e.args[0] 110 | return False 111 | # 112 | # Actions 113 | # 114 | def action_export(conn, tables, excluded_urls): 115 | items = [] 116 | for table in tables: 117 | items += get_saved_sessions(conn, table, False) 118 | 119 | items = remove_duplicates(filter_excluded(items)) 120 | 121 | print cjson.encode(items) 122 | 123 | def action_merge(conn, tables, excluded_urls): 124 | items = [] 125 | for table in tables: 126 | items += get_saved_sessions(conn, table, True) 127 | 128 | items = remove_duplicates(filter_excluded(items)) 129 | 130 | #TODO: merge records 131 | #TODO: clear existing sessions 132 | #TODO: add new session with merged data 133 | return None 134 | 135 | def action_clean(conn, tables, excluded_urls): 136 | try: 137 | cur = conn.cursor() 138 | for table in tables: 139 | cur.execute("DELETE * FROM %s WHERE id=?;" % table) 140 | except sqlite3.Error, e: 141 | print "Cleanup error: %s" % e.args[0] 142 | 143 | if __name__ == "__main__": 144 | tables = ["SavedSessions", "PreviousSessions"] 145 | 146 | # handle commandline arguments 147 | parser = argparse.ArgumentParser() 148 | parser.add_argument("-a", "--action", 149 | choices=['export', 'merge', 'clean'], 150 | help="Action: export, merge, clean", 151 | required=True) 152 | parser.add_argument("-e", "--exclude", 153 | help="Path to file with excluded urls", 154 | required=False) 155 | parser.add_argument("-p", "--profile", 156 | help="Path to Chrome profile", 157 | required=False) 158 | args = parser.parse_args() 159 | 160 | # apply parsed commandline arguments 161 | excluded_urls = [] 162 | if args.exclude: 163 | excluded_urls = load_exclude_file(args.exclude) 164 | 165 | chrome_profile = "%s/.config/google-chrome/Default/" % expanduser("~") 166 | if args.profile: 167 | chrome_profile = args.chrome_profile 168 | 169 | extension = "chrome-extension_edacconmaakjimmfgnblocblbcdcpbko_0" 170 | db_path = "%s/databases/%s/2" % (chrome_profile, extension) 171 | conn = sqlite3.connect(db_path) 172 | try: 173 | if args.action == "export": 174 | action_export(conn, tables, excluded_urls) 175 | elif args.action == "clean": 176 | action_clean(conn, tables, excluded_urls) 177 | elif args.action == "merge": 178 | action_merge(conn, tables, excluded_urls) 179 | else: 180 | parser.print_help() 181 | sys.exit(1) 182 | 183 | sys.exit(0) 184 | except Exception, e: 185 | print "Main error: %s" % e 186 | print '-'*60 187 | traceback.print_exc(file=sys.stdout) 188 | print '-'*60 189 | sys.exit(1) 190 | finally: 191 | if conn: 192 | conn.close() 193 | --------------------------------------------------------------------------------