UNPKG

@tomisakae/syosetu-api

Version:

Enterprise-grade Fastify TypeScript API for Syosetu.com data extraction using official API and web scraping. Run instantly with 'npx @tomisakae/syosetu-api'

326 lines 13.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.syosetuService = exports.SyosetuService = void 0; const axios_1 = __importDefault(require("axios")); const config_1 = require("@/config"); const utils_1 = require("@/utils"); const scraper_service_1 = require("./scraper.service"); const logger = (0, utils_1.createChildLogger)('SyosetuService'); class SyosetuService { apiBaseUrl; constructor() { this.apiBaseUrl = config_1.syosetuConfig.apiBaseUrl; } async getNovelMetadata(ncode) { const cacheKey = utils_1.CacheManager.generateMetadataKey(ncode); const cached = utils_1.cacheManager.getMetadata(cacheKey); if (cached) { logger.debug(`Metadata cache hit for ncode: ${ncode}`); return cached; } try { await utils_1.globalRateLimiter.waitForRateLimit(); logger.info(`Fetching metadata for ncode: ${ncode}`); const response = await axios_1.default.get(this.apiBaseUrl, { params: { out: 'json', ncode: ncode.toUpperCase(), of: 't-w-s-bg-g-k-gf-gl-nt-e-ga-l-ti-f-imp-r-a-ah', }, timeout: config_1.syosetuConfig.requestTimeout, }); if (response.data && response.data.length > 1) { const novel = response.data[1]; const result = { success: true, data: { ncode: novel.ncode, title: novel.title, author: novel.writer, summary: novel.story, biggenre: novel.biggenre, genre: novel.genre, keywords: novel.keyword, firstPublished: novel.general_firstup, lastUpdated: novel.general_lastup, novelType: novel.noveltype, isCompleted: novel.end === 0, totalChapters: novel.general_all_no, wordCount: novel.length, readingTime: novel.time, bookmarks: novel.fav_novel_cnt, impressions: novel.impression_cnt, reviews: novel.review_cnt, points: novel.all_point, raters: novel.all_hyoka_cnt, }, timestamp: new Date().toISOString(), }; utils_1.cacheManager.setMetadata(cacheKey, result); logger.info(`Metadata fetched successfully for ncode: ${ncode}`); return result; } else { const result = { success: false, error: 'Không tìm thấy tiểu thuyết với ncode này', timestamp: new Date().toISOString(), }; return result; } } catch (error) { logger.error(`Error fetching metadata for ncode ${ncode}:`, error); return { success: false, error: `Lỗi khi lấy metadata: ${error instanceof Error ? error.message : 'Unknown error'}`, timestamp: new Date().toISOString(), }; } } async getChapterContent(ncode, chapterNumber) { const cacheKey = utils_1.CacheManager.generateContentKey(ncode, chapterNumber); const cached = utils_1.cacheManager.getContent(cacheKey); if (cached) { logger.debug(`Content cache hit for ncode: ${ncode}, chapter: ${chapterNumber}`); return cached; } logger.info(`Fetching content for ncode: ${ncode}, chapter: ${chapterNumber} using Cheerio`); const result = await scraper_service_1.scraperService.getChapterContent(ncode, chapterNumber); if (result.success) { utils_1.cacheManager.setContent(cacheKey, result); logger.info(`Content fetched successfully for ncode: ${ncode}, chapter: ${chapterNumber}`); } return result; } async searchNovels(keyword, options = {}) { const cacheKey = utils_1.CacheManager.generateSearchKey(keyword, options); const cached = utils_1.cacheManager.getMetadata(cacheKey); if (cached) { logger.debug(`Search cache hit for keyword: ${keyword}`); return cached; } try { await utils_1.globalRateLimiter.waitForRateLimit(); logger.info(`Searching novels with keyword: ${keyword}`); const params = { out: 'json', word: keyword, order: options.order || 'new', lim: options.limit || 20, of: options.fields || 't-w-s-bg-g-k-gf-gl-nt-e-ga-l-f-a-gp-dp-wp-mp', }; if (options.start) params['st'] = options.start; if (options.gzip) params['gzip'] = options.gzip; if (options.notword) params['notword'] = options.notword; if (options.title) params['title'] = options.title; if (options.ex) params['ex'] = options.ex; if (options.keyword) params['keyword'] = options.keyword; if (options.wname) params['wname'] = options.wname; if (options.biggenre) params['biggenre'] = options.biggenre; if (options.genre) params['genre'] = options.genre; if (options.notbiggenre) params['notbiggenre'] = options.notbiggenre; if (options.notgenre) params['notgenre'] = options.notgenre; if (options.userid) params['userid'] = options.userid; if (options.isr15) params['isr15'] = options.isr15; if (options.isbl) params['isbl'] = options.isbl; if (options.isgl) params['isgl'] = options.isgl; if (options.iszankoku) params['iszankoku'] = options.iszankoku; if (options.istensei) params['istensei'] = options.istensei; if (options.istenni) params['istenni'] = options.istenni; if (options.istt) params['istt'] = options.istt; if (options.notr15) params['notr15'] = options.notr15; if (options.notbl) params['notbl'] = options.notbl; if (options.notgl) params['notgl'] = options.notgl; if (options.notzankoku) params['notzankoku'] = options.notzankoku; if (options.nottensei) params['nottensei'] = options.nottensei; if (options.nottenni) params['nottenni'] = options.nottenni; if (options.minlen) params['minlen'] = options.minlen; if (options.maxlen) params['maxlen'] = options.maxlen; if (options.length) params['length'] = options.length; if (options.mintime) params['mintime'] = options.mintime; if (options.maxtime) params['maxtime'] = options.maxtime; if (options.time) params['time'] = options.time; if (options.kaiwaritu) params['kaiwaritu'] = options.kaiwaritu; if (options.sasie) params['sasie'] = options.sasie; if (options.type) params['type'] = options.type; if (options.buntai) params['buntai'] = options.buntai; if (options.stop) params['stop'] = options.stop; if (options.lastup) params['lastup'] = options.lastup; if (options.lastupdate) params['lastupdate'] = options.lastupdate; if (options.ispickup) params['ispickup'] = options.ispickup; if (options.opt) params['opt'] = options.opt; const response = await axios_1.default.get(this.apiBaseUrl, { params, timeout: config_1.syosetuConfig.requestTimeout, }); if (response.data && response.data.length > 1) { const novels = response.data.slice(1); const result = { success: true, data: { keyword, totalFound: response.data[0].allcount, results: novels.map((novel) => ({ ncode: novel.ncode, title: novel.title, author: novel.writer, summary: novel.story, biggenre: novel.biggenre, genre: novel.genre, keywords: novel.keyword, firstPublished: novel.general_firstup, lastUpdated: novel.general_lastup, novelType: novel.noveltype, isCompleted: novel.end === 0, totalChapters: novel.general_all_no, wordCount: novel.length, bookmarks: novel.fav_novel_cnt, points: novel.all_point, globalPoints: novel.global_point, dailyPoints: novel.daily_point, weeklyPoints: novel.weekly_point, monthlyPoints: novel.monthly_point, weeklyUnique: novel.weekly_unique, })), }, timestamp: new Date().toISOString(), }; utils_1.cacheManager.setMetadata(cacheKey, result); logger.info(`Search completed successfully for keyword: ${keyword}`); return result; } else { const result = { success: true, data: { keyword, totalFound: 0, results: [], }, timestamp: new Date().toISOString(), }; return result; } } catch (error) { logger.error(`Error searching novels with keyword ${keyword}:`, error); return { success: false, error: `Lỗi khi tìm kiếm: ${error instanceof Error ? error.message : 'Unknown error'}`, timestamp: new Date().toISOString(), }; } } async getRanking(options = {}) { const cacheKey = utils_1.CacheManager.generateRankingKey(options); const cached = utils_1.cacheManager.getMetadata(cacheKey); if (cached) { logger.debug('Ranking cache hit'); return cached; } try { await utils_1.globalRateLimiter.waitForRateLimit(); logger.info('Fetching ranking'); const params = { out: 'json', order: options.order || 'hyoka', lim: options.limit || 50, of: 't-w-s-bg-g-f-a-gp', }; if (options.biggenre) params['biggenre'] = options.biggenre; if (options.genre) params['genre'] = options.genre; const response = await axios_1.default.get(this.apiBaseUrl, { params, timeout: config_1.syosetuConfig.requestTimeout, }); if (response.data && response.data.length > 1) { const novels = response.data.slice(1); const result = { success: true, data: { rankings: novels.map((novel, index) => ({ rank: index + 1, ncode: novel.ncode, title: novel.title, author: novel.writer, summary: novel.story, genre: novel.genre, bookmarks: novel.fav_novel_cnt, points: novel.all_point, globalPoints: novel.global_point, })), }, timestamp: new Date().toISOString(), }; utils_1.cacheManager.setMetadata(cacheKey, result); logger.info('Ranking fetched successfully'); return result; } else { const result = { success: true, data: { rankings: [] }, timestamp: new Date().toISOString(), }; return result; } } catch (error) { logger.error('Error fetching ranking:', error); return { success: false, error: `Lỗi khi lấy ranking: ${error instanceof Error ? error.message : 'Unknown error'}`, timestamp: new Date().toISOString(), }; } } } exports.SyosetuService = SyosetuService; exports.syosetuService = new SyosetuService(); //# sourceMappingURL=syosetu.service.js.map