BookPage.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. const path = require('path');
  2. const _ = require('lodash');
  3. const he = require('he');
  4. const dayjs = require('dayjs');
  5. const BasePage = require('./BasePage');
  6. const Fb2Parser = require('../fb2/Fb2Parser');
  7. class BookPage extends BasePage {
  8. constructor(config) {
  9. super(config);
  10. this.id = 'book';
  11. this.title = 'Книга';
  12. }
  13. formatSize(size) {
  14. size = size/1024;
  15. let unit = 'KB';
  16. if (size > 1024) {
  17. size = size/1024;
  18. unit = 'MB';
  19. }
  20. return `${size.toFixed(1)} ${unit}`;
  21. }
  22. inpxInfo(bookRec) {
  23. const mapping = [
  24. {name: 'fileInfo', label: 'Информация о файле', value: [
  25. {name: 'folder', label: 'Папка'},
  26. {name: 'file', label: 'Файл'},
  27. {name: 'size', label: 'Размер'},
  28. {name: 'date', label: 'Добавлен'},
  29. {name: 'del', label: 'Удален'},
  30. {name: 'libid', label: 'LibId'},
  31. {name: 'insno', label: 'InsideNo'},
  32. ]},
  33. {name: 'titleInfo', label: 'Общая информация', value: [
  34. {name: 'author', label: 'Автор(ы)'},
  35. {name: 'title', label: 'Название'},
  36. {name: 'series', label: 'Серия'},
  37. {name: 'genre', label: 'Жанр'},
  38. {name: 'librate', label: 'Оценка'},
  39. {name: 'lang', label: 'Язык книги'},
  40. {name: 'keywords', label: 'Ключевые слова'},
  41. ]},
  42. ];
  43. const valueToString = (value, nodePath, b) => {//eslint-disable-line no-unused-vars
  44. if (nodePath == 'fileInfo/file')
  45. return `${value}.${b.ext}`;
  46. if (nodePath == 'fileInfo/size')
  47. return `${this.formatSize(value)} (${value.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1 ')} Bytes)`;
  48. if (nodePath == 'fileInfo/date')
  49. return dayjs(value, 'YYYY-MM-DD').format('DD.MM.YYYY');
  50. if (nodePath == 'fileInfo/del')
  51. return (value ? 'Да' : null);
  52. if (nodePath == 'fileInfo/insno')
  53. return (value ? value : null);
  54. if (nodePath == 'titleInfo/author')
  55. return value.split(',').join(', ');
  56. if (nodePath == 'titleInfo/librate' && !value)
  57. return null;
  58. if (typeof(value) === 'string') {
  59. return value;
  60. }
  61. return (value.toString ? value.toString() : '');
  62. };
  63. let result = [];
  64. const book = _.cloneDeep(bookRec);
  65. book.series = [book.series, book.serno].filter(v => v).join(' #');
  66. for (const item of mapping) {
  67. const itemOut = {name: item.name, label: item.label, value: []};
  68. for (const subItem of item.value) {
  69. const subItemOut = {
  70. name: subItem.name,
  71. label: subItem.label,
  72. value: valueToString(book[subItem.name], `${item.name}/${subItem.name}`, book)
  73. };
  74. if (subItemOut.value)
  75. itemOut.value.push(subItemOut);
  76. }
  77. if (itemOut.value.length)
  78. result.push(itemOut);
  79. }
  80. return result;
  81. }
  82. htmlInfo(title, infoList) {
  83. let info = '';
  84. for (const part of infoList) {
  85. if (part.value.length)
  86. info += `<h3>${part.label}</h3>`;
  87. for (const rec of part.value)
  88. info += `<p>${rec.label}: ${rec.value}</p>`;
  89. }
  90. if (info)
  91. info = `<h2>${title}</h2>${info}`;
  92. return info;
  93. }
  94. async body(req) {
  95. const result = {};
  96. result.link = this.baseLinks(req, true);
  97. const bookUid = req.query.uid;
  98. const entry = [];
  99. if (bookUid) {
  100. const {bookInfo} = await this.webWorker.getBookInfo(bookUid);
  101. if (bookInfo) {
  102. const {genreMap} = await this.getGenres();
  103. //format
  104. const ext = bookInfo.book.ext;
  105. let fileFormat = `${ext}+zip`;
  106. let downHref = bookInfo.link;
  107. if (ext === 'mobi') {
  108. fileFormat = 'x-mobipocket-ebook';
  109. } else if (ext == 'epub') {
  110. //
  111. } else {
  112. downHref = `${bookInfo.link}/zip`;
  113. }
  114. //entry
  115. const e = this.makeEntry({
  116. id: bookUid,
  117. title: bookInfo.book.title || 'Без названия',
  118. });
  119. e['dc:language'] = bookInfo.book.lang;
  120. e['dc:format'] = fileFormat;
  121. //genre
  122. const genre = bookInfo.book.genre.split(',');
  123. for (const g of genre) {
  124. const genreName = genreMap.get(g);
  125. if (genreName) {
  126. if (!e.category)
  127. e.category = [];
  128. e.category.push({
  129. '*ATTRS': {term: genreName, label: genreName},
  130. });
  131. }
  132. }
  133. let content = '';
  134. let ann = '';
  135. let info = '';
  136. //fb2 info
  137. if (bookInfo.fb2) {
  138. const parser = new Fb2Parser(bookInfo.fb2);
  139. const infoObj = parser.bookInfo();
  140. if (infoObj.titleInfo) {
  141. if (infoObj.titleInfo.author.length) {
  142. e.author = infoObj.titleInfo.author.map(a => ({name: a}));
  143. }
  144. ann = infoObj.titleInfo.annotationHtml || '';
  145. const infoList = parser.bookInfoList(infoObj);
  146. info += this.htmlInfo('Fb2 инфо', infoList);
  147. }
  148. }
  149. //content
  150. info += this.htmlInfo('Inpx инфо', this.inpxInfo(bookInfo.book));
  151. content = `${ann}${info}`;
  152. if (content) {
  153. e.content = {
  154. '*ATTRS': {type: 'text/html'},
  155. '*TEXT': he.escape(content),
  156. };
  157. }
  158. //links
  159. e.link = [ this.downLink({href: downHref, type: `application/${fileFormat}`}) ];
  160. if (bookInfo.cover) {
  161. let coverType = 'image/jpeg';
  162. if (path.extname(bookInfo.cover) == '.png')
  163. coverType = 'image/png';
  164. e.link.push(this.imgLink({href: bookInfo.cover, type: coverType}));
  165. e.link.push(this.imgLink({href: bookInfo.cover, type: coverType, thumb: true}));
  166. }
  167. entry.push(e);
  168. }
  169. }
  170. result.entry = entry;
  171. return this.makeBody(result, req);
  172. }
  173. }
  174. module.exports = BookPage;