mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-12-14 15:59:38 +00:00
Update users table info #94, Reorder libraries in config #95, Use dropdown for library menu #96, update mobi reader
This commit is contained in:
parent
9715c53332
commit
cd6e99b4c3
30 changed files with 1361 additions and 557 deletions
|
|
@ -30,6 +30,7 @@ class ApiController {
|
|||
this.router.get('/find/:method', this.find.bind(this))
|
||||
|
||||
this.router.get('/libraries', this.getLibraries.bind(this))
|
||||
this.router.patch('/libraries/order', this.reorderLibraries.bind(this))
|
||||
this.router.get('/library/:id/search', this.searchLibrary.bind(this))
|
||||
this.router.get('/library/:id', this.getLibrary.bind(this))
|
||||
this.router.delete('/library/:id', this.deleteLibrary.bind(this))
|
||||
|
|
@ -98,6 +99,36 @@ class ApiController {
|
|||
res.json(libraries)
|
||||
}
|
||||
|
||||
async reorderLibraries(req, res) {
|
||||
if (!req.user || !req.user.isRoot) {
|
||||
Logger.error('[ApiController] ReorderLibraries invalid user', req.user)
|
||||
return res.sendStatus(401)
|
||||
}
|
||||
|
||||
var orderdata = req.body
|
||||
var hasUpdates = false
|
||||
for (let i = 0; i < orderdata.length; i++) {
|
||||
var library = this.db.libraries.find(lib => lib.id === orderdata[i].id)
|
||||
if (!library) {
|
||||
Logger.error(`[ApiController] Invalid library not found in reorder ${orderdata[i].id}`)
|
||||
return res.sendStatus(500)
|
||||
}
|
||||
if (library.update({ displayOrder: orderdata[i].newOrder })) {
|
||||
hasUpdates = true
|
||||
await this.db.updateEntity('library', library)
|
||||
}
|
||||
}
|
||||
|
||||
if (hasUpdates) {
|
||||
Logger.info(`[ApiController] Updated library display orders`)
|
||||
} else {
|
||||
Logger.info(`[ApiController] Library orders were up to date`)
|
||||
}
|
||||
|
||||
var libraries = this.db.libraries.map(lib => lib.toJSON())
|
||||
res.json(libraries)
|
||||
}
|
||||
|
||||
searchLibrary(req, res) {
|
||||
var library = this.db.libraries.find(lib => lib.id === req.params.id)
|
||||
if (!library) {
|
||||
|
|
@ -226,6 +257,7 @@ class ApiController {
|
|||
}
|
||||
|
||||
var library = new Library()
|
||||
newLibraryPayload.displayOrder = this.db.libraries.length + 1
|
||||
library.setData(newLibraryPayload)
|
||||
await this.db.insertEntity('library', library)
|
||||
this.emitter('library_added', library.toJSON())
|
||||
|
|
|
|||
|
|
@ -92,6 +92,10 @@ class Server {
|
|||
})
|
||||
}
|
||||
|
||||
authMiddleware(req, res, next) {
|
||||
this.auth.authMiddleware(req, res, next)
|
||||
}
|
||||
|
||||
async init() {
|
||||
Logger.info('[Server] Init v' + version)
|
||||
await this.streamManager.ensureStreamsDir()
|
||||
|
|
@ -270,6 +274,8 @@ class Server {
|
|||
Logger.info('[SOCKET] Unauth socket disconnected ' + socket.id)
|
||||
delete this.clients[socket.id]
|
||||
} else {
|
||||
socket.broadcast.emit('user_offline', _client.user.toJSONForPublic(this.streamManager.streams))
|
||||
|
||||
const disconnectTime = Date.now() - _client.connected_at
|
||||
Logger.info(`[SOCKET] Socket ${socket.id} disconnected from client "${_client.user.username}" after ${disconnectTime}ms`)
|
||||
delete this.clients[socket.id]
|
||||
|
|
@ -341,10 +347,6 @@ class Server {
|
|||
return purged
|
||||
}
|
||||
|
||||
authMiddleware(req, res, next) {
|
||||
this.auth.authMiddleware(req, res, next)
|
||||
}
|
||||
|
||||
async handleUpload(req, res) {
|
||||
if (!req.user.canUpload) {
|
||||
Logger.warn('User attempted to upload without permission', req.user)
|
||||
|
|
@ -460,6 +462,11 @@ class Server {
|
|||
}
|
||||
}
|
||||
|
||||
socket.broadcast.emit('user_online', client.user.toJSONForPublic(this.streamManager.streams))
|
||||
|
||||
user.lastSeen = Date.now()
|
||||
await this.db.updateEntity('user', user)
|
||||
|
||||
const initialPayload = {
|
||||
serverSettings: this.serverSettings.toJSON(),
|
||||
audiobookPath: this.AudiobookPath,
|
||||
|
|
|
|||
|
|
@ -111,6 +111,8 @@ class StreamManager {
|
|||
|
||||
var stream = await this.openStream(client, audiobook)
|
||||
this.db.updateUserStream(client.user.id, stream.id)
|
||||
|
||||
socket.broadcast.emit('user_stream_update', client.user.toJSONForPublic(this.streams))
|
||||
}
|
||||
|
||||
async closeStreamRequest(socket) {
|
||||
|
|
@ -126,6 +128,8 @@ class StreamManager {
|
|||
client.user.stream = null
|
||||
client.stream = null
|
||||
this.db.updateUserStream(client.user.id, null)
|
||||
|
||||
socket.broadcast.emit('user_stream_update', client.user.toJSONForPublic(this.streams))
|
||||
}
|
||||
|
||||
async openTestStream(StreamsPath, audiobookId) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
class AudiobookProgress {
|
||||
constructor(progress) {
|
||||
this.audiobookId = null
|
||||
this.audiobookTitle = null
|
||||
this.totalDuration = null // seconds
|
||||
this.progress = null // 0 to 1
|
||||
this.currentTime = null // seconds
|
||||
|
|
@ -17,6 +18,7 @@ class AudiobookProgress {
|
|||
toJSON() {
|
||||
return {
|
||||
audiobookId: this.audiobookId,
|
||||
audiobookTitle: this.audiobookTitle,
|
||||
totalDuration: this.totalDuration,
|
||||
progress: this.progress,
|
||||
currentTime: this.currentTime,
|
||||
|
|
@ -29,6 +31,7 @@ class AudiobookProgress {
|
|||
|
||||
construct(progress) {
|
||||
this.audiobookId = progress.audiobookId
|
||||
this.audiobookTitle = progress.audiobookTitle || null
|
||||
this.totalDuration = progress.totalDuration
|
||||
this.progress = progress.progress
|
||||
this.currentTime = progress.currentTime
|
||||
|
|
@ -40,6 +43,7 @@ class AudiobookProgress {
|
|||
|
||||
updateFromStream(stream) {
|
||||
this.audiobookId = stream.audiobookId
|
||||
this.audiobookTitle = stream.audiobookTitle
|
||||
this.totalDuration = stream.totalDuration
|
||||
this.progress = stream.clientProgress
|
||||
this.currentTime = stream.clientCurrentTime
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ class Library {
|
|||
this.id = null
|
||||
this.name = null
|
||||
this.folders = []
|
||||
this.displayOrder = 1
|
||||
this.icon = 'database'
|
||||
|
||||
this.lastScan = 0
|
||||
|
|
@ -25,6 +26,7 @@ class Library {
|
|||
this.id = library.id
|
||||
this.name = library.name
|
||||
this.folders = (library.folders || []).map(f => new Folder(f))
|
||||
this.displayOrder = library.displayOrder || 1
|
||||
this.icon = library.icon || 'database'
|
||||
|
||||
this.createdAt = library.createdAt
|
||||
|
|
@ -36,6 +38,7 @@ class Library {
|
|||
id: this.id,
|
||||
name: this.name,
|
||||
folders: (this.folders || []).map(f => f.toJSON()),
|
||||
displayOrder: this.displayOrder,
|
||||
icon: this.icon,
|
||||
createdAt: this.createdAt,
|
||||
lastUpdate: this.lastUpdate
|
||||
|
|
@ -59,6 +62,7 @@ class Library {
|
|||
return newFolder
|
||||
})
|
||||
}
|
||||
this.displayOrder = data.displayOrder || 1
|
||||
this.icon = data.icon || 'database'
|
||||
this.createdAt = Date.now()
|
||||
this.lastUpdate = Date.now()
|
||||
|
|
@ -70,6 +74,10 @@ class Library {
|
|||
this.name = payload.name
|
||||
hasUpdates = true
|
||||
}
|
||||
if (!isNaN(payload.displayOrder) && payload.displayOrder !== this.displayOrder) {
|
||||
this.displayOrder = Number(payload.displayOrder)
|
||||
hasUpdates = true
|
||||
}
|
||||
if (payload.folders) {
|
||||
var newFolders = payload.folders.filter(f => !f.id)
|
||||
var removedFolders = this.folders.filter(f => !payload.folders.find(_f => _f.id === f.id))
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ class User {
|
|||
this.token = null
|
||||
this.isActive = true
|
||||
this.isLocked = false
|
||||
this.lastSeen = null
|
||||
this.createdAt = null
|
||||
this.audiobooks = null
|
||||
|
||||
|
|
@ -78,6 +79,7 @@ class User {
|
|||
audiobooks: this.audiobooksToJSON(),
|
||||
isActive: this.isActive,
|
||||
isLocked: this.isLocked,
|
||||
lastSeen: this.lastSeen,
|
||||
createdAt: this.createdAt,
|
||||
settings: this.settings,
|
||||
permissions: this.permissions
|
||||
|
|
@ -94,12 +96,25 @@ class User {
|
|||
audiobooks: this.audiobooksToJSON(),
|
||||
isActive: this.isActive,
|
||||
isLocked: this.isLocked,
|
||||
lastSeen: this.lastSeen,
|
||||
createdAt: this.createdAt,
|
||||
settings: this.settings,
|
||||
permissions: this.permissions
|
||||
}
|
||||
}
|
||||
|
||||
toJSONForPublic(streams) {
|
||||
var stream = this.stream && streams ? streams.find(s => s.id === this.stream) : null
|
||||
return {
|
||||
id: this.id,
|
||||
username: this.username,
|
||||
type: this.type,
|
||||
stream: stream ? stream.toJSON() : null,
|
||||
lastSeen: this.lastSeen,
|
||||
createdAt: this.createdAt
|
||||
}
|
||||
}
|
||||
|
||||
construct(user) {
|
||||
this.id = user.id
|
||||
this.username = user.username
|
||||
|
|
@ -117,6 +132,7 @@ class User {
|
|||
}
|
||||
this.isActive = (user.isActive === undefined || user.type === 'root') ? true : !!user.isActive
|
||||
this.isLocked = user.type === 'root' ? false : !!user.isLocked
|
||||
this.lastSeen = user.lastSeen || null
|
||||
this.createdAt = user.createdAt || Date.now()
|
||||
this.settings = user.settings || this.getDefaultUserSettings()
|
||||
this.permissions = user.permissions || this.getDefaultUserPermissions()
|
||||
|
|
|
|||
|
|
@ -1,404 +0,0 @@
|
|||
function ab2str(buf) {
|
||||
if (buf instanceof ArrayBuffer) {
|
||||
buf = new Uint8Array(buf);
|
||||
}
|
||||
return new TextDecoder("utf-8").decode(buf);
|
||||
}
|
||||
|
||||
var domParser = new DOMParser();
|
||||
|
||||
class Buffer {
|
||||
constructor(capacity) {
|
||||
this.capacity = capacity;
|
||||
this.fragment_list = [];
|
||||
this.cur_fragment = new Fragment(capacity);
|
||||
this.fragment_list.push(this.cur_fragment);
|
||||
}
|
||||
write(byte) {
|
||||
var result = this.cur_fragment.write(byte);
|
||||
if (!result) {
|
||||
this.cur_fragment = new Fragment(this.capacity);
|
||||
this.fragment_list.push(this.cur_fragment);
|
||||
this.cur_fragment.write(byte);
|
||||
}
|
||||
}
|
||||
get(idx) {
|
||||
var fi = 0;
|
||||
while (fi < this.fragment_list.length) {
|
||||
var frag = this.fragment_list[fi];
|
||||
if (idx < frag.size) {
|
||||
return frag.get(idx);
|
||||
}
|
||||
idx -= frag.size;
|
||||
fi += 1;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
size() {
|
||||
var s = 0;
|
||||
for (var i = 0; i < this.fragment_list.length; i++) {
|
||||
s += this.fragment_list[i].size;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
shrink() {
|
||||
var total_buffer = new Uint8Array(this.size());
|
||||
var offset = 0;
|
||||
for (var i = 0; i < this.fragment_list.length; i++) {
|
||||
var frag = this.fragment_list[i];
|
||||
if (frag.full()) {
|
||||
total_buffer.set(frag.buffer, offset);
|
||||
} else {
|
||||
total_buffer.set(frag.buffer.slice(0, frag.size), offset);
|
||||
}
|
||||
offset += frag.size;
|
||||
}
|
||||
return total_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
var combine_uint8array = function (buffers) {
|
||||
var total_size = 0;
|
||||
for (var i = 0; i < buffers.length; i++) {
|
||||
var buffer = buffers[i];
|
||||
total_size += buffer.length;
|
||||
}
|
||||
var total_buffer = new Uint8Array(total_size);
|
||||
var offset = 0;
|
||||
for (var i = 0; i < buffers.length; i++) {
|
||||
var buffer = buffers[i];
|
||||
total_buffer.set(buffer, offset);
|
||||
offset += buffer.length;
|
||||
}
|
||||
return total_buffer;
|
||||
}
|
||||
|
||||
class Fragment {
|
||||
constructor(capacity) {
|
||||
this.buffer = new Uint8Array(capacity);
|
||||
this.capacity = capacity;
|
||||
this.size = 0;
|
||||
}
|
||||
|
||||
write(byte) {
|
||||
if (this.size >= this.capacity) {
|
||||
return false;
|
||||
}
|
||||
this.buffer[this.size] = byte;
|
||||
this.size += 1;
|
||||
return true;
|
||||
}
|
||||
full() {
|
||||
return this.size === this.capacity;
|
||||
}
|
||||
get(idx) {
|
||||
return this.buffer[idx];
|
||||
}
|
||||
}
|
||||
|
||||
var uncompression_lz77 = function (data) {
|
||||
var length = data.length;
|
||||
var offset = 0; // Current offset into data
|
||||
var buffer = new Buffer(data.length);
|
||||
|
||||
while (offset < length) {
|
||||
var char = data[offset];
|
||||
offset += 1;
|
||||
|
||||
if (char == 0) {
|
||||
buffer.write(char);
|
||||
} else if (char <= 8) {
|
||||
for (var i = offset; i < offset + char; i++) {
|
||||
buffer.write(data[i]);
|
||||
}
|
||||
offset += char;
|
||||
} else if (char <= 0x7f) {
|
||||
buffer.write(char);
|
||||
} else if (char <= 0xbf) {
|
||||
var next = data[offset];
|
||||
offset += 1;
|
||||
var distance = ((char << 8 | next) >> 3) & 0x7ff;
|
||||
var lz_length = (next & 0x7) + 3;
|
||||
|
||||
var buffer_size = buffer.size();
|
||||
for (var i = 0; i < lz_length; i++) {
|
||||
buffer.write(buffer.get(buffer_size - distance))
|
||||
buffer_size += 1;
|
||||
}
|
||||
} else {
|
||||
buffer.write(32);
|
||||
buffer.write(char ^ 0x80);
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
};
|
||||
|
||||
class MobiFile {
|
||||
constructor(data) {
|
||||
this.view = new DataView(data);
|
||||
this.buffer = this.view.buffer;
|
||||
this.offset = 0;
|
||||
this.header = null;
|
||||
}
|
||||
|
||||
parse() {
|
||||
|
||||
}
|
||||
|
||||
getUint8() {
|
||||
var v = this.view.getUint8(this.offset);
|
||||
this.offset += 1;
|
||||
return v;
|
||||
}
|
||||
|
||||
getUint16() {
|
||||
var v = this.view.getUint16(this.offset);
|
||||
this.offset += 2;
|
||||
return v;
|
||||
}
|
||||
|
||||
getUint32() {
|
||||
var v = this.view.getUint32(this.offset);
|
||||
this.offset += 4;
|
||||
return v;
|
||||
}
|
||||
|
||||
getStr(size) {
|
||||
var v = ab2str(this.buffer.slice(this.offset, this.offset + size));
|
||||
this.offset += size;
|
||||
return v;
|
||||
}
|
||||
|
||||
skip(size) {
|
||||
this.offset += size;
|
||||
}
|
||||
|
||||
setoffset(_of) {
|
||||
this.offset = _of;
|
||||
}
|
||||
|
||||
get_record_extrasize(data, flags) {
|
||||
var pos = data.length - 1;
|
||||
var extra = 0;
|
||||
for (var i = 15; i > 0; i--) {
|
||||
if (flags & (1 << i)) {
|
||||
var res = this.buffer_get_varlen(data, pos);
|
||||
var size = res[0];
|
||||
var l = res[1];
|
||||
pos = res[2];
|
||||
pos -= size - l;
|
||||
extra += size;
|
||||
}
|
||||
}
|
||||
if (flags & 1) {
|
||||
var a = data[pos];
|
||||
extra += (a & 0x3) + 1;
|
||||
}
|
||||
return extra;
|
||||
}
|
||||
|
||||
// data should be uint8array
|
||||
buffer_get_varlen(data, pos) {
|
||||
var l = 0;
|
||||
var size = 0;
|
||||
var byte_count = 0;
|
||||
var mask = 0x7f;
|
||||
var stop_flag = 0x80;
|
||||
var shift = 0;
|
||||
for (var i = 0; ; i++) {
|
||||
var byte = data[pos];
|
||||
size |= (byte & mask) << shift;
|
||||
shift += 7;
|
||||
l += 1;
|
||||
byte_count += 1;
|
||||
pos -= 1;
|
||||
|
||||
var to_stop = byte & stop_flag;
|
||||
if (byte_count >= 4 || to_stop > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return [size, l, pos];
|
||||
}
|
||||
|
||||
read_text() {
|
||||
var text_end = this.palm_header.record_count;
|
||||
var buffers = [];
|
||||
for (var i = 1; i <= text_end; i++) {
|
||||
buffers.push(this.read_text_record(i));
|
||||
}
|
||||
var all = combine_uint8array(buffers)
|
||||
return ab2str(all);
|
||||
}
|
||||
|
||||
read_text_record(i) {
|
||||
var flags = this.mobi_header.extra_flags;
|
||||
var begin = this.reclist[i].offset;
|
||||
var end = this.reclist[i + 1].offset;
|
||||
|
||||
var data = new Uint8Array(this.buffer.slice(begin, end))
|
||||
var ex = this.get_record_extrasize(data, flags);
|
||||
|
||||
data = new Uint8Array(this.buffer.slice(begin, end - ex));
|
||||
if (this.palm_header.compression === 2) {
|
||||
var buffer = uncompression_lz77(data);
|
||||
return buffer.shrink();
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
read_image(idx) {
|
||||
var first_image_idx = this.mobi_header.first_image_idx;
|
||||
var begin = this.reclist[first_image_idx + idx].offset;
|
||||
var end = this.reclist[first_image_idx + idx + 1].offset;
|
||||
var data = new Uint8Array(this.buffer.slice(begin, end))
|
||||
return new Blob([data.buffer]);
|
||||
}
|
||||
|
||||
load() {
|
||||
this.header = this.load_pdbheader();
|
||||
this.reclist = this.load_reclist();
|
||||
this.load_record0();
|
||||
}
|
||||
|
||||
load_pdbheader() {
|
||||
var header = {};
|
||||
header.name = this.getStr(32);
|
||||
header.attr = this.getUint16();
|
||||
header.version = this.getUint16();
|
||||
header.ctime = this.getUint32();
|
||||
header.mtime = this.getUint32();
|
||||
header.btime = this.getUint32();
|
||||
header.mod_num = this.getUint32();
|
||||
header.appinfo_offset = this.getUint32();
|
||||
header.sortinfo_offset = this.getUint32();
|
||||
header.type = this.getStr(4);
|
||||
header.creator = this.getStr(4);
|
||||
header.uid = this.getUint32();
|
||||
header.next_rec = this.getUint32();
|
||||
header.record_num = this.getUint16();
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
load_reclist() {
|
||||
var reclist = [];
|
||||
for (var i = 0; i < this.header.record_num; i++) {
|
||||
var record = {};
|
||||
record.offset = this.getUint32();
|
||||
// TODO(zz) change
|
||||
record.attr = this.getUint32();
|
||||
reclist.push(record);
|
||||
}
|
||||
return reclist;
|
||||
}
|
||||
load_record0() {
|
||||
this.palm_header = this.load_record0_header();
|
||||
this.mobi_header = this.load_mobi_header();
|
||||
}
|
||||
|
||||
load_record0_header() {
|
||||
var p_header = {};
|
||||
var first_record = this.reclist[0];
|
||||
this.setoffset(first_record.offset);
|
||||
|
||||
p_header.compression = this.getUint16();
|
||||
this.skip(2);
|
||||
p_header.text_length = this.getUint32();
|
||||
p_header.record_count = this.getUint16();
|
||||
p_header.record_size = this.getUint16();
|
||||
p_header.encryption_type = this.getUint16();
|
||||
this.skip(2);
|
||||
|
||||
return p_header;
|
||||
}
|
||||
|
||||
load_mobi_header() {
|
||||
var mobi_header = {};
|
||||
|
||||
var start_offset = this.offset;
|
||||
|
||||
mobi_header.identifier = this.getUint32();
|
||||
mobi_header.header_length = this.getUint32();
|
||||
mobi_header.mobi_type = this.getUint32();
|
||||
mobi_header.text_encoding = this.getUint32();
|
||||
mobi_header.uid = this.getUint32();
|
||||
mobi_header.generator_version = this.getUint32();
|
||||
|
||||
this.skip(40);
|
||||
|
||||
mobi_header.first_nonbook_index = this.getUint32();
|
||||
mobi_header.full_name_offset = this.getUint32();
|
||||
mobi_header.full_name_length = this.getUint32();
|
||||
|
||||
mobi_header.language = this.getUint32();
|
||||
mobi_header.input_language = this.getUint32();
|
||||
mobi_header.output_language = this.getUint32();
|
||||
mobi_header.min_version = this.getUint32();
|
||||
mobi_header.first_image_idx = this.getUint32();
|
||||
|
||||
mobi_header.huff_rec_index = this.getUint32();
|
||||
mobi_header.huff_rec_count = this.getUint32();
|
||||
mobi_header.datp_rec_index = this.getUint32();
|
||||
mobi_header.datp_rec_count = this.getUint32();
|
||||
|
||||
mobi_header.exth_flags = this.getUint32();
|
||||
|
||||
this.skip(36);
|
||||
|
||||
mobi_header.drm_offset = this.getUint32();
|
||||
mobi_header.drm_count = this.getUint32();
|
||||
mobi_header.drm_size = this.getUint32();
|
||||
mobi_header.drm_flags = this.getUint32();
|
||||
|
||||
this.skip(8);
|
||||
|
||||
// TODO (zz) fdst_index
|
||||
this.skip(4);
|
||||
|
||||
this.skip(46);
|
||||
|
||||
mobi_header.extra_flags = this.getUint16();
|
||||
|
||||
this.setoffset(start_offset + mobi_header.header_length)
|
||||
|
||||
return mobi_header;
|
||||
}
|
||||
load_exth_header() {
|
||||
// TODO
|
||||
return {};
|
||||
}
|
||||
|
||||
render_to(id) {
|
||||
this.load();
|
||||
var content = this.read_text();
|
||||
|
||||
var bookDom = document.getElementById(id);
|
||||
while (bookDom.firstChild) {
|
||||
bookDom.removeChild(bookDom.firstChild);
|
||||
}
|
||||
|
||||
var bookDoc = domParser.parseFromString(content, "text/html");
|
||||
bookDoc.body.childNodes.forEach(function (x) {
|
||||
if (x instanceof Element) {
|
||||
bookDom.appendChild(x);
|
||||
}
|
||||
});
|
||||
|
||||
var imgDoms = bookDom.getElementsByTagName("img");
|
||||
for (var i = 0; i < imgDoms.length; i++) {
|
||||
this.render_image(imgDoms, i);
|
||||
}
|
||||
}
|
||||
render_image(imgDoms, i) {
|
||||
var imgDom = imgDoms[i];
|
||||
var idx = +imgDom.getAttribute("recindex");
|
||||
var blob = this.read_image(idx - 1);
|
||||
var imgReader = new FileReader();
|
||||
imgReader.onload = function (e) {
|
||||
imgDom.src = e.target.result;
|
||||
};
|
||||
imgReader.readAsDataURL(blob);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue