~kris/dots

srice

ref: e98f3b030dc24445bd55c68d95d2d81933fd68b3 srice/.local/bin/history/history-sync-browsers -rw-r--r-- 4.6 KiB
e98f3b03 — Kris Yotam chore: sync local state after restore (push updates, no pull) a month ago
                                                                                
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!/bin/sh
# history browser sync (encrypted with SQLCipher)
# Syncs search history from browsers to unified database
# Run via systemd timer every minute

DB_PATH="$HOME/.local/share/history/history.db"
KEY_FILE="$HOME/.local/share/history/.key"
LAST_SYNC_FILE="$HOME/.local/share/history/.last_sync"

# Load encryption key
if [ ! -f "$KEY_FILE" ]; then
    echo "Encryption key not found. Run 'history-init' first." >&2
    exit 1
fi
KEY=$(cat "$KEY_FILE")

# Get last sync timestamp or default to epoch
if [ -f "$LAST_SYNC_FILE" ]; then
    LAST_SYNC=$(cat "$LAST_SYNC_FILE")
else
    LAST_SYNC="1970-01-01 00:00:00"
fi

# Current timestamp
NOW=$(date '+%Y-%m-%d %H:%M:%S')

# Function to extract search query from URL
extract_search_query() {
    local url="$1"
    local query=""

    # Google, DuckDuckGo, Bing, Brave Search
    query=$(echo "$url" | grep -oP '(?<=[?&]q=)[^&]*' | head -1)
    [ -n "$query" ] && echo "$query" | sed 's/+/ /g' | python3 -c "import sys, urllib.parse; print(urllib.parse.unquote(sys.stdin.read().strip()))" 2>/dev/null && return

    # YouTube
    query=$(echo "$url" | grep -oP '(?<=search_query=)[^&]*' | head -1)
    [ -n "$query" ] && echo "$query" | sed 's/+/ /g' | python3 -c "import sys, urllib.parse; print(urllib.parse.unquote(sys.stdin.read().strip()))" 2>/dev/null && return

    echo ""
}

# Check if URL is a search
is_search_url() {
    local url="$1"
    echo "$url" | grep -qE '(google\.(com|[a-z]{2})/search|duckduckgo\.com/\?|bing\.com/search|search\.brave\.com/search|youtube\.com/(results|search))'
}

# Sync from Firefox/Librewolf
sync_firefox() {
    local browser="$1"
    local profile_dir="$2"

    [ ! -d "$profile_dir" ] && return

    # Find the default profile
    local profile=$(find "$profile_dir" -maxdepth 1 -name "*.default*" -type d 2>/dev/null | head -1)
    [ -z "$profile" ] && return

    local places_db="$profile/places.sqlite"
    [ ! -f "$places_db" ] && return

    # Copy to temp to avoid lock issues
    local temp_db=$(mktemp)
    cp "$places_db" "$temp_db" 2>/dev/null || return

    # Extract URLs with timestamps
    sqlite3 "$temp_db" "SELECT datetime(visit_date/1000000, 'unixepoch', 'localtime'), url FROM moz_historyvisits JOIN moz_places ON moz_historyvisits.place_id = moz_places.id WHERE datetime(visit_date/1000000, 'unixepoch', 'localtime') > '$LAST_SYNC' ORDER BY visit_date;" 2>/dev/null | while IFS='|' read -r timestamp url; do
        if is_search_url "$url"; then
            query=$(extract_search_query "$url")
            if [ -n "$query" ]; then
                # Escape quotes
                query=$(echo "$query" | sed "s/'/''/g")
                url=$(echo "$url" | sed "s/'/''/g")
                hash=$(echo "${timestamp}${browser}${query}" | md5sum | cut -d' ' -f1)

                sqlcipher "$DB_PATH" "PRAGMA key = '$KEY'; INSERT OR IGNORE INTO search_history (timestamp, query, browser, url, hash) VALUES ('$timestamp', '$query', '$browser', '$url', '$hash');" 2>/dev/null
            fi
        fi
    done

    rm -f "$temp_db"
}

# Sync from Chromium-based browsers
sync_chromium() {
    local browser="$1"
    local history_file="$2"

    [ ! -f "$history_file" ] && return

    # Copy to temp to avoid lock issues
    local temp_db=$(mktemp)
    cp "$history_file" "$temp_db" 2>/dev/null || return

    # Chromium stores time as microseconds since 1601-01-01
    sqlite3 "$temp_db" "SELECT datetime((last_visit_time - 11644473600000000) / 1000000, 'unixepoch', 'localtime'), url FROM urls WHERE datetime((last_visit_time - 11644473600000000) / 1000000, 'unixepoch', 'localtime') > '$LAST_SYNC' ORDER BY last_visit_time;" 2>/dev/null | while IFS='|' read -r timestamp url; do
        if is_search_url "$url"; then
            query=$(extract_search_query "$url")
            if [ -n "$query" ]; then
                query=$(echo "$query" | sed "s/'/''/g")
                url=$(echo "$url" | sed "s/'/''/g")
                hash=$(echo "${timestamp}${browser}${query}" | md5sum | cut -d' ' -f1)

                sqlcipher "$DB_PATH" "PRAGMA key = '$KEY'; INSERT OR IGNORE INTO search_history (timestamp, query, browser, url, hash) VALUES ('$timestamp', '$query', '$browser', '$url', '$hash');" 2>/dev/null
            fi
        fi
    done

    rm -f "$temp_db"
}

# Sync all browsers
sync_firefox "librewolf" "$HOME/.librewolf"
sync_firefox "firefox" "$HOME/.mozilla/firefox"
sync_chromium "brave" "$HOME/.config/BraveSoftware/Brave-Browser/Default/History"
sync_chromium "chrome" "$HOME/.config/google-chrome/Default/History"
sync_chromium "chromium" "$HOME/.config/chromium/Default/History"

# Update last sync time
echo "$NOW" > "$LAST_SYNC_FILE"