Merge branch 'master' into fix/chinese-book-download-failed
This commit is contained in:
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.git
|
||||||
|
/uploads
|
||||||
|
/node_modules
|
||||||
32
Dockerfile
Normal file
32
Dockerfile
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
FROM node:20
|
||||||
|
|
||||||
|
# Create app directory
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
# Copy files needed by npm install
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install app dependencies
|
||||||
|
RUN npm install --omit=dev
|
||||||
|
|
||||||
|
# Download and install kepubify
|
||||||
|
RUN wget https://github.com/pgaskin/kepubify/releases/download/v4.0.4/kepubify-linux-64bit && \
|
||||||
|
mv kepubify-linux-64bit /usr/local/bin/kepubify && \
|
||||||
|
chmod +x /usr/local/bin/kepubify
|
||||||
|
|
||||||
|
# Download and install kindlegen
|
||||||
|
RUN wget https://archive.org/download/kindlegen2.9/kindlegen_linux_2.6_i386_v2_9.tar.gz && \
|
||||||
|
mkdir kindlegen && \
|
||||||
|
tar xvf kindlegen_linux_2.6_i386_v2_9.tar.gz --directory kindlegen && \
|
||||||
|
cp kindlegen/kindlegen /usr/local/bin/kindlegen && \
|
||||||
|
chmod +x /usr/local/bin/kindlegen && \
|
||||||
|
rm -rf kindlegen
|
||||||
|
|
||||||
|
# Copy the rest of the app files (see .dockerignore)
|
||||||
|
COPY . ./
|
||||||
|
|
||||||
|
# Create uploads directory if it doesn't exist
|
||||||
|
RUN mkdir uploads
|
||||||
|
|
||||||
|
EXPOSE 3001
|
||||||
|
CMD [ "npm", "start" ]
|
||||||
19
README.md
Normal file
19
README.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# send2ereader
|
||||||
|
|
||||||
|
A self hostable service for sending ebooks to a Kobo or Kindle ereader through the built-in browser.
|
||||||
|
|
||||||
|
## How To Run
|
||||||
|
|
||||||
|
### On Your Host OS
|
||||||
|
|
||||||
|
1. Have Node.js 16 or 20 installed
|
||||||
|
2. Install this service's dependencies by running `$ npm install`
|
||||||
|
3. Install [Kepubify](https://github.com/pgaskin/kepubify), and have the kepubify executable in your PATH.
|
||||||
|
4. Install [KindleGen](https://archive.org/details/kindlegen2.9), and have the kindlegen executable in your PATH.
|
||||||
|
5. Start this service by running: `$ npm start` and access it on HTTP port 3001
|
||||||
|
|
||||||
|
### Containerized
|
||||||
|
|
||||||
|
1. Have Docker installed
|
||||||
|
2. Run `$ docker compose up`
|
||||||
|
3. Access the service on HTTP port 3001
|
||||||
9
docker-compose.yaml
Normal file
9
docker-compose.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
send2ereader:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./Dockerfile
|
||||||
|
container_name: send2ereader
|
||||||
|
ports:
|
||||||
|
- 3001:3001
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<title>Send to Kobo/Kindle</title>
|
<title>Send to Kobo/Kindle</title>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<link rel="stylesheet" type="text/css" href="/style.css"/>
|
<link rel="stylesheet" type="text/css" href="/style.css?o"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -12,25 +12,40 @@
|
|||||||
<h1 class="center">Send to Kobo/Kindle</h1>
|
<h1 class="center">Send to Kobo/Kindle</h1>
|
||||||
|
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<div style="font-size: 1.4em;">Unique key:</div>
|
<div style="font-size: 1.3em;">Unique key:</div>
|
||||||
<div id="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>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="center" id="downloads">
|
||||||
|
<div style="font-size: 1.3em;"><em>Downloads</em></div>
|
||||||
|
<br/>
|
||||||
|
<div><a id="downloadlink"></a></div>
|
||||||
<br/>
|
<br/>
|
||||||
<button id="keygen">Generate new key</button>
|
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
|
||||||
<hr/>
|
<hr/>
|
||||||
<br/>
|
<br/>
|
||||||
<div class="center" id="downloads">
|
<div class="center">
|
||||||
<a id="downloadlink"></a>
|
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>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
var keyOutput = document.getElementById('key')
|
var keyOutput = document.getElementById('keyoutput')
|
||||||
var keyGenBtn = document.getElementById('keygen')
|
var keyGenBtn = document.getElementById('keygen')
|
||||||
var downloads = document.getElementById('downloads')
|
var downloads = document.getElementById('downloads')
|
||||||
var downloadlink = document.getElementById('downloadlink')
|
var downloadlink = document.getElementById('downloadlink')
|
||||||
|
var siteurl = document.getElementById('siteurl')
|
||||||
var key = null
|
var key = null
|
||||||
var pollTimer = null
|
var pollTimer = null
|
||||||
|
|
||||||
@@ -47,13 +62,18 @@ function xhr(method, url, cb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function pollFile () {
|
function pollFile () {
|
||||||
|
if (!key) {
|
||||||
|
keyOutput.textContent = '––––'
|
||||||
|
downloads.style.display = 'none'
|
||||||
|
return
|
||||||
|
}
|
||||||
xhr('GET', '/status/' + key, function (x) {
|
xhr('GET', '/status/' + key, function (x) {
|
||||||
var data
|
var data
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(x.responseText)
|
data = JSON.parse(x.responseText)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
keyOutput.textContent = '––––'
|
|
||||||
key = null
|
key = null
|
||||||
|
keyOutput.textContent = '––––'
|
||||||
downloads.style.display = 'none'
|
downloads.style.display = 'none'
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -84,8 +104,8 @@ function generateKey () {
|
|||||||
if (pollTimer) clearInterval(pollTimer)
|
if (pollTimer) clearInterval(pollTimer)
|
||||||
pollTimer = setInterval(pollFile, 5 * 1000)
|
pollTimer = setInterval(pollFile, 5 * 1000)
|
||||||
} else {
|
} else {
|
||||||
keyOutput.textContent = '––––'
|
|
||||||
key = null
|
key = null
|
||||||
|
keyOutput.textContent = '––––'
|
||||||
downloadlink.href = ''
|
downloadlink.href = ''
|
||||||
}
|
}
|
||||||
keyGenBtn.blur()
|
keyGenBtn.blur()
|
||||||
@@ -97,6 +117,10 @@ window.onload = function () {
|
|||||||
generateKey()
|
generateKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
siteurl.textContent = window.location.origin
|
||||||
|
siteurl.href = siteurl.textContent
|
||||||
|
siteurl.target = '_self'
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
22
index.js
22
index.js
@@ -5,7 +5,7 @@ const Router = require('@koa/router')
|
|||||||
const multer = require('@koa/multer')
|
const multer = require('@koa/multer')
|
||||||
const logger = require('koa-logger')
|
const logger = require('koa-logger')
|
||||||
const sendfile = require('koa-sendfile')
|
const sendfile = require('koa-sendfile')
|
||||||
const mkdirp = require('mkdirp')
|
const { mkdirp } = require('mkdirp')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const { spawn } = require('child_process')
|
const { spawn } = require('child_process')
|
||||||
const { extname, basename, dirname } = require('path')
|
const { extname, basename, dirname } = require('path')
|
||||||
@@ -114,6 +114,10 @@ const upload = multer({
|
|||||||
files: 1
|
files: 1
|
||||||
},
|
},
|
||||||
fileFilter: (req, file, cb) => {
|
fileFilter: (req, file, cb) => {
|
||||||
|
// Fixes charset
|
||||||
|
// https://github.com/expressjs/multer/issues/1104#issuecomment-1152987772
|
||||||
|
file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf8')
|
||||||
|
|
||||||
console.log('Incoming file:', file)
|
console.log('Incoming file:', file)
|
||||||
const key = req.body.key.toUpperCase()
|
const key = req.body.key.toUpperCase()
|
||||||
if (!app.context.keys.has(key)) {
|
if (!app.context.keys.has(key)) {
|
||||||
@@ -121,7 +125,7 @@ const upload = multer({
|
|||||||
cb(null, false)
|
cb(null, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!allowedTypes.includes(file.mimetype) || !allowedExtensions.includes(extname(file.originalname.toLowerCase()).substr(1))) {
|
if ((!allowedTypes.includes(file.mimetype) && file.mimetype != "application/octet-stream") || !allowedExtensions.includes(extname(file.originalname.toLowerCase()).substring(1))) {
|
||||||
console.error('FileFilter: File is of an invalid type ', file)
|
console.error('FileFilter: File is of an invalid type ', file)
|
||||||
cb(null, false)
|
cb(null, false)
|
||||||
return
|
return
|
||||||
@@ -175,9 +179,17 @@ router.get('/download/:key', async ctx => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
expireKey(key)
|
expireKey(key)
|
||||||
console.log('Sending file', info.file.path)
|
// const fallback = basename(info.file.path)
|
||||||
|
const sanename = info.file.name.replace(/[^\.\w\-"']/g, '_')
|
||||||
|
console.log('Sending file', [info.file.path, info.file.name, sanename])
|
||||||
await sendfile(ctx, info.file.path)
|
await sendfile(ctx, info.file.path)
|
||||||
ctx.attachment(info.file.name)
|
if (info.agent.includes('Kindle')) {
|
||||||
|
// Kindle needs a safe name or it thinks it's an invalid file
|
||||||
|
ctx.attachment(sanename)
|
||||||
|
} else {
|
||||||
|
// Kobo always uses fallback
|
||||||
|
ctx.attachment(info.file.name, {fallback: sanename})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
router.post('/upload', upload.single('file'), async ctx => {
|
router.post('/upload', upload.single('file'), async ctx => {
|
||||||
@@ -225,7 +237,7 @@ router.post('/upload', upload.single('file'), async ctx => {
|
|||||||
|
|
||||||
const type = await FileType.fromFile(ctx.request.file.path)
|
const type = await FileType.fromFile(ctx.request.file.path)
|
||||||
|
|
||||||
if (!type || !allowedTypes.includes(type.mime)) {
|
if ((!type || !allowedTypes.includes(type.mime)) && !allowedTypes.includes(mimetype)) {
|
||||||
flash(ctx, {
|
flash(ctx, {
|
||||||
message: 'Uploaded file is of an invalid type: ' + ctx.request.file.originalname + ' (' + (type? type.mime : 'unknown mimetype') + ')',
|
message: 'Uploaded file is of an invalid type: ' + ctx.request.file.originalname + ' (' + (type? type.mime : 'unknown mimetype') + ')',
|
||||||
success: false,
|
success: false,
|
||||||
|
|||||||
1782
package-lock.json
generated
1782
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@@ -9,14 +9,13 @@
|
|||||||
"author": "djazz",
|
"author": "djazz",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@koa/multer": "^3.0.0",
|
"@koa/multer": "^3.0.2",
|
||||||
"@koa/router": "^10.1.0",
|
"@koa/router": "^12.0.1",
|
||||||
"chinese-to-pinyin": "^1.3.1",
|
"file-type": "^16.5.4",
|
||||||
"file-type": "^16.5.3",
|
"koa": "^2.14.2",
|
||||||
"koa": "^2.13.1",
|
|
||||||
"koa-logger": "^3.2.1",
|
"koa-logger": "^3.2.1",
|
||||||
"koa-sendfile": "^3.0.0",
|
"koa-sendfile": "^3.0.0",
|
||||||
"mkdirp": "^1.0.4",
|
"mkdirp": "^3.0.1",
|
||||||
"multer": "^1.4.3"
|
"multer": "^1.4.5-lts.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
style.css
38
style.css
@@ -14,14 +14,15 @@ h1 {
|
|||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
#key {
|
#keyoutput {
|
||||||
font-size: 5em;
|
font-size: 5em;
|
||||||
display: block;
|
display: inline-block;
|
||||||
letter-spacing: 0.2em;
|
letter-spacing: 0.2em;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
font-family: sans-serif;
|
font-family: monospace, sans-serif;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.center {
|
.center {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -29,7 +30,7 @@ h1 {
|
|||||||
.right {
|
.right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
#keygen, #downloadlink {
|
#keygen, #downloadlink, #choosebtn, input[type="submit"] {
|
||||||
background: #CCC;
|
background: #CCC;
|
||||||
border: none;
|
border: none;
|
||||||
color: black;
|
color: black;
|
||||||
@@ -39,11 +40,26 @@ h1 {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
|
margin: 0 0.5em;
|
||||||
|
vertical-align: middle;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
#keygen:focus {
|
#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;
|
background: black;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
#keygen svg {
|
||||||
|
display: block;
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
}
|
||||||
#downloads {
|
#downloads {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -79,10 +95,20 @@ h1 {
|
|||||||
background-color: #FDD;
|
background-color: #FDD;
|
||||||
border: 1px solid #F77;
|
border: 1px solid #F77;
|
||||||
}
|
}
|
||||||
td.right {
|
td {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td.aligntop {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
#fileinput {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
#fileinfo {
|
#fileinfo {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
width: 17em;
|
||||||
}
|
}
|
||||||
83
upload.html
83
upload.html
@@ -4,7 +4,7 @@
|
|||||||
<title>Send to Kobo/Kindle</title>
|
<title>Send to Kobo/Kindle</title>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<link rel="stylesheet" type="text/css" href="/style.css"/>
|
<link rel="stylesheet" type="text/css" href="/style.css?o"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -13,11 +13,10 @@
|
|||||||
|
|
||||||
<form action="/upload" method="post" enctype="multipart/form-data">
|
<form action="/upload" method="post" enctype="multipart/form-data">
|
||||||
<table style="margin: 0 auto;" cellpadding=0 cellspacing=0>
|
<table style="margin: 0 auto;" cellpadding=0 cellspacing=0>
|
||||||
<tr><td class="right"><label for="keyinput">Unique key</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"><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"><label for="fileinput">EPUB/MOBI file</label></td><td><input type="file" name="file" id="fileinput" accept=".epub,.mobi,.pdf,.cbz,.cbr,application/epub+zip,application/x-mobipocket-ebook,application/pdf,application/vnd.comicbook+zip,application/vnd.comicbook-rar" required /></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" required /><br/><br/><div id="fileinfo"></div></td></tr>
|
||||||
<tr><td></td><td id="fileinfo"></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><td class="right"><label for="kepubify">Kepubify</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>
|
||||||
<tr><td></td><td><input type="submit" value="Upload and send" /></td></tr>
|
|
||||||
</table>
|
</table>
|
||||||
<div id="uploadstatus"></div>
|
<div id="uploadstatus"></div>
|
||||||
</form>
|
</form>
|
||||||
@@ -29,31 +28,39 @@
|
|||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div class="center">
|
<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>
|
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>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function getCookies() {
|
function getCookies() {
|
||||||
var cookieRegex = /([\w\.]+)\s*=\s*(?:"((?:\\"|[^"])*)"|(.*?))\s*(?:[;,]|$)/g
|
var cookieRegex = /([\w\.]+)\s*=\s*(?:"((?:\\"|[^"])*)"|(.*?))\s*(?:[;,]|$)/g
|
||||||
var cookies = {}
|
var cookies = {}
|
||||||
var match
|
var match
|
||||||
while( (match = cookieRegex.exec(document.cookie)) !== null ) {
|
while( (match = cookieRegex.exec(document.cookie)) !== null ) {
|
||||||
var value = match[2] || match[3]
|
var value = match[2] || match[3]
|
||||||
cookies[match[1]] = decodeURIComponent(value)
|
cookies[match[1]] = decodeURIComponent(value)
|
||||||
try {
|
try {
|
||||||
cookies[match[1]] = JSON.parse(cookies[match[1]])
|
cookies[match[1]] = JSON.parse(cookies[match[1]])
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
}
|
}
|
||||||
return cookies
|
return cookies
|
||||||
}
|
}
|
||||||
function deleteCookie(name) {
|
function deleteCookie(name) {
|
||||||
document.cookie = name + "= ; expires = Thu, 01 Jan 1970 00:00:00 GMT"
|
document.cookie = name + "= ; expires = Thu, 01 Jan 1970 00:00:00 GMT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var uploadstatus = document.getElementById('uploadstatus')
|
var uploadstatus = document.getElementById('uploadstatus')
|
||||||
var keyinput = document.getElementById('keyinput')
|
var keyinput = document.getElementById('keyinput')
|
||||||
var fileinput = document.getElementById('fileinput')
|
var fileinput = document.getElementById('fileinput')
|
||||||
|
var fileinputAccept = fileinput.accept.split(',') // cache it
|
||||||
var fileinfo = document.getElementById('fileinfo')
|
var fileinfo = document.getElementById('fileinfo')
|
||||||
|
var siteurl = document.getElementById('siteurl')
|
||||||
|
|
||||||
|
|
||||||
|
var isIOS = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
|
||||||
|
|
||||||
var flash = getCookies().flash
|
var flash = getCookies().flash
|
||||||
deleteCookie('flash')
|
deleteCookie('flash')
|
||||||
@@ -78,15 +85,51 @@ uploadstatus.addEventListener('click', function () {
|
|||||||
uploadstatus.className = ''
|
uploadstatus.className = ''
|
||||||
}, 500)
|
}, 500)
|
||||||
}, false)
|
}, false)
|
||||||
|
|
||||||
function fileinputChange () {
|
function fileinputChange () {
|
||||||
if (!fileinput.files[0]) {
|
if (!fileinput.files[0] || fileinput.files.length === 0) {
|
||||||
fileinfo.textContent = ''
|
fileinfo.textContent = ''
|
||||||
|
fileinput.value = ''
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fileinfo.textContent = Math.ceil(fileinput.files[0].size / 1024) + ' kB'
|
|
||||||
|
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)
|
fileinput.addEventListener('change', fileinputChange, false)
|
||||||
fileinputChange()
|
fileinputChange()
|
||||||
|
|
||||||
|
if (isIOS) {
|
||||||
|
// Can't accept .mobi files otherwise (iOS thinks it's an unknown type)
|
||||||
|
fileinput.accept = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
siteurl.textContent = window.location.origin
|
||||||
|
siteurl.href = siteurl.textContent
|
||||||
|
siteurl.target = '_self'
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user