[ Firefox searcher ] - Mon 25 May 2020

Our goal is to be able to search our Firefox history -- as well as the currently opened tabs -- from the terminal.

We are going to create two scripts, one called history_search.py to search our history, and one called switch_tabs.py to show and select from the currently opened tabs.

Firefox stores its history in an .sqlite database, so we will use sqlite3 to export a smaller snapshot as out.csv and save it in /tmp.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#!/usr/bin/env python2

import csv
import os
import time

CREATION_TIME_DELTA_SECONDS = 60 * 5

def mk_csv():
    bash = """
#!/usr/bin/bash
mkdir -p /tmp/history
cd /tmp/history

sqlite3 /home/garg/.waterfox/ma29imgx.default/places.sqlite <<!
.mode csv
.output out.csv
select url,title,visit_count from moz_places where visit_count > 1 order by visit_count desc ;
!
"""
    os.system(bash)

###########

def check_if_database_recent():
    if(not os.path.exists("/tmp/history/out.csv")):
        print("no exists")
        return False

    ct = os.path.getctime("/tmp/history/out.csv")
    now = time.time()

    if ct < now - CREATION_TIME_DELTA_SECONDS:
        return False
    else:
        return True

###########

def in_list(domain):
    for i in roots:
        if domain == i[0]:
            return i
    return None

###########

if not check_if_database_recent():
    print("new")
    mk_csv()

with open("/tmp/history/out.csv") as f:
    reader = csv.reader(f)
    ls = list(reader)

roots = []

top = 100

for (count, i) in enumerate(ls):
    if len(i[1]) < 1:
        continue

    #print(str(i[1]) + "\t" + i[0] + "\t" + i[2])
    print(str(i[1]) + "\t" + i[0])

I am actually using Waterfox, which is based on older versions of Firefox. The second script will use Mozrepl to communicate with Waterfox:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#!/usr/bin/env python2

import sys, re
from telnetlib import Telnet
import os

class Mozrepl(object):
    def __init__(self, ip="127.0.0.1", port=4242):
        self.ip = ip
        self.port = port
        self.prompt = b"repl>"

    def __enter__(self):
        self.t = Telnet(self.ip, self.port)
        intro = self.t.read_until(self.prompt, 1)
        if not intro.endswith(self.prompt):
            self.prompt = re.search(br"repl\d+>", intro).group(0)
            print("Waited due to nonstandard prompt:", self.prompt.decode())
        return self

    def __exit__(self, type, value, traceback):
        self.t.close()
        del self.t

    def js(self, command):
        self.t.write(command.encode() + b"\n")
        f= self.t.read_until(self.prompt);
        f=unicode(f, errors='ignore').decode()
        return f.replace((b"\n" + self.prompt).decode(),'').strip()

gettabs = """
String.prototype.format = function() {
    var formatted = this;
    for(arg in arguments) {
        formatted = formatted.replace("{" + arg + "}", arguments[arg]);
    }
    return formatted;
};

var container = gBrowser.tabContainer;
var all_tabs = gBrowser.tabContainer.childNodes;

var tab_list = [];
for (var i = 0; i < all_tabs.length; ++i ) {
//repl.print(container.getItemAtIndex(i));
var p = gBrowser.getBrowserForTab(gBrowser.tabContainer.childNodes[i]).contentTitle + "\t" + gBrowser.getBrowserForTab(gBrowser.tabContainer.childNodes[i]).currentURI.spec ;
repl.print(p)

}

"""

switchtotab = """
gBrowser.tabContainer.selectedIndex=
"""

with Mozrepl() as mozrepl:
    if len(sys.argv) > 1:
        print(mozrepl.js(switchtotab + sys.argv[1]))
    else:
        print(mozrepl.js(gettabs))

The last script will make sure we're running in tmux, and pipe everything through fzf, then open the selected URL in the browser:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/usr/bin/bash

# * Make sure we're running in a tmux window
if ! { [[ "$TERM" = screen* ]] && [ -n "$TMUX" ]; } ; then
    TOGGLE_ON=0 monoterm.sh ;

    if ! tmux select-window -t searcher ; then # make sure we're only running once
        tmux new-window waterfox_searcher.sh
    fi;

    wmctrl -vR "xfce4-terminal"

    exit 1 ; 
fi ;


# * Get all URLs and pass them to fzf
OPEN_TABS=$(switch_waterfox_tab.py)

# * Save org/notes/url.org if mounted 
if [ -d "$HOME/org/" ] ; then
    SAVED_URLS=$(org-fzf.py)
fi;

# * Waterfox history (history_search.py)
WATERFOX_HISTORY=$(history_search.py --all)

# -------

# * Pipe it all to fzf then open the result in Waterfox

# notify-send "Running fzf"
 out=$(echo "$OPEN_TABS"\n"$SAVED_URLS"\n"$WATERFOX_HISTORY" |

    fzf --history "$HOME/.urlshistory" --delimiter='\t' --with-nth=1,2,3 |
    cut -f 2)

for job in `jobs -p`
do
    echo $job
    wait $job || let "FAIL+=1"
done

CURRID=0
while IFS=$'\t' read -r -a tmp ; do
    url="${tmp[1]}"
    if [[ "$url"  == "$out" ]]; then
        switch_waterfox_tab.py $CURRID
        wmctrl -vR "waterfox"
        exit 0;
    fi;
    CURRID=$((CURRID+1))
done <<< $OPEN_TABS

if [ ${#out} -gt 1 ]; then       
    (nohup waterfox "$out")& 
    notify-send "$out" ;
    exit 0;
fi;