From 79c8eb5de61c7db030758cbbf86805bc6b62f9cc Mon Sep 17 00:00:00 2001 From: Steffen Wittmeier Date: Fri, 4 Aug 2023 22:32:29 +0200 Subject: [PATCH 1/4] Add upload functionality --- api.py | 35 +++++++++++++++++++++++++++++++++ upload.py | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100755 upload.py diff --git a/api.py b/api.py index 7251ac6..548b2dd 100755 --- a/api.py +++ b/api.py @@ -177,3 +177,38 @@ def findId(filesOrRmFile, fileId): if rmFile.id == fileId: return rmFile return None + +def getRmFileFor(targetFolder): + ''' + Search for given targetFolder on device. + + Returns RmFile of targetFolder if found. Otherwise None. + ''' + files = fetchFileStructure() + for rmFile in iterateAll(files): + if rmFile.path() == targetFolder: + return rmFile + return None + +def navigateTo(targetFolder): + ''' + Navigates to a given folder. + + Raises a RuntimeError in case the given targetFolder cannot be found on the device or is not a folder + ''' + rmFile = getRmFileFor(targetFolder) + if rmFile is None: + raise RuntimeError("Folder {} could not be found on device".format(targetFolder)) + if not rmFile.isFolder: + raise RuntimeError("Given path {} is not a folder on the device".format(targetFolder)) + + requests.post(RM_WEB_UI_URL + "/documents", "/".join(rmFile.id)) + +def upload(file): + ''' + Uploads a file given by the provided file handle to the currently selected folder. + ''' + files = {'file': file} + response = requests.post(RM_WEB_UI_URL + "/upload", files=files) + if not response.ok: + raise RuntimeError('Upload failed with status code %d' % (response.status_code)) diff --git a/upload.py b/upload.py new file mode 100755 index 0000000..9dcb013 --- /dev/null +++ b/upload.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +''' +Upload - Upload either PDFs or ePubs to your remarkable. +''' + + +import api +import argparse + +from sys import stderr + +# ------------------------------ +# Config: +DEBUG = False +# ------------------------------ + +def uploadTo(files, targetFolder): + api.navigateTo(targetFolder) + for file in files: + try: + api.upload(file) + except Exception as ex: + print('ERROR: Failed to upload "%s" to "%s"' % (file, targetFolder)) + raise ex + finally: + file.close() + +def printUsageAndExit(): + print('Usage: %s ' % argv[0], file=stderr) + exit(1) + + +if __name__ == '__main__': + # Argument parsing: + ap = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + + ap.add_argument('-t', '--target-folder', help='Folder on your remarkable to put the uploaded files in') + ap.add_argument('file', type=argparse.FileType('rb'), nargs='+') + + args = ap.parse_args() + + try: + uploadTo(args.file, args.target_folder) + print('Done!') + except KeyboardInterrupt: + print('Cancelled.') + exit(0) + except Exception as ex: + # Error handling: + if DEBUG: + raise ex + exit(1) + else: + print('ERROR: %s' % ex, file=stderr) + print(file=stderr) + print('Please make sure your reMarkable is connected to this PC and you have enabled the USB Webinterface in "Settings -> Storage".', file=stderr) + exit(1) From 8fbce2d9adbc3bd3247ec39357cf503fc0621e45 Mon Sep 17 00:00:00 2001 From: Steffen Wittmeier Date: Sat, 5 Aug 2023 09:06:30 +0200 Subject: [PATCH 2/4] Refactoring and clean-up --- api.py | 10 +++++----- upload.py | 39 ++++++++++++--------------------------- 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/api.py b/api.py index 548b2dd..2ebfaa7 100755 --- a/api.py +++ b/api.py @@ -178,19 +178,19 @@ def findId(filesOrRmFile, fileId): return rmFile return None -def getRmFileFor(targetFolder): +def getRmFileFor(fileOrFolderPath): ''' - Search for given targetFolder on device. + Search for given file or folder and return the corresponding RmFile. - Returns RmFile of targetFolder if found. Otherwise None. + Returns RmFile if file or folder was found. Otherwise None. ''' files = fetchFileStructure() for rmFile in iterateAll(files): - if rmFile.path() == targetFolder: + if rmFile.path() == fileOrFolderPath: return rmFile return None -def navigateTo(targetFolder): +def changeDirectory(targetFolder): ''' Navigates to a given folder. diff --git a/upload.py b/upload.py index 9dcb013..c9ed883 100755 --- a/upload.py +++ b/upload.py @@ -9,29 +9,12 @@ from sys import stderr -# ------------------------------ -# Config: -DEBUG = False -# ------------------------------ - -def uploadTo(files, targetFolder): - api.navigateTo(targetFolder) - for file in files: - try: - api.upload(file) - except Exception as ex: - print('ERROR: Failed to upload "%s" to "%s"' % (file, targetFolder)) - raise ex - finally: - file.close() - def printUsageAndExit(): print('Usage: %s ' % argv[0], file=stderr) exit(1) if __name__ == '__main__': - # Argument parsing: ap = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) @@ -42,18 +25,20 @@ def printUsageAndExit(): args = ap.parse_args() try: - uploadTo(args.file, args.target_folder) + api.changeDirectory(args.targetFolder) + for file in args.file: + print('Uploading {} to {}'.format(file.name, args.targetFolder)) + api.upload(file) + print('Successfully uploaded {} to {}'.format(file.name, args.targetFolder)) print('Done!') except KeyboardInterrupt: print('Cancelled.') exit(0) except Exception as ex: - # Error handling: - if DEBUG: - raise ex - exit(1) - else: - print('ERROR: %s' % ex, file=stderr) - print(file=stderr) - print('Please make sure your reMarkable is connected to this PC and you have enabled the USB Webinterface in "Settings -> Storage".', file=stderr) - exit(1) + print('ERROR: %s' % ex, file=stderr) + print(file=stderr) + print('Please make sure your reMarkable is connected to this PC and you have enabled the USB Webinterface in "Settings -> Storage".', file=stderr) + exit(1) + finally: + for file in args.file: + file.close() From 2ce699362b5170088c7bce096b239f784424a5bd Mon Sep 17 00:00:00 2001 From: Steffen Wittmeier Date: Sun, 6 Aug 2023 12:32:14 +0200 Subject: [PATCH 3/4] Fix uploading bug --- api.py | 2 +- upload.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api.py b/api.py index 2ebfaa7..d7ee633 100755 --- a/api.py +++ b/api.py @@ -202,7 +202,7 @@ def changeDirectory(targetFolder): if not rmFile.isFolder: raise RuntimeError("Given path {} is not a folder on the device".format(targetFolder)) - requests.post(RM_WEB_UI_URL + "/documents", "/".join(rmFile.id)) + requests.post(RM_WEB_UI_URL + "/documents/" + rmFile.id) def upload(file): ''' diff --git a/upload.py b/upload.py index c9ed883..d5f7aa5 100755 --- a/upload.py +++ b/upload.py @@ -25,11 +25,11 @@ def printUsageAndExit(): args = ap.parse_args() try: - api.changeDirectory(args.targetFolder) + api.changeDirectory(args.target_folder) for file in args.file: - print('Uploading {} to {}'.format(file.name, args.targetFolder)) + print('Uploading {} to {}'.format(file.name, args.target_folder)) api.upload(file) - print('Successfully uploaded {} to {}'.format(file.name, args.targetFolder)) + print('Successfully uploaded {} to {}'.format(file.name, args.target_folder)) print('Done!') except KeyboardInterrupt: print('Cancelled.') From 8ee5e1c8d2496e7e7c8cd66a1daef67f024e800b Mon Sep 17 00:00:00 2001 From: Steffen Wittmeier Date: Sun, 6 Aug 2023 12:40:55 +0200 Subject: [PATCH 4/4] Add check for ePub and PDF file type --- upload.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/upload.py b/upload.py index d5f7aa5..cf4b0ff 100755 --- a/upload.py +++ b/upload.py @@ -6,6 +6,7 @@ import api import argparse +import os from sys import stderr @@ -27,6 +28,10 @@ def printUsageAndExit(): try: api.changeDirectory(args.target_folder) for file in args.file: + file_name, file_extension = os.path.splitext(file.name) + if file_extension.lower() not in [".pdf", ".epub"]: + print('Only PDFs and ePubs are supported. Skipping {}'.format(file.name)) + continue print('Uploading {} to {}'.format(file.name, args.target_folder)) api.upload(file) print('Successfully uploaded {} to {}'.format(file.name, args.target_folder))