Работа над opds
This commit is contained in:
@@ -534,28 +534,105 @@ class DbSearcher {
|
||||
}
|
||||
}
|
||||
|
||||
async getAuthorBookList(authorId) {
|
||||
async opdsQuery(from, query) {
|
||||
if (this.closed)
|
||||
throw new Error('DbSearcher closed');
|
||||
|
||||
if (!authorId)
|
||||
if (!['author', 'series', 'title'].includes(from))
|
||||
throw new Error(`Unknown value for param 'from'`);
|
||||
|
||||
this.searchFlag++;
|
||||
|
||||
try {
|
||||
const db = this.db;
|
||||
|
||||
const queryKey = this.queryKey(query);
|
||||
const opdsKey = `${from}-opds-${queryKey}`;
|
||||
let result = await this.getCached(opdsKey);
|
||||
|
||||
if (result === null) {
|
||||
const ids = await this.selectTableIds(from, query);
|
||||
|
||||
const totalFound = ids.length;
|
||||
const depth = query.depth || 1;
|
||||
|
||||
//группировка по name длиной depth
|
||||
const found = await db.select({
|
||||
table: from,
|
||||
rawResult: true,
|
||||
where: `
|
||||
const depth = ${db.esc(depth)};
|
||||
const group = new Map();
|
||||
|
||||
const ids = ${db.esc(Array.from(ids))};
|
||||
for (const id of ids) {
|
||||
const row = @unsafeRow(id);
|
||||
const s = row.name.substring(0, depth);
|
||||
let g = group.get(s);
|
||||
if (!g) {
|
||||
g = {id: row.id, name: s, count: 0};
|
||||
group.set(s, g);
|
||||
}
|
||||
g.count++;
|
||||
}
|
||||
|
||||
const result = Array.from(group.values());
|
||||
result.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
return result;
|
||||
`
|
||||
});
|
||||
|
||||
result = {found: found[0].rawResult, totalFound};
|
||||
|
||||
await this.putCached(opdsKey, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
this.searchFlag--;
|
||||
}
|
||||
}
|
||||
|
||||
async getAuthorBookList(authorId, author) {
|
||||
if (this.closed)
|
||||
throw new Error('DbSearcher closed');
|
||||
|
||||
if (!authorId && !author)
|
||||
return {author: '', books: ''};
|
||||
|
||||
this.searchFlag++;
|
||||
|
||||
try {
|
||||
//выборка книг автора по authorId
|
||||
const rows = await this.restoreBooks('author', [authorId])
|
||||
const db = this.db;
|
||||
|
||||
let author = '';
|
||||
if (!authorId) {
|
||||
//восстановим authorId
|
||||
authorId = 0;
|
||||
author = author.toLowerCase();
|
||||
|
||||
const rows = await db.select({
|
||||
table: 'author',
|
||||
rawResult: true,
|
||||
where: `return Array.from(@dirtyIndexLR('value', ${db.esc(author)}, ${db.esc(author)}))`
|
||||
});
|
||||
|
||||
if (rows.length && rows[0].rawResult.length)
|
||||
authorId = rows[0].rawResult[0];
|
||||
}
|
||||
|
||||
//выборка книг автора по authorId
|
||||
const rows = await this.restoreBooks('author', [authorId]);
|
||||
|
||||
let authorName = '';
|
||||
let books = '';
|
||||
|
||||
if (rows.length) {
|
||||
author = rows[0].name;
|
||||
authorName = rows[0].name;
|
||||
books = rows[0].books;
|
||||
}
|
||||
|
||||
return {author, books: (books && books.length ? JSON.stringify(books) : '')};
|
||||
return {author: authorName, books: (books && books.length ? JSON.stringify(books) : '')};
|
||||
} finally {
|
||||
this.searchFlag--;
|
||||
}
|
||||
|
||||
@@ -267,10 +267,16 @@ class WebWorker {
|
||||
return result;
|
||||
}
|
||||
|
||||
async getAuthorBookList(authorId) {
|
||||
async opdsQuery(from, query) {
|
||||
this.checkMyState();
|
||||
|
||||
return await this.dbSearcher.getAuthorBookList(authorId);
|
||||
return await this.dbSearcher.opdsQuery(from, query);
|
||||
}
|
||||
|
||||
async getAuthorBookList(authorId, author) {
|
||||
this.checkMyState();
|
||||
|
||||
return await this.dbSearcher.getAuthorBookList(authorId, author);
|
||||
}
|
||||
|
||||
async getSeriesBookList(series) {
|
||||
|
||||
@@ -8,12 +8,66 @@ class AuthorPage extends BasePage {
|
||||
this.title = 'Авторы';
|
||||
}
|
||||
|
||||
async body() {
|
||||
bookAuthor(author) {
|
||||
if (author) {
|
||||
let a = author.split(',');
|
||||
return a.slice(0, 3).join(', ') + (a.length > 3 ? ' и др.' : '');
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
async body(req) {
|
||||
const result = {};
|
||||
|
||||
result.entry = [
|
||||
];
|
||||
const query = {author: '', depth: 1, del: 0, limit: 100};
|
||||
if (req.query.author) {
|
||||
query.author = req.query.author;
|
||||
query.depth = query.author.length + 1;
|
||||
}
|
||||
|
||||
if (req.query.author == '___others') {
|
||||
query.author = '';
|
||||
query.depth = 1;
|
||||
query.others = true;
|
||||
}
|
||||
|
||||
const entry = [];
|
||||
if (query.author && query.author[0] == '=') {
|
||||
//книги по автору
|
||||
const bookList = await this.webWorker.getAuthorBookList(0, query.author.substring(1));
|
||||
|
||||
if (bookList.books) {
|
||||
const books = JSON.parse(bookList.books);
|
||||
|
||||
for (const book of books) {
|
||||
const title = book.title || 'Без названия';
|
||||
entry.push(
|
||||
this.makeEntry({
|
||||
id: book._uid,
|
||||
title,
|
||||
link: this.navLink({rel: 'subsection', href: `/${this.id}?book=${book._uid}`}),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//поиск по каталогу
|
||||
const queryRes = await this.opdsQuery('author', query);
|
||||
|
||||
for (const rec of queryRes) {
|
||||
console.log(rec);
|
||||
entry.push(
|
||||
this.makeEntry({
|
||||
id: rec.id,
|
||||
title: this.bookAuthor(rec.title),//${(query.depth > 1 && rec.count ? ` (${rec.count})` : '')}
|
||||
link: this.navLink({rel: 'subsection', href: `/${this.id}?author=${rec.q}`}),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
result.entry = entry;
|
||||
return this.makeBody(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
const he = require('he');
|
||||
|
||||
const WebWorker = require('../WebWorker');//singleton
|
||||
const XmlParser = require('../xml/XmlParser');
|
||||
|
||||
const spaceChar = String.fromCodePoint(0x00B7);
|
||||
const ruAlphabet = 'абвгдеёжзийклмнопрстуфхцчшщъыьэюя';
|
||||
const enAlphabet = 'abcdefghijklmnopqrstuvwxyz';
|
||||
const enruArr = (ruAlphabet + enAlphabet).split('');
|
||||
const enru = new Set(enruArr);
|
||||
|
||||
class BasePage {
|
||||
constructor(config) {
|
||||
this.config = config;
|
||||
@@ -16,6 +24,8 @@ class BasePage {
|
||||
if (!entry.title)
|
||||
throw new Error('makeEntry: no title');
|
||||
|
||||
entry.title = he.escape(entry.title);
|
||||
|
||||
const result = {
|
||||
updated: (new Date()).toISOString().substring(0, 19) + 'Z',
|
||||
};
|
||||
@@ -73,6 +83,62 @@ class BasePage {
|
||||
async body() {
|
||||
throw new Error('Body not implemented');
|
||||
}
|
||||
|
||||
// -- stuff -------------------------------------------
|
||||
async search(from, query) {
|
||||
const result = [];
|
||||
const queryRes = await this.webWorker.search(from, query);
|
||||
|
||||
for (const row of queryRes.found) {
|
||||
const rec = {
|
||||
id: row.id,
|
||||
title: '=' + (row[from] || 'Без имени'),
|
||||
q: `=${encodeURIComponent(row[from])}`,
|
||||
};
|
||||
|
||||
result.push(rec);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async opdsQuery(from, query) {
|
||||
const result = [];
|
||||
|
||||
const queryRes = await this.webWorker.opdsQuery(from, query);
|
||||
let count = 0;
|
||||
for (const row of queryRes.found)
|
||||
count += row.count;
|
||||
|
||||
if (count <= query.limit)
|
||||
return await this.search(from, query);
|
||||
|
||||
const names = new Set();
|
||||
const others = [];
|
||||
for (const row of queryRes.found) {
|
||||
const name = row.name.toUpperCase();
|
||||
|
||||
if (!names.has(name)) {
|
||||
const rec = {
|
||||
id: row.id,
|
||||
title: name.replace(/ /g, spaceChar),
|
||||
q: encodeURIComponent(row.name.toLowerCase()),
|
||||
count: row.count,
|
||||
};
|
||||
if (query.depth > 1 || enru.has(row.name[0].toLowerCase())) {
|
||||
result.push(rec);
|
||||
} else {
|
||||
others.push(rec);
|
||||
}
|
||||
names.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!query.others && query.depth == 1)
|
||||
result.push({id: 'other', title: 'Все остальные', q: '___others'});
|
||||
|
||||
return (!query.others ? result : others);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BasePage;
|
||||
@@ -13,10 +13,9 @@ class RootPage extends BasePage {
|
||||
|
||||
async body() {
|
||||
const result = {};
|
||||
const ww = this.webWorker;
|
||||
|
||||
if (!this.title) {
|
||||
const dbConfig = await ww.dbConfig();
|
||||
const dbConfig = await this.webWorker.dbConfig();
|
||||
const collection = dbConfig.inpxInfo.collection.split('\n');
|
||||
this.title = collection[0].trim();
|
||||
if (!this.title)
|
||||
|
||||
Reference in New Issue
Block a user