{"id":13770778,"url":"https://github.com/rschroll/rmcl","last_synced_at":"2025-05-11T03:32:54.104Z","repository":{"id":42623803,"uuid":"336684459","full_name":"rschroll/rmcl","owner":"rschroll","description":"reMarkable Cloud Library for Python","archived":false,"fork":true,"pushed_at":"2022-03-30T16:39:05.000Z","size":108,"stargazers_count":31,"open_issues_count":7,"forks_count":10,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-20T19:47:03.940Z","etag":null,"topics":["remarkable-tablet"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"subutux/rmapy","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rschroll.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-02-07T02:37:57.000Z","updated_at":"2025-04-08T10:06:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rschroll/rmcl","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rschroll%2Frmcl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rschroll%2Frmcl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rschroll%2Frmcl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rschroll%2Frmcl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rschroll","download_url":"https://codeload.github.com/rschroll/rmcl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253514352,"owners_count":21920327,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["remarkable-tablet"],"created_at":"2024-08-03T17:00:41.906Z","updated_at":"2025-05-11T03:32:53.822Z","avatar_url":"https://github.com/rschroll.png","language":"Python","funding_links":[],"categories":["Python","APIs"],"sub_categories":["Cloud API"],"readme":"# rmcl: reMarkable Cloud Library\n\nrmcl is a Python library for interacting with the reMarkable cloud.  It\nsupports a file-tree view onto your files, exposes metadata, and gives\naccess to the original PDF or EPUB documents.  If\n[rmrl](https://github.com/rschroll/rmrl) is installed, it can also\nproduce PDF versions of any type of document with your annotations\nincluded.  rmcl can create, update, and delete items from the\nreMarkable cloud.\n\n## Quick Demo\n\nAs a taste, this code will list all of the files and their types in the\nroot directory of your reMarkable cloud.\n```python\nfrom rmcl import Item, Document, Folder\nimport trio\n\nasync def list_files():\n    root = await Item.get_by_id('')  # The root folder has ID of empty string\n    for child in root.children:\n        if isinstance(child, Folder):\n            print(f\"{child.name}: folder\")\n        elif isinstance(child, Document):  # The only other possibility\n            print(f\"{child.name}: {await child.type()}\")\n\ntrio.run(list_files)\n```\n\n## Installation\n\nrmcl requires Python 3.7 or later.  If that's installed, the easiest\ninstallation is to do a\n```bash\npip install rmcl\n```\nAlternatively, you may clone this repository.\n[Poetry](https://python-poetry.org/) is used for development, so once\nthat is installed you can run\n```bash\npoetry install\n```\nto get a virtual environment all set up.\n\nrmcl is asynchronous, and must be used with the\n[trio](https://trio.readthedocs.io/en/stable/) async library.\n\n## Features\n\n### Asynchronous\n\nrmcl is asynchronous at its core.  This keeps it from blocking during the\nmany HTTP transactions it uses.  Most methods are `async`, and therefore\nmust be called with the `await` keyword.  rmcl is designed to work with\nthe [trio](https://trio.readthedocs.io/en/stable/) async library.\n\n### Synchronous Option\n\nAsynchronous code can be overkill for simple scripts, so rmcl offers\nsynchronous versions of all of its async functions.  These functions\nhave a suffix of `'_s'` in their names.  This means that the above\nexample could be written\n```python\ndef list_files_sync():\n    root = Item.get_by_id_s('')\n    for child in root.children:\n        if isinstance(child, Folder):\n            print(f\"{child.name}: folder\")\n        elif isinstance(child, Document):\n            print(f\"{child.name}: {child.type_s()}\")\n```\nNote that these synchronous functions are still calling the asynchronous\nlow-level code.  They may fail if called within another asynchronous\nframework.\n\n### Object Oriented\n\nThe main interface to rmcl is the `Item` class and its two subclasses,\n`Folder` and `Document`.  The reMarkable cloud gives each item an ID.\nIf the ID of an Item is known, it can be retrieved with the\n`Item.get_by_id()` static method.  New `Folder`s and `Document`s can be\nmade with the `.new()` class method, which expects as arguments the item's\nname and parent ID.\n\nWhile the cloud API presents a flat directory structure, rmcl assembles\nthese objects into a file tree.  Every `Folder` has a `.children` attribute,\na list of the `Item`s within it.  Each `Item` has a `.parent` property,\nwhich gives the ID of its parent.  (The ID is given instead of the parent to avoid circular references.)  The parent object of `item` may be looked up\nwith `await Item.get_by_id(item.parent)`.\n\nTwo \"virtual\" folders are provided.  The root folder (ID = `''`) contains\nall `Item`s with no explicit parent.  The trash folder (ID = `'trash'`)\ncontains all `Item`s marked as trashed.\n\nVarious metadata are exposed as properties: `.name`, `.id`, `.version`,\n`.parent`, and `.mtime`, for modification time.  The `.name` and `.parent`\nproperties can be modified, which updates the underlying metadata.  Such\nchanges can be sent to the cloud with the `.update_metadata()` method.\n\nThe contents of `Document`s can be retrieved in several ways.  The\n`.raw()` method gets the zip file that the reMarkable cloud API uses\nto transfer documents.  The `.contents()` method gets the original PDF\nor EPUB file associated with a `Document`.  If\n[rmrl](https://github.com/rschroll/rmrl) is installed, the `.annotated()`\nmethod yields a PDF file with the user's annotations.  All three methods\nreturn a file-like object, but the exact type is not guaranteed and may\nvary in the future.  The same object may be returned from multiple calls\nto a given method; the user is responsible for coordinating `read`s and\n`seek`s to avoid contention.\n\nNew `Item`s or existing `Document`s with new contents may be uploaded to\nthe cloud with `.upload()` method.\n\n### Smart Updates\n\nrmcl keeps a list of all documents in the reMarkable cloud, so that it\ndoesn't need to query the API for each bit of information.  To ensure\nthis list is up to date, it automatically refreshes itself when you use\n`Item.get_by_id()` more than five minutes after the last refresh.  You\ncan also trigger this behavior by calling `rmcl.invalidate_cache()`\nbefore `Item.get_by_id()`.\n\nrmcl only updates objects that have a new version, so existing objects\nremain valid.  Nonetheless, it is better to call `Item.get_by_id()`\noften, instead of keeping your own list of `Item`s.\n\n### Caching Expensive Operations\n\nSome of this information, like document type and size, require several\nround trips to calculate.  rmcl stores these values locally, so they do\nnot need to be recalculated every time.  They are stored in a persistent\ndatabase, so this information is not lost when the process stops.  The\ndatabase stores this information by version, so it knows when it must be\nrecalculated.\n\n### Handles Authentication\n\nThe reMarkable cloud requires two tokens: The *device token* identifies\nyour device and needs only to be set once.  The *user token* is used on\nmost requests, and should be updated occasionally.  rmcl automatically\nrenews the user token once a day, or when it receives an authentication\nerror from the API.\n\nGetting the device token requires user interaction the first time a\nprogram is run.  If rmcl detects that it is being run interactively, it\nwill print instructions and prompt to user to input a new device token.\nOtherwise, it will throw a `rmcl.exceptions.AuthError`.  You must call\n`register_device(code)` with the code provided obtained from\nhttps://my.remarkable.com/connect/desktop.  Once obtained, the device\ntoken is stored for future use.\n\n## Comparison with rMapy\n\nrmcl started as a fork of [rMapy](https://github.com/subutux/rmapy).\nAs we started moving in a rather different direction, we decided it\nwould be better to make it into its own project.  At this point, only\nsome of the low-level API code from rMapy remains.\n\nTo help users decide which library best fits their needs, here are the\nmajor differences:\n- rmcl is asynchronous, while rMapy is synchonous.  This means that\n  rmcl will not block while making HTTP requests to the reMarkable API.\n  rmcl's synchronous functions will block, much like rMapy.\n- rmcl has a simpler object structure, mostly focused around `Item` and\n  its subclasses `Document` and `Folder`.  rMapy has similar objects\n  (`Meta`, `Document`, and `Folder`), but it also has a `Client` and\n  `Collection` singleton objects, and operations like getting documents\n  or listing directories are provided by these singletons.\n- rmcl abstracts away some details of the reMarkable cloud API, while\n  rMapy provides a more direct mapping of the API into Python.  For\n  instance, rMapy items have a `.VissibleName` _[sic]_ property, because\n  that's what the API provides.  rmcl turns this into a `.name` property.\n  In rmcl, `Folder`s have a list of children, but in rMapy, the children\n  must be looked up via the `Collection` singleton.  (The API provides\n  a flat file list.)\n- rMapy is more complete than rmcl.  rMapy exposes all metadata as\n  properties, while rmcl only exposes some metadata items.  rMapy provides\n  an object model for the zip files provided by the API, while rmcl just\n  exposes the zip files themselves.\n- rMapy has [better documentation](https://rmapy.readthedocs.io/en/latest/)\n  than rmcl.  (But we're working on it!)\n\n## Trademarks\n\nreMarkable(R) is a registered trademark of reMarkable AS. rmrl is not\naffiliated with, or endorsed by, reMarkable AS. The use of \"reMarkable\" in\nthis work refers to the company’s e-paper tablet product(s).\n\n## Copyright\n\nCopyright 2019 Stijn Van Campenhout\n\nCopyright 2020-2021 Robert Schroll\n\nrmcl is released under the MIT license.  See LICENSE.txt for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frschroll%2Frmcl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frschroll%2Frmcl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frschroll%2Frmcl/lists"}