From 3eb4a3f443fb3268c1eba460af86853a4356d3f5 Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 14 May 2020 15:18:18 +0200 Subject: [PATCH] initial commit --- bin/sync_zotero_rm | 106 +++++++++++++++++++++++++++++++++++++ requirements.txt | 3 ++ setup.py | 14 +++++ sync_zotero_rm/__init__.py | 0 sync_zotero_rm/config.py | 57 ++++++++++++++++++++ 5 files changed, 180 insertions(+) create mode 100644 bin/sync_zotero_rm create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 sync_zotero_rm/__init__.py create mode 100644 sync_zotero_rm/config.py diff --git a/bin/sync_zotero_rm b/bin/sync_zotero_rm new file mode 100644 index 0000000..20375c4 --- /dev/null +++ b/bin/sync_zotero_rm @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 + +from pyzotero import zotero +from webdav3.client import Client +from tempfile import TemporaryDirectory +from os.path import join +import subprocess +import zipfile +from rmapy.api import Client as RMClient +from rmapy.folder import Folder +from rmapy.document import Document +from rmapy.document import ZipDocument +from sync_zotero_rm.config import get_config + +if __name__ == "__main__": + # get/create config + cfg = get_config() + + # TODO init remarkabel connection + + # init all services and apis we need + zot = zotero.Zotero(cfg['zot_user_id'], "user", cfg['zot_api_key']) + rmapy = RMClient() + rmapy.renew_token() + + # used for zotero pdf download + webdav = Client({ + "webdav_hostname": cfg['webdav_url'], + "webdav_login": cfg['webdav_user'], + "webdav_password": cfg['webdav_password'] + }) + + # temp dir for file processing + tempdir = TemporaryDirectory() + + # find the zotero collection key + print("Fetching collection from Zotero... ", end="", flush=True) + zot_collections = zot.collections() + for collection in zot_collections: + if collection['data']['name'] == cfg['zot_collection']: + zot_collection_key = collection['data']['key'] + print("Done") + + # get pdf items in collection + print("Fetching item list... ", end="", flush=True) + zot_items = zot.collection_items(zot_collection_key) + zot_items_pdf = [item for item in zot_items + if "contentType" in item['data'] + and item['data']['contentType'] == "application/pdf" + ] + print("Done") + + print(f"Items to sync: {len(zot_items_pdf)}") + + # get existing documents and folders on remarkable + rm_items = rmapy.get_meta_items() + rm_folders = [f for f in rm_items if isinstance(f, Folder)] + rm_documents = [f for f in rm_items if isinstance(f, Document)] + rm_existing_names = [f.VissibleName for f in rm_documents] + + # find or create correct folder on remarkable + folder = [f for f in rm_folders if f.VissibleName == cfg['rm_folder']] + if len(folder) > 0: + folder = folder[0] + else: # folder does not exist yet + print(f"Creating folder {remarkable_folder}") + folder = Folder(cfg['rm_folder']) + rmapy.create_folder(folder) + + # Process items + for item in zot_items_pdf: + title = item['data']['title'] + print(title) + + if title[:-4] in rm_existing_names: + print("skip (already exists)") + else: + # Download via webdav + print("download, ", end="", flush=True) + key = item['key'] + local_path = join(tempdir.name, f"{key}.zip") + webdav.download_sync(remote_path=f"{key}.zip", local_path=local_path) + + # Unpack file + print("unpack, ", end="", flush=True) + with zipfile.ZipFile(local_path, "r") as zip: + zip.extract(title, path=tempdir.name) + file_path = join(tempdir.name, title) + + # Upload to RM + print("upload ", end="", flush=True) + rawDocument = ZipDocument(doc=file_path) + success = rmapy.upload(rawDocument, folder) + if success: + print("done") + else: + print("Upload failed") + exit(1) + + # Delete files from zotero collection + print("Removing items from zotero collection") + for item in zot_items: + if item['data']['itemType'] == "attachment": + continue + zot.deletefrom_collection(zot_collection_key, item) + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..062be91 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +rmapy==0.2.1 +Pyzotero==1.4.16 +webdavclient3==3.14.4 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..b3c4a6b --- /dev/null +++ b/setup.py @@ -0,0 +1,14 @@ +from setuptools import setup, find_packages + +install_reqs = [line.strip() for line in open("requirements.txt").readlines()] +packs = find_packages(exclude=["bin"]) + +setup( + name="sync_zotero_rm", + version="0.0.1", + author="Daniel Bauer", + description="Sync zotero collections to remarkable devices", + install_requires=install_reqs, + scripts=["bin/sync_zotero_rm"], + packages=packs +) \ No newline at end of file diff --git a/sync_zotero_rm/__init__.py b/sync_zotero_rm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sync_zotero_rm/config.py b/sync_zotero_rm/config.py new file mode 100644 index 0000000..a6e39e9 --- /dev/null +++ b/sync_zotero_rm/config.py @@ -0,0 +1,57 @@ +import yaml +from pathlib import Path +from os.path import join + +CONFIG_FILE = join(str(Path.home()), ".zotero_remarkable.yaml") + +def _read_config(): + try: + with open(CONFIG_FILE, "r") as f: + return yaml.safe_load(f) + except: + return None + +def _write_config(cfg): + with open(CONFIG_FILE, "w", encoding="utf8") as f: + yaml.dump(cfg, f, default_flow_style=False, allow_unicode=True) + +def _check_config(cfg): + if not isinstance(cfg, dict): + return False + valid = True + for i in [ + "zot_api_key", + "zot_user_id", + "zot_collection", + "webdav_url", + "webdav_user", + "webdav_password", + "rm_folder"]: + if i not in cfg: + valid = False + return valid + +def _create_config(): + cfg = {} + for i in [ + "zot_api_key", + "zot_user_id", + "zot_collection", + "webdav_url", + "webdav_user", + "webdav_password", + "rm_folder"]: + answer = input(i + ": ") + cfg[i] = answer.strip() + _write_config(cfg) + return cfg + +def get_config(): + cfg = _read_config() + if not _check_config(cfg): + return _create_config() + else: + return cfg + + +