Commit f79d5499 authored by Ozzieisaacs's avatar Ozzieisaacs

Fix duplicate user and email (now case insensitive #948)

Fix sorting in comics (#950)
Fix log error on Calibre converter error (#953)
Fix long running tasks (#954)
parent 66283c54
...@@ -34,7 +34,8 @@ from flask import send_from_directory, make_response, redirect, abort ...@@ -34,7 +34,8 @@ from flask import send_from_directory, make_response, redirect, abort
from flask_babel import gettext as _ from flask_babel import gettext as _
from flask_login import current_user from flask_login import current_user
from babel.dates import format_datetime from babel.dates import format_datetime
from datetime import datetime from babel.units import format_unit
from datetime import datetime, timedelta
import shutil import shutil
import requests import requests
try: try:
...@@ -566,8 +567,33 @@ def json_serial(obj): ...@@ -566,8 +567,33 @@ def json_serial(obj):
if isinstance(obj, (datetime)): if isinstance(obj, (datetime)):
return obj.isoformat() return obj.isoformat()
if isinstance(obj, (timedelta)):
return {
'__type__': 'timedelta',
'days': obj.days,
'seconds': obj.seconds,
'microseconds': obj.microseconds,
}
raise TypeError ("Type %s not serializable" % type(obj)) raise TypeError ("Type %s not serializable" % type(obj))
# helper function for displaying the runtime of tasks
def format_runtime(runtime):
retVal = ""
if runtime.days:
retVal = format_unit(runtime.days, 'duration-day', length="long", locale=web.get_locale()) + ', '
mins, seconds = divmod(runtime.seconds, 60)
hours, minutes = divmod(mins, 60)
# ToDo: locale.number_symbols._data['timeSeparator'] -> localize time separator ?
if hours:
retVal += '{:d}:{:02d}:{:02d}s'.format(hours, minutes, seconds)
elif minutes:
retVal += '{:2d}:{:02d}s'.format(minutes, seconds)
else:
retVal += '{:2d}s'.format(seconds)
return retVal
# helper function to apply localize status information in tasklist entries # helper function to apply localize status information in tasklist entries
def render_task_status(tasklist): def render_task_status(tasklist):
renderedtasklist=list() renderedtasklist=list()
...@@ -579,6 +605,8 @@ def render_task_status(tasklist): ...@@ -579,6 +605,8 @@ def render_task_status(tasklist):
if 'starttime' not in task: if 'starttime' not in task:
task['starttime'] = "" task['starttime'] = ""
task['runtime'] = format_runtime(task['formRuntime'])
# localize the task status # localize the task status
if isinstance( task['stat'], int ): if isinstance( task['stat'], int ):
if task['stat'] == worker.STAT_WAITING: if task['stat'] == worker.STAT_WAITING:
......
/* alphanum.js (C) Brian Huisman
* Based on the Alphanum Algorithm by David Koelle
* The Alphanum Algorithm is discussed at http://www.DaveKoelle.com
*
* Distributed under same license as original
*
* Released under the MIT License - https://opensource.org/licenses/MIT
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* ********************************************************************
* Alphanum sort() function version - case insensitive
* - Slower, but easier to modify for arrays of objects which contain
* string properties
*
*/
function alphanumCase(a, b) {
function chunkify(t) {
var tz = new Array();
var x = 0, y = -1, n = 0, i, j;
while (i = (j = t.charAt(x++)).charCodeAt(0)) {
var m = (i == 46 || (i >=48 && i <= 57));
if (m !== n) {
tz[++y] = "";
n = m;
}
tz[y] += j;
}
return tz;
}
var aa = chunkify(a.filename.toLowerCase());
var bb = chunkify(b.filename.toLowerCase());
for (x = 0; aa[x] && bb[x]; x++) {
if (aa[x] !== bb[x]) {
var c = Number(aa[x]), d = Number(bb[x]);
if (c == aa[x] && d == bb[x]) {
return c - d;
} else return (aa[x] > bb[x]) ? 1 : -1;
}
}
return aa.length - bb.length;
}
// ===========================================================================
/** /**
* archive.js * archive.js
* *
......
...@@ -1332,12 +1332,7 @@ var unrar = function(arrayBuffer) { ...@@ -1332,12 +1332,7 @@ var unrar = function(arrayBuffer) {
totalFilesInArchive = localFiles.length; totalFilesInArchive = localFiles.length;
// now we have all information but things are unpacked // now we have all information but things are unpacked
// TODO: unpack localFiles.sort(alphanumCase);
localFiles = localFiles.sort(function(a, b) {
var aname = a.filename.toLowerCase();
var bname = b.filename.toLowerCase();
return aname > bname ? 1 : -1;
});
info(localFiles.map(function(a) { info(localFiles.map(function(a) {
return a.filename; return a.filename;
......
...@@ -115,6 +115,7 @@ var TarLocalFile = function(bstream) { ...@@ -115,6 +115,7 @@ var TarLocalFile = function(bstream) {
} }
}; };
var untar = function(arrayBuffer) { var untar = function(arrayBuffer) {
postMessage(new bitjs.archive.UnarchiveStartEvent()); postMessage(new bitjs.archive.UnarchiveStartEvent());
currentFilename = ""; currentFilename = "";
...@@ -127,14 +128,22 @@ var untar = function(arrayBuffer) { ...@@ -127,14 +128,22 @@ var untar = function(arrayBuffer) {
var bstream = new bitjs.io.ByteStream(arrayBuffer); var bstream = new bitjs.io.ByteStream(arrayBuffer);
postProgress(); postProgress();
// While we don't encounter an empty block, keep making TarLocalFiles. /*
// go through whole file, read header of each block and memorize, filepointer
*/
while (bstream.peekNumber(4) !== 0) { while (bstream.peekNumber(4) !== 0) {
var oneLocalFile = new TarLocalFile(bstream); var localFile = new TarLocalFile(bstream);
allLocalFiles.push(localFile);
postProgress();
}
// got all local files, now sort them
allLocalFiles.sort(alphanumCase);
allLocalFiles.forEach(function(oneLocalFile) {
// While we don't encounter an empty block, keep making TarLocalFiles.
if (oneLocalFile && oneLocalFile.isValid) { if (oneLocalFile && oneLocalFile.isValid) {
// If we make it to this point and haven't thrown an error, we have successfully // If we make it to this point and haven't thrown an error, we have successfully
// read in the data for a local file, so we can update the actual bytestream. // read in the data for a local file, so we can update the actual bytestream.
allLocalFiles.push(oneLocalFile);
totalUncompressedBytesInArchive += oneLocalFile.size; totalUncompressedBytesInArchive += oneLocalFile.size;
// update progress // update progress
...@@ -145,7 +154,7 @@ var untar = function(arrayBuffer) { ...@@ -145,7 +154,7 @@ var untar = function(arrayBuffer) {
postMessage(new bitjs.archive.UnarchiveExtractEvent(oneLocalFile)); postMessage(new bitjs.archive.UnarchiveExtractEvent(oneLocalFile));
postProgress(); postProgress();
} }
} });
totalFilesInArchive = allLocalFiles.length; totalFilesInArchive = allLocalFiles.length;
postProgress(); postProgress();
......
...@@ -72,23 +72,10 @@ var ZipLocalFile = function(bstream) { ...@@ -72,23 +72,10 @@ var ZipLocalFile = function(bstream) {
this.filename = bstream.readString(this.fileNameLength); this.filename = bstream.readString(this.fileNameLength);
} }
info("Zip Local File Header:");
info(" version=" + this.version);
info(" general purpose=" + this.generalPurpose);
info(" compression method=" + this.compressionMethod);
info(" last mod file time=" + this.lastModFileTime);
info(" last mod file date=" + this.lastModFileDate);
info(" crc32=" + this.crc32);
info(" compressed size=" + this.compressedSize);
info(" uncompressed size=" + this.uncompressedSize);
info(" file name length=" + this.fileNameLength);
info(" extra field length=" + this.extraFieldLength);
info(" filename = '" + this.filename + "'");
this.extraField = null; this.extraField = null;
if (this.extraFieldLength > 0) { if (this.extraFieldLength > 0) {
this.extraField = bstream.readString(this.extraFieldLength); this.extraField = bstream.readString(this.extraFieldLength);
info(" extra field=" + this.extraField); info(" extra field=" + this.extraField);
} }
// read in the compressed data // read in the compressed data
...@@ -107,6 +94,21 @@ var ZipLocalFile = function(bstream) { ...@@ -107,6 +94,21 @@ var ZipLocalFile = function(bstream) {
this.compressedSize = bstream.readNumber(4); this.compressedSize = bstream.readNumber(4);
this.uncompressedSize = bstream.readNumber(4); this.uncompressedSize = bstream.readNumber(4);
} }
// Now that we have all the bytes for this file, we can print out some information.
info("Zip Local File Header:");
info(" version=" + this.version);
info(" general purpose=" + this.generalPurpose);
info(" compression method=" + this.compressionMethod);
info(" last mod file time=" + this.lastModFileTime);
info(" last mod file date=" + this.lastModFileDate);
info(" crc32=" + this.crc32);
info(" compressed size=" + this.compressedSize);
info(" uncompressed size=" + this.uncompressedSize);
info(" file name length=" + this.fileNameLength);
info(" extra field length=" + this.extraFieldLength);
info(" filename = '" + this.filename + "'");
}; };
// determine what kind of compressed data we have and decompress // determine what kind of compressed data we have and decompress
...@@ -132,6 +134,7 @@ ZipLocalFile.prototype.unzip = function() { ...@@ -132,6 +134,7 @@ ZipLocalFile.prototype.unzip = function() {
// Takes an ArrayBuffer of a zip file in // Takes an ArrayBuffer of a zip file in
// returns null on error // returns null on error
// returns an array of DecompressedFile objects on success // returns an array of DecompressedFile objects on success
// ToDo This function differs
var unzip = function(arrayBuffer) { var unzip = function(arrayBuffer) {
postMessage(new bitjs.archive.UnarchiveStartEvent()); postMessage(new bitjs.archive.UnarchiveStartEvent());
...@@ -159,11 +162,7 @@ var unzip = function(arrayBuffer) { ...@@ -159,11 +162,7 @@ var unzip = function(arrayBuffer) {
totalFilesInArchive = localFiles.length; totalFilesInArchive = localFiles.length;
// got all local files, now sort them // got all local files, now sort them
localFiles.sort(function(a, b) { localFiles.sort(alphanumCase);
var aname = a.filename.toLowerCase();
var bname = b.filename.toLowerCase();
return aname > bname ? 1 : -1;
});
// archive extra data record // archive extra data record
if (bstream.peekNumber(4) === zArchiveExtraDataSignature) { if (bstream.peekNumber(4) === zArchiveExtraDataSignature) {
...@@ -253,9 +252,9 @@ function getHuffmanCodes(bitLengths) { ...@@ -253,9 +252,9 @@ function getHuffmanCodes(bitLengths) {
} }
// Reference: http://tools.ietf.org/html/rfc1951#page-8 // Reference: http://tools.ietf.org/html/rfc1951#page-8
var numLengths = bitLengths.length, var numLengths = bitLengths.length;
blCount = [], var blCount = [];
MAX_BITS = 1; var MAX_BITS = 1;
// Step 1: count up how many codes of each length we have // Step 1: count up how many codes of each length we have
for (var i = 0; i < numLengths; ++i) { for (var i = 0; i < numLengths; ++i) {
...@@ -274,8 +273,8 @@ function getHuffmanCodes(bitLengths) { ...@@ -274,8 +273,8 @@ function getHuffmanCodes(bitLengths) {
} }
// Step 2: Find the numerical value of the smallest code for each code length // Step 2: Find the numerical value of the smallest code for each code length
var nextCode = [], var nextCode = [];
code = 0; var code = 0;
for (var bits = 1; bits <= MAX_BITS; ++bits) { for (var bits = 1; bits <= MAX_BITS; ++bits) {
var length2 = bits - 1; var length2 = bits - 1;
// ensure undefined lengths are zero // ensure undefined lengths are zero
...@@ -285,8 +284,8 @@ function getHuffmanCodes(bitLengths) { ...@@ -285,8 +284,8 @@ function getHuffmanCodes(bitLengths) {
} }
// Step 3: Assign numerical values to all codes // Step 3: Assign numerical values to all codes
var table = {}, var table = {};
tableLength = 0; var tableLength = 0;
for (var n = 0; n < numLengths; ++n) { for (var n = 0; n < numLengths; ++n) {
var len = bitLengths[n]; var len = bitLengths[n];
if (len !== 0) { if (len !== 0) {
...@@ -353,7 +352,8 @@ function getFixedDistanceTable() { ...@@ -353,7 +352,8 @@ function getFixedDistanceTable() {
// extract one bit at a time until we find a matching Huffman Code // extract one bit at a time until we find a matching Huffman Code
// then return that symbol // then return that symbol
function decodeSymbol(bstream, hcTable) { function decodeSymbol(bstream, hcTable) {
var code = 0, len = 0; var code = 0;
var len = 0;
// loop until we match // loop until we match
for (;;) { for (;;) {
...@@ -364,7 +364,6 @@ function decodeSymbol(bstream, hcTable) { ...@@ -364,7 +364,6 @@ function decodeSymbol(bstream, hcTable) {
// check against Huffman Code table and break if found // check against Huffman Code table and break if found
if (hcTable.hasOwnProperty(code) && hcTable[code].length === len) { if (hcTable.hasOwnProperty(code) && hcTable[code].length === len) {
break; break;
} }
if (len > hcTable.maxLength) { if (len > hcTable.maxLength) {
...@@ -500,10 +499,10 @@ function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) { ...@@ -500,10 +499,10 @@ function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) {
if (symbol === 256) { if (symbol === 256) {
break; break;
} else { } else {
var lengthLookup = LengthLookupTable[symbol - 257], var lengthLookup = LengthLookupTable[symbol - 257];
length = lengthLookup[1] + bstream.readBits(lengthLookup[0]), var length = lengthLookup[1] + bstream.readBits(lengthLookup[0]);
distLookup = DistLookupTable[decodeSymbol(bstream, hcDistanceTable)], var distLookup = DistLookupTable[decodeSymbol(bstream, hcDistanceTable)];
distance = distLookup[1] + bstream.readBits(distLookup[0]); var distance = distLookup[1] + bstream.readBits(distLookup[0]);
// now apply length and distance appropriately and copy to output // now apply length and distance appropriately and copy to output
...@@ -634,8 +633,8 @@ function inflate(compressedData, numDecompressedBytes) { ...@@ -634,8 +633,8 @@ function inflate(compressedData, numDecompressedBytes) {
var distanceCodeLengths = literalCodeLengths.splice(numLiteralLengthCodes, numDistanceCodes); var distanceCodeLengths = literalCodeLengths.splice(numLiteralLengthCodes, numDistanceCodes);
// now generate the true Huffman Code tables using these code lengths // now generate the true Huffman Code tables using these code lengths
var hcLiteralTable = getHuffmanCodes(literalCodeLengths), var hcLiteralTable = getHuffmanCodes(literalCodeLengths);
hcDistanceTable = getHuffmanCodes(distanceCodeLengths); var hcDistanceTable = getHuffmanCodes(distanceCodeLengths);
blockSize = inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer); blockSize = inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer);
} else { } else {
// error // error
......
/*
* io.js
*
* Provides readers for bit/byte streams (reading) and a byte buffer (writing).
*
* Licensed under the MIT License
*
* Copyright(c) 2011 Google Inc.
* Copyright(c) 2011 antimatter15
*/
/* global bitjs, Uint8Array */
var bitjs = bitjs || {};
bitjs.io = bitjs.io || {};
(function() {
// mask for getting the Nth bit (zero-based)
bitjs.BIT = [ 0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80,
0x100, 0x200, 0x400, 0x800,
0x1000, 0x2000, 0x4000, 0x8000];
// mask for getting N number of bits (0-8)
var BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF ];
/**
* This bit stream peeks and consumes bits out of a binary stream.
*
* @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array.
* @param {boolean} rtl Whether the stream reads bits from the byte starting
* from bit 7 to 0 (true) or bit 0 to 7 (false).
* @param {Number} optOffset The offset into the ArrayBuffer
* @param {Number} optLength The length of this BitStream
*/
bitjs.io.BitStream = function(ab, rtl, optOffset, optLength) {
if (!ab || !ab.toString || ab.toString() !== "[object ArrayBuffer]") {
throw "Error! BitArray constructed with an invalid ArrayBuffer object";
}
var offset = optOffset || 0;
var length = optLength || ab.byteLength;
this.bytes = new Uint8Array(ab, offset, length);
this.bytePtr = 0; // tracks which byte we are on
this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7)
this.peekBits = rtl ? this.peekBitsRtl : this.peekBitsLtr;
};
/**
* byte0 byte1 byte2 byte3
* 7......0 | 7......0 | 7......0 | 7......0
*
* The bit pointer starts at bit0 of byte0 and moves left until it reaches
* bit7 of byte0, then jumps to bit0 of byte1, etc.
* @param {number} n The number of bits to peek.
* @param {boolean=} movePointers Whether to move the pointer, defaults false.
* @return {number} The peeked bits, as an unsigned number.
*/
bitjs.io.BitStream.prototype.peekBitsLtr = function(n, movePointers) {
if (n <= 0 || typeof n !== typeof 1) {
return 0;
}
var movePointers = movePointers || false;
var bytePtr = this.bytePtr;
var bitPtr = this.bitPtr;
var result = 0;
var bitsIn = 0;
var bytes = this.bytes;
// keep going until we have no more bits left to peek at
// TODO: Consider putting all bits from bytes we will need into a variable and then
// shifting/masking it to just extract the bits we want.
// This could be considerably faster when reading more than 3 or 4 bits at a time.
while (n > 0) {
if (bytePtr >= bytes.length) {
throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" +
bytes.length + ", bitPtr=" + bitPtr;
}
var numBitsLeftInThisByte = (8 - bitPtr);
var mask;
if (n >= numBitsLeftInThisByte) {
mask = (BITMASK[numBitsLeftInThisByte] << bitPtr);
result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn);
bytePtr++;
bitPtr = 0;
bitsIn += numBitsLeftInThisByte;
n -= numBitsLeftInThisByte;
} else {
mask = (BITMASK[n] << bitPtr);
result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn);
bitPtr += n;
bitsIn += n;
n = 0;
}
}
if (movePointers) {
this.bitPtr = bitPtr;
this.bytePtr = bytePtr;
}
return result;
};
/**
* byte0 byte1 byte2 byte3
* 7......0 | 7......0 | 7......0 | 7......0
*
* The bit pointer starts at bit7 of byte0 and moves right until it reaches
* bit0 of byte0, then goes to bit7 of byte1, etc.
* @param {number} n The number of bits to peek.
* @param {boolean=} movePointers Whether to move the pointer, defaults false.
* @return {number} The peeked bits, as an unsigned number.
*/
bitjs.io.BitStream.prototype.peekBitsRtl = function(n, movePointers) {
if (n <= 0 || typeof n !== typeof 1) {
return 0;
}
var movePointers = movePointers || false;
var bytePtr = this.bytePtr;
var bitPtr = this.bitPtr;
var result = 0;
var bytes = this.bytes;
// keep going until we have no more bits left to peek at
// TODO: Consider putting all bits from bytes we will need into a variable and then
// shifting/masking it to just extract the bits we want.
// This could be considerably faster when reading more than 3 or 4 bits at a time.
while (n > 0) {
if (bytePtr >= bytes.length) {
throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" +
bytes.length + ", bitPtr=" + bitPtr;
// return -1;
}
var numBitsLeftInThisByte = (8 - bitPtr);
if (n >= numBitsLeftInThisByte) {
result <<= numBitsLeftInThisByte;
result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]);
bytePtr++;
bitPtr = 0;
n -= numBitsLeftInThisByte;
} else {
result <<= n;
result |= ((bytes[bytePtr] & (BITMASK[n] << (8 - n - bitPtr))) >> (8 - n - bitPtr));
bitPtr += n;
n = 0;
}
}
if (movePointers) {
this.bitPtr = bitPtr;
this.bytePtr = bytePtr;
}
return result;
};
/**
* Some voodoo magic.
*/
bitjs.io.BitStream.prototype.getBits = function() {
return (((((this.bytes[this.bytePtr] & 0xff) << 16) +
((this.bytes[this.bytePtr + 1] & 0xff) << 8) +
((this.bytes[this.bytePtr + 2] & 0xff))) >>> (8 - this.bitPtr)) & 0xffff);
};
/**
* Reads n bits out of the stream, consuming them (moving the bit pointer).
* @param {number} n The number of bits to read.
* @return {number} The read bits, as an unsigned number.
*/
bitjs.io.BitStream.prototype.readBits = function(n) {
return this.peekBits(n, true);
};
/**
* This returns n bytes as a sub-array, advancing the pointer if movePointers
* is true. Only use this for uncompressed blocks as this throws away remaining
* bits in the current byte.
* @param {number} n The number of bytes to peek.
* @param {boolean=} movePointers Whether to move the pointer, defaults false.
* @return {Uint8Array} The subarray.
*/
bitjs.io.BitStream.prototype.peekBytes = function(n, movePointers) {
if (n <= 0 || typeof n != typeof 1) {
return 0;
}
// from http://tools.ietf.org/html/rfc1951#page-11
// "Any bits of input up to the next byte boundary are ignored."
while (this.bitPtr !== 0) {
this.readBits(1);
}
movePointers = movePointers || false;
var bytePtr = this.bytePtr;
// var bitPtr = this.bitPtr;
var result = this.bytes.subarray(bytePtr, bytePtr + n);
if (movePointers) {
this.bytePtr += n;
}
return result;
};
/**
* @param {number} n The number of bytes to read.
* @return {Uint8Array} The subarray.
*/
bitjs.io.BitStream.prototype.readBytes = function(n) {
return this.peekBytes(n, true);
};
/**
* This object allows you to peek and consume bytes as numbers and strings
* out of an ArrayBuffer. In this buffer, everything must be byte-aligned.
*
* @param {ArrayBuffer} ab The ArrayBuffer object.
* @param {number=} optOffset The offset into the ArrayBuffer
* @param {number=} optLength The length of this BitStream
* @constructor
*/
bitjs.io.ByteStream = function(ab, optOffset, optLength) {
var offset = optOffset || 0;
var length = optLength || ab.byteLength;
this.bytes = new Uint8Array(ab, offset, length);
this.ptr = 0;
};
/**
* Peeks at the next n bytes as an unsigned number but does not advance the
* pointer
* TODO: This apparently cannot read more than 4 bytes as a number?
* @param {number} n The number of bytes to peek at.
* @return {number} The n bytes interpreted as an unsigned number.
*/
bitjs.io.ByteStream.prototype.peekNumber = function(n) {
// TODO: return error if n would go past the end of the stream?
if (n <= 0 || typeof n !== typeof 1) {
return -1;
}
var result = 0;
// read from last byte to first byte and roll them in
var curByte = this.ptr + n - 1;
while (curByte >= this.ptr) {
result <<= 8;
result |= this.bytes[curByte];
--curByte;
}
return result;
};
/**
* Returns the next n bytes as an unsigned number (or -1 on error)
* and advances the stream pointer n bytes.
* @param {number} n The number of bytes to read.
* @return {number} The n bytes interpreted as an unsigned number.
*/
bitjs.io.ByteStream.prototype.readNumber = function(n) {
var num = this.peekNumber( n );
this.ptr += n;
return num;
};
/**
* Returns the next n bytes as a signed number but does not advance the
* pointer.
* @param {number} n The number of bytes to read.
* @return {number} The bytes interpreted as a signed number.
*/
bitjs.io.ByteStream.prototype.peekSignedNumber = function(n) {
var num = this.peekNumber(n);
var HALF = Math.pow(2, (n * 8) - 1);
var FULL = HALF * 2;
if (num >= HALF) num -= FULL;
return num;
};
/**
* Returns the next n bytes as a signed number and advances the stream pointer.
* @param {number} n The number of bytes to read.
* @return {number} The bytes interpreted as a signed number.
*/
bitjs.io.ByteStream.prototype.readSignedNumber = function(n) {
var num = this.peekSignedNumber(n);
this.ptr += n;
return num;
};
/**
* This returns n bytes as a sub-array, advancing the pointer if movePointers
* is true.
* @param {number} n The number of bytes to read.
* @param {boolean} movePointers Whether to move the pointers.
* @return {Uint8Array} The subarray.
*/
bitjs.io.ByteStream.prototype.peekBytes = function(n, movePointers) {
if (n <= 0 || typeof n != typeof 1) {
return null;
}
var result = this.bytes.subarray(this.ptr, this.ptr + n);
if (movePointers) {
this.ptr += n;
}
return result;
};
/**
* Reads the next n bytes as a sub-array.
* @param {number} n The number of bytes to read.
* @return {Uint8Array} The subarray.
*/
bitjs.io.ByteStream.prototype.readBytes = function(n) {
return this.peekBytes(n, true);
};
/**
* Peeks at the next n bytes as a string but does not advance the pointer.
* @param {number} n The number of bytes to peek at.
* @return {string} The next n bytes as a string.
*/
bitjs.io.ByteStream.prototype.peekString = function(n) {
if (n <= 0 || typeof n != typeof 1) {
return "";
}
var result = "";
for (var p = this.ptr, end = this.ptr + n; p < end; ++p) {
result += String.fromCharCode(this.bytes[p]);
}
return result;
};
/**
* Returns the next n bytes as an ASCII string and advances the stream pointer
* n bytes.
* @param {number} n The number of bytes to read.
* @return {string} The next n bytes as a string.
*/
bitjs.io.ByteStream.prototype.readString = function(n) {
var strToReturn = this.peekString(n);
this.ptr += n;
return strToReturn;
};
/**
* A write-only Byte buffer which uses a Uint8 Typed Array as a backing store.
* @param {number} numBytes The number of bytes to allocate.
* @constructor
*/
bitjs.io.ByteBuffer = function(numBytes) {
if (typeof numBytes !== typeof 1 || numBytes <= 0) {
throw "Error! ByteBuffer initialized with '" + numBytes + "'";
}
this.data = new Uint8Array(numBytes);
this.ptr = 0;
};
/**
* @param {number} b The byte to insert.
*/
bitjs.io.ByteBuffer.prototype.insertByte = function(b) {
// TODO: throw if byte is invalid?
this.data[this.ptr++] = b;
};
/**
* @param {Array.<number>|Uint8Array|Int8Array} bytes The bytes to insert.
*/
bitjs.io.ByteBuffer.prototype.insertBytes = function(bytes) {
// TODO: throw if bytes is invalid?
this.data.set(bytes, this.ptr);
this.ptr += bytes.length;
};
/**
* Writes an unsigned number into the next n bytes. If the number is too large
* to fit into n bytes or is negative, an error is thrown.
* @param {number} num The unsigned number to write.
* @param {number} numBytes The number of bytes to write the number into.
*/
bitjs.io.ByteBuffer.prototype.writeNumber = function(num, numBytes) {
if (numBytes < 1) {
throw "Trying to write into too few bytes: " + numBytes;
}
if (num < 0) {
throw "Trying to write a negative number (" + num +
") as an unsigned number to an ArrayBuffer";
}
if (num > (Math.pow(2, numBytes * 8) - 1)) {
throw "Trying to write " + num + " into only " + numBytes + " bytes";
}
// Roll 8-bits at a time into an array of bytes.
var bytes = [];
while (numBytes-- > 0) {
var eightBits = num & 255;
bytes.push(eightBits);
num >>= 8;
}
this.insertBytes(bytes);
};
/**
* Writes a signed number into the next n bytes. If the number is too large
* to fit into n bytes, an error is thrown.
* @param {number} num The signed number to write.
* @param {number} numBytes The number of bytes to write the number into.
*/
bitjs.io.ByteBuffer.prototype.writeSignedNumber = function(num, numBytes) {
if (numBytes < 1) {
throw "Trying to write into too few bytes: " + numBytes;
}
var HALF = Math.pow(2, (numBytes * 8) - 1);
if (num >= HALF || num < -HALF) {
throw "Trying to write " + num + " into only " + numBytes + " bytes";
}
// Roll 8-bits at a time into an array of bytes.
var bytes = [];
while (numBytes-- > 0) {
var eightBits = num & 255;
bytes.push(eightBits);
num >>= 8;
}
this.insertBytes(bytes);
};
/**
* @param {string} str The ASCII string to write.
*/
bitjs.io.ByteBuffer.prototype.writeASCIIString = function(str) {
for (var i = 0; i < str.length; ++i) {
var curByte = str.charCodeAt(i);
if (curByte < 0 || curByte > 255) {
throw "Trying to write a non-ASCII string!";
}
this.insertByte(curByte);
}
};
})();
...@@ -3171,13 +3171,22 @@ def new_user(): ...@@ -3171,13 +3171,22 @@ def new_user():
return render_title_template("user_edit.html", new_user=1, content=content, translations=translations, return render_title_template("user_edit.html", new_user=1, content=content, translations=translations,
title=_(u"Add new user")) title=_(u"Add new user"))
content.password = generate_password_hash(to_save["password"]) content.password = generate_password_hash(to_save["password"])
content.nickname = to_save["nickname"] existing_user = ub.session.query(ub.User).filter(func.lower(ub.User.nickname) == to_save["nickname"].lower())\
if config.config_public_reg and not check_valid_domain(to_save["email"]): .first()
flash(_(u"E-mail is not from valid domain"), category="error") existing_email = ub.session.query(ub.User).filter(ub.User.email == to_save["email"].lower())\
return render_title_template("user_edit.html", new_user=1, content=content, translations=translations, .first()
title=_(u"Add new user")) if not existing_user and not existing_email:
content.nickname = to_save["nickname"]
if config.config_public_reg and not check_valid_domain(to_save["email"]):
flash(_(u"E-mail is not from valid domain"), category="error")
return render_title_template("user_edit.html", new_user=1, content=content, translations=translations,
title=_(u"Add new user"))
else:
content.email = to_save["email"]
else: else:
content.email = to_save["email"] flash(_(u"Found an existing account for this e-mail address or nickname."), category="error")
return render_title_template("user_edit.html", new_user=1, content=content, translations=translations,
languages=languages, title=_(u"Add new user"), page="newuser")
try: try:
ub.session.add(content) ub.session.add(content)
ub.session.commit() ub.session.commit()
...@@ -3362,14 +3371,24 @@ def edit_user(user_id): ...@@ -3362,14 +3371,24 @@ def edit_user(user_id):
if "locale" in to_save and to_save["locale"]: if "locale" in to_save and to_save["locale"]:
content.locale = to_save["locale"] content.locale = to_save["locale"]
if to_save["email"] and to_save["email"] != content.email: if to_save["email"] and to_save["email"] != content.email:
content.email = to_save["email"] existing_email = ub.session.query(ub.User).filter(ub.User.email == to_save["email"].lower()) \
.first()
if not existing_email:
content.email = to_save["email"]
else:
flash(_(u"Found an existing account for this e-mail address."), category="error")
return render_title_template("user_edit.html", translations=translations, languages=languages,
new_user=0, content=content, downloads=downloads,
title=_(u"Edit User %(nick)s", nick=content.nickname), page="edituser")
if "kindle_mail" in to_save and to_save["kindle_mail"] != content.kindle_mail: if "kindle_mail" in to_save and to_save["kindle_mail"] != content.kindle_mail:
content.kindle_mail = to_save["kindle_mail"] content.kindle_mail = to_save["kindle_mail"]
try: try:
ub.session.commit() ub.session.commit()
flash(_(u"User '%(nick)s' updated", nick=content.nickname), category="success") flash(_(u"User '%(nick)s' updated", nick=content.nickname), category="success")
except IntegrityError: except IntegrityError as e:
ub.session.rollback() ub.session.rollback()
print(e)
flash(_(u"An unknown error occured."), category="error") flash(_(u"An unknown error occured."), category="error")
return render_title_template("user_edit.html", translations=translations, languages=languages, new_user=0, return render_title_template("user_edit.html", translations=translations, languages=languages, new_user=0,
content=content, downloads=downloads, title=_(u"Edit User %(nick)s", content=content, downloads=downloads, title=_(u"Edit User %(nick)s",
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
from __future__ import print_function from __future__ import print_function
import smtplib import smtplib
import threading import threading
from datetime import datetime from datetime import datetime, timedelta
import logging import logging
import time import time
import socket import socket
...@@ -221,8 +221,10 @@ class WorkerThread(threading.Thread): ...@@ -221,8 +221,10 @@ class WorkerThread(threading.Thread):
if self.UIqueue[self.current]['stat'] == STAT_STARTED: if self.UIqueue[self.current]['stat'] == STAT_STARTED:
if self.queue[self.current]['taskType'] == TASK_EMAIL: if self.queue[self.current]['taskType'] == TASK_EMAIL:
self.UIqueue[self.current]['progress'] = self.get_send_status() self.UIqueue[self.current]['progress'] = self.get_send_status()
self.UIqueue[self.current]['runtime'] = self._formatRuntime( self.UIqueue[self.current]['formRuntime'] = datetime.now() - self.queue[self.current]['starttime']
datetime.now() - self.queue[self.current]['starttime']) self.UIqueue[self.current]['rt'] = self.UIqueue[self.current]['formRuntime'].days*24*60 \
+ self.UIqueue[self.current]['formRuntime'].seconds \
+ self.UIqueue[self.current]['formRuntime'].microseconds
return self.UIqueue return self.UIqueue
def _convert_any_format(self): def _convert_any_format(self):
...@@ -259,7 +261,8 @@ class WorkerThread(threading.Thread): ...@@ -259,7 +261,8 @@ class WorkerThread(threading.Thread):
self._handleSuccess() self._handleSuccess()
return file_path + format_new_ext return file_path + format_new_ext
else: else:
web.app.logger.info("Book id %d - target format of %s does not exist. Moving forward with convert.", bookid, format_new_ext) web.app.logger.info("Book id %d - target format of %s does not exist. Moving forward with convert.",
bookid, format_new_ext)
# check if converter-executable is existing # check if converter-executable is existing
if not os.path.exists(web.ub.config.config_converterpath): if not os.path.exists(web.ub.config.config_converterpath):
...@@ -300,7 +303,7 @@ class WorkerThread(threading.Thread): ...@@ -300,7 +303,7 @@ class WorkerThread(threading.Thread):
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
command = [x.encode(sys.getfilesystemencoding()) for x in command] command = [x.encode(sys.getfilesystemencoding()) for x in command]
p = subprocess.Popen(command, stdout=subprocess.PIPE, universal_newlines=True) p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
except OSError as e: except OSError as e:
self._handleError(_(u"Ebook-converter failed: %(error)s", error=e)) self._handleError(_(u"Ebook-converter failed: %(error)s", error=e))
return return
...@@ -328,6 +331,11 @@ class WorkerThread(threading.Thread): ...@@ -328,6 +331,11 @@ class WorkerThread(threading.Thread):
# process returncode # process returncode
check = p.returncode check = p.returncode
calibre_traceback = p.stderr.readlines()
for ele in calibre_traceback:
web.app.logger.debug(ele.strip('\n'))
if not ele.startswith('Traceback') and not ele.startswith(' File'):
error_message = "Calibre failed with error: %s" % ele.strip('\n')
# kindlegen returncodes # kindlegen returncodes
# 0 = Info(prcgen):I1036: Mobi file built successfully # 0 = Info(prcgen):I1036: Mobi file built successfully
...@@ -481,31 +489,17 @@ class WorkerThread(threading.Thread): ...@@ -481,31 +489,17 @@ class WorkerThread(threading.Thread):
self._handleError(u'Error sending email: ' + e.strerror) self._handleError(u'Error sending email: ' + e.strerror)
return None return None
def _formatRuntime(self, runtime):
self.UIqueue[self.current]['rt'] = runtime.total_seconds()
val = re.split('\:|\.', str(runtime))[0:3]
erg = list()
for v in val:
if int(v) > 0:
erg.append(v)
retVal = (':'.join(erg)).lstrip('0') + ' s'
if retVal == ' s':
retVal = '0 s'
return retVal
def _handleError(self, error_message): def _handleError(self, error_message):
web.app.logger.error(error_message) web.app.logger.error(error_message)
self.UIqueue[self.current]['stat'] = STAT_FAIL self.UIqueue[self.current]['stat'] = STAT_FAIL
self.UIqueue[self.current]['progress'] = "100 %" self.UIqueue[self.current]['progress'] = "100 %"
self.UIqueue[self.current]['runtime'] = self._formatRuntime( self.UIqueue[self.current]['formRuntime'] = datetime.now() - self.queue[self.current]['starttime']
datetime.now() - self.queue[self.current]['starttime'])
self.UIqueue[self.current]['message'] = error_message self.UIqueue[self.current]['message'] = error_message
def _handleSuccess(self): def _handleSuccess(self):
self.UIqueue[self.current]['stat'] = STAT_FINISH_SUCCESS self.UIqueue[self.current]['stat'] = STAT_FINISH_SUCCESS
self.UIqueue[self.current]['progress'] = "100 %" self.UIqueue[self.current]['progress'] = "100 %"
self.UIqueue[self.current]['runtime'] = self._formatRuntime( self.UIqueue[self.current]['formRuntime'] = datetime.now() - self.queue[self.current]['starttime']
datetime.now() - self.queue[self.current]['starttime'])
# Enable logging of smtp lib debug output # Enable logging of smtp lib debug output
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment