diff --git a/bookmark_sync/firefox_bookmarks.py b/bookmark_sync/firefox_bookmarks.py new file mode 100644 index 0000000..fd7b00f --- /dev/null +++ b/bookmark_sync/firefox_bookmarks.py @@ -0,0 +1,63 @@ +from platform import system +from pathlib import Path + +from more_itertools import only +from sqlalchemy.ext.automap import automap_base +from sqlalchemy.orm import Session +from sqlalchemy import create_engine, MetaData + +from . import Bookmarks, Bookmark, Folder + + +class FirefoxBookmarks(Bookmarks): + _locations = { + "Windows": Path.home()/"AppData/Roaming/Mozilla/Firefox/Profiles", + "Linux": Path.home()/".mozilla/firefox", + "Darwin": Path.home()/"Library/Application Support/Firefox/Profiles" + } + + def read_data(self): + try: + self.bookmarks_file = only(self._locations[system()].glob( + "*.default-release"))/"places.sqlite" + except KeyError: + # Assume everthing that isn't Windows or MacOS works like Linux + self.bookmarks_file = only(self._locations["Linux"].glob( + "*.default-release"))/"places.sqlite" + + if not self.bookmarks_file.exists(): + raise RuntimeError( + "Firefox profile cannot be found or does not exist") + else: + self._engine = create_engine(f"sqlite:///{self.bookmarks_file}") + self._base = automap_base() + self._base.metadata = MetaData(bind=self._engine) + self._base.prepare(self._engine, reflect=True, + classname_for_table=lambda base, name, table: name.replace("moz_", "")) + self._session = Session(self._engine) + + @property + def _all_elements(self): + return (self._session.query(self._base.classes.bookmarks, self._base.classes.places) + .outerjoin(self._base.classes.places, self._base.classes.bookmarks.fk == self._base.classes.places.id) + .order_by(self._base.classes.moz_bookmarks.parent, self._base.classes.moz_bookmarks.position) + ) + + @property + def folders(self): + return self._all_elements.filter(self._base.classes.bookmarks.type == 2) + + @property + def bookmarks(self): + return self._all_elements.filter(self._base.classes.bookmarks.type == 1) + + @property + def root_folder(self): + return only(self.folders.filter(self._base.classes.bookmarks.id == 1)) + + @property + def toolbar_folder(self): + return only(self.folders + .filter(self._base.classes.bookmarks.id < 7) + .filter(self._base.classes.bookmarks.title == "toolbar") + )