Skip to content

Commit 3a5ec5d

Browse files
authored
feat: support upload through usb (#155)
1 parent 109d625 commit 3a5ec5d

File tree

3 files changed

+84
-7
lines changed

3 files changed

+84
-7
lines changed

paper2remarkable/providers/_base.py

+15-6
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
from ..utils import check_pdftool
2626
from ..utils import download_url
2727
from ..utils import follow_redirects
28-
from ..utils import upload_to_remarkable
28+
from ..utils import upload_to_remarkable_rmapi
29+
from ..utils import upload_to_remarkable_usb
2930
from ._info import Informer
3031

3132
logger = Logger()
@@ -43,6 +44,7 @@ def __init__(
4344
crop="left",
4445
blank=False,
4546
remarkable_dir="/",
47+
usb_upload=False,
4648
rmapi_path="rmapi",
4749
pdftoppm_path="pdftoppm",
4850
pdftk_path="pdftk",
@@ -57,6 +59,7 @@ def __init__(
5759
self.experimental = experimental
5860
self.remarkable_dir = remarkable_dir
5961
self.rmapi_path = rmapi_path
62+
self.usb_upload = usb_upload
6063
self.pdftoppm_path = pdftoppm_path
6164
self.pdftk_path = pdftk_path
6265
self.qpdf_path = qpdf_path
@@ -231,11 +234,17 @@ def run(self, src, filename=None):
231234
return input()
232235

233236
if self.upload:
234-
return upload_to_remarkable(
235-
clean_filename,
236-
remarkable_dir=self.remarkable_dir,
237-
rmapi_path=self.rmapi_path,
238-
)
237+
if not self.usb_upload:
238+
return upload_to_remarkable_rmapi(
239+
clean_filename,
240+
remarkable_dir=self.remarkable_dir,
241+
rmapi_path=self.rmapi_path,
242+
)
243+
else:
244+
return upload_to_remarkable_usb(
245+
clean_filename,
246+
remarkable_dir=self.remarkable_dir,
247+
)
239248

240249
target_path = os.path.join(self.initial_dir, clean_filename)
241250
while os.path.exists(target_path):

paper2remarkable/ui.py

+7
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ def build_argument_parser():
9090
help="Filename to use for the file on reMarkable",
9191
action="append",
9292
)
93+
parser.add_argument(
94+
"--usb-upload",
95+
help="upload through usb instead of rmapi",
96+
action="store_true",
97+
)
9398
parser.add_argument(
9499
"--gs", help="path to gs executable (default: gs)", default=None
95100
)
@@ -260,6 +265,7 @@ def set_path(d, key, value):
260265
set_bool(opts["core"], "verbose", args.verbose)
261266
set_bool(opts["core"], "upload", args.no_upload, invert=True)
262267
set_bool(opts["core"], "experimental", args.experimental)
268+
set_bool(opts["core"], "usb_upload", args.usb_upload)
263269

264270
if args.center:
265271
opts["core"]["crop"] = "center"
@@ -325,6 +331,7 @@ def runner(inputs, filenames, options, debug=False):
325331
crop=options["core"]["crop"],
326332
blank=options["core"]["blank"],
327333
remarkable_dir=options["core"]["remarkable_dir"],
334+
usb_upload=options["core"]["usb_upload"],
328335
rmapi_path=options["system"]["rmapi"],
329336
pdftoppm_path=options["system"]["pdftoppm"],
330337
pdftk_path=options["system"]["pdftk"],

paper2remarkable/utils.py

+62-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434

3535
HTTP_SERVICE_UNAVAILABLE = 503
3636

37+
REMARKABLE_USB_URL = "http://10.11.99.1"
38+
3739
logger = Logger()
3840

3941

@@ -156,7 +158,9 @@ def follow_redirects(url):
156158
return url, jar
157159

158160

159-
def upload_to_remarkable(filepath, remarkable_dir="/", rmapi_path="rmapi"):
161+
def upload_to_remarkable_rmapi(
162+
filepath, remarkable_dir="/", rmapi_path="rmapi"
163+
):
160164
logger.info("Starting upload to reMarkable")
161165

162166
# Create the reMarkable dir if it doesn't exist
@@ -188,6 +192,63 @@ def upload_to_remarkable(filepath, remarkable_dir="/", rmapi_path="rmapi"):
188192
logger.info("Upload successful.")
189193

190194

195+
def list_usb_dir(guid):
196+
url = REMARKABLE_USB_URL + "/documents/"
197+
if guid:
198+
url += guid + "/"
199+
response = requests.post(url)
200+
return response.json()
201+
202+
203+
def crawl_usb_dirs(remarkable_dir):
204+
parts = [p for p in remarkable_dir.split("/") if p]
205+
guid = None
206+
while len(parts) > 0:
207+
part = parts.pop(0)
208+
data = list_usb_dir(guid)
209+
guid = None
210+
for item in data:
211+
if item["VissibleName"] == part:
212+
guid = item["ID"]
213+
if not guid:
214+
raise RemarkableError(
215+
"Directory %s does not exist on reMarkable" % remarkable_dir
216+
)
217+
# necessary because the last listed dir is the one uploaded to
218+
list_usb_dir(guid)
219+
220+
221+
def upload_to_remarkable_usb(filepath, remarkable_dir="/"):
222+
# https://remarkable.guide/tech/usb-web-interface.html
223+
logger.info("Starting upload to reMarkable")
224+
225+
# Check if the remarkable dir exists (no creation through usb api)
226+
crawl_usb_dirs(remarkable_dir)
227+
228+
headers = {
229+
"Origin": REMARKABLE_USB_URL,
230+
"Accept": "*/*",
231+
"Referer": REMARKABLE_USB_URL + "/",
232+
"Connection": "keep-alive",
233+
}
234+
response = requests.post(
235+
REMARKABLE_USB_URL + "/upload",
236+
files={
237+
"file": (
238+
os.path.basename(filepath),
239+
open(filepath, "rb"),
240+
"application/pdf",
241+
)
242+
},
243+
headers=headers,
244+
)
245+
if not response.ok:
246+
raise RemarkableError(
247+
"Uploading file %s to reMarkable failed" % filepath
248+
)
249+
logger.info("Upload successful.")
250+
251+
191252
def is_url(string):
192253
"""Check if the string is a valid URL
193254

0 commit comments

Comments
 (0)