Initial commit

This commit is contained in:
Marcus Noble 2020-03-22 13:51:40 +00:00
commit 974fa1660a
13 changed files with 314 additions and 0 deletions

3
.dockerignore Normal file
View File

@ -0,0 +1,3 @@
node_modules
.env
Makefile

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.env
node_modules
.DS_Store

15
Dockerfile Normal file
View File

@ -0,0 +1,15 @@
FROM node:10-alpine
RUN apk add --no-cache chromium nss freetype freetype-dev harfbuzz ca-certificates ttf-freefont
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
ENV CHROMIUM_PATH "/usr/bin/chromium-browser"
WORKDIR /app
ADD package.json .
RUN npm install
ADD . .
CMD npm start

18
Makefile Normal file
View File

@ -0,0 +1,18 @@
.DEFAULT_GOAL := default
IMAGE := docker.cloud.cluster.fun/averagemarcus/website-to-remarkable:latest
.PHONY: docker-build # Build the docker image
docker-build:
@docker build -t $(IMAGE) .
.PHONY: publish # Publish the docker image to the Artifactory registry
publish:
@docker push $(IMAGE)
.PHONY: help # Show this list of commands
help:
@grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1: \2/' | expand -t20
default: format test build

29
failure.html Normal file
View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel='manifest' href='/manifest.json'>
<title>Website to reMarkable</title>
<style>
* {
box-sizing: border-box;
}
body {
font-size: 2em;
display: flex;
flex-direction: column;
justify-content: center;
height: 100vh;
margin: 0;
padding: 0;
text-align: center;
}
</style>
</head>
<body>
<h1>⚠️</h1>
<h2>Failed to send to reMarkable</h2>
</body>
</html>

54
index.html Normal file
View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel='manifest' href='/manifest.json'>
<title>Website to reMarkable</title>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
</script>
<style>
* {
box-sizing: border-box;
}
body {
font-size: 2em;
display: flex;
flex-direction: column;
justify-content: center;
height: 100vh;
margin: 0;
padding: 0;
text-align: center;
}
input, button {
margin: 10px 10px;
font-size: 1.8em;
line-height: 1.5em;
text-align: center;
}
</style>
</head>
<body>
<h1>🚀</h1>
<input type="url" id="URL" />
<button id="sendButton">SEND</button>
<script>
document.getElementById('sendButton').addEventListener('click', () => {
fetch(window.location + `?website=${document.getElementById("URL").value}`)
.then(res => tes.text())
.then(console.log);
})
</script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@pwabuilder/pwainstall"></script>
<pwa-install></pwa-install>
</body>
</html>

100
index.js Normal file
View File

@ -0,0 +1,100 @@
require("dotenv").config();
const http = require('http');
const fs = require("fs");
const puppeteer = require('puppeteer');
const { Remarkable } = require('remarkable-typescript');
const client = new Remarkable({ token: process.env.REMARKABLE_TOKEN });
const server = http.createServer(async (req, res) => {
const incomingURL = new URL(`http://localhost:8000${req.url}`);
if (incomingURL.searchParams.get("website")) {
const website = new URL(incomingURL.searchParams.get("website"));
console.log(`Fetching '${website.toString()}'`);
if (await sendPage(website)) {
fs.readFile(__dirname + "/success.html", function (err,data) {
if (err) {
res.writeHead(404);
res.end(JSON.stringify(err));
return;
}
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(data);
});
} else {
fs.readFile(__dirname + "/failure.html", function (err,data) {
if (err) {
res.writeHead(404);
res.end(JSON.stringify(err));
return;
}
res.writeHead(500, {'Content-Type': 'text/html'});
res.end(data);
});
}
}else {
let url = req.url === "/" ? "/index.html": req.url;
fs.readFile(__dirname + url || "/index.html", function (err,data) {
if (err) {
res.writeHead(404);
res.end(JSON.stringify(err));
return;
}
if (url.endsWith(".js")) {
res.writeHead(200, {'Content-Type': 'application/javascript'});
} else if (url.endsWith(".json")) {
res.writeHead(200, {'Content-Type': 'application/json'});
} else if (url.endsWith(".png")) {
res.writeHead(200, {'Content-Type': 'image/png'});
} else {
res.writeHead(200, {'Content-Type': 'text/html'});
}
res.end(data);
});
}
});
server.listen(8000);
async function sendPage(website, tries = 0) {
const browser = await puppeteer.launch({
executablePath: process.env.CHROMIUM_PATH,
args: ['--disable-dev-shm-usage', '--no-sandbox']
});
try {
const page = await browser.newPage();
await page.emulate(Object.assign({}, puppeteer.devices["iPad Pro"], { userAgent: "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" }));
await page.goto(website.toString());
const title = await page.title()
console.log("Page loaded. Title - " + title)
await page.evaluate( function(){
[...document.querySelectorAll('*')].forEach(node => {
const pos = window.getComputedStyle(node).getPropertyValue("position");
if (pos == "fixed" || pos == "sticky") {
node.style.position = "unset";
}
})
} );
const myPDF = await page.pdf({ format: 'A3' });
console.log("Saved to PDF")
await client.uploadPDF(title, myPDF);
console.log("Uploaded to reMarkable");
return true;
} catch (ex) {
console.log(ex);
if (tries < 5) {
return await sendPage(website, ++tries);
} else {
return false;
}
} finally {
await browser.close();
}
}

26
manifest.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "Website-to-reMarkable",
"short_name": "Website-to-reMarkable",
"description": "Send websites as PDFs to reMarkable",
"theme_color": "#999",
"background_color": "#999",
"display": "standalone",
"scope": "/",
"start_url": "/",
"icons": [
{
"src": "rocket.png",
"sizes": "192x192",
"type": "image/png"
}
],
"splash_pages": null,
"share_target": {
"action": "/",
"method": "GET",
"params": {
"url": "website",
"text": "website"
}
}
}

18
package.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "website-to-remarkable",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"crypto-random-string": "^3.2.0",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"puppeteer": "^2.1.1",
"remarkable-typescript": "^1.0.5"
}
}

BIN
rocket.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

29
success.html Normal file
View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel='manifest' href='/manifest.json'>
<title>Website to reMarkable</title>
<style>
* {
box-sizing: border-box;
}
body {
font-size: 2em;
display: flex;
flex-direction: column;
justify-content: center;
height: 100vh;
margin: 0;
padding: 0;
text-align: center;
}
</style>
</head>
<body>
<h1>🚀</h1>
<h2>Sent to reMarkable</h2>
</body>
</html>

16
sw-toolbox.js Normal file

File diff suppressed because one or more lines are too long

3
sw.js Normal file
View File

@ -0,0 +1,3 @@
importScripts('sw-toolbox.js');
toolbox.precache([]);
toolbox.router.get('/*', toolbox.networkFirst, { networkTimeoutSeconds: 60 });