audiobookshelf/server/providers/FantLab.js

152 lines
4.3 KiB
JavaScript
Raw Normal View History

2023-02-06 22:25:18 +01:00
const axios = require('axios')
const Logger = require('../Logger')
class FantLab {
// 7 - other
// 11 - essay
// 12 - article
// 22 - disser
// 23 - monography
// 24 - study
// 25 - encyclopedy
// 26 - magazine
// 46 - sketch
// 47 - reportage
// 49 - excerpt
// 51 - interview
// 52 - review
// 55 - libretto
// 56 - anthology series
// 57 - newspaper
// types can get here https://api.fantlab.ru/config.json
_filterWorkType = [7, 11, 12, 22, 23, 24, 25, 26, 46, 47, 49, 51, 52, 55, 56, 57]
2023-02-06 22:25:18 +01:00
_baseUrl = 'https://api.fantlab.ru'
constructor() { }
async search(title, author) {
let searchString = encodeURIComponent(title)
2023-02-06 22:25:18 +01:00
if (author) {
searchString += encodeURIComponent(' ' + author)
}
const url = `${this._baseUrl}/search-works?q=${searchString}&page=1&onlymatches=1`
2023-02-06 22:25:18 +01:00
Logger.debug(`[FantLab] Search url: ${url}`)
const items = await axios.get(url).then((res) => {
2023-02-06 22:25:18 +01:00
return res.data || []
}).catch(error => {
Logger.error('[FantLab] search error', error)
return []
})
return Promise.all(items.map(async item => await this.getWork(item))).then(resArray => {
2023-02-11 21:25:25 +01:00
return resArray.filter(res => res)
})
2023-02-06 22:25:18 +01:00
}
async getWork(item) {
const { work_id, work_type_id } = item
2023-02-06 22:25:18 +01:00
if (this._filterWorkType.includes(work_type_id)) {
return null
2023-02-06 22:25:18 +01:00
}
const url = `${this._baseUrl}/work/${work_id}/extended`
const bookData = await axios.get(url).then((resp) => {
2023-02-06 22:25:18 +01:00
return resp.data || null
}).catch((error) => {
2023-02-11 21:25:25 +01:00
Logger.error(`[FantLab] work info request for url "${url}" error`, error)
2023-02-06 22:25:18 +01:00
return null
})
return this.cleanBookData(bookData)
2023-02-06 22:25:18 +01:00
}
async cleanBookData(bookData) {
let { authors, work_name_alts, work_id, work_name, work_year, work_description, image, classificatory, editions_blocks } = bookData
2023-02-06 22:25:18 +01:00
const subtitle = Array.isArray(work_name_alts) ? work_name_alts[0] : null
2023-02-11 21:25:25 +01:00
const authorNames = authors.map(au => (au.name || '').trim()).filter(au => au)
2023-02-06 22:25:18 +01:00
const imageAndIsbn = await this.tryGetCoverFromEditions(editions_blocks)
2023-02-06 22:25:18 +01:00
2023-02-11 21:25:25 +01:00
const imageToUse = imageAndIsbn?.imageUrl || image
2023-02-06 22:25:18 +01:00
return {
id: work_id,
title: work_name,
subtitle: subtitle || null,
2023-02-11 21:25:25 +01:00
author: authorNames.length ? authorNames.join(', ') : null,
2023-02-06 22:25:18 +01:00
publisher: null,
publishedYear: work_year,
description: work_description,
2023-02-11 21:25:25 +01:00
cover: imageToUse ? `https://fantlab.ru${imageToUse}` : null,
genres: this.tryGetGenres(classificatory),
isbn: imageAndIsbn?.isbn || null
2023-02-06 22:25:18 +01:00
}
}
tryGetGenres(classificatory) {
2023-02-11 21:25:25 +01:00
if (!classificatory || !classificatory.genre_group) return []
const genresGroup = classificatory.genre_group.find(group => group.genre_group_id == 1) // genres and subgenres
2023-02-06 22:25:18 +01:00
// genre_group_id=2 - General Characteristics
// genre_group_id=3 - Arena
// genre_group_id=4 - Duration of action
// genre_group_id=6 - Story moves
// genre_group_id=7 - Story linearity
// genre_group_id=5 - Recommended age of the reader
2023-02-11 21:25:25 +01:00
if (!genresGroup || !genresGroup.genre || !genresGroup.genre.length) return []
2023-02-06 22:25:18 +01:00
2023-02-11 21:25:25 +01:00
const rootGenre = genresGroup.genre[0]
2023-02-06 22:25:18 +01:00
const { label } = rootGenre
2023-02-06 22:25:18 +01:00
return [label].concat(this.tryGetSubGenres(rootGenre))
}
tryGetSubGenres(rootGenre) {
2023-02-11 21:25:25 +01:00
if (!rootGenre.genre || !rootGenre.genre.length) return []
return rootGenre.genre.map(g => g.label).filter(g => g)
2023-02-06 22:25:18 +01:00
}
async tryGetCoverFromEditions(editions) {
if (!editions) {
return null
}
2023-02-11 21:25:25 +01:00
// 30 = audio, 10 = paper
// Prefer audio if available
const bookEditions = editions['30'] || editions['10']
if (!bookEditions || !bookEditions.list || !bookEditions.list.length) {
2023-02-06 22:25:18 +01:00
return null
}
2023-02-11 21:25:25 +01:00
const lastEdition = bookEditions.list.pop()
2023-02-06 22:25:18 +01:00
const editionId = lastEdition['edition_id']
2023-02-11 21:25:25 +01:00
const isbn = lastEdition['isbn'] || null // get only from paper edition
2023-02-06 22:25:18 +01:00
return {
imageUrl: await this.getCoverFromEdition(editionId),
2023-02-11 21:25:25 +01:00
isbn
2023-02-06 22:25:18 +01:00
}
}
async getCoverFromEdition(editionId) {
2023-02-11 21:25:25 +01:00
if (!editionId) return null
const url = `${this._baseUrl}/edition/${editionId}`
2023-02-06 22:25:18 +01:00
const editionInfo = await axios.get(url).then((resp) => {
2023-02-06 22:25:18 +01:00
return resp.data || null
}).catch(error => {
2023-02-11 21:25:25 +01:00
Logger.error(`[FantLab] search cover from edition with url "${url}" error`, error)
2023-02-06 22:25:18 +01:00
return null
})
2023-02-11 21:25:25 +01:00
return editionInfo?.image || null
2023-02-06 22:25:18 +01:00
}
}
module.exports = FantLab