diff --git a/README.md b/README.md index 4f778d3..f4ece62 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ ![Example article](assets/logo-title.png) -*Remarkable Pocket* synchronizes articles from [Pocket](https://getpocket.com) to your [Remarkable](https://remarkable.com/) tablet. It can be run on your computer or on a server. Because it does not run on the device itself this approach saves battery life, and is resistant to Remarkable software updates. +*Remarkable Pocket* synchronizes articles from [Pocket](https://getpocket.com) to +your [Remarkable](https://remarkable.com/) tablet. It can be run on your computer or on a server. Because it does not +run on the device itself this approach saves battery life, and is resistant to Remarkable software updates. An example run of the program can be found below: @@ -32,28 +34,48 @@ An example run of the program can be found below: ## Features + - **No installation required.** The application can be run with a single command. -- **Works on Remarkable 1 and 2.** +- **Works on Remarkable 1 and 2.** - **Full support for images, code blocks, and formulas.** - **Articles are downloaded as epubs.** This allows you to customize the font, font size, margins, etc. -- **Automatically archive read articles on Pocket.** When you finish reading an article and close it while on the last page, it will be automatically deleted from the Remarkable and archived on Pocket. A new unread article will be downloaded in its place. -- **Download articles from Pocket with a given tag.** If a `tag-filter` (see [Configuration](#configuration)) is supplied then only articles with that tag will be downloaded. +- **Automatically archive read articles on Pocket.** When you finish reading an article and close it while on the last + page, it will be automatically deleted from the Remarkable and archived on Pocket. A new unread article will be + downloaded in its place. +- **Download articles from Pocket with a given tag.** If a `tag-filter` (see [Configuration](#configuration)) is + supplied then only articles with that tag will be downloaded. ## Usage -The easiest way to run the application is using Docker. First install Docker for your platform from https://docs.docker.com/get-docker/. Then run the following command to start the application on Linux or Mac (I have not tested it on Windows yet): + +The easiest way to run the application is using Docker. First install Docker for your platform +from https://docs.docker.com/get-docker/. Then run the following command to start the application on Linux or Mac (I +have not tested it on Windows yet): ``` -touch ~/.remarkable-pocket ~/.rmapi && mkdir -p ~/.rmapi-cache && docker run -it --env TZ=Europe/Amsterdam -p 65112:65112 -v ~/.remarkable-pocket:/root/.remarkable-pocket -v ~/.rmapi:/root/.rmapi -v ~/.rmapi-cache:/root/.cache/rmapi ghcr.io/nov1n/remarkable-pocket:0.1.0 +touch ~/.remarkable-pocket ~/.rmapi && mkdir -p ~/.rmapi-cache && docker run -it --env TZ=Europe/Amsterdam -p 65112:65112 -v ~/.remarkable-pocket:/root/.remarkable-pocket -v ~/.rmapi:/root/.rmapi -v ~/.rmapi-cache:/root/.cache/rmapi ghcr.io/nov1n/remarkable-pocket:0.2.0 ``` -The first time you run the application, you will be asked to authorize Pocket and Remarkable Cloud. Once you have done this subsequent runs will read the credentials from the `~/.remarkable-pocket` and `~/.rmapi` file. You can also change the timezone in the command to match your location. + +The first time you run the application, you will be asked to authorize Pocket and Remarkable Cloud. Once you have done +this subsequent runs will read the credentials from the `~/.remarkable-pocket` and `~/.rmapi` file. You can also change +the timezone in the command to match your location. By default, articles are synchronized to the `/Pocket/` directory on the Remarkable every 60 minutes. -*TIP:* If you want to launch the program on startup and keep it running in the background you can use *launchd* (on Mac) or *systemd* (on Linux). On Mac copy [this](nl.carosi.remarkable-pocket.plist) file to `~/Library/LaunchAgents/` followed by: `launchctl load -w nl.carosi.remarkable-pocket.plist`. Logs will be sent to `~/.remarkable-pocket.log`. +*TIP:* If you want to launch the program on startup and keep it running in the background you can use *launchd* (on Mac) +or *systemd* (on Linux). On Mac copy [this](nl.carosi.remarkable-pocket.plist) file to `~/Library/LaunchAgents/` +followed by: `launchctl load -w nl.carosi.remarkable-pocket.plist`. Logs will be sent to `~/.remarkable-pocket.log`. +## Raspberry Pi + +There is also a Docker image available for the Raspberry Pi, so the command in [Usage](#usage) will work. You do need a +browser to complete the authentication flow. If your Pi runs without a screen I recommend using a VNC client when +running the program for the first time. ## Configuration -The default configuration can be changed by providing command-line arguments. Simply append these to the `docker run` command. Below is a list of all available options. + +The default configuration can be changed by providing command-line arguments. Simply append these to the `docker run` +command. Below is a list of all available options. + ``` Usage: remarkable-pocket [-hnorV] [-d=] [-f=] [-i=] [-l=] Synchronizes articles from Pocket to the Remarkable tablet. @@ -76,28 +98,45 @@ Synchronizes articles from Pocket to the Remarkable tablet. ``` ## Limitations + - Articles behind a paywall cannot be downloaded. -- Articles on websites with sophisticated DDOS protection cannot be downloaded. +- Articles on websites with DDOS protection or captcha cannot be downloaded. - Articles that use javascript to load the content cannot be downloaded. ## Build Instructions -The project uses Gradle as a build tool and can be built using the `gradle build` command. This will generate jars in `build/libs/`. To run the jar, use the `java -jar build/libs/remarkable-pocket-x.x.x.jar` command, replacing `x.x.x` with the correct version. + +The project uses Gradle as a build tool and can be built using the `gradle build` command. This will generate jars +in `build/libs/`. To run the jar, use the `java -jar build/libs/remarkable-pocket-x.x.x.jar` command, replacing `x.x.x` +with the correct version. ### Building docker -To build the docker image run `gradle jib`. This will use a dynamically generated Dockerfile based on the configuration in the `jib` section of the `build.gradle` file. To run the image, see the command in the [Usage](#usage) section. + +To build the docker image run `gradle jib`. This will use a dynamically generated Dockerfile based on the configuration +in the `jib` section of the `build.gradle` file. To run the image, see the command in the [Usage](#usage) section. ### Other package formats -If you would like to package the application in another format e.g. `deb`, `nix` or `AUR`, I'm happy to review a Pull Request. + +If you would like to package the application in another format e.g. `deb`, `nix` or `AUR`, I'm happy to review a Pull +Request. ### Disclaimer -RemarkablePocket uses rmapi to connect to Remarkable cloud. The newly released sync protocol is not yet tested through and may contain bugs. As [recommended](https://github.com/juruen/rmapi#warning-experimental-support-for-the-new-sync-protocol) by rmapi please make sure you have a backup of your files. + +RemarkablePocket uses rmapi to connect to Remarkable cloud. The newly released sync protocol is not yet tested through +and may contain bugs. +As [recommended](https://github.com/juruen/rmapi#warning-experimental-support-for-the-new-sync-protocol) by rmapi please +make sure you have a backup of your files. ## Thanks + - https://epub.press/ for providing a free epub generator API. Consider donating to support this project. - https://github.com/juruen/rmapi for providing a client for the Remarkable Cloud. ## Support -[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/nov1n) if you want to say thanks. :-) + +[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/nov1n) +if you want to say thanks. :-) ## Disclaimer -The author(s) and contributor(s) are not associated with reMarkable AS, Norway. reMarkable is a registered trademark of reMarkable AS in some countries. Please see https://remarkable.com for their product. + +The author(s) and contributor(s) are not associated with reMarkable AS, Norway. reMarkable is a registered trademark of +reMarkable AS in some countries. Please see https://remarkable.com for their product. diff --git a/build.gradle b/build.gradle index ad19899..24cd7ed 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,13 @@ plugins { id "org.springframework.boot" version "2.5.5" id "io.spring.dependency-management" version "1.0.11.RELEASE" - id "com.google.cloud.tools.jib" version "3.1.4" + id "com.google.cloud.tools.jib" version "3.3.0" id "com.github.johnrengelman.shadow" version "7.1.0" id "java" } group = "nl.carosi" -version = "0.1.0" +version = "0.2.0" java { toolchain { @@ -17,11 +17,21 @@ java { jib { from { - image = "azul/zulu-openjdk:17-jre-headless" + image = "eclipse-temurin:17-jre" + platforms { + platform { + architecture = 'amd64' + os = 'linux' + } + platform { + architecture = 'arm' + os = 'linux' + } + } + } container { jvmFlags = ["-Xshare:auto", "-XX:TieredStopAtLevel=1", "-XX:CICompilerCount=1", "-XX:+UseSerialGC", "-Xmx512m"] - format = "OCI" } to { image = "ghcr.io/nov1n/remarkable-pocket" @@ -30,7 +40,7 @@ jib { extraDirectories { paths = 'src/main/jib' permissions = [ - '/usr/local/bin/rmapi': '755' + '/usr/local/bin/rmapi*': '755' ] } } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ecadbec --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +version: '3' + +services: + remarkable-pocket: + image: ghcr.io/nov1n/remarkable-pocket:0.2.0 + restart: always + ports: + - 65112:65112 + volumes: + - ~/.remarkable-pocket:/root/.remarkable-pocket + - ~/.rmapi:/root/.rmapi + - ~/.rmapi-cache:/root/.cache/rmapi diff --git a/nl.carosi.remarkable-pocket.plist b/nl.carosi.remarkable-pocket.plist index 4c258e9..d8ee6b9 100644 --- a/nl.carosi.remarkable-pocket.plist +++ b/nl.carosi.remarkable-pocket.plist @@ -8,7 +8,7 @@ /bin/sh -c - while ! /usr/local/bin/docker version > /dev/null 2>&1; do sleep 5; done && /usr/local/bin/docker run --env TZ=Europe/Amsterdam -v ~/.remarkable-pocket:/root/.remarkable-pocket -v ~/.rmapi:/root/.rmapi -v ~/.rmapi-cache:/root/.cache/rmapi ghcr.io/nov1n/remarkable-pocket:0.1.0 1>>$HOME/.remarkable-pocket.log 2>&1 + while ! /usr/local/bin/docker version > /dev/null 2>&1; do sleep 5; done && /usr/local/bin/docker run --env TZ=Europe/Amsterdam -v ~/.remarkable-pocket:/root/.remarkable-pocket -v ~/.rmapi:/root/.rmapi -v ~/.rmapi-cache:/root/.cache/rmapi ghcr.io/nov1n/remarkable-pocket:0.2.0 1>>$HOME/.remarkable-pocket.log 2>&1 RunAtLoad diff --git a/src/main/java/nl/carosi/remarkablepocket/PocketAuthenticator.java b/src/main/java/nl/carosi/remarkablepocket/PocketAuthenticator.java index d28a0e8..6827fd3 100644 --- a/src/main/java/nl/carosi/remarkablepocket/PocketAuthenticator.java +++ b/src/main/java/nl/carosi/remarkablepocket/PocketAuthenticator.java @@ -84,13 +84,15 @@ public class PocketAuthenticator { String authUrl = factory.getAuthUrl(); LOG.info("Visit {} and authorize this application.\n", authUrl); try { - boolean terminated = execService.awaitTermination(1, MINUTES); + boolean terminated = execService.awaitTermination(5, MINUTES); if (!terminated) { throw new InterruptedException(); } } catch (InterruptedException e) { LOG.info("Pocket authorization timed out. Please try again."); - System.exit(1); + // System.exit doesn't work here. I suspect there is a deadlock in the + // 'logStream' method where it blocks on stdin. + Runtime.getRuntime().halt(1); } return factory.create(); diff --git a/src/main/java/nl/carosi/remarkablepocket/RemarkableApi.java b/src/main/java/nl/carosi/remarkablepocket/RemarkableApi.java index f109ba1..fd5065e 100644 --- a/src/main/java/nl/carosi/remarkablepocket/RemarkableApi.java +++ b/src/main/java/nl/carosi/remarkablepocket/RemarkableApi.java @@ -31,7 +31,11 @@ public class RemarkableApi { "WARNING!!!", " Using the new 1.5 sync", " Make sure you have a backup"); - private static final String RMAPI_EXECUTABLE = "/usr/local/bin/rmapi"; + private static final String RMAPI_EXECUTABLE = + "/usr/local/bin/rmapi" + + (new File("/.dockerenv").exists() + ? ("_" + System.getProperty("os.arch")) + : ""); private final String rmStorageDir; private final ObjectMapper objectMapper; diff --git a/src/main/java/nl/carosi/remarkablepocket/SyncCommand.java b/src/main/java/nl/carosi/remarkablepocket/SyncCommand.java index 12a8948..6a546ac 100644 --- a/src/main/java/nl/carosi/remarkablepocket/SyncCommand.java +++ b/src/main/java/nl/carosi/remarkablepocket/SyncCommand.java @@ -19,7 +19,7 @@ import picocli.CommandLine.Option; sortOptions = false, usageHelpAutoWidth = true, // TODO: Read from gradle.properties - version = "0.1.0", + version = "0.2.0", mixinStandardHelpOptions = true) class SyncCommand implements Callable { @Option( @@ -97,6 +97,9 @@ class SyncCommand implements Callable { public Integer call() { ensureConnected(System.err::println); + // Handle sigterm (^C) + Runtime.getRuntime().addShutdownHook(new Thread(() -> Runtime.getRuntime().halt(1))); + Map cliProperties = Map.ofEntries( entry("pocket.archive-read", Boolean.toString(!noArchive)), diff --git a/src/main/jib/usr/local/bin/rmapi b/src/main/jib/usr/local/bin/rmapi_amd64 similarity index 100% rename from src/main/jib/usr/local/bin/rmapi rename to src/main/jib/usr/local/bin/rmapi_amd64 diff --git a/src/main/jib/usr/local/bin/rmapi_arm b/src/main/jib/usr/local/bin/rmapi_arm new file mode 100755 index 0000000..0c1477e Binary files /dev/null and b/src/main/jib/usr/local/bin/rmapi_arm differ