Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for using rmapy as reMarkable client #88

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 1 addition & 13 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
FROM golang:buster AS rmapi

ENV GOPATH /go
ENV PATH ${GOPATH}/bin:/usr/local/go/bin:$PATH
ENV RMAPIREPO github.com/juruen/rmapi

RUN go get -u ${RMAPIREPO}


FROM python:3.7-slim-buster

# rmapi
COPY --from=rmapi /go/bin/rmapi /usr/bin/rmapi

# needed to install openjdk-11-jre-headless
RUN mkdir -p /usr/share/man/man1

Expand All @@ -21,7 +9,7 @@ RUN apt-get update \
libmagickwand-dev \
pdftk \
ghostscript \
poppler-utils
poppler-utils

RUN pip install --no-cache-dir paper2remarkable

Expand Down
56 changes: 22 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ let me know!
3. Crops the pdf to remove unnecessary borders
4. Shrinks the pdf file to reduce the filesize
5. Generates a nice filename based on author/title/year of the paper
6. Uploads it to your reMarkable using
[rMapi](https://github.com/juruen/rmapi).
6. Uploads it to your reMarkable

Optionally, you can:

Expand Down Expand Up @@ -103,16 +102,10 @@ The script requires the following external programs to be available:
[pdftk-java](https://gitlab.com/pdftk-java/pdftk), whichever your package
manager provides.
- [GhostScript](https://www.ghostscript.com/)
- [rMAPI](https://github.com/juruen/rmapi)

Specifically:

1. First install [rMAPI](https://github.com/juruen/rmapi), using
```
$ go get -u github.com/juruen/rmapi
```

2. Then install system dependencies:
1. Install system dependencies:
- **Arch Linux:** ``pacman -S pdftk ghostscript poppler``
- **Ubuntu:** ``apt-get install pdftk ghostscript poppler-utils``. Replace
``pdftk`` with ``qpdf`` if your distribution doesn't package ``pdftk``.
Expand All @@ -125,7 +118,7 @@ Specifically:
instructions, so we welcome clarifications where needed. The Docker
instructions below may be more convenient on Windows.

3. Finally, install ``paper2remarkable``:
2. Next, install ``paper2remarkable``:
```
$ pip install paper2remarkable
```
Expand All @@ -141,23 +134,27 @@ Specifically:
[Readability.js](https://github.com/mozilla/readability) for HTML articles.
This is known to improve the output of certain web articles.

If any of the dependencies (such as rmapi or ghostscript) are not available on
the ``PATH`` variable, you can supply them with the relevant options to the
script (for instance ``p2r --rmapi /path/to/rmapi``). If you run into trouble
with the installation, please let me know by opening an issue [on
Github][github-url].
If any of the dependencies (such as ghostscript) are not available on the
``PATH`` variable, you can supply them with the relevant options to the script
(for instance ``p2r --gs /path/to/gs``). If you run into trouble with the
installation, please let me know by opening an issue [on Github][github-url].

## Usage

The full help of the script is as follows. Hopefully the various command line
flags are self-explanatory, but if you'd like more information see the [man
page](docs/man.md) (``man p2r``) or open an issue [on GitHub][github-url].

The first time you use the `p2r` command to upload to the reMarkable, you will
be prompted to authenticate your computer with a one-time code. This will
create the ``~/.rmapi`` file with authentication details. Alternatively, you
can run the ``p2r-auth`` program to do the authentication only.

```
usage: p2r [-h] [-b] [-c] [-d] [-e] [-n] [-p REMARKABLE_DIR] [-r] [-k] [-v]
[-V] [-f FILENAME] [--gs GS] [--pdftoppm PDFTOPPM] [--pdftk PDFTK]
[--qpdf QPDF] [--rmapi RMAPI] [--css CSS] [--font-urls FONT_URLS]
[-C CONFIG] input [input ...]
[--qpdf QPDF] [--css CSS] [--font-urls FONT_URLS] [-C CONFIG]
input [input ...]

Paper2reMarkable version 0.9.1

Expand Down Expand Up @@ -186,7 +183,6 @@ optional arguments:
--pdftoppm PDFTOPPM path to pdftoppm executable (default: pdftoppm)
--pdftk PDFTK path to pdftk executable (default: pdftk)
--qpdf QPDF path to qpdf executable (default: qpdf)
--rmapi RMAPI path to rmapi executable (default: rmapi)
--css CSS path to custom CSS file for HTML output
--font-urls FONT_URLS
path to custom font urls file for HTML output
Expand Down Expand Up @@ -261,32 +257,24 @@ docker build -t p2r .

### Authorization

``paper2remarkable`` uses [rMapi](https://github.com/juruen/rmapi) to sync
``paper2remarkable`` uses [rmapy](https://github.com/subutux/rmapy) to sync
documents to the reMarkable. The first time you run ``paper2remarkable`` you
will have to authenticate rMapi using a one-time code provided by reMarkable.
By default, rMapi uses the ``${HOME}/.rmapi`` file as a configuration file to
store the credentials, and so this is the location we will use in the commands
below. If you'd like to use a different location for the configuration (for
instance, ``${HOME}/.config/rmapi/rmapi.conf``), make sure to change the
commands below accordingly.
By default, ``rmapy`` uses the ``${HOME}/.rmapi`` file as a configuration file
to store the credentials, and so this is the location we will use in the
commands below.

If you already have a `~/.rmapi` file with the authentication details, you can
skip this section. Otherwise we'll create it and run ``rmapi`` in the docker
container for authentication:
skip this section. Otherwise we'll create it and run ``p2r-auth`` in the
docker container for authentication:

```bash
$ touch ${HOME}/.rmapi
$ docker run --rm -i -t -v "${HOME}/.rmapi:/home/user/.rmapi:rw" --entrypoint=rmapi p2r version
$ docker run --rm -i -t -v "${HOME}/.rmapi:/home/user/.rmapi:rw" --entrypoint=p2r-auth p2r version
```

This command will print a link where you can obtain a one-time code to
authenticate rMapi and afterwards print the rMapi version (the version number
may be different):

```bash
ReMarkable Cloud API Shell
rmapi version: 0.0.12
```
authenticate rmapy.

### Usage

Expand Down
3 changes: 1 addition & 2 deletions config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ core:
experimental: true # options: true, false

# System settings are all optional, but can be used if executables are not on
# the PATH. Options in this section include: gs, pdftk, pdftoppm, qpdf, and
# rmapi.
# the PATH. Options in this section include: gs, pdftk, pdftoppm, qpdf
system:
gs: /usr/bin/gs

Expand Down
8 changes: 2 additions & 6 deletions docs/man.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,6 @@ the PATH variable.
--qpdf=QPDF
Path to qpdf executable (default: qpdf). Either pdftk or qpdf is needed.

--rmapi=RMAPI
Path to rmapi executable (default: rmapi).

Developer options:

-d, --debug
Expand Down Expand Up @@ -163,9 +160,8 @@ with the ``--config`` option to the script.
The configuration file consists of three sections: ``core``, ``system``, and
``html``. In the ``core`` section options for cropping, verbosity, and blank
pages can be added, among others. The ``system`` section allows setting paths
to executables such as ``rmapi``, ``pdftk``, etc. Finally, the ``html``
section allows you to provide custom CSS and font urls for formatting the
output of web articles.
to executables such as ``pdftk`` etc. Finally, the ``html`` section allows you
to provide custom CSS and font urls for formatting the output of web articles.

Options provided on the command line overwrite those in the configuration
file. So, for instance, if the configuration file has the setting ``crop:
Expand Down
5 changes: 1 addition & 4 deletions paper2remarkable/providers/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
from ..exceptions import _CalledProcessError
from ..log import Logger
from ..pdf_ops import prepare_pdf, blank_pdf, shrink_pdf
from ..remarkable import upload_to_remarkable
from ..utils import (
assert_file_is_pdf,
check_pdftool,
download_url,
follow_redirects,
upload_to_remarkable,
)
from ._info import Informer

Expand All @@ -42,7 +42,6 @@ def __init__(
crop="left",
blank=False,
remarkable_dir="/",
rmapi_path="rmapi",
pdftoppm_path="pdftoppm",
pdftk_path="pdftk",
qpdf_path="qpdf",
Expand All @@ -55,7 +54,6 @@ def __init__(
self.debug = debug
self.experimental = experimental
self.remarkable_dir = remarkable_dir
self.rmapi_path = rmapi_path
self.pdftoppm_path = pdftoppm_path
self.pdftk_path = pdftk_path
self.qpdf_path = qpdf_path
Expand Down Expand Up @@ -231,7 +229,6 @@ def run(self, src, filename=None):
return upload_to_remarkable(
clean_filename,
remarkable_dir=self.remarkable_dir,
rmapi_path=self.rmapi_path,
)

target_path = os.path.join(self.initial_dir, clean_filename)
Expand Down
126 changes: 126 additions & 0 deletions paper2remarkable/remarkable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# -*- coding: utf-8 -*-

"""Module for dealing with transfers to the reMarkable

Author: G.J.J. van den Burg
License: See LICENSE file
Copyright: 2021, G.J.J. van den Burg

"""

import argparse
import sys

from rmapy.api import Client
from rmapy.document import ZipDocument
from rmapy.exceptions import ApiError, AuthError
from rmapy.folder import Folder

from .exceptions import RemarkableError
from .log import Logger


logger = Logger()


def authenticate_rmapy(token=None):
msg = (
"\n"
"The reMarkable needs to be authenticated before we can upload.\n"
"Please visit:\n\n"
"\thttps://my.remarkable.com/connect/desktop\n"
"\nand copy the one-time code for a desktop application.\n"
)
client = Client()
if not client.is_auth():
if token is None:
print(msg)
token = input("Please enter the one-time code: ")
token = token.strip()
print()
try:
client.register_device(token)
except AuthError:
raise RemarkableError(
"Failed to authenticate the reMarkable client"
)
client.renew_token()
if not client.is_auth():
raise RemarkableError("Failed to authenticate the reMarkable client")
return client


def upload_to_remarkable(filepath, remarkable_dir="/"):
client = authenticate_rmapy()
logger.info("Starting upload to reMarkable")

is_folder = lambda x: isinstance(x, Folder)

remarkable_dir = remarkable_dir.rstrip("/")
if remarkable_dir:
parts = remarkable_dir.split("/")
parent_id = ""

while parts:
rmdir = parts.pop(0)
if not rmdir:
continue

# get the folders with the desired parent
folders = list(filter(is_folder, client.get_meta_items()))
siblings = [f for f in folders if f.Parent == parent_id]

# if the folder already exists, record its ID and continue
match = next(
(f for f in siblings if f.VissibleName == rmdir), None
)
if not match is None:
parent_id = match.ID
continue

# create a new folder with the desired parent
new_folder = Folder(rmdir)
new_folder.Parent = parent_id

try:
client.create_folder(new_folder)
except ApiError:
raise RemarkableError(
f"Creating directory {remarkable_dir} on reMarkable failed"
)
parent_id = new_folder.ID

# upload target is the folder with the last recorded parent_id
target = next(
filter(lambda i: is_folder(i) and i.ID == parent_id),
client.get_meta_items(),
None,
)
if target is None:
raise RemarkableError(
f"Creating directory {remarkable_dir} on reMarkable failed"
)
else:
target = Folder(ID="")

doc = ZipDocument(doc=filepath)
try:
client.upload(zip_doc=doc, to=target)
except ApiError:
raise RemarkableError(
f"Uploading file {filepath} to reMarkable failed"
)
logger.info("Upload successful.")


def auth_cli():
"""Command line interface to authenticate rmapy"""
parser = argparse.ArgumentParser("Authenticate the rmapy client")
parser.add_argument("token", help="Authentication token", nargs="?")
args = parser.parse_args()
try:
authenticate_rmapy(args.token)
except RemarkableError:
print("Authentication failed.", file=sys.stderr)
raise SystemExit(1)
print("Authentication successful.")
7 changes: 0 additions & 7 deletions paper2remarkable/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,6 @@ def build_argument_parser():
help="path to qpdf executable (default: qpdf)",
default=None,
)
parser.add_argument(
"--rmapi",
help="path to rmapi executable (default: rmapi)",
default=None,
)
parser.add_argument(
"--css", help="path to custom CSS file for HTML output", default=None
)
Expand Down Expand Up @@ -249,7 +244,6 @@ def set_path(d, key, value):
set_path(opts["system"], "pdftoppm", args.pdftoppm)
set_path(opts["system"], "pdftk", args.pdftk)
set_path(opts["system"], "qpdf", args.qpdf)
set_path(opts["system"], "rmapi", args.rmapi)

if args.css and os.path.exists(args.css):
with open(args.css, "r") as fp:
Expand Down Expand Up @@ -315,7 +309,6 @@ def main():
crop=options["core"]["crop"],
blank=options["core"]["blank"],
remarkable_dir=args.remarkable_dir,
rmapi_path=options["system"]["rmapi"],
pdftoppm_path=options["system"]["pdftoppm"],
pdftk_path=options["system"]["pdftk"],
qpdf_path=options["system"]["qpdf"],
Expand Down
Loading