Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 102 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ OPDS-сервер доступен по адресу [http://127.0.0.1:12380/opd
* [Конфигурация](#config)
* [Удаленная библиотека](#remotelib)
* [Фильтр по авторам и книгам](#filter)
* [Подключение внешних программ (конвертеры, читалки, экспорт)](#external_tools)
* [Настройка https с помощью nginx](#https)
* [Сборка релизов](#build)
* [Запуск без сборки релиза](#native_run)
Expand Down Expand Up @@ -114,6 +115,10 @@ Options:
// пустая строка: использовать значение по умолчанию - файл filter.json в директории файла конфигурации
"inpxFilterFile": "",

// конфигурационный файл для подключения внешник утилит и програм: конвертеров, читалок, экспорта и прочего
// пустая строка: использовать значение по умолчанию - файл external_tools.json в директории файла конфигурации
"externalToolsConfig": "",

// разрешить(true)/запретить(false) перезаписывать файл конфигурации, если появились новые параметры для настройки
// файл перезаписывается с сохранением всех предыдущих настроек и с новыми по умолчанию
// бывает полезно при выходе новых версий приложения
Expand Down Expand Up @@ -263,7 +268,7 @@ Options:
На клиенте:
```
"remoteLib": {
"accessPassword": "123456",
"accessPassword": "123456",
"url": "ws://server.host:12380"
},
```
Expand Down Expand Up @@ -328,6 +333,102 @@ Options:
или разрешением в конфиге `allowUnsafeFilter`.
Названия атрибутов inpxRec соответствуют названиям в нижнем регистре из структуры structure.info в .inpx-файле.
Файл `filter.json` можно расположить где угодно, что задается параметром `inpxFilterFile` в конфиге.

<a id="external_tools" />

### Подключение внешних программ (конвертеры, читалки, экспорт)

Можно подключать любые внешние программы, конвертеры, читалки, экспорты, отсылку на почту и прочее. Будут запущены команды указанные в конфиге `external_tools.json` в параметре `cmd`.

Пример минимально конфига (под Linux, для других OS - немного другие команды, но принцип тот же):

```json
{
"mobi": {
"active": true,
"cmd": "cp '${HASHFILE}.raw' '${HASHFILE}.fb2'; ${EXTDIR}/fb2c/fb2c convert --to mobi --nodirs --overwrite '${HASHFILE}.fb2'; rm -f '${HASHFILE}.fb2'",
"type": "download"
},
"pdf": {
"active": true,
"cmd": "cp '${HASHFILE}.raw' '${HASHFILE}.fb2'; java -jar '${EXTDIR}/fb2pdf/lib/fb2pdf.jar' -o -e UTF-8 '${HASHFILE}.fb2'; rm -f '${HASHFILE}.fb2'",
"type": "download"
},
"webReader": {
"active": true,
"link": "http://127.0.0.1:44080/#/reader?url=http://127.0.0.1:12382${DOWNLOAD_URI}",
"type": "link"
},
"fbReader": {
"active": true,
"cmd": "fbreader '${BOOKFILE}.raw'",
"type": "gui"
}
}
```

Возможные действия:

- `"type": "download"` // будет запущена команда `cmd` и результат будет загружен в браузере.
- `"type": "cmd"` // будет просто запущена команда `cmd`, полезно для экспорта или отсылку куда-то.
- `"type": "link"` // можно добавить свою ссылку.
- `"type": "gui"` // запуск чего-то в GUI, например, свою десктопную читалку.

Полный перечень доступных параметров (в реальном конфиге комментарии не допустимы, их нужно удалить):

```js
{
"mobiDownload": {
"active": true, // включен/выключен
"title": "(mobi)", // Название
"hint": "Скачать mobi файл", // Подсказка при наведении мышки на ссылку
"ext": "mobi", // расширение файла (для одного и того же типа файла можно выполнять разные действия)
"cmd": "...", // первая cmd команда, например конвертация
"cmdExport": "...", // вторая cmd команда, если хотим после конвертации выполнить еще действия: экспорт, отправка на почту
"debug": true, // в логе покажет, что запускалось
"type": "download"
},
"mobiExport": {
"active": true,
"title": "(mobi export)",
"hint": "Экспортировать mobi файл",
"ext": "mobi",
"cmd": "...",
"cmdExport": "...",
"debug": true,
"type": "cmd"
},
"webReader": {
"active": true,
"title": "(web)",
"hint": "Веб читалка",
"link": "http://127.0.0.1:44080/#/reader?url=http://127.0.0.1:12382${DOWNLOAD_URI}",
"type": "link"
},
"fbReader": {
"active": true,
"title": "(fbreader)",
"hint": "Десктопная читалка",
"cmd": "fbreader '${BOOKFILE}.raw'", // для типа 'gui' нет запуска второй команды 'cmdExport'
"debug": true,
"type": "gui"
}
```

Доступные переменные при запуске `cmd` или `cmdExport`:

Переменные | Описание
--- | ---
`${BOOKFILE}` | полный путь к исходному файлу
`${HASHFILE}` | хеш имени
`${RESULTFILE}` | полный путь к выходному файлу
`${FILENAME}` | культурное имя файла, то которое "download", не забываем заключать его в кавычки, т.к. там могут быть пробелы
`${EXTDIR}` | директория в которой лежит конфиг `external_tools.json`, в этой директории удобно размещать свои конвертеры и утилиты
`${LIBFOLDER}` | имя архива в котором находится книга
`${LIBFILE}` | имя файла книги в архиве

Примеры конфигов можно посмотреть в папке [examples](./examples)

<a id="https" />

### Настройка https с помощью nginx
Expand Down
26 changes: 26 additions & 0 deletions client/components/Search/BaseList.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,31 @@ export default class BaseList {
//информация о книге
const response = await this.api.getBookInfo(book._uid);
this.$emit('listEvent', {action: 'bookInfo', data: response.bookInfo});
} else {
//external tools
let extType = action.match(/^ext-([0-9a-z]+)$/i);
if (extType && this.config.external[extType[1]]) {
let ext = this.config.external[extType[1]];
if (ext.type === "download") {
href += '/' + action;
await axios.head(href);
const d = this.$refs.download;
d.href = href;
d.download = "";
d.click();
} else if (ext.type === "link") {
const hrefUrl = new URL(href);
let url = ext.link || this.config.bookReadLink;
url = url.replace('${DOWNLOAD_LINK}', href)
.replace('${DOWNLOAD_URI}', hrefUrl.pathname + hrefUrl.search + hrefUrl.hash);
window.open(url, '_blank');
} else {
href += '/' + action;
if (await axios.get(href)) {
this.$root.notify.success(ext.title || extType[1]);
}
}
}
}
} catch(e) {
this.$root.stdDialog.alert(e.message, 'Ошибка');
Expand All @@ -220,6 +245,7 @@ export default class BaseList {
case 'copyLink':
case 'readBook':
case 'bookInfo':
case String(event.action.match(/^ext-.*/i)):
this.download(event.book, event.action);//no await
break;
}
Expand Down
10 changes: 10 additions & 0 deletions client/components/Search/BookView/BookView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@
(скачать)
</div>

<div v-for="(item, key) in this.config.external">
<div v-if="item.active" class="q-ml-sm clickable" :title="item.hint" @click.stop.prevent="emit('ext-' + key)">
{{ item.title || key }}
</div>
</div>

<div class="q-ml-sm clickable" @click.stop.prevent="emit('copyLink')">
<q-icon name="la la-copy" size="20px" />
</div>
Expand Down Expand Up @@ -134,6 +140,10 @@ class BookView {
this.showJson = settings.showJson;
}

get config() {
return this.$store.state.config;
}

get settings() {
return this.$store.state.settings;
}
Expand Down
104 changes: 104 additions & 0 deletions examples/linux/external_tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"mobi": {
"active": true,
"title": "(mobi)",
"hint": "fb2converter был установлен отсюда: https://github.com/rupor-github/fb2converter/releases",
"ext": "mobi",
"cmd": "cp '${HASHFILE}.raw' '${HASHFILE}.fb2'; ${EXTDIR}/fb2c/fb2c convert --to mobi --nodirs --overwrite '${HASHFILE}.fb2'; rm -f '${HASHFILE}.fb2'",
"cmdExport": "mkdir -p '${EXTDIR}/export'; cp '${RESULTFILE}' '${EXTDIR}/export/${FILENAME}'",
"debug": true,
"type": "download"
},
"epub": {
"active": true,
"title": "(epub)",
"hint": "пример: конвертация, сохранение копии в отддельной папке, download",
"ext": "epub",
"cmd": "cp '${HASHFILE}.raw' '${HASHFILE}.fb2'; ${EXTDIR}/fb2c/fb2c convert --to epub --nodirs --overwrite '${HASHFILE}.fb2'; rm -f '${HASHFILE}.fb2'",
"cmdExport": "mkdir -p '${EXTDIR}/export'; cp '${RESULTFILE}' '${EXTDIR}/export/${FILENAME}'",
"debug": true,
"type": "download"
},
"azw3": {
"active": true,
"title": "(azw3)",
"hint": "пример: конвертация и сохранение копии в папке export. в принципе, можно вместо экспорта сделать отправку на почту или в телеграмм",
"ext": "azw3",
"cmd": "cp '${HASHFILE}.raw' '${HASHFILE}.fb2'; ${EXTDIR}/fb2c/fb2c convert --to azw3 --nodirs --overwrite '${HASHFILE}.fb2'; rm -f '${HASHFILE}.fb2'",
"cmdExport": "mkdir -p '${EXTDIR}/export'; cp '${RESULTFILE}' '${EXTDIR}/export/${FILENAME}'",
"debug": true,
"type": "cmd"
},
"kepub": {
"active": true,
"title": "(kepub)",
"hint": "пример: конвертация и download",
"ext": "kepub.epub",
"cmd": "cp '${HASHFILE}.raw' '${HASHFILE}.fb2'; ${EXTDIR}/fb2c/fb2c convert --to kepub --nodirs --overwrite '${HASHFILE}.fb2'; rm -f '${HASHFILE}.fb2'",
"debug": true,
"type": "download"
},

"pdf": {
"active": true,
"title": "(pdf)",
"hint": "другой конвертер: https://github.com/viktor-z/fb2pdf/releases",
"ext": "pdf",
"cmd": "cp '${HASHFILE}.raw' '${HASHFILE}.fb2'; java -jar '${EXTDIR}/fb2pdf/lib/fb2pdf.jar' -o -e UTF-8 '${HASHFILE}.fb2'; rm -f '${HASHFILE}.fb2'",
"cmdExport": "mkdir -p '${EXTDIR}/export'; cp '${RESULTFILE}' '${EXTDIR}/export/${FILENAME}'",
"debug": true,
"type": "download"
},

"fb2Export": {
"active": true,
"title": "(export fb2)",
"hint": "пример: просто экспорт",
"ext": "fb2",
"cmd": "mkdir -p '${EXTDIR}/export'; cp '${BOOKFILE}.raw' '${EXTDIR}/export/${FILENAME}'",
"type": "cmd"
},
"fb2ExportZip": {
"active": true,
"title": "(export fb2.zip)",
"hint": "печальный архиватор zip не понимает stdin.. тут хотелось проверить универсальность подхода, а не взять готовый .zip из папки ",
"ext": "fb2",
"cmd": "rm -f '${RESULTFILE}'; cp '${HASHFILE}.raw' '${FILENAME}'; zip -m '${RESULTFILE}' '${FILENAME}'",
"cmdExport": "mkdir -p '${EXTDIR}/export'; mv '${RESULTFILE}' '${EXTDIR}/export/${FILENAME}.zip'",
"type": "cmd"
},


"localReader1": {
"active": true,
"title": "(read1)",
"hint": "добавление своей ссылки на страницу",
"link": "http://127.0.0.1:44080/#/reader?url=http://127.0.0.1:12382${DOWNLOAD_URI}",
"type": "link"
},
"localReader2": {
"active": true,
"title": "(read2)",
"hint": "еще ссылка",
"link": "http://127.0.0.1:44080/#/reader?url=${DOWNLOAD_LINK}",
"type": "link"
},


"fbreader": {
"active": true,
"title": "(fbreader)",
"hint": "можно даже что-то свое в GUI запустить и подсунуть файл нужной книги",
"cmd": "gtk-launch fbreader_fbreader.desktop '${BOOKFILE}.raw'",
"debug": true,
"type": "gui"
},
"fbreader2": {
"active": true,
"title": "(fbreader-old)",
"hint": "можно даже что-то свое в GUI запустить и подсунуть файл нужной книги",
"cmd": "fbreader '${BOOKFILE}.raw'",
"debug": true,
"type": "gui"
}
}
4 changes: 3 additions & 1 deletion server/config/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ module.exports = {
libDir: '',
inpx: '',
inpxFilterFile: '',
externalToolsConfig: '',
external: '',

allowConfigRewrite: false,
allowUnsafeFilter: false,
Expand All @@ -42,7 +44,7 @@ module.exports = {
lowMemoryMode: false,
fullOptimization: false,

webConfigParams: ['name', 'version', 'latestVersion', 'branch', 'bookReadLink', 'dbVersion', 'extendedSearch', 'latestReleaseLink', 'uiDefaults'],
webConfigParams: ['name', 'version', 'latestVersion', 'branch', 'bookReadLink', 'dbVersion', 'extendedSearch', 'latestReleaseLink', 'uiDefaults', 'external'],

allowRemoteLib: false,
remoteLib: false,
Expand Down
1 change: 1 addition & 0 deletions server/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const propsToSave = [
'libDir',
'inpx',
'inpxFilterFile',
'externalToolsConfig',
'allowConfigRewrite',
'allowUnsafeFilter',
'accessPassword',
Expand Down
13 changes: 12 additions & 1 deletion server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ async function init() {
branch = config.branch;

//dirs
config.dataDir = config.dataDir || argvDataDir || `${config.execDir}/.${config.name}`;
config.dataDir = path.resolve(config.dataDir || argvDataDir || `${config.execDir}/.${config.name}`);
config.tempDir = config.tempDir || `${config.dataDir}/tmp`;
if (config.tempDir === '${OS}')
config.tempDir = `${os.tmpdir()}/${config.name}`
Expand Down Expand Up @@ -137,6 +137,7 @@ async function init() {
config.recreateDb = argv.recreate || false;
config.inpxFilterFile = config.inpxFilterFile || `${path.dirname(config.configFile)}/filter.json`;
config.allowUnsafeFilter = argv['unsafe-filter'] || config.allowUnsafeFilter || false;
config.externalToolsConfig = config.externalToolsConfig || `${path.dirname(config.configFile)}/external_tools.json`;

//web app
if (branch !== 'development') {
Expand All @@ -151,6 +152,16 @@ async function init() {

if (await fs.pathExists(config.inpxFilterFile))
log(`inpxFilterFile: ${config.inpxFilterFile}`)

if (await fs.pathExists(config.externalToolsConfig)) {
log(`externalToolsConfig: ${config.externalToolsConfig}`);
try {
config.external = JSON.parse(await fs.readFile(config.externalToolsConfig, 'utf8'));
} catch(e) {
log(LM_ERR, `externalToolsConfig: ${e.message}`);
config.external = {};
}
}
}

function logQueries(app) {
Expand Down
Loading