reorganize file structure, add sending urls
This commit is contained in:
41
static/common.js
Normal file
41
static/common.js
Normal file
@@ -0,0 +1,41 @@
|
||||
'use strict'
|
||||
|
||||
function log(str) {
|
||||
var node = document.createElement("div")
|
||||
node.textContent = str
|
||||
logs.appendChild(node)
|
||||
}
|
||||
window.addEventListener("error", function (event) {
|
||||
log(event.filename + ":" + event.lineno + " " + event.message)
|
||||
}, false)
|
||||
|
||||
function xhr(method, url, cb) {
|
||||
var x = new XMLHttpRequest()
|
||||
x.onload = function () {
|
||||
cb(x)
|
||||
}
|
||||
x.onerror = function () {
|
||||
cb(x)
|
||||
}
|
||||
x.open(method, url, true)
|
||||
x.send(null)
|
||||
}
|
||||
|
||||
var isIOS = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
|
||||
|
||||
function getCookies() {
|
||||
var cookieRegex = /([\w\.]+)\s*=\s*(?:"((?:\\"|[^"])*)"|(.*?))\s*(?:[;,]|$)/g
|
||||
var cookies = {}
|
||||
var match
|
||||
while( (match = cookieRegex.exec(document.cookie)) !== null ) {
|
||||
var value = match[2] || match[3]
|
||||
cookies[match[1]] = decodeURIComponent(value)
|
||||
try {
|
||||
cookies[match[1]] = JSON.parse(cookies[match[1]])
|
||||
} catch (err) {}
|
||||
}
|
||||
return cookies
|
||||
}
|
||||
// function deleteCookie(name) {
|
||||
// document.cookie = name + "= ; expires = Thu, 01 Jan 1970 00:00:00 GMT"
|
||||
// }
|
||||
137
static/download.html
Normal file
137
static/download.html
Normal file
@@ -0,0 +1,137 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Send to Kobo/Kindle</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="stylesheet" type="text/css" href="./style.css?s"/>
|
||||
<script src="./common.js?a"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<h1 class="center">Send to Kobo/Kindle</h1>
|
||||
|
||||
<div class="center">
|
||||
<div style="font-size: 1.3em;">Unique key:</div>
|
||||
<div id="key">
|
||||
<span id="keyoutput">––––</span>
|
||||
<button id="keygen" title="Generate new key">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M19.146 4.854l-1.489 1.489A8 8 0 1 0 12 20a8.094 8.094 0 0 0 7.371-4.886 1 1 0 1 0-1.842-.779A6.071 6.071 0 0 1 12 18a6 6 0 1 1 4.243-10.243l-1.39 1.39a.5.5 0 0 0 .354.854H19.5A.5.5 0 0 0 20 9.5V5.207a.5.5 0 0 0-.854-.353z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="center">
|
||||
<div id="downloads">
|
||||
<br/>
|
||||
<div style="font-size: 1.3em;"><em>Downloads</em></div>
|
||||
<a id="downloadlink" class="downloadlink"></a>
|
||||
</div>
|
||||
<div id="urls"></div>
|
||||
<br/>
|
||||
</div>
|
||||
<hr/>
|
||||
<br/>
|
||||
<div class="center">
|
||||
Visit this on other devices to send ebooks to this ereader:<br/><a id="siteurl">https://send.djazz.se</a><br/>
|
||||
<br/>
|
||||
Created by djazz. Source code on <a href="https://github.com/daniel-j/send2ereader" target="_blank">Github</a><br/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="logs"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var keyOutput = document.getElementById('keyoutput')
|
||||
var keyGenBtn = document.getElementById('keygen')
|
||||
var downloads = document.getElementById('downloads')
|
||||
var downloadlink = document.getElementById('downloadlink')
|
||||
var urlsnode = document.getElementById('urls')
|
||||
var siteurl = document.getElementById('siteurl')
|
||||
var key = null
|
||||
var pollTimer = null
|
||||
|
||||
function pollFile () {
|
||||
if (!key) {
|
||||
keyOutput.textContent = '––––'
|
||||
downloads.style.display = 'none'
|
||||
return
|
||||
}
|
||||
xhr('GET', './status/' + key, function (x) {
|
||||
var data
|
||||
try {
|
||||
data = JSON.parse(x.responseText)
|
||||
} catch (err) {
|
||||
key = null
|
||||
keyOutput.textContent = '––––'
|
||||
downloads.style.display = 'none'
|
||||
return
|
||||
}
|
||||
if (data.error) {
|
||||
if (pollTimer) clearInterval(pollTimer)
|
||||
key = null
|
||||
keyOutput.textContent = '––––'
|
||||
downloads.style.display = 'none'
|
||||
}
|
||||
var urls = data.urls && data.urls.length > 0 ? data.urls : null
|
||||
if (data.file || urls) {
|
||||
} else {
|
||||
}
|
||||
if (data.file) {
|
||||
downloads.style.display = 'block'
|
||||
downloadlink.textContent = data.file.name
|
||||
} else {
|
||||
downloads.style.display = 'none'
|
||||
}
|
||||
if (urls) {
|
||||
urlsnode.innerHTML = '<br/><div style="font-size: 1.3em;"><em>Urls</em></div>'
|
||||
urls.forEach(function (url) {
|
||||
var urllink = document.createElement("a")
|
||||
urllink.href = url
|
||||
urllink.target = '_blank'
|
||||
urllink.textContent = url
|
||||
urllink.className = "downloadlink"
|
||||
var div = document.createElement('div')
|
||||
div.appendChild(urllink)
|
||||
urlsnode.appendChild(div)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function generateKey () {
|
||||
keyOutput.textContent = '––––'
|
||||
if (pollTimer) clearInterval(pollTimer)
|
||||
downloads.style.display = 'none'
|
||||
xhr('POST', './generate', function (x) {
|
||||
if (x.responseText !== 'error' && x.status === 200) {
|
||||
key = x.responseText
|
||||
keyOutput.textContent = key
|
||||
downloadlink.href = './download/' + key
|
||||
if (pollTimer) clearInterval(pollTimer)
|
||||
pollTimer = setInterval(pollFile, 5 * 1000)
|
||||
} else {
|
||||
key = null
|
||||
keyOutput.textContent = '––––'
|
||||
downloadlink.href = ''
|
||||
}
|
||||
keyGenBtn.blur()
|
||||
})
|
||||
}
|
||||
|
||||
window.onload = function () {
|
||||
keyGenBtn.onclick = generateKey
|
||||
generateKey()
|
||||
}
|
||||
|
||||
siteurl.textContent = window.location.href
|
||||
siteurl.href = siteurl.textContent
|
||||
siteurl.target = '_self'
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
124
static/style.css
Normal file
124
static/style.css
Normal file
@@ -0,0 +1,124 @@
|
||||
|
||||
body {
|
||||
line-height: 1.3;
|
||||
font-family: serif;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
max-width: 650px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
#keyoutput {
|
||||
font-size: 5em;
|
||||
display: inline-block;
|
||||
letter-spacing: 0.2em;
|
||||
margin: 10px 0;
|
||||
font-family: monospace, sans-serif;
|
||||
white-space: nowrap;
|
||||
text-transform: uppercase;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.right {
|
||||
text-align: right;
|
||||
}
|
||||
#keygen, .downloadlink, #choosebtn, input[type="submit"] {
|
||||
background: #CCC;
|
||||
border: none;
|
||||
color: black;
|
||||
font-style: italic;
|
||||
padding: 0.6em 1.3em;
|
||||
line-height: 1.6;
|
||||
display: inline-block;
|
||||
font-family: inherit;
|
||||
font-size: 1.2em;
|
||||
margin: 0 0.5em;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
font-weight: normal;
|
||||
}
|
||||
#keygen {
|
||||
margin-left: 1em;
|
||||
margin-right: -2em;
|
||||
padding: 1em;
|
||||
}
|
||||
#keygen:focus, #downloadlink:focus, #choosebtn:focus, input[type="submit"]:focus,
|
||||
#keygen:active, #downloadlink:active, #choosebtn:active, input[type="submit"]:active {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
#keygen svg {
|
||||
display: block;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
}
|
||||
.downloadlink {
|
||||
margin: 0.5em;
|
||||
}
|
||||
#downloads {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#keyinput {
|
||||
font-size: 4em;
|
||||
width: 3.5em;
|
||||
text-align: center;
|
||||
font-family: monospace;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
input[type="url"], input[type="text"] {
|
||||
background: white;
|
||||
border: 1px solid #AAA;
|
||||
padding: 5px;
|
||||
font-family: serif;
|
||||
}
|
||||
|
||||
#uploadstatus {
|
||||
opacity: 0;
|
||||
transition: opacity .5s ease-in-out;
|
||||
padding: 10px;
|
||||
margin-top: 20px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
#uploadstatus.success {
|
||||
background-color: #DFD;
|
||||
border: 1px solid #7F7;
|
||||
}
|
||||
|
||||
#uploadstatus.error {
|
||||
background-color: #FDD;
|
||||
border: 1px solid #F77;
|
||||
}
|
||||
td {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
td.aligntop {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#fileinput {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#fileinfo {
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
width: 18em;
|
||||
}
|
||||
124
static/upload.html
Normal file
124
static/upload.html
Normal file
@@ -0,0 +1,124 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Send to Kobo/Kindle</title>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="stylesheet" type="text/css" href="./style.css?o"/>
|
||||
<script src="./common.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<h1 class="center">Send to Kobo/Kindle</h1>
|
||||
|
||||
<form action="./upload" method="post" enctype="multipart/form-data">
|
||||
<table style="margin: 0 auto;" cellpadding=0 cellspacing=0>
|
||||
<tr><td class="right"><label for="keyinput"><strong>Unique key</strong></label></td><td><input type="text" name="key" id="keyinput" autocomplete="off" pattern="...." placeholder="––––" required style="text-transform: uppercase;" maxlength=4/></td></tr>
|
||||
<tr><td class="right aligntop"><label for="fileinput"><strong>Ebook file</strong></label><br/><em>EPUB, MOBI, PDF,<br/>TXT, CBZ, CBR</em></td><td class="aligntop"><label for="fileinput" id="choosebtn">Choose file</label><input type="file" name="file" id="fileinput" accept=".txt,.epub,.mobi,.pdf,.cbz,.cbr,application/epub+zip,application/x-mobipocket-ebook,application/pdf,application/vnd.comicbook+zip,application/vnd.comicbook-rar"/><br/><br/><div id="fileinfo"></div></td></tr>
|
||||
<tr><td class="right"><label for="urlinput"><strong>Send url</strong></label></td><td><input type="url" name="url" id="urlinput" autocomplete="off" style="width: 100%"></td></tr>
|
||||
<tr><td class="right"><label for="kepubify"><strong>Kepubify</strong><br/><em>Kobo only</em></label></td><td><input type="checkbox" name="kepubify" id="kepubify" checked /></td></tr>
|
||||
<tr class="center"><td colspan="2"><input type="submit" value="Upload and send" /></td></tr>
|
||||
</table>
|
||||
<div id="uploadstatus"></div>
|
||||
</form>
|
||||
<div style="padding: 15px; padding-top: 0; text-align: justify;">
|
||||
<p>Go this this page on your Kobo/Kindle ereader and you see a unique key. Enter it in this form and upload an ebook and it will appear as a download link on the ereader.</p>
|
||||
<p>If you send an EPUB file to to a Kindle it will be converted to MOBI with KindleGen. If you send a MOBI file to a Kindle it will be sent unprocessed. If you send an EPUB file and tick the Kepubify checkbox, it will be converted into a Kobo EPUB using Kepubify. If you send a MOBI file to a Kobo, it will not be converted.</p>
|
||||
<p>Your ebook will be stored on the server as long as your Kobo/Kindle is viewing the unique key and is connected to wifi. It will be deleted irrevocably when the key expires about 30 seconds after you close the browser, generate a new key or disable wifi on your ereader.</p>
|
||||
<p>By using this tool you agree that the ebook you upload is processed on the server and stored for a short time.</p>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="center">
|
||||
Created by djazz. Powered by <a href="https://koajs.com/" target="_blank">Koa</a>, <a href="https://pgaskin.net/kepubify/" target="_blank">Kepubify</a> and <a href="https://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211" target="_blank">KindleGen</a><br/>
|
||||
Source code on <a href="https://github.com/daniel-j/send2ereader" target="_blank">Github</a> - <a id="siteurl">https://send.djazz.se</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="logs"></div>
|
||||
|
||||
<script>
|
||||
var uploadstatus = document.getElementById('uploadstatus')
|
||||
var keyinput = document.getElementById('keyinput')
|
||||
var fileinput = document.getElementById('fileinput')
|
||||
var fileinputAccept = fileinput.accept.split(',') // cache it
|
||||
var fileinfo = document.getElementById('fileinfo')
|
||||
var urlinput = document.getElementById('urlinput')
|
||||
var siteurl = document.getElementById('siteurl')
|
||||
|
||||
var flash = getCookies().flash
|
||||
// deleteCookie('flash')
|
||||
|
||||
if (flash) {
|
||||
if (flash.message) {
|
||||
if (flash.success) {
|
||||
uploadstatus.className = " success"
|
||||
uploadstatus.innerHTML = flash.message
|
||||
} else {
|
||||
uploadstatus.className = " error"
|
||||
uploadstatus.textContent = flash.message
|
||||
}
|
||||
uploadstatus.style.opacity = 1
|
||||
}
|
||||
keyinput.value = flash.key || ''
|
||||
urlinput.value = flash.url || ''
|
||||
}
|
||||
uploadstatus.addEventListener('click', function () {
|
||||
uploadstatus.style.opacity = 0
|
||||
setTimeout(function () {
|
||||
uploadstatus.textContent = ''
|
||||
uploadstatus.className = ''
|
||||
}, 500)
|
||||
}, false)
|
||||
|
||||
document.body.addEventListener("drop", function () {
|
||||
log("file dropped")
|
||||
}, false)
|
||||
|
||||
function fileinputChange () {
|
||||
if (!fileinput.files[0] || fileinput.files.length === 0) {
|
||||
fileinfo.textContent = ''
|
||||
fileinput.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
var filename = fileinput.files[0].name
|
||||
var type = fileinput.files[0].type
|
||||
var found = false
|
||||
for (var i = 0; i < fileinputAccept.length; i++) {
|
||||
var item = fileinputAccept[i]
|
||||
if (item.length > 1 && item[0] == '.') {
|
||||
if (filename.toLowerCase().endsWith(item.toLowerCase())) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
} else if (type == item) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
alert("Invalid file: " + filename + "\nPlease select another file.")
|
||||
fileinfo.textContent = ''
|
||||
fileinput.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
fileinfo.innerHTML = filename + "<br/>Filesize: " + Math.ceil(fileinput.files[0].size / 1024) + ' kB'
|
||||
}
|
||||
|
||||
fileinput.addEventListener('change', fileinputChange, false)
|
||||
fileinputChange()
|
||||
|
||||
if (isIOS) {
|
||||
// Can't accept .mobi files otherwise (iOS thinks it's an unknown type)
|
||||
fileinput.accept = ''
|
||||
}
|
||||
|
||||
siteurl.textContent = window.location.href
|
||||
siteurl.href = siteurl.textContent
|
||||
siteurl.target = '_self'
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user