Feineigle.com - Firefox Session Saver

Home · Projects · 2017 · Firefox Session Saver

Published: September 10, 2017 (6 years 7 months ago.)
Tags:  Computer · Firefox · Python · Software


I have an almost 10 year old netbook. I actually love the thing; its battery lasts for 8+ hours (off-line, reading pdfs, writing in vim), but it is really underpowered by today’s standards. Running the latest version of Firefox is painful to say the least. I run a considerably older version, but with a few add-ons it is still fairly heavy.

My Essential Add-ons

Adblock and NoScript are essential and there is no way I’d surf without them.

Scrapbook is my preferred off-line page archiver and is light enough that I have no real complaints, even if managing and search is a little slow I don’t use those features often enough for it to matter.

Stylish is essential so that I can turn pages dark when I have to spend hours reading them.

The heaviest add-on by far is Pentadactyl. This is pretty full featured and although I could probably slim down by using Vimperator, I do take advantage of some of the fancier parts in Pentadactyl.

Non-essential Add-ons

Tab Mix and Menu Editor and all seem light enough to warrant inclusion without being requirements in my opinion. Tab Mix has a bunch of options to customize tabs gasp and also has a session manager that I was using, until now.

Session Managing / Tab Saver

I was using Tab Mixes’ session manager to save sessions, but it regularly fails when Firefox crashes (rarely). It is still enough of an annoyance that I wrote a script to use wmctrl and xdotool to automatically save a session every night. This worked swimmingly except that I ended up with a ton of sessions saved and I had to leave the machine on all night. Additionally since it was using xdotool, I couldn’t very well have it run every hour or so without it interfering with my regular use.

Eventually I decided to write the script on this page so that I could completely remove any session managing from Firefox. Digging around a little I found that the session information was stored in a JSON file:

~/.mozilla/firefox/PROFILE.default/sessionstore.js

It was simple enough to extract this information and stick it into a plain-text file via a python script. This script keeps a log of the tabs open (the –save option) and generates a urls.txt (the –generate option) that can be used to restore the previous saved tab state. They can be run together to simultaneously update the running log and create a current urls.txt file.

import argparse, json, time
from collections import defaultdict

parser = argparse.ArgumentParser()
parser.add_argument("-s", "--save", 
                    action="store_true",
                    help="save list of currently open tabs")
parser.add_argument("-g", "--generate", 
                    action="store_true",
                    help="generate url.txt from saved tabs")

args = parser.parse_args()

if args.save == False and args.generate == False:
  parser.print_help()

def save():
  # save tabs
  ff_path = "/home/USER/.mozilla/firefox/PROFILE.default/sessionstore.js"
  log_path = "/home/USER/.mozilla/firefox/PROFILE.default/my_tab_logger/"\
             +time.strftime("%Y-%m")\
             +"-firefox_tabs.log"
  
  with open(ff_path, "r") as f:
    jdata = json.loads(f.read())
  
  with open(log_path, "a") as f:
    f.write(time.strftime("%Y-%m-%d %H:%M")+"\n")
  
  for win in jdata.get("windows"):
    for tab in win.get("tabs"):
      i = tab.get("index") - 1
      #print tab.get("entries")[i].get("url")
      with open(log_path, "a") as f:
        f.write(tab.get("entries")[i].get("url")+"\n")
  print "Tabs saved."

def generate():
  #create url.txt from tabs log
  path = "/home/USER/.mozilla/firefox/PROFILE.default/my_tab_logger/"
  log_path = path+time.strftime("%Y-%m")+"-firefox_tabs.log"
  #TODO test file exists, if crash on last day of month, won't find the right log
  # if doesn't exist, get the last month's log
  out_path = path+"urls.txt"
  
  dd = defaultdict(list)
  with open(log_path, "r") as logs: #builds dictionary of log
    for ent in logs.readlines():
      if ent.startswith(time.strftime("%Y-%m")):
         date = ent
      else:
         dd[date].append(ent.strip())
  
  last = None
  for key in dd: #locates the latest entry
    if key > last:
      last = key
  
  with open(out_path, "w") as f: #outputs to txt file
    for url in dd[last]:
      f.write(url+"\n")

if __name__ == "__main__":
  if args.save:
    save()
  if args.generate:
    generate()

With this running daily (actually every 6 hours) via cron (0 */6 * * * /pathTo/ff_tabs.py -s -g), I could completely scrap the session manager feature and have something more robust in the face of future crashes. Additionally, since it is not using xdotool, it will execute silently in the background, allowing me to up the cron job frequency as necessary. To restore the tabs you simply launch:

firefox $(cat /pathTo/urls.txt)

Where urls.txt contains the URLs that the above script saves. Make sure not to have the option selected for Firefox to open the tabs from last session, otherwise you’ll get a bunch of duplicate tabs (if the built-in restore works).

Conclusion

This is very rough and dirty, but it gets the job done for me. It is Linux only and the urls.txt generator has a bug if the last save was last month. For example if you crash with 6 hours (or however often the cron job runs) of a new month, the restore script will fail, but the session is still available to restore manually.

By adding a command to Pentadactyl, :command log !/PATH_TO_SCRIPT/SCRIPT.py, you can manually save your tabs at your leisure with :log.