diff --git a/.dist/nyagami/plugins.json b/.dist/nyagami/plugins.json new file mode 100644 index 0000000..b4d2dcd --- /dev/null +++ b/.dist/nyagami/plugins.json @@ -0,0 +1,708 @@ +{ + "العربية": [ + { + "id": "ar-mtl", + "name": "ArMTL[madara]", + "site": "https://ar-mtl.club/", + "lang": "العربية", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/ArMTL[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/armtl.png" + }, + { + "id": "arnovel", + "name": "ArNovel[madara]", + "site": "https://arnovel.me/", + "lang": "العربية", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/ArNovel[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/arnovel.png" + }, + { + "id": "hizomanga", + "name": "HizoManga[madara]", + "site": "https://hizomanga.com/", + "lang": "العربية", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/HizoManga[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/hizomanga.png" + }, + { + "id": "kol-novel", + "name": "Kol Novel[wpmangastream]", + "site": "https://kolnovel.com/", + "lang": "العربية", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/KolNovel[wpmangastream].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/wpmangastream/icons/kol-novel.png" + }, + { + "id": "kolnovel", + "name": "KolNovel", + "site": "https://kolnovel.lol/", + "lang": "العربية", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/kolnovel.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/wpmangastream/icons/kolnovel.png" + }, + { + "id": "novel4up", + "name": "Novel4Up[madara]", + "site": "https://novel4up.com/", + "lang": "العربية", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/Novel4Up[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/novel4up.png" + }, + { + "id": "riwyat", + "name": "Riwyat[madara]", + "site": "https://riwyat.com/", + "lang": "العربية", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/Riwyat[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/riwyat.png" + }, + { + "id": "teamxnovel", + "name": "TeamXNovel[madara]", + "site": "https://teamxnovel.com/", + "lang": "العربية", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/TeamXNovel[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/teamxnovel.png" + } + ], + "中文, 汉语, 漢語": [ + { + "id": "linovelib", + "name": "Linovelib", + "site": "https://w.linovelib.com", + "lang": "中文, 汉语, 漢語", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/chinese/linovelib.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/cn/linovelib/icon.png" + } + ], + "English": [ + { + "id": "1stkissnovel", + "name": "FirstKissNovel[madara]", + "site": "https://1stkissnovel.org/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/FirstKissNovel[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/firstkissnovel.png" + }, + { + "id": "BLN.com", + "name": "BestLightNovel", + "site": "https://bestlightnovel.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/bestlightnovel.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/en/bestlightnovel/icon.png" + }, + { + "id": "boxnovel", + "name": "BoxNovel[madara]", + "site": "https://boxnovel.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/BoxNovel[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/boxnovel.png" + }, + { + "id": "clicknovel", + "name": "ClickNovel[madara]", + "site": "https://clicknovel.net/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/ClickNovel[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/clicknovel.png" + }, + { + "id": "cratenovel", + "name": "CrateNovel[madara]", + "site": "https://cratenovel.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/CrateNovel[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/cratenovel.png" + }, + { + "id": "daonovel", + "name": "DaoNovel[madara]", + "site": "https://daonovel.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/DaoNovel[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/daonovel.png" + }, + { + "id": "fanTLS", + "name": "Fans Translations[madara]", + "site": "https://fanstranslations.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/FansTranslations[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/fanstranslations.png" + }, + { + "id": "freenovel.me", + "name": "FreeNovelMe[madara]", + "site": "https://freenovel.me/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/FreeNovelMe[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/freenovelme.png" + }, + { + "id": "ftest", + "name": "Filter Test", + "site": "https://noblemtl.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/filterTests.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/en/noblemtl/icon.png" + }, + { + "id": "FWN.com", + "name": "Web NOVEL", + "site": "https://freewebnovel.com", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/freewebnovel.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/en/freewebnovel/icon.png" + }, + { + "id": "guavaread", + "name": "Guavaread[madara]", + "site": "https://guavaread.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/Guavaread[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/guavaread.png" + }, + { + "id": "lnheaven", + "name": "LightNovelHeaven[madara]", + "site": "https://lightnovelheaven.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/LightNovelHeaven[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/lightnovelheaven.png" + }, + { + "id": "Ltnovel.com", + "name": "Ltnovel[readwn]", + "site": "https://www.ltnovel.com", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/Ltnovel[readwn].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/readwn/icons/ltnovel.png" + }, + { + "id": "lunarletters", + "name": "LunarLetters[madara]", + "site": "https://www.lunarletters.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/LunarLetters[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/lunarletters.png" + }, + { + "id": "mostnovel", + "name": "MostNovel[madara]", + "site": "https://mostnovel.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/MostNovel[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/mostnovel.png" + }, + { + "id": "mtl-novel", + "name": "MTL-Novel[madara]", + "site": "https://mtl-novel.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/MTLNovel[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/mtl-novel.png" + }, + { + "id": "mtlnovel.club", + "name": "MTLNovel.Club[madara]", + "site": "https://mtlnovel.club/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/MTLNovelClub[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/mtlnovel.club.png" + }, + { + "id": "mysticalmerries", + "name": "MysticalSeries[madara]", + "site": "https://mysticalmerries.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/MysticalSeries[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/mysticalseries.png" + }, + { + "id": "neosekaiTLS", + "name": "NeoSekai Translations[madara]", + "site": "https://www.neosekaitranslations.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/NeoSekaiTranslations[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/neosekaitranslations.png" + }, + { + "id": "noblemtl", + "name": "NobleMTL", + "site": "https://noblemtl.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/nobleMTL.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/en/noblemtl/icon.png" + }, + { + "id": "nocturneTLS", + "name": "Nocturne Translations[madara]", + "site": "https://nocturnetls.net/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/NocturneTranslations[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/nocturnetranslations.png" + }, + { + "id": "noobchanTL", + "name": "Noobchan Translation[madara]", + "site": "https://noobchan.xyz/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/NoobchanTranslation[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/noobchantranslation.png" + }, + { + "id": "novelmultiverse", + "name": "NovelMultiverse[madara]", + "site": "https://www.novelmultiverse.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/NovelMultiverse[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/novelmultiverse.png" + }, + { + "id": "novelr18", + "name": "NovelR18[madara]", + "site": "https://novelr18.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/NovelR18[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/novelr18.png" + }, + { + "id": "novelroom", + "name": "Novelroom.net[madara]", + "site": "https://novelroom.net/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/Novelroomnet[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/novelroom.net.png" + }, + { + "id": "novelstic", + "name": "Novelstic[madara]", + "site": "https://novelstic.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/Novelstic[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/novelstic.png" + }, + { + "id": "novelTL", + "name": "NovelTranslate[madara]", + "site": "https://noveltranslate.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/NovelTranslate[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/noveltranslate.png" + }, + { + "id": "onlymtl", + "name": "OnlyMTL[madara]", + "site": "https://www.onlymtl.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/OnlyMTL[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/onlymtl.png" + }, + { + "id": "pandamtl", + "name": "PandaMTL", + "site": "https://pandamtl.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/pandaMTL.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/en/wordpress/icon.png" + }, + { + "id": "readwebnovels", + "name": "ReadWebNovels[madara]", + "site": "https://readwebnovels.net/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/ReadWebNovels[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/readwebnovels.png" + }, + { + "id": "sleeptTLS", + "name": "SleepyTranslations[madara]", + "site": "https://sleepytranslations.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/SleepyTranslations[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/sleepytranslations.png" + }, + { + "id": "sonicmtl", + "name": "SonicMTL[madara]", + "site": "https://www.sonicmtl.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/SonicMTL[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/sonicmtl.png" + }, + { + "id": "sugarbbscan", + "name": "Sugar Babies[madara]", + "site": "https://sugarbbscan.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/SugarBabies[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/sugarbabies.png" + }, + { + "id": "sweetEscapeTL", + "name": "Sweet Escape Translations[madara]", + "site": "https://sweetescapetranslations.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/SweetEscapeTranslations[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/sweetescapetranslations.png" + }, + { + "id": "webnovelover", + "name": "WebNovelLover[madara]", + "site": "https://www.webnovelover.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/WebNovelLover[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/webnovellover.png" + }, + { + "id": "wuxiacity", + "name": "Wuxiacity[readwn]", + "site": "https://www.wuxiacity.com", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/Wuxiacity[readwn].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/readwn/icons/wuxiacity.png" + }, + { + "id": "wuxiap", + "name": "Wuxiap[readwn]", + "site": "https://www.wuxiap.com", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/Wuxiap[readwn].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/readwn/icons/wuxiap.png" + }, + { + "id": "wuxiaworld.site", + "name": "WuxiaWorld.Site[madara]", + "site": "https://wuxiaworld.site/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/WuxiaWorldSite[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/wuxiaworld.site.png" + }, + { + "id": "zetroTL", + "name": "Zetro Translation[madara]", + "site": "https://zetrotranslation.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/ZetroTranslation[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/zetrotranslation.png" + }, + { + "id": "zinnovel", + "name": "ZinnNovel[madara]", + "site": "https://zinnovel.com/", + "lang": "English", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/ZinnNovel[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/zinnnovel.png" + } + ], + "français, française": [ + { + "id": "chireads.com", + "name": "Chireads", + "site": "https://chireads.com", + "lang": "français, française", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/french/chireads.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/fr/chireads/icon.png" + }, + { + "id": "lightnovelfr", + "name": "Ligh Novel FR[wpmangastream]", + "site": "https://lightnovelfr.com/", + "lang": "français, française", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/french/LighNovelFR[wpmangastream].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/wpmangastream/icons/lightnovelfr.png" + }, + { + "id": "NDG.com", + "name": "NovelDeGlace", + "site": "https://noveldeglace.com/", + "lang": "français, française", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/french/noveldeglace.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/fr/noveldeglace/icon.png" + } + ], + "Bahasa Indonesia": [ + { + "id": "meionovel", + "name": "MeioNovel[madara]", + "site": "https://meionovel.id/", + "lang": "Bahasa Indonesia", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/indonesian/MeioNovel[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/meionovel.png" + }, + { + "id": "morenovel", + "name": "MoreNovel[madara]", + "site": "https://morenovel.net/", + "lang": "Bahasa Indonesia", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/indonesian/MoreNovel[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/morenovel.png" + }, + { + "id": "wbnovel", + "name": "WBNovel[madara]", + "site": "https://wbnovel.com/", + "lang": "Bahasa Indonesia", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/indonesian/WBNovel[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/wbnovel.png" + } + ], + "Português": [ + { + "id": "lightnovelbrasil", + "name": "Light Novel Brasil", + "site": "https://lightnovelbrasil.com/", + "lang": "Português", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/portuguese/lightnovelbrasil.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/wpmangastream/icons/lightnovelbrasil.png" + } + ], + "русский язык": [ + { + "id": "AT", + "name": "Автор Тудей", + "site": "https://author.today", + "lang": "русский язык", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/authortoday.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/authortoday/icon.png" + }, + { + "id": "bookhamster", + "name": "Bookhamster[ifreedom]", + "site": "https://bookhamster.ru", + "lang": "русский язык", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/Bookhamster[ifreedom].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/ifreedom/icons/bookhamster.png" + }, + { + "id": "bookriver", + "name": "Bookriver", + "site": "https://bookriver.ru", + "lang": "русский язык", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/bookriver.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/bookriver/icon.png" + }, + { + "id": "erolate", + "name": "Erolate[rulate]", + "site": "https://erolate.com", + "lang": "русский язык", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/Erolate[rulate].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/rulate/icons/erolate.png" + }, + { + "id": "ficbook", + "name": "ficbook", + "site": "https://ficbook.net", + "lang": "русский язык", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/ficbook.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/ficbook/icon.png" + }, + { + "id": "freedlit.space", + "name": "LitSpace", + "site": "https://freedlit.space", + "lang": "русский язык", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/LitSpace.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/freedlit/icon.png" + }, + { + "id": "ifreedom", + "name": "Свободный Мир Ранобэ[ifreedom]", + "site": "https://ifreedom.su", + "lang": "русский язык", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/СвободныйМирРанобэ[ifreedom].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/ifreedom/icons/ifreedom.png" + }, + { + "id": "jaomix.ru", + "name": "Jaomix", + "site": "https://jaomix.ru", + "lang": "русский язык", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/jaomix.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/jaomix/icon.png" + }, + { + "id": "ReN", + "name": "Renovels", + "site": "https://renovels.org", + "lang": "русский язык", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/renovels.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/renovels/icon.png" + }, + { + "id": "RLIB", + "name": "RanobeLib", + "site": "https://ranobelib.me", + "lang": "русский язык", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/ranobelib.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/ranobelib/icon.png" + }, + { + "id": "RNBH.org", + "name": "RanobeHub", + "site": "https://ranobehub.org/", + "lang": "русский язык", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/ranobehub.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/ranobehub/icon.png" + }, + { + "id": "RNRF", + "name": "РанобэРФ", + "site": "https://ранобэ.рф", + "lang": "русский язык", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/ranoberf.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/ranoberf/icon.png" + }, + { + "id": "TL", + "name": "NovelTL", + "site": "https://novel.tl", + "lang": "русский язык", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/novelTL.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/noveltl/icon.png" + }, + { + "id": "tl.rulate", + "name": "Rulate[rulate]", + "site": "https://tl.rulate.ru", + "lang": "русский язык", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/Rulate[rulate].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/rulate/icons/rulate.png" + } + ], + "español, castellano": [ + { + "id": "tunovelaligera", + "name": "TuNovelaLigera", + "site": "https://tunovelaligera.com/", + "lang": "español, castellano", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/spanish/tunovelaligera.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/es/tunovelaligera/icon.png" + } + ], + "Türkçe": [ + { + "id": "lshnovel", + "name": "Liebe Schnee Hiver Novel", + "site": "https://lshnovel.com/", + "lang": "Türkçe", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/turkish/lshnovel.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/wpmangastream/icons/lshnovel.png" + }, + { + "id": "turkcelightnovels", + "name": "TurkceLightNovels[madara]", + "site": "https://turkcelightnovels.com/", + "lang": "Türkçe", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/turkish/TurkceLightNovels[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/turkcelightnovels.png" + }, + { + "id": "webnoveloku", + "name": "WebNovelOku [madara]", + "site": "https://www.webnoveloku.com/", + "lang": "Türkçe", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/turkish/WebNovelOku[madara].js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/webnoveloku.png" + } + ], + "Україна": [ + { + "id": "smakolykytl", + "name": "Смаколики", + "site": "https://smakolykytl.site/", + "lang": "Україна", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/ukraine/smakolykytl.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ua/smakolykytl/icon.png" + } + ], + "Tiếng Việt": [ + { + "id": "ln.hako", + "name": "Hako", + "site": "https://ln.hako.vn", + "lang": "Tiếng Việt", + "version": "1.0.0", + "url": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/vietnamese/LNHako.js", + "iconUrl": "https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/vi/hakolightnovel/icon.png" + } + ] +} \ No newline at end of file diff --git a/.dist/nyagami/plugins.min.json b/.dist/nyagami/plugins.min.json new file mode 100644 index 0000000..46bc7d2 --- /dev/null +++ b/.dist/nyagami/plugins.min.json @@ -0,0 +1 @@ +{"العربية":[{"id":"ar-mtl","name":"ArMTL[madara]","site":"https://ar-mtl.club/","lang":"العربية","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/ArMTL[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/armtl.png"},{"id":"arnovel","name":"ArNovel[madara]","site":"https://arnovel.me/","lang":"العربية","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/ArNovel[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/arnovel.png"},{"id":"hizomanga","name":"HizoManga[madara]","site":"https://hizomanga.com/","lang":"العربية","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/HizoManga[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/hizomanga.png"},{"id":"kol-novel","name":"Kol Novel[wpmangastream]","site":"https://kolnovel.com/","lang":"العربية","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/KolNovel[wpmangastream].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/wpmangastream/icons/kol-novel.png"},{"id":"kolnovel","name":"KolNovel","site":"https://kolnovel.lol/","lang":"العربية","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/kolnovel.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/wpmangastream/icons/kolnovel.png"},{"id":"novel4up","name":"Novel4Up[madara]","site":"https://novel4up.com/","lang":"العربية","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/Novel4Up[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/novel4up.png"},{"id":"riwyat","name":"Riwyat[madara]","site":"https://riwyat.com/","lang":"العربية","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/Riwyat[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/riwyat.png"},{"id":"teamxnovel","name":"TeamXNovel[madara]","site":"https://teamxnovel.com/","lang":"العربية","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/arabic/TeamXNovel[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/teamxnovel.png"}],"中文, 汉语, 漢語":[{"id":"linovelib","name":"Linovelib","site":"https://w.linovelib.com","lang":"中文, 汉语, 漢語","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/chinese/linovelib.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/cn/linovelib/icon.png"}],"English":[{"id":"1stkissnovel","name":"FirstKissNovel[madara]","site":"https://1stkissnovel.org/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/FirstKissNovel[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/firstkissnovel.png"},{"id":"BLN.com","name":"BestLightNovel","site":"https://bestlightnovel.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/bestlightnovel.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/en/bestlightnovel/icon.png"},{"id":"boxnovel","name":"BoxNovel[madara]","site":"https://boxnovel.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/BoxNovel[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/boxnovel.png"},{"id":"clicknovel","name":"ClickNovel[madara]","site":"https://clicknovel.net/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/ClickNovel[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/clicknovel.png"},{"id":"cratenovel","name":"CrateNovel[madara]","site":"https://cratenovel.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/CrateNovel[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/cratenovel.png"},{"id":"daonovel","name":"DaoNovel[madara]","site":"https://daonovel.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/DaoNovel[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/daonovel.png"},{"id":"fanTLS","name":"Fans Translations[madara]","site":"https://fanstranslations.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/FansTranslations[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/fanstranslations.png"},{"id":"freenovel.me","name":"FreeNovelMe[madara]","site":"https://freenovel.me/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/FreeNovelMe[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/freenovelme.png"},{"id":"ftest","name":"Filter Test","site":"https://noblemtl.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/filterTests.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/en/noblemtl/icon.png"},{"id":"FWN.com","name":"Web NOVEL","site":"https://freewebnovel.com","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/freewebnovel.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/en/freewebnovel/icon.png"},{"id":"guavaread","name":"Guavaread[madara]","site":"https://guavaread.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/Guavaread[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/guavaread.png"},{"id":"lnheaven","name":"LightNovelHeaven[madara]","site":"https://lightnovelheaven.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/LightNovelHeaven[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/lightnovelheaven.png"},{"id":"Ltnovel.com","name":"Ltnovel[readwn]","site":"https://www.ltnovel.com","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/Ltnovel[readwn].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/readwn/icons/ltnovel.png"},{"id":"lunarletters","name":"LunarLetters[madara]","site":"https://www.lunarletters.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/LunarLetters[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/lunarletters.png"},{"id":"mostnovel","name":"MostNovel[madara]","site":"https://mostnovel.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/MostNovel[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/mostnovel.png"},{"id":"mtl-novel","name":"MTL-Novel[madara]","site":"https://mtl-novel.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/MTLNovel[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/mtl-novel.png"},{"id":"mtlnovel.club","name":"MTLNovel.Club[madara]","site":"https://mtlnovel.club/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/MTLNovelClub[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/mtlnovel.club.png"},{"id":"mysticalmerries","name":"MysticalSeries[madara]","site":"https://mysticalmerries.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/MysticalSeries[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/mysticalseries.png"},{"id":"neosekaiTLS","name":"NeoSekai Translations[madara]","site":"https://www.neosekaitranslations.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/NeoSekaiTranslations[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/neosekaitranslations.png"},{"id":"noblemtl","name":"NobleMTL","site":"https://noblemtl.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/nobleMTL.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/en/noblemtl/icon.png"},{"id":"nocturneTLS","name":"Nocturne Translations[madara]","site":"https://nocturnetls.net/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/NocturneTranslations[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/nocturnetranslations.png"},{"id":"noobchanTL","name":"Noobchan Translation[madara]","site":"https://noobchan.xyz/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/NoobchanTranslation[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/noobchantranslation.png"},{"id":"novelmultiverse","name":"NovelMultiverse[madara]","site":"https://www.novelmultiverse.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/NovelMultiverse[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/novelmultiverse.png"},{"id":"novelr18","name":"NovelR18[madara]","site":"https://novelr18.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/NovelR18[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/novelr18.png"},{"id":"novelroom","name":"Novelroom.net[madara]","site":"https://novelroom.net/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/Novelroomnet[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/novelroom.net.png"},{"id":"novelstic","name":"Novelstic[madara]","site":"https://novelstic.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/Novelstic[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/novelstic.png"},{"id":"novelTL","name":"NovelTranslate[madara]","site":"https://noveltranslate.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/NovelTranslate[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/noveltranslate.png"},{"id":"onlymtl","name":"OnlyMTL[madara]","site":"https://www.onlymtl.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/OnlyMTL[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/onlymtl.png"},{"id":"pandamtl","name":"PandaMTL","site":"https://pandamtl.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/pandaMTL.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/en/wordpress/icon.png"},{"id":"readwebnovels","name":"ReadWebNovels[madara]","site":"https://readwebnovels.net/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/ReadWebNovels[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/readwebnovels.png"},{"id":"sleeptTLS","name":"SleepyTranslations[madara]","site":"https://sleepytranslations.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/SleepyTranslations[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/sleepytranslations.png"},{"id":"sonicmtl","name":"SonicMTL[madara]","site":"https://www.sonicmtl.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/SonicMTL[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/sonicmtl.png"},{"id":"sugarbbscan","name":"Sugar Babies[madara]","site":"https://sugarbbscan.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/SugarBabies[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/sugarbabies.png"},{"id":"sweetEscapeTL","name":"Sweet Escape Translations[madara]","site":"https://sweetescapetranslations.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/SweetEscapeTranslations[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/sweetescapetranslations.png"},{"id":"webnovelover","name":"WebNovelLover[madara]","site":"https://www.webnovelover.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/WebNovelLover[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/webnovellover.png"},{"id":"wuxiacity","name":"Wuxiacity[readwn]","site":"https://www.wuxiacity.com","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/Wuxiacity[readwn].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/readwn/icons/wuxiacity.png"},{"id":"wuxiap","name":"Wuxiap[readwn]","site":"https://www.wuxiap.com","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/Wuxiap[readwn].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/readwn/icons/wuxiap.png"},{"id":"wuxiaworld.site","name":"WuxiaWorld.Site[madara]","site":"https://wuxiaworld.site/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/WuxiaWorldSite[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/wuxiaworld.site.png"},{"id":"zetroTL","name":"Zetro Translation[madara]","site":"https://zetrotranslation.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/ZetroTranslation[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/zetrotranslation.png"},{"id":"zinnovel","name":"ZinnNovel[madara]","site":"https://zinnovel.com/","lang":"English","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/english/ZinnNovel[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/zinnnovel.png"}],"français, française":[{"id":"chireads.com","name":"Chireads","site":"https://chireads.com","lang":"français, française","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/french/chireads.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/fr/chireads/icon.png"},{"id":"lightnovelfr","name":"Ligh Novel FR[wpmangastream]","site":"https://lightnovelfr.com/","lang":"français, française","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/french/LighNovelFR[wpmangastream].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/wpmangastream/icons/lightnovelfr.png"},{"id":"NDG.com","name":"NovelDeGlace","site":"https://noveldeglace.com/","lang":"français, française","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/french/noveldeglace.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/fr/noveldeglace/icon.png"}],"Bahasa Indonesia":[{"id":"meionovel","name":"MeioNovel[madara]","site":"https://meionovel.id/","lang":"Bahasa Indonesia","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/indonesian/MeioNovel[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/meionovel.png"},{"id":"morenovel","name":"MoreNovel[madara]","site":"https://morenovel.net/","lang":"Bahasa Indonesia","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/indonesian/MoreNovel[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/morenovel.png"},{"id":"wbnovel","name":"WBNovel[madara]","site":"https://wbnovel.com/","lang":"Bahasa Indonesia","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/indonesian/WBNovel[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/wbnovel.png"}],"Português":[{"id":"lightnovelbrasil","name":"Light Novel Brasil","site":"https://lightnovelbrasil.com/","lang":"Português","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/portuguese/lightnovelbrasil.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/wpmangastream/icons/lightnovelbrasil.png"}],"русский язык":[{"id":"AT","name":"Автор Тудей","site":"https://author.today","lang":"русский язык","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/authortoday.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/authortoday/icon.png"},{"id":"bookhamster","name":"Bookhamster[ifreedom]","site":"https://bookhamster.ru","lang":"русский язык","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/Bookhamster[ifreedom].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/ifreedom/icons/bookhamster.png"},{"id":"bookriver","name":"Bookriver","site":"https://bookriver.ru","lang":"русский язык","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/bookriver.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/bookriver/icon.png"},{"id":"erolate","name":"Erolate[rulate]","site":"https://erolate.com","lang":"русский язык","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/Erolate[rulate].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/rulate/icons/erolate.png"},{"id":"ficbook","name":"ficbook","site":"https://ficbook.net","lang":"русский язык","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/ficbook.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/ficbook/icon.png"},{"id":"freedlit.space","name":"LitSpace","site":"https://freedlit.space","lang":"русский язык","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/LitSpace.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/freedlit/icon.png"},{"id":"ifreedom","name":"Свободный Мир Ранобэ[ifreedom]","site":"https://ifreedom.su","lang":"русский язык","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/СвободныйМирРанобэ[ifreedom].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/ifreedom/icons/ifreedom.png"},{"id":"jaomix.ru","name":"Jaomix","site":"https://jaomix.ru","lang":"русский язык","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/jaomix.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/jaomix/icon.png"},{"id":"ReN","name":"Renovels","site":"https://renovels.org","lang":"русский язык","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/renovels.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/renovels/icon.png"},{"id":"RLIB","name":"RanobeLib","site":"https://ranobelib.me","lang":"русский язык","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/ranobelib.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/ranobelib/icon.png"},{"id":"RNBH.org","name":"RanobeHub","site":"https://ranobehub.org/","lang":"русский язык","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/ranobehub.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/ranobehub/icon.png"},{"id":"RNRF","name":"РанобэРФ","site":"https://ранобэ.рф","lang":"русский язык","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/ranoberf.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/ranoberf/icon.png"},{"id":"TL","name":"NovelTL","site":"https://novel.tl","lang":"русский язык","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/novelTL.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ru/noveltl/icon.png"},{"id":"tl.rulate","name":"Rulate[rulate]","site":"https://tl.rulate.ru","lang":"русский язык","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/russian/Rulate[rulate].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/rulate/icons/rulate.png"}],"español, castellano":[{"id":"tunovelaligera","name":"TuNovelaLigera","site":"https://tunovelaligera.com/","lang":"español, castellano","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/spanish/tunovelaligera.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/es/tunovelaligera/icon.png"}],"Türkçe":[{"id":"lshnovel","name":"Liebe Schnee Hiver Novel","site":"https://lshnovel.com/","lang":"Türkçe","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/turkish/lshnovel.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/wpmangastream/icons/lshnovel.png"},{"id":"turkcelightnovels","name":"TurkceLightNovels[madara]","site":"https://turkcelightnovels.com/","lang":"Türkçe","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/turkish/TurkceLightNovels[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/turkcelightnovels.png"},{"id":"webnoveloku","name":"WebNovelOku [madara]","site":"https://www.webnoveloku.com/","lang":"Türkçe","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/turkish/WebNovelOku[madara].js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/multisrc/madara/icons/webnoveloku.png"}],"Україна":[{"id":"smakolykytl","name":"Смаколики","site":"https://smakolykytl.site/","lang":"Україна","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/ukraine/smakolykytl.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/ua/smakolykytl/icon.png"}],"Tiếng Việt":[{"id":"ln.hako","name":"Hako","site":"https://ln.hako.vn","lang":"Tiếng Việt","version":"1.0.0","url":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/.js/plugins/vietnamese/LNHako.js","iconUrl":"https://raw.githubusercontent.com/nyagami/plugins/fix-plugins/icons/src/vi/hakolightnovel/icon.png"}]} \ No newline at end of file diff --git a/.js/plugins/arabic/ArMTL[madara].js b/.js/plugins/arabic/ArMTL[madara].js index 058ef94..6e5c3ad 100644 --- a/.js/plugins/arabic/ArMTL[madara].js +++ b/.js/plugins/arabic/ArMTL[madara].js @@ -62,7 +62,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/arabic/ArNovel[madara].js b/.js/plugins/arabic/ArNovel[madara].js index a413818..4e19077 100644 --- a/.js/plugins/arabic/ArNovel[madara].js +++ b/.js/plugins/arabic/ArNovel[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/arabic/HizoManga[madara].js b/.js/plugins/arabic/HizoManga[madara].js index 2796c26..99e8627 100644 --- a/.js/plugins/arabic/HizoManga[madara].js +++ b/.js/plugins/arabic/HizoManga[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/arabic/KolNovel[wpmangastream].js b/.js/plugins/arabic/KolNovel[wpmangastream].js index 0bb1f5a..950cba9 100644 --- a/.js/plugins/arabic/KolNovel[wpmangastream].js +++ b/.js/plugins/arabic/KolNovel[wpmangastream].js @@ -47,7 +47,6 @@ var WPmangaStreamPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; } WPmangaStreamPlugin.prototype.popularNovels = function (page) { diff --git a/.js/plugins/arabic/Novel4Up[madara].js b/.js/plugins/arabic/Novel4Up[madara].js index cbe2559..ce1c830 100644 --- a/.js/plugins/arabic/Novel4Up[madara].js +++ b/.js/plugins/arabic/Novel4Up[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/arabic/Riwyat[madara].js b/.js/plugins/arabic/Riwyat[madara].js index 0f0d017..18c74a1 100644 --- a/.js/plugins/arabic/Riwyat[madara].js +++ b/.js/plugins/arabic/Riwyat[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/arabic/TeamXNovel[madara].js b/.js/plugins/arabic/TeamXNovel[madara].js index 671e2f1..83f921c 100644 --- a/.js/plugins/arabic/TeamXNovel[madara].js +++ b/.js/plugins/arabic/TeamXNovel[madara].js @@ -62,7 +62,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/arabic/kolnovel.js b/.js/plugins/arabic/kolnovel.js index ee83f11..ac4d1ff 100644 --- a/.js/plugins/arabic/kolnovel.js +++ b/.js/plugins/arabic/kolnovel.js @@ -47,7 +47,6 @@ var KolNovel = /** @class */ (function () { this.site = "https://kolnovel.lol/"; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = { order: { label: "ترتيب حسب", @@ -148,9 +147,6 @@ var KolNovel = /** @class */ (function () { link += "&status=" + ((filters === null || filters === void 0 ? void 0 : filters.status) ? filters.status : ""); link += "&order=" + ((filters === null || filters === void 0 ? void 0 : filters.order) ? filters.order : "popular"); headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(link, { headers: headers }).then(function (result) { return result.text(); })]; @@ -185,9 +181,6 @@ var KolNovel = /** @class */ (function () { case 0: url = novelUrl; headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, fetch(url, { headers: headers })]; case 1: result = _a.sent(); @@ -258,9 +251,6 @@ var KolNovel = /** @class */ (function () { switch (_b.label) { case 0: headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, fetch(chapterUrl, { headers: headers })]; case 1: result = _b.sent(); @@ -286,9 +276,6 @@ var KolNovel = /** @class */ (function () { case 0: url = "".concat(this.site, "?s=").concat(searchTerm); headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(url, { headers: headers })]; case 1: result = _a.sent(); diff --git a/.js/plugins/chinese/linovelib.js b/.js/plugins/chinese/linovelib.js index af5c997..68b5eba 100644 --- a/.js/plugins/chinese/linovelib.js +++ b/.js/plugins/chinese/linovelib.js @@ -48,7 +48,6 @@ var Linovelib = /** @class */ (function () { this.site = "https://w.linovelib.com"; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = { sort: { label: "Sort By", @@ -83,9 +82,6 @@ var Linovelib = /** @class */ (function () { link += filters.sort; link += "/".concat(pageNo, ".html"); headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(link, { headers: headers }).then(function (result) { return result.text(); })]; @@ -124,9 +120,6 @@ var Linovelib = /** @class */ (function () { case 0: url = novelUrl; headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(url, { headers: headers })]; case 1: result = _b.sent(); @@ -206,9 +199,6 @@ var Linovelib = /** @class */ (function () { switch (_a.label) { case 0: headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } chapterText = "", pageText = ""; pageNumber = 1; skillgg = { @@ -416,9 +406,6 @@ var Linovelib = /** @class */ (function () { searchUrl = "".concat(this.site, "/search/"); Term = encodeURI(searchTerm); headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } pageNo = 1; novels = []; addPage = function (pageCheerio, redirect) { return __awaiter(_this, void 0, void 0, function () { diff --git a/.js/plugins/english/BoxNovel[madara].js b/.js/plugins/english/BoxNovel[madara].js index ec9ba27..fbb35c7 100644 --- a/.js/plugins/english/BoxNovel[madara].js +++ b/.js/plugins/english/BoxNovel[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/ClickNovel[madara].js b/.js/plugins/english/ClickNovel[madara].js index 8205354..5fe892c 100644 --- a/.js/plugins/english/ClickNovel[madara].js +++ b/.js/plugins/english/ClickNovel[madara].js @@ -62,7 +62,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/CrateNovel[madara].js b/.js/plugins/english/CrateNovel[madara].js index 4dd0ddd..ccf5133 100644 --- a/.js/plugins/english/CrateNovel[madara].js +++ b/.js/plugins/english/CrateNovel[madara].js @@ -62,7 +62,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/DaoNovel[madara].js b/.js/plugins/english/DaoNovel[madara].js index 3ad500d..957db49 100644 --- a/.js/plugins/english/DaoNovel[madara].js +++ b/.js/plugins/english/DaoNovel[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/FansTranslations[madara].js b/.js/plugins/english/FansTranslations[madara].js index cb72eef..65fa9de 100644 --- a/.js/plugins/english/FansTranslations[madara].js +++ b/.js/plugins/english/FansTranslations[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/FirstKissNovel[madara].js b/.js/plugins/english/FirstKissNovel[madara].js index 41e25c2..55f4dc5 100644 --- a/.js/plugins/english/FirstKissNovel[madara].js +++ b/.js/plugins/english/FirstKissNovel[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/FreeNovelMe[madara].js b/.js/plugins/english/FreeNovelMe[madara].js index e4f4d3d..8ea5734 100644 --- a/.js/plugins/english/FreeNovelMe[madara].js +++ b/.js/plugins/english/FreeNovelMe[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/Guavaread[madara].js b/.js/plugins/english/Guavaread[madara].js index 2e196eb..d3ce1a2 100644 --- a/.js/plugins/english/Guavaread[madara].js +++ b/.js/plugins/english/Guavaread[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/LightNovelHeaven[madara].js b/.js/plugins/english/LightNovelHeaven[madara].js index bb21c84..bb21424 100644 --- a/.js/plugins/english/LightNovelHeaven[madara].js +++ b/.js/plugins/english/LightNovelHeaven[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/Ltnovel[readwn].js b/.js/plugins/english/Ltnovel[readwn].js index de3d7a5..7ab3fe9 100644 --- a/.js/plugins/english/Ltnovel[readwn].js +++ b/.js/plugins/english/Ltnovel[readwn].js @@ -50,7 +50,6 @@ var ReadwnPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = metadata.filters; } ReadwnPlugin.prototype.popularNovels = function (pageNo, _a) { diff --git a/.js/plugins/english/LunarLetters[madara].js b/.js/plugins/english/LunarLetters[madara].js index 52934ef..e9cc152 100644 --- a/.js/plugins/english/LunarLetters[madara].js +++ b/.js/plugins/english/LunarLetters[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/MTLNovelClub[madara].js b/.js/plugins/english/MTLNovelClub[madara].js index 7322277..7cf11f7 100644 --- a/.js/plugins/english/MTLNovelClub[madara].js +++ b/.js/plugins/english/MTLNovelClub[madara].js @@ -62,7 +62,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/MTLNovel[madara].js b/.js/plugins/english/MTLNovel[madara].js index 80b1123..7d7d83a 100644 --- a/.js/plugins/english/MTLNovel[madara].js +++ b/.js/plugins/english/MTLNovel[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/MostNovel[madara].js b/.js/plugins/english/MostNovel[madara].js index 56efbbe..57fad40 100644 --- a/.js/plugins/english/MostNovel[madara].js +++ b/.js/plugins/english/MostNovel[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/MysticalSeries[madara].js b/.js/plugins/english/MysticalSeries[madara].js index bb16025..51cb318 100644 --- a/.js/plugins/english/MysticalSeries[madara].js +++ b/.js/plugins/english/MysticalSeries[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/NeoSekaiTranslations[madara].js b/.js/plugins/english/NeoSekaiTranslations[madara].js index 1a040ad..db2d111 100644 --- a/.js/plugins/english/NeoSekaiTranslations[madara].js +++ b/.js/plugins/english/NeoSekaiTranslations[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/NocturneTranslations[madara].js b/.js/plugins/english/NocturneTranslations[madara].js index 7fa2cff..8e8117e 100644 --- a/.js/plugins/english/NocturneTranslations[madara].js +++ b/.js/plugins/english/NocturneTranslations[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/NoobchanTranslation[madara].js b/.js/plugins/english/NoobchanTranslation[madara].js index c4afe19..c6869bd 100644 --- a/.js/plugins/english/NoobchanTranslation[madara].js +++ b/.js/plugins/english/NoobchanTranslation[madara].js @@ -62,7 +62,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/NovelMultiverse[madara].js b/.js/plugins/english/NovelMultiverse[madara].js index 16ce5e7..82999dd 100644 --- a/.js/plugins/english/NovelMultiverse[madara].js +++ b/.js/plugins/english/NovelMultiverse[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/NovelR18[madara].js b/.js/plugins/english/NovelR18[madara].js index 832f8ed..006a267 100644 --- a/.js/plugins/english/NovelR18[madara].js +++ b/.js/plugins/english/NovelR18[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/NovelTranslate[madara].js b/.js/plugins/english/NovelTranslate[madara].js index c9bf1bf..132ba49 100644 --- a/.js/plugins/english/NovelTranslate[madara].js +++ b/.js/plugins/english/NovelTranslate[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/Novelroomnet[madara].js b/.js/plugins/english/Novelroomnet[madara].js index 858af3f..b46a881 100644 --- a/.js/plugins/english/Novelroomnet[madara].js +++ b/.js/plugins/english/Novelroomnet[madara].js @@ -62,7 +62,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/Novelstic[madara].js b/.js/plugins/english/Novelstic[madara].js index 753b6b9..ea89ef7 100644 --- a/.js/plugins/english/Novelstic[madara].js +++ b/.js/plugins/english/Novelstic[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/OnlyMTL[madara].js b/.js/plugins/english/OnlyMTL[madara].js index 3f5c69a..7f8aaca 100644 --- a/.js/plugins/english/OnlyMTL[madara].js +++ b/.js/plugins/english/OnlyMTL[madara].js @@ -62,7 +62,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/ReadWebNovels[madara].js b/.js/plugins/english/ReadWebNovels[madara].js index a2ae423..a9d44bc 100644 --- a/.js/plugins/english/ReadWebNovels[madara].js +++ b/.js/plugins/english/ReadWebNovels[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/SleepyTranslations[madara].js b/.js/plugins/english/SleepyTranslations[madara].js index ff37039..5ac5b62 100644 --- a/.js/plugins/english/SleepyTranslations[madara].js +++ b/.js/plugins/english/SleepyTranslations[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/SonicMTL[madara].js b/.js/plugins/english/SonicMTL[madara].js index c42c8d2..f4832dd 100644 --- a/.js/plugins/english/SonicMTL[madara].js +++ b/.js/plugins/english/SonicMTL[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/SugarBabies[madara].js b/.js/plugins/english/SugarBabies[madara].js index 4e9570b..e6bb195 100644 --- a/.js/plugins/english/SugarBabies[madara].js +++ b/.js/plugins/english/SugarBabies[madara].js @@ -62,7 +62,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/SweetEscapeTranslations[madara].js b/.js/plugins/english/SweetEscapeTranslations[madara].js index 7b44d52..3e3f9fb 100644 --- a/.js/plugins/english/SweetEscapeTranslations[madara].js +++ b/.js/plugins/english/SweetEscapeTranslations[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/WebNovelLover[madara].js b/.js/plugins/english/WebNovelLover[madara].js index 49249af..7a39e84 100644 --- a/.js/plugins/english/WebNovelLover[madara].js +++ b/.js/plugins/english/WebNovelLover[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/WuxiaWorldSite[madara].js b/.js/plugins/english/WuxiaWorldSite[madara].js index 0c8f52e..e4d2307 100644 --- a/.js/plugins/english/WuxiaWorldSite[madara].js +++ b/.js/plugins/english/WuxiaWorldSite[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/Wuxiacity[readwn].js b/.js/plugins/english/Wuxiacity[readwn].js index 61d40c3..3e83f84 100644 --- a/.js/plugins/english/Wuxiacity[readwn].js +++ b/.js/plugins/english/Wuxiacity[readwn].js @@ -50,7 +50,6 @@ var ReadwnPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = metadata.filters; } ReadwnPlugin.prototype.popularNovels = function (pageNo, _a) { diff --git a/.js/plugins/english/Wuxiap[readwn].js b/.js/plugins/english/Wuxiap[readwn].js index 3b7e786..7791b1d 100644 --- a/.js/plugins/english/Wuxiap[readwn].js +++ b/.js/plugins/english/Wuxiap[readwn].js @@ -50,7 +50,6 @@ var ReadwnPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = metadata.filters; } ReadwnPlugin.prototype.popularNovels = function (pageNo, _a) { diff --git a/.js/plugins/english/ZetroTranslation[madara].js b/.js/plugins/english/ZetroTranslation[madara].js index 08e00a2..72d9727 100644 --- a/.js/plugins/english/ZetroTranslation[madara].js +++ b/.js/plugins/english/ZetroTranslation[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/ZinnNovel[madara].js b/.js/plugins/english/ZinnNovel[madara].js index 600de11..3435000 100644 --- a/.js/plugins/english/ZinnNovel[madara].js +++ b/.js/plugins/english/ZinnNovel[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/english/bestlightnovel.js b/.js/plugins/english/bestlightnovel.js index 633730f..d630f5b 100644 --- a/.js/plugins/english/bestlightnovel.js +++ b/.js/plugins/english/bestlightnovel.js @@ -48,7 +48,6 @@ var BLN = /** @class */ (function () { this.site = "https://bestlightnovel.com/"; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; } BLN.prototype.popularNovels = function (pageNo, options) { return __awaiter(this, void 0, void 0, function () { diff --git a/.js/plugins/english/filterTests.js b/.js/plugins/english/filterTests.js index 758908b..845db14 100644 --- a/.js/plugins/english/filterTests.js +++ b/.js/plugins/english/filterTests.js @@ -46,7 +46,6 @@ var FTest = /** @class */ (function () { this.site = "https://noblemtl.com/"; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = { picker: { type: filterInputs_1.FilterTypes.Picker, diff --git a/.js/plugins/english/freewebnovel.js b/.js/plugins/english/freewebnovel.js index 7ca1b8d..3b608f2 100644 --- a/.js/plugins/english/freewebnovel.js +++ b/.js/plugins/english/freewebnovel.js @@ -46,7 +46,6 @@ var FreeWebNovel = /** @class */ (function () { this.version = "1.0.0"; this.icon = "src/en/freewebnovel/icon.png"; this.userAgent = ""; - this.cookieString = ""; this.fetchImage = fetch_1.fetchFile; } FreeWebNovel.prototype.popularNovels = function (page, _a) { diff --git a/.js/plugins/english/nobleMTL.js b/.js/plugins/english/nobleMTL.js index f625bd7..bdf52e9 100644 --- a/.js/plugins/english/nobleMTL.js +++ b/.js/plugins/english/nobleMTL.js @@ -47,7 +47,6 @@ var NobleMTL = /** @class */ (function () { this.site = "https://noblemtl.com/"; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = { order: { label: "Sort By", @@ -196,9 +195,6 @@ var NobleMTL = /** @class */ (function () { link += "&status=" + filters.status.value; link += "&order=" + filters.order.value; headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(link, { headers: headers }).then(function (result) { return result.text(); })]; @@ -233,9 +229,6 @@ var NobleMTL = /** @class */ (function () { case 0: url = novelUrl; headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(url, { headers: headers })]; case 1: result = _a.sent(); @@ -311,9 +304,6 @@ var NobleMTL = /** @class */ (function () { switch (_a.label) { case 0: headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl, { headers: headers })]; case 1: result = _a.sent(); @@ -335,9 +325,6 @@ var NobleMTL = /** @class */ (function () { case 0: url = "".concat(this.site, "?s=").concat(searchTerm); headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(url, { headers: headers })]; case 1: result = _a.sent(); diff --git a/.js/plugins/english/pandaMTL.js b/.js/plugins/english/pandaMTL.js index e8123af..68a7aba 100644 --- a/.js/plugins/english/pandaMTL.js +++ b/.js/plugins/english/pandaMTL.js @@ -47,7 +47,6 @@ var PandaMTL = /** @class */ (function () { this.site = "https://pandamtl.com/"; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = { order: { label: "Sort By", @@ -126,9 +125,6 @@ var PandaMTL = /** @class */ (function () { link += "&status=" + ((filters === null || filters === void 0 ? void 0 : filters.status) ? filters.status : ""); link += "&order=" + ((filters === null || filters === void 0 ? void 0 : filters.order) ? filters.order : "popular"); headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(link, { headers: headers }).then(function (result) { return result.text(); })]; @@ -163,9 +159,6 @@ var PandaMTL = /** @class */ (function () { case 0: url = novelUrl; headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(url, { headers: headers })]; case 1: result = _a.sent(); @@ -234,9 +227,6 @@ var PandaMTL = /** @class */ (function () { switch (_a.label) { case 0: headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl, { headers: headers })]; case 1: result = _a.sent(); @@ -258,9 +248,6 @@ var PandaMTL = /** @class */ (function () { case 0: url = "".concat(this.site, "?s=").concat(searchTerm); headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(url, { headers: headers })]; case 1: result = _a.sent(); diff --git a/.js/plugins/french/LighNovelFR[wpmangastream].js b/.js/plugins/french/LighNovelFR[wpmangastream].js index 9d31a24..9b12f47 100644 --- a/.js/plugins/french/LighNovelFR[wpmangastream].js +++ b/.js/plugins/french/LighNovelFR[wpmangastream].js @@ -47,7 +47,6 @@ var WPmangaStreamPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; } WPmangaStreamPlugin.prototype.popularNovels = function (page) { diff --git a/.js/plugins/french/chireads.js b/.js/plugins/french/chireads.js index a88fbb4..f9705d7 100644 --- a/.js/plugins/french/chireads.js +++ b/.js/plugins/french/chireads.js @@ -46,7 +46,6 @@ var ChireadsPlugin = /** @class */ (function () { this.site = "https://chireads.com"; this.version = "1.0.0"; this.userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"; - this.cookieString = "''"; } ChireadsPlugin.prototype.parseNovels = function (loadedCheerio) { return []; diff --git a/.js/plugins/french/noveldeglace.js b/.js/plugins/french/noveldeglace.js index 1e026a1..4c01288 100644 --- a/.js/plugins/french/noveldeglace.js +++ b/.js/plugins/french/noveldeglace.js @@ -47,7 +47,6 @@ var NovelDeGlacePlugin = /** @class */ (function () { this.site = "https://noveldeglace.com/"; this.version = "1.0.0"; this.userAgent = "''"; - this.cookieString = "''"; } NovelDeGlacePlugin.prototype.popularNovels = function (page) { return __awaiter(this, void 0, void 0, function () { diff --git a/.js/plugins/indonesian/MeioNovel[madara].js b/.js/plugins/indonesian/MeioNovel[madara].js index a94c653..693ddf6 100644 --- a/.js/plugins/indonesian/MeioNovel[madara].js +++ b/.js/plugins/indonesian/MeioNovel[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/indonesian/MoreNovel[madara].js b/.js/plugins/indonesian/MoreNovel[madara].js index 41e5ba4..0a6b70b 100644 --- a/.js/plugins/indonesian/MoreNovel[madara].js +++ b/.js/plugins/indonesian/MoreNovel[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/indonesian/WBNovel[madara].js b/.js/plugins/indonesian/WBNovel[madara].js index 0720190..b6ef766 100644 --- a/.js/plugins/indonesian/WBNovel[madara].js +++ b/.js/plugins/indonesian/WBNovel[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/portuguese/lightnovelbrasil.js b/.js/plugins/portuguese/lightnovelbrasil.js index 3775095..d97b4aa 100644 --- a/.js/plugins/portuguese/lightnovelbrasil.js +++ b/.js/plugins/portuguese/lightnovelbrasil.js @@ -47,7 +47,6 @@ var LightNovelBrasil = /** @class */ (function () { this.site = "https://lightnovelbrasil.com/"; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = { order: { label: "Sort By", @@ -196,9 +195,6 @@ var LightNovelBrasil = /** @class */ (function () { link += "&status=" + ((filters === null || filters === void 0 ? void 0 : filters.status) ? filters.status : ""); link += "&order=" + ((filters === null || filters === void 0 ? void 0 : filters.order) ? filters.order : "popular"); headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(link, { headers: headers }).then(function (result) { return result.text(); })]; @@ -233,9 +229,6 @@ var LightNovelBrasil = /** @class */ (function () { case 0: url = novelUrl; headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(url, { headers: headers })]; case 1: result = _a.sent(); @@ -312,9 +305,6 @@ var LightNovelBrasil = /** @class */ (function () { switch (_a.label) { case 0: headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl, { headers: headers })]; case 1: result = _a.sent(); @@ -336,9 +326,6 @@ var LightNovelBrasil = /** @class */ (function () { case 0: url = "".concat(this.site, "?s=").concat(searchTerm); headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(url, { headers: headers })]; case 1: result = _a.sent(); diff --git a/.js/plugins/russian/Bookhamster[ifreedom].js b/.js/plugins/russian/Bookhamster[ifreedom].js index 35fd9bf..de850fa 100644 --- a/.js/plugins/russian/Bookhamster[ifreedom].js +++ b/.js/plugins/russian/Bookhamster[ifreedom].js @@ -49,7 +49,6 @@ var IfreedomPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = metadata.filters; } IfreedomPlugin.prototype.popularNovels = function (page, _a) { diff --git a/.js/plugins/russian/Erolate[rulate].js b/.js/plugins/russian/Erolate[rulate].js index bec8a53..c964bb4 100644 --- a/.js/plugins/russian/Erolate[rulate].js +++ b/.js/plugins/russian/Erolate[rulate].js @@ -50,7 +50,6 @@ var RulatePlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = metadata.filters; } RulatePlugin.prototype.popularNovels = function (pageNo, _a) { diff --git a/.js/plugins/russian/LitSpace.js b/.js/plugins/russian/LitSpace.js index f333f5f..62c61ce 100644 --- a/.js/plugins/russian/LitSpace.js +++ b/.js/plugins/russian/LitSpace.js @@ -47,7 +47,6 @@ var freedlit = /** @class */ (function () { this.version = "1.0.0"; this.icon = "src/ru/freedlit/icon.png"; this.userAgent = ""; - this.cookieString = ""; this.fetchImage = fetch_1.fetchFile; this.filters = { sort: { diff --git a/.js/plugins/russian/Rulate[rulate].js b/.js/plugins/russian/Rulate[rulate].js index 6490867..7d9b143 100644 --- a/.js/plugins/russian/Rulate[rulate].js +++ b/.js/plugins/russian/Rulate[rulate].js @@ -50,7 +50,6 @@ var RulatePlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = metadata.filters; } RulatePlugin.prototype.popularNovels = function (pageNo, _a) { diff --git a/.js/plugins/russian/authortoday.js b/.js/plugins/russian/authortoday.js index 24e521e..f17f182 100644 --- a/.js/plugins/russian/authortoday.js +++ b/.js/plugins/russian/authortoday.js @@ -55,7 +55,6 @@ var AuthorToday = /** @class */ (function () { this.site = "https://author.today"; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.fetchImage = fetch_1.fetchFile; this.filters = { sort: { diff --git a/.js/plugins/russian/bookriver.js b/.js/plugins/russian/bookriver.js index e899700..073a616 100644 --- a/.js/plugins/russian/bookriver.js +++ b/.js/plugins/russian/bookriver.js @@ -52,7 +52,6 @@ var Bookriver = /** @class */ (function () { this.version = "1.0.0"; this.icon = "src/ru/bookriver/icon.png"; this.userAgent = ""; - this.cookieString = ""; this.fetchImage = fetch_1.fetchFile; this.filters = { sort: { diff --git a/.js/plugins/russian/ficbook.js b/.js/plugins/russian/ficbook.js index aaed01a..05392a7 100644 --- a/.js/plugins/russian/ficbook.js +++ b/.js/plugins/russian/ficbook.js @@ -49,7 +49,6 @@ var ficbook = /** @class */ (function () { this.version = "1.0.0"; this.icon = "src/ru/ficbook/icon.png"; this.userAgent = ""; - this.cookieString = ""; this.fetchImage = fetch_1.fetchFile; this.filters = { sort: { diff --git a/.js/plugins/russian/jaomix.js b/.js/plugins/russian/jaomix.js index 855eed6..fc68345 100644 --- a/.js/plugins/russian/jaomix.js +++ b/.js/plugins/russian/jaomix.js @@ -48,7 +48,6 @@ var Jaomix = /** @class */ (function () { this.version = "1.0.0"; this.icon = "src/ru/jaomix/icon.png"; this.userAgent = ""; - this.cookieString = ""; this.fetchImage = fetch_1.fetchFile; this.filters = { sortby: { diff --git a/.js/plugins/russian/novelTL.js b/.js/plugins/russian/novelTL.js index 3fe290c..bd1555f 100644 --- a/.js/plugins/russian/novelTL.js +++ b/.js/plugins/russian/novelTL.js @@ -53,7 +53,6 @@ var TL = /** @class */ (function () { this.version = "1.0.0"; this.icon = "src/ru/noveltl/icon.png"; this.userAgent = ""; - this.cookieString = ""; this.fetchImage = fetch_1.fetchFile; this.filters = { tags: { diff --git a/.js/plugins/russian/ranobehub.js b/.js/plugins/russian/ranobehub.js index ed46d68..176816a 100644 --- a/.js/plugins/russian/ranobehub.js +++ b/.js/plugins/russian/ranobehub.js @@ -52,7 +52,6 @@ var RNBH = /** @class */ (function () { this.site = "https://ranobehub.org/"; this.icon = "src/ru/ranobehub/icon.png"; this.userAgent = ""; - this.cookieString = ""; this.fetchImage = fetch_1.fetchFile; this.filters = { sort: { diff --git a/.js/plugins/russian/ranobelib.js b/.js/plugins/russian/ranobelib.js index 5439908..8277a58 100644 --- a/.js/plugins/russian/ranobelib.js +++ b/.js/plugins/russian/ranobelib.js @@ -53,7 +53,6 @@ var RLIB = /** @class */ (function () { this.icon = "src/ru/ranobelib/icon.png"; this.ui = undefined; this.userAgent = ""; - this.cookieString = ""; this.fetchImage = fetch_1.fetchFile; this.filters = { sort: { diff --git a/.js/plugins/russian/ranoberf.js b/.js/plugins/russian/ranoberf.js index 05e3c2b..3211a5c 100644 --- a/.js/plugins/russian/ranoberf.js +++ b/.js/plugins/russian/ranoberf.js @@ -53,7 +53,6 @@ var RNRF = /** @class */ (function () { this.version = "1.0.0"; this.icon = "src/ru/ranoberf/icon.png"; this.userAgent = ""; - this.cookieString = ""; this.fetchImage = fetch_1.fetchFile; this.filters = { sort: { diff --git a/.js/plugins/russian/renovels.js b/.js/plugins/russian/renovels.js index 2ea98f2..22d0d6d 100644 --- a/.js/plugins/russian/renovels.js +++ b/.js/plugins/russian/renovels.js @@ -51,7 +51,6 @@ var ReN = /** @class */ (function () { this.version = "1.0.0"; this.icon = "src/ru/renovels/icon.png"; this.userAgent = ""; - this.cookieString = ""; this.fetchImage = fetch_1.fetchFile; this.filters = { sort: { diff --git a/.js/plugins/russian/СвободныйМирРанобэ[ifreedom].js b/.js/plugins/russian/СвободныйМирРанобэ[ifreedom].js index eedc31f..185d4f9 100644 --- a/.js/plugins/russian/СвободныйМирРанобэ[ifreedom].js +++ b/.js/plugins/russian/СвободныйМирРанобэ[ifreedom].js @@ -49,7 +49,6 @@ var IfreedomPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = metadata.filters; } IfreedomPlugin.prototype.popularNovels = function (page, _a) { diff --git a/.js/plugins/spanish/tunovelaligera.js b/.js/plugins/spanish/tunovelaligera.js index 6b71268..b8d5ee7 100644 --- a/.js/plugins/spanish/tunovelaligera.js +++ b/.js/plugins/spanish/tunovelaligera.js @@ -50,7 +50,6 @@ var TuNovelaLigera = /** @class */ (function () { this.site = "https://tunovelaligera.com/"; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = { order: { value: "?m_orderby=rating", @@ -137,9 +136,6 @@ var TuNovelaLigera = /** @class */ (function () { link += "/page/".concat(pageNo); link += filters.order; headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(link, { headers: headers })]; case 1: result = _b.sent(); @@ -175,9 +171,6 @@ var TuNovelaLigera = /** @class */ (function () { case 0: url = novelUrl; headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(url, { headers: headers })]; case 1: result = _c.sent(); @@ -353,9 +346,6 @@ var TuNovelaLigera = /** @class */ (function () { switch (_a.label) { case 0: headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl)]; case 1: result = _a.sent(); diff --git a/.js/plugins/turkish/TurkceLightNovels[madara].js b/.js/plugins/turkish/TurkceLightNovels[madara].js index dae58c6..924899b 100644 --- a/.js/plugins/turkish/TurkceLightNovels[madara].js +++ b/.js/plugins/turkish/TurkceLightNovels[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/turkish/WebNovelOku[madara].js b/.js/plugins/turkish/WebNovelOku[madara].js index cd83255..3d6aaae 100644 --- a/.js/plugins/turkish/WebNovelOku[madara].js +++ b/.js/plugins/turkish/WebNovelOku[madara].js @@ -63,7 +63,6 @@ var MadaraPlugin = /** @class */ (function () { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/.js/plugins/turkish/lshnovel.js b/.js/plugins/turkish/lshnovel.js index aa8f69f..46fa369 100644 --- a/.js/plugins/turkish/lshnovel.js +++ b/.js/plugins/turkish/lshnovel.js @@ -47,7 +47,6 @@ var LSHNovel = /** @class */ (function () { this.site = "https://lshnovel.com/"; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = { order: { value: "popular", @@ -144,9 +143,6 @@ var LSHNovel = /** @class */ (function () { link += "&status=" + filters.status; link += "&order=" + filters.order; headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(link, { headers: headers }).then(function (result) { return result.text(); })]; @@ -181,9 +177,6 @@ var LSHNovel = /** @class */ (function () { case 0: url = novelUrl; headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(url, { headers: headers })]; case 1: result = _a.sent(); @@ -259,9 +252,6 @@ var LSHNovel = /** @class */ (function () { switch (_a.label) { case 0: headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl, { headers: headers })]; case 1: result = _a.sent(); @@ -283,9 +273,6 @@ var LSHNovel = /** @class */ (function () { case 0: url = "".concat(this.site, "?s=").concat(searchTerm); headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } return [4 /*yield*/, (0, fetch_1.fetchApi)(url, { headers: headers })]; case 1: result = _a.sent(); diff --git a/.js/plugins/ukraine/smakolykytl.js b/.js/plugins/ukraine/smakolykytl.js index 213faec..24ff2df 100644 --- a/.js/plugins/ukraine/smakolykytl.js +++ b/.js/plugins/ukraine/smakolykytl.js @@ -50,7 +50,6 @@ var Smakolykytl = /** @class */ (function () { this.version = "1.0.0"; this.icon = "src/ua/smakolykytl/icon.png"; this.userAgent = ""; - this.cookieString = ""; this.fetchImage = fetch_1.fetchFile; } Smakolykytl.prototype.popularNovels = function (pageNo, _a) { diff --git a/.js/plugins/vietnamese/LNHako.js b/.js/plugins/vietnamese/LNHako.js index 8653ffb..ca217ce 100644 --- a/.js/plugins/vietnamese/LNHako.js +++ b/.js/plugins/vietnamese/LNHako.js @@ -47,7 +47,6 @@ var HakoPlugin = /** @class */ (function () { this.site = "https://ln.hako.vn"; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; } HakoPlugin.prototype.parseNovels = function (loadedCheerio) { var _this = this; diff --git a/README.md b/README.md index 3e38c65..6f42a30 100644 --- a/README.md +++ b/README.md @@ -40,10 +40,6 @@ npm install ``` npm start ``` -or -``` -nodemon -``` + Then open http://localhost:3000 diff --git a/index.ts b/index.ts index f3a9cc3..3d52091 100644 --- a/index.ts +++ b/index.ts @@ -6,6 +6,19 @@ import express from "express"; import bodyParser from "body-parser"; import * as pluginApi from "./test_web/api/plugins"; import localizedFormat from "dayjs/plugin/localizedFormat"; +interface GlobalState { + UserAgent?: string, + Cookie?: string, +} +let globalState: GlobalState = { +} + +export const getHeaders = () => { + return { + 'User-Agent': globalState.UserAgent, + 'Cookie': globalState.Cookie, + }; +}; const app = express(); const port = 3000; @@ -13,6 +26,14 @@ const host = "localhost"; const dirname = path.join(__dirname, ".."); +app.use((req, res, next) => { + globalState.UserAgent = req.headers["user-agent"]; + console.log('---------'); + console.log('default headers: ', globalState); + console.log('---------'); + console.log(); + next(); +}); app.use(bodyParser.json()); app.use("/static", express.static(path.join(dirname, "test_web", "static"))); app.use("/icons", express.static(path.join(dirname, "icons"))); @@ -20,6 +41,14 @@ app.get("/all_plugins", (req, res) => { const allPlugins = pluginApi.all_plugins(); res.json(allPlugins); }); +app.post("/add_cookie", async (req, res) => { + globalState.Cookie = req.body['cookie']; + console.log('---------'); + console.log('default headers: ', globalState); + console.log('---------'); + console.log(); + res.json({}); +}); app.post("/filters", async (req, res) => { const filters = await pluginApi.getFilter(req.body['pluginRequirePath']); res.json(filters || []); diff --git a/libs/fetch.ts b/libs/fetch.ts index 4249884..3678fa3 100644 --- a/libs/fetch.ts +++ b/libs/fetch.ts @@ -1,16 +1,44 @@ -const defaultUserAgentString = - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"; - export async function fetchApi( url: string, - init?: RequestInit, + init?: { + headers?: Record | Headers; + [x: string]: string | Record | undefined | FormData | Headers; + }, ) { - const headers = { - "User-Agent": defaultUserAgentString, - ...init?.headers, - }; - console.log(url, { ...init, headers }) - return await fetch(url, { ...init, headers }); + let defaultHeaders: { + 'User-Agent'?: string, + 'Cookie'?: string, + } = {}; + try { + const getHeaders = (await import('../index.js')).getHeaders; + defaultHeaders = getHeaders(); + }catch{ + // nothing to do + } + + if(init?.headers) { + if(init.headers instanceof Headers){ + if(!init.headers.get('User-Agent') && defaultHeaders['User-Agent']){ + init.headers.set('User-Agent', defaultHeaders['User-Agent']); + } + if(defaultHeaders.Cookie) { + init.headers.set('Cookie', defaultHeaders.Cookie); + } + }else{ + init.headers = { + ...defaultHeaders, + ...init.headers, + }; + } + }else{ + init = { + ...init, + headers: defaultHeaders + } + } + + console.log(url, init); + return await fetch(url, init); } export const fetchFile = async function (url: string, init?: RequestInit) { diff --git a/nodemon.json b/nodemon.json deleted file mode 100644 index f2189c1..0000000 --- a/nodemon.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ext": "js,mjs,json,ts", - "ignore": [".js/**"], - "exec": "tsc && node ./.js/index.js" -} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7ae610e..32b5060 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,8 +26,8 @@ "@types/express": "^4.17.17", "@types/jquery": "^3.5.28", "@types/node": "^20.3.1", - "nodemon": "^2.0.22", - "pre-commit": "^1.2.2" + "pre-commit": "^1.2.2", + "tsc-watch": "^6.0.4" } }, "node_modules/@cspotcode/source-map-support": { @@ -216,12 +216,6 @@ "integrity": "sha512-sU5MNKvSFdbT97qLSabU6jxceY9BfYQe9uF+b8E1rdTecpq9ROkkKoiHlblUlhgfaz9bdCByWjAI40Y3mHf1iQ==", "dev": true }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -994,7 +988,7 @@ "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "devOptional": true, + "optional": true, "dependencies": { "ms": "^2.1.1" } @@ -1194,6 +1188,12 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, "node_modules/duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -1334,6 +1334,21 @@ "es5-ext": "~0.10.14" } }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -1548,6 +1563,12 @@ "node": ">= 0.6" } }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1670,15 +1691,6 @@ "node": ">= 0.4.0" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/has-property-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", @@ -1866,12 +1878,6 @@ "node": ">= 4" } }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true - }, "node_modules/image-size": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", @@ -2178,6 +2184,12 @@ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -2434,48 +2446,11 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, - "node_modules/nodemon": { - "version": "2.0.22", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", - "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", - "dev": true, - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^3.2.7", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^5.7.1", - "simple-update-notifier": "^1.0.7", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=8.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dev": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "*" - } + "node_modules/node-cleanup": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", + "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==", + "dev": true }, "node_modules/normalize-path": { "version": "3.0.0", @@ -2644,6 +2619,15 @@ "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -2670,6 +2654,15 @@ "node": ">=8" } }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, "node_modules/pbkdf2": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", @@ -2788,18 +2781,27 @@ "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", "optional": true }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "dev": true }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, "node_modules/public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -3049,7 +3051,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "devOptional": true, + "optional": true, "bin": { "semver": "bin/semver" } @@ -3204,27 +3206,6 @@ } ] }, - "node_modules/simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", - "dev": true, - "dependencies": { - "semver": "~7.0.0" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -3260,6 +3241,18 @@ "os-shim": "^0.1.2" } }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -3290,6 +3283,15 @@ "node": ">= 6" } }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, "node_modules/stream-combiner2": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", @@ -3345,6 +3347,15 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/subarg": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", @@ -3353,18 +3364,6 @@ "minimist": "^1.1.0" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -3437,18 +3436,6 @@ "node": ">=0.6" } }, - "node_modules/touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "dependencies": { - "nopt": "~1.0.10" - }, - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -3507,6 +3494,77 @@ "tsc-alias": "dist/bin/index.js" } }, + "node_modules/tsc-watch": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-6.0.4.tgz", + "integrity": "sha512-cHvbvhjO86w2aGlaHgSCeQRl+Aqw6X6XN4sQMPZKF88GoP30O+oTuh5lRIJr5pgFWrRpF1AgXnJJ2DoFEIPHyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "node-cleanup": "^2.1.2", + "ps-tree": "^1.2.0", + "string-argv": "^0.3.1" + }, + "bin": { + "tsc-watch": "dist/lib/tsc-watch.js" + }, + "engines": { + "node": ">=12.12.0" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/tsc-watch/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tsc-watch/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tsc-watch/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tsc-watch/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -3574,12 +3632,6 @@ "undeclared-identifiers": "bin.js" } }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -3884,12 +3936,6 @@ "integrity": "sha512-sU5MNKvSFdbT97qLSabU6jxceY9BfYQe9uF+b8E1rdTecpq9ROkkKoiHlblUlhgfaz9bdCByWjAI40Y3mHf1iQ==", "dev": true }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -4543,7 +4589,7 @@ "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "devOptional": true, + "optional": true, "requires": { "ms": "^2.1.1" } @@ -4686,6 +4732,12 @@ "domhandler": "^5.0.3" } }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, "duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -4803,6 +4855,21 @@ "es5-ext": "~0.10.14" } }, + "event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, "events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -4995,6 +5062,12 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -5083,12 +5156,6 @@ "function-bind": "^1.1.1" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, "has-property-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", @@ -5215,12 +5282,6 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true - }, "image-size": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", @@ -5452,6 +5513,12 @@ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -5642,32 +5709,11 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, - "nodemon": { - "version": "2.0.22", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", - "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", - "dev": true, - "requires": { - "chokidar": "^3.5.2", - "debug": "^3.2.7", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^5.7.1", - "simple-update-notifier": "^1.0.7", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - } - }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dev": true, - "requires": { - "abbrev": "1" - } + "node-cleanup": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", + "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==", + "dev": true }, "normalize-path": { "version": "3.0.0", @@ -5797,6 +5843,12 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -5817,6 +5869,15 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "requires": { + "through": "~2.3" + } + }, "pbkdf2": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", @@ -5899,18 +5960,21 @@ "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", "optional": true }, + "ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "requires": { + "event-stream": "=3.3.4" + } + }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "dev": true }, - "pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, "public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -6094,7 +6158,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "devOptional": true + "optional": true }, "send": { "version": "0.18.0", @@ -6212,23 +6276,6 @@ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" }, - "simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", - "dev": true, - "requires": { - "semver": "~7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } - } - }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -6254,6 +6301,15 @@ "os-shim": "^0.1.2" } }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "requires": { + "through": "2" + } + }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -6280,6 +6336,15 @@ } } }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "requires": { + "duplexer": "~0.1.1" + } + }, "stream-combiner2": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", @@ -6336,6 +6401,12 @@ } } }, + "string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true + }, "subarg": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", @@ -6344,15 +6415,6 @@ "minimist": "^1.1.0" } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -6410,15 +6472,6 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, - "touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "requires": { - "nopt": "~1.0.10" - } - }, "ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -6452,6 +6505,55 @@ "plimit-lit": "^1.2.6" } }, + "tsc-watch": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-6.0.4.tgz", + "integrity": "sha512-cHvbvhjO86w2aGlaHgSCeQRl+Aqw6X6XN4sQMPZKF88GoP30O+oTuh5lRIJr5pgFWrRpF1AgXnJJ2DoFEIPHyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "node-cleanup": "^2.1.2", + "ps-tree": "^1.2.0", + "string-argv": "^0.3.1" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -6503,12 +6605,6 @@ "xtend": "^4.0.1" } }, - "undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true - }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index 93f5f7e..7b710e9 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,17 @@ { "name": "lnreader-plugins", "version": "1.0.0", - "description": "", + "description": "Plugins repo for LNReader", "main": "index.js", "scripts": { - "start": "nodemon", + "start": "tsc-watch --onSuccess \"node ./.js/index.js\"", "test": "exit 0", - "json": "tsc && node ./.js/scripts/json_plugins.js && git add plugins && git add .dist && git add .js/plugins", + "json": "tsc && rm ./.js/index.js && node ./.js/scripts/json_plugins.js && git add plugins && git add .dist && git add .js/plugins", "generate": "ts-node ./scripts/multisrc/generate.ts && tsc", "clearMulti": "ts-node ./scripts/clearMultisrc.ts", "less": "npx less ./test_web/static/css/index.less ./test_web/static/css/index.css" }, - "author": "", + "author": "LNReader", "license": "MIT", "dependencies": { "body-parser": "^1.20.2", @@ -38,8 +38,8 @@ "@types/express": "^4.17.17", "@types/jquery": "^3.5.28", "@types/node": "^20.3.1", - "nodemon": "^2.0.22", - "pre-commit": "^1.2.2" + "pre-commit": "^1.2.2", + "tsc-watch": "^6.0.4" }, "pre-commit": [ "json" diff --git a/plugins/arabic/kolnovel.ts b/plugins/arabic/kolnovel.ts index d73b78a..7dd851d 100644 --- a/plugins/arabic/kolnovel.ts +++ b/plugins/arabic/kolnovel.ts @@ -1,328 +1,315 @@ -import { load as parseHTML } from "cheerio"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { Plugin } from "@typings/plugin"; - -class KolNovel implements Plugin.PluginBase { - id = "kolnovel"; - name = "KolNovel"; - icon = "multisrc/wpmangastream/icons/kolnovel.png"; - site = "https://kolnovel.lol/"; - version = "1.0.0"; - userAgent = ""; - cookieString = ""; - async popularNovels( - pageNo: number, - { filters }: Plugin.PopularNovelsOptions - ): Promise { - let link = `${this.site}series/?page=${pageNo}`; - - if (filters.genres.value.length) - link += filters.genres.value.map((i) => `&genre[]=${i}`).join(""); - if (filters.type.value.length) - link += filters.type.value.map((i) => `&lang[]=${i}`).join(""); - - link += "&status=" + (filters?.status ? filters.status : ""); - - link += "&order=" + (filters?.order ? filters.order : "popular"); - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const body = await fetchApi(link, { headers }).then((result) => - result.text() - ); - - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - - loadedCheerio("article.maindet").each(function () { - const novelName = loadedCheerio(this).find("h2").text(); - let image = loadedCheerio(this).find("img"); - const novelCover = image.attr("data-src") || image.attr("src"); - const novelUrl = loadedCheerio(this).find("h2 a").attr("href"); - - if (!novelUrl) return; - - const novel = { - name: novelName, - cover: novelCover, - url: novelUrl, - }; - - novels.push(novel); - }); - - return novels; - } - async parseNovelAndChapters(novelUrl: string): Promise { - const url = novelUrl; - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetch(url, { headers }); - const body = await result.text(); - - let loadedCheerio = parseHTML(body); - - const novel: Plugin.SourceNovel = { - url, - chapters: [], - }; - - novel.name = loadedCheerio("h1.entry-title").text(); - - novel.cover = - loadedCheerio("img.wp-post-image").attr("data-src") || - loadedCheerio("img.wp-post-image").attr("src"); - - loadedCheerio("div.serl:nth-child(3) > span").each(function () { - const detailName = loadedCheerio(this).text().trim(); - const detail = loadedCheerio(this).next().text().trim(); - - switch (detailName) { - case "الكاتب": - case "Author": - novel.author = detail; - break; - } - }); - - novel.status = loadedCheerio("div.sertostat > span").attr("class"); - - novel.genres = loadedCheerio(".sertogenre") - .children("a") - .map((i, el) => loadedCheerio(el).text()) - .toArray() - .join(","); - - novel.summary = loadedCheerio(".sersys") - .find("br") - .replaceWith("\n") - .end() - .text(); - - let chapter: Plugin.ChapterItem[] = []; - - loadedCheerio(".eplister") - .find("li") - .each(function () { - const chapterName = - loadedCheerio(this).find(".epl-num").text() + - " - " + - loadedCheerio(this).find(".epl-title").text(); - - const releaseDate = loadedCheerio(this) - .find(".epl-date") - .text() - .trim(); - - const chapterUrl = loadedCheerio(this).find("a").attr("href"); - - if (!chapterUrl) return; - - chapter.push({ - name: chapterName, - releaseTime: releaseDate, - url: chapterUrl, - }); - }); - - novel.chapters = chapter.reverse(); - - return novel; - } - async parseChapter(chapterUrl: string): Promise { - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetch(chapterUrl, { headers }); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - loadedCheerio(".epcontent > div, i").remove(); - let ignore = loadedCheerio("article > style").text().trim().split(","); - ignore.push(...(ignore.pop()?.match(/^\.\w+/) || [])); - ignore.map((tag) => loadedCheerio(`p${tag}`).remove()); - - let chapterText = loadedCheerio(".epcontent").html() || ""; - - return chapterText; - } - async searchNovels( - searchTerm: string, - pageNo?: number | undefined - ): Promise { - const url = `${this.site}?s=${searchTerm}`; - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(url, { headers }); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - - loadedCheerio("article.maindet").each(function () { - const novelName = loadedCheerio(this).find("h2").text(); - let image = loadedCheerio(this).find("img"); - const novelCover = image.attr("data-src") || image.attr("src"); - const novelUrl = loadedCheerio(this).find("h2 a").attr("href"); - - if (!novelUrl) return; - - novels.push({ - name: novelName, - url: novelUrl, - cover: novelCover, - }); - }); - - return novels; - } - async fetchImage(url: string): Promise { - return await fetchFile(url); - } - filters = { - order: { - label: "ترتيب حسب", - options: [ - { label: "الإعداد الأولي", value: "" }, - - { label: "A-Z", value: "title" }, - - { label: "Z-A", value: "titlereverse" }, - - { label: "أخر التحديثات", value: "update" }, - - { label: "أخر ما تم إضافته", value: "latest" }, - - { label: "الرائجة", value: "popular" }, - ], - value: "", - type: FilterTypes.Picker, - }, - status: { - value: "", - label: "الحالة", - options: [ - { label: "All", value: "" }, - - { label: "Ongoing", value: "ongoing" }, - - { label: "Hiatus", value: "hiatus" }, - - { label: "Completed", value: "completed" }, - ], - type: FilterTypes.Picker, - }, - type: { - value: [], - label: "النوع", - options: [ - { label: "إنجليزية", value: "english" }, - - { label: "روايةلايت", value: "light-novel" }, - - { label: "روايةويب", value: "web-novel" }, - - { label: "صينية", value: "chinese" }, - - { label: "عربية", value: "arabic" }, - - { label: "كورية", value: "korean" }, - - { label: "يابانية", value: "japanese" }, - ], - type: FilterTypes.CheckboxGroup, - }, - genres: { - value: [], - label: "تصنيف", - options: [ - { label: "Wuxia", value: "wuxia" }, - - { label: "Xianxia", value: "xianxia" }, - - { label: "XUANHUAN", value: "xuanhuan" }, - - { label: "أكشن", value: "action" }, - - { label: "إثارة", value: "excitement" }, - - { label: "إنتقالالىعالمأخر", value: "isekai" }, - - { label: "إيتشي", value: "etchi" }, - - { label: "الخيالالعلمي", value: "sci-fi" }, - - { label: "بوليسي", value: "policy" }, - - { label: "تاريخي", value: "historical" }, - - { label: "تحقيقات", value: "%d8%aa%d8%ad%d9%82%d9%8a%d9%82" }, - - { label: "تقمصشخصيات", value: "rpg" }, - - { label: "جريمة", value: "crime" }, - - { label: "جوسى", value: "josei" }, - - { label: "حريم", value: "harem" }, - - { label: "حياةمدرسية", value: "school-life" }, - - { label: "خيالي(فانتازيا)", value: "fantasy" }, - - { label: "دراما", value: "drama" }, - - { label: "رعب", value: "horror" }, - - { label: "رومانسي", value: "romantic" }, - - { label: "سحر", value: "magic" }, - - { label: "سينن", value: "senen" }, - - { label: "شريحةمنالحياة", value: "slice-of-life" }, - - { label: "شوجو", value: "shojo" }, - - { label: "شونين", value: "shonen" }, - - { label: "طبي", value: "medical" }, - - { label: "ظواهرخارقةللطبيعة", value: "supernatural" }, - - { label: "غموض", value: "mysteries" }, - - { label: "فنونالقتال", value: "martial-arts" }, - - { label: "قوىخارقة", value: "superpower" }, - - { label: "كوميدي", value: "comedy" }, - - { label: "مأساوي", value: "tragedy" }, - - { label: "مابعدالكارثة", value: "after-the-disaster" }, - - { label: "مغامرة", value: "adventure" }, - - { label: "ميكا", value: "mechanical" }, - - { label: "ناضج", value: "mature" }, - - { label: "نفسي", value: "psychological" }, - ], - type: FilterTypes.CheckboxGroup, - }, - } satisfies Filters; -} - -export default new KolNovel(); +import { load as parseHTML } from "cheerio"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { Plugin } from "@typings/plugin"; + +class KolNovel implements Plugin.PluginBase { + id = "kolnovel"; + name = "KolNovel"; + icon = "multisrc/wpmangastream/icons/kolnovel.png"; + site = "https://kolnovel.lol/"; + version = "1.0.0"; + userAgent = ""; + async popularNovels( + pageNo: number, + { filters }: Plugin.PopularNovelsOptions + ): Promise { + let link = `${this.site}series/?page=${pageNo}`; + + if (filters.genres.value.length) + link += filters.genres.value.map((i) => `&genre[]=${i}`).join(""); + if (filters.type.value.length) + link += filters.type.value.map((i) => `&lang[]=${i}`).join(""); + + link += "&status=" + (filters?.status ? filters.status : ""); + + link += "&order=" + (filters?.order ? filters.order : "popular"); + const headers = new Headers(); + const body = await fetchApi(link, { headers }).then((result) => + result.text() + ); + + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + loadedCheerio("article.maindet").each(function () { + const novelName = loadedCheerio(this).find("h2").text(); + let image = loadedCheerio(this).find("img"); + const novelCover = image.attr("data-src") || image.attr("src"); + const novelUrl = loadedCheerio(this).find("h2 a").attr("href"); + + if (!novelUrl) return; + + const novel = { + name: novelName, + cover: novelCover, + url: novelUrl, + }; + + novels.push(novel); + }); + + return novels; + } + async parseNovelAndChapters(novelUrl: string): Promise { + const url = novelUrl; + const headers = new Headers(); + const result = await fetch(url, { headers }); + const body = await result.text(); + + let loadedCheerio = parseHTML(body); + + const novel: Plugin.SourceNovel = { + url, + chapters: [], + }; + + novel.name = loadedCheerio("h1.entry-title").text(); + + novel.cover = + loadedCheerio("img.wp-post-image").attr("data-src") || + loadedCheerio("img.wp-post-image").attr("src"); + + loadedCheerio("div.serl:nth-child(3) > span").each(function () { + const detailName = loadedCheerio(this).text().trim(); + const detail = loadedCheerio(this).next().text().trim(); + + switch (detailName) { + case "الكاتب": + case "Author": + novel.author = detail; + break; + } + }); + + novel.status = loadedCheerio("div.sertostat > span").attr("class"); + + novel.genres = loadedCheerio(".sertogenre") + .children("a") + .map((i, el) => loadedCheerio(el).text()) + .toArray() + .join(","); + + novel.summary = loadedCheerio(".sersys") + .find("br") + .replaceWith("\n") + .end() + .text(); + + let chapter: Plugin.ChapterItem[] = []; + + loadedCheerio(".eplister") + .find("li") + .each(function () { + const chapterName = + loadedCheerio(this).find(".epl-num").text() + + " - " + + loadedCheerio(this).find(".epl-title").text(); + + const releaseDate = loadedCheerio(this) + .find(".epl-date") + .text() + .trim(); + + const chapterUrl = loadedCheerio(this).find("a").attr("href"); + + if (!chapterUrl) return; + + chapter.push({ + name: chapterName, + releaseTime: releaseDate, + url: chapterUrl, + }); + }); + + novel.chapters = chapter.reverse(); + + return novel; + } + async parseChapter(chapterUrl: string): Promise { + const headers = new Headers(); + const result = await fetch(chapterUrl, { headers }); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + loadedCheerio(".epcontent > div, i").remove(); + let ignore = loadedCheerio("article > style").text().trim().split(","); + ignore.push(...(ignore.pop()?.match(/^\.\w+/) || [])); + ignore.map((tag) => loadedCheerio(`p${tag}`).remove()); + + let chapterText = loadedCheerio(".epcontent").html() || ""; + + return chapterText; + } + async searchNovels( + searchTerm: string, + pageNo?: number | undefined + ): Promise { + const url = `${this.site}?s=${searchTerm}`; + const headers = new Headers(); + const result = await fetchApi(url, { headers }); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + loadedCheerio("article.maindet").each(function () { + const novelName = loadedCheerio(this).find("h2").text(); + let image = loadedCheerio(this).find("img"); + const novelCover = image.attr("data-src") || image.attr("src"); + const novelUrl = loadedCheerio(this).find("h2 a").attr("href"); + + if (!novelUrl) return; + + novels.push({ + name: novelName, + url: novelUrl, + cover: novelCover, + }); + }); + + return novels; + } + async fetchImage(url: string): Promise { + return await fetchFile(url); + } + filters = { + order: { + label: "ترتيب حسب", + options: [ + { label: "الإعداد الأولي", value: "" }, + + { label: "A-Z", value: "title" }, + + { label: "Z-A", value: "titlereverse" }, + + { label: "أخر التحديثات", value: "update" }, + + { label: "أخر ما تم إضافته", value: "latest" }, + + { label: "الرائجة", value: "popular" }, + ], + value: "", + type: FilterTypes.Picker, + }, + status: { + value: "", + label: "الحالة", + options: [ + { label: "All", value: "" }, + + { label: "Ongoing", value: "ongoing" }, + + { label: "Hiatus", value: "hiatus" }, + + { label: "Completed", value: "completed" }, + ], + type: FilterTypes.Picker, + }, + type: { + value: [], + label: "النوع", + options: [ + { label: "إنجليزية", value: "english" }, + + { label: "روايةلايت", value: "light-novel" }, + + { label: "روايةويب", value: "web-novel" }, + + { label: "صينية", value: "chinese" }, + + { label: "عربية", value: "arabic" }, + + { label: "كورية", value: "korean" }, + + { label: "يابانية", value: "japanese" }, + ], + type: FilterTypes.CheckboxGroup, + }, + genres: { + value: [], + label: "تصنيف", + options: [ + { label: "Wuxia", value: "wuxia" }, + + { label: "Xianxia", value: "xianxia" }, + + { label: "XUANHUAN", value: "xuanhuan" }, + + { label: "أكشن", value: "action" }, + + { label: "إثارة", value: "excitement" }, + + { label: "إنتقالالىعالمأخر", value: "isekai" }, + + { label: "إيتشي", value: "etchi" }, + + { label: "الخيالالعلمي", value: "sci-fi" }, + + { label: "بوليسي", value: "policy" }, + + { label: "تاريخي", value: "historical" }, + + { label: "تحقيقات", value: "%d8%aa%d8%ad%d9%82%d9%8a%d9%82" }, + + { label: "تقمصشخصيات", value: "rpg" }, + + { label: "جريمة", value: "crime" }, + + { label: "جوسى", value: "josei" }, + + { label: "حريم", value: "harem" }, + + { label: "حياةمدرسية", value: "school-life" }, + + { label: "خيالي(فانتازيا)", value: "fantasy" }, + + { label: "دراما", value: "drama" }, + + { label: "رعب", value: "horror" }, + + { label: "رومانسي", value: "romantic" }, + + { label: "سحر", value: "magic" }, + + { label: "سينن", value: "senen" }, + + { label: "شريحةمنالحياة", value: "slice-of-life" }, + + { label: "شوجو", value: "shojo" }, + + { label: "شونين", value: "shonen" }, + + { label: "طبي", value: "medical" }, + + { label: "ظواهرخارقةللطبيعة", value: "supernatural" }, + + { label: "غموض", value: "mysteries" }, + + { label: "فنونالقتال", value: "martial-arts" }, + + { label: "قوىخارقة", value: "superpower" }, + + { label: "كوميدي", value: "comedy" }, + + { label: "مأساوي", value: "tragedy" }, + + { label: "مابعدالكارثة", value: "after-the-disaster" }, + + { label: "مغامرة", value: "adventure" }, + + { label: "ميكا", value: "mechanical" }, + + { label: "ناضج", value: "mature" }, + + { label: "نفسي", value: "psychological" }, + ], + type: FilterTypes.CheckboxGroup, + }, + } satisfies Filters; +} + +export default new KolNovel(); diff --git a/plugins/chinese/linovelib.ts b/plugins/chinese/linovelib.ts index 01b6d07..667267a 100644 --- a/plugins/chinese/linovelib.ts +++ b/plugins/chinese/linovelib.ts @@ -1,587 +1,574 @@ -import { CheerioAPI, load as parseHTML } from "cheerio"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { Plugin } from "@typings/plugin"; -import { showToast } from "@libs/showToast"; - -class Linovelib implements Plugin.PluginBase { - id = "linovelib"; - name = "Linovelib"; - icon = "src/cn/linovelib/icon.png"; - site = "https://w.linovelib.com"; - version = "1.0.0"; - userAgent = ""; - cookieString = ""; - async popularNovels( - pageNo: number, - { filters }: Plugin.PopularNovelsOptions - ): Promise { - let link = `${this.site}/top/`; - - link += filters.sort; - - link += `/${pageNo}.html`; - - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const body = await fetchApi(link, { headers }).then((result) => - result.text() - ); - - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - - loadedCheerio(".module-rank-booklist .book-layout").each((i, el) => { - let url = loadedCheerio(el).attr("href"); - - const novelName = loadedCheerio(el).find(".book-title").text(); - const novelCover = loadedCheerio(el) - .find("img.book-cover") - .attr("data-src"); - const novelUrl = this.site + url; - - if (!url) return; - - const novel = { - name: novelName, - cover: novelCover, - url: novelUrl, - }; - - novels.push(novel); - }); - - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const url = novelUrl; - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(url, { headers }); - const body = await result.text(); - - let loadedCheerio = parseHTML(body); - - const novel: Plugin.SourceNovel = { - url, - chapters: [], - }; - - novel.name = loadedCheerio("#bookDetailWrapper .book-title").text(); - - novel.cover = loadedCheerio("#bookDetailWrapper img.book-cover").attr( - "src" - ); - - novel.summary = loadedCheerio("#bookSummary content").text(); - - novel.author = loadedCheerio( - "#bookDetailWrapper .book-rand-a a" - ).text(); - - // TODO: Need some regex and dirty selector to get it - // Need to look into how to translate that message - novel.status = undefined; - - novel.genres = loadedCheerio(".tag-small.red") - .children("a") - .map((i, el) => loadedCheerio(el).text()) - .toArray() - .join(","); - - // Table of Content is on a different page than the summary page - let chapter: Plugin.ChapterItem[] = []; - - const idPattern = /\/(\d+)\.html/; - const novelId = url.match(idPattern)?.[1]; - - const chaptersUrl = - this.site + loadedCheerio("#btnReadBook").attr("href"); - const chaptersResult = await fetchApi(chaptersUrl, { headers }); - const chaptersBody = await chaptersResult.text(); - - const chaptersLoadedCheerio = parseHTML(chaptersBody); - - let volumeName: string, chapterId: number; - - chaptersLoadedCheerio("#volumes .chapter-li").each((i, el) => { - if (chaptersLoadedCheerio(el).hasClass("chapter-bar")) { - volumeName = chaptersLoadedCheerio(el).text(); - } else { - const urlPart = chaptersLoadedCheerio(el) - .find(".chapter-li-a") - .attr("href"); - const chapterIdMatch = urlPart?.match(idPattern); - - // Sometimes the href attribute does not contain the url, but javascript:cid(0). - // Increment the previous chapter ID should result in the right URL - if (chapterIdMatch) { - chapterId = +chapterIdMatch[1]; - } else { - chapterId++; - } - } - - const chapterUrl = `${this.site}/novel/${novelId}/${chapterId}.html`; - const chapterName = - volumeName + - " — " + - chaptersLoadedCheerio(el).find(".chapter-index").text().trim(); - const releaseDate = null; - - if (!chapterId) return; - - chapter.push({ - name: chapterName, - releaseTime: releaseDate, - url: chapterUrl, - }); - }); - - novel.chapters = chapter; - - return novel; - } - - async parseChapter(chapterUrl: string): Promise { - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - let chapterName, - chapterText = "", - hasNextPage, - pageHasNextPage, - pageText = ""; - let pageNumber = 1; - - /* - * TODO: Maybe there are other ways to get the translation table - * It is embed and encrypted inside readtool.js - * UPDATE: Decrypted, see skillgg - */ - // const mapping_dict = { - // '“': '「', - // '’': '』', - // '': '是', - // '': '不', - // '': '好', - // '': '个', - // '': '开', - // '': '样', - // '': '想', - // '': '说', - // '': '年', - // '': '那', - // '': '她', - // '': '美', - // '': '自', - // '': '家', - // '': '而', - // '': '去', - // '': '都', - // '': '于', - // '': '舔', - // '': '他', - // '': '只', - // '': '看', - // '': '来', - // '': '用', - // '': '道', - // '': '得', - // '': '乳', - // '': '茎', - // '': '肉', - // '': '胸', - // '': '淫', - // '': '性', - // '': '骚', - // '”': '」', - // '': '的', - // '': '当', - // '': '人', - // '': '有', - // '': '上', - // '': '到', - // '': '地', - // '': '中', - // '': '生', - // '': '着', - // '': '和', - // '': '起', - // '': '交', - // '': '以', - // '': '可', - // '': '过', - // '': '能', - // '': '多', - // '': '心', - // '': '小', - // '': '成', - // '': '了', - // '': '把', - // '': '发', - // '': '第', - // '': '子', - // '': '事', - // '': '阴', - // '': '欲', - // '': '里', - // '': '私', - // '': '臀', - // '': '脱', - // '': '唇', - // '‘': '『', - // '': '一', - // '': '我', - // '': '在', - // '': '这', - // '': '们', - // '': '时', - // '': '为', - // '': '你', - // '': '国', - // '': '就', - // '': '要', - // '': '也', - // '': '后', - // '': '没', - // '': '下', - // '': '天', - // '': '对', - // '': '然', - // '': '学', - // '': '之', - // '': '出', - // '': '没', - // '': '如', - // '': '还', - // '': '大', - // '': '作', - // '': '种', - // '': '液', - // '': '呻', - // '': '射', - // '': '穴', - // '': '么', - // '': '裸', - // }; - const skillgg: Record = { - "\u201c": "\u300c", - "\u201d": "\u300d", - "\u2018": "\u300e", - "\u2019": "\u300f", - "\ue82c": "\u7684", - "\ue852": "\u4e00", - "\ue82d": "\u662f", - "\ue819": "\u4e86", - "\ue856": "\u6211", - "\ue857": "\u4e0d", - "\ue816": "\u4eba", - "\ue83c": "\u5728", - "\ue830": "\u4ed6", - "\ue82e": "\u6709", - "\ue836": "\u8fd9", - "\ue859": "\u4e2a", - "\ue80a": "\u4e0a", - "\ue855": "\u4eec", - "\ue842": "\u6765", - "\ue858": "\u5230", - "\ue80b": "\u65f6", - "\ue81f": "\u5927", - "\ue84a": "\u5730", - "\ue853": "\u4e3a", - "\ue81e": "\u5b50", - "\ue822": "\u4e2d", - "\ue813": "\u4f60", - "\ue85b": "\u8bf4", - "\ue807": "\u751f", - "\ue818": "\u56fd", - "\ue810": "\u5e74", - "\ue812": "\u7740", - "\ue851": "\u5c31", - "\ue801": "\u90a3", - "\ue80c": "\u548c", - "\ue815": "\u8981", - "\ue84c": "\u5979", - "\ue840": "\u51fa", - "\ue848": "\u4e5f", - "\ue835": "\u5f97", - "\ue800": "\u91cc", - "\ue826": "\u540e", - "\ue863": "\u81ea", - "\ue861": "\u4ee5", - "\ue854": "\u4f1a", - "\ue827": "\u5bb6", - "\ue83b": "\u53ef", - "\ue85d": "\u4e0b", - "\ue84d": "\u800c", - "\ue862": "\u8fc7", - "\ue81c": "\u5929", - "\ue81d": "\u53bb", - "\ue860": "\u80fd", - "\ue843": "\u5bf9", - "\ue82f": "\u5c0f", - "\ue802": "\u591a", - "\ue831": "\u7136", - "\ue84b": "\u4e8e", - "\ue837": "\u5fc3", - "\ue829": "\u5b66", - "\ue85e": "\u4e48", - "\ue83a": "\u4e4b", - "\ue832": "\u90fd", - "\ue808": "\u597d", - "\ue841": "\u770b", - "\ue821": "\u8d77", - "\ue845": "\u53d1", - "\ue803": "\u5f53", - "\ue828": "\u6ca1", - "\ue81b": "\u6210", - "\ue83e": "\u53ea", - "\ue820": "\u5982", - "\ue84e": "\u4e8b", - "\ue85a": "\u628a", - "\ue806": "\u8fd8", - "\ue83f": "\u7528", - "\ue833": "\u7b2c", - "\ue811": "\u6837", - "\ue804": "\u9053", - "\ue814": "\u60f3", - "\ue80f": "\u4f5c", - "\ue84f": "\u79cd", - "\ue80e": "\u5f00", - "\ue823": "\u7f8e", - "\ue849": "\u4e73", - "\ue805": "\u9634", - "\ue809": "\u6db2", - "\ue81a": "\u830e", - "\ue844": "\u6b32", - "\ue847": "\u547b", - "\ue850": "\u8089", - "\ue824": "\u4ea4", - "\ue85f": "\u6027", - "\ue817": "\u80f8", - "\ue85c": "\u79c1", - "\ue838": "\u7a74", - "\ue82a": "\u6deb", - "\ue83d": "\u81c0", - "\ue82b": "\u8214", - "\ue80d": "\u5c04", - "\ue839": "\u8131", - "\ue834": "\u88f8", - "\ue846": "\u9a9a", - "\ue825": "\u5507", - }; - const addPage = async (pageCheerio: CheerioAPI) => { - const formatPage = async () => { - // Remove JS - pageCheerio("#ccacontent .cgo").remove(); - - // Load lazyloaded images - pageCheerio("#ccacontent img.imagecontent").each((i, el) => { - // Sometimes images are either in data-src or src - const imgSrc = - pageCheerio(el).attr("data-src") || - pageCheerio(el).attr("src"); - if (imgSrc) { - // The original CDN URL is locked behind a CF-like challenge, switch the URL to bypass that - // There are no react-native-url-polyfill lib, can't use URL API - const regex = /\/\/.+\.com\//; - const imgUrl = imgSrc.replace( - regex, - "//img.linovelib.com/" - ); - // Clean up img element - pageCheerio(el) - .attr("src", imgUrl) - .removeAttr("data-src") - .removeClass("lazyload"); - } - }); - - // Recover the original character - pageText = pageCheerio("#ccacontent").html() || ""; - pageText = pageText.replace( - /./g, - (char) => skillgg[char] || char - ); - - return Promise.resolve(); - }; - - await formatPage(); - chapterName = - pageCheerio("#atitle + h3").text() + - " — " + - pageCheerio("#atitle").text(); - if (chapterText === "") { - chapterText = "

" + chapterName + "

"; - } - chapterText += pageText; - }; - - const loadPage = async (url: string) => { - const result = await fetchApi(url, { headers }); - const body = await result.text(); - const pageCheerio = parseHTML(body); - await addPage(pageCheerio); - pageHasNextPage = - pageCheerio("#footlink a:last").text() === "下一页" - ? true - : false; - return { pageCheerio, pageHasNextPage }; - }; - - let url = chapterUrl; - do { - const page = await loadPage(url); - hasNextPage = page.pageHasNextPage; - if (hasNextPage === true) { - pageNumber++; - url = chapterUrl.replace( - /\.html/gi, - `_${pageNumber}` + ".html" - ); - } - } while (hasNextPage === true); - - return chapterText; - } - - async searchNovels( - searchTerm: string, - pageNo?: number | undefined - ): Promise { - const searchUrl = `${this.site}/search/`; - const Term = encodeURI(searchTerm); - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - let NextPage, NoNextPage: boolean, DeadEnd; - pageNo = 1; - const novels: Plugin.NovelItem[] = []; - - const addPage = async (pageCheerio: CheerioAPI, redirect: string) => { - const loadSearchResults = () => { - pageCheerio(".book-ol .book-layout").each((i, el) => { - let nUrl = pageCheerio(el).attr("href"); - - const novelName = pageCheerio(el) - .find(".book-title") - .text(); - const novelCover = pageCheerio(el) - .find("img.book-cover") - .attr("data-src"); - const novelUrl = this.site + nUrl; - - if (!nUrl) return; - - novels.push({ - name: novelName, - url: novelUrl, - cover: novelCover, - }); - }); - }; - - const novelResults = pageCheerio(".book-ol a.book-layout"); - if (novelResults.length === 0) { - showToast("Bypass check by searching in Webview"); - } else { - loadSearchResults(); - } - - if (redirect.length) { - novels.length = 0; - const novelName = pageCheerio( - "#bookDetailWrapper .book-title" - ).text(); - - const novelCover = pageCheerio( - "#bookDetailWrapper img.book-cover" - ).attr("src"); - const novelUrl = - this.site + - pageCheerio("#btnReadBook").attr("href")?.slice(0, -8) + - ".html"; - novels.push({ - name: novelName, - url: novelUrl, - cover: novelCover, - }); - } - }; - - const loadPage = async (url: string) => { - const result = await fetchApi(url, { headers }); - const body = await result.text(); - const pageCheerio = parseHTML(body); - const redirect = pageCheerio("div.book-layout").text(); - await addPage(pageCheerio, redirect); - NextPage = pageCheerio(".next").attr("href"); - if (!NextPage) { - NoNextPage === true; - } else { - NoNextPage = NextPage === "#" ? true : false; - } - return { pageCheerio, NoNextPage }; - }; - - let url = `${searchUrl}${Term}_${pageNo}.html`; - do { - const page = await loadPage(url); - DeadEnd = page.NoNextPage; - if (DeadEnd === false) { - pageNo++; - url = `${searchUrl}${Term}_${pageNo}.html`; - } - } while (DeadEnd === false); - - return novels; - } - - async fetchImage(url: string): Promise { - return await fetchFile(url); - } - filters = { - sort: { - label: "Sort By", - value: "monthvisit", - options: [ - { label: "月点击榜", value: "monthvisit" }, - - { label: "周点击榜", value: "weekvisit" }, - - { label: "月推荐榜", value: "monthvote" }, - - { label: "周推荐榜", value: "weekvote" }, - - { label: "月鲜花榜", value: "monthflower" }, - - { label: "周鲜花榜", value: "weekflower" }, - - { label: "月鸡蛋榜", value: "monthegg" }, - - { label: "周鸡蛋榜", value: "weekegg" }, - - { label: "最近更新", value: "lastupdate" }, - - { label: "最新入库", value: "postdate" }, - - { label: "收藏榜", value: "goodnum" }, - - { label: "新书榜", value: "newhot" }, - ], - type: FilterTypes.Picker, - }, - } satisfies Filters; -} - -export default new Linovelib(); +import { CheerioAPI, load as parseHTML } from "cheerio"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { Plugin } from "@typings/plugin"; +import { showToast } from "@libs/showToast"; + +class Linovelib implements Plugin.PluginBase { + id = "linovelib"; + name = "Linovelib"; + icon = "src/cn/linovelib/icon.png"; + site = "https://w.linovelib.com"; + version = "1.0.0"; + userAgent = ""; + async popularNovels( + pageNo: number, + { filters }: Plugin.PopularNovelsOptions + ): Promise { + let link = `${this.site}/top/`; + + link += filters.sort; + + link += `/${pageNo}.html`; + + const headers = new Headers(); + const body = await fetchApi(link, { headers }).then((result) => + result.text() + ); + + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + loadedCheerio(".module-rank-booklist .book-layout").each((i, el) => { + let url = loadedCheerio(el).attr("href"); + + const novelName = loadedCheerio(el).find(".book-title").text(); + const novelCover = loadedCheerio(el) + .find("img.book-cover") + .attr("data-src"); + const novelUrl = this.site + url; + + if (!url) return; + + const novel = { + name: novelName, + cover: novelCover, + url: novelUrl, + }; + + novels.push(novel); + }); + + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const url = novelUrl; + const headers = new Headers(); + const result = await fetchApi(url, { headers }); + const body = await result.text(); + + let loadedCheerio = parseHTML(body); + + const novel: Plugin.SourceNovel = { + url, + chapters: [], + }; + + novel.name = loadedCheerio("#bookDetailWrapper .book-title").text(); + + novel.cover = loadedCheerio("#bookDetailWrapper img.book-cover").attr( + "src" + ); + + novel.summary = loadedCheerio("#bookSummary content").text(); + + novel.author = loadedCheerio( + "#bookDetailWrapper .book-rand-a a" + ).text(); + + // TODO: Need some regex and dirty selector to get it + // Need to look into how to translate that message + novel.status = undefined; + + novel.genres = loadedCheerio(".tag-small.red") + .children("a") + .map((i, el) => loadedCheerio(el).text()) + .toArray() + .join(","); + + // Table of Content is on a different page than the summary page + let chapter: Plugin.ChapterItem[] = []; + + const idPattern = /\/(\d+)\.html/; + const novelId = url.match(idPattern)?.[1]; + + const chaptersUrl = + this.site + loadedCheerio("#btnReadBook").attr("href"); + const chaptersResult = await fetchApi(chaptersUrl, { headers }); + const chaptersBody = await chaptersResult.text(); + + const chaptersLoadedCheerio = parseHTML(chaptersBody); + + let volumeName: string, chapterId: number; + + chaptersLoadedCheerio("#volumes .chapter-li").each((i, el) => { + if (chaptersLoadedCheerio(el).hasClass("chapter-bar")) { + volumeName = chaptersLoadedCheerio(el).text(); + } else { + const urlPart = chaptersLoadedCheerio(el) + .find(".chapter-li-a") + .attr("href"); + const chapterIdMatch = urlPart?.match(idPattern); + + // Sometimes the href attribute does not contain the url, but javascript:cid(0). + // Increment the previous chapter ID should result in the right URL + if (chapterIdMatch) { + chapterId = +chapterIdMatch[1]; + } else { + chapterId++; + } + } + + const chapterUrl = `${this.site}/novel/${novelId}/${chapterId}.html`; + const chapterName = + volumeName + + " — " + + chaptersLoadedCheerio(el).find(".chapter-index").text().trim(); + const releaseDate = null; + + if (!chapterId) return; + + chapter.push({ + name: chapterName, + releaseTime: releaseDate, + url: chapterUrl, + }); + }); + + novel.chapters = chapter; + + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const headers = new Headers(); + let chapterName, + chapterText = "", + hasNextPage, + pageHasNextPage, + pageText = ""; + let pageNumber = 1; + + /* + * TODO: Maybe there are other ways to get the translation table + * It is embed and encrypted inside readtool.js + * UPDATE: Decrypted, see skillgg + */ + // const mapping_dict = { + // '“': '「', + // '’': '』', + // '': '是', + // '': '不', + // '': '好', + // '': '个', + // '': '开', + // '': '样', + // '': '想', + // '': '说', + // '': '年', + // '': '那', + // '': '她', + // '': '美', + // '': '自', + // '': '家', + // '': '而', + // '': '去', + // '': '都', + // '': '于', + // '': '舔', + // '': '他', + // '': '只', + // '': '看', + // '': '来', + // '': '用', + // '': '道', + // '': '得', + // '': '乳', + // '': '茎', + // '': '肉', + // '': '胸', + // '': '淫', + // '': '性', + // '': '骚', + // '”': '」', + // '': '的', + // '': '当', + // '': '人', + // '': '有', + // '': '上', + // '': '到', + // '': '地', + // '': '中', + // '': '生', + // '': '着', + // '': '和', + // '': '起', + // '': '交', + // '': '以', + // '': '可', + // '': '过', + // '': '能', + // '': '多', + // '': '心', + // '': '小', + // '': '成', + // '': '了', + // '': '把', + // '': '发', + // '': '第', + // '': '子', + // '': '事', + // '': '阴', + // '': '欲', + // '': '里', + // '': '私', + // '': '臀', + // '': '脱', + // '': '唇', + // '‘': '『', + // '': '一', + // '': '我', + // '': '在', + // '': '这', + // '': '们', + // '': '时', + // '': '为', + // '': '你', + // '': '国', + // '': '就', + // '': '要', + // '': '也', + // '': '后', + // '': '没', + // '': '下', + // '': '天', + // '': '对', + // '': '然', + // '': '学', + // '': '之', + // '': '出', + // '': '没', + // '': '如', + // '': '还', + // '': '大', + // '': '作', + // '': '种', + // '': '液', + // '': '呻', + // '': '射', + // '': '穴', + // '': '么', + // '': '裸', + // }; + const skillgg: Record = { + "\u201c": "\u300c", + "\u201d": "\u300d", + "\u2018": "\u300e", + "\u2019": "\u300f", + "\ue82c": "\u7684", + "\ue852": "\u4e00", + "\ue82d": "\u662f", + "\ue819": "\u4e86", + "\ue856": "\u6211", + "\ue857": "\u4e0d", + "\ue816": "\u4eba", + "\ue83c": "\u5728", + "\ue830": "\u4ed6", + "\ue82e": "\u6709", + "\ue836": "\u8fd9", + "\ue859": "\u4e2a", + "\ue80a": "\u4e0a", + "\ue855": "\u4eec", + "\ue842": "\u6765", + "\ue858": "\u5230", + "\ue80b": "\u65f6", + "\ue81f": "\u5927", + "\ue84a": "\u5730", + "\ue853": "\u4e3a", + "\ue81e": "\u5b50", + "\ue822": "\u4e2d", + "\ue813": "\u4f60", + "\ue85b": "\u8bf4", + "\ue807": "\u751f", + "\ue818": "\u56fd", + "\ue810": "\u5e74", + "\ue812": "\u7740", + "\ue851": "\u5c31", + "\ue801": "\u90a3", + "\ue80c": "\u548c", + "\ue815": "\u8981", + "\ue84c": "\u5979", + "\ue840": "\u51fa", + "\ue848": "\u4e5f", + "\ue835": "\u5f97", + "\ue800": "\u91cc", + "\ue826": "\u540e", + "\ue863": "\u81ea", + "\ue861": "\u4ee5", + "\ue854": "\u4f1a", + "\ue827": "\u5bb6", + "\ue83b": "\u53ef", + "\ue85d": "\u4e0b", + "\ue84d": "\u800c", + "\ue862": "\u8fc7", + "\ue81c": "\u5929", + "\ue81d": "\u53bb", + "\ue860": "\u80fd", + "\ue843": "\u5bf9", + "\ue82f": "\u5c0f", + "\ue802": "\u591a", + "\ue831": "\u7136", + "\ue84b": "\u4e8e", + "\ue837": "\u5fc3", + "\ue829": "\u5b66", + "\ue85e": "\u4e48", + "\ue83a": "\u4e4b", + "\ue832": "\u90fd", + "\ue808": "\u597d", + "\ue841": "\u770b", + "\ue821": "\u8d77", + "\ue845": "\u53d1", + "\ue803": "\u5f53", + "\ue828": "\u6ca1", + "\ue81b": "\u6210", + "\ue83e": "\u53ea", + "\ue820": "\u5982", + "\ue84e": "\u4e8b", + "\ue85a": "\u628a", + "\ue806": "\u8fd8", + "\ue83f": "\u7528", + "\ue833": "\u7b2c", + "\ue811": "\u6837", + "\ue804": "\u9053", + "\ue814": "\u60f3", + "\ue80f": "\u4f5c", + "\ue84f": "\u79cd", + "\ue80e": "\u5f00", + "\ue823": "\u7f8e", + "\ue849": "\u4e73", + "\ue805": "\u9634", + "\ue809": "\u6db2", + "\ue81a": "\u830e", + "\ue844": "\u6b32", + "\ue847": "\u547b", + "\ue850": "\u8089", + "\ue824": "\u4ea4", + "\ue85f": "\u6027", + "\ue817": "\u80f8", + "\ue85c": "\u79c1", + "\ue838": "\u7a74", + "\ue82a": "\u6deb", + "\ue83d": "\u81c0", + "\ue82b": "\u8214", + "\ue80d": "\u5c04", + "\ue839": "\u8131", + "\ue834": "\u88f8", + "\ue846": "\u9a9a", + "\ue825": "\u5507", + }; + const addPage = async (pageCheerio: CheerioAPI) => { + const formatPage = async () => { + // Remove JS + pageCheerio("#ccacontent .cgo").remove(); + + // Load lazyloaded images + pageCheerio("#ccacontent img.imagecontent").each((i, el) => { + // Sometimes images are either in data-src or src + const imgSrc = + pageCheerio(el).attr("data-src") || + pageCheerio(el).attr("src"); + if (imgSrc) { + // The original CDN URL is locked behind a CF-like challenge, switch the URL to bypass that + // There are no react-native-url-polyfill lib, can't use URL API + const regex = /\/\/.+\.com\//; + const imgUrl = imgSrc.replace( + regex, + "//img.linovelib.com/" + ); + // Clean up img element + pageCheerio(el) + .attr("src", imgUrl) + .removeAttr("data-src") + .removeClass("lazyload"); + } + }); + + // Recover the original character + pageText = pageCheerio("#ccacontent").html() || ""; + pageText = pageText.replace( + /./g, + (char) => skillgg[char] || char + ); + + return Promise.resolve(); + }; + + await formatPage(); + chapterName = + pageCheerio("#atitle + h3").text() + + " — " + + pageCheerio("#atitle").text(); + if (chapterText === "") { + chapterText = "

" + chapterName + "

"; + } + chapterText += pageText; + }; + + const loadPage = async (url: string) => { + const result = await fetchApi(url, { headers }); + const body = await result.text(); + const pageCheerio = parseHTML(body); + await addPage(pageCheerio); + pageHasNextPage = + pageCheerio("#footlink a:last").text() === "下一页" + ? true + : false; + return { pageCheerio, pageHasNextPage }; + }; + + let url = chapterUrl; + do { + const page = await loadPage(url); + hasNextPage = page.pageHasNextPage; + if (hasNextPage === true) { + pageNumber++; + url = chapterUrl.replace( + /\.html/gi, + `_${pageNumber}` + ".html" + ); + } + } while (hasNextPage === true); + + return chapterText; + } + + async searchNovels( + searchTerm: string, + pageNo?: number | undefined + ): Promise { + const searchUrl = `${this.site}/search/`; + const Term = encodeURI(searchTerm); + const headers = new Headers(); + let NextPage, NoNextPage: boolean, DeadEnd; + pageNo = 1; + const novels: Plugin.NovelItem[] = []; + + const addPage = async (pageCheerio: CheerioAPI, redirect: string) => { + const loadSearchResults = () => { + pageCheerio(".book-ol .book-layout").each((i, el) => { + let nUrl = pageCheerio(el).attr("href"); + + const novelName = pageCheerio(el) + .find(".book-title") + .text(); + const novelCover = pageCheerio(el) + .find("img.book-cover") + .attr("data-src"); + const novelUrl = this.site + nUrl; + + if (!nUrl) return; + + novels.push({ + name: novelName, + url: novelUrl, + cover: novelCover, + }); + }); + }; + + const novelResults = pageCheerio(".book-ol a.book-layout"); + if (novelResults.length === 0) { + showToast("Bypass check by searching in Webview"); + } else { + loadSearchResults(); + } + + if (redirect.length) { + novels.length = 0; + const novelName = pageCheerio( + "#bookDetailWrapper .book-title" + ).text(); + + const novelCover = pageCheerio( + "#bookDetailWrapper img.book-cover" + ).attr("src"); + const novelUrl = + this.site + + pageCheerio("#btnReadBook").attr("href")?.slice(0, -8) + + ".html"; + novels.push({ + name: novelName, + url: novelUrl, + cover: novelCover, + }); + } + }; + + const loadPage = async (url: string) => { + const result = await fetchApi(url, { headers }); + const body = await result.text(); + const pageCheerio = parseHTML(body); + const redirect = pageCheerio("div.book-layout").text(); + await addPage(pageCheerio, redirect); + NextPage = pageCheerio(".next").attr("href"); + if (!NextPage) { + NoNextPage === true; + } else { + NoNextPage = NextPage === "#" ? true : false; + } + return { pageCheerio, NoNextPage }; + }; + + let url = `${searchUrl}${Term}_${pageNo}.html`; + do { + const page = await loadPage(url); + DeadEnd = page.NoNextPage; + if (DeadEnd === false) { + pageNo++; + url = `${searchUrl}${Term}_${pageNo}.html`; + } + } while (DeadEnd === false); + + return novels; + } + + async fetchImage(url: string): Promise { + return await fetchFile(url); + } + filters = { + sort: { + label: "Sort By", + value: "monthvisit", + options: [ + { label: "月点击榜", value: "monthvisit" }, + + { label: "周点击榜", value: "weekvisit" }, + + { label: "月推荐榜", value: "monthvote" }, + + { label: "周推荐榜", value: "weekvote" }, + + { label: "月鲜花榜", value: "monthflower" }, + + { label: "周鲜花榜", value: "weekflower" }, + + { label: "月鸡蛋榜", value: "monthegg" }, + + { label: "周鸡蛋榜", value: "weekegg" }, + + { label: "最近更新", value: "lastupdate" }, + + { label: "最新入库", value: "postdate" }, + + { label: "收藏榜", value: "goodnum" }, + + { label: "新书榜", value: "newhot" }, + ], + type: FilterTypes.Picker, + }, + } satisfies Filters; +} + +export default new Linovelib(); diff --git a/plugins/english/bestlightnovel.ts b/plugins/english/bestlightnovel.ts index abe9c21..9da3f1c 100644 --- a/plugins/english/bestlightnovel.ts +++ b/plugins/english/bestlightnovel.ts @@ -1,181 +1,180 @@ -import { load as cheerioload } from "cheerio"; -import { Plugin } from "@typings/plugin"; -import { defaultCover } from "@libs/defaultCover"; -import { fetchFile } from "@libs/fetch"; -import { NovelStatus } from "@libs/novelStatus"; - -class BLN implements Plugin.PluginBase { - id = "BLN.com"; - name = "BestLightNovel"; - icon = "src/en/bestlightnovel/icon.png"; - site = "https://bestlightnovel.com/"; - filter?: undefined; - version = "1.0.0"; - userAgent = ""; - cookieString = ""; - async popularNovels( - pageNo: number, - options: Plugin.PopularNovelsOptions - ): Promise { - const url = - this.site + - "novel_list?type=topview&category=all&state=all&page=1" + - pageNo; - - const result = await fetch(url); - if (!result.ok) { - console.error(await result.text()); - // TODO: Cloudflare protection or other error - return []; - } - const body = await result.text(); - - const loadedCheerio = cheerioload(body); - - let novels: Plugin.NovelItem[] = []; - - loadedCheerio(".update_item.list_category").each(function () { - const novelName = loadedCheerio(this).find("h3 > a").text(); - const novelCover = loadedCheerio(this).find("img").attr("src"); - const novelUrl = loadedCheerio(this).find("h3 > a").attr("href"); - - if (!novelUrl) { - // TODO: Handle error - console.error("No novel url!"); - return; - } - - const novel = { - name: novelName, - url: novelUrl, - cover: novelCover, - }; - - novels.push(novel); - }); - - return novels; - } - async parseNovelAndChapters(novelUrl: string): Promise { - const url = novelUrl; - - const result = await fetch(url); - if (!result.ok) { - console.error(await result.text()); - // TODO: Cloudflare protection - return { url, chapters: [] }; - } - const body = await result.text(); - - let loadedCheerio = cheerioload(body); - - let novel: Plugin.SourceNovel = { - url, - name: "", - cover: "", - author: "", - status: NovelStatus.Unknown, - genres: "", - summary: "", - chapters: [], - }; - - novel.name = loadedCheerio(".truyen_info_right h1").text().trim(); - novel.cover = - loadedCheerio(".info_image img").attr("src") || defaultCover; - novel.summary = loadedCheerio("#noidungm").text()?.trim(); - novel.author = loadedCheerio( - "#main_body > div.cotgiua > div.truyen_info_wrapper > div.truyen_info > div.entry-header > div.truyen_if_wrap > ul > li:nth-child(2) > a" - ) - .text() - ?.trim(); - - let status = loadedCheerio( - "#main_body > div.cotgiua > div.truyen_info_wrapper > div.truyen_info > div.entry-header > div.truyen_if_wrap > ul > li:nth-child(4) > a" - ) - .text() - ?.trim(); - - novel.status = - status === "ONGOING" - ? NovelStatus.Ongoing - : status === "COMPLETED" - ? NovelStatus.Completed - : NovelStatus.Unknown; - - let novelChapters: Plugin.ChapterItem[] = []; - - loadedCheerio(".chapter-list div.row").each(function () { - const chapterName = loadedCheerio(this).find("a").text().trim(); - const releaseDate = loadedCheerio(this) - .find("span:nth-child(2)") - .text() - .trim(); - const chapterUrl = loadedCheerio(this).find("a").attr("href"); - - if (!chapterUrl) { - // TODO: Handle error - console.error("No chapter url!"); - return; - } - - novelChapters.push({ - name: chapterName, - releaseTime: releaseDate, - url: chapterUrl, - }); - }); - - novel.chapters = novelChapters.reverse(); - - return novel; - } - async parseChapter(chapterUrl: string): Promise { - const url = chapterUrl; - - const result = await fetch(url); - const body = await result.text(); - - let loadedCheerio = cheerioload(body); - - const chapterText = loadedCheerio("#vung_doc").html() || ""; - - return chapterText; - } - async searchNovels( - searchTerm: string, - pageNo?: number | undefined - ): Promise { - const url = `${this.site}search_novels/${searchTerm}`; - - const result = await fetch(url); - const body = await result.text(); - - let loadedCheerio = cheerioload(body); - - let novels: Plugin.NovelItem[] = []; - - loadedCheerio(".update_item.list_category").each(function () { - const novelName = loadedCheerio(this).find("h3 > a").text(); - const novelCover = loadedCheerio(this).find("img").attr("src"); - const novelUrl = loadedCheerio(this).find("h3 > a").attr("href"); - - if (!novelUrl) { - // TODO: Handle error - console.error("No novel url!"); - return; - } - - const novel = { name: novelName, cover: novelCover, url: novelUrl }; - - novels.push(novel); - }); - - return novels; - } - async fetchImage(url: string): Promise { - return await fetchFile(url); - } -} - -export default new BLN(); +import { load as cheerioload } from "cheerio"; +import { Plugin } from "@typings/plugin"; +import { defaultCover } from "@libs/defaultCover"; +import { fetchFile } from "@libs/fetch"; +import { NovelStatus } from "@libs/novelStatus"; + +class BLN implements Plugin.PluginBase { + id = "BLN.com"; + name = "BestLightNovel"; + icon = "src/en/bestlightnovel/icon.png"; + site = "https://bestlightnovel.com/"; + filter?: undefined; + version = "1.0.0"; + userAgent = ""; + async popularNovels( + pageNo: number, + options: Plugin.PopularNovelsOptions + ): Promise { + const url = + this.site + + "novel_list?type=topview&category=all&state=all&page=1" + + pageNo; + + const result = await fetch(url); + if (!result.ok) { + console.error(await result.text()); + // TODO: Cloudflare protection or other error + return []; + } + const body = await result.text(); + + const loadedCheerio = cheerioload(body); + + let novels: Plugin.NovelItem[] = []; + + loadedCheerio(".update_item.list_category").each(function () { + const novelName = loadedCheerio(this).find("h3 > a").text(); + const novelCover = loadedCheerio(this).find("img").attr("src"); + const novelUrl = loadedCheerio(this).find("h3 > a").attr("href"); + + if (!novelUrl) { + // TODO: Handle error + console.error("No novel url!"); + return; + } + + const novel = { + name: novelName, + url: novelUrl, + cover: novelCover, + }; + + novels.push(novel); + }); + + return novels; + } + async parseNovelAndChapters(novelUrl: string): Promise { + const url = novelUrl; + + const result = await fetch(url); + if (!result.ok) { + console.error(await result.text()); + // TODO: Cloudflare protection + return { url, chapters: [] }; + } + const body = await result.text(); + + let loadedCheerio = cheerioload(body); + + let novel: Plugin.SourceNovel = { + url, + name: "", + cover: "", + author: "", + status: NovelStatus.Unknown, + genres: "", + summary: "", + chapters: [], + }; + + novel.name = loadedCheerio(".truyen_info_right h1").text().trim(); + novel.cover = + loadedCheerio(".info_image img").attr("src") || defaultCover; + novel.summary = loadedCheerio("#noidungm").text()?.trim(); + novel.author = loadedCheerio( + "#main_body > div.cotgiua > div.truyen_info_wrapper > div.truyen_info > div.entry-header > div.truyen_if_wrap > ul > li:nth-child(2) > a" + ) + .text() + ?.trim(); + + let status = loadedCheerio( + "#main_body > div.cotgiua > div.truyen_info_wrapper > div.truyen_info > div.entry-header > div.truyen_if_wrap > ul > li:nth-child(4) > a" + ) + .text() + ?.trim(); + + novel.status = + status === "ONGOING" + ? NovelStatus.Ongoing + : status === "COMPLETED" + ? NovelStatus.Completed + : NovelStatus.Unknown; + + let novelChapters: Plugin.ChapterItem[] = []; + + loadedCheerio(".chapter-list div.row").each(function () { + const chapterName = loadedCheerio(this).find("a").text().trim(); + const releaseDate = loadedCheerio(this) + .find("span:nth-child(2)") + .text() + .trim(); + const chapterUrl = loadedCheerio(this).find("a").attr("href"); + + if (!chapterUrl) { + // TODO: Handle error + console.error("No chapter url!"); + return; + } + + novelChapters.push({ + name: chapterName, + releaseTime: releaseDate, + url: chapterUrl, + }); + }); + + novel.chapters = novelChapters.reverse(); + + return novel; + } + async parseChapter(chapterUrl: string): Promise { + const url = chapterUrl; + + const result = await fetch(url); + const body = await result.text(); + + let loadedCheerio = cheerioload(body); + + const chapterText = loadedCheerio("#vung_doc").html() || ""; + + return chapterText; + } + async searchNovels( + searchTerm: string, + pageNo?: number | undefined + ): Promise { + const url = `${this.site}search_novels/${searchTerm}`; + + const result = await fetch(url); + const body = await result.text(); + + let loadedCheerio = cheerioload(body); + + let novels: Plugin.NovelItem[] = []; + + loadedCheerio(".update_item.list_category").each(function () { + const novelName = loadedCheerio(this).find("h3 > a").text(); + const novelCover = loadedCheerio(this).find("img").attr("src"); + const novelUrl = loadedCheerio(this).find("h3 > a").attr("href"); + + if (!novelUrl) { + // TODO: Handle error + console.error("No novel url!"); + return; + } + + const novel = { name: novelName, cover: novelCover, url: novelUrl }; + + novels.push(novel); + }); + + return novels; + } + async fetchImage(url: string): Promise { + return await fetchFile(url); + } +} + +export default new BLN(); diff --git a/plugins/english/filterTests.ts b/plugins/english/filterTests.ts index 480a202..95f4b10 100644 --- a/plugins/english/filterTests.ts +++ b/plugins/english/filterTests.ts @@ -9,8 +9,7 @@ class FTest implements Plugin.PluginBase { icon = "src/en/noblemtl/icon.png"; site = "https://noblemtl.com/"; version = "1.0.0"; - userAgent = ""; - cookieString = ""; + userAgent = ""; async popularNovels( pageNo: number, { filters }: Plugin.PopularNovelsOptions diff --git a/plugins/english/freewebnovel.ts b/plugins/english/freewebnovel.ts index ebb6b52..4008ea8 100644 --- a/plugins/english/freewebnovel.ts +++ b/plugins/english/freewebnovel.ts @@ -1,104 +1,103 @@ -import { Plugin } from "@typings/plugin"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { NovelStatus } from "@libs/novelStatus"; -import { load as parseHTML } from "cheerio"; - -class FreeWebNovel implements Plugin.PluginBase { - id = "FWN.com"; - name = "Web NOVEL"; - site = "https://freewebnovel.com"; - version = "1.0.0"; - icon = "src/en/freewebnovel/icon.png"; - userAgent = ""; - cookieString = ""; - - async popularNovels( - page: number, - { showLatestNovels }: Plugin.PopularNovelsOptions, - ): Promise { - const sort = showLatestNovels - ? "/latest-release-novels/" - : "/completed-novels/"; - - const body = await fetchApi(this.site + sort + page).then((res) => res.text()); - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = loadedCheerio(".li-row") - .map((index, element) => ({ - name: loadedCheerio(element).find(".tit").text(), - cover: loadedCheerio(element).find("img").attr("src"), - url: this.site + loadedCheerio(element).find("h3 > a").attr("href"), - })) - .get(); - - return novels; - } - - async parseNovelAndChapters(url: string): Promise { - const body = await fetchApi(url).then((res) => res.text()); - const loadedCheerio = parseHTML(body); - - const novel: Plugin.SourceNovel = { url }; - novel.name = loadedCheerio("h1.tit").text(); - novel.cover = loadedCheerio(".pic > img").attr("src"); - novel.genres = loadedCheerio("[title=Genre]") - .next() - .text() - .replace(/[\t\n]/g, ""); - - novel.author = loadedCheerio("[title=Author]") - .next() - .text() - .replace(/[\t\n]/g, ""); - - novel.status = loadedCheerio("[title=Status]") - .next() - .text() - .replace(/[\t\n]/g, ""); - - novel.summary = loadedCheerio(".inner").text().trim(); - - const chapters: Plugin.ChapterItem[] = loadedCheerio("#idData > li > a") - .map((index, element) => ({ - name: loadedCheerio(element).attr("title") || "Chapter " + index, - releaseTime: null, - url: this.site + loadedCheerio(element).attr("href"), - })) - .get(); - - novel.chapters = chapters; - return novel; - } - - async parseChapter(chapterUrl: string): Promise { - const body = await fetchApi(chapterUrl).then((res) => res.text()); - const loadedCheerio = parseHTML(body); - - const chapterText = loadedCheerio("div.txt").html() || ""; - return chapterText; - } - - async searchNovels( - searchkey: string, - ): Promise { - const body = await fetchApi(this.site + "/search/", { - method: "POST", - body: JSON.stringify({ searchkey }), - }).then((res) => res.text()); - - const loadedCheerio = parseHTML(body); - const novels: Plugin.NovelItem[] = loadedCheerio(".li-row > .li > .con") - .map((index, element) => ({ - name: loadedCheerio(element).find(".tit").text(), - cover: loadedCheerio(element).find(".pic > a > img").attr("data-cfsrc"), - url: this.site + loadedCheerio(element).find("h3 > a").attr("href"), - })) - .get(); - - return novels; - } - - fetchImage = fetchFile; -} - -export default new FreeWebNovel(); +import { Plugin } from "@typings/plugin"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { NovelStatus } from "@libs/novelStatus"; +import { load as parseHTML } from "cheerio"; + +class FreeWebNovel implements Plugin.PluginBase { + id = "FWN.com"; + name = "Web NOVEL"; + site = "https://freewebnovel.com"; + version = "1.0.0"; + icon = "src/en/freewebnovel/icon.png"; + userAgent = ""; + + async popularNovels( + page: number, + { showLatestNovels }: Plugin.PopularNovelsOptions, + ): Promise { + const sort = showLatestNovels + ? "/latest-release-novels/" + : "/completed-novels/"; + + const body = await fetchApi(this.site + sort + page).then((res) => res.text()); + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = loadedCheerio(".li-row") + .map((index, element) => ({ + name: loadedCheerio(element).find(".tit").text(), + cover: loadedCheerio(element).find("img").attr("src"), + url: this.site + loadedCheerio(element).find("h3 > a").attr("href"), + })) + .get(); + + return novels; + } + + async parseNovelAndChapters(url: string): Promise { + const body = await fetchApi(url).then((res) => res.text()); + const loadedCheerio = parseHTML(body); + + const novel: Plugin.SourceNovel = { url }; + novel.name = loadedCheerio("h1.tit").text(); + novel.cover = loadedCheerio(".pic > img").attr("src"); + novel.genres = loadedCheerio("[title=Genre]") + .next() + .text() + .replace(/[\t\n]/g, ""); + + novel.author = loadedCheerio("[title=Author]") + .next() + .text() + .replace(/[\t\n]/g, ""); + + novel.status = loadedCheerio("[title=Status]") + .next() + .text() + .replace(/[\t\n]/g, ""); + + novel.summary = loadedCheerio(".inner").text().trim(); + + const chapters: Plugin.ChapterItem[] = loadedCheerio("#idData > li > a") + .map((index, element) => ({ + name: loadedCheerio(element).attr("title") || "Chapter " + index, + releaseTime: null, + url: this.site + loadedCheerio(element).attr("href"), + })) + .get(); + + novel.chapters = chapters; + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const body = await fetchApi(chapterUrl).then((res) => res.text()); + const loadedCheerio = parseHTML(body); + + const chapterText = loadedCheerio("div.txt").html() || ""; + return chapterText; + } + + async searchNovels( + searchkey: string, + ): Promise { + const body = await fetchApi(this.site + "/search/", { + method: "POST", + body: JSON.stringify({ searchkey }), + }).then((res) => res.text()); + + const loadedCheerio = parseHTML(body); + const novels: Plugin.NovelItem[] = loadedCheerio(".li-row > .li > .con") + .map((index, element) => ({ + name: loadedCheerio(element).find(".tit").text(), + cover: loadedCheerio(element).find(".pic > a > img").attr("data-cfsrc"), + url: this.site + loadedCheerio(element).find("h3 > a").attr("href"), + })) + .get(); + + return novels; + } + + fetchImage = fetchFile; +} + +export default new FreeWebNovel(); diff --git a/plugins/english/nobleMTL.ts b/plugins/english/nobleMTL.ts index 90d3a86..51e57c2 100644 --- a/plugins/english/nobleMTL.ts +++ b/plugins/english/nobleMTL.ts @@ -1,428 +1,415 @@ -import { load as parseHTML } from "cheerio"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { Plugin } from "@typings/plugin"; - -class NobleMTL implements Plugin.PluginBase { - id = "noblemtl"; - name = "NobleMTL"; - icon = "src/en/noblemtl/icon.png"; - site = "https://noblemtl.com/"; - version = "1.0.0"; - userAgent = ""; - cookieString = ""; - async popularNovels( - pageNo: number, - { filters }: Plugin.PopularNovelsOptions - ): Promise { - let link = `${this.site}series/?page=${pageNo}`; - - if (filters.genres.value.length) - link += filters.genres.value.map((i) => `&genre[]=${i}`).join(""); - - if (filters.type.value.length) - link += filters.type.value.map((i) => `&lang[]=${i}`).join(""); - - link += "&status=" + filters.status.value; - - link += "&order=" + filters.order.value; - - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const body = await fetchApi(link, { headers }).then((result) => - result.text() - ); - - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - - loadedCheerio("article.bs").each(function () { - const novelName = loadedCheerio(this).find(".ntitle").text().trim(); - let image = loadedCheerio(this).find("img"); - const novelCover = image.attr("data-src") || image.attr("src"); - - const novelUrl = loadedCheerio(this).find("a").attr("href"); - - if (!novelUrl) return; - - const novel = { - name: novelName, - cover: novelCover, - url: novelUrl, - }; - - novels.push(novel); - }); - - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const url = novelUrl; - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(url, { headers }); - const body = await result.text(); - - let loadedCheerio = parseHTML(body); - - const novel: Plugin.SourceNovel = { - url, - chapters: [], - }; - - novel.name = loadedCheerio(".entry-title").text(); - - novel.cover = - loadedCheerio("img.wp-post-image").attr("data-src") || - loadedCheerio("img.wp-post-image").attr("src"); - - loadedCheerio("div.spe > span").each(function () { - const detailName = loadedCheerio(this).find("b").text().trim(); - const detail = loadedCheerio(this) - .find("b") - .remove() - .end() - .text() - .trim(); - - switch (detailName) { - case "Author:": - novel.author = detail; - break; - case "Status:": - novel.status = detail; - break; - } - }); - - novel.genres = loadedCheerio(".genxed") - .text() - .trim() - .replace(/\s/g, ","); - - loadedCheerio('div[itemprop="description"] h3,p.a,strong').remove(); - novel.summary = loadedCheerio('div[itemprop="description"]') - .find("br") - .replaceWith("\n") - .end() - .text(); - - let chapter: Plugin.ChapterItem[] = []; - - loadedCheerio(".eplister") - .find("li") - .each(function () { - const chapterName = - loadedCheerio(this).find(".epl-num").text() + - " - " + - loadedCheerio(this).find(".epl-title").text(); - - const releaseDate = loadedCheerio(this) - .find(".epl-date") - .text() - .trim(); - - const chapterUrl = loadedCheerio(this).find("a").attr("href"); - - if (!chapterUrl) return; - - chapter.push({ - name: chapterName, - releaseTime: releaseDate, - url: chapterUrl, - }); - }); - - novel.chapters = chapter.reverse(); - - return novel; - } - async parseChapter(chapterUrl: string): Promise { - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(chapterUrl, { headers }); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - let chapterText = loadedCheerio(".epcontent").html() || ""; - - return chapterText; - } - - async searchNovels( - searchTerm: string, - pageNo?: number | undefined - ): Promise { - const url = `${this.site}?s=${searchTerm}`; - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(url, { headers }); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - - loadedCheerio("article.bs").each(function () { - const novelName = loadedCheerio(this).find(".ntitle").text().trim(); - const novelCover = loadedCheerio(this).find("img").attr("src"); - const novelUrl = loadedCheerio(this).find("a").attr("href"); - if (!novelUrl) return; - - novels.push({ - name: novelName, - url: novelUrl, - cover: novelCover, - }); - }); - - return novels; - } - async fetchImage(url: string): Promise { - return await fetchFile(url); - } - - filters = { - order: { - label: "Sort By", - type: FilterTypes.Picker, - options: [ - { label: "Default", value: "" }, - - { label: "A-Z", value: "title" }, - - { label: "Z-A", value: "titlereverse" }, - - { label: "Latest Update", value: "update" }, - - { label: "Latest Added", value: "latest" }, - - { label: "Popular", value: "popular" }, - ], - value: "", - }, - - status: { - label: "Status", - options: [ - { label: "All", value: "" }, - - { label: "Ongoing", value: "ongoing" }, - - { label: "Hiatus", value: "hiatus" }, - - { label: "Completed", value: "completed" }, - ], - type: FilterTypes.Picker, - value: "", - }, - type: { - label: "Type", - options: [ - { label: "Chinese novel", value: "chinese-novel" }, - - { label: "habyeol", value: "habyeol" }, - - { label: "korean novel", value: "korean-novel" }, - - { label: "Web Novel", value: "web-novel" }, - - { label: "삼심", value: "%ec%82%bc%ec%8b%ac" }, - - { label: "호곡", value: "%ed%98%b8%ea%b3%a1" }, - ], - type: FilterTypes.CheckboxGroup, - value: [], - }, - genres: { - label: "Genres", - value: [], - options: [ - { label: "A.I", value: "a.i" }, - - { label: "Academy", value: "academy" }, - - { label: "Action", value: "action" }, - - { label: "Adult", value: "adult" }, - - { label: "Adventure", value: "adventure" }, - - { label: "Alternative History", value: "alternative-history" }, - - { label: "Another World", value: "another-world" }, - - { label: "Apocalypse", value: "apocalypse" }, - - { label: "Bromance", value: "bromance" }, - - { label: "Comedy", value: "comedy" }, - - { label: "Dark fantasy", value: "dark-fantasy" }, - - { label: "Demons", value: "demons" }, - - { label: "Drama", value: "drama" }, - - { label: "Dystopia", value: "dystopia" }, - - { label: "Ecchi", value: "ecchi" }, - - { label: "Entertainment", value: "entertainment" }, - - { label: "Exhaustion", value: "exhaustion" }, - - { label: "Fanfiction", value: "fanfiction" }, - - { label: "fantasy", value: "fantasy" }, - - { label: "finance", value: "finance" }, - - { label: "Full color", value: "full-color" }, - - { label: "Game", value: "game" }, - - { label: "Gender Bender", value: "gender-bender" }, - - { label: "Genius", value: "genius" }, - - { label: "Harem", value: "harem" }, - - { label: "Hero", value: "hero" }, - - { label: "Historical", value: "historical" }, - - { label: "Hunter", value: "hunter" }, - - { label: "korean novel", value: "korean-novel" }, - - { label: "Light Novel", value: "light-novel" }, - - { - label: "List Adventure Manga Genres", - value: "list-adventure-manga-genres", - }, - - { label: "Long Strip", value: "long-strip" }, - - { label: "Love comedy", value: "love-comedy" }, - - { label: "magic", value: "magic" }, - - { label: "Manhua", value: "manhua" }, - - { label: "Martial Arts", value: "martial-arts" }, - - { label: "Mature", value: "mature" }, - - { label: "Medieval", value: "medieval" }, - - { label: "Misunderstanding", value: "misunderstanding" }, - - { label: "Modern", value: "modern" }, - - { label: "modern fantasy", value: "modern-fantasy" }, - - { label: "music", value: "music" }, - - { label: "Mystery", value: "mystery" }, - - { label: "Necromancy", value: "necromancy" }, - - { label: "No Romance", value: "no-romance" }, - - { label: "NTL", value: "ntl" }, - - { label: "o", value: "o" }, - - { label: "Obsession", value: "obsession" }, - - { label: "Politics", value: "politics" }, - - { label: "Possession", value: "possession" }, - - { label: "Programming", value: "programming" }, - - { label: "Psychological", value: "psychological" }, - - { label: "Pure Love", value: "pure-love" }, - - { label: "Redemption", value: "redemption" }, - - { label: "Regression", value: "regression" }, - - { label: "Regret", value: "regret" }, - - { label: "Reincarnation", value: "reincarnation" }, - - { label: "Revenge", value: "revenge" }, - - { label: "Romance", value: "romance" }, - - { label: "Romance Fanrasy", value: "romance-fanrasy" }, - - { label: "Salvation", value: "salvation" }, - - { label: "School Life", value: "school-life" }, - - { label: "Sci-fi", value: "sci-fi" }, - - { label: "Science fiction", value: "science-fiction" }, - - { label: "Seinen", value: "seinen" }, - - { label: "Shounen", value: "shounen" }, - - { label: "Slice of Life", value: "slice-of-life" }, - - { label: "Soft yandere", value: "soft-yandere" }, - - { label: "Sports", value: "sports" }, - - { label: "Supernatural", value: "supernatural" }, - - { label: "Survival", value: "survival" }, - - { label: "system", value: "system" }, - - { label: "Time limit", value: "time-limit" }, - - { label: "Tragedy", value: "tragedy" }, - - { label: "Transmigration", value: "transmigration" }, - - { label: "TS", value: "ts" }, - - { label: "Tsundere", value: "tsundere" }, - - { label: "Unique", value: "unique" }, - - { label: "Wholesome", value: "wholesome" }, - - { label: "Wuxia", value: "wuxia" }, - - { label: "Xuanhuan", value: "xuanhuan" }, - - { label: "Yandere", value: "yandere" }, - - { label: "Yuri", value: "yuri" }, - ], - type: FilterTypes.CheckboxGroup, - }, - } satisfies Filters; -} - -const Noble = new NobleMTL(); - -export default new NobleMTL(); +import { load as parseHTML } from "cheerio"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { Plugin } from "@typings/plugin"; + +class NobleMTL implements Plugin.PluginBase { + id = "noblemtl"; + name = "NobleMTL"; + icon = "src/en/noblemtl/icon.png"; + site = "https://noblemtl.com/"; + version = "1.0.0"; + userAgent = ""; + async popularNovels( + pageNo: number, + { filters }: Plugin.PopularNovelsOptions + ): Promise { + let link = `${this.site}series/?page=${pageNo}`; + + if (filters.genres.value.length) + link += filters.genres.value.map((i) => `&genre[]=${i}`).join(""); + + if (filters.type.value.length) + link += filters.type.value.map((i) => `&lang[]=${i}`).join(""); + + link += "&status=" + filters.status.value; + + link += "&order=" + filters.order.value; + + const headers = new Headers(); + const body = await fetchApi(link, { headers }).then((result) => + result.text() + ); + + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + loadedCheerio("article.bs").each(function () { + const novelName = loadedCheerio(this).find(".ntitle").text().trim(); + let image = loadedCheerio(this).find("img"); + const novelCover = image.attr("data-src") || image.attr("src"); + + const novelUrl = loadedCheerio(this).find("a").attr("href"); + + if (!novelUrl) return; + + const novel = { + name: novelName, + cover: novelCover, + url: novelUrl, + }; + + novels.push(novel); + }); + + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const url = novelUrl; + const headers = new Headers(); + const result = await fetchApi(url, { headers }); + const body = await result.text(); + + let loadedCheerio = parseHTML(body); + + const novel: Plugin.SourceNovel = { + url, + chapters: [], + }; + + novel.name = loadedCheerio(".entry-title").text(); + + novel.cover = + loadedCheerio("img.wp-post-image").attr("data-src") || + loadedCheerio("img.wp-post-image").attr("src"); + + loadedCheerio("div.spe > span").each(function () { + const detailName = loadedCheerio(this).find("b").text().trim(); + const detail = loadedCheerio(this) + .find("b") + .remove() + .end() + .text() + .trim(); + + switch (detailName) { + case "Author:": + novel.author = detail; + break; + case "Status:": + novel.status = detail; + break; + } + }); + + novel.genres = loadedCheerio(".genxed") + .text() + .trim() + .replace(/\s/g, ","); + + loadedCheerio('div[itemprop="description"] h3,p.a,strong').remove(); + novel.summary = loadedCheerio('div[itemprop="description"]') + .find("br") + .replaceWith("\n") + .end() + .text(); + + let chapter: Plugin.ChapterItem[] = []; + + loadedCheerio(".eplister") + .find("li") + .each(function () { + const chapterName = + loadedCheerio(this).find(".epl-num").text() + + " - " + + loadedCheerio(this).find(".epl-title").text(); + + const releaseDate = loadedCheerio(this) + .find(".epl-date") + .text() + .trim(); + + const chapterUrl = loadedCheerio(this).find("a").attr("href"); + + if (!chapterUrl) return; + + chapter.push({ + name: chapterName, + releaseTime: releaseDate, + url: chapterUrl, + }); + }); + + novel.chapters = chapter.reverse(); + + return novel; + } + async parseChapter(chapterUrl: string): Promise { + const headers = new Headers(); + const result = await fetchApi(chapterUrl, { headers }); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + let chapterText = loadedCheerio(".epcontent").html() || ""; + + return chapterText; + } + + async searchNovels( + searchTerm: string, + pageNo?: number | undefined + ): Promise { + const url = `${this.site}?s=${searchTerm}`; + const headers = new Headers(); + const result = await fetchApi(url, { headers }); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + loadedCheerio("article.bs").each(function () { + const novelName = loadedCheerio(this).find(".ntitle").text().trim(); + const novelCover = loadedCheerio(this).find("img").attr("src"); + const novelUrl = loadedCheerio(this).find("a").attr("href"); + if (!novelUrl) return; + + novels.push({ + name: novelName, + url: novelUrl, + cover: novelCover, + }); + }); + + return novels; + } + async fetchImage(url: string): Promise { + return await fetchFile(url); + } + + filters = { + order: { + label: "Sort By", + type: FilterTypes.Picker, + options: [ + { label: "Default", value: "" }, + + { label: "A-Z", value: "title" }, + + { label: "Z-A", value: "titlereverse" }, + + { label: "Latest Update", value: "update" }, + + { label: "Latest Added", value: "latest" }, + + { label: "Popular", value: "popular" }, + ], + value: "", + }, + + status: { + label: "Status", + options: [ + { label: "All", value: "" }, + + { label: "Ongoing", value: "ongoing" }, + + { label: "Hiatus", value: "hiatus" }, + + { label: "Completed", value: "completed" }, + ], + type: FilterTypes.Picker, + value: "", + }, + type: { + label: "Type", + options: [ + { label: "Chinese novel", value: "chinese-novel" }, + + { label: "habyeol", value: "habyeol" }, + + { label: "korean novel", value: "korean-novel" }, + + { label: "Web Novel", value: "web-novel" }, + + { label: "삼심", value: "%ec%82%bc%ec%8b%ac" }, + + { label: "호곡", value: "%ed%98%b8%ea%b3%a1" }, + ], + type: FilterTypes.CheckboxGroup, + value: [], + }, + genres: { + label: "Genres", + value: [], + options: [ + { label: "A.I", value: "a.i" }, + + { label: "Academy", value: "academy" }, + + { label: "Action", value: "action" }, + + { label: "Adult", value: "adult" }, + + { label: "Adventure", value: "adventure" }, + + { label: "Alternative History", value: "alternative-history" }, + + { label: "Another World", value: "another-world" }, + + { label: "Apocalypse", value: "apocalypse" }, + + { label: "Bromance", value: "bromance" }, + + { label: "Comedy", value: "comedy" }, + + { label: "Dark fantasy", value: "dark-fantasy" }, + + { label: "Demons", value: "demons" }, + + { label: "Drama", value: "drama" }, + + { label: "Dystopia", value: "dystopia" }, + + { label: "Ecchi", value: "ecchi" }, + + { label: "Entertainment", value: "entertainment" }, + + { label: "Exhaustion", value: "exhaustion" }, + + { label: "Fanfiction", value: "fanfiction" }, + + { label: "fantasy", value: "fantasy" }, + + { label: "finance", value: "finance" }, + + { label: "Full color", value: "full-color" }, + + { label: "Game", value: "game" }, + + { label: "Gender Bender", value: "gender-bender" }, + + { label: "Genius", value: "genius" }, + + { label: "Harem", value: "harem" }, + + { label: "Hero", value: "hero" }, + + { label: "Historical", value: "historical" }, + + { label: "Hunter", value: "hunter" }, + + { label: "korean novel", value: "korean-novel" }, + + { label: "Light Novel", value: "light-novel" }, + + { + label: "List Adventure Manga Genres", + value: "list-adventure-manga-genres", + }, + + { label: "Long Strip", value: "long-strip" }, + + { label: "Love comedy", value: "love-comedy" }, + + { label: "magic", value: "magic" }, + + { label: "Manhua", value: "manhua" }, + + { label: "Martial Arts", value: "martial-arts" }, + + { label: "Mature", value: "mature" }, + + { label: "Medieval", value: "medieval" }, + + { label: "Misunderstanding", value: "misunderstanding" }, + + { label: "Modern", value: "modern" }, + + { label: "modern fantasy", value: "modern-fantasy" }, + + { label: "music", value: "music" }, + + { label: "Mystery", value: "mystery" }, + + { label: "Necromancy", value: "necromancy" }, + + { label: "No Romance", value: "no-romance" }, + + { label: "NTL", value: "ntl" }, + + { label: "o", value: "o" }, + + { label: "Obsession", value: "obsession" }, + + { label: "Politics", value: "politics" }, + + { label: "Possession", value: "possession" }, + + { label: "Programming", value: "programming" }, + + { label: "Psychological", value: "psychological" }, + + { label: "Pure Love", value: "pure-love" }, + + { label: "Redemption", value: "redemption" }, + + { label: "Regression", value: "regression" }, + + { label: "Regret", value: "regret" }, + + { label: "Reincarnation", value: "reincarnation" }, + + { label: "Revenge", value: "revenge" }, + + { label: "Romance", value: "romance" }, + + { label: "Romance Fanrasy", value: "romance-fanrasy" }, + + { label: "Salvation", value: "salvation" }, + + { label: "School Life", value: "school-life" }, + + { label: "Sci-fi", value: "sci-fi" }, + + { label: "Science fiction", value: "science-fiction" }, + + { label: "Seinen", value: "seinen" }, + + { label: "Shounen", value: "shounen" }, + + { label: "Slice of Life", value: "slice-of-life" }, + + { label: "Soft yandere", value: "soft-yandere" }, + + { label: "Sports", value: "sports" }, + + { label: "Supernatural", value: "supernatural" }, + + { label: "Survival", value: "survival" }, + + { label: "system", value: "system" }, + + { label: "Time limit", value: "time-limit" }, + + { label: "Tragedy", value: "tragedy" }, + + { label: "Transmigration", value: "transmigration" }, + + { label: "TS", value: "ts" }, + + { label: "Tsundere", value: "tsundere" }, + + { label: "Unique", value: "unique" }, + + { label: "Wholesome", value: "wholesome" }, + + { label: "Wuxia", value: "wuxia" }, + + { label: "Xuanhuan", value: "xuanhuan" }, + + { label: "Yandere", value: "yandere" }, + + { label: "Yuri", value: "yuri" }, + ], + type: FilterTypes.CheckboxGroup, + }, + } satisfies Filters; +} + +const Noble = new NobleMTL(); + +export default new NobleMTL(); diff --git a/plugins/english/pandaMTL.ts b/plugins/english/pandaMTL.ts index 43e1988..4a84c75 100644 --- a/plugins/english/pandaMTL.ts +++ b/plugins/english/pandaMTL.ts @@ -1,280 +1,267 @@ -import { load as parseHTML } from "cheerio"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { Plugin } from "@typings/plugin"; - -class PandaMTL implements Plugin.PluginBase { - id = "pandamtl"; - name = "PandaMTL"; - icon = "src/en/wordpress/icon.png"; - site = "https://pandamtl.com/"; - version = "1.0.0"; - userAgent = ""; - cookieString = ""; - async popularNovels( - pageNo: number, - { filters }: Plugin.PopularNovelsOptions - ): Promise { - let link = `${this.site}series/?page=${pageNo}`; - - if (filters.genres.value.length) { - link += filters.genres.value.map((i) => `&genre[]=${i}`).join(""); - } - - if (filters.type.value.length) - link += filters.type.value.map((i) => `&lang[]=${i}`).join(""); - - link += "&status=" + (filters?.status ? filters.status : ""); - - link += "&order=" + (filters?.order ? filters.order : "popular"); - - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const body = await fetchApi(link, { headers }).then((result) => - result.text() - ); - - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - - loadedCheerio("article.maindet").each(function () { - const novelName = loadedCheerio(this).find("h2").text(); - let image = loadedCheerio(this).find("img"); - const novelCover = image.attr("data-src") || image.attr("src"); - const novelUrl = loadedCheerio(this).find("h2 a").attr("href"); - - if (!novelUrl) return; - - const novel = { - name: novelName, - cover: novelCover, - url: novelUrl, - }; - - novels.push(novel); - }); - - return novels; - } - async parseNovelAndChapters(novelUrl: string): Promise { - const url = novelUrl; - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(url, { headers }); - const body = await result.text(); - - let loadedCheerio = parseHTML(body); - - const novel: Plugin.SourceNovel = { - url, - chapters: [], - }; - - novel.name = loadedCheerio("h1.entry-title").text(); - - novel.cover = - loadedCheerio("img.wp-post-image").attr("data-src") || - loadedCheerio("img.wp-post-image").attr("src"); - - loadedCheerio(".serl:nth-child(3) > span").each(function () { - const detailName = loadedCheerio(this).text().trim(); - const detail = loadedCheerio(this).next().text().trim(); - - switch (detailName) { - case "Author": - novel.author = detail; - break; - } - }); - - novel.status = loadedCheerio(".sertostat > span").attr("class"); - - novel.genres = loadedCheerio(".sertogenre") - .children("a") - .map((i, el) => loadedCheerio(el).text()) - .toArray() - .join(","); - - novel.summary = loadedCheerio(".sersys") - .find("br") - .replaceWith("\n") - .end() - .text(); - - let chapter: Plugin.ChapterItem[] = []; - - loadedCheerio(".eplister") - .find("li") - .each(function () { - const chapterName = - loadedCheerio(this).find(".epl-num").text() + - " - " + - loadedCheerio(this).find(".epl-title").text(); - - const releaseDate = loadedCheerio(this) - .find(".epl-date") - .text() - .trim(); - - const chapterUrl = loadedCheerio(this).find("a").attr("href"); - - if (!chapterUrl) return; - - chapter.push({ - name: chapterName, - releaseTime: releaseDate, - url: chapterUrl, - }); - }); - - novel.chapters = chapter.reverse(); - - return novel; - } - async parseChapter(chapterUrl: string): Promise { - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(chapterUrl, { headers }); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - let chapterText = loadedCheerio(".epcontent").html() || ""; - - return chapterText; - } - async searchNovels( - searchTerm: string, - pageNo?: number | undefined - ): Promise { - const url = `${this.site}?s=${searchTerm}`; - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(url, { headers }); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - - loadedCheerio("article.maindet").each(function () { - const novelName = loadedCheerio(this).find("h2").text(); - let image = loadedCheerio(this).find("img"); - const novelCover = image.attr("data-src") || image.attr("src"); - const novelUrl = loadedCheerio(this).find("h2 a").attr("href"); - - if (!novelUrl) return; - - novels.push({ - name: novelName, - url: novelUrl, - cover: novelCover, - }); - }); - - return novels; - } - async fetchImage(url: string): Promise { - return await fetchFile(url); - } - - filters = { - order: { - label: "Sort By", - value: "", - options: [ - { label: "Default", value: "" }, - - { label: "A-Z", value: "title" }, - - { label: "Z-A", value: "titlereverse" }, - - { label: "Latest Update", value: "update" }, - - { label: "Latest Added", value: "latest" }, - - { label: "Popular", value: "popular" }, - ], - type: FilterTypes.Picker, - }, - status: { - label: "Status", - value: "", - options: [ - { label: "All", value: "" }, - - { label: "Ongoing", value: "ongoing" }, - - { label: "Hiatus", value: "hiatus" }, - - { label: "Completed", value: "completed" }, - ], - type: FilterTypes.Picker, - }, - type: { - value: [], - label: "Type", - options: [ - { label: "Light Novel (KR)", value: "light-novel-kr" }, - - { label: "Web Novel", value: "web-novel" }, - ], - type: FilterTypes.CheckboxGroup, - }, - genres: { - label: "Genres", - value: [], - options: [ - { label: "Action", value: "action" }, - - { label: "Adult", value: "adult" }, - - { label: "Adventure", value: "adventure" }, - - { label: "Comedy", value: "comedy" }, - - { label: "Ecchi", value: "ecchi" }, - - { label: "Fantasy", value: "fantasy" }, - - { label: "Harem", value: "harem" }, - - { label: "Josei", value: "josei" }, - - { label: "Martial Arts", value: "martial-arts" }, - - { label: "Mature", value: "mature" }, - - { label: "Romance", value: "romance" }, - - { label: "School Life", value: "school-life" }, - - { label: "Sci-fi", value: "sci-fi" }, - - { label: "Seinen", value: "seinen" }, - - { label: "Slice of Life", value: "slice-of-life" }, - - { label: "Smut", value: "smut" }, - - { label: "Sports", value: "sports" }, - - { label: "Supernatural", value: "supernatural" }, - - { label: "Tragedy", value: "tragedy" }, - ], - type: FilterTypes.CheckboxGroup, - }, - } satisfies Filters; -} - -export default new PandaMTL(); +import { load as parseHTML } from "cheerio"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { Plugin } from "@typings/plugin"; + +class PandaMTL implements Plugin.PluginBase { + id = "pandamtl"; + name = "PandaMTL"; + icon = "src/en/wordpress/icon.png"; + site = "https://pandamtl.com/"; + version = "1.0.0"; + userAgent = ""; + async popularNovels( + pageNo: number, + { filters }: Plugin.PopularNovelsOptions + ): Promise { + let link = `${this.site}series/?page=${pageNo}`; + + if (filters.genres.value.length) { + link += filters.genres.value.map((i) => `&genre[]=${i}`).join(""); + } + + if (filters.type.value.length) + link += filters.type.value.map((i) => `&lang[]=${i}`).join(""); + + link += "&status=" + (filters?.status ? filters.status : ""); + + link += "&order=" + (filters?.order ? filters.order : "popular"); + + const headers = new Headers(); + const body = await fetchApi(link, { headers }).then((result) => + result.text() + ); + + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + loadedCheerio("article.maindet").each(function () { + const novelName = loadedCheerio(this).find("h2").text(); + let image = loadedCheerio(this).find("img"); + const novelCover = image.attr("data-src") || image.attr("src"); + const novelUrl = loadedCheerio(this).find("h2 a").attr("href"); + + if (!novelUrl) return; + + const novel = { + name: novelName, + cover: novelCover, + url: novelUrl, + }; + + novels.push(novel); + }); + + return novels; + } + async parseNovelAndChapters(novelUrl: string): Promise { + const url = novelUrl; + const headers = new Headers(); + const result = await fetchApi(url, { headers }); + const body = await result.text(); + + let loadedCheerio = parseHTML(body); + + const novel: Plugin.SourceNovel = { + url, + chapters: [], + }; + + novel.name = loadedCheerio("h1.entry-title").text(); + + novel.cover = + loadedCheerio("img.wp-post-image").attr("data-src") || + loadedCheerio("img.wp-post-image").attr("src"); + + loadedCheerio(".serl:nth-child(3) > span").each(function () { + const detailName = loadedCheerio(this).text().trim(); + const detail = loadedCheerio(this).next().text().trim(); + + switch (detailName) { + case "Author": + novel.author = detail; + break; + } + }); + + novel.status = loadedCheerio(".sertostat > span").attr("class"); + + novel.genres = loadedCheerio(".sertogenre") + .children("a") + .map((i, el) => loadedCheerio(el).text()) + .toArray() + .join(","); + + novel.summary = loadedCheerio(".sersys") + .find("br") + .replaceWith("\n") + .end() + .text(); + + let chapter: Plugin.ChapterItem[] = []; + + loadedCheerio(".eplister") + .find("li") + .each(function () { + const chapterName = + loadedCheerio(this).find(".epl-num").text() + + " - " + + loadedCheerio(this).find(".epl-title").text(); + + const releaseDate = loadedCheerio(this) + .find(".epl-date") + .text() + .trim(); + + const chapterUrl = loadedCheerio(this).find("a").attr("href"); + + if (!chapterUrl) return; + + chapter.push({ + name: chapterName, + releaseTime: releaseDate, + url: chapterUrl, + }); + }); + + novel.chapters = chapter.reverse(); + + return novel; + } + async parseChapter(chapterUrl: string): Promise { + const headers = new Headers(); + const result = await fetchApi(chapterUrl, { headers }); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + let chapterText = loadedCheerio(".epcontent").html() || ""; + + return chapterText; + } + async searchNovels( + searchTerm: string, + pageNo?: number | undefined + ): Promise { + const url = `${this.site}?s=${searchTerm}`; + const headers = new Headers(); + const result = await fetchApi(url, { headers }); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + loadedCheerio("article.maindet").each(function () { + const novelName = loadedCheerio(this).find("h2").text(); + let image = loadedCheerio(this).find("img"); + const novelCover = image.attr("data-src") || image.attr("src"); + const novelUrl = loadedCheerio(this).find("h2 a").attr("href"); + + if (!novelUrl) return; + + novels.push({ + name: novelName, + url: novelUrl, + cover: novelCover, + }); + }); + + return novels; + } + async fetchImage(url: string): Promise { + return await fetchFile(url); + } + + filters = { + order: { + label: "Sort By", + value: "", + options: [ + { label: "Default", value: "" }, + + { label: "A-Z", value: "title" }, + + { label: "Z-A", value: "titlereverse" }, + + { label: "Latest Update", value: "update" }, + + { label: "Latest Added", value: "latest" }, + + { label: "Popular", value: "popular" }, + ], + type: FilterTypes.Picker, + }, + status: { + label: "Status", + value: "", + options: [ + { label: "All", value: "" }, + + { label: "Ongoing", value: "ongoing" }, + + { label: "Hiatus", value: "hiatus" }, + + { label: "Completed", value: "completed" }, + ], + type: FilterTypes.Picker, + }, + type: { + value: [], + label: "Type", + options: [ + { label: "Light Novel (KR)", value: "light-novel-kr" }, + + { label: "Web Novel", value: "web-novel" }, + ], + type: FilterTypes.CheckboxGroup, + }, + genres: { + label: "Genres", + value: [], + options: [ + { label: "Action", value: "action" }, + + { label: "Adult", value: "adult" }, + + { label: "Adventure", value: "adventure" }, + + { label: "Comedy", value: "comedy" }, + + { label: "Ecchi", value: "ecchi" }, + + { label: "Fantasy", value: "fantasy" }, + + { label: "Harem", value: "harem" }, + + { label: "Josei", value: "josei" }, + + { label: "Martial Arts", value: "martial-arts" }, + + { label: "Mature", value: "mature" }, + + { label: "Romance", value: "romance" }, + + { label: "School Life", value: "school-life" }, + + { label: "Sci-fi", value: "sci-fi" }, + + { label: "Seinen", value: "seinen" }, + + { label: "Slice of Life", value: "slice-of-life" }, + + { label: "Smut", value: "smut" }, + + { label: "Sports", value: "sports" }, + + { label: "Supernatural", value: "supernatural" }, + + { label: "Tragedy", value: "tragedy" }, + ], + type: FilterTypes.CheckboxGroup, + }, + } satisfies Filters; +} + +export default new PandaMTL(); diff --git a/plugins/french/chireads.ts b/plugins/french/chireads.ts index 7e77776..3d44644 100644 --- a/plugins/french/chireads.ts +++ b/plugins/french/chireads.ts @@ -9,7 +9,7 @@ class ChireadsPlugin implements Plugin.PluginBase { site: string; version: string; userAgent: string; - cookieString: string; + constructor() { this.id = "chireads.com"; this.name = "Chireads"; @@ -17,7 +17,6 @@ class ChireadsPlugin implements Plugin.PluginBase { this.site = "https://chireads.com"; this.version = "1.0.0"; this.userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"; - this.cookieString = "''"; } parseNovels(loadedCheerio: CheerioAPI) { return []; diff --git a/plugins/french/noveldeglace.ts b/plugins/french/noveldeglace.ts index c480c08..56d44ae 100644 --- a/plugins/french/noveldeglace.ts +++ b/plugins/french/noveldeglace.ts @@ -10,7 +10,7 @@ class NovelDeGlacePlugin implements Plugin.PluginBase{ site: string; version: string; userAgent: string; - cookieString: string; + constructor() { this.id = "NDG.com"; this.name = "NovelDeGlace"; @@ -18,7 +18,6 @@ class NovelDeGlacePlugin implements Plugin.PluginBase{ this.site = "https://noveldeglace.com/"; this.version = "1.0.0"; this.userAgent = "''"; - this.cookieString = "''"; } async popularNovels(page: number): Promise { diff --git a/plugins/portuguese/lightnovelbrasil.ts b/plugins/portuguese/lightnovelbrasil.ts index dc50f5e..16823c7 100644 --- a/plugins/portuguese/lightnovelbrasil.ts +++ b/plugins/portuguese/lightnovelbrasil.ts @@ -1,426 +1,413 @@ -import { load as parseHTML } from "cheerio"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { Plugin } from "@typings/plugin"; - -class LightNovelBrasil implements Plugin.PluginBase { - id = "lightnovelbrasil"; - name = "Light Novel Brasil"; - icon = "multisrc/wpmangastream/icons/lightnovelbrasil.png"; - site = "https://lightnovelbrasil.com/"; - version = "1.0.0"; - userAgent = ""; - cookieString = ""; - async popularNovels( - pageNo: number, - { filters }: Plugin.PopularNovelsOptions - ): Promise { - let link = `${this.site}series/?page=${pageNo}`; - - if (filters.genres.value.length) - link += filters.genres.value.map((i) => `&genre[]=${i}`).join(""); - - if (filters.type.value.length) - link += filters.type.value.map((i) => `&lang[]=${i}`).join(""); - - link += "&status=" + (filters?.status ? filters.status : ""); - - link += "&order=" + (filters?.order ? filters.order : "popular"); - - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const body = await fetchApi(link, { headers }).then((result) => - result.text() - ); - - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - - loadedCheerio("article.bs").each(function () { - const novelName = loadedCheerio(this).find(".ntitle").text().trim(); - let image = loadedCheerio(this).find("img"); - const novelCover = image.attr("data-src") || image.attr("src"); - - const novelUrl = loadedCheerio(this).find("a").attr("href"); - - if (!novelUrl) return; - - const novel = { - name: novelName, - cover: novelCover, - url: novelUrl, - }; - - novels.push(novel); - }); - - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const url = novelUrl; - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(url, { headers }); - const body = await result.text(); - - let loadedCheerio = parseHTML(body); - - const novel: Plugin.SourceNovel = { - url, - chapters: [], - }; - - novel.name = loadedCheerio(".entry-title").text(); - - novel.cover = - loadedCheerio("img.wp-post-image").attr("data-src") || - loadedCheerio("img.wp-post-image").attr("src"); - - loadedCheerio("div.spe > span").each(function () { - const detailName = loadedCheerio(this).find("b").text().trim(); - const detail = loadedCheerio(this) - .find("b") - .remove() - .end() - .text() - .trim(); - - switch (detailName) { - case "Autor:": - case "Author:": - novel.author = detail; - break; - case "Status:": - novel.status = detail; - break; - } - }); - - novel.genres = loadedCheerio(".genxed") - .text() - .trim() - .replace(/\s/g, ","); - - loadedCheerio('div[itemprop="description"] h3,p.a,strong').remove(); - novel.summary = loadedCheerio('div[itemprop="description"]') - .find("br") - .replaceWith("\n") - .end() - .text(); - - let chapter: Plugin.ChapterItem[] = []; - - loadedCheerio(".eplister") - .find("li") - .each(function () { - const chapterName = - loadedCheerio(this).find(".epl-num").text() + - " - " + - loadedCheerio(this).find(".epl-title").text(); - - const releaseDate = loadedCheerio(this) - .find(".epl-date") - .text() - .trim(); - - const chapterUrl = loadedCheerio(this).find("a").attr("href"); - - if (!chapterUrl) return; - - chapter.push({ - name: chapterName, - releaseTime: releaseDate, - url: chapterUrl, - }); - }); - - novel.chapters = chapter.reverse(); - - return novel; - } - async parseChapter(chapterUrl: string): Promise { - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(chapterUrl, { headers }); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - let chapterText = loadedCheerio(".epcontent").html() || ""; - - return chapterText; - } - - async searchNovels( - searchTerm: string, - pageNo?: number | undefined - ): Promise { - const url = `${this.site}?s=${searchTerm}`; - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(url, { headers }); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - - loadedCheerio("article.bs").each(function () { - const novelName = loadedCheerio(this).find(".ntitle").text().trim(); - const novelCover = loadedCheerio(this).find("img").attr("src"); - const novelUrl = loadedCheerio(this).find("a").attr("href"); - if (!novelUrl) return; - - novels.push({ - name: novelName, - url: novelUrl, - cover: novelCover, - }); - }); - - return novels; - } - async fetchImage(url: string): Promise { - return await fetchFile(url); - } - - filters = { - order: { - label: "Sort By", - value: "", - options: [ - { label: "Default", value: "" }, - - { label: "A-Z", value: "title" }, - - { label: "Z-A", value: "titlereverse" }, - - { label: "Latest Update", value: "update" }, - - { label: "Latest Added", value: "latest" }, - - { label: "Popular", value: "popular" }, - ], - type: FilterTypes.Picker, - }, - status: { - label: "Status", - value: "", - options: [ - { label: "All", value: "" }, - - { label: "Ongoing", value: "ongoing" }, - - { label: "Hiatus", value: "hiatus" }, - - { label: "Completed", value: "completed" }, - ], - type: FilterTypes.Picker, - }, - type: { - label: "Type", - value: [], - options: [ - { label: "Chinese novel", value: "chinese-novel" }, - - { label: "habyeol", value: "habyeol" }, - - { label: "korean novel", value: "korean-novel" }, - - { label: "Web Novel", value: "web-novel" }, - - { label: "삼심", value: "%ec%82%bc%ec%8b%ac" }, - - { label: "호곡", value: "%ed%98%b8%ea%b3%a1" }, - ], - type: FilterTypes.CheckboxGroup, - }, - genres: { - value: [], - label: "Genres", - options: [ - { label: "A.I", value: "a.i" }, - - { label: "Academy", value: "academy" }, - - { label: "Action", value: "action" }, - - { label: "Adult", value: "adult" }, - - { label: "Adventure", value: "adventure" }, - - { label: "Alternative History", value: "alternative-history" }, - - { label: "Another World", value: "another-world" }, - - { label: "Apocalypse", value: "apocalypse" }, - - { label: "Bromance", value: "bromance" }, - - { label: "Comedy", value: "comedy" }, - - { label: "Dark fantasy", value: "dark-fantasy" }, - - { label: "Demons", value: "demons" }, - - { label: "Drama", value: "drama" }, - - { label: "Dystopia", value: "dystopia" }, - - { label: "Ecchi", value: "ecchi" }, - - { label: "Entertainment", value: "entertainment" }, - - { label: "Exhaustion", value: "exhaustion" }, - - { label: "Fanfiction", value: "fanfiction" }, - - { label: "fantasy", value: "fantasy" }, - - { label: "finance", value: "finance" }, - - { label: "Full color", value: "full-color" }, - - { label: "Game", value: "game" }, - - { label: "Gender Bender", value: "gender-bender" }, - - { label: "Genius", value: "genius" }, - - { label: "Harem", value: "harem" }, - - { label: "Hero", value: "hero" }, - - { label: "Historical", value: "historical" }, - - { label: "Hunter", value: "hunter" }, - - { label: "korean novel", value: "korean-novel" }, - - { label: "Light Novel", value: "light-novel" }, - - { - label: "List Adventure Manga Genres", - value: "list-adventure-manga-genres", - }, - - { label: "Long Strip", value: "long-strip" }, - - { label: "Love comedy", value: "love-comedy" }, - - { label: "magic", value: "magic" }, - - { label: "Manhua", value: "manhua" }, - - { label: "Martial Arts", value: "martial-arts" }, - - { label: "Mature", value: "mature" }, - - { label: "Medieval", value: "medieval" }, - - { label: "Misunderstanding", value: "misunderstanding" }, - - { label: "Modern", value: "modern" }, - - { label: "modern fantasy", value: "modern-fantasy" }, - - { label: "music", value: "music" }, - - { label: "Mystery", value: "mystery" }, - - { label: "Necromancy", value: "necromancy" }, - - { label: "No Romance", value: "no-romance" }, - - { label: "NTL", value: "ntl" }, - - { label: "o", value: "o" }, - - { label: "Obsession", value: "obsession" }, - - { label: "Politics", value: "politics" }, - - { label: "Possession", value: "possession" }, - - { label: "Programming", value: "programming" }, - - { label: "Psychological", value: "psychological" }, - - { label: "Pure Love", value: "pure-love" }, - - { label: "Redemption", value: "redemption" }, - - { label: "Regression", value: "regression" }, - - { label: "Regret", value: "regret" }, - - { label: "Reincarnation", value: "reincarnation" }, - - { label: "Revenge", value: "revenge" }, - - { label: "Romance", value: "romance" }, - - { label: "Romance Fanrasy", value: "romance-fanrasy" }, - - { label: "Salvation", value: "salvation" }, - - { label: "School Life", value: "school-life" }, - - { label: "Sci-fi", value: "sci-fi" }, - - { label: "Science fiction", value: "science-fiction" }, - - { label: "Seinen", value: "seinen" }, - - { label: "Shounen", value: "shounen" }, - - { label: "Slice of Life", value: "slice-of-life" }, - - { label: "Soft yandere", value: "soft-yandere" }, - - { label: "Sports", value: "sports" }, - - { label: "Supernatural", value: "supernatural" }, - - { label: "Survival", value: "survival" }, - - { label: "system", value: "system" }, - - { label: "Time limit", value: "time-limit" }, - - { label: "Tragedy", value: "tragedy" }, - - { label: "Transmigration", value: "transmigration" }, - - { label: "TS", value: "ts" }, - - { label: "Tsundere", value: "tsundere" }, - - { label: "Unique", value: "unique" }, - - { label: "Wholesome", value: "wholesome" }, - - { label: "Wuxia", value: "wuxia" }, - - { label: "Xuanhuan", value: "xuanhuan" }, - - { label: "Yandere", value: "yandere" }, - - { label: "Yuri", value: "yuri" }, - ], - type: FilterTypes.CheckboxGroup, - }, - } satisfies Filters; -} - -export default new LightNovelBrasil(); +import { load as parseHTML } from "cheerio"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { Plugin } from "@typings/plugin"; + +class LightNovelBrasil implements Plugin.PluginBase { + id = "lightnovelbrasil"; + name = "Light Novel Brasil"; + icon = "multisrc/wpmangastream/icons/lightnovelbrasil.png"; + site = "https://lightnovelbrasil.com/"; + version = "1.0.0"; + userAgent = ""; + async popularNovels( + pageNo: number, + { filters }: Plugin.PopularNovelsOptions + ): Promise { + let link = `${this.site}series/?page=${pageNo}`; + + if (filters.genres.value.length) + link += filters.genres.value.map((i) => `&genre[]=${i}`).join(""); + + if (filters.type.value.length) + link += filters.type.value.map((i) => `&lang[]=${i}`).join(""); + + link += "&status=" + (filters?.status ? filters.status : ""); + + link += "&order=" + (filters?.order ? filters.order : "popular"); + + const headers = new Headers(); + const body = await fetchApi(link, { headers }).then((result) => + result.text() + ); + + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + loadedCheerio("article.bs").each(function () { + const novelName = loadedCheerio(this).find(".ntitle").text().trim(); + let image = loadedCheerio(this).find("img"); + const novelCover = image.attr("data-src") || image.attr("src"); + + const novelUrl = loadedCheerio(this).find("a").attr("href"); + + if (!novelUrl) return; + + const novel = { + name: novelName, + cover: novelCover, + url: novelUrl, + }; + + novels.push(novel); + }); + + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const url = novelUrl; + const headers = new Headers(); + const result = await fetchApi(url, { headers }); + const body = await result.text(); + + let loadedCheerio = parseHTML(body); + + const novel: Plugin.SourceNovel = { + url, + chapters: [], + }; + + novel.name = loadedCheerio(".entry-title").text(); + + novel.cover = + loadedCheerio("img.wp-post-image").attr("data-src") || + loadedCheerio("img.wp-post-image").attr("src"); + + loadedCheerio("div.spe > span").each(function () { + const detailName = loadedCheerio(this).find("b").text().trim(); + const detail = loadedCheerio(this) + .find("b") + .remove() + .end() + .text() + .trim(); + + switch (detailName) { + case "Autor:": + case "Author:": + novel.author = detail; + break; + case "Status:": + novel.status = detail; + break; + } + }); + + novel.genres = loadedCheerio(".genxed") + .text() + .trim() + .replace(/\s/g, ","); + + loadedCheerio('div[itemprop="description"] h3,p.a,strong').remove(); + novel.summary = loadedCheerio('div[itemprop="description"]') + .find("br") + .replaceWith("\n") + .end() + .text(); + + let chapter: Plugin.ChapterItem[] = []; + + loadedCheerio(".eplister") + .find("li") + .each(function () { + const chapterName = + loadedCheerio(this).find(".epl-num").text() + + " - " + + loadedCheerio(this).find(".epl-title").text(); + + const releaseDate = loadedCheerio(this) + .find(".epl-date") + .text() + .trim(); + + const chapterUrl = loadedCheerio(this).find("a").attr("href"); + + if (!chapterUrl) return; + + chapter.push({ + name: chapterName, + releaseTime: releaseDate, + url: chapterUrl, + }); + }); + + novel.chapters = chapter.reverse(); + + return novel; + } + async parseChapter(chapterUrl: string): Promise { + const headers = new Headers(); + const result = await fetchApi(chapterUrl, { headers }); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + let chapterText = loadedCheerio(".epcontent").html() || ""; + + return chapterText; + } + + async searchNovels( + searchTerm: string, + pageNo?: number | undefined + ): Promise { + const url = `${this.site}?s=${searchTerm}`; + const headers = new Headers(); + const result = await fetchApi(url, { headers }); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + loadedCheerio("article.bs").each(function () { + const novelName = loadedCheerio(this).find(".ntitle").text().trim(); + const novelCover = loadedCheerio(this).find("img").attr("src"); + const novelUrl = loadedCheerio(this).find("a").attr("href"); + if (!novelUrl) return; + + novels.push({ + name: novelName, + url: novelUrl, + cover: novelCover, + }); + }); + + return novels; + } + async fetchImage(url: string): Promise { + return await fetchFile(url); + } + + filters = { + order: { + label: "Sort By", + value: "", + options: [ + { label: "Default", value: "" }, + + { label: "A-Z", value: "title" }, + + { label: "Z-A", value: "titlereverse" }, + + { label: "Latest Update", value: "update" }, + + { label: "Latest Added", value: "latest" }, + + { label: "Popular", value: "popular" }, + ], + type: FilterTypes.Picker, + }, + status: { + label: "Status", + value: "", + options: [ + { label: "All", value: "" }, + + { label: "Ongoing", value: "ongoing" }, + + { label: "Hiatus", value: "hiatus" }, + + { label: "Completed", value: "completed" }, + ], + type: FilterTypes.Picker, + }, + type: { + label: "Type", + value: [], + options: [ + { label: "Chinese novel", value: "chinese-novel" }, + + { label: "habyeol", value: "habyeol" }, + + { label: "korean novel", value: "korean-novel" }, + + { label: "Web Novel", value: "web-novel" }, + + { label: "삼심", value: "%ec%82%bc%ec%8b%ac" }, + + { label: "호곡", value: "%ed%98%b8%ea%b3%a1" }, + ], + type: FilterTypes.CheckboxGroup, + }, + genres: { + value: [], + label: "Genres", + options: [ + { label: "A.I", value: "a.i" }, + + { label: "Academy", value: "academy" }, + + { label: "Action", value: "action" }, + + { label: "Adult", value: "adult" }, + + { label: "Adventure", value: "adventure" }, + + { label: "Alternative History", value: "alternative-history" }, + + { label: "Another World", value: "another-world" }, + + { label: "Apocalypse", value: "apocalypse" }, + + { label: "Bromance", value: "bromance" }, + + { label: "Comedy", value: "comedy" }, + + { label: "Dark fantasy", value: "dark-fantasy" }, + + { label: "Demons", value: "demons" }, + + { label: "Drama", value: "drama" }, + + { label: "Dystopia", value: "dystopia" }, + + { label: "Ecchi", value: "ecchi" }, + + { label: "Entertainment", value: "entertainment" }, + + { label: "Exhaustion", value: "exhaustion" }, + + { label: "Fanfiction", value: "fanfiction" }, + + { label: "fantasy", value: "fantasy" }, + + { label: "finance", value: "finance" }, + + { label: "Full color", value: "full-color" }, + + { label: "Game", value: "game" }, + + { label: "Gender Bender", value: "gender-bender" }, + + { label: "Genius", value: "genius" }, + + { label: "Harem", value: "harem" }, + + { label: "Hero", value: "hero" }, + + { label: "Historical", value: "historical" }, + + { label: "Hunter", value: "hunter" }, + + { label: "korean novel", value: "korean-novel" }, + + { label: "Light Novel", value: "light-novel" }, + + { + label: "List Adventure Manga Genres", + value: "list-adventure-manga-genres", + }, + + { label: "Long Strip", value: "long-strip" }, + + { label: "Love comedy", value: "love-comedy" }, + + { label: "magic", value: "magic" }, + + { label: "Manhua", value: "manhua" }, + + { label: "Martial Arts", value: "martial-arts" }, + + { label: "Mature", value: "mature" }, + + { label: "Medieval", value: "medieval" }, + + { label: "Misunderstanding", value: "misunderstanding" }, + + { label: "Modern", value: "modern" }, + + { label: "modern fantasy", value: "modern-fantasy" }, + + { label: "music", value: "music" }, + + { label: "Mystery", value: "mystery" }, + + { label: "Necromancy", value: "necromancy" }, + + { label: "No Romance", value: "no-romance" }, + + { label: "NTL", value: "ntl" }, + + { label: "o", value: "o" }, + + { label: "Obsession", value: "obsession" }, + + { label: "Politics", value: "politics" }, + + { label: "Possession", value: "possession" }, + + { label: "Programming", value: "programming" }, + + { label: "Psychological", value: "psychological" }, + + { label: "Pure Love", value: "pure-love" }, + + { label: "Redemption", value: "redemption" }, + + { label: "Regression", value: "regression" }, + + { label: "Regret", value: "regret" }, + + { label: "Reincarnation", value: "reincarnation" }, + + { label: "Revenge", value: "revenge" }, + + { label: "Romance", value: "romance" }, + + { label: "Romance Fanrasy", value: "romance-fanrasy" }, + + { label: "Salvation", value: "salvation" }, + + { label: "School Life", value: "school-life" }, + + { label: "Sci-fi", value: "sci-fi" }, + + { label: "Science fiction", value: "science-fiction" }, + + { label: "Seinen", value: "seinen" }, + + { label: "Shounen", value: "shounen" }, + + { label: "Slice of Life", value: "slice-of-life" }, + + { label: "Soft yandere", value: "soft-yandere" }, + + { label: "Sports", value: "sports" }, + + { label: "Supernatural", value: "supernatural" }, + + { label: "Survival", value: "survival" }, + + { label: "system", value: "system" }, + + { label: "Time limit", value: "time-limit" }, + + { label: "Tragedy", value: "tragedy" }, + + { label: "Transmigration", value: "transmigration" }, + + { label: "TS", value: "ts" }, + + { label: "Tsundere", value: "tsundere" }, + + { label: "Unique", value: "unique" }, + + { label: "Wholesome", value: "wholesome" }, + + { label: "Wuxia", value: "wuxia" }, + + { label: "Xuanhuan", value: "xuanhuan" }, + + { label: "Yandere", value: "yandere" }, + + { label: "Yuri", value: "yuri" }, + ], + type: FilterTypes.CheckboxGroup, + }, + } satisfies Filters; +} + +export default new LightNovelBrasil(); diff --git a/plugins/russian/LitSpace.ts b/plugins/russian/LitSpace.ts index 14ed6b3..7f7fcb2 100644 --- a/plugins/russian/LitSpace.ts +++ b/plugins/russian/LitSpace.ts @@ -1,242 +1,241 @@ -import { Plugin } from "@typings/plugin"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { NovelStatus } from "@libs/novelStatus"; -import { load as parseHTML } from "cheerio"; - -class freedlit implements Plugin.PluginBase { - id = "freedlit.space"; - name = "LitSpace"; - site = "https://freedlit.space"; - version = "1.0.0"; - icon = "src/ru/freedlit/icon.png"; - userAgent = ""; - cookieString = ""; - - async popularNovels( - page: number, - { showLatestNovels, filters }: Plugin.PopularNovelsOptions, - ): Promise { - let url = this.site + "/get-books/all/list/" + page + "?sort="; - url += showLatestNovels ? "recent" : filters?.sort?.value || "popular"; - url += "&status=" + (filters?.status?.value || "all"); - url += "&access=" + (filters?.access?.value || "all"); - url += "&adult=" + (filters?.adult?.value || "hide"); - - if (filters?.genre?.value?.include?.length) { - url += filters.genre.value.include - .map(id => '&genres_included[]=' + id).join(""); - } - - if (filters?.genre?.value?.exclude?.length) { - url += filters.genre.value.exclude - .map(id => '&genres_excluded[]=' + id).join(""); - } - - const body = await fetchApi(url).then((res) => res.text()); - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - loadedCheerio("#bookListBlock > div > div").each(function () { - const name = loadedCheerio(this).find("div > h4 > a").text()?.trim(); - const cover = loadedCheerio(this) - .find("div > a > img") - .attr("src") - ?.trim(); - const url = loadedCheerio(this).find("div > h4 > a").attr("href")?.trim(); - if (!name || !url) return; - - novels.push({ name, cover, url }); - }); - - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const body = await fetchApi(novelUrl).then((res) => res.text()); - const loadedCheerio = parseHTML(body); - - const novel: Plugin.SourceNovel = { - url: novelUrl, - name: loadedCheerio(".book-info h4").text(), - cover: loadedCheerio(".book-cover > div > img").attr("src")?.trim(), - summary: loadedCheerio("#nav-home").text()?.trim(), - author: loadedCheerio(".book-info h5 > a").text(), - genres: loadedCheerio(".genre-list > a") - .map((index, element) => loadedCheerio(element).text()) - .get() - .join(","), - }; - - const chapters: Plugin.ChapterItem[] = []; - - loadedCheerio("a.chapter-line").each(function () { - const name = loadedCheerio(this).find("h6").text(); - const releaseTime = loadedCheerio(this).find('span[class="date"]').text(); - const url = loadedCheerio(this).attr("href"); - if (!name || !url) return; - - chapters.push({ name, releaseTime, url }); - }); - - novel.chapters = chapters; - return novel; - } - - async parseChapter(chapterUrl: string): Promise { - const body = await fetchApi(chapterUrl).then((res) => res.text()); - const loadedCheerio = parseHTML(body); - - loadedCheerio("div.mobile-block").remove(); - loadedCheerio("div.standart-block").remove(); - - const chapterText = loadedCheerio('div[class="chapter"]').html() || ""; - return chapterText; - } - - async searchNovels(searchTerm: string): Promise { - const url = `${this.site}/search?query=${searchTerm}&type=all`; - const body = await fetchApi(url).then((res) => res.text()); - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - loadedCheerio("#bookListBlock > div").each(function () { - const name = loadedCheerio(this).find("h4 > a").text()?.trim(); - const cover = loadedCheerio(this).find("a > img").attr("src")?.trim(); - const url = loadedCheerio(this).find("h4 > a").attr("href")?.trim(); - if (!name || !url) return; - - novels.push({ name, cover, url }); - }); - - return novels; - } - fetchImage = fetchFile; - - filters = { - sort: { - label: "Сортировка:", - value: "", - options: [ - { label: "По популярности", value: "popular" }, - { label: "По количеству комментариев", value: "comments" }, - { label: "По количеству лайков", value: "likes" }, - { label: "По новизне", value: "recent" }, - { label: "По просмотрам", value: "views" }, - ], - type: FilterTypes.Picker, - }, - genre: { - label: "Жанры:", - value: { include: [], exclude: [] }, - options: [ - { label: "Любой жанр", value: "all" }, - { label: "Альтернативная история", value: "alternative-history" }, - { label: "Антиутопия", value: "dystopia" }, - { label: "Бизнес-литература", value: "business-literature" }, - { label: "Боевая фантастика", value: "combat-fiction" }, - { label: "Боевик", value: "action" }, - { label: "Боевое фэнтези", value: "combat-fantasy" }, - { label: "Бояръ-Аниме", value: "boyar-anime" }, - { label: "Героическая фантастика", value: "heroic-fiction" }, - { label: "Героическое фэнтези", value: "heroic-fantasy" }, - { label: "Городское фэнтези", value: "urban-fantasy" }, - { label: "Гримдарк", value: "grimdark" }, - { label: "Детектив", value: "mystery" }, - { label: "Детская литература", value: "kids-literature" }, - { label: "Документальная проза", value: "biography" }, - { label: "Историческая проза", value: "historical-fiction" }, - { label: "Исторический детектив", value: "historical-mystery" }, - { label: "Исторический любовный роман", value: "historical-romantic-novel" }, - { label: "Историческое фэнтези", value: "historical-fantasy" }, - { label: "Киберпанк", value: "cyberpunk" }, - { label: "Космическая фантастика", value: "cosmic-fiction" }, - { label: "ЛитРПГ", value: "litrpg" }, - { label: "Лоу / Низкое фэнтези", value: "low-fantasy" }, - { label: "Любовное фэнтези", value: "romantic-fantasy" }, - { label: "Любовный роман", value: "romantic-novel" }, - { label: "Мистика", value: "mystic" }, - { label: "Мистический детектив", value: "mystic-detective" }, - { label: "Научная фантастика", value: "science-fiction" }, - { label: "Подростковая проза", value: "young-adult" }, - { label: "Политический роман", value: "political-romance" }, - { label: "Попаданцы", value: "accidental-travel" }, - { label: "Попаданцы в магические миры", value: "magic-worlds-travel" }, - { label: "Попаданцы во времени", value: "time-travel" }, - { label: "Порнотика", value: "pornotica" }, - { label: "Постапокалипсис", value: "post-apocalypse" }, - { label: "Поэзия", value: "poetry" }, - { label: "Приключения", value: "adventure" }, - { label: "Публицистика", value: "journalism" }, - { label: "Пьеса", value: "play" }, - { label: "Развитие личности", value: "how-to-book" }, - { label: "Разное", value: "other" }, - { label: "Реализм", value: "Realism" }, - { label: "РеалРПГ", value: "realrpg" }, - { label: "Репликация", value: "replication" }, - { label: "Романтическая эротика", value: "romantic-erotic-fiction" }, - { label: "Сказка", value: "fairy-tale" }, - { label: "Слэш", value: "slash" }, - { label: "Современная проза", value: "modern-prose" }, - { label: "Современный любовный роман", value: "modern-romantic-novel" }, - { label: "Социальная фантастика", value: "social-fiction" }, - { label: "Стимпанк", value: "steampunk" }, - { label: "Сценарий", value: "scenario" }, - { label: "Сюаньхуань", value: "xuanhuan" }, - { label: "Сянься", value: "xianxia" }, - { label: "Тёмное фэнтези", value: "dark-fantasy" }, - { label: "Триллер", value: "thriller" }, - { label: "Уся", value: "wuxia" }, - { label: "Фантастика", value: "fiction" }, - { label: "Фантастический детектив", value: "mystery-fiction" }, - { label: "Фанфик", value: "fan-fiction" }, - { label: "Фемслэш", value: "femslash" }, - { label: "Фэнтези", value: "fantasy" }, - { label: "Хоррор", value: "horror" }, - { label: "Шпионский детектив", value: "spy-crime" }, - { label: "Эпическое фэнтези", value: "epic-fantasy" }, - { label: "Эротика", value: "erotic-fiction" }, - { label: "Эротическая фантастика", value: "erotic-fiction" }, - { label: "Эротический фанфик", value: "erotic-fan-fiction" }, - { label: "Эротическое фэнтези", value: "erotic-fantasy" }, - { label: "Этническое фэнтези", value: "ethnic-fantasy" }, - { label: "Юмор", value: "humor" }, - { label: "Юмористическая фантастика", value: "humor-fiction" }, - { label: "Юмористическое фэнтези", value: "humor-fantasy" }, - { label: "RPS", value: "rps" }, - ], - type: FilterTypes.ExcludableCheckboxGroup, - }, - status: { - label: "Статус:", - value: "", - options: [ - { label: "Любой статус", value: "all" }, - { label: "В процессе", value: "in-process" }, - { label: "Завершено", value: "finished" }, - ], - type: FilterTypes.Picker, - }, - access: { - label: "Доступ:", - value: "", - options: [ - { label: "Любой доступ", value: "all" }, - { label: "Бесплатные", value: "free" }, - { label: "Платные", value: "paid" }, - ], - type: FilterTypes.Picker, - }, - adult: { - label: "Возрастные ограничения:", - value: "", - options: [ - { label: "Скрыть 18+", value: "hide" }, - { label: "Показать +18", value: "show" }, - ], - type: FilterTypes.Picker, - }, - } satisfies Filters; -} - -export default new freedlit(); +import { Plugin } from "@typings/plugin"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { NovelStatus } from "@libs/novelStatus"; +import { load as parseHTML } from "cheerio"; + +class freedlit implements Plugin.PluginBase { + id = "freedlit.space"; + name = "LitSpace"; + site = "https://freedlit.space"; + version = "1.0.0"; + icon = "src/ru/freedlit/icon.png"; + userAgent = ""; + + async popularNovels( + page: number, + { showLatestNovels, filters }: Plugin.PopularNovelsOptions, + ): Promise { + let url = this.site + "/get-books/all/list/" + page + "?sort="; + url += showLatestNovels ? "recent" : filters?.sort?.value || "popular"; + url += "&status=" + (filters?.status?.value || "all"); + url += "&access=" + (filters?.access?.value || "all"); + url += "&adult=" + (filters?.adult?.value || "hide"); + + if (filters?.genre?.value?.include?.length) { + url += filters.genre.value.include + .map(id => '&genres_included[]=' + id).join(""); + } + + if (filters?.genre?.value?.exclude?.length) { + url += filters.genre.value.exclude + .map(id => '&genres_excluded[]=' + id).join(""); + } + + const body = await fetchApi(url).then((res) => res.text()); + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + loadedCheerio("#bookListBlock > div > div").each(function () { + const name = loadedCheerio(this).find("div > h4 > a").text()?.trim(); + const cover = loadedCheerio(this) + .find("div > a > img") + .attr("src") + ?.trim(); + const url = loadedCheerio(this).find("div > h4 > a").attr("href")?.trim(); + if (!name || !url) return; + + novels.push({ name, cover, url }); + }); + + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const body = await fetchApi(novelUrl).then((res) => res.text()); + const loadedCheerio = parseHTML(body); + + const novel: Plugin.SourceNovel = { + url: novelUrl, + name: loadedCheerio(".book-info h4").text(), + cover: loadedCheerio(".book-cover > div > img").attr("src")?.trim(), + summary: loadedCheerio("#nav-home").text()?.trim(), + author: loadedCheerio(".book-info h5 > a").text(), + genres: loadedCheerio(".genre-list > a") + .map((index, element) => loadedCheerio(element).text()) + .get() + .join(","), + }; + + const chapters: Plugin.ChapterItem[] = []; + + loadedCheerio("a.chapter-line").each(function () { + const name = loadedCheerio(this).find("h6").text(); + const releaseTime = loadedCheerio(this).find('span[class="date"]').text(); + const url = loadedCheerio(this).attr("href"); + if (!name || !url) return; + + chapters.push({ name, releaseTime, url }); + }); + + novel.chapters = chapters; + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const body = await fetchApi(chapterUrl).then((res) => res.text()); + const loadedCheerio = parseHTML(body); + + loadedCheerio("div.mobile-block").remove(); + loadedCheerio("div.standart-block").remove(); + + const chapterText = loadedCheerio('div[class="chapter"]').html() || ""; + return chapterText; + } + + async searchNovels(searchTerm: string): Promise { + const url = `${this.site}/search?query=${searchTerm}&type=all`; + const body = await fetchApi(url).then((res) => res.text()); + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + loadedCheerio("#bookListBlock > div").each(function () { + const name = loadedCheerio(this).find("h4 > a").text()?.trim(); + const cover = loadedCheerio(this).find("a > img").attr("src")?.trim(); + const url = loadedCheerio(this).find("h4 > a").attr("href")?.trim(); + if (!name || !url) return; + + novels.push({ name, cover, url }); + }); + + return novels; + } + fetchImage = fetchFile; + + filters = { + sort: { + label: "Сортировка:", + value: "", + options: [ + { label: "По популярности", value: "popular" }, + { label: "По количеству комментариев", value: "comments" }, + { label: "По количеству лайков", value: "likes" }, + { label: "По новизне", value: "recent" }, + { label: "По просмотрам", value: "views" }, + ], + type: FilterTypes.Picker, + }, + genre: { + label: "Жанры:", + value: { include: [], exclude: [] }, + options: [ + { label: "Любой жанр", value: "all" }, + { label: "Альтернативная история", value: "alternative-history" }, + { label: "Антиутопия", value: "dystopia" }, + { label: "Бизнес-литература", value: "business-literature" }, + { label: "Боевая фантастика", value: "combat-fiction" }, + { label: "Боевик", value: "action" }, + { label: "Боевое фэнтези", value: "combat-fantasy" }, + { label: "Бояръ-Аниме", value: "boyar-anime" }, + { label: "Героическая фантастика", value: "heroic-fiction" }, + { label: "Героическое фэнтези", value: "heroic-fantasy" }, + { label: "Городское фэнтези", value: "urban-fantasy" }, + { label: "Гримдарк", value: "grimdark" }, + { label: "Детектив", value: "mystery" }, + { label: "Детская литература", value: "kids-literature" }, + { label: "Документальная проза", value: "biography" }, + { label: "Историческая проза", value: "historical-fiction" }, + { label: "Исторический детектив", value: "historical-mystery" }, + { label: "Исторический любовный роман", value: "historical-romantic-novel" }, + { label: "Историческое фэнтези", value: "historical-fantasy" }, + { label: "Киберпанк", value: "cyberpunk" }, + { label: "Космическая фантастика", value: "cosmic-fiction" }, + { label: "ЛитРПГ", value: "litrpg" }, + { label: "Лоу / Низкое фэнтези", value: "low-fantasy" }, + { label: "Любовное фэнтези", value: "romantic-fantasy" }, + { label: "Любовный роман", value: "romantic-novel" }, + { label: "Мистика", value: "mystic" }, + { label: "Мистический детектив", value: "mystic-detective" }, + { label: "Научная фантастика", value: "science-fiction" }, + { label: "Подростковая проза", value: "young-adult" }, + { label: "Политический роман", value: "political-romance" }, + { label: "Попаданцы", value: "accidental-travel" }, + { label: "Попаданцы в магические миры", value: "magic-worlds-travel" }, + { label: "Попаданцы во времени", value: "time-travel" }, + { label: "Порнотика", value: "pornotica" }, + { label: "Постапокалипсис", value: "post-apocalypse" }, + { label: "Поэзия", value: "poetry" }, + { label: "Приключения", value: "adventure" }, + { label: "Публицистика", value: "journalism" }, + { label: "Пьеса", value: "play" }, + { label: "Развитие личности", value: "how-to-book" }, + { label: "Разное", value: "other" }, + { label: "Реализм", value: "Realism" }, + { label: "РеалРПГ", value: "realrpg" }, + { label: "Репликация", value: "replication" }, + { label: "Романтическая эротика", value: "romantic-erotic-fiction" }, + { label: "Сказка", value: "fairy-tale" }, + { label: "Слэш", value: "slash" }, + { label: "Современная проза", value: "modern-prose" }, + { label: "Современный любовный роман", value: "modern-romantic-novel" }, + { label: "Социальная фантастика", value: "social-fiction" }, + { label: "Стимпанк", value: "steampunk" }, + { label: "Сценарий", value: "scenario" }, + { label: "Сюаньхуань", value: "xuanhuan" }, + { label: "Сянься", value: "xianxia" }, + { label: "Тёмное фэнтези", value: "dark-fantasy" }, + { label: "Триллер", value: "thriller" }, + { label: "Уся", value: "wuxia" }, + { label: "Фантастика", value: "fiction" }, + { label: "Фантастический детектив", value: "mystery-fiction" }, + { label: "Фанфик", value: "fan-fiction" }, + { label: "Фемслэш", value: "femslash" }, + { label: "Фэнтези", value: "fantasy" }, + { label: "Хоррор", value: "horror" }, + { label: "Шпионский детектив", value: "spy-crime" }, + { label: "Эпическое фэнтези", value: "epic-fantasy" }, + { label: "Эротика", value: "erotic-fiction" }, + { label: "Эротическая фантастика", value: "erotic-fiction" }, + { label: "Эротический фанфик", value: "erotic-fan-fiction" }, + { label: "Эротическое фэнтези", value: "erotic-fantasy" }, + { label: "Этническое фэнтези", value: "ethnic-fantasy" }, + { label: "Юмор", value: "humor" }, + { label: "Юмористическая фантастика", value: "humor-fiction" }, + { label: "Юмористическое фэнтези", value: "humor-fantasy" }, + { label: "RPS", value: "rps" }, + ], + type: FilterTypes.ExcludableCheckboxGroup, + }, + status: { + label: "Статус:", + value: "", + options: [ + { label: "Любой статус", value: "all" }, + { label: "В процессе", value: "in-process" }, + { label: "Завершено", value: "finished" }, + ], + type: FilterTypes.Picker, + }, + access: { + label: "Доступ:", + value: "", + options: [ + { label: "Любой доступ", value: "all" }, + { label: "Бесплатные", value: "free" }, + { label: "Платные", value: "paid" }, + ], + type: FilterTypes.Picker, + }, + adult: { + label: "Возрастные ограничения:", + value: "", + options: [ + { label: "Скрыть 18+", value: "hide" }, + { label: "Показать +18", value: "show" }, + ], + type: FilterTypes.Picker, + }, + } satisfies Filters; +} + +export default new freedlit(); diff --git a/plugins/russian/authortoday.ts b/plugins/russian/authortoday.ts index 5630442..a83517f 100644 --- a/plugins/russian/authortoday.ts +++ b/plugins/russian/authortoday.ts @@ -1,539 +1,538 @@ -import { Plugin } from "@typings/plugin"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { defaultCover } from "@libs/defaultCover"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { NovelStatus } from "@libs/novelStatus"; -import { load as parseHTML } from "cheerio"; -import dayjs from "dayjs"; - -const apiUrl = "https://api.author.today/"; -const token = "Bearer guest"; - -class AuthorToday implements Plugin.PluginBase { - id = "AT"; - name = "Автор Тудей"; - icon = "src/ru/authortoday/icon.png"; - site = "https://author.today"; - version = "1.0.0"; - userAgent = ""; - cookieString = ""; - - async popularNovels( - pageNo: number, - { showLatestNovels, filters }: Plugin.PopularNovelsOptions, - ): Promise { - let url = apiUrl + "v1/catalog/search?page=" + pageNo; - if (filters?.genre?.value) { - url += "&genre=" + filters.genre.value; - } - - url += - "&sorting=" + (showLatestNovels ? "recent" : filters?.sort?.value || "popular"); - - url += "&form=" + (filters?.form?.value || "any"); - url += "&state=" + (filters?.state?.value || "any"); - url += "&series=" + (filters?.series?.value || "any"); - url += "&access=" + (filters?.access?.value || "any"); - url += "&promo=" + (filters?.promo?.value || "hide"); - - const result = await fetchApi(url, { - headers: { - Authorization: token, - }, - }); - const json = (await result.json()) as response; - const novels: Plugin.NovelItem[] = []; - - if (json?.code === "NotFound") { - return novels; - } - - json?.searchResults?.forEach((novel) => - novels.push({ - name: novel.title, - cover: novel?.coverUrl - ? "https://cm.author.today/content/" + novel.coverUrl - : defaultCover, - url: this.site + "/work/" + novel.id, - }), - ); - - return novels; - } - async parseNovelAndChapters(novelUrl: string): Promise { - const workID = novelUrl.split("/")[4]; - const result = await fetchApi(`${apiUrl}v1/work/${workID}/details`, { - headers: { - Authorization: token, - }, - }); - - const json = (await result.json()) as responseBook; - const novel: Plugin.SourceNovel = { - url: novelUrl, - name: json.title, - cover: json?.coverUrl ? json.coverUrl.split("?")[0] : defaultCover, - author: - json?.originalAuthor || - json?.authorFIO || - json?.coAuthorFIO || - json?.secondCoAuthorFIO || - json?.translator || - "", - genres: json?.tags?.join(", "), - status: json?.isFinished ? NovelStatus.Completed : NovelStatus.Ongoing, - }; - - novel.summary = ""; - novel.summary += json?.annotation ? json.annotation + "\n" : ""; - novel.summary += json?.authorNotes - ? "Примечания автора:\n" + json.authorNotes - : ""; - // all chapters - const chaptersRaw = await fetchApi(`${apiUrl}v1/work/${workID}/content`, { - headers: { - Authorization: token, - }, - }); - - const chaptersJSON = (await chaptersRaw.json()) as ChaptersEntity[]; - const chapters: Plugin.ChapterItem[] = []; - chaptersJSON?.forEach((chapter, index) => { - if (chapter?.isAvailable && !chapter?.isDraft) { - chapters.push({ - name: chapter?.title || `Глава ${index + 1}`, - releaseTime: dayjs( - chapter?.publishTime || chapter.lastModificationTime, - ).format("LLL"), - url: `${apiUrl}v1/work/${workID}/chapter/${chapter.id}/text`, - }); - } - }); - - novel.chapters = chapters; - return novel; - } - - async parseChapter(chapterUrl: string): Promise { - const result = await fetchApi(chapterUrl, { - headers: { - Authorization: token, - }, - }); - const json = (await result.json()) as encryptedСhapter; - - if (json?.code) { - return json.code + "\n" + json?.message; - } - - const key = json.key.split("").reverse().join("") + "@_@"; - let text = ""; - - for (let i = 0; i < json.text.length; i++) { - text += String.fromCharCode( - json.text.charCodeAt(i) ^ key.charCodeAt(Math.floor(i % key.length)), - ); - } - - const loadedCheerio = parseHTML(text); - const baseUrl = this.site; - loadedCheerio("img").each(function () { - if (!loadedCheerio(this).attr("src")?.startsWith("http")) { - const src = loadedCheerio(this).attr("src"); - loadedCheerio(this).attr("src", baseUrl + src); - } - }); - const chapterText = loadedCheerio.html(); - return chapterText; - } - - async searchNovels( - searchTerm: string, - pageNo?: number | undefined, - ): Promise { - const baseUrl = this.site; - const url = baseUrl + "/search?category=works&q=" + searchTerm + "&page="; - const result = await fetchApi(url + (pageNo || 1)); - const body = await result.text(); - const loadedCheerio = parseHTML(body); - const novels: Plugin.NovelItem[] = []; - - loadedCheerio("div.book-row").each(function () { - const name = loadedCheerio(this) - .find('div[class="book-title"] a') - .text() - .trim(); - let cover = loadedCheerio(this).find("img").attr("src"); - const url = - baseUrl + - "/work/" + - loadedCheerio(this) - .find('div[class="book-title"] a') - .attr("href") - ?.split("/")[2]; - - if (cover) { - cover = cover.split("?")[0]; - } else { - cover = defaultCover; - } - - novels.push({ name, cover, url }); - }); - - return novels; - } - - fetchImage = fetchFile; - - filters = { - sort: { - label: "Сортировка", - value: "", - options: [ - { label: "По популярности", value: "popular" }, - { label: "По количеству лайков", value: "likes" }, - { label: "По комментариям", value: "comments" }, - { label: "По новизне", value: "recent" }, - { label: "По просмотрам", value: "views" }, - { label: "Набирающие популярность", value: "trending" }, - ], - type: FilterTypes.Picker, - }, - genre: { - label: "Жанры", - value: "", - options: [ - { label: "Альтернативная история", value: "sf-history" }, - { label: "Антиутопия", value: "dystopia" }, - { label: "Бизнес-литература", value: "biznes-literatura" }, - { label: "Боевая фантастика", value: "sf-action" }, - { label: "Боевик", value: "action" }, - { label: "Боевое фэнтези", value: "fantasy-action" }, - { label: "Бояръ-Аниме", value: "boyar-anime" }, - { label: "Героическая фантастика", value: "sf-heroic" }, - { label: "Героическое фэнтези", value: "heroic-fantasy" }, - { label: "Городское фэнтези", value: "urban-fantasy" }, - { label: "Детектив", value: "detective" }, - { label: "Детская литература", value: "detskaya-literatura" }, - { label: "Документальная проза", value: "non-fiction" }, - { label: "Историческая проза", value: "historical-fiction" }, - { label: "Исторические приключения", value: "historical-adventure" }, - { label: "Исторический детектив", value: "historical-mystery" }, - { label: "Исторический любовный роман", value: "historical-romance" }, - { label: "Историческое фэнтези", value: "historical-fantasy" }, - { label: "Киберпанк", value: "cyberpunk" }, - { label: "Короткий любовный роман", value: "short-romance" }, - { label: "Космическая фантастика", value: "sf-space" }, - { label: "ЛитРПГ", value: "litrpg" }, - { label: "Любовное фэнтези", value: "love-fantasy" }, - { label: "Любовные романы", value: "romance" }, - { label: "Мистика", value: "paranormal" }, - { label: "Назад в СССР", value: "back-to-ussr" }, - { label: "Научная фантастика", value: "science-fiction" }, - { label: "Подростковая проза", value: "teen-prose" }, - { label: "Политический роман", value: "political-fiction" }, - { label: "Попаданцы", value: "popadantsy" }, - { label: "Попаданцы в космос", value: "popadantsy-v-kosmos" }, - { label: "Попаданцы в магические миры", value: "popadantsy-v-magicheskie-miry" }, - { label: "Попаданцы во времени", value: "popadantsy-vo-vremeni" }, - { label: "Постапокалипсис", value: "postapocalyptic" }, - { label: "Поэзия", value: "poetry" }, - { label: "Приключения", value: "adventure" }, - { label: "Публицистика", value: "publicism" }, - { label: "Развитие личности", value: "razvitie-lichnosti" }, - { label: "Разное", value: "other" }, - { label: "РеалРПГ", value: "realrpg" }, - { label: "Романтическая эротика", value: "romantic-erotika" }, - { label: "Сказка", value: "fairy-tale" }, - { label: "Современная проза", value: "modern-prose" }, - { label: "Современный любовный роман", value: "contemporary-romance" }, - { label: "Социальная фантастика", value: "sf-social" }, - { label: "Стимпанк", value: "steampunk" }, - { label: "Темное фэнтези", value: "dark-fantasy" }, - { label: "Триллер", value: "thriller" }, - { label: "Ужасы", value: "horror" }, - { label: "Фантастика", value: "sci-fi" }, - { label: "Фантастический детектив", value: "detective-science-fiction" }, - { label: "Фанфик", value: "fanfiction" }, - { label: "Фэнтези", value: "fantasy" }, - { label: "Шпионский детектив", value: "spy-mystery" }, - { label: "Эпическое фэнтези", value: "epic-fantasy" }, - { label: "Эротика", value: "erotica" }, - { label: "Эротическая фантастика", value: "sf-erotika" }, - { label: "Эротический фанфик", value: "fanfiction-erotika" }, - { label: "Эротическое фэнтези", value: "fantasy-erotika" }, - { label: "Юмор", value: "humor" }, - { label: "Юмористическая фантастика", value: "sf-humor" }, - { label: "Юмористическое фэнтези", value: "ironical-fantasy" }, - ], - type: FilterTypes.Picker, - }, - form: { - label: "Форма произведения", - value: "", - options: [ - { label: "Любой", value: "any" }, - { label: "Перевод", value: "translation" }, - { label: "Повесть", value: "tale" }, - { label: "Рассказ", value: "story" }, - { label: "Роман", value: "novel" }, - { label: "Сборник поэзии", value: "poetry" }, - { label: "Сборник рассказов", value: "story-book" }, - ], - type: FilterTypes.Picker, - }, - state: { - label: "Статус произведения", - value: "", - options: [ - { label: "Любой статус", value: "any" }, - { label: "В процессе", value: "in-progress" }, - { label: "Завершено", value: "finished" }, - ], - type: FilterTypes.Picker, - }, - series: { - label: "Статус цикла", - value: "", - options: [ - { label: "Не важно", value: "any" }, - { label: "Вне цикла", value: "out" }, - { label: "Цикл завершен", value: "finished" }, - { label: "Цикл не завершен", value: "unfinished" }, - ], - type: FilterTypes.Picker, - }, - access: { - label: "Тип доступа", - value: "", - options: [ - { label: "Любой", value: "any" }, - { label: "Платный", value: "paid" }, - { label: "Бесплатный", value: "free" }, - ], - type: FilterTypes.Picker, - }, - promo: { - label: "Промо-фрагмент", - value: "", - options: [ - { label: "Скрывать", value: "hide" }, - { label: "Показывать", value: "show" }, - ], - type: FilterTypes.Picker, - }, - } satisfies Filters; -} - -export default new AuthorToday(); - -interface response { - searchResults?: SearchResultsEntity[] | null; - realTotalCount: number; - code?: string; - errorMessage?: null; - isLastPage: boolean; - format: Format; - duration: Duration; - isWorkMarkEnabled: boolean; -} -interface SearchResultsEntity { - id: number; - title: string; - annotation: string; - authorId: number; - authorFIO: string; - authorUserName: string; - originalAuthor?: null; - coAuthorId?: number | null; - coAuthorFIO?: string | null; - coAuthorUserName?: string | null; - coAuthorConfirmed: boolean; - seriesId: number; - seriesTitle?: string | null; - publishTime: string; - lastModificationTime: string; - finishTime?: string | null; - isDraft: boolean; - formEnum: string; - genreId: number; - firstSubGenreId: number; - secondSubGenreId?: number | null; - adultOnly: boolean; - tags?: (string | null)[] | null; - coverUrl: string; - viewCount: number; - commentCount: number; - reviewCount: number; - textLength: number; - likeCount: number; - showAuthor: boolean; - price?: number | null; - discount?: null; - discountStartDate?: null; - discountEndDate?: null; - status: string; - promoFragment: boolean; - isExclusive: boolean; - format: string; - marks?: null[] | null; - isLiked: boolean; - isWorkMarkEnabled: boolean; - workInLibraryState: string; -} -interface Format { - enumValue: string; - value: string; - title: string; - mobileTitle: string; -} -interface Duration { - value: string; - title: string; - mobileTitle: string; -} - -interface responseBook { - chapters?: ChaptersEntity[] | null; - allowDownloads: boolean; - downloadErrorCode: string; - downloadErrorMessage: string; - privacyDownloads: string; - collectionCount: number; - annotation: string; - authorNotes: string; - atRecommendation: boolean; - seriesWorkIds?: number[] | null; - seriesWorkNumber: number; - reviewCount: number; - tags?: string[] | null; - orderId?: null; - orderStatus: string; - orderStatusMessage?: null; - contests?: null[] | null; - galleryImages?: GalleryImagesEntity[] | null; - booktrailerVideoUrl?: null; - isExclusive: boolean; - freeChapterCount: number; - promoFragment: boolean; - recommendations?: RecommendationsEntity[] | null; - linkedWork?: null; - id: number; - title: string; - coverUrl: string; - lastModificationTime: string; - lastUpdateTime: string; - finishTime?: null; - isFinished: boolean; - textLength: number; - textLengthLastRead: number; - price: number; - discount?: null; - workForm: string; - status: string; - authorId: number; - authorFIO: string; - authorUserName: string; - originalAuthor?: null; - translator?: null; - reciter?: null; - coAuthorId?: null; - coAuthorFIO?: null; - coAuthorUserName?: null; - coAuthorConfirmed: boolean; - secondCoAuthorId?: null; - secondCoAuthorFIO?: null; - secondCoAuthorUserName?: null; - secondCoAuthorConfirmed: boolean; - isPurchased: boolean; - userLikeId?: null; - lastReadTime?: null; - lastChapterId?: null; - lastChapterProgress: number; - likeCount: number; - commentCount: number; - rewardCount: number; - rewardsEnabled: boolean; - inLibraryState: string; - addedToLibraryTime: string; - updateInLibraryTime: string; - privacyDisplay: string; - state: string; - isDraft: boolean; - enableRedLine: boolean; - enableTTS: boolean; - adultOnly: boolean; - seriesId: number; - seriesOrder: number; - seriesTitle: string; - afterword: string; - seriesNextWorkId?: null; - genreId: number; - firstSubGenreId: number; - secondSubGenreId: number; - format: string; - marks?: null; - purchaseTime?: null; - likeTime?: null; - markTime?: null; -} -interface ChaptersEntity { - id: number; - workId: number; - title: string; - isDraft: boolean; - sortOrder: number; - publishTime: string; - lastModificationTime: string; - textLength: number; - isAvailable: boolean; -} - -interface GalleryImagesEntity { - id: string; - caption?: null; - url: string; - height: number; - width: number; -} -interface RecommendationsEntity { - title: string; - coverUrl: string; - authorId: number; - authorFIO: string; - authorUserName: string; - originalAuthor?: null; - coAuthorId?: null; - coAuthorFIO?: null; - coAuthorUserName?: null; - coAuthorConfirmed: boolean; - secondCoAuthorId?: null; - secondCoAuthorFIO?: null; - secondCoAuthorUserName?: null; - secondCoAuthorConfirmed: boolean; - discount?: null; - price?: number | null; - lastModificationTime: string; - finishTime: string; - finished: boolean; - isExclusive: boolean; - format: string; - status: string; - state: string; - removalReason?: null; - removalReasonComment?: null; - id: number; -} - -interface encryptedСhapter { - id: number; - title: string; - isDraft: boolean; - publishTime: string; - lastModificationTime: string; - text: string; - key: string; - code?: string; - message?: string; -} +import { Plugin } from "@typings/plugin"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { defaultCover } from "@libs/defaultCover"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { NovelStatus } from "@libs/novelStatus"; +import { load as parseHTML } from "cheerio"; +import dayjs from "dayjs"; + +const apiUrl = "https://api.author.today/"; +const token = "Bearer guest"; + +class AuthorToday implements Plugin.PluginBase { + id = "AT"; + name = "Автор Тудей"; + icon = "src/ru/authortoday/icon.png"; + site = "https://author.today"; + version = "1.0.0"; + userAgent = ""; + + async popularNovels( + pageNo: number, + { showLatestNovels, filters }: Plugin.PopularNovelsOptions, + ): Promise { + let url = apiUrl + "v1/catalog/search?page=" + pageNo; + if (filters?.genre?.value) { + url += "&genre=" + filters.genre.value; + } + + url += + "&sorting=" + (showLatestNovels ? "recent" : filters?.sort?.value || "popular"); + + url += "&form=" + (filters?.form?.value || "any"); + url += "&state=" + (filters?.state?.value || "any"); + url += "&series=" + (filters?.series?.value || "any"); + url += "&access=" + (filters?.access?.value || "any"); + url += "&promo=" + (filters?.promo?.value || "hide"); + + const result = await fetchApi(url, { + headers: { + Authorization: token, + }, + }); + const json = (await result.json()) as response; + const novels: Plugin.NovelItem[] = []; + + if (json?.code === "NotFound") { + return novels; + } + + json?.searchResults?.forEach((novel) => + novels.push({ + name: novel.title, + cover: novel?.coverUrl + ? "https://cm.author.today/content/" + novel.coverUrl + : defaultCover, + url: this.site + "/work/" + novel.id, + }), + ); + + return novels; + } + async parseNovelAndChapters(novelUrl: string): Promise { + const workID = novelUrl.split("/")[4]; + const result = await fetchApi(`${apiUrl}v1/work/${workID}/details`, { + headers: { + Authorization: token, + }, + }); + + const json = (await result.json()) as responseBook; + const novel: Plugin.SourceNovel = { + url: novelUrl, + name: json.title, + cover: json?.coverUrl ? json.coverUrl.split("?")[0] : defaultCover, + author: + json?.originalAuthor || + json?.authorFIO || + json?.coAuthorFIO || + json?.secondCoAuthorFIO || + json?.translator || + "", + genres: json?.tags?.join(", "), + status: json?.isFinished ? NovelStatus.Completed : NovelStatus.Ongoing, + }; + + novel.summary = ""; + novel.summary += json?.annotation ? json.annotation + "\n" : ""; + novel.summary += json?.authorNotes + ? "Примечания автора:\n" + json.authorNotes + : ""; + // all chapters + const chaptersRaw = await fetchApi(`${apiUrl}v1/work/${workID}/content`, { + headers: { + Authorization: token, + }, + }); + + const chaptersJSON = (await chaptersRaw.json()) as ChaptersEntity[]; + const chapters: Plugin.ChapterItem[] = []; + chaptersJSON?.forEach((chapter, index) => { + if (chapter?.isAvailable && !chapter?.isDraft) { + chapters.push({ + name: chapter?.title || `Глава ${index + 1}`, + releaseTime: dayjs( + chapter?.publishTime || chapter.lastModificationTime, + ).format("LLL"), + url: `${apiUrl}v1/work/${workID}/chapter/${chapter.id}/text`, + }); + } + }); + + novel.chapters = chapters; + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const result = await fetchApi(chapterUrl, { + headers: { + Authorization: token, + }, + }); + const json = (await result.json()) as encryptedСhapter; + + if (json?.code) { + return json.code + "\n" + json?.message; + } + + const key = json.key.split("").reverse().join("") + "@_@"; + let text = ""; + + for (let i = 0; i < json.text.length; i++) { + text += String.fromCharCode( + json.text.charCodeAt(i) ^ key.charCodeAt(Math.floor(i % key.length)), + ); + } + + const loadedCheerio = parseHTML(text); + const baseUrl = this.site; + loadedCheerio("img").each(function () { + if (!loadedCheerio(this).attr("src")?.startsWith("http")) { + const src = loadedCheerio(this).attr("src"); + loadedCheerio(this).attr("src", baseUrl + src); + } + }); + const chapterText = loadedCheerio.html(); + return chapterText; + } + + async searchNovels( + searchTerm: string, + pageNo?: number | undefined, + ): Promise { + const baseUrl = this.site; + const url = baseUrl + "/search?category=works&q=" + searchTerm + "&page="; + const result = await fetchApi(url + (pageNo || 1)); + const body = await result.text(); + const loadedCheerio = parseHTML(body); + const novels: Plugin.NovelItem[] = []; + + loadedCheerio("div.book-row").each(function () { + const name = loadedCheerio(this) + .find('div[class="book-title"] a') + .text() + .trim(); + let cover = loadedCheerio(this).find("img").attr("src"); + const url = + baseUrl + + "/work/" + + loadedCheerio(this) + .find('div[class="book-title"] a') + .attr("href") + ?.split("/")[2]; + + if (cover) { + cover = cover.split("?")[0]; + } else { + cover = defaultCover; + } + + novels.push({ name, cover, url }); + }); + + return novels; + } + + fetchImage = fetchFile; + + filters = { + sort: { + label: "Сортировка", + value: "", + options: [ + { label: "По популярности", value: "popular" }, + { label: "По количеству лайков", value: "likes" }, + { label: "По комментариям", value: "comments" }, + { label: "По новизне", value: "recent" }, + { label: "По просмотрам", value: "views" }, + { label: "Набирающие популярность", value: "trending" }, + ], + type: FilterTypes.Picker, + }, + genre: { + label: "Жанры", + value: "", + options: [ + { label: "Альтернативная история", value: "sf-history" }, + { label: "Антиутопия", value: "dystopia" }, + { label: "Бизнес-литература", value: "biznes-literatura" }, + { label: "Боевая фантастика", value: "sf-action" }, + { label: "Боевик", value: "action" }, + { label: "Боевое фэнтези", value: "fantasy-action" }, + { label: "Бояръ-Аниме", value: "boyar-anime" }, + { label: "Героическая фантастика", value: "sf-heroic" }, + { label: "Героическое фэнтези", value: "heroic-fantasy" }, + { label: "Городское фэнтези", value: "urban-fantasy" }, + { label: "Детектив", value: "detective" }, + { label: "Детская литература", value: "detskaya-literatura" }, + { label: "Документальная проза", value: "non-fiction" }, + { label: "Историческая проза", value: "historical-fiction" }, + { label: "Исторические приключения", value: "historical-adventure" }, + { label: "Исторический детектив", value: "historical-mystery" }, + { label: "Исторический любовный роман", value: "historical-romance" }, + { label: "Историческое фэнтези", value: "historical-fantasy" }, + { label: "Киберпанк", value: "cyberpunk" }, + { label: "Короткий любовный роман", value: "short-romance" }, + { label: "Космическая фантастика", value: "sf-space" }, + { label: "ЛитРПГ", value: "litrpg" }, + { label: "Любовное фэнтези", value: "love-fantasy" }, + { label: "Любовные романы", value: "romance" }, + { label: "Мистика", value: "paranormal" }, + { label: "Назад в СССР", value: "back-to-ussr" }, + { label: "Научная фантастика", value: "science-fiction" }, + { label: "Подростковая проза", value: "teen-prose" }, + { label: "Политический роман", value: "political-fiction" }, + { label: "Попаданцы", value: "popadantsy" }, + { label: "Попаданцы в космос", value: "popadantsy-v-kosmos" }, + { label: "Попаданцы в магические миры", value: "popadantsy-v-magicheskie-miry" }, + { label: "Попаданцы во времени", value: "popadantsy-vo-vremeni" }, + { label: "Постапокалипсис", value: "postapocalyptic" }, + { label: "Поэзия", value: "poetry" }, + { label: "Приключения", value: "adventure" }, + { label: "Публицистика", value: "publicism" }, + { label: "Развитие личности", value: "razvitie-lichnosti" }, + { label: "Разное", value: "other" }, + { label: "РеалРПГ", value: "realrpg" }, + { label: "Романтическая эротика", value: "romantic-erotika" }, + { label: "Сказка", value: "fairy-tale" }, + { label: "Современная проза", value: "modern-prose" }, + { label: "Современный любовный роман", value: "contemporary-romance" }, + { label: "Социальная фантастика", value: "sf-social" }, + { label: "Стимпанк", value: "steampunk" }, + { label: "Темное фэнтези", value: "dark-fantasy" }, + { label: "Триллер", value: "thriller" }, + { label: "Ужасы", value: "horror" }, + { label: "Фантастика", value: "sci-fi" }, + { label: "Фантастический детектив", value: "detective-science-fiction" }, + { label: "Фанфик", value: "fanfiction" }, + { label: "Фэнтези", value: "fantasy" }, + { label: "Шпионский детектив", value: "spy-mystery" }, + { label: "Эпическое фэнтези", value: "epic-fantasy" }, + { label: "Эротика", value: "erotica" }, + { label: "Эротическая фантастика", value: "sf-erotika" }, + { label: "Эротический фанфик", value: "fanfiction-erotika" }, + { label: "Эротическое фэнтези", value: "fantasy-erotika" }, + { label: "Юмор", value: "humor" }, + { label: "Юмористическая фантастика", value: "sf-humor" }, + { label: "Юмористическое фэнтези", value: "ironical-fantasy" }, + ], + type: FilterTypes.Picker, + }, + form: { + label: "Форма произведения", + value: "", + options: [ + { label: "Любой", value: "any" }, + { label: "Перевод", value: "translation" }, + { label: "Повесть", value: "tale" }, + { label: "Рассказ", value: "story" }, + { label: "Роман", value: "novel" }, + { label: "Сборник поэзии", value: "poetry" }, + { label: "Сборник рассказов", value: "story-book" }, + ], + type: FilterTypes.Picker, + }, + state: { + label: "Статус произведения", + value: "", + options: [ + { label: "Любой статус", value: "any" }, + { label: "В процессе", value: "in-progress" }, + { label: "Завершено", value: "finished" }, + ], + type: FilterTypes.Picker, + }, + series: { + label: "Статус цикла", + value: "", + options: [ + { label: "Не важно", value: "any" }, + { label: "Вне цикла", value: "out" }, + { label: "Цикл завершен", value: "finished" }, + { label: "Цикл не завершен", value: "unfinished" }, + ], + type: FilterTypes.Picker, + }, + access: { + label: "Тип доступа", + value: "", + options: [ + { label: "Любой", value: "any" }, + { label: "Платный", value: "paid" }, + { label: "Бесплатный", value: "free" }, + ], + type: FilterTypes.Picker, + }, + promo: { + label: "Промо-фрагмент", + value: "", + options: [ + { label: "Скрывать", value: "hide" }, + { label: "Показывать", value: "show" }, + ], + type: FilterTypes.Picker, + }, + } satisfies Filters; +} + +export default new AuthorToday(); + +interface response { + searchResults?: SearchResultsEntity[] | null; + realTotalCount: number; + code?: string; + errorMessage?: null; + isLastPage: boolean; + format: Format; + duration: Duration; + isWorkMarkEnabled: boolean; +} +interface SearchResultsEntity { + id: number; + title: string; + annotation: string; + authorId: number; + authorFIO: string; + authorUserName: string; + originalAuthor?: null; + coAuthorId?: number | null; + coAuthorFIO?: string | null; + coAuthorUserName?: string | null; + coAuthorConfirmed: boolean; + seriesId: number; + seriesTitle?: string | null; + publishTime: string; + lastModificationTime: string; + finishTime?: string | null; + isDraft: boolean; + formEnum: string; + genreId: number; + firstSubGenreId: number; + secondSubGenreId?: number | null; + adultOnly: boolean; + tags?: (string | null)[] | null; + coverUrl: string; + viewCount: number; + commentCount: number; + reviewCount: number; + textLength: number; + likeCount: number; + showAuthor: boolean; + price?: number | null; + discount?: null; + discountStartDate?: null; + discountEndDate?: null; + status: string; + promoFragment: boolean; + isExclusive: boolean; + format: string; + marks?: null[] | null; + isLiked: boolean; + isWorkMarkEnabled: boolean; + workInLibraryState: string; +} +interface Format { + enumValue: string; + value: string; + title: string; + mobileTitle: string; +} +interface Duration { + value: string; + title: string; + mobileTitle: string; +} + +interface responseBook { + chapters?: ChaptersEntity[] | null; + allowDownloads: boolean; + downloadErrorCode: string; + downloadErrorMessage: string; + privacyDownloads: string; + collectionCount: number; + annotation: string; + authorNotes: string; + atRecommendation: boolean; + seriesWorkIds?: number[] | null; + seriesWorkNumber: number; + reviewCount: number; + tags?: string[] | null; + orderId?: null; + orderStatus: string; + orderStatusMessage?: null; + contests?: null[] | null; + galleryImages?: GalleryImagesEntity[] | null; + booktrailerVideoUrl?: null; + isExclusive: boolean; + freeChapterCount: number; + promoFragment: boolean; + recommendations?: RecommendationsEntity[] | null; + linkedWork?: null; + id: number; + title: string; + coverUrl: string; + lastModificationTime: string; + lastUpdateTime: string; + finishTime?: null; + isFinished: boolean; + textLength: number; + textLengthLastRead: number; + price: number; + discount?: null; + workForm: string; + status: string; + authorId: number; + authorFIO: string; + authorUserName: string; + originalAuthor?: null; + translator?: null; + reciter?: null; + coAuthorId?: null; + coAuthorFIO?: null; + coAuthorUserName?: null; + coAuthorConfirmed: boolean; + secondCoAuthorId?: null; + secondCoAuthorFIO?: null; + secondCoAuthorUserName?: null; + secondCoAuthorConfirmed: boolean; + isPurchased: boolean; + userLikeId?: null; + lastReadTime?: null; + lastChapterId?: null; + lastChapterProgress: number; + likeCount: number; + commentCount: number; + rewardCount: number; + rewardsEnabled: boolean; + inLibraryState: string; + addedToLibraryTime: string; + updateInLibraryTime: string; + privacyDisplay: string; + state: string; + isDraft: boolean; + enableRedLine: boolean; + enableTTS: boolean; + adultOnly: boolean; + seriesId: number; + seriesOrder: number; + seriesTitle: string; + afterword: string; + seriesNextWorkId?: null; + genreId: number; + firstSubGenreId: number; + secondSubGenreId: number; + format: string; + marks?: null; + purchaseTime?: null; + likeTime?: null; + markTime?: null; +} +interface ChaptersEntity { + id: number; + workId: number; + title: string; + isDraft: boolean; + sortOrder: number; + publishTime: string; + lastModificationTime: string; + textLength: number; + isAvailable: boolean; +} + +interface GalleryImagesEntity { + id: string; + caption?: null; + url: string; + height: number; + width: number; +} +interface RecommendationsEntity { + title: string; + coverUrl: string; + authorId: number; + authorFIO: string; + authorUserName: string; + originalAuthor?: null; + coAuthorId?: null; + coAuthorFIO?: null; + coAuthorUserName?: null; + coAuthorConfirmed: boolean; + secondCoAuthorId?: null; + secondCoAuthorFIO?: null; + secondCoAuthorUserName?: null; + secondCoAuthorConfirmed: boolean; + discount?: null; + price?: number | null; + lastModificationTime: string; + finishTime: string; + finished: boolean; + isExclusive: boolean; + format: string; + status: string; + state: string; + removalReason?: null; + removalReasonComment?: null; + id: number; +} + +interface encryptedСhapter { + id: number; + title: string; + isDraft: boolean; + publishTime: string; + lastModificationTime: string; + text: string; + key: string; + code?: string; + message?: string; +} diff --git a/plugins/russian/bookriver.ts b/plugins/russian/bookriver.ts index ddf5209..32a733e 100644 --- a/plugins/russian/bookriver.ts +++ b/plugins/russian/bookriver.ts @@ -1,259 +1,258 @@ -import { Plugin } from "@typings/plugin"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { NovelStatus } from "@libs/novelStatus"; -import { load as parseHTML } from "cheerio"; -import dayjs from "dayjs"; - -class Bookriver implements Plugin.PluginBase { - id = "bookriver"; - name = "Bookriver"; - site = "https://bookriver.ru"; - version = "1.0.0"; - icon = "src/ru/bookriver/icon.png"; - userAgent = ""; - cookieString = ""; - - async popularNovels( - pageNo: number, - { showLatestNovels, filters }: Plugin.PopularNovelsOptions, - ): Promise { - let url = this.site + `/genre?page=${pageNo}&perPage=24&sortingType=`; - url += showLatestNovels - ? "last-update" - : filters?.sort?.value || "bestseller"; - - if (filters?.genres?.value instanceof Array && filters?.genres?.value.length) { - url += "&g=" + filters.genres.value.join(","); - } - - const result = await fetchApi(url); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - const novels: Plugin.NovelItem[] = []; - - const jsonRaw = loadedCheerio("#__NEXT_DATA__").html(); - if (jsonRaw) { - const json: response = JSON.parse(jsonRaw); - json.props.pageProps.state.pagesFilter?.genre?.books?.forEach((novel) => - novels.push({ - name: novel.name, - cover: novel.coverImages[0].url, - url: this.site + "/book/" + novel.slug, - }), - ); - } - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const result = await fetchApi(novelUrl); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - const jsonRaw = loadedCheerio("#__NEXT_DATA__").html(); - const json: response = JSON.parse(jsonRaw || "{}"); - const book = json.props.pageProps.state.book?.bookPage; - const novel: Plugin.SourceNovel = { - url: novelUrl, - name: book?.name, - cover: book?.coverImages[0].url, - summary: book?.annotation, - author: book?.author?.name, - genres: book?.tags?.map((item) => item.name).join(", "), - status: - book?.statusComplete === "writing" - ? NovelStatus.Ongoing - : NovelStatus.Completed, - }; - - const chapters: Plugin.ChapterItem[] = []; - book?.ebook?.chapters?.forEach((chapter) => { - if (chapter.available) { - chapters.push({ - name: chapter.name, - releaseTime: dayjs( - chapter?.firstPublishedAt || chapter.createdAt, - ).format("LLL"), - url: this.site + "/reader/" + book?.slug + "/" + chapter.chapterId, - }); - } - }); - novel.chapters = chapters; - return novel; - } - - async parseChapter(chapterUrl: string): Promise { - const url = "https://api.bookriver.ru/api/v1/books/chapter/text/"; - const result = await fetchApi(url + chapterUrl.split("/").pop()); - const json = (await result.json()) as responseChapter; - - let chapterText = json.data.content || "Конец произведения"; - if (json.data?.audio?.available) { - chapterText += "\n" + json.data.audio.url; - } - - return chapterText; - } - - async searchNovels( - searchTerm: string, - pageNo: number | undefined = 1, - ): Promise { - const url = `https://api.bookriver.ru/api/v1/search/autocomplete?keyword=${searchTerm}&page=${pageNo}&perPage=10`; - const result = await fetchApi(url); - const json = (await result.json()) as responseSearch; - - const novels: Plugin.NovelItem[] = []; - json?.data?.books?.forEach((novel) => - novels.push({ - name: novel.name, - cover: novel.coverImages[0].url, - url: this.site + "/book/" + novel.slug, - }), - ); - - return novels; - } - fetchImage = fetchFile; - - filters = { - sort: { - label: "Сортировка", - value: "", - options: [ - { label: "Бестселлеры", value: "bestseller" }, - { label: "Дате добавления", value: "newest" }, - { label: "Дате обновления", value: "last-update" }, - ], - type: FilterTypes.Picker, - }, - genres: { - label: "жанры", - value: [], - options: [ - { label: "Альтернативная история", value: "alternativnaya-istoriya" }, - { label: "Боевая фантастика", value: "boevaya-fantastika" }, - { label: "Боевое фэнтези", value: "boevoe-fentezi" }, - { label: "Бытовое фэнтези", value: "bytovoe-fentezi" }, - { label: "Героическая фантастика", value: "geroicheskaya-fantastika" }, - { label: "Героическое фэнтези", value: "geroicheskoe-fentezi" }, - { label: "Городское фэнтези", value: "gorodskoe-fentezi" }, - { label: "Детектив", value: "detektiv" }, - { label: "Детективная фантастика", value: "detektivnaya-fantastika" }, - { label: "Жёсткая эротика", value: "zhyostkaya-erotika" }, - { label: "Исторический детектив", value: "istoricheskii-detektiv" }, - { label: "Исторический любовный роман", value: "istoricheskii-lyubovnyi-roman" }, - { label: "Историческое фэнтези", value: "istoricheskoe-fentezi" }, - { label: "Киберпанк", value: "kiberpank" }, - { label: "Классический детектив", value: "klassicheskii-detektiv" }, - { label: "Короткий любовный роман", value: "korotkii-lyubovnyi-roman" }, - { label: "Космическая фантастика", value: "kosmicheskaya-fantastika" }, - { label: "Криминальный детектив", value: "kriminalnyi-detektiv" }, - { label: "ЛитРПГ", value: "litrpg" }, - { label: "Любовная фантастика", value: "lyubovnaya-fantastika" }, - { label: "Любовное фэнтези", value: "lyubovnoe-fentezi" }, - { label: "Любовный роман", value: "lyubovnyi-roman" }, - { label: "Мистика", value: "mistika" }, - { label: "Молодежная проза", value: "molodezhnaya-proza" }, - { label: "Научная фантастика", value: "nauchnaya-fantastika" }, - { label: "Остросюжетный любовный роман", value: "ostrosyuzhetnyi-lyubovnyi-roman" }, - { label: "Политический детектив", value: "politicheskii-detektiv" }, - { label: "Попаданцы", value: "popadantsy" }, - { label: "Постапокалипсис", value: "postapokalipsis" }, - { label: "Приключенческое фэнтези", value: "priklyuchencheskoe-fentezi" }, - { label: "Романтическая эротика", value: "romanticheskaya-erotika" }, - { label: "С элементами эротики", value: "s-elementami-erotiki" }, - { label: "Славянское фэнтези", value: "slavyanskoe-fentezi" }, - { label: "Современный любовный роман", value: "sovremennyi-lyubovnyi-roman" }, - { label: "Социальная фантастика", value: "sotsialnaya-fantastika" }, - { label: "Тёмное фэнтези", value: "temnoe-fentezi" }, - { label: "Фантастика", value: "fantastika" }, - { label: "Фэнтези", value: "fentezi" }, - { label: "Шпионский детектив", value: "shpionskii-detektiv" }, - { label: "Эпическое фэнтези", value: "epicheskoe-fentezi" }, - { label: "Эротика", value: "erotika" }, - { label: "Эротическая фантастика", value: "eroticheskaya-fantastika" }, - { label: "Эротический фанфик", value: "eroticheskii-fanfik" }, - { label: "Эротическое фэнтези", value: "eroticheskoe-fentezi" }, - { label: "Юмористический детектив", value: "yumoristicheskii-detektiv" }, - { label: "Юмористическое фэнтези", value: "yumoristicheskoe-fentezi" }, - ], - type: FilterTypes.CheckboxGroup, - }, - } satisfies Filters; -} -export default new Bookriver(); - -interface response { - props: { - pageProps: { - state: { - book?: { - bookPage: BooksEntity; - }; - pagesFilter?: { - genre: { - books: BooksEntity[]; - }; - }; - }; - }; - }; -} - -interface BooksEntity { - name: string; - coverImages: { url: string }[]; - slug: string; - annotation?: string; - author?: { - name: string; - }; - tags?: { name: string }[]; - ebook?: { - chapters: Chapter[]; - }; - statusComplete: string; -} - -interface Chapter { - name: string; - available: boolean; - firstPublishedAt?: Date | null; - createdAt?: Date | null; - chapterId: number | string; -} - -interface responseChapter { - data: { - id: number; - bookId: number; - name: string; - content: string; - symbols: number; - number: number; - authorPages: number; - createdAt: string; - updatedAt: string; - firstPublishedAt: string; - status: string; - protected: boolean; - free: boolean; - audio?: { - available: boolean; - url: string; - duration: number; - }; - publicationScheduledFor?: null; - }; -} - -interface responseSearch { - data: Data; - total: number; -} -interface Data { - books?: BooksEntity[] | null; -} +import { Plugin } from "@typings/plugin"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { NovelStatus } from "@libs/novelStatus"; +import { load as parseHTML } from "cheerio"; +import dayjs from "dayjs"; + +class Bookriver implements Plugin.PluginBase { + id = "bookriver"; + name = "Bookriver"; + site = "https://bookriver.ru"; + version = "1.0.0"; + icon = "src/ru/bookriver/icon.png"; + userAgent = ""; + + async popularNovels( + pageNo: number, + { showLatestNovels, filters }: Plugin.PopularNovelsOptions, + ): Promise { + let url = this.site + `/genre?page=${pageNo}&perPage=24&sortingType=`; + url += showLatestNovels + ? "last-update" + : filters?.sort?.value || "bestseller"; + + if (filters?.genres?.value instanceof Array && filters?.genres?.value.length) { + url += "&g=" + filters.genres.value.join(","); + } + + const result = await fetchApi(url); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + const novels: Plugin.NovelItem[] = []; + + const jsonRaw = loadedCheerio("#__NEXT_DATA__").html(); + if (jsonRaw) { + const json: response = JSON.parse(jsonRaw); + json.props.pageProps.state.pagesFilter?.genre?.books?.forEach((novel) => + novels.push({ + name: novel.name, + cover: novel.coverImages[0].url, + url: this.site + "/book/" + novel.slug, + }), + ); + } + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const result = await fetchApi(novelUrl); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + const jsonRaw = loadedCheerio("#__NEXT_DATA__").html(); + const json: response = JSON.parse(jsonRaw || "{}"); + const book = json.props.pageProps.state.book?.bookPage; + const novel: Plugin.SourceNovel = { + url: novelUrl, + name: book?.name, + cover: book?.coverImages[0].url, + summary: book?.annotation, + author: book?.author?.name, + genres: book?.tags?.map((item) => item.name).join(", "), + status: + book?.statusComplete === "writing" + ? NovelStatus.Ongoing + : NovelStatus.Completed, + }; + + const chapters: Plugin.ChapterItem[] = []; + book?.ebook?.chapters?.forEach((chapter) => { + if (chapter.available) { + chapters.push({ + name: chapter.name, + releaseTime: dayjs( + chapter?.firstPublishedAt || chapter.createdAt, + ).format("LLL"), + url: this.site + "/reader/" + book?.slug + "/" + chapter.chapterId, + }); + } + }); + novel.chapters = chapters; + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const url = "https://api.bookriver.ru/api/v1/books/chapter/text/"; + const result = await fetchApi(url + chapterUrl.split("/").pop()); + const json = (await result.json()) as responseChapter; + + let chapterText = json.data.content || "Конец произведения"; + if (json.data?.audio?.available) { + chapterText += "\n" + json.data.audio.url; + } + + return chapterText; + } + + async searchNovels( + searchTerm: string, + pageNo: number | undefined = 1, + ): Promise { + const url = `https://api.bookriver.ru/api/v1/search/autocomplete?keyword=${searchTerm}&page=${pageNo}&perPage=10`; + const result = await fetchApi(url); + const json = (await result.json()) as responseSearch; + + const novels: Plugin.NovelItem[] = []; + json?.data?.books?.forEach((novel) => + novels.push({ + name: novel.name, + cover: novel.coverImages[0].url, + url: this.site + "/book/" + novel.slug, + }), + ); + + return novels; + } + fetchImage = fetchFile; + + filters = { + sort: { + label: "Сортировка", + value: "", + options: [ + { label: "Бестселлеры", value: "bestseller" }, + { label: "Дате добавления", value: "newest" }, + { label: "Дате обновления", value: "last-update" }, + ], + type: FilterTypes.Picker, + }, + genres: { + label: "жанры", + value: [], + options: [ + { label: "Альтернативная история", value: "alternativnaya-istoriya" }, + { label: "Боевая фантастика", value: "boevaya-fantastika" }, + { label: "Боевое фэнтези", value: "boevoe-fentezi" }, + { label: "Бытовое фэнтези", value: "bytovoe-fentezi" }, + { label: "Героическая фантастика", value: "geroicheskaya-fantastika" }, + { label: "Героическое фэнтези", value: "geroicheskoe-fentezi" }, + { label: "Городское фэнтези", value: "gorodskoe-fentezi" }, + { label: "Детектив", value: "detektiv" }, + { label: "Детективная фантастика", value: "detektivnaya-fantastika" }, + { label: "Жёсткая эротика", value: "zhyostkaya-erotika" }, + { label: "Исторический детектив", value: "istoricheskii-detektiv" }, + { label: "Исторический любовный роман", value: "istoricheskii-lyubovnyi-roman" }, + { label: "Историческое фэнтези", value: "istoricheskoe-fentezi" }, + { label: "Киберпанк", value: "kiberpank" }, + { label: "Классический детектив", value: "klassicheskii-detektiv" }, + { label: "Короткий любовный роман", value: "korotkii-lyubovnyi-roman" }, + { label: "Космическая фантастика", value: "kosmicheskaya-fantastika" }, + { label: "Криминальный детектив", value: "kriminalnyi-detektiv" }, + { label: "ЛитРПГ", value: "litrpg" }, + { label: "Любовная фантастика", value: "lyubovnaya-fantastika" }, + { label: "Любовное фэнтези", value: "lyubovnoe-fentezi" }, + { label: "Любовный роман", value: "lyubovnyi-roman" }, + { label: "Мистика", value: "mistika" }, + { label: "Молодежная проза", value: "molodezhnaya-proza" }, + { label: "Научная фантастика", value: "nauchnaya-fantastika" }, + { label: "Остросюжетный любовный роман", value: "ostrosyuzhetnyi-lyubovnyi-roman" }, + { label: "Политический детектив", value: "politicheskii-detektiv" }, + { label: "Попаданцы", value: "popadantsy" }, + { label: "Постапокалипсис", value: "postapokalipsis" }, + { label: "Приключенческое фэнтези", value: "priklyuchencheskoe-fentezi" }, + { label: "Романтическая эротика", value: "romanticheskaya-erotika" }, + { label: "С элементами эротики", value: "s-elementami-erotiki" }, + { label: "Славянское фэнтези", value: "slavyanskoe-fentezi" }, + { label: "Современный любовный роман", value: "sovremennyi-lyubovnyi-roman" }, + { label: "Социальная фантастика", value: "sotsialnaya-fantastika" }, + { label: "Тёмное фэнтези", value: "temnoe-fentezi" }, + { label: "Фантастика", value: "fantastika" }, + { label: "Фэнтези", value: "fentezi" }, + { label: "Шпионский детектив", value: "shpionskii-detektiv" }, + { label: "Эпическое фэнтези", value: "epicheskoe-fentezi" }, + { label: "Эротика", value: "erotika" }, + { label: "Эротическая фантастика", value: "eroticheskaya-fantastika" }, + { label: "Эротический фанфик", value: "eroticheskii-fanfik" }, + { label: "Эротическое фэнтези", value: "eroticheskoe-fentezi" }, + { label: "Юмористический детектив", value: "yumoristicheskii-detektiv" }, + { label: "Юмористическое фэнтези", value: "yumoristicheskoe-fentezi" }, + ], + type: FilterTypes.CheckboxGroup, + }, + } satisfies Filters; +} +export default new Bookriver(); + +interface response { + props: { + pageProps: { + state: { + book?: { + bookPage: BooksEntity; + }; + pagesFilter?: { + genre: { + books: BooksEntity[]; + }; + }; + }; + }; + }; +} + +interface BooksEntity { + name: string; + coverImages: { url: string }[]; + slug: string; + annotation?: string; + author?: { + name: string; + }; + tags?: { name: string }[]; + ebook?: { + chapters: Chapter[]; + }; + statusComplete: string; +} + +interface Chapter { + name: string; + available: boolean; + firstPublishedAt?: Date | null; + createdAt?: Date | null; + chapterId: number | string; +} + +interface responseChapter { + data: { + id: number; + bookId: number; + name: string; + content: string; + symbols: number; + number: number; + authorPages: number; + createdAt: string; + updatedAt: string; + firstPublishedAt: string; + status: string; + protected: boolean; + free: boolean; + audio?: { + available: boolean; + url: string; + duration: number; + }; + publicationScheduledFor?: null; + }; +} + +interface responseSearch { + data: Data; + total: number; +} +interface Data { + books?: BooksEntity[] | null; +} diff --git a/plugins/russian/ficbook.ts b/plugins/russian/ficbook.ts index 3056e41..ab0fdd6 100644 --- a/plugins/russian/ficbook.ts +++ b/plugins/russian/ficbook.ts @@ -1,2644 +1,2643 @@ -import { Plugin } from "@typings/plugin"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { NovelStatus } from "@libs/novelStatus"; -import { load as parseHTML } from "cheerio"; -import { defaultCover } from "@libs/defaultCover"; - -class ficbook implements Plugin.PluginBase { - id = "ficbook"; - name = "ficbook"; - site = "https://ficbook.net"; - version = "1.0.0"; - icon = "src/ru/ficbook/icon.png"; - userAgent = ""; - cookieString = ""; - - async popularNovels( - pageNo: number, - { showLatestNovels, filters }: Plugin.PopularNovelsOptions, - ): Promise { - const baseUrl = this.site; - let url = baseUrl; - - if (filters?.directions?.value) { - url += "/popular-fanfics/" + filters.directions.value; - } else if (filters?.tags?.value) { - url += "/tags/" + filters.tags.value + "?p=" + pageNo; - } else { - url += "/" + (filters?.sort?.value || "fanfiction") + "?p=" + pageNo; - } - - const result = await fetchApi(url); - const body = await result.text(); - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - - loadedCheerio("article.fanfic-inline").each(function () { - const name = loadedCheerio(this).find("h3 > a").text().trim(); - let cover = loadedCheerio(this).find("picture > img").attr("src"); - const url = loadedCheerio(this).find("h3 > a").attr("href"); - - cover = cover - ? cover.replace(/covers\/m_|covers\/d_/g, "covers/") - : defaultCover; - if (!name || !url) return; - - novels.push({ name, cover, url: baseUrl + url.replace(/\?.*/g, "") }); - }); - - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const result = await fetchApi(novelUrl); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - const novel: Plugin.SourceNovel = { - url: novelUrl, - }; - - novel.name = ( - loadedCheerio('h1[itemprop="headline"]').text() || - loadedCheerio('h1[itemprop="name"]').text() - ).trim(); - - novel.cover = loadedCheerio('meta[property="og:image"]').attr("content"); - novel.summary = loadedCheerio('div[itemprop="description"]').text().trim(); - novel.author = loadedCheerio('a[itemprop="author"]').text(); - - novel.status = - loadedCheerio( - "div.fanfic-main-info > section:nth-child(3) > div:nth-child(3) > span:nth-child(2)", - ).text() === "В процессе" - ? NovelStatus.Ongoing - : NovelStatus.Completed; - - const tags = loadedCheerio('div[class="tags"] > a') - .map((index, element) => loadedCheerio(element).text()) - .get(); - if (tags) { - novel.genres = tags.join(","); - } - - if (!novel.cover || novel.cover?.includes("/design/")) { - novel.cover = defaultCover; - } else { - novel.cover = novel.cover.replace(/covers\/m_|covers\/d_/g, "covers/"); - } - - const chapters: Plugin.ChapterItem[] = []; - const baseUrl = this.site; - - if (loadedCheerio("#content").length == 1) { - const name = loadedCheerio(".title-area > h2").text(); - const releaseTime = loadedCheerio(".part-date > span").attr("title"); - - if (name) chapters.push({ name, releaseTime, url: novelUrl }); - } else { - loadedCheerio("li.part").each(function () { - const name = loadedCheerio(this).find("h3").text(); - const releaseTime = loadedCheerio(this) - .find("div > span") - .attr("title"); - const url = loadedCheerio(this).find("a:nth-child(1)").attr("href"); - if (!name || !url) return; - - chapters.push({ name, releaseTime, url: baseUrl + url }); - }); - } - - novel.chapters = chapters; - return novel; - } - - async parseChapter(chapterUrl: string): Promise { - const result = await fetchApi(chapterUrl); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - let chapterText = ""; - - loadedCheerio("#content") - .html() - ?.split("\n") - ?.forEach((line) => { - if (line.trim()) { - //blank line skip - if (line.includes("

")) { - //new paragraph - chapterText += line; - } else { - //plaintext - chapterText += "

" + line + "

"; - } - } - }); - - return chapterText; - } - - async searchNovels( - searchTerm: string, - pageNo: number | undefined = 1, - ): Promise { - const formData = new FormData(); - formData.append("term", searchTerm); - formData.append("page", pageNo.toString()); - - const result = await fetchApi(this.site + "/search/fanfic", { - method: "POST", - body: formData, - }); - const json = (await result.json()) as { data: Data }; - const novels: Plugin.NovelItem[] = []; - - json.data?.data?.forEach((novel) => { - const name = novel.title.trim(); - const url = this.site + "/readfic/" + novel.slug; - const cover = novel.cover - ? "https://images.ficbook.net/fanfic-covers/" + novel.cover - : defaultCover; - - novels.push({ name, cover, url }); - }); - - return novels; - } - fetchImage = fetchFile; - - filters = { - sort: { - label: "Сортировка:", - value: "", - options: [ - { label: "Горячие работы", value: "fanfiction" }, - { label: "Популярные ", value: "popular-fanfics" }, - ], - type: FilterTypes.Picker, - }, - directions: { - label: "Направление:", - value: "", - options: [ - { label: "Все", value: "" }, - { label: "Джен", value: "gen" }, - { label: "Гет", value: "het" }, - { label: "Слэш", value: "slash-fics-3712917" }, - { label: "Фемслэш", value: "femslash-fanfics-9374932" }, - { label: "Статьи", value: "article" }, - { label: "Смешанный", value: "mixed" }, - { label: "Другой", value: "other" }, - ], - type: FilterTypes.Picker, - }, - tags: { - label: "Тэги:", - value: "", - options: [ - { label: "Все", value: "" }, - { label: "#1fic1week", value: "884" }, - { label: "#7daystowrite", value: "2961" }, - { label: "#Бинго_ТФ", value: "1458" }, - { label: "#миниатюры3days", value: "2118" }, - { label: "#Не_Тайный_Санта", value: "2760" }, - { label: "#Писательские фанты-блиц", value: "2613" }, - { label: "#cosmowrite", value: "2284" }, - { label: "#femKHRweek", value: "2658" }, - { label: "#FourShips", value: "2588" }, - { label: "#Goretober", value: "1861" }, - { label: "#InternationalVampireDay / #WorldDraculaDay", value: "2048" }, - { label: "#KattWeek", value: "2717" }, - { label: "#Kinktober", value: "1867" }, - { label: "#LotorWeek", value: "2657" }, - { label: "#MerMay", value: "1122" }, - { label: "#NaNoWriMo", value: "1852" }, - { label: "#nsfweruriweek", value: "2944" }, - { label: "#RCMiaChallenge", value: "2700" }, - { label: "#SheithMonth", value: "2432" }, - { label: "#SixDrabbles", value: "2293" }, - { label: "#Writober", value: "1831" }, - { label: "1900-е годы", value: "369" }, - { label: "1910-е годы", value: "213" }, - { label: "1920-е годы", value: "81" }, - { label: "1930-е годы", value: "82" }, - { label: "1940-е годы", value: "83" }, - { label: "1950-е годы", value: "84" }, - { label: "1960-е годы", value: "85" }, - { label: "1970-е годы", value: "86" }, - { label: "1980-е годы", value: "87" }, - { label: "1990-е годы", value: "88" }, - { label: "2000-е годы", value: "590" }, - { label: "2010-е годы", value: "2562" }, - { label: "2014 год", value: "2977" }, - { label: "2P", value: "1910" }, - { label: "9 мая", value: "1019" }, - { label: "Аборт / Выкидыш", value: "306" }, - { label: "Аватары", value: "2401" }, - { label: "Авиатранспорт", value: "962" }, - { label: "Авиация", value: "455" }, - { label: "Аврорат", value: "1906" }, - { label: "Австралия", value: "767" }, - { label: "Австрия", value: "1024" }, - { label: "Автомастерские", value: "788" }, - { label: "Авторская пунктуация", value: "2886" }, - { label: "Авторские неологизмы", value: "1123" }, - { label: "Автоспорт", value: "856" }, - { label: "Автостоп", value: "1429" }, - { label: "Аддикции", value: "1230" }, - { label: "Адреналиновая зависимость", value: "1480" }, - { label: "Азартные игры", value: "1009" }, - { label: "Азкабан", value: "1922" }, - { label: "Аквакинез", value: "1380" }, - { label: "Аквапарки", value: "789" }, - { label: "Акростих", value: "911" }, - { label: "Актеры", value: "408" }, - { label: "Акустикофилия", value: "903" }, - { label: "Алекситимия", value: "2020" }, - { label: "Алкоголь", value: "201" }, - { label: "Алкогольные игры", value: "1534" }, - { label: "Аллергии", value: "746" }, - { label: "Алхимия", value: "1174" }, - { label: "Альбинизм", value: "1213" }, - { label: "Альпинизм", value: "1448" }, - { label: "Альтернативная мировая история", value: "442" }, - { label: "Альтернативное размножение", value: "2751" }, - { label: "Альтернативные судьбы", value: "2839" }, - { label: "Амазонки", value: "1424" }, - { label: "Амегакуре", value: "2571" }, - { label: "Американский футбол", value: "808" }, - { label: "Ампутация", value: "704" }, - { label: "Амфибии", value: "2792" }, - { label: "Анальгезия", value: "1254" }, - { label: "Анальный оргазм", value: "999" }, - { label: "Анальный секс", value: "215" }, - { label: "Анархия", value: "1175" }, - { label: "Анахронизмы", value: "780" }, - { label: "Ангелы", value: "91" }, - { label: "Ангелы-хранители", value: "1044" }, - { label: "Ангст", value: "1665" }, - { label: "Андрогинная внешность", value: "2602" }, - { label: "Андроиды", value: "155" }, - { label: "Анимагия", value: "2519" }, - { label: "Анимализм", value: "1443" }, - { label: "Аномальные зоны", value: "822" }, - { label: "Анонимный секс", value: "112" }, - { label: "Анрождение", value: "1246" }, - { label: "Антарктида", value: "2205" }, - { label: "Анти-Сью (Анти-Стью)", value: "1150" }, - { label: "Антигерои", value: "418" }, - { label: "Антизлодеи", value: "1833" }, - { label: "Антикварные магазины", value: "1496" }, - { label: "Антисоциальное расстройство личности", value: "734" }, - { label: "Антиутопия", value: "1700" }, - { label: "Античность", value: "237" }, - { label: "Апатия", value: "1850" }, - { label: "Аптеки", value: "1240" }, - { label: "Аргентина", value: "1146" }, - { label: "Арены", value: "940" }, - { label: "Аристократия", value: "16" }, - { label: "Арктика", value: "2426" }, - { label: "Армия", value: "22" }, - { label: "Аромантичные персонажи", value: "339" }, - { label: "Артефакты", value: "924" }, - { label: "Археологи", value: "405" }, - { label: "Архивы", value: "2594" }, - { label: "Архитекторы", value: "1011" }, - { label: "Асексофобия", value: "1120" }, - { label: "Асексуальные персонажи", value: "168" }, - { label: "Аскетизм", value: "606" }, - { label: "Асоциальность", value: "2019" }, - { label: "Ассистенты", value: "2641" }, - { label: "Астма", value: "1121" }, - { label: "Астральное проецирование", value: "2530" }, - { label: "Астральные миры", value: "1379" }, - { label: "Астрология", value: "476" }, - { label: "Астронавты", value: "2220" }, - { label: "Астрономия", value: "1372" }, - { label: "Асфиксия", value: "104" }, - { label: "Атеисты", value: "669" }, - { label: "Атлантида", value: "1144" }, - { label: "Атмокинез", value: "1964" }, - { label: "Атмосферная зарисовка", value: "2802" }, - { label: "Атомные электростанции", value: "2192" }, - { label: "Аудиокинез", value: "2849" }, - { label: "Аукционы", value: "607" }, - { label: "Аутоассасинофилия", value: "2025" }, - { label: "Аутоканнибализм", value: "2593" }, - { label: "Аутофелляция / Аутокуннилингус", value: "610" }, - { label: "Африка", value: "791" }, - { label: "Афродизиаки", value: "103" }, - { label: "Аффект", value: "938" }, - { label: "Аэрокинез", value: "2850" }, - { label: "Байкеры", value: "522" }, - { label: "Байронические герои", value: "2900" }, - { label: "Балет", value: "404" }, - { label: "Баллада", value: "2318" }, - { label: "Банки / Биржи", value: "937" }, - { label: "Банши", value: "250" }, - { label: "Барти Крауч-младший — не Пожиратель Смерти", value: "2795" }, - { label: "Бары", value: "142" }, - { label: "Баскетбол", value: "829" }, - { label: "Баски", value: "1763" }, - { label: "Бастарды", value: "603" }, - { label: "Батохаки", value: "2830" }, - { label: "Бедность", value: "979" }, - { label: "Без Волдеморта", value: "2160" }, - { label: "Без диалогов (стилизация)", value: "2885" }, - { label: "Без золотого трио", value: "2648" }, - { label: "Без Избранного (Гарри Поттер)", value: "2762" }, - { label: "Без канонических персонажей", value: "2694" }, - { label: "Без Таноса", value: "2381" }, - { label: "БезГражданки", value: "2418" }, - { label: "Бездомные", value: "916" }, - { label: "Безответственность", value: "2152" }, - { label: "Безумные ученые", value: "2559" }, - { label: "Безэмоциональность", value: "1488" }, - { label: "Бейсбол", value: "1489" }, - { label: "Беларусь", value: "1030" }, - { label: "Белый день", value: "683" }, - { label: "Белый стих", value: "1998" }, - { label: "Бельгия", value: "1039" }, - { label: "Беременность", value: "1712" }, - { label: "Бермудский треугольник", value: "1158" }, - { label: "Берсерки", value: "566" }, - { label: "Бесконтактный секс", value: "2214" }, - { label: "Бесплодие", value: "719" }, - { label: "Беспокойство", value: "2888" }, - { label: "Беспризорники", value: "604" }, - { label: "Беспричудный Изуку", value: "2422" }, - { label: "Бессмертие", value: "219" }, - { label: "Бессмертный конкурс 2023", value: "3016" }, - { label: "Бестелесность", value: "2607" }, - { label: "Би-персонажи", value: "300" }, - { label: "Библейские темы и мотивы", value: "1468" }, - { label: "Библиотекари", value: "2631" }, - { label: "Библиотеки", value: "310" }, - { label: "Бизнесмены / Бизнесвумен", value: "573" }, - { label: "Биологическое оружие", value: "1188" }, - { label: "Биопанк", value: "281" }, - { label: "Биороботы", value: "2256" }, - { label: "Биполярное расстройство", value: "690" }, - { label: "Битва за Хогвартс", value: "2774" }, - { label: "Битва Историй", value: "2960" }, - { label: "Благородные разбойники", value: "952" }, - { label: "Благотворительность", value: "613" }, - { label: "Бладплей", value: "109" }, - { label: "Ближний Восток", value: "1041" }, - { label: "Близкие враги", value: "721" }, - { label: "Близнецы", value: "295" }, - { label: "Блиц-свидания", value: "2511" }, - { label: "Блогеры", value: "1471" }, - { label: "Блокадный Ленинград", value: "1111" }, - { label: "Блэкигуд", value: "2905" }, - { label: "Богачи", value: "1549" }, - { label: "Боги / Божественные сущности", value: "149" }, - { label: "Боди-арт", value: "2260" }, - { label: "Бодипозитив", value: "1153" }, - { label: "Боевая пара", value: "1919" }, - { label: "Боевые искусства", value: "918" }, - { label: "Бои без правил", value: "1390" }, - { label: "Бокс", value: "765" }, - { label: "Болгария", value: "1786" }, - { label: "Болезнь Альцгеймера", value: "2533" }, - { label: "Боль", value: "1803" }, - { label: "Больницы", value: "20" }, - { label: "Большая Игра профессора Дамблдора", value: "2890" }, - { label: "Бордели", value: "120" }, - { label: "Борьба за власть", value: "2300" }, - { label: "Борьба за отношения", value: "1251" }, - { label: "Борьба за справедливость", value: "2335" }, - { label: "Боязнь боли", value: "2441" }, - { label: "Боязнь воды", value: "1084" }, - { label: "Боязнь врачей", value: "2063" }, - { label: "Боязнь высоты", value: "1085" }, - { label: "Боязнь грозы", value: "1124" }, - { label: "Боязнь громких звуков", value: "1090" }, - { label: "Боязнь грязи", value: "1277" }, - { label: "Боязнь замкнутых пространств", value: "821" }, - { label: "Боязнь зеркал", value: "2739" }, - { label: "Боязнь клоунов", value: "2970" }, - { label: "Боязнь крови", value: "1312" }, - { label: "Боязнь людей", value: "1082" }, - { label: "Боязнь мужчин", value: "1264" }, - { label: "Боязнь насекомых / Боязнь пауков", value: "1119" }, - { label: "Боязнь неудачи", value: "2713" }, - { label: "Боязнь огня", value: "1258" }, - { label: "Боязнь одиночества", value: "1491" }, - { label: "Боязнь открытых пространств", value: "965" }, - { label: "Боязнь привязанности", value: "1343" }, - { label: "Боязнь призраков", value: "2770" }, - { label: "Боязнь прикосновений", value: "823" }, - { label: "Боязнь сексуальных домогательств", value: "2319" }, - { label: "Боязнь смерти", value: "1515" }, - { label: "Боязнь сна", value: "2262" }, - { label: "Боязнь темноты", value: "1494" }, - { label: "Боязнь тишины", value: "2233" }, - { label: "Бразилия", value: "833" }, - { label: "Брак по договоренности", value: "35" }, - { label: "Брак по расчету", value: "303" }, - { label: "БРД", value: "2907" }, - { label: "Бродячие артисты", value: "2661" }, - { label: "Бродячие певцы", value: "1497" }, - { label: "Броманс", value: "171" }, - { label: "Будущее", value: "227" }, - { label: "Буллёзный эпидермолиз", value: "2725" }, - { label: "Буллинг", value: "436" }, - { label: "Бухгалтеры", value: "2509" }, - { label: "Буч и фэм", value: "486" }, - { label: "Бывшие", value: "311" }, - { label: "Бывшие враги", value: "2812" }, - { label: "Бывшие серийные убийцы", value: "2498" }, - { label: "Бытовое фэнтези", value: "2702" }, - { label: "В одном теле", value: "1425" }, - { label: "В поисках отношений", value: "1576" }, - { label: "В. А. О. Н.", value: "2377" }, - { label: "Вагинальный секс", value: "395" }, - { label: "Ваканда", value: "2668" }, - { label: "Валькирии", value: "557" }, - { label: "Вампиры", value: "27" }, - { label: "Василиски", value: "863" }, - { label: "Вдовство", value: "2030" }, - { label: "Вебкам-модели", value: "2299" }, - { label: "Вегетарианцы / Веганы", value: "577" }, - { label: "Ведьмы / Колдуны", value: "33" }, - { label: "Великая депрессия", value: "2224" }, - { label: "Великая французская революция", value: "2000" }, - { label: "Великобритания", value: "673" }, - { label: "Великое Княжество Литовское", value: "2587" }, - { label: "Великолепный мерзавец", value: "1244" }, - { label: "Велоспорт", value: "2978" }, - { label: "Венгрия", value: "1432" }, - { label: "Вендиго", value: "251" }, - { label: "Верность", value: "459" }, - { label: "Вертикальный инцест", value: "337" }, - { label: "Вестерн", value: "2329" }, - { label: "Ветераны", value: "555" }, - { label: "Ветеринары", value: "629" }, - { label: "Вечеринки", value: "854" }, - { label: "Вечная молодость", value: "2405" }, - { label: "Вещие сны", value: "1993" }, - { label: "Взаимопонимание", value: "2887" }, - { label: "Взросление", value: "909" }, - { label: "Вигиланты", value: "1514" }, - { label: "Видеоигры", value: "2508" }, - { label: "Визажисты / Гримеры", value: "1801" }, - { label: "Викинги", value: "676" }, - { label: "Виктимблейминг", value: "331" }, - { label: "Викторианская эпоха", value: "469" }, - { label: "Виртуальная реальность", value: "803" }, - { label: "Виртуальный секс", value: "268" }, - { label: "Витилиго", value: "1167" }, - { label: "Вице-королевство Новая Испания", value: "2603" }, - { label: "ВИЧ / СПИД", value: "542" }, - { label: "Влюбленность", value: "1303" }, - { label: "Вне закона", value: "1089" }, - { label: "Внутренний сексизм", value: "2155" }, - { label: "Внутренняя гомофобия", value: "2154" }, - { label: "Внутренняя трансфобия", value: "2716" }, - { label: "Водители / Шоферы", value: "879" }, - { label: "Военные", value: "313" }, - { label: "Военные городки", value: "2095" }, - { label: "Военные преступления", value: "2356" }, - { label: "Возвращение", value: "1522" }, - { label: "Возвращение Артура", value: "2570" }, - { label: "Воздержание", value: "921" }, - { label: "Воздушная атлетика", value: "2811" }, - { label: "Война", value: "232" }, - { label: "Война в Афганистане", value: "1050" }, - { label: "Война во Вьетнаме", value: "1332" }, - { label: "Война за независимость США", value: "2065" }, - { label: "Война миров", value: "1222" }, - { label: "Волдигуд", value: "1874" }, - { label: "Волейбол", value: "1095" }, - { label: "Волк в овечьей шкуре", value: "2228" }, - { label: "Волонтеры", value: "900" }, - { label: "Волшебники / Волшебницы", value: "93" }, - { label: "Воображаемые друзья", value: "738" }, - { label: "Ворарефилия", value: "157" }, - { label: "Воры", value: "320" }, - { label: "Воскрешение", value: "256" }, - { label: "Воспоминания", value: "1118" }, - { label: "Воссоединение", value: "498" }, - { label: "Восстание машин", value: "817" }, - { label: "Восточное побережье", value: "770" }, - { label: "Восьмое марта", value: "493" }, - { label: "Вражда", value: "681" }, - { label: "Вражда семей", value: "2867" }, - { label: "Врачи", value: "374" }, - { label: "Времена Мародеров", value: "1872" }, - { label: "Времена Основателей", value: "1891" }, - { label: "Временная смерть персонажа", value: "439" }, - { label: "Временное бессмертие", value: "2755" }, - { label: "Временное воскрешение персонажей", value: "2649" }, - { label: "Временные петли", value: "147" }, - { label: "Временные превращения", value: "927" }, - { label: "Врожденные заболевания", value: "1781" }, - { label: "Всадники Апокалипсиса", value: "1326" }, - { label: "Все живы / Никто не умер", value: "535" }, - { label: "Все за Одного — отец Изуку", value: "2421" }, - { label: "Всезнающий рассказчик", value: "1368" }, - { label: "Вселение в тело", value: "1181" }, - { label: "Вторая мировая", value: "488" }, - { label: "Вторжение пришельцев", value: "755" }, - { label: "Вторичный стыд", value: "1393" }, - { label: "Второй шанс", value: "1799" }, - { label: "Второстепенные оригинальные персонажи", value: "1536" }, - { label: "Вуайеризм", value: "174" }, - { label: "Вуду", value: "678" }, - { label: "Выбор", value: "2808" }, - { label: "Выгорание", value: "2914" }, - { label: "Выживание", value: "216" }, - { label: "Вымирающий вид", value: "2373" }, - { label: "Вымышленная анатомия", value: "1317" }, - { label: "Вымышленная география", value: "1294" }, - { label: "Вымышленная религия", value: "2062" }, - { label: "Вымышленная физика", value: "2132" }, - { label: "Вымышленные виды спорта", value: "1777" }, - { label: "Вымышленные заболевания", value: "1295" }, - { label: "Вымышленные науки", value: "1333" }, - { label: "Вымышленные праздники", value: "1769" }, - { label: "Вымышленные профессии", value: "1391" }, - { label: "Вымышленные существа", value: "1685" }, - { label: "Вымышленные языки", value: "536" }, - { label: "Выпускные", value: "717" }, - { label: "Высшее общество", value: "1093" }, - { label: "Высшие учебные заведения", value: "97" }, - { label: "Выученная беспомощность", value: "2772" }, - { label: "Выхаживание", value: "511" }, - { label: "Выход из нездоровых отношений", value: "2891" }, - { label: "Гадалки / Ясновидящие", value: "2662" }, - { label: "Гадкий утенок", value: "1225" }, - { label: "Газлайтинг", value: "1418" }, - { label: "Галлюцинации / Иллюзии", value: "365" }, - { label: "Гаремник", value: "50" }, - { label: "Гаремы", value: "115" }, - { label: "Гарпии", value: "1482" }, - { label: "Гарри Поттер и Драко Малфой — друзья", value: "1923" }, - { label: "Гастроли", value: "2487" }, - { label: "Гедонизм", value: "736" }, - { label: "Гей-клубы", value: "2482" }, - { label: "Геймеры", value: "356" }, - { label: "Геймлит", value: "23" }, - { label: "Гейши / Кисэн", value: "332" }, - { label: "Гемофилия", value: "1341" }, - { label: "Гендерная дисфория", value: "431" }, - { label: "Гендерная интрига", value: "55" }, - { label: "Гендерное неравенство", value: "2568" }, - { label: "Гендерный нонконформизм", value: "1417" }, - { label: "Гендерсвап", value: "1691" }, - { label: "Генетика / Эволюция", value: "2131" }, - { label: "Гении", value: "445" }, - { label: "Генитальные пытки", value: "2104" }, - { label: "Геноцид", value: "720" }, - { label: "Геокинез", value: "2889" }, - { label: "Геологи / Географы", value: "934" }, - { label: "Германия", value: "688" }, - { label: "Германия — не Священная Римская Империя", value: "2470" }, - { label: "Герой поневоле", value: "2145" }, - { label: "Геронтофилия", value: "162" }, - { label: "Гетерохромия", value: "1210" }, - { label: "Гетто", value: "1783" }, - { label: "Гибристофилия", value: "1843" }, - { label: "Гиганты", value: "735" }, - { label: "Гидра трэш-пати", value: "2804" }, - { label: "Гики", value: "482" }, - { label: "Гильдии", value: "957" }, - { label: "Гимнастика", value: "1974" }, - { label: "Гиперакузия", value: "2866" }, - { label: "Гиперсексуальность", value: "945" }, - { label: "Гипноз", value: "700" }, - { label: "Гладиаторы", value: "677" }, - { label: "Глобальные катастрофы", value: "525" }, - { label: "Глухота", value: "595" }, - { label: "Гнев", value: "2806" }, - { label: "Гнездование", value: "1452" }, - { label: "Гномы", value: "138" }, - { label: "Гоблины", value: "188" }, - { label: "Големы", value: "1208" }, - { label: "Голод", value: "868" }, - { label: "Голодные Фэндомные Игры", value: "2895" }, - { label: "Гомофобия", value: "428" }, - { label: "Гомункулы", value: "1366" }, - { label: "Горе / Утрата", value: "1113" }, - { label: "Горизонтальный инцест", value: "338" }, - { label: "Городские легенды", value: "1832" }, - { label: "Горы", value: "702" }, - { label: "Горячие источники", value: "2813" }, - { label: "Гостевой брак", value: "1354" }, - { label: "Гостиницы", value: "384" }, - { label: "Готический роман", value: "963" }, - { label: "Готы", value: "575" }, - { label: "Гравиокинез", value: "1985" }, - { label: "Гражданская война", value: "370" }, - { label: "Гражданская война в России", value: "1282" }, - { label: "Гражданская война в США", value: "1018" }, - { label: "Графичные описания", value: "2854" }, - { label: "Грейнджергад", value: "1892" }, - { label: "Грейромантичные персонажи", value: "707" }, - { label: "Гремлины", value: "189" }, - { label: "Греция", value: "1040" }, - { label: "Грифоны", value: "335" }, - { label: "Громкий секс", value: "1184" }, - { label: "Грубый секс", value: "121" }, - { label: "Грудное кормление", value: "296" }, - { label: "Групи", value: "2925" }, - { label: "Групповое изнасилование", value: "2203" }, - { label: "Групповой секс", value: "1654" }, - { label: "Грязный реализм", value: "634" }, - { label: "Гувернантки", value: "1179" }, - { label: "Гули", value: "367" }, - { label: "Гэвин Рид — андроид", value: "2692" }, - { label: "Даб-кон", value: "57" }, - { label: "Даби — не Тойя Тодороки", value: "2821" }, - { label: "Даби — Тойя Тодороки", value: "2423" }, - { label: "Дадзава", value: "2915" }, - { label: "Дадмайт", value: "2917" }, - { label: "Дайверы", value: "939" }, - { label: "Дайвинг", value: "2823" }, - { label: "Дама в беде", value: "1259" }, - { label: "Дамбигад", value: "1863" }, - { label: "Дамбигуд", value: "1873" }, - { label: "Дампиры", value: "2860" }, - { label: "Дандэрэ", value: "1364" }, - { label: "Дарк", value: "1678" }, - { label: "Даркнет", value: "1287" }, - { label: "Дачи", value: "930" }, - { label: "Двойники", value: "305" }, - { label: "Двойное проникновение", value: "107" }, - { label: "Двойной кноттинг", value: "2619" }, - { label: "Двойные агенты", value: "441" }, - { label: "Дворцовые интриги", value: "2075" }, - { label: "Девиантное поведение", value: "1814" }, - { label: "Девичники / Мальчишники", value: "1191" }, - { label: "Девочки из Эквестрии", value: "2912" }, - { label: "Дежавю", value: "2049" }, - { label: "Декаданс", value: "2353" }, - { label: "Деконструкция", value: "1322" }, - { label: "Деми-персонажи", value: "723" }, - { label: "Демиурги", value: "819" }, - { label: "Демонология", value: "2485" }, - { label: "Демоны", value: "73" }, - { label: "Демоны-хранители", value: "2045" }, - { label: "Дендрофилия", value: "491" }, - { label: "День благодарения", value: "754" }, - { label: "День знаний", value: "1075" }, - { label: "День матери", value: "2979" }, - { label: "День мертвых", value: "1074" }, - { label: "День независимости США", value: "1008" }, - { label: "День рождения", value: "298" }, - { label: "День святого Валентина", value: "204" }, - { label: "День святого Патрика", value: "1129" }, - { label: "День смерти", value: "2251" }, - { label: "День учителя", value: "1076" }, - { label: "Деперсонализация", value: "1197" }, - { label: "Депрессия", value: "354" }, - { label: "Депривация сна", value: "551" }, - { label: "Дереализация", value: "1198" }, - { label: "Деревни", value: "364" }, - { label: "Дерматозойный бред", value: "2779" }, - { label: "Деспоты / Тираны", value: "1345" }, - { label: "Детектив", value: "1672" }, - { label: "Дети", value: "217" }, - { label: "Детоненавистничество", value: "1836" }, - { label: "Детская влюбленность", value: "186" }, - { label: "Детские дома", value: "355" }, - { label: "Детские лагеря", value: "222" }, - { label: "Детские сады", value: "567" }, - { label: "Дефекты зрения", value: "2639" }, - { label: "Дефекты речи", value: "737" }, - { label: "Джинны / Пери", value: "456" }, - { label: "Джон Ватсон — преступник", value: "1932" }, - { label: "Джуманджи — не игровой мир", value: "2799" }, - { label: "Диабет", value: "820" }, - { label: "Диалоги (стилизация)", value: "1139" }, - { label: "Дизайнеры", value: "1875" }, - { label: "Дизельпанк", value: "294" }, - { label: "Дикие животные", value: "622" }, - { label: "Дикий Запад", value: "293" }, - { label: "Дилетанты", value: "2443" }, - { label: "Динозавры", value: "670" }, - { label: "Дипломатия", value: "1790" }, - { label: "Дисбаланс власти", value: "732" }, - { label: "Дискриминация", value: "2230" }, - { label: "Дискриминация по внешности", value: "1414" }, - { label: "Дискриминация по возрасту", value: "2843" }, - { label: "Дислексия", value: "860" }, - { label: "Дисморфофобия", value: "1108" }, - { label: "Диссоциативное расстройство идентичности", value: "2127" }, - { label: "Дистанционное обучение", value: "2592" }, - { label: "Дисфункциональные семьи", value: "602" }, - { label: "Дневники (стилизация)", value: "214" }, - { label: "Добрый Сальери", value: "2522" }, - { label: "Доверие", value: "1117" }, - { label: "Договоры / Сделки", value: "2137" }, - { label: "Документы / Отчеты (стилизация)", value: "752" }, - { label: "Долголетие", value: "2130" }, - { label: "Домашнее насилие", value: "42" }, - { label: "Домашние животные", value: "517" }, - { label: "Домашний арест", value: "2526" }, - { label: "Домашний ремонт", value: "867" }, - { label: "Домовые", value: "1423" }, - { label: "Допросы", value: "2654" }, - { label: "Дорожное приключение", value: "47" }, - { label: "Драббл", value: "2935" }, - { label: "Драки", value: "1367" }, - { label: "Драконы", value: "43" }, - { label: "Драма", value: "1668" }, - { label: "Драуги", value: "2264" }, - { label: "Древний Восток", value: "1940" }, - { label: "Древний Египет", value: "474" }, - { label: "Древний Китай", value: "1438" }, - { label: "Древняя Русь", value: "466" }, - { label: "Дремлющее зло", value: "2719" }, - { label: "Дремлющие способности", value: "2084" }, - { label: "Другая магическая школа", value: "1864" }, - { label: "Другие планеты", value: "1415" }, - { label: "Другой избранный", value: "2054" }, - { label: "Другой факультет", value: "1866" }, - { label: "Дружба", value: "1701" }, - { label: "Дружба втайне", value: "2303" }, - { label: "Дружба по расчету", value: "2331" }, - { label: "Друзья детства", value: "343" }, - { label: "Друзья по переписке", value: "1466" }, - { label: "Друзья поневоле", value: "627" }, - { label: "Друзья с привилегиями", value: "113" }, - { label: "Друиды", value: "1199" }, - { label: "Дрэг (субкультура)", value: "1248" }, - { label: "Дурмстранг", value: "2389" }, - { label: "Дурслигуд", value: "2058" }, - { label: "Духи природы", value: "503" }, - { label: "Духовенство", value: "478" }, - { label: "Духовная связь", value: "2090" }, - { label: "Духокинез", value: "2500" }, - { label: "Дуэли", value: "446" }, - { label: "Дьяволы", value: "2369" }, - { label: "Дэдди-кинк", value: "165" }, - { label: "Еда / Кулинария", value: "1195" }, - { label: "Единороги", value: "373" }, - { label: "Ёкаи", value: "416" }, - { label: "Естественные враги", value: "728" }, - { label: "Жамевю", value: "2050" }, - { label: "Жаргон", value: "1016" }, - { label: "Ждун Фест", value: "2936" }, - { label: "Железнодорожники", value: "2680" }, - { label: "Железные дороги", value: "811" }, - { label: "Женская дружба", value: "2156" }, - { label: "Жертвы обстоятельств", value: "1475" }, - { label: "Жестокое обращение с животными", value: "722" }, - { label: "Жестокость", value: "1710" }, - { label: "Живой сосуд", value: "2072" }, - { label: "Животные-компаньоны", value: "2371" }, - { label: "Живые машины", value: "961" }, - { label: "ЖиП", value: "143" }, - { label: "Журналисты", value: "438" }, - { label: "Заболевания", value: "1268" }, - { label: "Заболевания крови", value: "2254" }, - { label: "Заболевания сердца", value: "1767" }, - { label: "Забота / Поддержка", value: "1302" }, - { label: "Заброшенные города", value: "1276" }, - { label: "Заброшенные здания", value: "1483" }, - { label: "Забытый герой", value: "2337" }, - { label: "Завещание", value: "1262" }, - { label: "Зависимое расстройство личности", value: "2349" }, - { label: "Зависть", value: "990" }, - { label: "Завоевание", value: "978" }, - { label: "Загробный мир", value: "275" }, - { label: "Закавказье", value: "2819" }, - { label: "Заклятые друзья", value: "1316" }, - { label: "Закрытые города", value: "1481" }, - { label: "Закрытые учебные заведения", value: "98" }, - { label: "Закрытый детектив", value: "2616" }, - { label: "Заложники", value: "845" }, - { label: "Замки", value: "654" }, - { label: "Замкнутое пространство", value: "2306" }, - { label: "Замкнутый мир", value: "874" }, - { label: "Занавесочная история", value: "1688" }, - { label: "Запахи", value: "1385" }, - { label: "Заповедники", value: "618" }, - { label: "Запредельно одаренный персонаж", value: "1241" }, - { label: "Запрет на магию", value: "2663" }, - { label: "Запретные отношения", value: "362" }, - { label: "Засосы / Укусы", value: "913" }, - { label: "Застенчивость", value: "2807" }, - { label: "Затворники", value: "1059" }, - { label: "Затерянные миры", value: "2448" }, - { label: "Защита любимого", value: "2492" }, - { label: "Защитники природы", value: "628" }, - { label: "Защищенный секс", value: "636" }, - { label: "Звездные слезы", value: "2971" }, - { label: "Здоровые механизмы преодоления", value: "2147" }, - { label: "Здоровые отношения", value: "1853" }, - { label: "Зеркала", value: "724" }, - { label: "Зимняя Фандомная Битва", value: "254" }, - { label: "Злобные бывшие", value: "518" }, - { label: "Зловещая находка", value: "2275" }, - { label: "Знаки зодиака", value: "643" }, - { label: "Знаменитости", value: "1360" }, - { label: "Золотая клетка", value: "480" }, - { label: "Золотая молодежь", value: "796" }, - { label: "Золотая Орда", value: "1143" }, - { label: "Золушка", value: "1226" }, - { label: "Зомби", value: "31" }, - { label: "Зоопарки", value: "792" }, - { label: "Зоофилия", value: "1662" }, - { label: "Зоофобии", value: "2715" }, - { label: "ЗППП", value: "630" }, - { label: "ЗПР", value: "2259" }, - { label: "Зрелые персонажи", value: "851" }, - { label: "Иван Купала", value: "1068" }, - { label: "Игровое расстройство", value: "949" }, - { label: "Игрушки", value: "1346" }, - { label: "Игры на выживание", value: "554" }, - { label: "Игры на раздевание", value: "1535" }, - { label: "Игры с сосками", value: "423" }, - { label: "Игры с температурой", value: "230" }, - { label: "Идеализация", value: "1540" }, - { label: "Иерархический строй", value: "652" }, - { label: "Избранные", value: "847" }, - { label: "Избыточный физиологизм", value: "2334" }, - { label: "Изгнанники", value: "871" }, - { label: "Измена", value: "62" }, - { label: "Измененное состояние сознания", value: "2242" }, - { label: "Изменились за лето", value: "1908" }, - { label: "Изнасилование", value: "1650" }, - { label: "Изоляция", value: "1296" }, - { label: "Израиль", value: "1094" }, - { label: "Изуку Мидория — злодей", value: "2472" }, - { label: "Иллюзионисты", value: "532" }, - { label: "Имейджин", value: "838" }, - { label: "Импотенция", value: "632" }, - { label: "Импринтинг", value: "635" }, - { label: "Импрореал", value: "2955" }, - { label: "Импрофест", value: "2947" }, - { label: "Имя вместо цвета", value: "2919" }, - { label: "Инвалидность", value: "197" }, - { label: "Индия", value: "1002" }, - { label: "Инквизиция", value: "1130" }, - { label: "Инопланетяне", value: "58" }, - { label: "Инсектоиды / Арахноиды", value: "953" }, - { label: "Инсектофилия / Арахнофилия", value: "664" }, - { label: "Инсценированная смерть персонажа", value: "440" }, - { label: "Интерактивная работа", value: "419" }, - { label: "Интервью (стилизация)", value: "2446" }, - { label: "Интернет", value: "1286" }, - { label: "Интерсекс-персонажи", value: "299" }, - { label: "Интрамаммарный секс", value: "840" }, - { label: "Инфантильность", value: "1300" }, - { label: "Инцест", value: "1651" }, - { label: "Ипохондрия", value: "2178" }, - { label: "Ирландия", value: "1035" }, - { label: "Иронический детектив", value: "2638" }, - { label: "Искра Стайлз Стилински", value: "1975" }, - { label: "Искупление", value: "877" }, - { label: "Искусственно вызванные чувства", value: "626" }, - { label: "Искусственные интеллекты", value: "150" }, - { label: "Искусственные существа", value: "2277" }, - { label: "Искусство", value: "2257" }, - { label: "Искусствоведы", value: "2873" }, - { label: "Искушение", value: "776" }, - { label: "Испания", value: "1020" }, - { label: "Исповеди", value: "1342" }, - { label: "Исправительные лагеря", value: "2439" }, - { label: "Истинные", value: "1855" }, - { label: "Историческая Хеталия", value: "1865" }, - { label: "Исторические эпохи", value: "1698" }, - { label: "Исторический Кантриболс", value: "2913" }, - { label: "Историческое допущение", value: "2681" }, - { label: "Исцеление", value: "802" }, - { label: "Италия", value: "1003" }, - { label: "Ихтиоморфы", value: "972" }, - { label: "Йога", value: "483" }, - { label: "Йоль", value: "2143" }, - { label: "Кабаре", value: "457" }, - { label: "Кабэ-дон", value: "2626" }, - { label: "Казахстан", value: "1260" }, - { label: "Казино", value: "620" }, - { label: "Казнь", value: "548" }, - { label: "Как ориджинал", value: "1032" }, - { label: "Каламбуры", value: "881" }, - { label: "Каминг-аут", value: "559" }, - { label: "Канада", value: "695" }, - { label: "Каникулы", value: "805" }, - { label: "Каннибализм", value: "1706" }, - { label: "Канонная смерть персонажа", value: "564" }, - { label: "Капитан Гидра", value: "1928" }, - { label: "Карантинные зоны", value: "1290" }, - { label: "Карантины", value: "2734" }, - { label: "Караоке", value: "886" }, - { label: "Карательная психиатрия", value: "730" }, - { label: "Карибский бассейн", value: "1172" }, - { label: "Карма", value: "647" }, - { label: "Карьеризм", value: "956" }, - { label: "Каскадеры", value: "2267" }, - { label: "Катарсис", value: "824" }, - { label: "Кафе / Кофейни / Чайные", value: "13" }, - { label: "Квай-Гад", value: "2894" }, - { label: "Квантовая физика", value: "2744" }, - { label: "Квисины", value: "2722" }, - { label: "Кемономими", value: "997" }, - { label: "Кемпинг", value: "182" }, - { label: "Кентавры", value: "64" }, - { label: "Кибербуллинг", value: "1858" }, - { label: "Киберпанк", value: "24" }, - { label: "Киберспорт", value: "745" }, - { label: "Кибертрон", value: "2775" }, - { label: "Киборги", value: "154" }, - { label: "Киевская Русь", value: "515" }, - { label: "Кинк на беременность", value: "2281" }, - { label: "Кинк на волосы", value: "2089" }, - { label: "Кинк на волосы на теле", value: "1337" }, - { label: "Кинк на девственность", value: "2282" }, - { label: "Кинк на инвалидность", value: "633" }, - { label: "Кинк на интеллект", value: "2798" }, - { label: "Кинк на клыки", value: "2325" }, - { label: "Кинк на колготки", value: "2644" }, - { label: "Кинк на крылья", value: "2035" }, - { label: "Кинк на латекс", value: "134" }, - { label: "Кинк на мольбы", value: "1543" }, - { label: "Кинк на наручники", value: "1065" }, - { label: "Кинк на нижнее белье", value: "145" }, - { label: "Кинк на очки", value: "1723" }, - { label: "Кинк на ошейники", value: "388" }, - { label: "Кинк на полноту", value: "552" }, - { label: "Кинк на похвалу", value: "1042" }, - { label: "Кинк на протезы", value: "2682" }, - { label: "Кинк на рога", value: "2320" }, - { label: "Кинк на руки", value: "915" }, - { label: "Кинк на сердцебиение", value: "941" }, - { label: "Кинк на силу", value: "1476" }, - { label: "Кинк на слезы", value: "1373" }, - { label: "Кинк на служение", value: "2980" }, - { label: "Кинк на страх", value: "2246" }, - { label: "Кинк на стыд", value: "747" }, - { label: "Кинк на унижение", value: "2291" }, - { label: "Кинк на униформу", value: "1350" }, - { label: "Кинк на уши", value: "2370" }, - { label: "Кинк на формальную одежду", value: "1064" }, - { label: "Кинк на чужую одежду", value: "2920" }, - { label: "Кинк на чулки", value: "810" }, - { label: "Кинк на шрамы", value: "2831" }, - { label: "Кинк на щекотку", value: "2396" }, - { label: "Кинк-бинго", value: "2290" }, - { label: "Кинки / Фетиши", value: "1659" }, - { label: "Кинотеатры", value: "981" }, - { label: "Киригакуре", value: "2475" }, - { label: "Китай", value: "757" }, - { label: "Кладбища", value: "501" }, - { label: "Кланы", value: "2879" }, - { label: "Классизм", value: "1927" }, - { label: "Клейма", value: "1548" }, - { label: "Клептомания", value: "1160" }, - { label: "Клетка Люцифера", value: "1884" }, - { label: "Клетки", value: "1069" }, - { label: "Клиническая смерть", value: "2068" }, - { label: "Клокпанк / Ветропанк", value: "1812" }, - { label: "Клоны", value: "358" }, - { label: "Клоуны", value: "1520" }, - { label: "Клубы по интересам", value: "1369" }, - { label: "Клюква", value: "1001" }, - { label: "Кляпы / Затыкание рта", value: "379" }, - { label: "Книги", value: "2746" }, - { label: "Книжные магазины", value: "444" }, - { label: "Кноттинг", value: "99" }, - { label: "Ковбои", value: "330" }, - { label: "Кода", value: "1943" }, - { label: "Кожаная одежда", value: "1517" }, - { label: "Колдовстворец", value: "2600" }, - { label: "Кома", value: "516" }, - { label: "Командная работа", value: "708" }, - { label: "Комики", value: "1315" }, - { label: "Комиксисты / Мангаки", value: "576" }, - { label: "Коммунальные квартиры", value: "1437" }, - { label: "Коммуны", value: "797" }, - { label: "Комнатные растения", value: "1792" }, - { label: "Комплекс Бога", value: "2087" }, - { label: "Комплекс превосходства", value: "2191" }, - { label: "Комплексы", value: "2529" }, - { label: "Конкурс от BUBBLE", value: "2937" }, - { label: "Конный спорт", value: "1378" }, - { label: "Конохагакуре", value: "2477" }, - { label: "Конспирология", value: "1911" }, - { label: "Контрабандисты", value: "2363" }, - { label: "Контроль / Подчинение", value: "932" }, - { label: "Контроль памяти", value: "2540" }, - { label: "Контроль сознания", value: "257" }, - { label: "Конфликт мировоззрений", value: "2678" }, - { label: "Концентрационные лагеря", value: "547" }, - { label: "Концерты / Выступления", value: "1236" }, - { label: "Конюшни", value: "514" }, - { label: "Копирование сознания", value: "1231" }, - { label: "Копирование способностей", value: "1825" }, - { label: "Копро-кинк", value: "763" }, - { label: "Корё", value: "1142" }, - { label: "Корейская война", value: "2187" }, - { label: "Коренные народы", value: "1505" }, - { label: "Королевства", value: "2794" }, - { label: "Корпорация BOBSOC", value: "2940" }, - { label: "Корректирующее изнасилование", value: "2856" }, - { label: "Коррупция", value: "2150" }, - { label: "Косвенные убийства", value: "2846" }, - { label: "Космоопера", value: "48" }, - { label: "Космос", value: "236" }, - { label: "Косплееры", value: "944" }, - { label: "Кочевники", value: "1793" }, - { label: "Кошмары", value: "1228" }, - { label: "Красная нить судьбы", value: "608" }, - { label: "Крепости / Башни", value: "2321" }, - { label: "Крестовые походы", value: "2070" }, - { label: "Крестражи", value: "2238" }, - { label: "Кризис гендера", value: "1406" }, - { label: "Кризис ориентации", value: "1405" }, - { label: "Криминалистическая экспертиза", value: "2361" }, - { label: "Криминальная пара", value: "1920" }, - { label: "Криокинез", value: "1804" }, - { label: "Криптиды", value: "60" }, - { label: "Криптоистория", value: "179" }, - { label: "Кровь / Травмы", value: "800" }, - { label: "Кроссдрессинг", value: "65" }, - { label: "Кроссовер", value: "10" }, - { label: "Кроули — Архангел Рафаэль", value: "2471" }, - { label: "Круизы", value: "2488" }, - { label: "Крушение «Титаника»", value: "2200" }, - { label: "Крылатые", value: "92" }, - { label: "Крэк", value: "1408" }, - { label: "Ксенофилия", value: "1711" }, - { label: "Ксенофобия", value: "589" }, - { label: "Куба", value: "773" }, - { label: "Кудэрэ", value: "1216" }, - { label: "Куклы", value: "1221" }, - { label: "Куколдинг", value: "524" }, - { label: "Культ Дракона", value: "2244" }, - { label: "Кумогакуре", value: "2474" }, - { label: "Куннилингус", value: "172" }, - { label: "Купальни", value: "964" }, - { label: "Курение", value: "202" }, - { label: "Курортный роман", value: "181" }, - { label: "Курьеры", value: "2735" }, - { label: "Лабиринты", value: "1981" }, - { label: "Лаборатории", value: "1115" }, - { label: "Лабораторные опыты", value: "146" }, - { label: "Лав-отели", value: "801" }, - { label: "Лавандовый брак", value: "1357" }, - { label: "Лакросс", value: "830" }, - { label: "Лапслок", value: "267" }, - { label: "Латентная гомосексуальность", value: "2339" }, - { label: "ЛГБТ-родители", value: "2723" }, - { label: "Лебединая верность", value: "537" }, - { label: "Левитация", value: "1281" }, - { label: "Легкая атлетика", value: "1797" }, - { label: "Лекарственная зависимость", value: "2632" }, - { label: "Леса", value: "703" }, - { label: "Летние ОтМетки", value: "2956" }, - { label: "Летние школы", value: "2803" }, - { label: "Лето девяносто девятого", value: "1990" }, - { label: "Лжебоги", value: "2869" }, - { label: "Лингвистический кинк", value: "2042" }, - { label: "Лирика", value: "2005" }, - { label: "Лихтенштейн", value: "1023" }, - { label: "Личность против системы", value: "1912" }, - { label: "Ложная беременность", value: "1428" }, - { label: "Ложные воспоминания", value: "376" }, - { label: "Ложные обвинения", value: "1204" }, - { label: "Локальный постапокалипсис", value: "2455" }, - { label: "Лолита (субкультура)", value: "1207" }, - { label: "Ломка", value: "1846" }, - { label: "Лошади", value: "1083" }, - { label: "Лугнасад", value: "2621" }, - { label: "Лудомания", value: "947" }, - { label: "Лучники", value: "1521" }, - { label: "Любить луну", value: "2901" }, - { label: "Любовная магия", value: "2317" }, - { label: "Любовный магнит", value: "1407" }, - { label: "Любовный многоугольник", value: "63" }, - { label: "Любовь с первого взгляда", value: "813" }, - { label: "Любовь/Ненависть", value: "1703" }, - { label: "Люди", value: "1538" }, - { label: "Людоеды", value: "1288" }, - { label: "Магазины", value: "1063" }, - { label: "Магическая связь", value: "2720" }, - { label: "Магические клятвы", value: "2245" }, - { label: "Магические лавки", value: "2629" }, - { label: "Магические учебные заведения", value: "1338" }, - { label: "Магический реализм", value: "9" }, - { label: "Магия", value: "1419" }, - { label: "Магия крови", value: "2857" }, - { label: "Маленькие города", value: "221" }, - { label: "Малфоигуд", value: "1890" }, - { label: "Мамми-кинк", value: "273" }, - { label: "Мандалорцы", value: "2870" }, - { label: "Манекены", value: "1509" }, - { label: "Манипуляции", value: "992" }, - { label: "Марафонный секс", value: "316" }, - { label: "Маргиналы", value: "394" }, - { label: "Марди Гра", value: "2407" }, - { label: "Маскарады / Балы", value: "443" }, - { label: "Массаж", value: "2218" }, - { label: "Мастурбация", value: "226" }, - { label: "Матриархат", value: "706" }, - { label: "Матчасть", value: "2328" }, - { label: "Махотокоро", value: "2943" }, - { label: "Маяки", value: "1356" }, - { label: "Мегаломания", value: "985" }, - { label: "Мегаполисы", value: "451" }, - { label: "Медицинский кинк", value: "748" }, - { label: "Медицинское использование наркотиков", value: "925" }, - { label: "Медовый месяц", value: "2235" }, - { label: "Медсестры / Медбратья", value: "2236" }, - { label: "Межбедренный секс", value: "931" }, - { label: "Междумирье", value: "2280" }, - { label: "Межэтнические отношения", value: "2121" }, - { label: "Мейлдом", value: "59" }, - { label: "Мексика", value: "768" }, - { label: "Меланхолия", value: "2108" }, - { label: "Мелодрама", value: "2881" }, - { label: "Менструация", value: "1570" }, - { label: "Ментальная тюрьма", value: "2294" }, - { label: "Месть", value: "907" }, - { label: "Металлисты", value: "1206" }, - { label: "Металлокинез", value: "1532" }, - { label: "Метафизические существа", value: "2097" }, - { label: "Метки", value: "1398" }, - { label: "Метросексуалы", value: "558" }, - { label: "Механофилия / Технофилия", value: "587" }, - { label: "Мечники", value: "2783" }, - { label: "Мечты", value: "993" }, - { label: "Миддлпанк", value: "2076" }, - { label: "Мизофилия", value: "1970" }, - { label: "Микро / Макро", value: "407" }, - { label: "Микрофикшен", value: "417" }, - { label: "Мимикрия", value: "2083" }, - { label: "Минет", value: "231" }, - { label: "Министерство Магии", value: "1917" }, - { label: "Минотавры", value: "2820" }, - { label: "Мир без гомофобии", value: "2660" }, - { label: "Мир Желаний (Однажды в сказке)", value: "2923" }, - { label: "Мир Института Войны", value: "2379" }, - { label: "Мироустройство", value: "989" }, - { label: "Мистерия", value: "2012" }, - { label: "Мистика", value: "1671" }, - { label: "Мистическая беременность", value: "1784" }, - { label: "Мистические защитники", value: "2162" }, - { label: "Мифы и мифология", value: "18" }, - { label: "Мнимая беременность", value: "1096" }, - { label: "Мнимая проза", value: "1098" }, - { label: "Мнимая свобода", value: "2708" }, - { label: "Многодетные семьи", value: "2785" }, - { label: "Множественное проникновение", value: "2059" }, - { label: "Множественные оргазмы", value: "317" }, - { label: "Множественные финалы", value: "312" }, - { label: "Могучие Рейнджеры: Дино Заряд", value: "2931" }, - { label: "Могучие Рейнджеры: Мегафорс", value: "2930" }, - { label: "Могучие Рейнджеры: Мистическая Сила", value: "2933" }, - { label: "Могучие Рейнджеры: Самураи", value: "2929" }, - { label: "Могучие Рейнджеры: Успеть на помощь", value: "2934" }, - { label: "Могучие Рейнджеры: Ярость Джунглей", value: "2932" }, - { label: "Модели", value: "399" }, - { label: "Модельеры", value: "598" }, - { label: "Модельный бизнес", value: "435" }, - { label: "Модификации тела", value: "890" }, - { label: "Молдова", value: "2967" }, - { label: "Молчание", value: "2465" }, - { label: "Монастыри", value: "352" }, - { label: "Монахи", value: "353" }, - { label: "Монолог", value: "2333" }, - { label: "Монстрофилия", value: "94" }, - { label: "Монстры", value: "529" }, - { label: "Моральные дилеммы", value: "959" }, - { label: "Морлед", value: "2255" }, - { label: "Моря / Океаны", value: "955" }, - { label: "Моряки", value: "406" }, - { label: "Мотоспорт", value: "1461" }, - { label: "Мужская беременность", value: "1660" }, - { label: "Мужская дружба", value: "2157" }, - { label: "Мужское грудное кормление", value: "297" }, - { label: "Музеи / Галереи", value: "795" }, - { label: "Музы", value: "2001" }, - { label: "Музыканты", value: "141" }, - { label: "Муковисцидоз", value: "2764" }, - { label: "Мультикроссовер", value: "2247" }, - { label: "Мумии", value: "579" }, - { label: "Мутанты", value: "740" }, - { label: "Мэри Сью (Марти Стью)", value: "1655" }, - { label: "Мятежи / Восстания", value: "584" }, - { label: "На грани жизни и смерти", value: "2545" }, - { label: "Наблюдатели", value: "1816" }, - { label: "Навязчивая опека", value: "954" }, - { label: "Навязчивые мысли", value: "2851" }, - { label: "Наги", value: "90" }, - { label: "Надежда", value: "2627" }, - { label: "Наемники", value: "512" }, - { label: "Наемные убийцы", value: "192" }, - { label: "Названные сиблинги", value: "2927" }, - { label: "Намеки на отношения", value: "1387" }, - { label: "Намеки на секс", value: "1382" }, - { label: "Напарники", value: "1291" }, - { label: "Наперегонки со временем", value: "2240" }, - { label: "НапиСанта", value: "2946" }, - { label: "Наполеоновские войны", value: "470" }, - { label: "Нарколепсия", value: "586" }, - { label: "Наркоторговля", value: "1116" }, - { label: "Нарушение этических норм", value: "968" }, - { label: "Нарциссизм", value: "546" }, - { label: "Насекомые", value: "2358" }, - { label: "Насилие", value: "1649" }, - { label: "Насилие над детьми", value: "726" }, - { label: "Наследие (Гарри Поттер)", value: "1935" }, - { label: "Наследство", value: "2172" }, - { label: "Наставничество", value: "935" }, - { label: "Настольные игры", value: "1513" }, - { label: "Настольные ролевые игры", value: "644" }, - { label: "Натуризм / Нудизм", value: "1810" }, - { label: "Наука", value: "2624" }, - { label: "Научная фантастика", value: "596" }, - { label: "Научно-исследовательские организации", value: "1503" }, - { label: "Научное фэнтези", value: "180" }, - { label: "Начало отношений", value: "1328" }, - { label: "Небинарные персонажи", value: "314" }, - { label: "Невезение", value: "2689" }, - { label: "Невзаимные предначертанные", value: "1484" }, - { label: "Невзаимные чувства", value: "105" }, - { label: "Невидимость", value: "359" }, - { label: "Невидимый мир", value: "2286" }, - { label: "Неграфичные описания", value: "2855" }, - { label: "Недоверие", value: "1842" }, - { label: "Недопонимания", value: "614" }, - { label: "Недостатки внешности", value: "923" }, - { label: "Нежелательная беременность", value: "715" }, - { label: "Нежелательные сверхспособности", value: "1335" }, - { label: "Нежелательные чувства", value: "2078" }, - { label: "Нежный секс", value: "280" }, - { label: "Незащищенный секс", value: "497" }, - { label: "Нездоровые механизмы преодоления", value: "1528" }, - { label: "Нездоровые отношения", value: "71" }, - { label: "Нездоровый образ жизни", value: "2148" }, - { label: "Нездоровый BDSM", value: "1062" }, - { label: "Неидеальный омегаверс", value: "2712" }, - { label: "Неизвестность", value: "2826" }, - { label: "Неизвестные родственники", value: "2752" }, - { label: "Неизлечимые заболевания", value: "1269" }, - { label: "Нейтрализация сверхспособностей", value: "2343" }, - { label: "Некромаги", value: "679" }, - { label: "Некрофилия", value: "1663" }, - { label: "Нелинейное повествование", value: "1699" }, - { label: "Неловкий секс", value: "2883" }, - { label: "Неловкость", value: "2459" }, - { label: "Нелюбящие родители", value: "2817" }, - { label: "Немертвые", value: "495" }, - { label: "Немота", value: "414" }, - { label: "Ненависть", value: "685" }, - { label: "Ненависть к себе", value: "1441" }, - { label: "Ненависть с первого взгляда", value: "814" }, - { label: "Ненадежный рассказчик", value: "322" }, - { label: "Ненасильственная смерть", value: "2531" }, - { label: "Необитаемые острова", value: "1196" }, - { label: "Необратимые превращения", value: "926" }, - { label: "Неозвученные чувства", value: "258" }, - { label: "Неприятие отношений", value: "2056" }, - { label: "Неравные отношения", value: "545" }, - { label: "Неравный брак", value: "409" }, - { label: "Неразрывная связь", value: "2569" }, - { label: "Нервный срыв", value: "1190" }, - { label: "Нерды", value: "481" }, - { label: "Нереалистичность", value: "2350" }, - { label: "Несексуальная близость", value: "2874" }, - { label: "Нестандартная поэзия", value: "1704" }, - { label: "Несчастливые отношения", value: "2080" }, - { label: "Несчастливый финал", value: "61" }, - { label: "Несчастные случаи", value: "499" }, - { label: "Нетаймори", value: "2323" }, - { label: "Неторопливое повествование", value: "1815" }, - { label: "Нетрадиционное использование Силы", value: "2520" }, - { label: "Неумышленное употребление наркотических веществ", value: "1192" }, - { label: "Неуставные отношения", value: "1396" }, - { label: "Нефилимы", value: "1086" }, - { label: "Неформальный брак", value: "1355" }, - { label: "Нецензурная лексика", value: "1653" }, - { label: "Нечеловеческая мораль", value: "2551" }, - { label: "Нечеловеческие виды", value: "995" }, - { label: "Нечистая сила", value: "1464" }, - { label: "Нидерланды", value: "1141" }, - { label: "Низкая самооценка", value: "2014" }, - { label: "Низкое фэнтези", value: "2007" }, - { label: "Нимфы", value: "1477" }, - { label: "Ниндзя", value: "471" }, - { label: "Новая жизнь", value: "2018" }, - { label: "Новая Зеландия", value: "1789" }, - { label: "Новеллизация", value: "262" }, - { label: "Новые отношения", value: "1827" }, - { label: "Новый год", value: "206" }, - { label: "Новый канон ЗВ", value: "1948" }, - { label: "Норны", value: "2206" }, - { label: "Ностальгия", value: "878" }, - { label: "Ночное видение", value: "2006" }, - { label: "Ночные клубы", value: "531" }, - { label: "Нуар", value: "137" }, - { label: "Няни", value: "570" }, - { label: "Обездвиживание", value: "116" }, - { label: "Обещания / Клятвы", value: "1823" }, - { label: "Обиды", value: "905" }, - { label: "Обман / Заблуждение", value: "650" }, - { label: "Обмен квами", value: "2572" }, - { label: "Обмен ролями", value: "1055" }, - { label: "Обмен телами", value: "49" }, - { label: "Обмороки", value: "2484" }, - { label: "Обнажение", value: "970" }, - { label: "Оборотни", value: "28" }, - { label: "Обоснованный ООС", value: "2549" }, - { label: "Образ тела", value: "2289" }, - { label: "Обратный омегаверс", value: "2435" }, - { label: "Обретенные семьи", value: "601" }, - { label: "Обреченные отношения", value: "653" }, - { label: "Обскуры", value: "2237" }, - { label: "Обсуждение кинков", value: "1072" }, - { label: "Обусловленная контекстом гомофобия", value: "2685" }, - { label: "Обусловленный контекстом расизм", value: "2686" }, - { label: "Обусловленный контекстом сексизм", value: "2687" }, - { label: "Общежития", value: "534" }, - { label: "Общественный транспорт", value: "615" }, - { label: "Объективация", value: "1837" }, - { label: "Объектофилия", value: "366" }, - { label: "Овипозиция", value: "269" }, - { label: "Огнестрельное оружие", value: "1189" }, - { label: "Ограбления", value: "2269" }, - { label: "Одержимость", value: "2248" }, - { label: "Одержимые", value: "496" }, - { label: "Один день", value: "2647" }, - { label: "Одиночество", value: "1015" }, - { label: "Одичавшие дети", value: "835" }, - { label: "Однолюбы", value: "2146" }, - { label: "Одноминутный канонический персонаж", value: "2184" }, - { label: "Однополый мир", value: "1462" }, - { label: "Ожидание", value: "2909" }, - { label: "ОЖП", value: "1657" }, - { label: "Окаменение", value: "2052" }, - { label: "Океания", value: "1058" }, - { label: "Оккультизм", value: "2374" }, - { label: "ОКР", value: "594" }, - { label: "Окулолинктус", value: "1470" }, - { label: "Олимпийские игры", value: "1127" }, - { label: "Олфактофилия", value: "1945" }, - { label: "Омегаверс", value: "1693" }, - { label: "Омегаверс: Альфа/Альфа", value: "2437" }, - { label: "Омегаверс: Альфа/Бета", value: "2741" }, - { label: "Омегаверс: Альфа/Бета/Омега", value: "2898" }, - { label: "Омегаверс: Альфа/Омега/Альфа", value: "2436" }, - { label: "Омегаверс: Бета/Бета", value: "2966" }, - { label: "Омегаверс: Больше трех полов", value: "2782" }, - { label: "Омегаверс: Омега/Альфа/Омега", value: "2544" }, - { label: "Омегаверс: Омега/Бета", value: "2965" }, - { label: "Омегаверс: Омега/Омега", value: "2564" }, - { label: "Омегаверс: Смена второго пола", value: "2776" }, - { label: "Омегаверс: f!Альфа/m!Омега", value: "2777" }, - { label: "ОМП", value: "1656" }, - { label: "Онейрокинез", value: "2067" }, - { label: "Онкологические заболевания", value: "540" }, - { label: "ООС", value: "1648" }, - { label: "Опасность", value: "2847" }, - { label: "Опера", value: "549" }, - { label: "Опричнина", value: "2252" }, - { label: "Оптимизм", value: "2420" }, - { label: "Оракулы / Провидцы", value: "2221" }, - { label: "Оральная фиксация", value: "2523" }, - { label: "Оргазм без стимуляции", value: "1000" }, - { label: "Организаторы свадеб", value: "858" }, - { label: "Орден Феникса", value: "1907" }, - { label: "Орки", value: "187" }, - { label: "Оружие массового поражения", value: "1193" }, - { label: "Освоение земель", value: "1824" }, - { label: "Оседлание", value: "1223" }, - { label: "Османская империя", value: "527" }, - { label: "Особняки / Резиденции", value: "500" }, - { label: "Осознанные сновидения", value: "530" }, - { label: "Остеокинез", value: "2845" }, - { label: "Острова", value: "799" }, - { label: "От антигероя к герою", value: "2173" }, - { label: "От антигероя к злодею", value: "2213" }, - { label: "От божественного существа к смертному", value: "2664" }, - { label: "От возлюбленных к врагам", value: "783" }, - { label: "От возлюбленных к друзьям", value: "782" }, - { label: "От врагов к возлюбленным", value: "346" }, - { label: "От врагов к друзьям", value: "347" }, - { label: "От врагов к друзьям к возлюбленным", value: "345" }, - { label: "От героя к антигерою", value: "2546" }, - { label: "От героя к злодею", value: "1027" }, - { label: "От героя к монстру", value: "2659" }, - { label: "От героя к обычному персонажу", value: "2584" }, - { label: "От друзей к возлюбленным", value: "344" }, - { label: "От друзей к врагам", value: "463" }, - { label: "От друзей к незнакомцам", value: "1870" }, - { label: "От злодея к антигерою", value: "2640" }, - { label: "От злодея к герою", value: "1026" }, - { label: "От напарников к друзьям к возлюбленным", value: "2460" }, - { label: "От нездоровых отношений к здоровым", value: "2301" }, - { label: "От незнакомцев к возлюбленным", value: "372" }, - { label: "От простонародья к аристократу", value: "2816" }, - { label: "От сексуальных партнеров к возлюбленным", value: "2069" }, - { label: "От смертного к божественному существу", value: "2665" }, - { label: "От супругов к возлюбленным", value: "2359" }, - { label: "Ответвление от канона", value: "2336" }, - { label: "Ответственность", value: "2468" }, - { label: "Отдаю на вдохновение", value: "2288" }, - { label: "Отечественная война 1812 года", value: "1162" }, - { label: "Отказ от чувств", value: "2227" }, - { label: "Отклонения от канона", value: "5" }, - { label: "Открытый финал", value: "185" }, - { label: "ОтМетки", value: "2945" }, - { label: "Отношения втайне", value: "285" }, - { label: "Отношения на расстоянии", value: "37" }, - { label: "Отношения на спор", value: "383" }, - { label: "Отношения наполовину", value: "1573" }, - { label: "Отпуск", value: "1828" }, - { label: "Отрицание", value: "1868" }, - { label: "Отрицание чувств", value: "648" }, - { label: "Отрицательный протагонист", value: "1442" }, - { label: "Отсутствие души", value: "1897" }, - { label: "Отшельники", value: "543" }, - { label: "Офисы", value: "74" }, - { label: "Официанты / Официантки", value: "2560" }, - { label: "Охота", value: "825" }, - { label: "Охота на разумных существ", value: "2784" }, - { label: "Охотники", value: "2036" }, - { label: "Охотники за головами", value: "2103" }, - { label: "Охотники на нечисть", value: "46" }, - { label: "Охранники", value: "1173" }, - { label: "Ошибки", value: "2554" }, - { label: "Ошибочная идентификация", value: "2490" }, - { label: "Паладины", value: "1194" }, - { label: "Пан-персонажи", value: "302" }, - { label: "Пандемия COVID-19", value: "2724" }, - { label: "Панические атаки", value: "751" }, - { label: "Паническое расстройство", value: "733" }, - { label: "Панки", value: "625" }, - { label: "Паразиты", value: "1877" }, - { label: "Паралич", value: "1941" }, - { label: "Параллельные миры", value: "378" }, - { label: "Паранойя", value: "1253" }, - { label: "Пари", value: "778" }, - { label: "Парки / Зоны отдыха", value: "1005" }, - { label: "Парки аттракционов / Ярмарки", value: "793" }, - { label: "Паркур", value: "553" }, - { label: "Пародия", value: "1676" }, - { label: "Партизаны", value: "850" }, - { label: "Парфюмеры", value: "1384" }, - { label: "Пасха", value: "562" }, - { label: "Патологическое бесстрашие", value: "1498" }, - { label: "Патологическое накопительство", value: "2691" }, - { label: "Патологоанатомы", value: "502" }, - { label: "Патриархат", value: "705" }, - { label: "Патриотические темы и мотивы", value: "2231" }, - { label: "Патронусы", value: "2390" }, - { label: "Патрули", value: "1819" }, - { label: "Пафос", value: "2589" }, - { label: "Пацифисты", value: "1939" }, - { label: "Пегасы", value: "1047" }, - { label: "Пеггинг", value: "159" }, - { label: "Пейзажная лирика", value: "2330" }, - { label: "Пекарни", value: "78" }, - { label: "Первая мировая", value: "487" }, - { label: "Первая чеченская война", value: "2428" }, - { label: "Первобытные времена", value: "465" }, - { label: "Первое апреля", value: "2315" }, - { label: "Первопроходцы", value: "351" }, - { label: "Первый контакт", value: "2093" }, - { label: "Первый поцелуй", value: "2790" }, - { label: "Первый раз", value: "1292" }, - { label: "Передача магических способностей", value: "2497" }, - { label: "Переезд", value: "658" }, - { label: "Перезапуск мира", value: "2710" }, - { label: "Перемирие", value: "1331" }, - { label: "Переписки и чаты (стилизация)", value: "38" }, - { label: "Перерыв в отношениях", value: "519" }, - { label: "Переселение душ", value: "1237" }, - { label: "Перестрелки", value: "2268" }, - { label: "Переходный возраст", value: "1297" }, - { label: "Период Эдо", value: "1140" }, - { label: "Персекуторный бред", value: "2136" }, - { label: "Персонажи-геи", value: "2141" }, - { label: "Персонажи-лесбиянки", value: "2142" }, - { label: "Персонификация", value: "994" }, - { label: "Персонификация смерти", value: "1046" }, - { label: "Перу", value: "2017" }, - { label: "Перфекционизм", value: "2730" }, - { label: "Пессимизм", value: "2709" }, - { label: "Пет-плей", value: "391" }, - { label: "Петербург: бикейм #Чимин Сергеевич", value: "1859" }, - { label: "Петтинг", value: "301" }, - { label: "Пещеры", value: "655" }, - { label: "Пижамные вечеринки", value: "1340" }, - { label: "Пикацизм", value: "1227" }, - { label: "Пикники", value: "1165" }, - { label: "Пионеры", value: "668" }, - { label: "Пираты", value: "72" }, - { label: "Пирокинез", value: "1180" }, - { label: "Пиромания", value: "899" }, - { label: "Пирсинг", value: "507" }, - { label: "Писатели", value: "196" }, - { label: "Письма", value: "2298" }, - { label: "Письма (стилизация)", value: "266" }, - { label: "Пиццерии / Дайнеры", value: "593" }, - { label: "Плавание", value: "1135" }, - { label: "Плантокинез", value: "2029" }, - { label: "Пластические операции", value: "1847" }, - { label: "Платонические отношения", value: "385" }, - { label: "Племена", value: "1336" }, - { label: "Плен", value: "290" }, - { label: "Плененные путешествием", value: "2615" }, - { label: "Плохая компания", value: "1502" }, - { label: "Плохие друзья", value: "2445" }, - { label: "Плохой хороший финал", value: "1265" }, - { label: "Плюшефилия", value: "520" }, - { label: "Пляжи", value: "766" }, - { label: "По разные стороны", value: "897" }, - { label: "Побег", value: "661" }, - { label: "Побег из дома", value: "2309" }, - { label: "Победа Волдеморта", value: "1887" }, - { label: "Победа Гриндевальда", value: "2398" }, - { label: "Повара", value: "2853" }, - { label: "Повелитель смерти", value: "1909" }, - { label: "Повествование в будущем времени", value: "2633" }, - { label: "Повествование в настоящем времени", value: "1826" }, - { label: "Повествование во втором лице", value: "166" }, - { label: "Повествование от нескольких лиц", value: "1157" }, - { label: "Повествование от первого лица", value: "1681" }, - { label: "Повседневность", value: "1677" }, - { label: "Повстанцы", value: "659" }, - { label: "Повторение судьбы", value: "1997" }, - { label: "Поглощение способностей", value: "2605" }, - { label: "Погони / Преследования", value: "1996" }, - { label: "Пограничный синдром", value: "710" }, - { label: "Погребение заживо", value: "1279" }, - { label: "Под одной крышей", value: "462" }, - { label: "Под старину (стилизация)", value: "2486" }, - { label: "Подарки", value: "1109" }, - { label: "Подвешивание", value: "914" }, - { label: "Подводный мир", value: "901" }, - { label: "Подземелья", value: "2314" }, - { label: "Подземный мир", value: "1453" }, - { label: "Подлость", value: "2625" }, - { label: "Подменыши", value: "2250" }, - { label: "Подпольные бои", value: "1389" }, - { label: "Подразумеваемая смерть персонажа", value: "2204" }, - { label: "Подростки", value: "218" }, - { label: "Подростковая беременность", value: "986" }, - { label: "Подростковая влюбленность", value: "581" }, - { label: "Подсознание", value: "2273" }, - { label: "Поедание разумных существ", value: "1795" }, - { label: "Пожарные", value: "2557" }, - { label: "Пожилые персонажи", value: "852" }, - { label: "Поза 69", value: "699" }, - { label: "Поиск работы", value: "2646" }, - { label: "Поиск родителей", value: "2033" }, - { label: "Поклонение телу", value: "712" }, - { label: "Покровительство", value: "2223" }, - { label: "Покушение на жизнь", value: "1057" }, - { label: "Покушение на свободу", value: "2655" }, - { label: "Полиамория", value: "1705" }, - { label: "Полиандрия", value: "761" }, - { label: "Полигиния", value: "760" }, - { label: "Политика", value: "1835" }, - { label: "Политики", value: "711" }, - { label: "Политические интриги", value: "183" }, - { label: "Полицейские", value: "14" }, - { label: "Половой диморфизм", value: "1780" }, - { label: "Полубоги", value: "437" }, - { label: "Полудемоны", value: "2872" }, - { label: "Полудраконы", value: "2859" }, - { label: "Полукровки", value: "368" }, - { label: "Польша", value: "777" }, - { label: "Полярные станции", value: "1493" }, - { label: "Помощь врагу", value: "1325" }, - { label: "Попаданцы: В реальный мир", value: "1499" }, - { label: "Попаданцы: В своем теле", value: "1412" }, - { label: "Попаданцы: В чужом теле", value: "1413" }, - { label: "Попаданцы: Из одного фандома в другой", value: "2699" }, - { label: "Попаданцы: Из фандома в ориджинал", value: "2193" }, - { label: "Попаданцы: По обмену", value: "2196" }, - { label: "Попаданчество", value: "1695" }, - { label: "Популярность", value: "2581" }, - { label: "Попытка изнасилования", value: "1440" }, - { label: "Порноактеры", value: "289" }, - { label: "Португалия", value: "1749" }, - { label: "Порты", value: "1028" }, - { label: "Поселки", value: "1298" }, - { label: "После таймскипа (Haikyuu!!)", value: "2906" }, - { label: "После таймскипа (Shingeki no Kyojin)", value: "2903" }, - { label: "Послевоенное время", value: "1080" }, - { label: "Последний раз", value: "2645" }, - { label: "Последний рубеж", value: "1178" }, - { label: "Последний шанс", value: "1209" }, - { label: "Последствия", value: "2491" }, - { label: "Последствия болезни", value: "2612" }, - { label: "Послеродовая депрессия", value: "988" }, - { label: "Посмертная любовь", value: "2670" }, - { label: "Посмертный персонаж", value: "2653" }, - { label: "Пост-Пацифист", value: "2408" }, - { label: "Пост-Рейхенбах", value: "1903" }, - { label: "Пост-третий сезон (Шерлок)", value: "1977" }, - { label: "Постапокалиптика", value: "1697" }, - { label: "ПостВБ", value: "2386" }, - { label: "Постканон", value: "6" }, - { label: "ПостРагнарек", value: "2537" }, - { label: "ПостХог", value: "1883" }, - { label: "ПостЭндгейм", value: "2179" }, - { label: "Потеря девственности", value: "1234" }, - { label: "Потеря конечностей", value: "1289" }, - { label: "Потеря магических способностей", value: "1430" }, - { label: "Потеря памяти", value: "36" }, - { label: "Потеря сверхспособностей", value: "1051" }, - { label: "Поттергад", value: "2417" }, - { label: "Похищение", value: "224" }, - { label: "Похороны", value: "786" }, - { label: "Почтовые работники", value: "1821" }, - { label: "Пошлый юмор", value: "1362" }, - { label: "Поэма", value: "1938" }, - { label: "Поэты", value: "357" }, - { label: "Пояс верности", value: "1779" }, - { label: "Правда или действие", value: "1982" }, - { label: "Практопия", value: "2249" }, - { label: "Пре-гет", value: "209" }, - { label: "Пре-Рош", value: "2047" }, - { label: "Пре-слэш", value: "207" }, - { label: "Пре-фемслэш", value: "208" }, - { label: "Предательство", value: "361" }, - { label: "Предвидение", value: "1876" }, - { label: "Предложение руки и сердца", value: "1081" }, - { label: "Предопределенность", value: "411" }, - { label: "Предрассудки", value: "2046" }, - { label: "Предсмертные сообщения", value: "2174" }, - { label: "Преканон", value: "7" }, - { label: "ПреКербер", value: "2726" }, - { label: "Прекрасная эпоха", value: "1809" }, - { label: "Прелюдия", value: "2395" }, - { label: "Пренебрежение гигиеной", value: "2921" }, - { label: "Пренебрежение жизнью", value: "2216" }, - { label: "Преодоление комплексов", value: "588" }, - { label: "Преподаватели", value: "403" }, - { label: "Преподаватель/Обучающийся", value: "2507" }, - { label: "Преступники", value: "1446" }, - { label: "Преступный мир", value: "12" }, - { label: "Прибрежные города", value: "984" }, - { label: "Приватный танец", value: "2786" }, - { label: "Привязанность", value: "2372" }, - { label: "Пригород", value: "452" }, - { label: "Приемные семьи", value: "844" }, - { label: "Признания в любви", value: "1049" }, - { label: "Призраки", value: "26" }, - { label: "Приключения", value: "21" }, - { label: "Приметы / Суеверия", value: "612" }, - { label: "Примирение", value: "660" }, - { label: "Принудительная феминизация", value: "656" }, - { label: "Принудительное лечение", value: "971" }, - { label: "Принудительные отношения", value: "2302" }, - { label: "Принудительный брак", value: "307" }, - { label: "Принудительный инцест", value: "2942" }, - { label: "Принуждение", value: "1323" }, - { label: "Принятие себя", value: "2754" }, - { label: "Прислуга", value: "804" }, - { label: "Прист-кинк", value: "342" }, - { label: "Приступы агрессии", value: "2788" }, - { label: "Притча", value: "1088" }, - { label: "Приюты для животных", value: "2345" }, - { label: "ПРЛ", value: "1092" }, - { label: "Проблемы доверия", value: "2208" }, - { label: "Проблемы с законом", value: "1007" }, - { label: "Проводники душ", value: "2348" }, - { label: "Программисты", value: "574" }, - { label: "Прогрессорство", value: "449" }, - { label: "Прогулки", value: "2462" }, - { label: "Продажа души", value: "887" }, - { label: "Прозопагнозия", value: "2013" }, - { label: "Производственный роман", value: "2119" }, - { label: "Проклятия", value: "875" }, - { label: "Пролапс", value: "2926" }, - { label: "Промискуитет", value: "1817" }, - { label: "Промышленные предприятия", value: "1456" }, - { label: "Пропавшие без вести", value: "2073" }, - { label: "Пропущенная сцена", value: "1702" }, - { label: "Пророчества", value: "834" }, - { label: "Просмотр порно", value: "2341" }, - { label: "Проституция", value: "178" }, - { label: "Протезы", value: "1581" }, - { label: "Противоположности", value: "948" }, - { label: "Противоречивые чувства", value: "2528" }, - { label: "Прошлое", value: "983" }, - { label: "Прощение", value: "1545" }, - { label: "Псевдо-инцест", value: "336" }, - { label: "Псевдо-мистика", value: "2159" }, - { label: "Псевдоисторический сеттинг", value: "1479" }, - { label: "Псионика", value: "2023" }, - { label: "Психиатрические больницы", value: "135" }, - { label: "Психические расстройства", value: "69" }, - { label: "Психоз", value: "1339" }, - { label: "Психологи / Психоаналитики", value: "611" }, - { label: "Психологическая война", value: "2595" }, - { label: "Психологические пытки", value: "2825" }, - { label: "Психологические травмы", value: "40" }, - { label: "Психологический мазохизм", value: "2212" }, - { label: "Психологический ужас", value: "1261" }, - { label: "Психологическое насилие", value: "836" }, - { label: "Психология", value: "1674" }, - { label: "Психопатия", value: "869" }, - { label: "Психосоматические расстройства", value: "1834" }, - { label: "Психотерапия", value: "958" }, - { label: "ПТСР", value: "425" }, - { label: "Публичное обнажение", value: "319" }, - { label: "Пурпурная проза", value: "599" }, - { label: "Пустоши", value: "1114" }, - { label: "Пустыни", value: "692" }, - { label: "Путешественники-исследователи", value: "888" }, - { label: "Путешествия", value: "894" }, - { label: "Пытки", value: "210" }, - { label: "Пьеса", value: "434" }, - { label: "Рабочие", value: "1771" }, - { label: "Рабство", value: "51" }, - { label: "Равнодушие", value: "2516" }, - { label: "Равные отношения", value: "2728" }, - { label: "Радикальная медицина", value: "731" }, - { label: "Радио", value: "974" }, - { label: "Разбойники", value: "473" }, - { label: "Развитие отношений", value: "1404" }, - { label: "Развод", value: "200" }, - { label: "Разговоры", value: "1401" }, - { label: "Раздвоение личности", value: "287" }, - { label: "Раздельное обучение", value: "1959" }, - { label: "Разлука / Прощания", value: "1104" }, - { label: "Разница в возрасте", value: "2" }, - { label: "Разница культур", value: "1079" }, - { label: "Разновозрастная дружба", value: "2402" }, - { label: "Разнополая дружба", value: "806" }, - { label: "Разоблачения", value: "2684" }, - { label: "Разочарования", value: "646" }, - { label: "Разрушение стереотипов", value: "2805" }, - { label: "Разрушение четвертой стены", value: "381" }, - { label: "Разрыв связи", value: "2582" }, - { label: "Разум роя", value: "1459" }, - { label: "Разумное оружие", value: "2675" }, - { label: "Разумные животные", value: "895" }, - { label: "Разумные растения", value: "1037" }, - { label: "Раптофилия", value: "729" }, - { label: "Расизм", value: "433" }, - { label: "Раскрытие личностей", value: "2120" }, - { label: "Раскрытие магии", value: "1878" }, - { label: "Расплата", value: "2464" }, - { label: "Рассказ в рассказе", value: "2077" }, - { label: "Расставание", value: "506" }, - { label: "Расстройства аутистического спектра", value: "541" }, - { label: "Расстройства цветового восприятия", value: "1330" }, - { label: "Расстройства шизофренического спектра", value: "912" }, - { label: "Растаманы", value: "857" }, - { label: "Расширенная Вселенная ЗВ", value: "1950" }, - { label: "Реабилитационные центры", value: "1467" }, - { label: "Реактивное расстройство привязанности", value: "2576" }, - { label: "Реакции (стилизация)", value: "1283" }, - { label: "Реализм", value: "1201" }, - { label: "Реалити-шоу", value: "2312" }, - { label: "Ревность", value: "544" }, - { label: "Революции", value: "585" }, - { label: "Регенерация", value: "1530" }, - { label: "Регресс", value: "450" }, - { label: "Регрессия возраста", value: "102" }, - { label: "Редкие заболевания", value: "1856" }, - { label: "Реинкарнация", value: "101" }, - { label: "Рейтинг за лексику", value: "276" }, - { label: "Рейтинг за насилие и/или жестокость", value: "277" }, - { label: "Рейтинг за секс", value: "278" }, - { label: "Религиозная нетерпимость", value: "2451" }, - { label: "Религиозные войны", value: "2911" }, - { label: "Религиозные темы и мотивы", value: "198" }, - { label: "Религиозный фанатизм", value: "2548" }, - { label: "Ренессанс", value: "239" }, - { label: "Репетиторы", value: "2413" }, - { label: "Репетиции", value: "2241" }, - { label: "Репродуктивное насилие", value: "2310" }, - { label: "Реставраторы", value: "2239" }, - { label: "Рестораны", value: "412" }, - { label: "Ретеллинг", value: "52" }, - { label: "Ретрофутуризм", value: "727" }, - { label: "Реформация", value: "2199" }, - { label: "Речь Посполитая", value: "1529" }, - { label: "Риветхеды", value: "2904" }, - { label: "Римминг", value: "170" }, - { label: "Риск", value: "2467" }, - { label: "Рискованная беременность", value: "998" }, - { label: "Ритуалы", value: "749" }, - { label: "Ритуальные услуги", value: "2362" }, - { label: "Роботы", value: "513" }, - { label: "Родео", value: "1314" }, - { label: "Родители-одиночки", value: "325" }, - { label: "Родительские чувства", value: "2308" }, - { label: "Родомагия", value: "1899" }, - { label: "Роды", value: "991" }, - { label: "Рождество", value: "211" }, - { label: "Розыгрыши", value: "1807" }, - { label: "Рокеры", value: "861" }, - { label: "Роковая женщина / Роковой мужчина", value: "982" }, - { label: "Роковое усилие", value: "2604" }, - { label: "Роллер-дерби", value: "1014" }, - { label: "Романтизация", value: "1898" }, - { label: "Романтика", value: "1664" }, - { label: "Романтическая дружба", value: "2307" }, - { label: "Ромком", value: "2768" }, - { label: "Российская империя", value: "475" }, - { label: "Россия", value: "701" }, - { label: "РПП", value: "286" }, - { label: "Румыния", value: "862" }, - { label: "Русалки", value: "32" }, - { label: "Русальная неделя", value: "1067" }, - { label: "Русреал", value: "53" }, - { label: "Руферы", value: "578" }, - { label: "Рыбалка", value: "855" }, - { label: "Рыцари", value: "426" }, - { label: "Рыцарские турниры", value: "2759" }, - { label: "С чистого листа", value: "987" }, - { label: "Савойя", value: "1036" }, - { label: "Садизм / Мазохизм", value: "461" }, - { label: "Садовники", value: "1348" }, - { label: "Сайз-кинк", value: "100" }, - { label: "Самобытные культуры", value: "663" }, - { label: "Самовнушение", value: "2169" }, - { label: "Самовставка", value: "1689" }, - { label: "Самозванцы", value: "1811" }, - { label: "Самоистязание", value: "1386" }, - { label: "Самоопределение / Самопознание", value: "1278" }, - { label: "Самопожертвование", value: "750" }, - { label: "Самосуд", value: "1402" }, - { label: "Самоуничижение", value: "2667" }, - { label: "Самураи", value: "472" }, - { label: "Санатории", value: "223" }, - { label: "Сандалпанк", value: "1986" }, - { label: "Сарказм", value: "2225" }, - { label: "Сатира", value: "1944" }, - { label: "Сатиры", value: "264" }, - { label: "Сахарный диабет", value: "2609" }, - { label: "Сборник драбблов", value: "39" }, - { label: "Сборник лимериков", value: "1989" }, - { label: "Сборник пирожков", value: "743" }, - { label: "Сборник стихотворений", value: "600" }, - { label: "Сборник японской поэзии", value: "1351" }, - { label: "Свадьба", value: "184" }, - { label: "Сверхразумы", value: "966" }, - { label: "Сверхскорость", value: "1902" }, - { label: "Сверхспособности", value: "597" }, - { label: "Светлые властелины", value: "2596" }, - { label: "Светские мероприятия", value: "973" }, - { label: "Свидание вслепую", value: "741" }, - { label: "Свидания", value: "1235" }, - { label: "Свидетели", value: "1242" }, - { label: "Свингерство", value: "203" }, - { label: "Свобода", value: "2166" }, - { label: "Свободные отношения", value: "255" }, - { label: "Сводничество", value: "568" }, - { label: "Сводные родственники", value: "1445" }, - { label: "Святые", value: "393" }, - { label: "Сгенерировано ИИ", value: "3014" }, - { label: "СДВГ", value: "539" }, - { label: "Севвитус", value: "1871" }, - { label: "Северная Корея", value: "2767" }, - { label: "Северус Снейп жив", value: "2406" }, - { label: "Сегрегация", value: "2158" }, - { label: "Секретари", value: "1170" }, - { label: "Секретные миссии", value: "1829" }, - { label: "Секс без обязательств", value: "387" }, - { label: "Секс в воде", value: "718" }, - { label: "Секс в нетрезвом виде", value: "1110" }, - { label: "Секс в одежде", value: "1013" }, - { label: "Секс в публичных местах", value: "123" }, - { label: "Секс в транспорте", value: "124" }, - { label: "Секс в церкви", value: "2968" }, - { label: "Секс во время беременности", value: "279" }, - { label: "Секс во время болезни", value: "1365" }, - { label: "Секс во время менструации", value: "153" }, - { label: "Секс на камеру", value: "1101" }, - { label: "Секс на природе", value: "1256" }, - { label: "Секс перед зеркалом", value: "1255" }, - { label: "Секс по расчету", value: "1212" }, - { label: "Секс по телефону", value: "133" }, - { label: "Секс при посторонних", value: "2514" }, - { label: "Секс с воображаемыми партнерами", value: "2276" }, - { label: "Секс с использованием магии", value: "1563" }, - { label: "Секс с использованием одурманивающих веществ", value: "2635" }, - { label: "Секс с использованием посторонних предметов", value: "1661" }, - { label: "Секс с использованием сверхспособностей", value: "2610" }, - { label: "Секс со смертельным исходом", value: "1171" }, - { label: "Секс-игрушки", value: "176" }, - { label: "Секс-индустрия", value: "447" }, - { label: "Секс-клубы / Секс-вечеринки", value: "936" }, - { label: "Секс-куклы", value: "1202" }, - { label: "Секс-магия", value: "1131" }, - { label: "Секс-машины", value: "1112" }, - { label: "Секс-шопы", value: "739" }, - { label: "Сексизм", value: "464" }, - { label: "Сексомния", value: "2024" }, - { label: "Секстинг", value: "2215" }, - { label: "Сексуальная неопытность", value: "1717" }, - { label: "Сексуальное насилие", value: "2495" }, - { label: "Сексуальное обучение", value: "967" }, - { label: "Сексуальное рабство", value: "1353" }, - { label: "Сексуальные фобии", value: "1537" }, - { label: "Селфхарм", value: "76" }, - { label: "Селфцест", value: "1708" }, - { label: "Семейная сага", value: "920" }, - { label: "Семейные тайны", value: "779" }, - { label: "Семейный бизнес", value: "2338" }, - { label: "Семьи", value: "220" }, - { label: "Сенсорная депривация", value: "282" }, - { label: "Сенсорная перегрузка", value: "2351" }, - { label: "Сентиментальность", value: "2567" }, - { label: "Серая мораль", value: "839" }, - { label: "Серая мышь", value: "1250" }, - { label: "Серая реальность", value: "2278" }, - { label: "Серийные насильники", value: "1942" }, - { label: "Серийные убийцы", value: "193" }, - { label: "Серфинг", value: "764" }, - { label: "Сиамские близнецы", value: "1252" }, - { label: "Сибари", value: "131" }, - { label: "Сиблинги", value: "617" }, - { label: "Сидение на лице", value: "713" }, - { label: "Силкпанк", value: "1813" }, - { label: "Симбиоз", value: "849" }, - { label: "Символизм", value: "538" }, - { label: "Симфорофилия", value: "492" }, - { label: "Синдром восьмиклассника", value: "2032" }, - { label: "Синдром выжившего", value: "1100" }, - { label: "Синдром Дауна", value: "2106" }, - { label: "Синдром Котара", value: "2765" }, - { label: "Синдром Питера Пэна", value: "687" }, - { label: "Синдром Туретта", value: "946" }, - { label: "Синестезия", value: "960" }, - { label: "Сирены", value: "1830" }, - { label: "Сириус Блэк жив", value: "2617" }, - { label: "Сироты", value: "689" }, - { label: "Ситком", value: "2553" }, - { label: "Сказка", value: "288" }, - { label: "Скайпанк", value: "976" }, - { label: "Скандинавия", value: "781" }, - { label: "Скандинавская мифология", value: "2304" }, - { label: "Сквиртинг", value: "950" }, - { label: "Сквозное видение", value: "1280" }, - { label: "Скейтбординг", value: "1395" }, - { label: "Скелеты", value: "1766" }, - { label: "Скинхэды", value: "1757" }, - { label: "Скрытая беременность", value: "2580" }, - { label: "Скрытность", value: "2577" }, - { label: "Скрытые способности", value: "2579" }, - { label: "Скрытый Этаж", value: "2763" }, - { label: "Скульпторы", value: "2976" }, - { label: "Слатшейминг", value: "2910" }, - { label: "Следующее поколение", value: "698" }, - { label: "Слежка", value: "2102" }, - { label: "Слепота", value: "413" }, - { label: "Слияние", value: "2324" }, - { label: "Словакия", value: "2938" }, - { label: "Словения", value: "1788" }, - { label: "Сложные отношения", value: "326" }, - { label: "Слом личности", value: "623" }, - { label: "Слоуберн", value: "19" }, - { label: "Служебные животные", value: "1506" }, - { label: "Служебные отношения", value: "1347" }, - { label: "Служебный роман", value: "75" }, - { label: "Слухи / Сплетни", value: "2809" }, - { label: "Случайные убийства", value: "2673" }, - { label: "Случайный брак", value: "304" }, - { label: "Случайный поцелуй", value: "2852" }, - { label: "Случайный секс", value: "1275" }, - { label: "Смена имени", value: "2094" }, - { label: "Смена мировоззрения", value: "2865" }, - { label: "Смена сущности", value: "1707" }, - { label: "Смертельные заболевания", value: "1132" }, - { label: "Смерть антагониста", value: "2650" }, - { label: "Смерть всех персонажей", value: "2611" }, - { label: "Смерть второстепенных персонажей", value: "1713" }, - { label: "Смерть животных", value: "2210" }, - { label: "Смерть основных персонажей", value: "1647" }, - { label: "СМИ", value: "2656" }, - { label: "Смирение", value: "2272" }, - { label: "Смотрины", value: "1495" }, - { label: "Смутное время", value: "2002" }, - { label: "Снайперы", value: "1762" }, - { label: "Сновидения", value: "490" }, - { label: "Сноубординг", value: "2180" }, - { label: "Соблазнение / Ухаживания", value: "616" }, - { label: "Собственничество", value: "908" }, - { label: "Советпанк", value: "397" }, - { label: "Советский Союз", value: "468" }, - { label: "Совместная кровать", value: "1229" }, - { label: "Совместное купание", value: "1301" }, - { label: "Современное искусство", value: "1087" }, - { label: "Современность", value: "80" }, - { label: "Согласование с каноном", value: "1388" }, - { label: "Сожаления", value: "550" }, - { label: "Создание общества", value: "2643" }, - { label: "Созидание", value: "2590" }, - { label: "Сокровища", value: "893" }, - { label: "Сокрытие убийства", value: "2311" }, - { label: "Солянка из Сансов", value: "2515" }, - { label: "Сомелье / Бармены", value: "865" }, - { label: "Сомнамбулизм", value: "1394" }, - { label: "Сомнения", value: "1544" }, - { label: "Сомнофилия", value: "95" }, - { label: "Сонный паралич", value: "1203" }, - { label: "Соперничество", value: "671" }, - { label: "Сорванная свадьба", value: "2044" }, - { label: "Сострадание", value: "885" }, - { label: "Состязания", value: "910" }, - { label: "Состязания с участием животных", value: "1154" }, - { label: "Сотриголова — отец Хитоши Шинсо", value: "2877" }, - { label: "Соулмейты", value: "1696" }, - { label: "Соулмейты: Действия", value: "2575" }, - { label: "Соулмейты: Изменение восприятия", value: "1400" }, - { label: "Соулмейты: Контакт через кожу", value: "2608" }, - { label: "Соулмейты: Ментальная связь", value: "2841" }, - { label: "Соулмейты: Общая боль", value: "2583" }, - { label: "Соулмейты: Общие эмоции", value: "2637" }, - { label: "Соулмейты: Опознавательные фразы", value: "1399" }, - { label: "Соулмейты: Предметы", value: "2563" }, - { label: "Соулмейты: Предпочтения", value: "2521" }, - { label: "Соулмейты: Разноцветные глаза", value: "2461" }, - { label: "Соулмейты: Ранения", value: "2134" }, - { label: "Соулмейты: Сны", value: "2414" }, - { label: "Соулмейты: Тактильный контакт", value: "2190" }, - { label: "Соулмейты: Татуировки", value: "2711" }, - { label: "Соулмейты: Физические проявления", value: "2864" }, - { label: "Социальная фантастика", value: "591" }, - { label: "Социальные темы и мотивы", value: "1017" }, - { label: "Социальные эксперименты", value: "2756" }, - { label: "Социальный паразитизм", value: "2444" }, - { label: "Социофобия", value: "826" }, - { label: "Спарринги", value: "1575" }, - { label: "Спасатели", value: "392" }, - { label: "Спасение жизни", value: "1299" }, - { label: "Спасение мира", value: "812" }, - { label: "Спектрофилия", value: "2634" }, - { label: "Спецагенты", value: "401" }, - { label: "Спиритизм", value: "1478" }, - { label: "Сплошинг", value: "424" }, - { label: "Спонтанный секс", value: "2918" }, - { label: "Спорт", value: "15" }, - { label: "Спортзалы", value: "1359" }, - { label: "Сражения", value: "2868" }, - { label: "Средневековье", value: "238" }, - { label: "Ссоры / Конфликты", value: "645" }, - { label: "Стаи", value: "2884" }, - { label: "Сталкеры / Диггеры (субкультура)", value: "1029" }, - { label: "Сталкинг", value: "605" }, - { label: "Становление героя", value: "680" }, - { label: "Стёб", value: "1690" }, - { label: "Стереотипы", value: "809" }, - { label: "Стимпанк", value: "25" }, - { label: "Стимуляция руками", value: "225" }, - { label: "Стихи", value: "1692" }, - { label: "Стихийные бедствия", value: "1352" }, - { label: "Стихотворные вставки", value: "2404" }, - { label: "СтихоТворцы", value: "3015" }, - { label: "Стокгольмский синдром / Лимский синдром", value: "1217" }, - { label: "Столетняя война", value: "2243" }, - { label: "Сторонний рассказчик", value: "1383" }, - { label: "Стоунпанк", value: "1987" }, - { label: "Страдания", value: "2009" }, - { label: "Странногеддон", value: "1882" }, - { label: "Страны Балтии", value: "1031" }, - { label: "Страпоны", value: "380" }, - { label: "Страсть", value: "1955" }, - { label: "Страх перед родителями", value: "2332" }, - { label: "Стрельба из лука", value: "1791" }, - { label: "Стримеры / Ютуберы", value: "1473" }, - { label: "Стрип-клубы", value: "79" }, - { label: "Стрит-арт", value: "1012" }, - { label: "Строители", value: "1774" }, - { label: "Студенты", value: "274" }, - { label: "Студенты по обмену", value: "859" }, - { label: "Студенческие городки", value: "753" }, - { label: "Субкультуры", value: "1061" }, - { label: "Субординация", value: "1099" }, - { label: "Суд", value: "827" }, - { label: "Суккубы / Инкубы", value: "334" }, - { label: "Сумасшествие", value: "1239" }, - { label: "Сунагакуре", value: "2476" }, - { label: "Супергерои", value: "67" }, - { label: "Суперзлодеи", value: "1205" }, - { label: "Супермаркеты", value: "843" }, - { label: "Суррогатная беременность", value: "1427" }, - { label: "Сухой закон", value: "2100" }, - { label: "Сухой оргазм", value: "2818" }, - { label: "Сфера ИТ", value: "2534" }, - { label: "Сфинксы", value: "1349" }, - { label: "Сценарий (стилизация)", value: "323" }, - { label: "Счастливый финал", value: "3" }, - { label: "США", value: "640" }, - { label: "Съемочная площадка", value: "410" }, - { label: "Сын Тьмы", value: "2175" }, - { label: "Сэнгоку Джидай", value: "2380" }, - { label: "Сэнгоку Дзидай", value: "2834" }, - { label: "Сэцубун", value: "2969" }, - { label: "Сюрреализм / Фантасмагория", value: "872" }, - { label: "Т/И / Л/И", value: "167" }, - { label: "Таджикистан", value: "2974" }, - { label: "Таиланд", value: "1034" }, - { label: "Тайна происхождения", value: "662" }, - { label: "Тайная личность", value: "194" }, - { label: "Тайная сущность", value: "1512" }, - { label: "Тайны / Секреты", value: "906" }, - { label: "Тайные друзья (игра)", value: "2908" }, - { label: "Тайные организации", value: "572" }, - { label: "Тайные поклонники", value: "1374" }, - { label: "Тайный НапиСанта", value: "2972" }, - { label: "Таксисты", value: "2679" }, - { label: "Тактильный голод", value: "2197" }, - { label: "Тактильный контакт", value: "2875" }, - { label: "Танабата", value: "2340" }, - { label: "Танцы", value: "108" }, - { label: "Танцы на пилоне", value: "1370" }, - { label: "Таро", value: "642" }, - { label: "Тату-салоны", value: "324" }, - { label: "Татуировки", value: "327" }, - { label: "Твинцест", value: "1652" }, - { label: "Творческий кризис", value: "2107" }, - { label: "Театры", value: "195" }, - { label: "Телекинез", value: "951" }, - { label: "Телепатия", value: "127" }, - { label: "Телепортация", value: "1794" }, - { label: "Телесные жидкости", value: "1220" }, - { label: "Телесные наказания", value: "132" }, - { label: "Телесный хоррор", value: "212" }, - { label: "Телохранители", value: "400" }, - { label: "Темная сторона (Гарри Поттер)", value: "1937" }, - { label: "Темная сторона Силы", value: "1946" }, - { label: "Темная Эра (Bungou Stray Dogs)", value: "2494" }, - { label: "Темное прошлое", value: "2229" }, - { label: "Темное фэнтези", value: "1963" }, - { label: "Темные властелины", value: "1224" }, - { label: "Темные эльфы", value: "2597" }, - { label: "Темный романтизм", value: "2128" }, - { label: "Темы ментального здоровья", value: "2344" }, - { label: "Темы этики и морали", value: "1106" }, - { label: "Теннис", value: "1066" }, - { label: "Тентакли", value: "129" }, - { label: "Теория инверсии", value: "1980" }, - { label: "Теория Одурманивания", value: "2493" }, - { label: "Термокинез", value: "2793" }, - { label: "Террористы", value: "556" }, - { label: "Теслапанк", value: "1988" }, - { label: "Техники", value: "1272" }, - { label: "Техногенные катастрофы", value: "526" }, - { label: "Технокинез", value: "1271" }, - { label: "Технологии", value: "1232" }, - { label: "Технотриллер", value: "649" }, - { label: "Течка / Гон", value: "283" }, - { label: "Тим Дрейк — Джокер-младший", value: "2454" }, - { label: "Тим Дрейк — Кэтлэд", value: "2453" }, - { label: "Тихий секс", value: "1185" }, - { label: "Товарищи по несчастью", value: "2666" }, - { label: "Токсичные родственники", value: "2008" }, - { label: "Том Реддл — не Темный Лорд", value: "2394" }, - { label: "Томбои", value: "565" }, - { label: "Томгерлз", value: "848" }, - { label: "Торговля органами", value: "1358" }, - { label: "Торговцы", value: "2098" }, - { label: "Торговые центры", value: "1416" }, - { label: "Тоталитаризм", value: "1169" }, - { label: "Травники / Травницы", value: "1211" }, - { label: "Трагедия", value: "919" }, - { label: "Трагикомедия", value: "1454" }, - { label: "Традиции", value: "1463" }, - { label: "Трансгендерные персонажи", value: "175" }, - { label: "Трансплантация", value: "2364" }, - { label: "Трансфобия", value: "420" }, - { label: "Тревожное расстройство личности", value: "1914" }, - { label: "Тревожность", value: "1318" }, - { label: "Трейлеры", value: "785" }, - { label: "Тренировки / Обучение", value: "876" }, - { label: "Третий лишний", value: "1176" }, - { label: "Третья мировая", value: "624" }, - { label: "Три корейских государства", value: "2322" }, - { label: "Трикстеры", value: "1507" }, - { label: "Триллер", value: "169" }, - { label: "Трилогия Юникрона", value: "2378" }, - { label: "Трипофобия", value: "2342" }, - { label: "Трисам", value: "758" }, - { label: "Трихотилломания", value: "2185" }, - { label: "Тролли", value: "190" }, - { label: "Трудные отношения с родителями", value: "1490" }, - { label: "Трудный характер", value: "2123" }, - { label: "Трудоголизм", value: "1848" }, - { label: "Трущобы", value: "2822" }, - { label: "Туалетный юмор", value: "1247" }, - { label: "Туберкулез", value: "2163" }, - { label: "Турция", value: "794" }, - { label: "Тщеславие", value: "2578" }, - { label: "Тюрьмы / Темницы", value: "117" }, - { label: "Тяжелое детство", value: "2614" }, - { label: "Убийства", value: "621" }, - { label: "Убийственная пара", value: "1918" }, - { label: "Убийца поневоле", value: "2671" }, - { label: "Уважение", value: "2688" }, - { label: "Ужасы", value: "1679" }, - { label: "Узбекистан", value: "1293" }, - { label: "Уизлигады", value: "1862" }, - { label: "Украина", value: "716" }, - { label: "Укрытия", value: "199" }, - { label: "Уличные артисты", value: "2550" }, - { label: "Уличные гонки", value: "880" }, - { label: "Умбракинез", value: "2355" }, - { label: "Универсалы", value: "657" }, - { label: "Унижения", value: "1319" }, - { label: "Упоминания аддикций", value: "2848" }, - { label: "Упоминания алкоголя", value: "2415" }, - { label: "Упоминания беременности", value: "2057" }, - { label: "Упоминания войны", value: "2707" }, - { label: "Упоминания жестокости", value: "2447" }, - { label: "Упоминания зоофилии", value: "2620" }, - { label: "Упоминания измены", value: "2876" }, - { label: "Упоминания изнасилования", value: "521" }, - { label: "Упоминания инвалидности", value: "2705" }, - { label: "Упоминания инцеста", value: "2285" }, - { label: "Упоминания каннибализма", value: "2427" }, - { label: "Упоминания курения", value: "2450" }, - { label: "Упоминания мужской беременности", value: "2226" }, - { label: "Упоминания наркотиков", value: "144" }, - { label: "Упоминания насилия", value: "2207" }, - { label: "Упоминания нездоровых отношений", value: "2892" }, - { label: "Упоминания проституции", value: "2618" }, - { label: "Упоминания пыток", value: "2410" }, - { label: "Упоминания расизма", value: "2737" }, - { label: "Упоминания религии", value: "1048" }, - { label: "Упоминания самоубийства", value: "77" }, - { label: "Упоминания секса", value: "2747" }, - { label: "Упоминания селфхарма", value: "2105" }, - { label: "Упоминания смертей", value: "2081" }, - { label: "Упоминания смертей животных", value: "2211" }, - { label: "Упоминания телесного хоррора", value: "2466" }, - { label: "Упоминания терроризма", value: "774" }, - { label: "Упоминания убийств", value: "1501" }, - { label: "Управление оргазмом", value: "118" }, - { label: "Управление стихиями", value: "2091" }, - { label: "Уретральное проникновение", value: "1469" }, - { label: "Уро-кинк", value: "762" }, - { label: "Условное бессмертие", value: "2117" }, - { label: "Уся / Сянься", value: "2714" }, - { label: "Утопия", value: "350" }, - { label: "Утраченная тройка", value: "2535" }, - { label: "Утренний секс", value: "2882" }, - { label: "Уход за персонажами с инвалидностью", value: "2133" }, - { label: "Ухудшение отношений", value: "1219" }, - { label: "Учебные заведения", value: "1694" }, - { label: "Ученые", value: "402" }, - { label: "Уют", value: "2738" }, - { label: "Фавориты", value: "2135" }, - { label: "Фамильяры", value: "1045" }, - { label: "Фан-Бинго", value: "2957" }, - { label: "Фан-Фанты", value: "2953" }, - { label: "Фандомная аналитика", value: "742" }, - { label: "Фандомная Битва", value: "253" }, - { label: "Фансервис", value: "2483" }, - { label: "Фантастика", value: "1670" }, - { label: "Фастберн", value: "2016" }, - { label: "Фастфуд / Закусочные", value: "1025" }, - { label: "Феи", value: "191" }, - { label: "Фельчинг", value: "2368" }, - { label: "Фемдом", value: "54" }, - { label: "Феминистские темы и мотивы", value: "523" }, - { label: "Феминитивы", value: "390" }, - { label: "Фениксы", value: "1215" }, - { label: "Феодализм", value: "1504" }, - { label: "Фермы / Ранчо", value: "329" }, - { label: "Фестивали", value: "1006" }, - { label: "Фехтование", value: "1775" }, - { label: "Фиггинг", value: "1439" }, - { label: "Фигурное катание", value: "460" }, - { label: "Физиотерапия", value: "2222" }, - { label: "Физическая сверхсила", value: "2880" }, - { label: "Фикрайтеры / Фикридеры", value: "1284" }, - { label: "Фиксированная раскладка", value: "1344" }, - { label: "Фиктивные отношения", value: "308" }, - { label: "Фиктивный брак", value: "34" }, - { label: "Философия", value: "1675" }, - { label: "Финляндия", value: "1133" }, - { label: "Фистинг", value: "151" }, - { label: "Флафф", value: "1667" }, - { label: "Флирт", value: "349" }, - { label: "Флот", value: "846" }, - { label: "Фобии", value: "977" }, - { label: "Фольклор и предания", value: "1273" }, - { label: "Форнифилия", value: "580" }, - { label: "Фотографы", value: "348" }, - { label: "Фотокинез", value: "1805" }, - { label: "Франция", value: "691" }, - { label: "Фред Уизли жив", value: "2941" }, - { label: "Фроттаж", value: "96" }, - { label: "Фуд-плей", value: "509" }, - { label: "Фуд-фетиш", value: "2168" }, - { label: "Фурри", value: "996" }, - { label: "Фут-фетиш", value: "432" }, - { label: "Футбол", value: "458" }, - { label: "Фэйри", value: "2028" }, - { label: "Фэнтези", value: "1669" }, - { label: "Хакеры", value: "340" }, - { label: "Ханами", value: "1155" }, - { label: "Ханахаки", value: "45" }, - { label: "Ханжество", value: "882" }, - { label: "Характерная для канона жестокость", value: "896" }, - { label: "Харассмент", value: "477" }, - { label: "Хиерофилия", value: "2366" }, - { label: "Химеры", value: "816" }, - { label: "Химическое оружие", value: "1186" }, - { label: "Химэдэрэ / Оджидэрэ", value: "1363" }, - { label: "Хип-хоп (субкультура)", value: "1822" }, - { label: "Хиппи", value: "784" }, - { label: "Хиромантия", value: "2749" }, - { label: "Хирургическая коррекция пола", value: "1249" }, - { label: "Хирургические операции", value: "1845" }, - { label: "Хобби", value: "1161" }, - { label: "Хогсмид", value: "2388" }, - { label: "Хоккей", value: "828" }, - { label: "Холодная война", value: "2140" }, - { label: "Холодное оружие", value: "1187" }, - { label: "Хорватия", value: "1433" }, - { label: "Хороший плохой финал", value: "1266" }, - { label: "Хронические заболевания", value: "1267" }, - { label: "Хронокинез", value: "1447" }, - { label: "Хронофантастика", value: "56" }, - { label: "Хтонические существа", value: "504" }, - { label: "Художники", value: "140" }, - { label: "Хуманизация", value: "177" }, - { label: "Хэллоуин", value: "205" }, - { label: "Хюльдры", value: "1397" }, - { label: "Цветочные магазины", value: "164" }, - { label: "Целители", value: "1107" }, - { label: "Церкви", value: "533" }, - { label: "Цикличность", value: "1979" }, - { label: "Цирки", value: "259" }, - { label: "Цукими", value: "1798" }, - { label: "Цундэрэ", value: "1137" }, - { label: "Цыгане", value: "1091" }, - { label: "Чайлдфри", value: "592" }, - { label: "Чайные церемонии", value: "2416" }, - { label: "Частичный ООС", value: "1409" }, - { label: "Частные детективы", value: "427" }, - { label: "Черная археология", value: "2503" }, - { label: "Черная мораль", value: "2438" }, - { label: "Чернобыльская катастрофа", value: "775" }, - { label: "Черный юмор", value: "66" }, - { label: "Чехия", value: "1021" }, - { label: "Чиновники", value: "1991" }, - { label: "Чирлидинг", value: "1431" }, - { label: "Чистокровные AU (Гарри Поттер)", value: "1905" }, - { label: "Чосон", value: "571" }, - { label: "Чувство вины", value: "902" }, - { label: "Чумные доктора", value: "1125" }, - { label: "Чхусок", value: "2973" }, - { label: "Шаманы", value: "484" }, - { label: "Шантаж", value: "569" }, - { label: "Шармбатон", value: "2733" }, - { label: "Швейцария", value: "1022" }, - { label: "Шейпшифтеры", value: "29" }, - { label: "Шелки", value: "2431" }, - { label: "Шифры", value: "2232" }, - { label: "Школьная иерархия", value: "2787" }, - { label: "Школьники", value: "2566" }, - { label: "Школьные годы Тома Реддла", value: "1913" }, - { label: "Школьные ОтМетки", value: "3017" }, - { label: "Школьные поездки", value: "2481" }, - { label: "Школьный роман", value: "2440" }, - { label: "Шопинг", value: "864" }, - { label: "Шоу-бизнес", value: "17" }, - { label: "Шпионы", value: "148" }, - { label: "Шрамирование", value: "891" }, - { label: "Шрамы", value: "328" }, - { label: "Эвтаназия", value: "1102" }, - { label: "Эгоизм", value: "2071" }, - { label: "Эдвардианская эпоха", value: "2975" }, - { label: "Эзотерические темы и мотивы", value: "2801" }, - { label: "Эйблизм", value: "453" }, - { label: "Эйдетическая память", value: "975" }, - { label: "Эйфория", value: "2458" }, - { label: "Экзамены", value: "2591" }, - { label: "Экзистенциальный кризис", value: "1200" }, - { label: "Экзорцизм", value: "494" }, - { label: "Экологи / Биологи", value: "631" }, - { label: "Экологический кризис", value: "485" }, - { label: "Экономический кризис", value: "1547" }, - { label: "Эксаудиризм", value: "2151" }, - { label: "Эксгибиционизм", value: "489" }, - { label: "Эксперимент", value: "1686" }, - { label: "Экстрасенсы", value: "371" }, - { label: "Экшн", value: "1673" }, - { label: "Электрокинез", value: "1857" }, - { label: "Электростимуляция", value: "807" }, - { label: "Элементы ангста", value: "2696" }, - { label: "Элементы гета", value: "1714" }, - { label: "Элементы дарка", value: "2703" }, - { label: "Элементы детектива", value: "2698" }, - { label: "Элементы драмы", value: "2697" }, - { label: "Элементы других видов отношений", value: "454" }, - { label: "Элементы мистики", value: "1324" }, - { label: "Элементы психологии", value: "2824" }, - { label: "Элементы романтики", value: "2742" }, - { label: "Элементы слэша", value: "1715" }, - { label: "Элементы ужасов", value: "2721" }, - { label: "Элементы фемслэша", value: "1716" }, - { label: "Элементы флаффа", value: "2731" }, - { label: "Элементы юмора / Элементы стёба", value: "2011" }, - { label: "Эльфы", value: "44" }, - { label: "Эметофилия", value: "1434" }, - { label: "Эмо", value: "641" }, - { label: "Эмоциональная одержимость", value: "1270" }, - { label: "Эмпатия", value: "1177" }, - { label: "Энергетический вампиризм", value: "818" }, - { label: "Эпидемии", value: "619" }, - { label: "Эпизод 23.5", value: "2469" }, - { label: "Эпилепсия", value: "1511" }, - { label: "Эпилог? Какой эпилог?", value: "2651" }, - { label: "Эпоха Бакумацу", value: "1472" }, - { label: "Эпоха Мэйдзи", value: "1435" }, - { label: "Эпоха Тайсё", value: "2015" }, - { label: "Эрик Киллмонгер жив", value: "2897" }, - { label: "Эрогуро", value: "1731" }, - { label: "Эротическая дрессировка", value: "2026" }, - { label: "Эротическая лактация", value: "284" }, - { label: "Эротическая мумификация", value: "415" }, - { label: "Эротическая сверхстимуляция", value: "1245" }, - { label: "Эротические наказания", value: "125" }, - { label: "Эротические ролевые игры", value: "111" }, - { label: "Эротические сны", value: "128" }, - { label: "Эротические фантазии", value: "561" }, - { label: "Эротический массаж", value: "1518" }, - { label: "Эротический перенос", value: "744" }, - { label: "Эротомания", value: "2922" }, - { label: "Эскапизм", value: "1552" }, - { label: "Эскорты", value: "922" }, - { label: "Эстетика", value: "2021" }, - { label: "ЭТЕРНА", value: "2948" }, - { label: "Этнические анклавы", value: "2732" }, - { label: "Этническое фэнтези", value: "667" }, - { label: "Эффект бабочки", value: "1572" }, - { label: "Ювелиры", value: "2964" }, - { label: "Южная Америка", value: "832" }, - { label: "Южная готика", value: "1077" }, - { label: "Южная Корея", value: "674" }, - { label: "Юмор", value: "1666" }, - { label: "Юристы", value: "333" }, - { label: "Я никогда не... (игра)", value: "1436" }, - { label: "Явное согласие", value: "1569" }, - { label: "Ядерная война", value: "889" }, - { label: "Язык жестов", value: "2962" }, - { label: "Язык животных", value: "1327" }, - { label: "Язык растений", value: "2622" }, - { label: "Язык цветов", value: "272" }, - { label: "Ямадад", value: "2916" }, - { label: "Янгирэ", value: "1257" }, - { label: "Яндэрэ", value: "1136" }, - { label: "Яогуаи", value: "2347" }, - { label: "Япония", value: "665" }, - { label: "Японские фестивали", value: "2555" }, - { label: "Ящеролюди", value: "682" }, - { label: "Abuse/Comfort", value: "2599" }, - { label: "Aged down", value: "229" }, - { label: "Aged up", value: "4" }, - { label: "Artfic", value: "68" }, - { label: "AU", value: "1683" }, - { label: "AU: Альтернативные способности", value: "2086" }, - { label: "AU: Без апокалипсиса", value: "2051" }, - { label: "AU: Без войны", value: "2305" }, - { label: "AU: Без магии", value: "1052" }, - { label: "AU: Без мистики", value: "2489" }, - { label: "AU: Без сверхспособностей", value: "1053" }, - { label: "AU: Без супергероев", value: "1152" }, - { label: "AU: Все гражданские", value: "2797" }, - { label: "AU: Все друзья", value: "2552" }, - { label: "AU: Все люди", value: "1151" }, - { label: "AU: Все хорошо", value: "2124" }, - { label: "AU: Другая страна", value: "2165" }, - { label: "AU: Другая эпоха", value: "2452" }, - { label: "AU: Другое детство", value: "2360" }, - { label: "AU: Другое знакомство", value: "2217" }, - { label: "AU: Другое семейное положение", value: "2902" }, - { label: "AU: Не родственники", value: "2167" }, - { label: "AU: Профессиональные герои (BNHA)", value: "2924" }, - { label: "AU: Родственники", value: "2164" }, - { label: "AU: Школа", value: "2532" }, - { label: "AU: Age swap", value: "917" }, - { label: "AU: Kitty Gang", value: "2928" }, - { label: "AU: Race swap", value: "933" }, - { label: "AU: Reverse", value: "1156" }, - { label: "AU: Role swap", value: "1054" }, - { label: "Authors Time", value: "2959" }, - { label: "BDSM", value: "1646" }, - { label: "BDSM-вселенная", value: "1078" }, - { label: "BDSM: Домспейс", value: "1444" }, - { label: "BDSM: Дроп", value: "1411" }, - { label: "BDSM: Сабспейс", value: "1410" }, - { label: "BDSM: Aftercare", value: "1523" }, - { label: "BEAST (Bungou Stray Dogs)", value: "2457" }, - { label: "Blackwatch", value: "2116" }, - { label: "Body inflation", value: "389" }, - { label: "Character study", value: "363" }, - { label: "Cockwarming", value: "318" }, - { label: "Dark academia", value: "2430" }, - { label: "DDLG / DDLB / MDLG / MDLB", value: "1033" }, - { label: "Dirty talk", value: "139" }, - { label: "Doggy style", value: "1361" }, - { label: "Dollification", value: "2780" }, - { label: "Dramione Authors Tournament", value: "2950" }, - { label: "EIQ", value: "271" }, - { label: "ER", value: "1687" }, - { label: "Final Haikyuu Quest", value: "2652" }, - { label: "Fix-it", value: "1" }, - { label: "Frenemies", value: "666" }, - { label: "Gangbang", value: "315" }, - { label: "Glory hole", value: "2188" }, - { label: "Golden Age Overwatch", value: "2112" }, - { label: "GOLockdown", value: "2391" }, - { label: "Gun play", value: "252" }, - { label: "Hurt/Comfort", value: "1682" }, - { label: "Hydra!AU", value: "1931" }, - { label: "Ironqrow week", value: "2209" }, - { label: "Kimetsu Gakuen", value: "2574" }, - { label: "Knife play", value: "152" }, - { label: "KPOP-AUFEST", value: "1851" }, - { label: "LARP", value: "1510" }, - { label: "LOVE ОтМетки", value: "2981" }, - { label: "Maleval week", value: "2382" }, - { label: "Marvel ГФИ", value: "2896" }, - { label: "Mate or die", value: "375" }, - { label: "Mirror!verse (Звездный путь)", value: "2899" }, - { label: "Moresome", value: "759" }, - { label: "N раз", value: "263" }, - { label: "Omnic Crisis Era", value: "2111" }, - { label: "ORIGINAL'S 2021", value: "2939" }, - { label: "Parentlock", value: "2053" }, - { label: "Patronus of Dramione", value: "2958" }, - { label: "Post-Fall of Overwatch", value: "2114" }, - { label: "Post-Recall", value: "2115" }, - { label: "PWP", value: "1680" }, - { label: "Rape/Revenge", value: "2195" }, - { label: "Reverse Falls", value: "1879" }, - { label: "RofR ДраМиВояж", value: "2954" }, - { label: "RST", value: "815" }, - { label: "RTFM", value: "2327" }, - { label: "Service top / Power bottom", value: "563" }, - { label: "Shingeki no Kyojin High School AU", value: "2037" }, - { label: "Shingeki! Kyojin Chuugakkou", value: "2038" }, - { label: "Sickfic", value: "422" }, - { label: "Songfic", value: "1684" }, - { label: "Spider-son & Iron-dad", value: "1885" }, - { label: "Sugar daddy", value: "106" }, - { label: "Sugar mommy", value: "160" }, - { label: "Superfamily (Мстители)", value: "2623" }, - { label: "Superior Iron Man", value: "2676" }, - { label: "Talon (Overwatch)", value: "2110" }, - { label: "The Big Four / The Super Six", value: "1954" }, - { label: "Trench AU", value: "2387" }, - { label: "TYL", value: "1933" }, - { label: "Underage", value: "1658" }, - { label: "URT", value: "2761" }, - { label: "UST", value: "1709" }, - { label: "Versefic", value: "2729" }, - { label: "Videofic", value: "2480" }, - { label: "Womance", value: "265" }, - { label: "X век", value: "240" }, - { label: "XI век", value: "241" }, - { label: "XII век", value: "242" }, - { label: "XIII век", value: "243" }, - { label: "XIV век", value: "244" }, - { label: "XIX век", value: "249" }, - { label: "XV век", value: "245" }, - { label: "XVI век", value: "246" }, - { label: "XVII век", value: "247" }, - { label: "XVIII век", value: "248" }, - { label: "XX век", value: "1004" }, - ], - type: FilterTypes.Picker, - }, - } satisfies Filters; -} - -export default new ficbook(); - -interface Data { - data?: DataEntity[] | null; - more: boolean; -} -interface DataEntity { - id: string; - slug: string; - author_id: number; - user_slug: string; - author_username: string; - real_author_id: string | null; - direction: number; - part_cnt: number; - title: string; - description: string; - enable_marks: boolean; - marks_plus: number; - is_premium: boolean; - status: number; - last_updated: string; - readedDate?: string | null; - reward_cnt: number; - cover?: string | null; -} +import { Plugin } from "@typings/plugin"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { NovelStatus } from "@libs/novelStatus"; +import { load as parseHTML } from "cheerio"; +import { defaultCover } from "@libs/defaultCover"; + +class ficbook implements Plugin.PluginBase { + id = "ficbook"; + name = "ficbook"; + site = "https://ficbook.net"; + version = "1.0.0"; + icon = "src/ru/ficbook/icon.png"; + userAgent = ""; + + async popularNovels( + pageNo: number, + { showLatestNovels, filters }: Plugin.PopularNovelsOptions, + ): Promise { + const baseUrl = this.site; + let url = baseUrl; + + if (filters?.directions?.value) { + url += "/popular-fanfics/" + filters.directions.value; + } else if (filters?.tags?.value) { + url += "/tags/" + filters.tags.value + "?p=" + pageNo; + } else { + url += "/" + (filters?.sort?.value || "fanfiction") + "?p=" + pageNo; + } + + const result = await fetchApi(url); + const body = await result.text(); + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + loadedCheerio("article.fanfic-inline").each(function () { + const name = loadedCheerio(this).find("h3 > a").text().trim(); + let cover = loadedCheerio(this).find("picture > img").attr("src"); + const url = loadedCheerio(this).find("h3 > a").attr("href"); + + cover = cover + ? cover.replace(/covers\/m_|covers\/d_/g, "covers/") + : defaultCover; + if (!name || !url) return; + + novels.push({ name, cover, url: baseUrl + url.replace(/\?.*/g, "") }); + }); + + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const result = await fetchApi(novelUrl); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + const novel: Plugin.SourceNovel = { + url: novelUrl, + }; + + novel.name = ( + loadedCheerio('h1[itemprop="headline"]').text() || + loadedCheerio('h1[itemprop="name"]').text() + ).trim(); + + novel.cover = loadedCheerio('meta[property="og:image"]').attr("content"); + novel.summary = loadedCheerio('div[itemprop="description"]').text().trim(); + novel.author = loadedCheerio('a[itemprop="author"]').text(); + + novel.status = + loadedCheerio( + "div.fanfic-main-info > section:nth-child(3) > div:nth-child(3) > span:nth-child(2)", + ).text() === "В процессе" + ? NovelStatus.Ongoing + : NovelStatus.Completed; + + const tags = loadedCheerio('div[class="tags"] > a') + .map((index, element) => loadedCheerio(element).text()) + .get(); + if (tags) { + novel.genres = tags.join(","); + } + + if (!novel.cover || novel.cover?.includes("/design/")) { + novel.cover = defaultCover; + } else { + novel.cover = novel.cover.replace(/covers\/m_|covers\/d_/g, "covers/"); + } + + const chapters: Plugin.ChapterItem[] = []; + const baseUrl = this.site; + + if (loadedCheerio("#content").length == 1) { + const name = loadedCheerio(".title-area > h2").text(); + const releaseTime = loadedCheerio(".part-date > span").attr("title"); + + if (name) chapters.push({ name, releaseTime, url: novelUrl }); + } else { + loadedCheerio("li.part").each(function () { + const name = loadedCheerio(this).find("h3").text(); + const releaseTime = loadedCheerio(this) + .find("div > span") + .attr("title"); + const url = loadedCheerio(this).find("a:nth-child(1)").attr("href"); + if (!name || !url) return; + + chapters.push({ name, releaseTime, url: baseUrl + url }); + }); + } + + novel.chapters = chapters; + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const result = await fetchApi(chapterUrl); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + let chapterText = ""; + + loadedCheerio("#content") + .html() + ?.split("\n") + ?.forEach((line) => { + if (line.trim()) { + //blank line skip + if (line.includes("

")) { + //new paragraph + chapterText += line; + } else { + //plaintext + chapterText += "

" + line + "

"; + } + } + }); + + return chapterText; + } + + async searchNovels( + searchTerm: string, + pageNo: number | undefined = 1, + ): Promise { + const formData = new FormData(); + formData.append("term", searchTerm); + formData.append("page", pageNo.toString()); + + const result = await fetchApi(this.site + "/search/fanfic", { + method: "POST", + body: formData, + }); + const json = (await result.json()) as { data: Data }; + const novels: Plugin.NovelItem[] = []; + + json.data?.data?.forEach((novel) => { + const name = novel.title.trim(); + const url = this.site + "/readfic/" + novel.slug; + const cover = novel.cover + ? "https://images.ficbook.net/fanfic-covers/" + novel.cover + : defaultCover; + + novels.push({ name, cover, url }); + }); + + return novels; + } + fetchImage = fetchFile; + + filters = { + sort: { + label: "Сортировка:", + value: "", + options: [ + { label: "Горячие работы", value: "fanfiction" }, + { label: "Популярные ", value: "popular-fanfics" }, + ], + type: FilterTypes.Picker, + }, + directions: { + label: "Направление:", + value: "", + options: [ + { label: "Все", value: "" }, + { label: "Джен", value: "gen" }, + { label: "Гет", value: "het" }, + { label: "Слэш", value: "slash-fics-3712917" }, + { label: "Фемслэш", value: "femslash-fanfics-9374932" }, + { label: "Статьи", value: "article" }, + { label: "Смешанный", value: "mixed" }, + { label: "Другой", value: "other" }, + ], + type: FilterTypes.Picker, + }, + tags: { + label: "Тэги:", + value: "", + options: [ + { label: "Все", value: "" }, + { label: "#1fic1week", value: "884" }, + { label: "#7daystowrite", value: "2961" }, + { label: "#Бинго_ТФ", value: "1458" }, + { label: "#миниатюры3days", value: "2118" }, + { label: "#Не_Тайный_Санта", value: "2760" }, + { label: "#Писательские фанты-блиц", value: "2613" }, + { label: "#cosmowrite", value: "2284" }, + { label: "#femKHRweek", value: "2658" }, + { label: "#FourShips", value: "2588" }, + { label: "#Goretober", value: "1861" }, + { label: "#InternationalVampireDay / #WorldDraculaDay", value: "2048" }, + { label: "#KattWeek", value: "2717" }, + { label: "#Kinktober", value: "1867" }, + { label: "#LotorWeek", value: "2657" }, + { label: "#MerMay", value: "1122" }, + { label: "#NaNoWriMo", value: "1852" }, + { label: "#nsfweruriweek", value: "2944" }, + { label: "#RCMiaChallenge", value: "2700" }, + { label: "#SheithMonth", value: "2432" }, + { label: "#SixDrabbles", value: "2293" }, + { label: "#Writober", value: "1831" }, + { label: "1900-е годы", value: "369" }, + { label: "1910-е годы", value: "213" }, + { label: "1920-е годы", value: "81" }, + { label: "1930-е годы", value: "82" }, + { label: "1940-е годы", value: "83" }, + { label: "1950-е годы", value: "84" }, + { label: "1960-е годы", value: "85" }, + { label: "1970-е годы", value: "86" }, + { label: "1980-е годы", value: "87" }, + { label: "1990-е годы", value: "88" }, + { label: "2000-е годы", value: "590" }, + { label: "2010-е годы", value: "2562" }, + { label: "2014 год", value: "2977" }, + { label: "2P", value: "1910" }, + { label: "9 мая", value: "1019" }, + { label: "Аборт / Выкидыш", value: "306" }, + { label: "Аватары", value: "2401" }, + { label: "Авиатранспорт", value: "962" }, + { label: "Авиация", value: "455" }, + { label: "Аврорат", value: "1906" }, + { label: "Австралия", value: "767" }, + { label: "Австрия", value: "1024" }, + { label: "Автомастерские", value: "788" }, + { label: "Авторская пунктуация", value: "2886" }, + { label: "Авторские неологизмы", value: "1123" }, + { label: "Автоспорт", value: "856" }, + { label: "Автостоп", value: "1429" }, + { label: "Аддикции", value: "1230" }, + { label: "Адреналиновая зависимость", value: "1480" }, + { label: "Азартные игры", value: "1009" }, + { label: "Азкабан", value: "1922" }, + { label: "Аквакинез", value: "1380" }, + { label: "Аквапарки", value: "789" }, + { label: "Акростих", value: "911" }, + { label: "Актеры", value: "408" }, + { label: "Акустикофилия", value: "903" }, + { label: "Алекситимия", value: "2020" }, + { label: "Алкоголь", value: "201" }, + { label: "Алкогольные игры", value: "1534" }, + { label: "Аллергии", value: "746" }, + { label: "Алхимия", value: "1174" }, + { label: "Альбинизм", value: "1213" }, + { label: "Альпинизм", value: "1448" }, + { label: "Альтернативная мировая история", value: "442" }, + { label: "Альтернативное размножение", value: "2751" }, + { label: "Альтернативные судьбы", value: "2839" }, + { label: "Амазонки", value: "1424" }, + { label: "Амегакуре", value: "2571" }, + { label: "Американский футбол", value: "808" }, + { label: "Ампутация", value: "704" }, + { label: "Амфибии", value: "2792" }, + { label: "Анальгезия", value: "1254" }, + { label: "Анальный оргазм", value: "999" }, + { label: "Анальный секс", value: "215" }, + { label: "Анархия", value: "1175" }, + { label: "Анахронизмы", value: "780" }, + { label: "Ангелы", value: "91" }, + { label: "Ангелы-хранители", value: "1044" }, + { label: "Ангст", value: "1665" }, + { label: "Андрогинная внешность", value: "2602" }, + { label: "Андроиды", value: "155" }, + { label: "Анимагия", value: "2519" }, + { label: "Анимализм", value: "1443" }, + { label: "Аномальные зоны", value: "822" }, + { label: "Анонимный секс", value: "112" }, + { label: "Анрождение", value: "1246" }, + { label: "Антарктида", value: "2205" }, + { label: "Анти-Сью (Анти-Стью)", value: "1150" }, + { label: "Антигерои", value: "418" }, + { label: "Антизлодеи", value: "1833" }, + { label: "Антикварные магазины", value: "1496" }, + { label: "Антисоциальное расстройство личности", value: "734" }, + { label: "Антиутопия", value: "1700" }, + { label: "Античность", value: "237" }, + { label: "Апатия", value: "1850" }, + { label: "Аптеки", value: "1240" }, + { label: "Аргентина", value: "1146" }, + { label: "Арены", value: "940" }, + { label: "Аристократия", value: "16" }, + { label: "Арктика", value: "2426" }, + { label: "Армия", value: "22" }, + { label: "Аромантичные персонажи", value: "339" }, + { label: "Артефакты", value: "924" }, + { label: "Археологи", value: "405" }, + { label: "Архивы", value: "2594" }, + { label: "Архитекторы", value: "1011" }, + { label: "Асексофобия", value: "1120" }, + { label: "Асексуальные персонажи", value: "168" }, + { label: "Аскетизм", value: "606" }, + { label: "Асоциальность", value: "2019" }, + { label: "Ассистенты", value: "2641" }, + { label: "Астма", value: "1121" }, + { label: "Астральное проецирование", value: "2530" }, + { label: "Астральные миры", value: "1379" }, + { label: "Астрология", value: "476" }, + { label: "Астронавты", value: "2220" }, + { label: "Астрономия", value: "1372" }, + { label: "Асфиксия", value: "104" }, + { label: "Атеисты", value: "669" }, + { label: "Атлантида", value: "1144" }, + { label: "Атмокинез", value: "1964" }, + { label: "Атмосферная зарисовка", value: "2802" }, + { label: "Атомные электростанции", value: "2192" }, + { label: "Аудиокинез", value: "2849" }, + { label: "Аукционы", value: "607" }, + { label: "Аутоассасинофилия", value: "2025" }, + { label: "Аутоканнибализм", value: "2593" }, + { label: "Аутофелляция / Аутокуннилингус", value: "610" }, + { label: "Африка", value: "791" }, + { label: "Афродизиаки", value: "103" }, + { label: "Аффект", value: "938" }, + { label: "Аэрокинез", value: "2850" }, + { label: "Байкеры", value: "522" }, + { label: "Байронические герои", value: "2900" }, + { label: "Балет", value: "404" }, + { label: "Баллада", value: "2318" }, + { label: "Банки / Биржи", value: "937" }, + { label: "Банши", value: "250" }, + { label: "Барти Крауч-младший — не Пожиратель Смерти", value: "2795" }, + { label: "Бары", value: "142" }, + { label: "Баскетбол", value: "829" }, + { label: "Баски", value: "1763" }, + { label: "Бастарды", value: "603" }, + { label: "Батохаки", value: "2830" }, + { label: "Бедность", value: "979" }, + { label: "Без Волдеморта", value: "2160" }, + { label: "Без диалогов (стилизация)", value: "2885" }, + { label: "Без золотого трио", value: "2648" }, + { label: "Без Избранного (Гарри Поттер)", value: "2762" }, + { label: "Без канонических персонажей", value: "2694" }, + { label: "Без Таноса", value: "2381" }, + { label: "БезГражданки", value: "2418" }, + { label: "Бездомные", value: "916" }, + { label: "Безответственность", value: "2152" }, + { label: "Безумные ученые", value: "2559" }, + { label: "Безэмоциональность", value: "1488" }, + { label: "Бейсбол", value: "1489" }, + { label: "Беларусь", value: "1030" }, + { label: "Белый день", value: "683" }, + { label: "Белый стих", value: "1998" }, + { label: "Бельгия", value: "1039" }, + { label: "Беременность", value: "1712" }, + { label: "Бермудский треугольник", value: "1158" }, + { label: "Берсерки", value: "566" }, + { label: "Бесконтактный секс", value: "2214" }, + { label: "Бесплодие", value: "719" }, + { label: "Беспокойство", value: "2888" }, + { label: "Беспризорники", value: "604" }, + { label: "Беспричудный Изуку", value: "2422" }, + { label: "Бессмертие", value: "219" }, + { label: "Бессмертный конкурс 2023", value: "3016" }, + { label: "Бестелесность", value: "2607" }, + { label: "Би-персонажи", value: "300" }, + { label: "Библейские темы и мотивы", value: "1468" }, + { label: "Библиотекари", value: "2631" }, + { label: "Библиотеки", value: "310" }, + { label: "Бизнесмены / Бизнесвумен", value: "573" }, + { label: "Биологическое оружие", value: "1188" }, + { label: "Биопанк", value: "281" }, + { label: "Биороботы", value: "2256" }, + { label: "Биполярное расстройство", value: "690" }, + { label: "Битва за Хогвартс", value: "2774" }, + { label: "Битва Историй", value: "2960" }, + { label: "Благородные разбойники", value: "952" }, + { label: "Благотворительность", value: "613" }, + { label: "Бладплей", value: "109" }, + { label: "Ближний Восток", value: "1041" }, + { label: "Близкие враги", value: "721" }, + { label: "Близнецы", value: "295" }, + { label: "Блиц-свидания", value: "2511" }, + { label: "Блогеры", value: "1471" }, + { label: "Блокадный Ленинград", value: "1111" }, + { label: "Блэкигуд", value: "2905" }, + { label: "Богачи", value: "1549" }, + { label: "Боги / Божественные сущности", value: "149" }, + { label: "Боди-арт", value: "2260" }, + { label: "Бодипозитив", value: "1153" }, + { label: "Боевая пара", value: "1919" }, + { label: "Боевые искусства", value: "918" }, + { label: "Бои без правил", value: "1390" }, + { label: "Бокс", value: "765" }, + { label: "Болгария", value: "1786" }, + { label: "Болезнь Альцгеймера", value: "2533" }, + { label: "Боль", value: "1803" }, + { label: "Больницы", value: "20" }, + { label: "Большая Игра профессора Дамблдора", value: "2890" }, + { label: "Бордели", value: "120" }, + { label: "Борьба за власть", value: "2300" }, + { label: "Борьба за отношения", value: "1251" }, + { label: "Борьба за справедливость", value: "2335" }, + { label: "Боязнь боли", value: "2441" }, + { label: "Боязнь воды", value: "1084" }, + { label: "Боязнь врачей", value: "2063" }, + { label: "Боязнь высоты", value: "1085" }, + { label: "Боязнь грозы", value: "1124" }, + { label: "Боязнь громких звуков", value: "1090" }, + { label: "Боязнь грязи", value: "1277" }, + { label: "Боязнь замкнутых пространств", value: "821" }, + { label: "Боязнь зеркал", value: "2739" }, + { label: "Боязнь клоунов", value: "2970" }, + { label: "Боязнь крови", value: "1312" }, + { label: "Боязнь людей", value: "1082" }, + { label: "Боязнь мужчин", value: "1264" }, + { label: "Боязнь насекомых / Боязнь пауков", value: "1119" }, + { label: "Боязнь неудачи", value: "2713" }, + { label: "Боязнь огня", value: "1258" }, + { label: "Боязнь одиночества", value: "1491" }, + { label: "Боязнь открытых пространств", value: "965" }, + { label: "Боязнь привязанности", value: "1343" }, + { label: "Боязнь призраков", value: "2770" }, + { label: "Боязнь прикосновений", value: "823" }, + { label: "Боязнь сексуальных домогательств", value: "2319" }, + { label: "Боязнь смерти", value: "1515" }, + { label: "Боязнь сна", value: "2262" }, + { label: "Боязнь темноты", value: "1494" }, + { label: "Боязнь тишины", value: "2233" }, + { label: "Бразилия", value: "833" }, + { label: "Брак по договоренности", value: "35" }, + { label: "Брак по расчету", value: "303" }, + { label: "БРД", value: "2907" }, + { label: "Бродячие артисты", value: "2661" }, + { label: "Бродячие певцы", value: "1497" }, + { label: "Броманс", value: "171" }, + { label: "Будущее", value: "227" }, + { label: "Буллёзный эпидермолиз", value: "2725" }, + { label: "Буллинг", value: "436" }, + { label: "Бухгалтеры", value: "2509" }, + { label: "Буч и фэм", value: "486" }, + { label: "Бывшие", value: "311" }, + { label: "Бывшие враги", value: "2812" }, + { label: "Бывшие серийные убийцы", value: "2498" }, + { label: "Бытовое фэнтези", value: "2702" }, + { label: "В одном теле", value: "1425" }, + { label: "В поисках отношений", value: "1576" }, + { label: "В. А. О. Н.", value: "2377" }, + { label: "Вагинальный секс", value: "395" }, + { label: "Ваканда", value: "2668" }, + { label: "Валькирии", value: "557" }, + { label: "Вампиры", value: "27" }, + { label: "Василиски", value: "863" }, + { label: "Вдовство", value: "2030" }, + { label: "Вебкам-модели", value: "2299" }, + { label: "Вегетарианцы / Веганы", value: "577" }, + { label: "Ведьмы / Колдуны", value: "33" }, + { label: "Великая депрессия", value: "2224" }, + { label: "Великая французская революция", value: "2000" }, + { label: "Великобритания", value: "673" }, + { label: "Великое Княжество Литовское", value: "2587" }, + { label: "Великолепный мерзавец", value: "1244" }, + { label: "Велоспорт", value: "2978" }, + { label: "Венгрия", value: "1432" }, + { label: "Вендиго", value: "251" }, + { label: "Верность", value: "459" }, + { label: "Вертикальный инцест", value: "337" }, + { label: "Вестерн", value: "2329" }, + { label: "Ветераны", value: "555" }, + { label: "Ветеринары", value: "629" }, + { label: "Вечеринки", value: "854" }, + { label: "Вечная молодость", value: "2405" }, + { label: "Вещие сны", value: "1993" }, + { label: "Взаимопонимание", value: "2887" }, + { label: "Взросление", value: "909" }, + { label: "Вигиланты", value: "1514" }, + { label: "Видеоигры", value: "2508" }, + { label: "Визажисты / Гримеры", value: "1801" }, + { label: "Викинги", value: "676" }, + { label: "Виктимблейминг", value: "331" }, + { label: "Викторианская эпоха", value: "469" }, + { label: "Виртуальная реальность", value: "803" }, + { label: "Виртуальный секс", value: "268" }, + { label: "Витилиго", value: "1167" }, + { label: "Вице-королевство Новая Испания", value: "2603" }, + { label: "ВИЧ / СПИД", value: "542" }, + { label: "Влюбленность", value: "1303" }, + { label: "Вне закона", value: "1089" }, + { label: "Внутренний сексизм", value: "2155" }, + { label: "Внутренняя гомофобия", value: "2154" }, + { label: "Внутренняя трансфобия", value: "2716" }, + { label: "Водители / Шоферы", value: "879" }, + { label: "Военные", value: "313" }, + { label: "Военные городки", value: "2095" }, + { label: "Военные преступления", value: "2356" }, + { label: "Возвращение", value: "1522" }, + { label: "Возвращение Артура", value: "2570" }, + { label: "Воздержание", value: "921" }, + { label: "Воздушная атлетика", value: "2811" }, + { label: "Война", value: "232" }, + { label: "Война в Афганистане", value: "1050" }, + { label: "Война во Вьетнаме", value: "1332" }, + { label: "Война за независимость США", value: "2065" }, + { label: "Война миров", value: "1222" }, + { label: "Волдигуд", value: "1874" }, + { label: "Волейбол", value: "1095" }, + { label: "Волк в овечьей шкуре", value: "2228" }, + { label: "Волонтеры", value: "900" }, + { label: "Волшебники / Волшебницы", value: "93" }, + { label: "Воображаемые друзья", value: "738" }, + { label: "Ворарефилия", value: "157" }, + { label: "Воры", value: "320" }, + { label: "Воскрешение", value: "256" }, + { label: "Воспоминания", value: "1118" }, + { label: "Воссоединение", value: "498" }, + { label: "Восстание машин", value: "817" }, + { label: "Восточное побережье", value: "770" }, + { label: "Восьмое марта", value: "493" }, + { label: "Вражда", value: "681" }, + { label: "Вражда семей", value: "2867" }, + { label: "Врачи", value: "374" }, + { label: "Времена Мародеров", value: "1872" }, + { label: "Времена Основателей", value: "1891" }, + { label: "Временная смерть персонажа", value: "439" }, + { label: "Временное бессмертие", value: "2755" }, + { label: "Временное воскрешение персонажей", value: "2649" }, + { label: "Временные петли", value: "147" }, + { label: "Временные превращения", value: "927" }, + { label: "Врожденные заболевания", value: "1781" }, + { label: "Всадники Апокалипсиса", value: "1326" }, + { label: "Все живы / Никто не умер", value: "535" }, + { label: "Все за Одного — отец Изуку", value: "2421" }, + { label: "Всезнающий рассказчик", value: "1368" }, + { label: "Вселение в тело", value: "1181" }, + { label: "Вторая мировая", value: "488" }, + { label: "Вторжение пришельцев", value: "755" }, + { label: "Вторичный стыд", value: "1393" }, + { label: "Второй шанс", value: "1799" }, + { label: "Второстепенные оригинальные персонажи", value: "1536" }, + { label: "Вуайеризм", value: "174" }, + { label: "Вуду", value: "678" }, + { label: "Выбор", value: "2808" }, + { label: "Выгорание", value: "2914" }, + { label: "Выживание", value: "216" }, + { label: "Вымирающий вид", value: "2373" }, + { label: "Вымышленная анатомия", value: "1317" }, + { label: "Вымышленная география", value: "1294" }, + { label: "Вымышленная религия", value: "2062" }, + { label: "Вымышленная физика", value: "2132" }, + { label: "Вымышленные виды спорта", value: "1777" }, + { label: "Вымышленные заболевания", value: "1295" }, + { label: "Вымышленные науки", value: "1333" }, + { label: "Вымышленные праздники", value: "1769" }, + { label: "Вымышленные профессии", value: "1391" }, + { label: "Вымышленные существа", value: "1685" }, + { label: "Вымышленные языки", value: "536" }, + { label: "Выпускные", value: "717" }, + { label: "Высшее общество", value: "1093" }, + { label: "Высшие учебные заведения", value: "97" }, + { label: "Выученная беспомощность", value: "2772" }, + { label: "Выхаживание", value: "511" }, + { label: "Выход из нездоровых отношений", value: "2891" }, + { label: "Гадалки / Ясновидящие", value: "2662" }, + { label: "Гадкий утенок", value: "1225" }, + { label: "Газлайтинг", value: "1418" }, + { label: "Галлюцинации / Иллюзии", value: "365" }, + { label: "Гаремник", value: "50" }, + { label: "Гаремы", value: "115" }, + { label: "Гарпии", value: "1482" }, + { label: "Гарри Поттер и Драко Малфой — друзья", value: "1923" }, + { label: "Гастроли", value: "2487" }, + { label: "Гедонизм", value: "736" }, + { label: "Гей-клубы", value: "2482" }, + { label: "Геймеры", value: "356" }, + { label: "Геймлит", value: "23" }, + { label: "Гейши / Кисэн", value: "332" }, + { label: "Гемофилия", value: "1341" }, + { label: "Гендерная дисфория", value: "431" }, + { label: "Гендерная интрига", value: "55" }, + { label: "Гендерное неравенство", value: "2568" }, + { label: "Гендерный нонконформизм", value: "1417" }, + { label: "Гендерсвап", value: "1691" }, + { label: "Генетика / Эволюция", value: "2131" }, + { label: "Гении", value: "445" }, + { label: "Генитальные пытки", value: "2104" }, + { label: "Геноцид", value: "720" }, + { label: "Геокинез", value: "2889" }, + { label: "Геологи / Географы", value: "934" }, + { label: "Германия", value: "688" }, + { label: "Германия — не Священная Римская Империя", value: "2470" }, + { label: "Герой поневоле", value: "2145" }, + { label: "Геронтофилия", value: "162" }, + { label: "Гетерохромия", value: "1210" }, + { label: "Гетто", value: "1783" }, + { label: "Гибристофилия", value: "1843" }, + { label: "Гиганты", value: "735" }, + { label: "Гидра трэш-пати", value: "2804" }, + { label: "Гики", value: "482" }, + { label: "Гильдии", value: "957" }, + { label: "Гимнастика", value: "1974" }, + { label: "Гиперакузия", value: "2866" }, + { label: "Гиперсексуальность", value: "945" }, + { label: "Гипноз", value: "700" }, + { label: "Гладиаторы", value: "677" }, + { label: "Глобальные катастрофы", value: "525" }, + { label: "Глухота", value: "595" }, + { label: "Гнев", value: "2806" }, + { label: "Гнездование", value: "1452" }, + { label: "Гномы", value: "138" }, + { label: "Гоблины", value: "188" }, + { label: "Големы", value: "1208" }, + { label: "Голод", value: "868" }, + { label: "Голодные Фэндомные Игры", value: "2895" }, + { label: "Гомофобия", value: "428" }, + { label: "Гомункулы", value: "1366" }, + { label: "Горе / Утрата", value: "1113" }, + { label: "Горизонтальный инцест", value: "338" }, + { label: "Городские легенды", value: "1832" }, + { label: "Горы", value: "702" }, + { label: "Горячие источники", value: "2813" }, + { label: "Гостевой брак", value: "1354" }, + { label: "Гостиницы", value: "384" }, + { label: "Готический роман", value: "963" }, + { label: "Готы", value: "575" }, + { label: "Гравиокинез", value: "1985" }, + { label: "Гражданская война", value: "370" }, + { label: "Гражданская война в России", value: "1282" }, + { label: "Гражданская война в США", value: "1018" }, + { label: "Графичные описания", value: "2854" }, + { label: "Грейнджергад", value: "1892" }, + { label: "Грейромантичные персонажи", value: "707" }, + { label: "Гремлины", value: "189" }, + { label: "Греция", value: "1040" }, + { label: "Грифоны", value: "335" }, + { label: "Громкий секс", value: "1184" }, + { label: "Грубый секс", value: "121" }, + { label: "Грудное кормление", value: "296" }, + { label: "Групи", value: "2925" }, + { label: "Групповое изнасилование", value: "2203" }, + { label: "Групповой секс", value: "1654" }, + { label: "Грязный реализм", value: "634" }, + { label: "Гувернантки", value: "1179" }, + { label: "Гули", value: "367" }, + { label: "Гэвин Рид — андроид", value: "2692" }, + { label: "Даб-кон", value: "57" }, + { label: "Даби — не Тойя Тодороки", value: "2821" }, + { label: "Даби — Тойя Тодороки", value: "2423" }, + { label: "Дадзава", value: "2915" }, + { label: "Дадмайт", value: "2917" }, + { label: "Дайверы", value: "939" }, + { label: "Дайвинг", value: "2823" }, + { label: "Дама в беде", value: "1259" }, + { label: "Дамбигад", value: "1863" }, + { label: "Дамбигуд", value: "1873" }, + { label: "Дампиры", value: "2860" }, + { label: "Дандэрэ", value: "1364" }, + { label: "Дарк", value: "1678" }, + { label: "Даркнет", value: "1287" }, + { label: "Дачи", value: "930" }, + { label: "Двойники", value: "305" }, + { label: "Двойное проникновение", value: "107" }, + { label: "Двойной кноттинг", value: "2619" }, + { label: "Двойные агенты", value: "441" }, + { label: "Дворцовые интриги", value: "2075" }, + { label: "Девиантное поведение", value: "1814" }, + { label: "Девичники / Мальчишники", value: "1191" }, + { label: "Девочки из Эквестрии", value: "2912" }, + { label: "Дежавю", value: "2049" }, + { label: "Декаданс", value: "2353" }, + { label: "Деконструкция", value: "1322" }, + { label: "Деми-персонажи", value: "723" }, + { label: "Демиурги", value: "819" }, + { label: "Демонология", value: "2485" }, + { label: "Демоны", value: "73" }, + { label: "Демоны-хранители", value: "2045" }, + { label: "Дендрофилия", value: "491" }, + { label: "День благодарения", value: "754" }, + { label: "День знаний", value: "1075" }, + { label: "День матери", value: "2979" }, + { label: "День мертвых", value: "1074" }, + { label: "День независимости США", value: "1008" }, + { label: "День рождения", value: "298" }, + { label: "День святого Валентина", value: "204" }, + { label: "День святого Патрика", value: "1129" }, + { label: "День смерти", value: "2251" }, + { label: "День учителя", value: "1076" }, + { label: "Деперсонализация", value: "1197" }, + { label: "Депрессия", value: "354" }, + { label: "Депривация сна", value: "551" }, + { label: "Дереализация", value: "1198" }, + { label: "Деревни", value: "364" }, + { label: "Дерматозойный бред", value: "2779" }, + { label: "Деспоты / Тираны", value: "1345" }, + { label: "Детектив", value: "1672" }, + { label: "Дети", value: "217" }, + { label: "Детоненавистничество", value: "1836" }, + { label: "Детская влюбленность", value: "186" }, + { label: "Детские дома", value: "355" }, + { label: "Детские лагеря", value: "222" }, + { label: "Детские сады", value: "567" }, + { label: "Дефекты зрения", value: "2639" }, + { label: "Дефекты речи", value: "737" }, + { label: "Джинны / Пери", value: "456" }, + { label: "Джон Ватсон — преступник", value: "1932" }, + { label: "Джуманджи — не игровой мир", value: "2799" }, + { label: "Диабет", value: "820" }, + { label: "Диалоги (стилизация)", value: "1139" }, + { label: "Дизайнеры", value: "1875" }, + { label: "Дизельпанк", value: "294" }, + { label: "Дикие животные", value: "622" }, + { label: "Дикий Запад", value: "293" }, + { label: "Дилетанты", value: "2443" }, + { label: "Динозавры", value: "670" }, + { label: "Дипломатия", value: "1790" }, + { label: "Дисбаланс власти", value: "732" }, + { label: "Дискриминация", value: "2230" }, + { label: "Дискриминация по внешности", value: "1414" }, + { label: "Дискриминация по возрасту", value: "2843" }, + { label: "Дислексия", value: "860" }, + { label: "Дисморфофобия", value: "1108" }, + { label: "Диссоциативное расстройство идентичности", value: "2127" }, + { label: "Дистанционное обучение", value: "2592" }, + { label: "Дисфункциональные семьи", value: "602" }, + { label: "Дневники (стилизация)", value: "214" }, + { label: "Добрый Сальери", value: "2522" }, + { label: "Доверие", value: "1117" }, + { label: "Договоры / Сделки", value: "2137" }, + { label: "Документы / Отчеты (стилизация)", value: "752" }, + { label: "Долголетие", value: "2130" }, + { label: "Домашнее насилие", value: "42" }, + { label: "Домашние животные", value: "517" }, + { label: "Домашний арест", value: "2526" }, + { label: "Домашний ремонт", value: "867" }, + { label: "Домовые", value: "1423" }, + { label: "Допросы", value: "2654" }, + { label: "Дорожное приключение", value: "47" }, + { label: "Драббл", value: "2935" }, + { label: "Драки", value: "1367" }, + { label: "Драконы", value: "43" }, + { label: "Драма", value: "1668" }, + { label: "Драуги", value: "2264" }, + { label: "Древний Восток", value: "1940" }, + { label: "Древний Египет", value: "474" }, + { label: "Древний Китай", value: "1438" }, + { label: "Древняя Русь", value: "466" }, + { label: "Дремлющее зло", value: "2719" }, + { label: "Дремлющие способности", value: "2084" }, + { label: "Другая магическая школа", value: "1864" }, + { label: "Другие планеты", value: "1415" }, + { label: "Другой избранный", value: "2054" }, + { label: "Другой факультет", value: "1866" }, + { label: "Дружба", value: "1701" }, + { label: "Дружба втайне", value: "2303" }, + { label: "Дружба по расчету", value: "2331" }, + { label: "Друзья детства", value: "343" }, + { label: "Друзья по переписке", value: "1466" }, + { label: "Друзья поневоле", value: "627" }, + { label: "Друзья с привилегиями", value: "113" }, + { label: "Друиды", value: "1199" }, + { label: "Дрэг (субкультура)", value: "1248" }, + { label: "Дурмстранг", value: "2389" }, + { label: "Дурслигуд", value: "2058" }, + { label: "Духи природы", value: "503" }, + { label: "Духовенство", value: "478" }, + { label: "Духовная связь", value: "2090" }, + { label: "Духокинез", value: "2500" }, + { label: "Дуэли", value: "446" }, + { label: "Дьяволы", value: "2369" }, + { label: "Дэдди-кинк", value: "165" }, + { label: "Еда / Кулинария", value: "1195" }, + { label: "Единороги", value: "373" }, + { label: "Ёкаи", value: "416" }, + { label: "Естественные враги", value: "728" }, + { label: "Жамевю", value: "2050" }, + { label: "Жаргон", value: "1016" }, + { label: "Ждун Фест", value: "2936" }, + { label: "Железнодорожники", value: "2680" }, + { label: "Железные дороги", value: "811" }, + { label: "Женская дружба", value: "2156" }, + { label: "Жертвы обстоятельств", value: "1475" }, + { label: "Жестокое обращение с животными", value: "722" }, + { label: "Жестокость", value: "1710" }, + { label: "Живой сосуд", value: "2072" }, + { label: "Животные-компаньоны", value: "2371" }, + { label: "Живые машины", value: "961" }, + { label: "ЖиП", value: "143" }, + { label: "Журналисты", value: "438" }, + { label: "Заболевания", value: "1268" }, + { label: "Заболевания крови", value: "2254" }, + { label: "Заболевания сердца", value: "1767" }, + { label: "Забота / Поддержка", value: "1302" }, + { label: "Заброшенные города", value: "1276" }, + { label: "Заброшенные здания", value: "1483" }, + { label: "Забытый герой", value: "2337" }, + { label: "Завещание", value: "1262" }, + { label: "Зависимое расстройство личности", value: "2349" }, + { label: "Зависть", value: "990" }, + { label: "Завоевание", value: "978" }, + { label: "Загробный мир", value: "275" }, + { label: "Закавказье", value: "2819" }, + { label: "Заклятые друзья", value: "1316" }, + { label: "Закрытые города", value: "1481" }, + { label: "Закрытые учебные заведения", value: "98" }, + { label: "Закрытый детектив", value: "2616" }, + { label: "Заложники", value: "845" }, + { label: "Замки", value: "654" }, + { label: "Замкнутое пространство", value: "2306" }, + { label: "Замкнутый мир", value: "874" }, + { label: "Занавесочная история", value: "1688" }, + { label: "Запахи", value: "1385" }, + { label: "Заповедники", value: "618" }, + { label: "Запредельно одаренный персонаж", value: "1241" }, + { label: "Запрет на магию", value: "2663" }, + { label: "Запретные отношения", value: "362" }, + { label: "Засосы / Укусы", value: "913" }, + { label: "Застенчивость", value: "2807" }, + { label: "Затворники", value: "1059" }, + { label: "Затерянные миры", value: "2448" }, + { label: "Защита любимого", value: "2492" }, + { label: "Защитники природы", value: "628" }, + { label: "Защищенный секс", value: "636" }, + { label: "Звездные слезы", value: "2971" }, + { label: "Здоровые механизмы преодоления", value: "2147" }, + { label: "Здоровые отношения", value: "1853" }, + { label: "Зеркала", value: "724" }, + { label: "Зимняя Фандомная Битва", value: "254" }, + { label: "Злобные бывшие", value: "518" }, + { label: "Зловещая находка", value: "2275" }, + { label: "Знаки зодиака", value: "643" }, + { label: "Знаменитости", value: "1360" }, + { label: "Золотая клетка", value: "480" }, + { label: "Золотая молодежь", value: "796" }, + { label: "Золотая Орда", value: "1143" }, + { label: "Золушка", value: "1226" }, + { label: "Зомби", value: "31" }, + { label: "Зоопарки", value: "792" }, + { label: "Зоофилия", value: "1662" }, + { label: "Зоофобии", value: "2715" }, + { label: "ЗППП", value: "630" }, + { label: "ЗПР", value: "2259" }, + { label: "Зрелые персонажи", value: "851" }, + { label: "Иван Купала", value: "1068" }, + { label: "Игровое расстройство", value: "949" }, + { label: "Игрушки", value: "1346" }, + { label: "Игры на выживание", value: "554" }, + { label: "Игры на раздевание", value: "1535" }, + { label: "Игры с сосками", value: "423" }, + { label: "Игры с температурой", value: "230" }, + { label: "Идеализация", value: "1540" }, + { label: "Иерархический строй", value: "652" }, + { label: "Избранные", value: "847" }, + { label: "Избыточный физиологизм", value: "2334" }, + { label: "Изгнанники", value: "871" }, + { label: "Измена", value: "62" }, + { label: "Измененное состояние сознания", value: "2242" }, + { label: "Изменились за лето", value: "1908" }, + { label: "Изнасилование", value: "1650" }, + { label: "Изоляция", value: "1296" }, + { label: "Израиль", value: "1094" }, + { label: "Изуку Мидория — злодей", value: "2472" }, + { label: "Иллюзионисты", value: "532" }, + { label: "Имейджин", value: "838" }, + { label: "Импотенция", value: "632" }, + { label: "Импринтинг", value: "635" }, + { label: "Импрореал", value: "2955" }, + { label: "Импрофест", value: "2947" }, + { label: "Имя вместо цвета", value: "2919" }, + { label: "Инвалидность", value: "197" }, + { label: "Индия", value: "1002" }, + { label: "Инквизиция", value: "1130" }, + { label: "Инопланетяне", value: "58" }, + { label: "Инсектоиды / Арахноиды", value: "953" }, + { label: "Инсектофилия / Арахнофилия", value: "664" }, + { label: "Инсценированная смерть персонажа", value: "440" }, + { label: "Интерактивная работа", value: "419" }, + { label: "Интервью (стилизация)", value: "2446" }, + { label: "Интернет", value: "1286" }, + { label: "Интерсекс-персонажи", value: "299" }, + { label: "Интрамаммарный секс", value: "840" }, + { label: "Инфантильность", value: "1300" }, + { label: "Инцест", value: "1651" }, + { label: "Ипохондрия", value: "2178" }, + { label: "Ирландия", value: "1035" }, + { label: "Иронический детектив", value: "2638" }, + { label: "Искра Стайлз Стилински", value: "1975" }, + { label: "Искупление", value: "877" }, + { label: "Искусственно вызванные чувства", value: "626" }, + { label: "Искусственные интеллекты", value: "150" }, + { label: "Искусственные существа", value: "2277" }, + { label: "Искусство", value: "2257" }, + { label: "Искусствоведы", value: "2873" }, + { label: "Искушение", value: "776" }, + { label: "Испания", value: "1020" }, + { label: "Исповеди", value: "1342" }, + { label: "Исправительные лагеря", value: "2439" }, + { label: "Истинные", value: "1855" }, + { label: "Историческая Хеталия", value: "1865" }, + { label: "Исторические эпохи", value: "1698" }, + { label: "Исторический Кантриболс", value: "2913" }, + { label: "Историческое допущение", value: "2681" }, + { label: "Исцеление", value: "802" }, + { label: "Италия", value: "1003" }, + { label: "Ихтиоморфы", value: "972" }, + { label: "Йога", value: "483" }, + { label: "Йоль", value: "2143" }, + { label: "Кабаре", value: "457" }, + { label: "Кабэ-дон", value: "2626" }, + { label: "Казахстан", value: "1260" }, + { label: "Казино", value: "620" }, + { label: "Казнь", value: "548" }, + { label: "Как ориджинал", value: "1032" }, + { label: "Каламбуры", value: "881" }, + { label: "Каминг-аут", value: "559" }, + { label: "Канада", value: "695" }, + { label: "Каникулы", value: "805" }, + { label: "Каннибализм", value: "1706" }, + { label: "Канонная смерть персонажа", value: "564" }, + { label: "Капитан Гидра", value: "1928" }, + { label: "Карантинные зоны", value: "1290" }, + { label: "Карантины", value: "2734" }, + { label: "Караоке", value: "886" }, + { label: "Карательная психиатрия", value: "730" }, + { label: "Карибский бассейн", value: "1172" }, + { label: "Карма", value: "647" }, + { label: "Карьеризм", value: "956" }, + { label: "Каскадеры", value: "2267" }, + { label: "Катарсис", value: "824" }, + { label: "Кафе / Кофейни / Чайные", value: "13" }, + { label: "Квай-Гад", value: "2894" }, + { label: "Квантовая физика", value: "2744" }, + { label: "Квисины", value: "2722" }, + { label: "Кемономими", value: "997" }, + { label: "Кемпинг", value: "182" }, + { label: "Кентавры", value: "64" }, + { label: "Кибербуллинг", value: "1858" }, + { label: "Киберпанк", value: "24" }, + { label: "Киберспорт", value: "745" }, + { label: "Кибертрон", value: "2775" }, + { label: "Киборги", value: "154" }, + { label: "Киевская Русь", value: "515" }, + { label: "Кинк на беременность", value: "2281" }, + { label: "Кинк на волосы", value: "2089" }, + { label: "Кинк на волосы на теле", value: "1337" }, + { label: "Кинк на девственность", value: "2282" }, + { label: "Кинк на инвалидность", value: "633" }, + { label: "Кинк на интеллект", value: "2798" }, + { label: "Кинк на клыки", value: "2325" }, + { label: "Кинк на колготки", value: "2644" }, + { label: "Кинк на крылья", value: "2035" }, + { label: "Кинк на латекс", value: "134" }, + { label: "Кинк на мольбы", value: "1543" }, + { label: "Кинк на наручники", value: "1065" }, + { label: "Кинк на нижнее белье", value: "145" }, + { label: "Кинк на очки", value: "1723" }, + { label: "Кинк на ошейники", value: "388" }, + { label: "Кинк на полноту", value: "552" }, + { label: "Кинк на похвалу", value: "1042" }, + { label: "Кинк на протезы", value: "2682" }, + { label: "Кинк на рога", value: "2320" }, + { label: "Кинк на руки", value: "915" }, + { label: "Кинк на сердцебиение", value: "941" }, + { label: "Кинк на силу", value: "1476" }, + { label: "Кинк на слезы", value: "1373" }, + { label: "Кинк на служение", value: "2980" }, + { label: "Кинк на страх", value: "2246" }, + { label: "Кинк на стыд", value: "747" }, + { label: "Кинк на унижение", value: "2291" }, + { label: "Кинк на униформу", value: "1350" }, + { label: "Кинк на уши", value: "2370" }, + { label: "Кинк на формальную одежду", value: "1064" }, + { label: "Кинк на чужую одежду", value: "2920" }, + { label: "Кинк на чулки", value: "810" }, + { label: "Кинк на шрамы", value: "2831" }, + { label: "Кинк на щекотку", value: "2396" }, + { label: "Кинк-бинго", value: "2290" }, + { label: "Кинки / Фетиши", value: "1659" }, + { label: "Кинотеатры", value: "981" }, + { label: "Киригакуре", value: "2475" }, + { label: "Китай", value: "757" }, + { label: "Кладбища", value: "501" }, + { label: "Кланы", value: "2879" }, + { label: "Классизм", value: "1927" }, + { label: "Клейма", value: "1548" }, + { label: "Клептомания", value: "1160" }, + { label: "Клетка Люцифера", value: "1884" }, + { label: "Клетки", value: "1069" }, + { label: "Клиническая смерть", value: "2068" }, + { label: "Клокпанк / Ветропанк", value: "1812" }, + { label: "Клоны", value: "358" }, + { label: "Клоуны", value: "1520" }, + { label: "Клубы по интересам", value: "1369" }, + { label: "Клюква", value: "1001" }, + { label: "Кляпы / Затыкание рта", value: "379" }, + { label: "Книги", value: "2746" }, + { label: "Книжные магазины", value: "444" }, + { label: "Кноттинг", value: "99" }, + { label: "Ковбои", value: "330" }, + { label: "Кода", value: "1943" }, + { label: "Кожаная одежда", value: "1517" }, + { label: "Колдовстворец", value: "2600" }, + { label: "Кома", value: "516" }, + { label: "Командная работа", value: "708" }, + { label: "Комики", value: "1315" }, + { label: "Комиксисты / Мангаки", value: "576" }, + { label: "Коммунальные квартиры", value: "1437" }, + { label: "Коммуны", value: "797" }, + { label: "Комнатные растения", value: "1792" }, + { label: "Комплекс Бога", value: "2087" }, + { label: "Комплекс превосходства", value: "2191" }, + { label: "Комплексы", value: "2529" }, + { label: "Конкурс от BUBBLE", value: "2937" }, + { label: "Конный спорт", value: "1378" }, + { label: "Конохагакуре", value: "2477" }, + { label: "Конспирология", value: "1911" }, + { label: "Контрабандисты", value: "2363" }, + { label: "Контроль / Подчинение", value: "932" }, + { label: "Контроль памяти", value: "2540" }, + { label: "Контроль сознания", value: "257" }, + { label: "Конфликт мировоззрений", value: "2678" }, + { label: "Концентрационные лагеря", value: "547" }, + { label: "Концерты / Выступления", value: "1236" }, + { label: "Конюшни", value: "514" }, + { label: "Копирование сознания", value: "1231" }, + { label: "Копирование способностей", value: "1825" }, + { label: "Копро-кинк", value: "763" }, + { label: "Корё", value: "1142" }, + { label: "Корейская война", value: "2187" }, + { label: "Коренные народы", value: "1505" }, + { label: "Королевства", value: "2794" }, + { label: "Корпорация BOBSOC", value: "2940" }, + { label: "Корректирующее изнасилование", value: "2856" }, + { label: "Коррупция", value: "2150" }, + { label: "Косвенные убийства", value: "2846" }, + { label: "Космоопера", value: "48" }, + { label: "Космос", value: "236" }, + { label: "Косплееры", value: "944" }, + { label: "Кочевники", value: "1793" }, + { label: "Кошмары", value: "1228" }, + { label: "Красная нить судьбы", value: "608" }, + { label: "Крепости / Башни", value: "2321" }, + { label: "Крестовые походы", value: "2070" }, + { label: "Крестражи", value: "2238" }, + { label: "Кризис гендера", value: "1406" }, + { label: "Кризис ориентации", value: "1405" }, + { label: "Криминалистическая экспертиза", value: "2361" }, + { label: "Криминальная пара", value: "1920" }, + { label: "Криокинез", value: "1804" }, + { label: "Криптиды", value: "60" }, + { label: "Криптоистория", value: "179" }, + { label: "Кровь / Травмы", value: "800" }, + { label: "Кроссдрессинг", value: "65" }, + { label: "Кроссовер", value: "10" }, + { label: "Кроули — Архангел Рафаэль", value: "2471" }, + { label: "Круизы", value: "2488" }, + { label: "Крушение «Титаника»", value: "2200" }, + { label: "Крылатые", value: "92" }, + { label: "Крэк", value: "1408" }, + { label: "Ксенофилия", value: "1711" }, + { label: "Ксенофобия", value: "589" }, + { label: "Куба", value: "773" }, + { label: "Кудэрэ", value: "1216" }, + { label: "Куклы", value: "1221" }, + { label: "Куколдинг", value: "524" }, + { label: "Культ Дракона", value: "2244" }, + { label: "Кумогакуре", value: "2474" }, + { label: "Куннилингус", value: "172" }, + { label: "Купальни", value: "964" }, + { label: "Курение", value: "202" }, + { label: "Курортный роман", value: "181" }, + { label: "Курьеры", value: "2735" }, + { label: "Лабиринты", value: "1981" }, + { label: "Лаборатории", value: "1115" }, + { label: "Лабораторные опыты", value: "146" }, + { label: "Лав-отели", value: "801" }, + { label: "Лавандовый брак", value: "1357" }, + { label: "Лакросс", value: "830" }, + { label: "Лапслок", value: "267" }, + { label: "Латентная гомосексуальность", value: "2339" }, + { label: "ЛГБТ-родители", value: "2723" }, + { label: "Лебединая верность", value: "537" }, + { label: "Левитация", value: "1281" }, + { label: "Легкая атлетика", value: "1797" }, + { label: "Лекарственная зависимость", value: "2632" }, + { label: "Леса", value: "703" }, + { label: "Летние ОтМетки", value: "2956" }, + { label: "Летние школы", value: "2803" }, + { label: "Лето девяносто девятого", value: "1990" }, + { label: "Лжебоги", value: "2869" }, + { label: "Лингвистический кинк", value: "2042" }, + { label: "Лирика", value: "2005" }, + { label: "Лихтенштейн", value: "1023" }, + { label: "Личность против системы", value: "1912" }, + { label: "Ложная беременность", value: "1428" }, + { label: "Ложные воспоминания", value: "376" }, + { label: "Ложные обвинения", value: "1204" }, + { label: "Локальный постапокалипсис", value: "2455" }, + { label: "Лолита (субкультура)", value: "1207" }, + { label: "Ломка", value: "1846" }, + { label: "Лошади", value: "1083" }, + { label: "Лугнасад", value: "2621" }, + { label: "Лудомания", value: "947" }, + { label: "Лучники", value: "1521" }, + { label: "Любить луну", value: "2901" }, + { label: "Любовная магия", value: "2317" }, + { label: "Любовный магнит", value: "1407" }, + { label: "Любовный многоугольник", value: "63" }, + { label: "Любовь с первого взгляда", value: "813" }, + { label: "Любовь/Ненависть", value: "1703" }, + { label: "Люди", value: "1538" }, + { label: "Людоеды", value: "1288" }, + { label: "Магазины", value: "1063" }, + { label: "Магическая связь", value: "2720" }, + { label: "Магические клятвы", value: "2245" }, + { label: "Магические лавки", value: "2629" }, + { label: "Магические учебные заведения", value: "1338" }, + { label: "Магический реализм", value: "9" }, + { label: "Магия", value: "1419" }, + { label: "Магия крови", value: "2857" }, + { label: "Маленькие города", value: "221" }, + { label: "Малфоигуд", value: "1890" }, + { label: "Мамми-кинк", value: "273" }, + { label: "Мандалорцы", value: "2870" }, + { label: "Манекены", value: "1509" }, + { label: "Манипуляции", value: "992" }, + { label: "Марафонный секс", value: "316" }, + { label: "Маргиналы", value: "394" }, + { label: "Марди Гра", value: "2407" }, + { label: "Маскарады / Балы", value: "443" }, + { label: "Массаж", value: "2218" }, + { label: "Мастурбация", value: "226" }, + { label: "Матриархат", value: "706" }, + { label: "Матчасть", value: "2328" }, + { label: "Махотокоро", value: "2943" }, + { label: "Маяки", value: "1356" }, + { label: "Мегаломания", value: "985" }, + { label: "Мегаполисы", value: "451" }, + { label: "Медицинский кинк", value: "748" }, + { label: "Медицинское использование наркотиков", value: "925" }, + { label: "Медовый месяц", value: "2235" }, + { label: "Медсестры / Медбратья", value: "2236" }, + { label: "Межбедренный секс", value: "931" }, + { label: "Междумирье", value: "2280" }, + { label: "Межэтнические отношения", value: "2121" }, + { label: "Мейлдом", value: "59" }, + { label: "Мексика", value: "768" }, + { label: "Меланхолия", value: "2108" }, + { label: "Мелодрама", value: "2881" }, + { label: "Менструация", value: "1570" }, + { label: "Ментальная тюрьма", value: "2294" }, + { label: "Месть", value: "907" }, + { label: "Металлисты", value: "1206" }, + { label: "Металлокинез", value: "1532" }, + { label: "Метафизические существа", value: "2097" }, + { label: "Метки", value: "1398" }, + { label: "Метросексуалы", value: "558" }, + { label: "Механофилия / Технофилия", value: "587" }, + { label: "Мечники", value: "2783" }, + { label: "Мечты", value: "993" }, + { label: "Миддлпанк", value: "2076" }, + { label: "Мизофилия", value: "1970" }, + { label: "Микро / Макро", value: "407" }, + { label: "Микрофикшен", value: "417" }, + { label: "Мимикрия", value: "2083" }, + { label: "Минет", value: "231" }, + { label: "Министерство Магии", value: "1917" }, + { label: "Минотавры", value: "2820" }, + { label: "Мир без гомофобии", value: "2660" }, + { label: "Мир Желаний (Однажды в сказке)", value: "2923" }, + { label: "Мир Института Войны", value: "2379" }, + { label: "Мироустройство", value: "989" }, + { label: "Мистерия", value: "2012" }, + { label: "Мистика", value: "1671" }, + { label: "Мистическая беременность", value: "1784" }, + { label: "Мистические защитники", value: "2162" }, + { label: "Мифы и мифология", value: "18" }, + { label: "Мнимая беременность", value: "1096" }, + { label: "Мнимая проза", value: "1098" }, + { label: "Мнимая свобода", value: "2708" }, + { label: "Многодетные семьи", value: "2785" }, + { label: "Множественное проникновение", value: "2059" }, + { label: "Множественные оргазмы", value: "317" }, + { label: "Множественные финалы", value: "312" }, + { label: "Могучие Рейнджеры: Дино Заряд", value: "2931" }, + { label: "Могучие Рейнджеры: Мегафорс", value: "2930" }, + { label: "Могучие Рейнджеры: Мистическая Сила", value: "2933" }, + { label: "Могучие Рейнджеры: Самураи", value: "2929" }, + { label: "Могучие Рейнджеры: Успеть на помощь", value: "2934" }, + { label: "Могучие Рейнджеры: Ярость Джунглей", value: "2932" }, + { label: "Модели", value: "399" }, + { label: "Модельеры", value: "598" }, + { label: "Модельный бизнес", value: "435" }, + { label: "Модификации тела", value: "890" }, + { label: "Молдова", value: "2967" }, + { label: "Молчание", value: "2465" }, + { label: "Монастыри", value: "352" }, + { label: "Монахи", value: "353" }, + { label: "Монолог", value: "2333" }, + { label: "Монстрофилия", value: "94" }, + { label: "Монстры", value: "529" }, + { label: "Моральные дилеммы", value: "959" }, + { label: "Морлед", value: "2255" }, + { label: "Моря / Океаны", value: "955" }, + { label: "Моряки", value: "406" }, + { label: "Мотоспорт", value: "1461" }, + { label: "Мужская беременность", value: "1660" }, + { label: "Мужская дружба", value: "2157" }, + { label: "Мужское грудное кормление", value: "297" }, + { label: "Музеи / Галереи", value: "795" }, + { label: "Музы", value: "2001" }, + { label: "Музыканты", value: "141" }, + { label: "Муковисцидоз", value: "2764" }, + { label: "Мультикроссовер", value: "2247" }, + { label: "Мумии", value: "579" }, + { label: "Мутанты", value: "740" }, + { label: "Мэри Сью (Марти Стью)", value: "1655" }, + { label: "Мятежи / Восстания", value: "584" }, + { label: "На грани жизни и смерти", value: "2545" }, + { label: "Наблюдатели", value: "1816" }, + { label: "Навязчивая опека", value: "954" }, + { label: "Навязчивые мысли", value: "2851" }, + { label: "Наги", value: "90" }, + { label: "Надежда", value: "2627" }, + { label: "Наемники", value: "512" }, + { label: "Наемные убийцы", value: "192" }, + { label: "Названные сиблинги", value: "2927" }, + { label: "Намеки на отношения", value: "1387" }, + { label: "Намеки на секс", value: "1382" }, + { label: "Напарники", value: "1291" }, + { label: "Наперегонки со временем", value: "2240" }, + { label: "НапиСанта", value: "2946" }, + { label: "Наполеоновские войны", value: "470" }, + { label: "Нарколепсия", value: "586" }, + { label: "Наркоторговля", value: "1116" }, + { label: "Нарушение этических норм", value: "968" }, + { label: "Нарциссизм", value: "546" }, + { label: "Насекомые", value: "2358" }, + { label: "Насилие", value: "1649" }, + { label: "Насилие над детьми", value: "726" }, + { label: "Наследие (Гарри Поттер)", value: "1935" }, + { label: "Наследство", value: "2172" }, + { label: "Наставничество", value: "935" }, + { label: "Настольные игры", value: "1513" }, + { label: "Настольные ролевые игры", value: "644" }, + { label: "Натуризм / Нудизм", value: "1810" }, + { label: "Наука", value: "2624" }, + { label: "Научная фантастика", value: "596" }, + { label: "Научно-исследовательские организации", value: "1503" }, + { label: "Научное фэнтези", value: "180" }, + { label: "Начало отношений", value: "1328" }, + { label: "Небинарные персонажи", value: "314" }, + { label: "Невезение", value: "2689" }, + { label: "Невзаимные предначертанные", value: "1484" }, + { label: "Невзаимные чувства", value: "105" }, + { label: "Невидимость", value: "359" }, + { label: "Невидимый мир", value: "2286" }, + { label: "Неграфичные описания", value: "2855" }, + { label: "Недоверие", value: "1842" }, + { label: "Недопонимания", value: "614" }, + { label: "Недостатки внешности", value: "923" }, + { label: "Нежелательная беременность", value: "715" }, + { label: "Нежелательные сверхспособности", value: "1335" }, + { label: "Нежелательные чувства", value: "2078" }, + { label: "Нежный секс", value: "280" }, + { label: "Незащищенный секс", value: "497" }, + { label: "Нездоровые механизмы преодоления", value: "1528" }, + { label: "Нездоровые отношения", value: "71" }, + { label: "Нездоровый образ жизни", value: "2148" }, + { label: "Нездоровый BDSM", value: "1062" }, + { label: "Неидеальный омегаверс", value: "2712" }, + { label: "Неизвестность", value: "2826" }, + { label: "Неизвестные родственники", value: "2752" }, + { label: "Неизлечимые заболевания", value: "1269" }, + { label: "Нейтрализация сверхспособностей", value: "2343" }, + { label: "Некромаги", value: "679" }, + { label: "Некрофилия", value: "1663" }, + { label: "Нелинейное повествование", value: "1699" }, + { label: "Неловкий секс", value: "2883" }, + { label: "Неловкость", value: "2459" }, + { label: "Нелюбящие родители", value: "2817" }, + { label: "Немертвые", value: "495" }, + { label: "Немота", value: "414" }, + { label: "Ненависть", value: "685" }, + { label: "Ненависть к себе", value: "1441" }, + { label: "Ненависть с первого взгляда", value: "814" }, + { label: "Ненадежный рассказчик", value: "322" }, + { label: "Ненасильственная смерть", value: "2531" }, + { label: "Необитаемые острова", value: "1196" }, + { label: "Необратимые превращения", value: "926" }, + { label: "Неозвученные чувства", value: "258" }, + { label: "Неприятие отношений", value: "2056" }, + { label: "Неравные отношения", value: "545" }, + { label: "Неравный брак", value: "409" }, + { label: "Неразрывная связь", value: "2569" }, + { label: "Нервный срыв", value: "1190" }, + { label: "Нерды", value: "481" }, + { label: "Нереалистичность", value: "2350" }, + { label: "Несексуальная близость", value: "2874" }, + { label: "Нестандартная поэзия", value: "1704" }, + { label: "Несчастливые отношения", value: "2080" }, + { label: "Несчастливый финал", value: "61" }, + { label: "Несчастные случаи", value: "499" }, + { label: "Нетаймори", value: "2323" }, + { label: "Неторопливое повествование", value: "1815" }, + { label: "Нетрадиционное использование Силы", value: "2520" }, + { label: "Неумышленное употребление наркотических веществ", value: "1192" }, + { label: "Неуставные отношения", value: "1396" }, + { label: "Нефилимы", value: "1086" }, + { label: "Неформальный брак", value: "1355" }, + { label: "Нецензурная лексика", value: "1653" }, + { label: "Нечеловеческая мораль", value: "2551" }, + { label: "Нечеловеческие виды", value: "995" }, + { label: "Нечистая сила", value: "1464" }, + { label: "Нидерланды", value: "1141" }, + { label: "Низкая самооценка", value: "2014" }, + { label: "Низкое фэнтези", value: "2007" }, + { label: "Нимфы", value: "1477" }, + { label: "Ниндзя", value: "471" }, + { label: "Новая жизнь", value: "2018" }, + { label: "Новая Зеландия", value: "1789" }, + { label: "Новеллизация", value: "262" }, + { label: "Новые отношения", value: "1827" }, + { label: "Новый год", value: "206" }, + { label: "Новый канон ЗВ", value: "1948" }, + { label: "Норны", value: "2206" }, + { label: "Ностальгия", value: "878" }, + { label: "Ночное видение", value: "2006" }, + { label: "Ночные клубы", value: "531" }, + { label: "Нуар", value: "137" }, + { label: "Няни", value: "570" }, + { label: "Обездвиживание", value: "116" }, + { label: "Обещания / Клятвы", value: "1823" }, + { label: "Обиды", value: "905" }, + { label: "Обман / Заблуждение", value: "650" }, + { label: "Обмен квами", value: "2572" }, + { label: "Обмен ролями", value: "1055" }, + { label: "Обмен телами", value: "49" }, + { label: "Обмороки", value: "2484" }, + { label: "Обнажение", value: "970" }, + { label: "Оборотни", value: "28" }, + { label: "Обоснованный ООС", value: "2549" }, + { label: "Образ тела", value: "2289" }, + { label: "Обратный омегаверс", value: "2435" }, + { label: "Обретенные семьи", value: "601" }, + { label: "Обреченные отношения", value: "653" }, + { label: "Обскуры", value: "2237" }, + { label: "Обсуждение кинков", value: "1072" }, + { label: "Обусловленная контекстом гомофобия", value: "2685" }, + { label: "Обусловленный контекстом расизм", value: "2686" }, + { label: "Обусловленный контекстом сексизм", value: "2687" }, + { label: "Общежития", value: "534" }, + { label: "Общественный транспорт", value: "615" }, + { label: "Объективация", value: "1837" }, + { label: "Объектофилия", value: "366" }, + { label: "Овипозиция", value: "269" }, + { label: "Огнестрельное оружие", value: "1189" }, + { label: "Ограбления", value: "2269" }, + { label: "Одержимость", value: "2248" }, + { label: "Одержимые", value: "496" }, + { label: "Один день", value: "2647" }, + { label: "Одиночество", value: "1015" }, + { label: "Одичавшие дети", value: "835" }, + { label: "Однолюбы", value: "2146" }, + { label: "Одноминутный канонический персонаж", value: "2184" }, + { label: "Однополый мир", value: "1462" }, + { label: "Ожидание", value: "2909" }, + { label: "ОЖП", value: "1657" }, + { label: "Окаменение", value: "2052" }, + { label: "Океания", value: "1058" }, + { label: "Оккультизм", value: "2374" }, + { label: "ОКР", value: "594" }, + { label: "Окулолинктус", value: "1470" }, + { label: "Олимпийские игры", value: "1127" }, + { label: "Олфактофилия", value: "1945" }, + { label: "Омегаверс", value: "1693" }, + { label: "Омегаверс: Альфа/Альфа", value: "2437" }, + { label: "Омегаверс: Альфа/Бета", value: "2741" }, + { label: "Омегаверс: Альфа/Бета/Омега", value: "2898" }, + { label: "Омегаверс: Альфа/Омега/Альфа", value: "2436" }, + { label: "Омегаверс: Бета/Бета", value: "2966" }, + { label: "Омегаверс: Больше трех полов", value: "2782" }, + { label: "Омегаверс: Омега/Альфа/Омега", value: "2544" }, + { label: "Омегаверс: Омега/Бета", value: "2965" }, + { label: "Омегаверс: Омега/Омега", value: "2564" }, + { label: "Омегаверс: Смена второго пола", value: "2776" }, + { label: "Омегаверс: f!Альфа/m!Омега", value: "2777" }, + { label: "ОМП", value: "1656" }, + { label: "Онейрокинез", value: "2067" }, + { label: "Онкологические заболевания", value: "540" }, + { label: "ООС", value: "1648" }, + { label: "Опасность", value: "2847" }, + { label: "Опера", value: "549" }, + { label: "Опричнина", value: "2252" }, + { label: "Оптимизм", value: "2420" }, + { label: "Оракулы / Провидцы", value: "2221" }, + { label: "Оральная фиксация", value: "2523" }, + { label: "Оргазм без стимуляции", value: "1000" }, + { label: "Организаторы свадеб", value: "858" }, + { label: "Орден Феникса", value: "1907" }, + { label: "Орки", value: "187" }, + { label: "Оружие массового поражения", value: "1193" }, + { label: "Освоение земель", value: "1824" }, + { label: "Оседлание", value: "1223" }, + { label: "Османская империя", value: "527" }, + { label: "Особняки / Резиденции", value: "500" }, + { label: "Осознанные сновидения", value: "530" }, + { label: "Остеокинез", value: "2845" }, + { label: "Острова", value: "799" }, + { label: "От антигероя к герою", value: "2173" }, + { label: "От антигероя к злодею", value: "2213" }, + { label: "От божественного существа к смертному", value: "2664" }, + { label: "От возлюбленных к врагам", value: "783" }, + { label: "От возлюбленных к друзьям", value: "782" }, + { label: "От врагов к возлюбленным", value: "346" }, + { label: "От врагов к друзьям", value: "347" }, + { label: "От врагов к друзьям к возлюбленным", value: "345" }, + { label: "От героя к антигерою", value: "2546" }, + { label: "От героя к злодею", value: "1027" }, + { label: "От героя к монстру", value: "2659" }, + { label: "От героя к обычному персонажу", value: "2584" }, + { label: "От друзей к возлюбленным", value: "344" }, + { label: "От друзей к врагам", value: "463" }, + { label: "От друзей к незнакомцам", value: "1870" }, + { label: "От злодея к антигерою", value: "2640" }, + { label: "От злодея к герою", value: "1026" }, + { label: "От напарников к друзьям к возлюбленным", value: "2460" }, + { label: "От нездоровых отношений к здоровым", value: "2301" }, + { label: "От незнакомцев к возлюбленным", value: "372" }, + { label: "От простонародья к аристократу", value: "2816" }, + { label: "От сексуальных партнеров к возлюбленным", value: "2069" }, + { label: "От смертного к божественному существу", value: "2665" }, + { label: "От супругов к возлюбленным", value: "2359" }, + { label: "Ответвление от канона", value: "2336" }, + { label: "Ответственность", value: "2468" }, + { label: "Отдаю на вдохновение", value: "2288" }, + { label: "Отечественная война 1812 года", value: "1162" }, + { label: "Отказ от чувств", value: "2227" }, + { label: "Отклонения от канона", value: "5" }, + { label: "Открытый финал", value: "185" }, + { label: "ОтМетки", value: "2945" }, + { label: "Отношения втайне", value: "285" }, + { label: "Отношения на расстоянии", value: "37" }, + { label: "Отношения на спор", value: "383" }, + { label: "Отношения наполовину", value: "1573" }, + { label: "Отпуск", value: "1828" }, + { label: "Отрицание", value: "1868" }, + { label: "Отрицание чувств", value: "648" }, + { label: "Отрицательный протагонист", value: "1442" }, + { label: "Отсутствие души", value: "1897" }, + { label: "Отшельники", value: "543" }, + { label: "Офисы", value: "74" }, + { label: "Официанты / Официантки", value: "2560" }, + { label: "Охота", value: "825" }, + { label: "Охота на разумных существ", value: "2784" }, + { label: "Охотники", value: "2036" }, + { label: "Охотники за головами", value: "2103" }, + { label: "Охотники на нечисть", value: "46" }, + { label: "Охранники", value: "1173" }, + { label: "Ошибки", value: "2554" }, + { label: "Ошибочная идентификация", value: "2490" }, + { label: "Паладины", value: "1194" }, + { label: "Пан-персонажи", value: "302" }, + { label: "Пандемия COVID-19", value: "2724" }, + { label: "Панические атаки", value: "751" }, + { label: "Паническое расстройство", value: "733" }, + { label: "Панки", value: "625" }, + { label: "Паразиты", value: "1877" }, + { label: "Паралич", value: "1941" }, + { label: "Параллельные миры", value: "378" }, + { label: "Паранойя", value: "1253" }, + { label: "Пари", value: "778" }, + { label: "Парки / Зоны отдыха", value: "1005" }, + { label: "Парки аттракционов / Ярмарки", value: "793" }, + { label: "Паркур", value: "553" }, + { label: "Пародия", value: "1676" }, + { label: "Партизаны", value: "850" }, + { label: "Парфюмеры", value: "1384" }, + { label: "Пасха", value: "562" }, + { label: "Патологическое бесстрашие", value: "1498" }, + { label: "Патологическое накопительство", value: "2691" }, + { label: "Патологоанатомы", value: "502" }, + { label: "Патриархат", value: "705" }, + { label: "Патриотические темы и мотивы", value: "2231" }, + { label: "Патронусы", value: "2390" }, + { label: "Патрули", value: "1819" }, + { label: "Пафос", value: "2589" }, + { label: "Пацифисты", value: "1939" }, + { label: "Пегасы", value: "1047" }, + { label: "Пеггинг", value: "159" }, + { label: "Пейзажная лирика", value: "2330" }, + { label: "Пекарни", value: "78" }, + { label: "Первая мировая", value: "487" }, + { label: "Первая чеченская война", value: "2428" }, + { label: "Первобытные времена", value: "465" }, + { label: "Первое апреля", value: "2315" }, + { label: "Первопроходцы", value: "351" }, + { label: "Первый контакт", value: "2093" }, + { label: "Первый поцелуй", value: "2790" }, + { label: "Первый раз", value: "1292" }, + { label: "Передача магических способностей", value: "2497" }, + { label: "Переезд", value: "658" }, + { label: "Перезапуск мира", value: "2710" }, + { label: "Перемирие", value: "1331" }, + { label: "Переписки и чаты (стилизация)", value: "38" }, + { label: "Перерыв в отношениях", value: "519" }, + { label: "Переселение душ", value: "1237" }, + { label: "Перестрелки", value: "2268" }, + { label: "Переходный возраст", value: "1297" }, + { label: "Период Эдо", value: "1140" }, + { label: "Персекуторный бред", value: "2136" }, + { label: "Персонажи-геи", value: "2141" }, + { label: "Персонажи-лесбиянки", value: "2142" }, + { label: "Персонификация", value: "994" }, + { label: "Персонификация смерти", value: "1046" }, + { label: "Перу", value: "2017" }, + { label: "Перфекционизм", value: "2730" }, + { label: "Пессимизм", value: "2709" }, + { label: "Пет-плей", value: "391" }, + { label: "Петербург: бикейм #Чимин Сергеевич", value: "1859" }, + { label: "Петтинг", value: "301" }, + { label: "Пещеры", value: "655" }, + { label: "Пижамные вечеринки", value: "1340" }, + { label: "Пикацизм", value: "1227" }, + { label: "Пикники", value: "1165" }, + { label: "Пионеры", value: "668" }, + { label: "Пираты", value: "72" }, + { label: "Пирокинез", value: "1180" }, + { label: "Пиромания", value: "899" }, + { label: "Пирсинг", value: "507" }, + { label: "Писатели", value: "196" }, + { label: "Письма", value: "2298" }, + { label: "Письма (стилизация)", value: "266" }, + { label: "Пиццерии / Дайнеры", value: "593" }, + { label: "Плавание", value: "1135" }, + { label: "Плантокинез", value: "2029" }, + { label: "Пластические операции", value: "1847" }, + { label: "Платонические отношения", value: "385" }, + { label: "Племена", value: "1336" }, + { label: "Плен", value: "290" }, + { label: "Плененные путешествием", value: "2615" }, + { label: "Плохая компания", value: "1502" }, + { label: "Плохие друзья", value: "2445" }, + { label: "Плохой хороший финал", value: "1265" }, + { label: "Плюшефилия", value: "520" }, + { label: "Пляжи", value: "766" }, + { label: "По разные стороны", value: "897" }, + { label: "Побег", value: "661" }, + { label: "Побег из дома", value: "2309" }, + { label: "Победа Волдеморта", value: "1887" }, + { label: "Победа Гриндевальда", value: "2398" }, + { label: "Повара", value: "2853" }, + { label: "Повелитель смерти", value: "1909" }, + { label: "Повествование в будущем времени", value: "2633" }, + { label: "Повествование в настоящем времени", value: "1826" }, + { label: "Повествование во втором лице", value: "166" }, + { label: "Повествование от нескольких лиц", value: "1157" }, + { label: "Повествование от первого лица", value: "1681" }, + { label: "Повседневность", value: "1677" }, + { label: "Повстанцы", value: "659" }, + { label: "Повторение судьбы", value: "1997" }, + { label: "Поглощение способностей", value: "2605" }, + { label: "Погони / Преследования", value: "1996" }, + { label: "Пограничный синдром", value: "710" }, + { label: "Погребение заживо", value: "1279" }, + { label: "Под одной крышей", value: "462" }, + { label: "Под старину (стилизация)", value: "2486" }, + { label: "Подарки", value: "1109" }, + { label: "Подвешивание", value: "914" }, + { label: "Подводный мир", value: "901" }, + { label: "Подземелья", value: "2314" }, + { label: "Подземный мир", value: "1453" }, + { label: "Подлость", value: "2625" }, + { label: "Подменыши", value: "2250" }, + { label: "Подпольные бои", value: "1389" }, + { label: "Подразумеваемая смерть персонажа", value: "2204" }, + { label: "Подростки", value: "218" }, + { label: "Подростковая беременность", value: "986" }, + { label: "Подростковая влюбленность", value: "581" }, + { label: "Подсознание", value: "2273" }, + { label: "Поедание разумных существ", value: "1795" }, + { label: "Пожарные", value: "2557" }, + { label: "Пожилые персонажи", value: "852" }, + { label: "Поза 69", value: "699" }, + { label: "Поиск работы", value: "2646" }, + { label: "Поиск родителей", value: "2033" }, + { label: "Поклонение телу", value: "712" }, + { label: "Покровительство", value: "2223" }, + { label: "Покушение на жизнь", value: "1057" }, + { label: "Покушение на свободу", value: "2655" }, + { label: "Полиамория", value: "1705" }, + { label: "Полиандрия", value: "761" }, + { label: "Полигиния", value: "760" }, + { label: "Политика", value: "1835" }, + { label: "Политики", value: "711" }, + { label: "Политические интриги", value: "183" }, + { label: "Полицейские", value: "14" }, + { label: "Половой диморфизм", value: "1780" }, + { label: "Полубоги", value: "437" }, + { label: "Полудемоны", value: "2872" }, + { label: "Полудраконы", value: "2859" }, + { label: "Полукровки", value: "368" }, + { label: "Польша", value: "777" }, + { label: "Полярные станции", value: "1493" }, + { label: "Помощь врагу", value: "1325" }, + { label: "Попаданцы: В реальный мир", value: "1499" }, + { label: "Попаданцы: В своем теле", value: "1412" }, + { label: "Попаданцы: В чужом теле", value: "1413" }, + { label: "Попаданцы: Из одного фандома в другой", value: "2699" }, + { label: "Попаданцы: Из фандома в ориджинал", value: "2193" }, + { label: "Попаданцы: По обмену", value: "2196" }, + { label: "Попаданчество", value: "1695" }, + { label: "Популярность", value: "2581" }, + { label: "Попытка изнасилования", value: "1440" }, + { label: "Порноактеры", value: "289" }, + { label: "Португалия", value: "1749" }, + { label: "Порты", value: "1028" }, + { label: "Поселки", value: "1298" }, + { label: "После таймскипа (Haikyuu!!)", value: "2906" }, + { label: "После таймскипа (Shingeki no Kyojin)", value: "2903" }, + { label: "Послевоенное время", value: "1080" }, + { label: "Последний раз", value: "2645" }, + { label: "Последний рубеж", value: "1178" }, + { label: "Последний шанс", value: "1209" }, + { label: "Последствия", value: "2491" }, + { label: "Последствия болезни", value: "2612" }, + { label: "Послеродовая депрессия", value: "988" }, + { label: "Посмертная любовь", value: "2670" }, + { label: "Посмертный персонаж", value: "2653" }, + { label: "Пост-Пацифист", value: "2408" }, + { label: "Пост-Рейхенбах", value: "1903" }, + { label: "Пост-третий сезон (Шерлок)", value: "1977" }, + { label: "Постапокалиптика", value: "1697" }, + { label: "ПостВБ", value: "2386" }, + { label: "Постканон", value: "6" }, + { label: "ПостРагнарек", value: "2537" }, + { label: "ПостХог", value: "1883" }, + { label: "ПостЭндгейм", value: "2179" }, + { label: "Потеря девственности", value: "1234" }, + { label: "Потеря конечностей", value: "1289" }, + { label: "Потеря магических способностей", value: "1430" }, + { label: "Потеря памяти", value: "36" }, + { label: "Потеря сверхспособностей", value: "1051" }, + { label: "Поттергад", value: "2417" }, + { label: "Похищение", value: "224" }, + { label: "Похороны", value: "786" }, + { label: "Почтовые работники", value: "1821" }, + { label: "Пошлый юмор", value: "1362" }, + { label: "Поэма", value: "1938" }, + { label: "Поэты", value: "357" }, + { label: "Пояс верности", value: "1779" }, + { label: "Правда или действие", value: "1982" }, + { label: "Практопия", value: "2249" }, + { label: "Пре-гет", value: "209" }, + { label: "Пре-Рош", value: "2047" }, + { label: "Пре-слэш", value: "207" }, + { label: "Пре-фемслэш", value: "208" }, + { label: "Предательство", value: "361" }, + { label: "Предвидение", value: "1876" }, + { label: "Предложение руки и сердца", value: "1081" }, + { label: "Предопределенность", value: "411" }, + { label: "Предрассудки", value: "2046" }, + { label: "Предсмертные сообщения", value: "2174" }, + { label: "Преканон", value: "7" }, + { label: "ПреКербер", value: "2726" }, + { label: "Прекрасная эпоха", value: "1809" }, + { label: "Прелюдия", value: "2395" }, + { label: "Пренебрежение гигиеной", value: "2921" }, + { label: "Пренебрежение жизнью", value: "2216" }, + { label: "Преодоление комплексов", value: "588" }, + { label: "Преподаватели", value: "403" }, + { label: "Преподаватель/Обучающийся", value: "2507" }, + { label: "Преступники", value: "1446" }, + { label: "Преступный мир", value: "12" }, + { label: "Прибрежные города", value: "984" }, + { label: "Приватный танец", value: "2786" }, + { label: "Привязанность", value: "2372" }, + { label: "Пригород", value: "452" }, + { label: "Приемные семьи", value: "844" }, + { label: "Признания в любви", value: "1049" }, + { label: "Призраки", value: "26" }, + { label: "Приключения", value: "21" }, + { label: "Приметы / Суеверия", value: "612" }, + { label: "Примирение", value: "660" }, + { label: "Принудительная феминизация", value: "656" }, + { label: "Принудительное лечение", value: "971" }, + { label: "Принудительные отношения", value: "2302" }, + { label: "Принудительный брак", value: "307" }, + { label: "Принудительный инцест", value: "2942" }, + { label: "Принуждение", value: "1323" }, + { label: "Принятие себя", value: "2754" }, + { label: "Прислуга", value: "804" }, + { label: "Прист-кинк", value: "342" }, + { label: "Приступы агрессии", value: "2788" }, + { label: "Притча", value: "1088" }, + { label: "Приюты для животных", value: "2345" }, + { label: "ПРЛ", value: "1092" }, + { label: "Проблемы доверия", value: "2208" }, + { label: "Проблемы с законом", value: "1007" }, + { label: "Проводники душ", value: "2348" }, + { label: "Программисты", value: "574" }, + { label: "Прогрессорство", value: "449" }, + { label: "Прогулки", value: "2462" }, + { label: "Продажа души", value: "887" }, + { label: "Прозопагнозия", value: "2013" }, + { label: "Производственный роман", value: "2119" }, + { label: "Проклятия", value: "875" }, + { label: "Пролапс", value: "2926" }, + { label: "Промискуитет", value: "1817" }, + { label: "Промышленные предприятия", value: "1456" }, + { label: "Пропавшие без вести", value: "2073" }, + { label: "Пропущенная сцена", value: "1702" }, + { label: "Пророчества", value: "834" }, + { label: "Просмотр порно", value: "2341" }, + { label: "Проституция", value: "178" }, + { label: "Протезы", value: "1581" }, + { label: "Противоположности", value: "948" }, + { label: "Противоречивые чувства", value: "2528" }, + { label: "Прошлое", value: "983" }, + { label: "Прощение", value: "1545" }, + { label: "Псевдо-инцест", value: "336" }, + { label: "Псевдо-мистика", value: "2159" }, + { label: "Псевдоисторический сеттинг", value: "1479" }, + { label: "Псионика", value: "2023" }, + { label: "Психиатрические больницы", value: "135" }, + { label: "Психические расстройства", value: "69" }, + { label: "Психоз", value: "1339" }, + { label: "Психологи / Психоаналитики", value: "611" }, + { label: "Психологическая война", value: "2595" }, + { label: "Психологические пытки", value: "2825" }, + { label: "Психологические травмы", value: "40" }, + { label: "Психологический мазохизм", value: "2212" }, + { label: "Психологический ужас", value: "1261" }, + { label: "Психологическое насилие", value: "836" }, + { label: "Психология", value: "1674" }, + { label: "Психопатия", value: "869" }, + { label: "Психосоматические расстройства", value: "1834" }, + { label: "Психотерапия", value: "958" }, + { label: "ПТСР", value: "425" }, + { label: "Публичное обнажение", value: "319" }, + { label: "Пурпурная проза", value: "599" }, + { label: "Пустоши", value: "1114" }, + { label: "Пустыни", value: "692" }, + { label: "Путешественники-исследователи", value: "888" }, + { label: "Путешествия", value: "894" }, + { label: "Пытки", value: "210" }, + { label: "Пьеса", value: "434" }, + { label: "Рабочие", value: "1771" }, + { label: "Рабство", value: "51" }, + { label: "Равнодушие", value: "2516" }, + { label: "Равные отношения", value: "2728" }, + { label: "Радикальная медицина", value: "731" }, + { label: "Радио", value: "974" }, + { label: "Разбойники", value: "473" }, + { label: "Развитие отношений", value: "1404" }, + { label: "Развод", value: "200" }, + { label: "Разговоры", value: "1401" }, + { label: "Раздвоение личности", value: "287" }, + { label: "Раздельное обучение", value: "1959" }, + { label: "Разлука / Прощания", value: "1104" }, + { label: "Разница в возрасте", value: "2" }, + { label: "Разница культур", value: "1079" }, + { label: "Разновозрастная дружба", value: "2402" }, + { label: "Разнополая дружба", value: "806" }, + { label: "Разоблачения", value: "2684" }, + { label: "Разочарования", value: "646" }, + { label: "Разрушение стереотипов", value: "2805" }, + { label: "Разрушение четвертой стены", value: "381" }, + { label: "Разрыв связи", value: "2582" }, + { label: "Разум роя", value: "1459" }, + { label: "Разумное оружие", value: "2675" }, + { label: "Разумные животные", value: "895" }, + { label: "Разумные растения", value: "1037" }, + { label: "Раптофилия", value: "729" }, + { label: "Расизм", value: "433" }, + { label: "Раскрытие личностей", value: "2120" }, + { label: "Раскрытие магии", value: "1878" }, + { label: "Расплата", value: "2464" }, + { label: "Рассказ в рассказе", value: "2077" }, + { label: "Расставание", value: "506" }, + { label: "Расстройства аутистического спектра", value: "541" }, + { label: "Расстройства цветового восприятия", value: "1330" }, + { label: "Расстройства шизофренического спектра", value: "912" }, + { label: "Растаманы", value: "857" }, + { label: "Расширенная Вселенная ЗВ", value: "1950" }, + { label: "Реабилитационные центры", value: "1467" }, + { label: "Реактивное расстройство привязанности", value: "2576" }, + { label: "Реакции (стилизация)", value: "1283" }, + { label: "Реализм", value: "1201" }, + { label: "Реалити-шоу", value: "2312" }, + { label: "Ревность", value: "544" }, + { label: "Революции", value: "585" }, + { label: "Регенерация", value: "1530" }, + { label: "Регресс", value: "450" }, + { label: "Регрессия возраста", value: "102" }, + { label: "Редкие заболевания", value: "1856" }, + { label: "Реинкарнация", value: "101" }, + { label: "Рейтинг за лексику", value: "276" }, + { label: "Рейтинг за насилие и/или жестокость", value: "277" }, + { label: "Рейтинг за секс", value: "278" }, + { label: "Религиозная нетерпимость", value: "2451" }, + { label: "Религиозные войны", value: "2911" }, + { label: "Религиозные темы и мотивы", value: "198" }, + { label: "Религиозный фанатизм", value: "2548" }, + { label: "Ренессанс", value: "239" }, + { label: "Репетиторы", value: "2413" }, + { label: "Репетиции", value: "2241" }, + { label: "Репродуктивное насилие", value: "2310" }, + { label: "Реставраторы", value: "2239" }, + { label: "Рестораны", value: "412" }, + { label: "Ретеллинг", value: "52" }, + { label: "Ретрофутуризм", value: "727" }, + { label: "Реформация", value: "2199" }, + { label: "Речь Посполитая", value: "1529" }, + { label: "Риветхеды", value: "2904" }, + { label: "Римминг", value: "170" }, + { label: "Риск", value: "2467" }, + { label: "Рискованная беременность", value: "998" }, + { label: "Ритуалы", value: "749" }, + { label: "Ритуальные услуги", value: "2362" }, + { label: "Роботы", value: "513" }, + { label: "Родео", value: "1314" }, + { label: "Родители-одиночки", value: "325" }, + { label: "Родительские чувства", value: "2308" }, + { label: "Родомагия", value: "1899" }, + { label: "Роды", value: "991" }, + { label: "Рождество", value: "211" }, + { label: "Розыгрыши", value: "1807" }, + { label: "Рокеры", value: "861" }, + { label: "Роковая женщина / Роковой мужчина", value: "982" }, + { label: "Роковое усилие", value: "2604" }, + { label: "Роллер-дерби", value: "1014" }, + { label: "Романтизация", value: "1898" }, + { label: "Романтика", value: "1664" }, + { label: "Романтическая дружба", value: "2307" }, + { label: "Ромком", value: "2768" }, + { label: "Российская империя", value: "475" }, + { label: "Россия", value: "701" }, + { label: "РПП", value: "286" }, + { label: "Румыния", value: "862" }, + { label: "Русалки", value: "32" }, + { label: "Русальная неделя", value: "1067" }, + { label: "Русреал", value: "53" }, + { label: "Руферы", value: "578" }, + { label: "Рыбалка", value: "855" }, + { label: "Рыцари", value: "426" }, + { label: "Рыцарские турниры", value: "2759" }, + { label: "С чистого листа", value: "987" }, + { label: "Савойя", value: "1036" }, + { label: "Садизм / Мазохизм", value: "461" }, + { label: "Садовники", value: "1348" }, + { label: "Сайз-кинк", value: "100" }, + { label: "Самобытные культуры", value: "663" }, + { label: "Самовнушение", value: "2169" }, + { label: "Самовставка", value: "1689" }, + { label: "Самозванцы", value: "1811" }, + { label: "Самоистязание", value: "1386" }, + { label: "Самоопределение / Самопознание", value: "1278" }, + { label: "Самопожертвование", value: "750" }, + { label: "Самосуд", value: "1402" }, + { label: "Самоуничижение", value: "2667" }, + { label: "Самураи", value: "472" }, + { label: "Санатории", value: "223" }, + { label: "Сандалпанк", value: "1986" }, + { label: "Сарказм", value: "2225" }, + { label: "Сатира", value: "1944" }, + { label: "Сатиры", value: "264" }, + { label: "Сахарный диабет", value: "2609" }, + { label: "Сборник драбблов", value: "39" }, + { label: "Сборник лимериков", value: "1989" }, + { label: "Сборник пирожков", value: "743" }, + { label: "Сборник стихотворений", value: "600" }, + { label: "Сборник японской поэзии", value: "1351" }, + { label: "Свадьба", value: "184" }, + { label: "Сверхразумы", value: "966" }, + { label: "Сверхскорость", value: "1902" }, + { label: "Сверхспособности", value: "597" }, + { label: "Светлые властелины", value: "2596" }, + { label: "Светские мероприятия", value: "973" }, + { label: "Свидание вслепую", value: "741" }, + { label: "Свидания", value: "1235" }, + { label: "Свидетели", value: "1242" }, + { label: "Свингерство", value: "203" }, + { label: "Свобода", value: "2166" }, + { label: "Свободные отношения", value: "255" }, + { label: "Сводничество", value: "568" }, + { label: "Сводные родственники", value: "1445" }, + { label: "Святые", value: "393" }, + { label: "Сгенерировано ИИ", value: "3014" }, + { label: "СДВГ", value: "539" }, + { label: "Севвитус", value: "1871" }, + { label: "Северная Корея", value: "2767" }, + { label: "Северус Снейп жив", value: "2406" }, + { label: "Сегрегация", value: "2158" }, + { label: "Секретари", value: "1170" }, + { label: "Секретные миссии", value: "1829" }, + { label: "Секс без обязательств", value: "387" }, + { label: "Секс в воде", value: "718" }, + { label: "Секс в нетрезвом виде", value: "1110" }, + { label: "Секс в одежде", value: "1013" }, + { label: "Секс в публичных местах", value: "123" }, + { label: "Секс в транспорте", value: "124" }, + { label: "Секс в церкви", value: "2968" }, + { label: "Секс во время беременности", value: "279" }, + { label: "Секс во время болезни", value: "1365" }, + { label: "Секс во время менструации", value: "153" }, + { label: "Секс на камеру", value: "1101" }, + { label: "Секс на природе", value: "1256" }, + { label: "Секс перед зеркалом", value: "1255" }, + { label: "Секс по расчету", value: "1212" }, + { label: "Секс по телефону", value: "133" }, + { label: "Секс при посторонних", value: "2514" }, + { label: "Секс с воображаемыми партнерами", value: "2276" }, + { label: "Секс с использованием магии", value: "1563" }, + { label: "Секс с использованием одурманивающих веществ", value: "2635" }, + { label: "Секс с использованием посторонних предметов", value: "1661" }, + { label: "Секс с использованием сверхспособностей", value: "2610" }, + { label: "Секс со смертельным исходом", value: "1171" }, + { label: "Секс-игрушки", value: "176" }, + { label: "Секс-индустрия", value: "447" }, + { label: "Секс-клубы / Секс-вечеринки", value: "936" }, + { label: "Секс-куклы", value: "1202" }, + { label: "Секс-магия", value: "1131" }, + { label: "Секс-машины", value: "1112" }, + { label: "Секс-шопы", value: "739" }, + { label: "Сексизм", value: "464" }, + { label: "Сексомния", value: "2024" }, + { label: "Секстинг", value: "2215" }, + { label: "Сексуальная неопытность", value: "1717" }, + { label: "Сексуальное насилие", value: "2495" }, + { label: "Сексуальное обучение", value: "967" }, + { label: "Сексуальное рабство", value: "1353" }, + { label: "Сексуальные фобии", value: "1537" }, + { label: "Селфхарм", value: "76" }, + { label: "Селфцест", value: "1708" }, + { label: "Семейная сага", value: "920" }, + { label: "Семейные тайны", value: "779" }, + { label: "Семейный бизнес", value: "2338" }, + { label: "Семьи", value: "220" }, + { label: "Сенсорная депривация", value: "282" }, + { label: "Сенсорная перегрузка", value: "2351" }, + { label: "Сентиментальность", value: "2567" }, + { label: "Серая мораль", value: "839" }, + { label: "Серая мышь", value: "1250" }, + { label: "Серая реальность", value: "2278" }, + { label: "Серийные насильники", value: "1942" }, + { label: "Серийные убийцы", value: "193" }, + { label: "Серфинг", value: "764" }, + { label: "Сиамские близнецы", value: "1252" }, + { label: "Сибари", value: "131" }, + { label: "Сиблинги", value: "617" }, + { label: "Сидение на лице", value: "713" }, + { label: "Силкпанк", value: "1813" }, + { label: "Симбиоз", value: "849" }, + { label: "Символизм", value: "538" }, + { label: "Симфорофилия", value: "492" }, + { label: "Синдром восьмиклассника", value: "2032" }, + { label: "Синдром выжившего", value: "1100" }, + { label: "Синдром Дауна", value: "2106" }, + { label: "Синдром Котара", value: "2765" }, + { label: "Синдром Питера Пэна", value: "687" }, + { label: "Синдром Туретта", value: "946" }, + { label: "Синестезия", value: "960" }, + { label: "Сирены", value: "1830" }, + { label: "Сириус Блэк жив", value: "2617" }, + { label: "Сироты", value: "689" }, + { label: "Ситком", value: "2553" }, + { label: "Сказка", value: "288" }, + { label: "Скайпанк", value: "976" }, + { label: "Скандинавия", value: "781" }, + { label: "Скандинавская мифология", value: "2304" }, + { label: "Сквиртинг", value: "950" }, + { label: "Сквозное видение", value: "1280" }, + { label: "Скейтбординг", value: "1395" }, + { label: "Скелеты", value: "1766" }, + { label: "Скинхэды", value: "1757" }, + { label: "Скрытая беременность", value: "2580" }, + { label: "Скрытность", value: "2577" }, + { label: "Скрытые способности", value: "2579" }, + { label: "Скрытый Этаж", value: "2763" }, + { label: "Скульпторы", value: "2976" }, + { label: "Слатшейминг", value: "2910" }, + { label: "Следующее поколение", value: "698" }, + { label: "Слежка", value: "2102" }, + { label: "Слепота", value: "413" }, + { label: "Слияние", value: "2324" }, + { label: "Словакия", value: "2938" }, + { label: "Словения", value: "1788" }, + { label: "Сложные отношения", value: "326" }, + { label: "Слом личности", value: "623" }, + { label: "Слоуберн", value: "19" }, + { label: "Служебные животные", value: "1506" }, + { label: "Служебные отношения", value: "1347" }, + { label: "Служебный роман", value: "75" }, + { label: "Слухи / Сплетни", value: "2809" }, + { label: "Случайные убийства", value: "2673" }, + { label: "Случайный брак", value: "304" }, + { label: "Случайный поцелуй", value: "2852" }, + { label: "Случайный секс", value: "1275" }, + { label: "Смена имени", value: "2094" }, + { label: "Смена мировоззрения", value: "2865" }, + { label: "Смена сущности", value: "1707" }, + { label: "Смертельные заболевания", value: "1132" }, + { label: "Смерть антагониста", value: "2650" }, + { label: "Смерть всех персонажей", value: "2611" }, + { label: "Смерть второстепенных персонажей", value: "1713" }, + { label: "Смерть животных", value: "2210" }, + { label: "Смерть основных персонажей", value: "1647" }, + { label: "СМИ", value: "2656" }, + { label: "Смирение", value: "2272" }, + { label: "Смотрины", value: "1495" }, + { label: "Смутное время", value: "2002" }, + { label: "Снайперы", value: "1762" }, + { label: "Сновидения", value: "490" }, + { label: "Сноубординг", value: "2180" }, + { label: "Соблазнение / Ухаживания", value: "616" }, + { label: "Собственничество", value: "908" }, + { label: "Советпанк", value: "397" }, + { label: "Советский Союз", value: "468" }, + { label: "Совместная кровать", value: "1229" }, + { label: "Совместное купание", value: "1301" }, + { label: "Современное искусство", value: "1087" }, + { label: "Современность", value: "80" }, + { label: "Согласование с каноном", value: "1388" }, + { label: "Сожаления", value: "550" }, + { label: "Создание общества", value: "2643" }, + { label: "Созидание", value: "2590" }, + { label: "Сокровища", value: "893" }, + { label: "Сокрытие убийства", value: "2311" }, + { label: "Солянка из Сансов", value: "2515" }, + { label: "Сомелье / Бармены", value: "865" }, + { label: "Сомнамбулизм", value: "1394" }, + { label: "Сомнения", value: "1544" }, + { label: "Сомнофилия", value: "95" }, + { label: "Сонный паралич", value: "1203" }, + { label: "Соперничество", value: "671" }, + { label: "Сорванная свадьба", value: "2044" }, + { label: "Сострадание", value: "885" }, + { label: "Состязания", value: "910" }, + { label: "Состязания с участием животных", value: "1154" }, + { label: "Сотриголова — отец Хитоши Шинсо", value: "2877" }, + { label: "Соулмейты", value: "1696" }, + { label: "Соулмейты: Действия", value: "2575" }, + { label: "Соулмейты: Изменение восприятия", value: "1400" }, + { label: "Соулмейты: Контакт через кожу", value: "2608" }, + { label: "Соулмейты: Ментальная связь", value: "2841" }, + { label: "Соулмейты: Общая боль", value: "2583" }, + { label: "Соулмейты: Общие эмоции", value: "2637" }, + { label: "Соулмейты: Опознавательные фразы", value: "1399" }, + { label: "Соулмейты: Предметы", value: "2563" }, + { label: "Соулмейты: Предпочтения", value: "2521" }, + { label: "Соулмейты: Разноцветные глаза", value: "2461" }, + { label: "Соулмейты: Ранения", value: "2134" }, + { label: "Соулмейты: Сны", value: "2414" }, + { label: "Соулмейты: Тактильный контакт", value: "2190" }, + { label: "Соулмейты: Татуировки", value: "2711" }, + { label: "Соулмейты: Физические проявления", value: "2864" }, + { label: "Социальная фантастика", value: "591" }, + { label: "Социальные темы и мотивы", value: "1017" }, + { label: "Социальные эксперименты", value: "2756" }, + { label: "Социальный паразитизм", value: "2444" }, + { label: "Социофобия", value: "826" }, + { label: "Спарринги", value: "1575" }, + { label: "Спасатели", value: "392" }, + { label: "Спасение жизни", value: "1299" }, + { label: "Спасение мира", value: "812" }, + { label: "Спектрофилия", value: "2634" }, + { label: "Спецагенты", value: "401" }, + { label: "Спиритизм", value: "1478" }, + { label: "Сплошинг", value: "424" }, + { label: "Спонтанный секс", value: "2918" }, + { label: "Спорт", value: "15" }, + { label: "Спортзалы", value: "1359" }, + { label: "Сражения", value: "2868" }, + { label: "Средневековье", value: "238" }, + { label: "Ссоры / Конфликты", value: "645" }, + { label: "Стаи", value: "2884" }, + { label: "Сталкеры / Диггеры (субкультура)", value: "1029" }, + { label: "Сталкинг", value: "605" }, + { label: "Становление героя", value: "680" }, + { label: "Стёб", value: "1690" }, + { label: "Стереотипы", value: "809" }, + { label: "Стимпанк", value: "25" }, + { label: "Стимуляция руками", value: "225" }, + { label: "Стихи", value: "1692" }, + { label: "Стихийные бедствия", value: "1352" }, + { label: "Стихотворные вставки", value: "2404" }, + { label: "СтихоТворцы", value: "3015" }, + { label: "Стокгольмский синдром / Лимский синдром", value: "1217" }, + { label: "Столетняя война", value: "2243" }, + { label: "Сторонний рассказчик", value: "1383" }, + { label: "Стоунпанк", value: "1987" }, + { label: "Страдания", value: "2009" }, + { label: "Странногеддон", value: "1882" }, + { label: "Страны Балтии", value: "1031" }, + { label: "Страпоны", value: "380" }, + { label: "Страсть", value: "1955" }, + { label: "Страх перед родителями", value: "2332" }, + { label: "Стрельба из лука", value: "1791" }, + { label: "Стримеры / Ютуберы", value: "1473" }, + { label: "Стрип-клубы", value: "79" }, + { label: "Стрит-арт", value: "1012" }, + { label: "Строители", value: "1774" }, + { label: "Студенты", value: "274" }, + { label: "Студенты по обмену", value: "859" }, + { label: "Студенческие городки", value: "753" }, + { label: "Субкультуры", value: "1061" }, + { label: "Субординация", value: "1099" }, + { label: "Суд", value: "827" }, + { label: "Суккубы / Инкубы", value: "334" }, + { label: "Сумасшествие", value: "1239" }, + { label: "Сунагакуре", value: "2476" }, + { label: "Супергерои", value: "67" }, + { label: "Суперзлодеи", value: "1205" }, + { label: "Супермаркеты", value: "843" }, + { label: "Суррогатная беременность", value: "1427" }, + { label: "Сухой закон", value: "2100" }, + { label: "Сухой оргазм", value: "2818" }, + { label: "Сфера ИТ", value: "2534" }, + { label: "Сфинксы", value: "1349" }, + { label: "Сценарий (стилизация)", value: "323" }, + { label: "Счастливый финал", value: "3" }, + { label: "США", value: "640" }, + { label: "Съемочная площадка", value: "410" }, + { label: "Сын Тьмы", value: "2175" }, + { label: "Сэнгоку Джидай", value: "2380" }, + { label: "Сэнгоку Дзидай", value: "2834" }, + { label: "Сэцубун", value: "2969" }, + { label: "Сюрреализм / Фантасмагория", value: "872" }, + { label: "Т/И / Л/И", value: "167" }, + { label: "Таджикистан", value: "2974" }, + { label: "Таиланд", value: "1034" }, + { label: "Тайна происхождения", value: "662" }, + { label: "Тайная личность", value: "194" }, + { label: "Тайная сущность", value: "1512" }, + { label: "Тайны / Секреты", value: "906" }, + { label: "Тайные друзья (игра)", value: "2908" }, + { label: "Тайные организации", value: "572" }, + { label: "Тайные поклонники", value: "1374" }, + { label: "Тайный НапиСанта", value: "2972" }, + { label: "Таксисты", value: "2679" }, + { label: "Тактильный голод", value: "2197" }, + { label: "Тактильный контакт", value: "2875" }, + { label: "Танабата", value: "2340" }, + { label: "Танцы", value: "108" }, + { label: "Танцы на пилоне", value: "1370" }, + { label: "Таро", value: "642" }, + { label: "Тату-салоны", value: "324" }, + { label: "Татуировки", value: "327" }, + { label: "Твинцест", value: "1652" }, + { label: "Творческий кризис", value: "2107" }, + { label: "Театры", value: "195" }, + { label: "Телекинез", value: "951" }, + { label: "Телепатия", value: "127" }, + { label: "Телепортация", value: "1794" }, + { label: "Телесные жидкости", value: "1220" }, + { label: "Телесные наказания", value: "132" }, + { label: "Телесный хоррор", value: "212" }, + { label: "Телохранители", value: "400" }, + { label: "Темная сторона (Гарри Поттер)", value: "1937" }, + { label: "Темная сторона Силы", value: "1946" }, + { label: "Темная Эра (Bungou Stray Dogs)", value: "2494" }, + { label: "Темное прошлое", value: "2229" }, + { label: "Темное фэнтези", value: "1963" }, + { label: "Темные властелины", value: "1224" }, + { label: "Темные эльфы", value: "2597" }, + { label: "Темный романтизм", value: "2128" }, + { label: "Темы ментального здоровья", value: "2344" }, + { label: "Темы этики и морали", value: "1106" }, + { label: "Теннис", value: "1066" }, + { label: "Тентакли", value: "129" }, + { label: "Теория инверсии", value: "1980" }, + { label: "Теория Одурманивания", value: "2493" }, + { label: "Термокинез", value: "2793" }, + { label: "Террористы", value: "556" }, + { label: "Теслапанк", value: "1988" }, + { label: "Техники", value: "1272" }, + { label: "Техногенные катастрофы", value: "526" }, + { label: "Технокинез", value: "1271" }, + { label: "Технологии", value: "1232" }, + { label: "Технотриллер", value: "649" }, + { label: "Течка / Гон", value: "283" }, + { label: "Тим Дрейк — Джокер-младший", value: "2454" }, + { label: "Тим Дрейк — Кэтлэд", value: "2453" }, + { label: "Тихий секс", value: "1185" }, + { label: "Товарищи по несчастью", value: "2666" }, + { label: "Токсичные родственники", value: "2008" }, + { label: "Том Реддл — не Темный Лорд", value: "2394" }, + { label: "Томбои", value: "565" }, + { label: "Томгерлз", value: "848" }, + { label: "Торговля органами", value: "1358" }, + { label: "Торговцы", value: "2098" }, + { label: "Торговые центры", value: "1416" }, + { label: "Тоталитаризм", value: "1169" }, + { label: "Травники / Травницы", value: "1211" }, + { label: "Трагедия", value: "919" }, + { label: "Трагикомедия", value: "1454" }, + { label: "Традиции", value: "1463" }, + { label: "Трансгендерные персонажи", value: "175" }, + { label: "Трансплантация", value: "2364" }, + { label: "Трансфобия", value: "420" }, + { label: "Тревожное расстройство личности", value: "1914" }, + { label: "Тревожность", value: "1318" }, + { label: "Трейлеры", value: "785" }, + { label: "Тренировки / Обучение", value: "876" }, + { label: "Третий лишний", value: "1176" }, + { label: "Третья мировая", value: "624" }, + { label: "Три корейских государства", value: "2322" }, + { label: "Трикстеры", value: "1507" }, + { label: "Триллер", value: "169" }, + { label: "Трилогия Юникрона", value: "2378" }, + { label: "Трипофобия", value: "2342" }, + { label: "Трисам", value: "758" }, + { label: "Трихотилломания", value: "2185" }, + { label: "Тролли", value: "190" }, + { label: "Трудные отношения с родителями", value: "1490" }, + { label: "Трудный характер", value: "2123" }, + { label: "Трудоголизм", value: "1848" }, + { label: "Трущобы", value: "2822" }, + { label: "Туалетный юмор", value: "1247" }, + { label: "Туберкулез", value: "2163" }, + { label: "Турция", value: "794" }, + { label: "Тщеславие", value: "2578" }, + { label: "Тюрьмы / Темницы", value: "117" }, + { label: "Тяжелое детство", value: "2614" }, + { label: "Убийства", value: "621" }, + { label: "Убийственная пара", value: "1918" }, + { label: "Убийца поневоле", value: "2671" }, + { label: "Уважение", value: "2688" }, + { label: "Ужасы", value: "1679" }, + { label: "Узбекистан", value: "1293" }, + { label: "Уизлигады", value: "1862" }, + { label: "Украина", value: "716" }, + { label: "Укрытия", value: "199" }, + { label: "Уличные артисты", value: "2550" }, + { label: "Уличные гонки", value: "880" }, + { label: "Умбракинез", value: "2355" }, + { label: "Универсалы", value: "657" }, + { label: "Унижения", value: "1319" }, + { label: "Упоминания аддикций", value: "2848" }, + { label: "Упоминания алкоголя", value: "2415" }, + { label: "Упоминания беременности", value: "2057" }, + { label: "Упоминания войны", value: "2707" }, + { label: "Упоминания жестокости", value: "2447" }, + { label: "Упоминания зоофилии", value: "2620" }, + { label: "Упоминания измены", value: "2876" }, + { label: "Упоминания изнасилования", value: "521" }, + { label: "Упоминания инвалидности", value: "2705" }, + { label: "Упоминания инцеста", value: "2285" }, + { label: "Упоминания каннибализма", value: "2427" }, + { label: "Упоминания курения", value: "2450" }, + { label: "Упоминания мужской беременности", value: "2226" }, + { label: "Упоминания наркотиков", value: "144" }, + { label: "Упоминания насилия", value: "2207" }, + { label: "Упоминания нездоровых отношений", value: "2892" }, + { label: "Упоминания проституции", value: "2618" }, + { label: "Упоминания пыток", value: "2410" }, + { label: "Упоминания расизма", value: "2737" }, + { label: "Упоминания религии", value: "1048" }, + { label: "Упоминания самоубийства", value: "77" }, + { label: "Упоминания секса", value: "2747" }, + { label: "Упоминания селфхарма", value: "2105" }, + { label: "Упоминания смертей", value: "2081" }, + { label: "Упоминания смертей животных", value: "2211" }, + { label: "Упоминания телесного хоррора", value: "2466" }, + { label: "Упоминания терроризма", value: "774" }, + { label: "Упоминания убийств", value: "1501" }, + { label: "Управление оргазмом", value: "118" }, + { label: "Управление стихиями", value: "2091" }, + { label: "Уретральное проникновение", value: "1469" }, + { label: "Уро-кинк", value: "762" }, + { label: "Условное бессмертие", value: "2117" }, + { label: "Уся / Сянься", value: "2714" }, + { label: "Утопия", value: "350" }, + { label: "Утраченная тройка", value: "2535" }, + { label: "Утренний секс", value: "2882" }, + { label: "Уход за персонажами с инвалидностью", value: "2133" }, + { label: "Ухудшение отношений", value: "1219" }, + { label: "Учебные заведения", value: "1694" }, + { label: "Ученые", value: "402" }, + { label: "Уют", value: "2738" }, + { label: "Фавориты", value: "2135" }, + { label: "Фамильяры", value: "1045" }, + { label: "Фан-Бинго", value: "2957" }, + { label: "Фан-Фанты", value: "2953" }, + { label: "Фандомная аналитика", value: "742" }, + { label: "Фандомная Битва", value: "253" }, + { label: "Фансервис", value: "2483" }, + { label: "Фантастика", value: "1670" }, + { label: "Фастберн", value: "2016" }, + { label: "Фастфуд / Закусочные", value: "1025" }, + { label: "Феи", value: "191" }, + { label: "Фельчинг", value: "2368" }, + { label: "Фемдом", value: "54" }, + { label: "Феминистские темы и мотивы", value: "523" }, + { label: "Феминитивы", value: "390" }, + { label: "Фениксы", value: "1215" }, + { label: "Феодализм", value: "1504" }, + { label: "Фермы / Ранчо", value: "329" }, + { label: "Фестивали", value: "1006" }, + { label: "Фехтование", value: "1775" }, + { label: "Фиггинг", value: "1439" }, + { label: "Фигурное катание", value: "460" }, + { label: "Физиотерапия", value: "2222" }, + { label: "Физическая сверхсила", value: "2880" }, + { label: "Фикрайтеры / Фикридеры", value: "1284" }, + { label: "Фиксированная раскладка", value: "1344" }, + { label: "Фиктивные отношения", value: "308" }, + { label: "Фиктивный брак", value: "34" }, + { label: "Философия", value: "1675" }, + { label: "Финляндия", value: "1133" }, + { label: "Фистинг", value: "151" }, + { label: "Флафф", value: "1667" }, + { label: "Флирт", value: "349" }, + { label: "Флот", value: "846" }, + { label: "Фобии", value: "977" }, + { label: "Фольклор и предания", value: "1273" }, + { label: "Форнифилия", value: "580" }, + { label: "Фотографы", value: "348" }, + { label: "Фотокинез", value: "1805" }, + { label: "Франция", value: "691" }, + { label: "Фред Уизли жив", value: "2941" }, + { label: "Фроттаж", value: "96" }, + { label: "Фуд-плей", value: "509" }, + { label: "Фуд-фетиш", value: "2168" }, + { label: "Фурри", value: "996" }, + { label: "Фут-фетиш", value: "432" }, + { label: "Футбол", value: "458" }, + { label: "Фэйри", value: "2028" }, + { label: "Фэнтези", value: "1669" }, + { label: "Хакеры", value: "340" }, + { label: "Ханами", value: "1155" }, + { label: "Ханахаки", value: "45" }, + { label: "Ханжество", value: "882" }, + { label: "Характерная для канона жестокость", value: "896" }, + { label: "Харассмент", value: "477" }, + { label: "Хиерофилия", value: "2366" }, + { label: "Химеры", value: "816" }, + { label: "Химическое оружие", value: "1186" }, + { label: "Химэдэрэ / Оджидэрэ", value: "1363" }, + { label: "Хип-хоп (субкультура)", value: "1822" }, + { label: "Хиппи", value: "784" }, + { label: "Хиромантия", value: "2749" }, + { label: "Хирургическая коррекция пола", value: "1249" }, + { label: "Хирургические операции", value: "1845" }, + { label: "Хобби", value: "1161" }, + { label: "Хогсмид", value: "2388" }, + { label: "Хоккей", value: "828" }, + { label: "Холодная война", value: "2140" }, + { label: "Холодное оружие", value: "1187" }, + { label: "Хорватия", value: "1433" }, + { label: "Хороший плохой финал", value: "1266" }, + { label: "Хронические заболевания", value: "1267" }, + { label: "Хронокинез", value: "1447" }, + { label: "Хронофантастика", value: "56" }, + { label: "Хтонические существа", value: "504" }, + { label: "Художники", value: "140" }, + { label: "Хуманизация", value: "177" }, + { label: "Хэллоуин", value: "205" }, + { label: "Хюльдры", value: "1397" }, + { label: "Цветочные магазины", value: "164" }, + { label: "Целители", value: "1107" }, + { label: "Церкви", value: "533" }, + { label: "Цикличность", value: "1979" }, + { label: "Цирки", value: "259" }, + { label: "Цукими", value: "1798" }, + { label: "Цундэрэ", value: "1137" }, + { label: "Цыгане", value: "1091" }, + { label: "Чайлдфри", value: "592" }, + { label: "Чайные церемонии", value: "2416" }, + { label: "Частичный ООС", value: "1409" }, + { label: "Частные детективы", value: "427" }, + { label: "Черная археология", value: "2503" }, + { label: "Черная мораль", value: "2438" }, + { label: "Чернобыльская катастрофа", value: "775" }, + { label: "Черный юмор", value: "66" }, + { label: "Чехия", value: "1021" }, + { label: "Чиновники", value: "1991" }, + { label: "Чирлидинг", value: "1431" }, + { label: "Чистокровные AU (Гарри Поттер)", value: "1905" }, + { label: "Чосон", value: "571" }, + { label: "Чувство вины", value: "902" }, + { label: "Чумные доктора", value: "1125" }, + { label: "Чхусок", value: "2973" }, + { label: "Шаманы", value: "484" }, + { label: "Шантаж", value: "569" }, + { label: "Шармбатон", value: "2733" }, + { label: "Швейцария", value: "1022" }, + { label: "Шейпшифтеры", value: "29" }, + { label: "Шелки", value: "2431" }, + { label: "Шифры", value: "2232" }, + { label: "Школьная иерархия", value: "2787" }, + { label: "Школьники", value: "2566" }, + { label: "Школьные годы Тома Реддла", value: "1913" }, + { label: "Школьные ОтМетки", value: "3017" }, + { label: "Школьные поездки", value: "2481" }, + { label: "Школьный роман", value: "2440" }, + { label: "Шопинг", value: "864" }, + { label: "Шоу-бизнес", value: "17" }, + { label: "Шпионы", value: "148" }, + { label: "Шрамирование", value: "891" }, + { label: "Шрамы", value: "328" }, + { label: "Эвтаназия", value: "1102" }, + { label: "Эгоизм", value: "2071" }, + { label: "Эдвардианская эпоха", value: "2975" }, + { label: "Эзотерические темы и мотивы", value: "2801" }, + { label: "Эйблизм", value: "453" }, + { label: "Эйдетическая память", value: "975" }, + { label: "Эйфория", value: "2458" }, + { label: "Экзамены", value: "2591" }, + { label: "Экзистенциальный кризис", value: "1200" }, + { label: "Экзорцизм", value: "494" }, + { label: "Экологи / Биологи", value: "631" }, + { label: "Экологический кризис", value: "485" }, + { label: "Экономический кризис", value: "1547" }, + { label: "Эксаудиризм", value: "2151" }, + { label: "Эксгибиционизм", value: "489" }, + { label: "Эксперимент", value: "1686" }, + { label: "Экстрасенсы", value: "371" }, + { label: "Экшн", value: "1673" }, + { label: "Электрокинез", value: "1857" }, + { label: "Электростимуляция", value: "807" }, + { label: "Элементы ангста", value: "2696" }, + { label: "Элементы гета", value: "1714" }, + { label: "Элементы дарка", value: "2703" }, + { label: "Элементы детектива", value: "2698" }, + { label: "Элементы драмы", value: "2697" }, + { label: "Элементы других видов отношений", value: "454" }, + { label: "Элементы мистики", value: "1324" }, + { label: "Элементы психологии", value: "2824" }, + { label: "Элементы романтики", value: "2742" }, + { label: "Элементы слэша", value: "1715" }, + { label: "Элементы ужасов", value: "2721" }, + { label: "Элементы фемслэша", value: "1716" }, + { label: "Элементы флаффа", value: "2731" }, + { label: "Элементы юмора / Элементы стёба", value: "2011" }, + { label: "Эльфы", value: "44" }, + { label: "Эметофилия", value: "1434" }, + { label: "Эмо", value: "641" }, + { label: "Эмоциональная одержимость", value: "1270" }, + { label: "Эмпатия", value: "1177" }, + { label: "Энергетический вампиризм", value: "818" }, + { label: "Эпидемии", value: "619" }, + { label: "Эпизод 23.5", value: "2469" }, + { label: "Эпилепсия", value: "1511" }, + { label: "Эпилог? Какой эпилог?", value: "2651" }, + { label: "Эпоха Бакумацу", value: "1472" }, + { label: "Эпоха Мэйдзи", value: "1435" }, + { label: "Эпоха Тайсё", value: "2015" }, + { label: "Эрик Киллмонгер жив", value: "2897" }, + { label: "Эрогуро", value: "1731" }, + { label: "Эротическая дрессировка", value: "2026" }, + { label: "Эротическая лактация", value: "284" }, + { label: "Эротическая мумификация", value: "415" }, + { label: "Эротическая сверхстимуляция", value: "1245" }, + { label: "Эротические наказания", value: "125" }, + { label: "Эротические ролевые игры", value: "111" }, + { label: "Эротические сны", value: "128" }, + { label: "Эротические фантазии", value: "561" }, + { label: "Эротический массаж", value: "1518" }, + { label: "Эротический перенос", value: "744" }, + { label: "Эротомания", value: "2922" }, + { label: "Эскапизм", value: "1552" }, + { label: "Эскорты", value: "922" }, + { label: "Эстетика", value: "2021" }, + { label: "ЭТЕРНА", value: "2948" }, + { label: "Этнические анклавы", value: "2732" }, + { label: "Этническое фэнтези", value: "667" }, + { label: "Эффект бабочки", value: "1572" }, + { label: "Ювелиры", value: "2964" }, + { label: "Южная Америка", value: "832" }, + { label: "Южная готика", value: "1077" }, + { label: "Южная Корея", value: "674" }, + { label: "Юмор", value: "1666" }, + { label: "Юристы", value: "333" }, + { label: "Я никогда не... (игра)", value: "1436" }, + { label: "Явное согласие", value: "1569" }, + { label: "Ядерная война", value: "889" }, + { label: "Язык жестов", value: "2962" }, + { label: "Язык животных", value: "1327" }, + { label: "Язык растений", value: "2622" }, + { label: "Язык цветов", value: "272" }, + { label: "Ямадад", value: "2916" }, + { label: "Янгирэ", value: "1257" }, + { label: "Яндэрэ", value: "1136" }, + { label: "Яогуаи", value: "2347" }, + { label: "Япония", value: "665" }, + { label: "Японские фестивали", value: "2555" }, + { label: "Ящеролюди", value: "682" }, + { label: "Abuse/Comfort", value: "2599" }, + { label: "Aged down", value: "229" }, + { label: "Aged up", value: "4" }, + { label: "Artfic", value: "68" }, + { label: "AU", value: "1683" }, + { label: "AU: Альтернативные способности", value: "2086" }, + { label: "AU: Без апокалипсиса", value: "2051" }, + { label: "AU: Без войны", value: "2305" }, + { label: "AU: Без магии", value: "1052" }, + { label: "AU: Без мистики", value: "2489" }, + { label: "AU: Без сверхспособностей", value: "1053" }, + { label: "AU: Без супергероев", value: "1152" }, + { label: "AU: Все гражданские", value: "2797" }, + { label: "AU: Все друзья", value: "2552" }, + { label: "AU: Все люди", value: "1151" }, + { label: "AU: Все хорошо", value: "2124" }, + { label: "AU: Другая страна", value: "2165" }, + { label: "AU: Другая эпоха", value: "2452" }, + { label: "AU: Другое детство", value: "2360" }, + { label: "AU: Другое знакомство", value: "2217" }, + { label: "AU: Другое семейное положение", value: "2902" }, + { label: "AU: Не родственники", value: "2167" }, + { label: "AU: Профессиональные герои (BNHA)", value: "2924" }, + { label: "AU: Родственники", value: "2164" }, + { label: "AU: Школа", value: "2532" }, + { label: "AU: Age swap", value: "917" }, + { label: "AU: Kitty Gang", value: "2928" }, + { label: "AU: Race swap", value: "933" }, + { label: "AU: Reverse", value: "1156" }, + { label: "AU: Role swap", value: "1054" }, + { label: "Authors Time", value: "2959" }, + { label: "BDSM", value: "1646" }, + { label: "BDSM-вселенная", value: "1078" }, + { label: "BDSM: Домспейс", value: "1444" }, + { label: "BDSM: Дроп", value: "1411" }, + { label: "BDSM: Сабспейс", value: "1410" }, + { label: "BDSM: Aftercare", value: "1523" }, + { label: "BEAST (Bungou Stray Dogs)", value: "2457" }, + { label: "Blackwatch", value: "2116" }, + { label: "Body inflation", value: "389" }, + { label: "Character study", value: "363" }, + { label: "Cockwarming", value: "318" }, + { label: "Dark academia", value: "2430" }, + { label: "DDLG / DDLB / MDLG / MDLB", value: "1033" }, + { label: "Dirty talk", value: "139" }, + { label: "Doggy style", value: "1361" }, + { label: "Dollification", value: "2780" }, + { label: "Dramione Authors Tournament", value: "2950" }, + { label: "EIQ", value: "271" }, + { label: "ER", value: "1687" }, + { label: "Final Haikyuu Quest", value: "2652" }, + { label: "Fix-it", value: "1" }, + { label: "Frenemies", value: "666" }, + { label: "Gangbang", value: "315" }, + { label: "Glory hole", value: "2188" }, + { label: "Golden Age Overwatch", value: "2112" }, + { label: "GOLockdown", value: "2391" }, + { label: "Gun play", value: "252" }, + { label: "Hurt/Comfort", value: "1682" }, + { label: "Hydra!AU", value: "1931" }, + { label: "Ironqrow week", value: "2209" }, + { label: "Kimetsu Gakuen", value: "2574" }, + { label: "Knife play", value: "152" }, + { label: "KPOP-AUFEST", value: "1851" }, + { label: "LARP", value: "1510" }, + { label: "LOVE ОтМетки", value: "2981" }, + { label: "Maleval week", value: "2382" }, + { label: "Marvel ГФИ", value: "2896" }, + { label: "Mate or die", value: "375" }, + { label: "Mirror!verse (Звездный путь)", value: "2899" }, + { label: "Moresome", value: "759" }, + { label: "N раз", value: "263" }, + { label: "Omnic Crisis Era", value: "2111" }, + { label: "ORIGINAL'S 2021", value: "2939" }, + { label: "Parentlock", value: "2053" }, + { label: "Patronus of Dramione", value: "2958" }, + { label: "Post-Fall of Overwatch", value: "2114" }, + { label: "Post-Recall", value: "2115" }, + { label: "PWP", value: "1680" }, + { label: "Rape/Revenge", value: "2195" }, + { label: "Reverse Falls", value: "1879" }, + { label: "RofR ДраМиВояж", value: "2954" }, + { label: "RST", value: "815" }, + { label: "RTFM", value: "2327" }, + { label: "Service top / Power bottom", value: "563" }, + { label: "Shingeki no Kyojin High School AU", value: "2037" }, + { label: "Shingeki! Kyojin Chuugakkou", value: "2038" }, + { label: "Sickfic", value: "422" }, + { label: "Songfic", value: "1684" }, + { label: "Spider-son & Iron-dad", value: "1885" }, + { label: "Sugar daddy", value: "106" }, + { label: "Sugar mommy", value: "160" }, + { label: "Superfamily (Мстители)", value: "2623" }, + { label: "Superior Iron Man", value: "2676" }, + { label: "Talon (Overwatch)", value: "2110" }, + { label: "The Big Four / The Super Six", value: "1954" }, + { label: "Trench AU", value: "2387" }, + { label: "TYL", value: "1933" }, + { label: "Underage", value: "1658" }, + { label: "URT", value: "2761" }, + { label: "UST", value: "1709" }, + { label: "Versefic", value: "2729" }, + { label: "Videofic", value: "2480" }, + { label: "Womance", value: "265" }, + { label: "X век", value: "240" }, + { label: "XI век", value: "241" }, + { label: "XII век", value: "242" }, + { label: "XIII век", value: "243" }, + { label: "XIV век", value: "244" }, + { label: "XIX век", value: "249" }, + { label: "XV век", value: "245" }, + { label: "XVI век", value: "246" }, + { label: "XVII век", value: "247" }, + { label: "XVIII век", value: "248" }, + { label: "XX век", value: "1004" }, + ], + type: FilterTypes.Picker, + }, + } satisfies Filters; +} + +export default new ficbook(); + +interface Data { + data?: DataEntity[] | null; + more: boolean; +} +interface DataEntity { + id: string; + slug: string; + author_id: number; + user_slug: string; + author_username: string; + real_author_id: string | null; + direction: number; + part_cnt: number; + title: string; + description: string; + enable_marks: boolean; + marks_plus: number; + is_premium: boolean; + status: number; + last_updated: string; + readedDate?: string | null; + reward_cnt: number; + cover?: string | null; +} diff --git a/plugins/russian/jaomix.ts b/plugins/russian/jaomix.ts index 6d46e58..717b2be 100644 --- a/plugins/russian/jaomix.ts +++ b/plugins/russian/jaomix.ts @@ -1,265 +1,264 @@ -import { Plugin } from "@typings/plugin"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { NovelStatus } from "@libs/novelStatus"; -import { load as parseHTML } from "cheerio"; - -class Jaomix implements Plugin.PluginBase { - id = "jaomix.ru"; - name = "Jaomix"; - site = "https://jaomix.ru"; - version = "1.0.0"; - icon = "src/ru/jaomix/icon.png"; - userAgent = ""; - cookieString = ""; - - async popularNovels( - pageNo: number, - { - showLatestNovels, - filters, - }: Plugin.PopularNovelsOptions, - ): Promise { - let url = this.site + "/?searchrn"; - - if (filters?.lang?.value?.length) { - url += filters.lang.value - .map((lang, idx) => `&lang[${idx}]=${lang}`).join(""); - } - - if (filters?.genre?.value?.include?.length) { - url += filters.genre.value.include - .map((genre, idx) => `&genre[${idx}]=${genre}`).join(""); - } - - if (filters?.genre?.value?.exclude?.length) { - url += filters.genre.value.exclude - .map((genre, idx) => `&delgenre[${idx}]=del ${genre}`).join(""); - } - - url += "&sortcountchapt=" + (filters?.sortcountchapt?.value || "1"); - url += "&sortdaycreate=" + (filters?.sortdaycreate?.value || "1"); - url += - "&sortby=" + (showLatestNovels ? "upd" : filters?.sortby?.value || "topweek"); - url += "&gpage=" + pageNo; - - const body = await fetchApi(url).then((res) => res.text()); - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - loadedCheerio('div[class="block-home"] > div[class="one"]').each( - function () { - const name = loadedCheerio(this) - .find('div[class="img-home"] > a') - .attr("title"); - const cover = loadedCheerio(this) - .find('div[class="img-home"] > a > img') - .attr("src") - ?.replace("-150x150", ""); - const url = loadedCheerio(this) - .find('div[class="img-home"] > a') - .attr("href"); - - if (!name || !url) return; - - novels.push({ name, cover, url }); - }, - ); - - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const body = await fetchApi(novelUrl).then((res) => res.text()); - const loadedCheerio = parseHTML(body); - - const novel: Plugin.SourceNovel = { - url: novelUrl, - name: loadedCheerio('div[class="desc-book"] > h1').text().trim(), - cover: loadedCheerio('div[class="img-book"] > img').attr("src"), - summary: loadedCheerio('div[id="desc-tab"]').text().trim() - }; - - loadedCheerio("#info-book > p").each(function () { - let text = loadedCheerio(this).text().replace(/,/g, "").split(" "); - if (text[0] === "Автор:") { - novel.author = text.splice(1).join(" "); - } else if (text[0] === "Жанры:") { - novel.genres = text.splice(1).join(","); - } else if (text[0] === "Статус:") { - novel.status = text.includes("продолжается") - ? NovelStatus.Ongoing - : NovelStatus.Completed; - } - }); - - const chapters: Plugin.ChapterItem[] = []; - - loadedCheerio(".download-chapter div.title").each(function () { - const name = loadedCheerio(this).find("a").attr("title"); - const releaseTime = loadedCheerio(this).find("time").text(); - const url = loadedCheerio(this).find("a").attr("href"); - if (!name || !url) return; - - chapters.push({ name, releaseTime, url }); - }); - - novel.chapters = chapters.reverse(); - return novel; - } - - async parseChapter(chapterUrl: string): Promise { - const body = await fetchApi(chapterUrl).then((res) => res.text()); - const loadedCheerio = parseHTML(body); - - loadedCheerio('div[class="adblock-service"]').remove(); - const chapterText = loadedCheerio('div[class="entry-content"]').html(); - - return chapterText || ""; - } - - async searchNovels( - searchTerm: string, - pageNo: number | undefined = 1, - ): Promise { - const url = `${this.site}/?searchrn=${searchTerm}&but=Поиск по названию&sortby=upd&gpage=${pageNo}`; - const body = await fetchApi(url).then((res) => res.text()); - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - loadedCheerio('div[class="block-home"] > div[class="one"]').each( - function () { - const name = loadedCheerio(this) - .find('div[class="img-home"] > a') - .attr("title"); - const cover = loadedCheerio(this) - .find('div[class="img-home"] > a > img') - .attr("src") - ?.replace("-150x150", ""); - const url = loadedCheerio(this) - .find('div[class="img-home"] > a') - .attr("href"); - - if (!name || !url) return; - - novels.push({ name, cover, url }); - }, - ); - - return novels; - } - fetchImage = fetchFile; - - filters = { - sortby: { - label: "Сортировка:", - value: "topweek", - options: [ - { label: "Топ недели", value: "topweek" }, - { label: "По алфавиту", value: "alphabet" }, - { label: "По дате обновления", value: "upd" }, - { label: "По дате создания", value: "new" }, - { label: "По просмотрам", value: "count" }, - { label: "Топ года", value: "topyear" }, - { label: "Топ дня", value: "topday" }, - { label: "Топ за все время", value: "alltime" }, - { label: "Топ месяца", value: "topmonth" }, - ], - type: FilterTypes.Picker, - }, - sortdaycreate: { - label: "Дата добавления:", - value: "1", - options: [ - { label: "Любое", value: "1" }, - { label: "От 120 до 180 дней", value: "1218" }, - { label: "От 180 до 365 дней", value: "1836" }, - { label: "От 30 до 60 дней", value: "3060" }, - { label: "От 365 дней", value: "365" }, - { label: "От 60 до 90 дней", value: "6090" }, - { label: "От 90 до 120 дней", value: "9012" }, - { label: "Послед. 30 дней", value: "30" }, - ], - type: FilterTypes.Picker, - }, - sortcountchapt: { - label: "Количество глав:", - value: "1", - options: [ - { label: "Любое кол-во глав", value: "1" }, - { label: "До 500", value: "500" }, - { label: "От 1000 до 2000", value: "1020" }, - { label: "От 2000 до 3000", value: "2030" }, - { label: "От 3000 до 4000", value: "3040" }, - { label: "От 4000", value: "400" }, - { label: "От 500 до 1000", value: "510" }, - ], - type: FilterTypes.Picker, - }, - genre: { - label: "Жанры:", - value: { include: [], exclude: [] }, - options: [ - { label: "Боевые Искусства", value: "Боевые Искусства" }, - { label: "Виртуальный Мир", value: "Виртуальный Мир" }, - { label: "Гарем", value: "Гарем" }, - { label: "Детектив", value: "Детектив" }, - { label: "Драма", value: "Драма" }, - { label: "Игра", value: "Игра" }, - { label: "Истории из жизни", value: "Истории из жизни" }, - { label: "Исторический", value: "Исторический" }, - { label: "История", value: "История" }, - { label: "Исэкай", value: "Исэкай" }, - { label: "Комедия", value: "Комедия" }, - { label: "Меха", value: "Меха" }, - { label: "Мистика", value: "Мистика" }, - { label: "Научная Фантастика", value: "Научная Фантастика" }, - { label: "Повседневность", value: "Повседневность" }, - { label: "Постапокалипсис", value: "Постапокалипсис" }, - { label: "Приключения", value: "Приключения" }, - { label: "Психология", value: "Психология" }, - { label: "Романтика", value: "Романтика" }, - { label: "Сверхъестественное", value: "Сверхъестественное" }, - { label: "Сёнэн", value: "Сёнэн" }, - { label: "Сёнэн-ай", value: "Сёнэн-ай" }, - { label: "Спорт", value: "Спорт" }, - { label: "Сэйнэн", value: "Сэйнэн" }, - { label: "Сюаньхуа", value: "Сюаньхуа" }, - { label: "Трагедия", value: "Трагедия" }, - { label: "Триллер", value: "Триллер" }, - { label: "Фантастика", value: "Фантастика" }, - { label: "Фэнтези", value: "Фэнтези" }, - { label: "Хоррор", value: "Хоррор" }, - { label: "Школьная жизнь", value: "Школьная жизнь" }, - { label: "Шоунен", value: "Шоунен" }, - { label: "Экшн", value: "Экшн" }, - { label: "Этти", value: "Этти" }, - { label: "Юри", value: "Юри" }, - { label: "Adult", value: "Adult" }, - { label: "Ecchi", value: "Ecchi" }, - { label: "Josei", value: "Josei" }, - { label: "Lolicon", value: "Lolicon" }, - { label: "Mature", value: "Mature" }, - { label: "Shoujo", value: "Shoujo" }, - { label: "Wuxia", value: "Wuxia" }, - { label: "Xianxia", value: "Xianxia" }, - { label: "Xuanhuan", value: "Xuanhuan" }, - { label: "Yaoi", value: "Yaoi" }, - ], - type: FilterTypes.ExcludableCheckboxGroup, - }, - lang: { - label: "Выбрать языки:", - value: [], - options: [ - { label: "Английский", value: "Английский" }, - { label: "Китайский", value: "Китайский" }, - { label: "Корейский", value: "Корейский" }, - { label: "Японский", value: "Японский" }, - ], - type: FilterTypes.CheckboxGroup, - }, - } satisfies Filters; -} - -export default new Jaomix(); +import { Plugin } from "@typings/plugin"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { NovelStatus } from "@libs/novelStatus"; +import { load as parseHTML } from "cheerio"; + +class Jaomix implements Plugin.PluginBase { + id = "jaomix.ru"; + name = "Jaomix"; + site = "https://jaomix.ru"; + version = "1.0.0"; + icon = "src/ru/jaomix/icon.png"; + userAgent = ""; + + async popularNovels( + pageNo: number, + { + showLatestNovels, + filters, + }: Plugin.PopularNovelsOptions, + ): Promise { + let url = this.site + "/?searchrn"; + + if (filters?.lang?.value?.length) { + url += filters.lang.value + .map((lang, idx) => `&lang[${idx}]=${lang}`).join(""); + } + + if (filters?.genre?.value?.include?.length) { + url += filters.genre.value.include + .map((genre, idx) => `&genre[${idx}]=${genre}`).join(""); + } + + if (filters?.genre?.value?.exclude?.length) { + url += filters.genre.value.exclude + .map((genre, idx) => `&delgenre[${idx}]=del ${genre}`).join(""); + } + + url += "&sortcountchapt=" + (filters?.sortcountchapt?.value || "1"); + url += "&sortdaycreate=" + (filters?.sortdaycreate?.value || "1"); + url += + "&sortby=" + (showLatestNovels ? "upd" : filters?.sortby?.value || "topweek"); + url += "&gpage=" + pageNo; + + const body = await fetchApi(url).then((res) => res.text()); + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + loadedCheerio('div[class="block-home"] > div[class="one"]').each( + function () { + const name = loadedCheerio(this) + .find('div[class="img-home"] > a') + .attr("title"); + const cover = loadedCheerio(this) + .find('div[class="img-home"] > a > img') + .attr("src") + ?.replace("-150x150", ""); + const url = loadedCheerio(this) + .find('div[class="img-home"] > a') + .attr("href"); + + if (!name || !url) return; + + novels.push({ name, cover, url }); + }, + ); + + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const body = await fetchApi(novelUrl).then((res) => res.text()); + const loadedCheerio = parseHTML(body); + + const novel: Plugin.SourceNovel = { + url: novelUrl, + name: loadedCheerio('div[class="desc-book"] > h1').text().trim(), + cover: loadedCheerio('div[class="img-book"] > img').attr("src"), + summary: loadedCheerio('div[id="desc-tab"]').text().trim() + }; + + loadedCheerio("#info-book > p").each(function () { + let text = loadedCheerio(this).text().replace(/,/g, "").split(" "); + if (text[0] === "Автор:") { + novel.author = text.splice(1).join(" "); + } else if (text[0] === "Жанры:") { + novel.genres = text.splice(1).join(","); + } else if (text[0] === "Статус:") { + novel.status = text.includes("продолжается") + ? NovelStatus.Ongoing + : NovelStatus.Completed; + } + }); + + const chapters: Plugin.ChapterItem[] = []; + + loadedCheerio(".download-chapter div.title").each(function () { + const name = loadedCheerio(this).find("a").attr("title"); + const releaseTime = loadedCheerio(this).find("time").text(); + const url = loadedCheerio(this).find("a").attr("href"); + if (!name || !url) return; + + chapters.push({ name, releaseTime, url }); + }); + + novel.chapters = chapters.reverse(); + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const body = await fetchApi(chapterUrl).then((res) => res.text()); + const loadedCheerio = parseHTML(body); + + loadedCheerio('div[class="adblock-service"]').remove(); + const chapterText = loadedCheerio('div[class="entry-content"]').html(); + + return chapterText || ""; + } + + async searchNovels( + searchTerm: string, + pageNo: number | undefined = 1, + ): Promise { + const url = `${this.site}/?searchrn=${searchTerm}&but=Поиск по названию&sortby=upd&gpage=${pageNo}`; + const body = await fetchApi(url).then((res) => res.text()); + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + loadedCheerio('div[class="block-home"] > div[class="one"]').each( + function () { + const name = loadedCheerio(this) + .find('div[class="img-home"] > a') + .attr("title"); + const cover = loadedCheerio(this) + .find('div[class="img-home"] > a > img') + .attr("src") + ?.replace("-150x150", ""); + const url = loadedCheerio(this) + .find('div[class="img-home"] > a') + .attr("href"); + + if (!name || !url) return; + + novels.push({ name, cover, url }); + }, + ); + + return novels; + } + fetchImage = fetchFile; + + filters = { + sortby: { + label: "Сортировка:", + value: "topweek", + options: [ + { label: "Топ недели", value: "topweek" }, + { label: "По алфавиту", value: "alphabet" }, + { label: "По дате обновления", value: "upd" }, + { label: "По дате создания", value: "new" }, + { label: "По просмотрам", value: "count" }, + { label: "Топ года", value: "topyear" }, + { label: "Топ дня", value: "topday" }, + { label: "Топ за все время", value: "alltime" }, + { label: "Топ месяца", value: "topmonth" }, + ], + type: FilterTypes.Picker, + }, + sortdaycreate: { + label: "Дата добавления:", + value: "1", + options: [ + { label: "Любое", value: "1" }, + { label: "От 120 до 180 дней", value: "1218" }, + { label: "От 180 до 365 дней", value: "1836" }, + { label: "От 30 до 60 дней", value: "3060" }, + { label: "От 365 дней", value: "365" }, + { label: "От 60 до 90 дней", value: "6090" }, + { label: "От 90 до 120 дней", value: "9012" }, + { label: "Послед. 30 дней", value: "30" }, + ], + type: FilterTypes.Picker, + }, + sortcountchapt: { + label: "Количество глав:", + value: "1", + options: [ + { label: "Любое кол-во глав", value: "1" }, + { label: "До 500", value: "500" }, + { label: "От 1000 до 2000", value: "1020" }, + { label: "От 2000 до 3000", value: "2030" }, + { label: "От 3000 до 4000", value: "3040" }, + { label: "От 4000", value: "400" }, + { label: "От 500 до 1000", value: "510" }, + ], + type: FilterTypes.Picker, + }, + genre: { + label: "Жанры:", + value: { include: [], exclude: [] }, + options: [ + { label: "Боевые Искусства", value: "Боевые Искусства" }, + { label: "Виртуальный Мир", value: "Виртуальный Мир" }, + { label: "Гарем", value: "Гарем" }, + { label: "Детектив", value: "Детектив" }, + { label: "Драма", value: "Драма" }, + { label: "Игра", value: "Игра" }, + { label: "Истории из жизни", value: "Истории из жизни" }, + { label: "Исторический", value: "Исторический" }, + { label: "История", value: "История" }, + { label: "Исэкай", value: "Исэкай" }, + { label: "Комедия", value: "Комедия" }, + { label: "Меха", value: "Меха" }, + { label: "Мистика", value: "Мистика" }, + { label: "Научная Фантастика", value: "Научная Фантастика" }, + { label: "Повседневность", value: "Повседневность" }, + { label: "Постапокалипсис", value: "Постапокалипсис" }, + { label: "Приключения", value: "Приключения" }, + { label: "Психология", value: "Психология" }, + { label: "Романтика", value: "Романтика" }, + { label: "Сверхъестественное", value: "Сверхъестественное" }, + { label: "Сёнэн", value: "Сёнэн" }, + { label: "Сёнэн-ай", value: "Сёнэн-ай" }, + { label: "Спорт", value: "Спорт" }, + { label: "Сэйнэн", value: "Сэйнэн" }, + { label: "Сюаньхуа", value: "Сюаньхуа" }, + { label: "Трагедия", value: "Трагедия" }, + { label: "Триллер", value: "Триллер" }, + { label: "Фантастика", value: "Фантастика" }, + { label: "Фэнтези", value: "Фэнтези" }, + { label: "Хоррор", value: "Хоррор" }, + { label: "Школьная жизнь", value: "Школьная жизнь" }, + { label: "Шоунен", value: "Шоунен" }, + { label: "Экшн", value: "Экшн" }, + { label: "Этти", value: "Этти" }, + { label: "Юри", value: "Юри" }, + { label: "Adult", value: "Adult" }, + { label: "Ecchi", value: "Ecchi" }, + { label: "Josei", value: "Josei" }, + { label: "Lolicon", value: "Lolicon" }, + { label: "Mature", value: "Mature" }, + { label: "Shoujo", value: "Shoujo" }, + { label: "Wuxia", value: "Wuxia" }, + { label: "Xianxia", value: "Xianxia" }, + { label: "Xuanhuan", value: "Xuanhuan" }, + { label: "Yaoi", value: "Yaoi" }, + ], + type: FilterTypes.ExcludableCheckboxGroup, + }, + lang: { + label: "Выбрать языки:", + value: [], + options: [ + { label: "Английский", value: "Английский" }, + { label: "Китайский", value: "Китайский" }, + { label: "Корейский", value: "Корейский" }, + { label: "Японский", value: "Японский" }, + ], + type: FilterTypes.CheckboxGroup, + }, + } satisfies Filters; +} + +export default new Jaomix(); diff --git a/plugins/russian/novelTL.ts b/plugins/russian/novelTL.ts index 87a6548..05f51df 100644 --- a/plugins/russian/novelTL.ts +++ b/plugins/russian/novelTL.ts @@ -1,958 +1,957 @@ -import { Plugin } from "@typings/plugin"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { defaultCover } from "@libs/defaultCover"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { NovelStatus } from "@libs/novelStatus"; -import { load as parseHTML } from "cheerio"; -import dayjs from "dayjs"; - -class TL implements Plugin.PluginBase { - id = "TL"; - name = "NovelTL"; - site = "https://novel.tl"; - version = "1.0.0"; - icon = "src/ru/noveltl/icon.png"; - userAgent = ""; - cookieString = ""; - - async popularNovels( - page: number, - { showLatestNovels, filters }: Plugin.PopularNovelsOptions, - ): Promise { - const result = await fetchApi(this.site + "/api/site/v2/graphql", { - method: "post", - headers: { - Accept: "application/json, text/plain, */*", - "Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3", - "Content-Type": "application/json", - }, - referrer: this.site, - body: JSON.stringify({ - operationName: "Projects", - query: - "query Projects($hostname:String! $filter:SearchFilter $page:Int $limit:Int){projects(section:{fullUrl:$hostname}filter:$filter page:{pageSize:$limit,pageNumber:$page}){content{title fullUrl covers{url}}}}", - variables: { - filter: { - tags: filters?.tags?.value || [], - genres: filters?.genres?.value || [], - }, - hostname: "novel.tl", - limit: 40, - page, - }, - }), - }); - - const json = (await result.json()) as response; - const novels: Plugin.NovelItem[] = []; - - json.data?.projects?.content?.forEach((novel) => - novels.push({ - name: novel.title, - url: "https://" + novel.fullUrl, - cover: novel?.covers?.[0]?.url - ? this.site + novel.covers[0].url - : defaultCover, - }), - ); - - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const result = await fetchApi(this.site + "/api/site/v2/graphql", { - method: "post", - headers: { - Accept: "application/json, text/plain, */*", - "Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3", - "Content-Type": "application/json", - }, - referrer: this.site, - body: JSON.stringify({ - operationName: "Book", - query: - 'query Book($url:String){project(project:{fullUrl:$url}){title translationStatus fullUrl covers{url}persons(langs:["ru","en","*"],roles:["author","illustrator"]){role name{firstName lastName}}genres{nameRu nameEng}tags{nameRu nameEng}annotation{text}subprojects{content{title volumes{content{shortName chapters{title publishDate fullUrl published}}}}}}}', - variables: { - url: novelUrl, - }, - }), - }); - const json = (await result.json()) as response; - const novel: Plugin.SourceNovel = { - url: novelUrl, - name: json.data.project?.title, - cover: json.data.project?.covers?.[0]?.url - ? this.site + json.data.project.covers[0].url - : defaultCover, - summary: json.data.project?.annotation?.text, - status: - json.data.project?.translationStatus === "active" - ? NovelStatus.Ongoing - : NovelStatus.Completed, - }; - - const genres = [json.data.project?.tags, json.data.project?.genres] - .flat() - .map((item) => item?.nameRu || item?.nameEng) - .filter((item) => item); - - if (genres?.length) { - novel.genres = genres.join(", "); - } - - json.data.project?.persons?.forEach((person) => { - if (person.role == "author" && person.name.firstName) { - novel.author = - person.name.firstName + " " + (person.name?.lastName || ""); - } - }); - - const chapters: Plugin.ChapterItem[] = []; - json.data.project?.subprojects?.content?.forEach((work) => - work.volumes.content.forEach((volume) => - volume.chapters.forEach( - (chapter) => - chapter?.published && - chapters.push({ - name: volume.shortName + " " + chapter.title, - url: "https://" + chapter.fullUrl, - releaseTime: dayjs(chapter.publishDate).format("LLL"), - }), - ), - ), - ); - - novel.chapters = chapters; - return novel; - } - - async parseChapter(chapterUrl: string): Promise { - const result = await fetchApi(this.site + "/api/site/v2/graphql", { - method: "post", - headers: { - Accept: "application/json, text/plain, */*", - "Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3", - "Content-Type": "application/json", - }, - referrer: this.site, - body: JSON.stringify({ - query: - "query($url:String){chapter(chapter:{fullUrl:$url}){text{text}}}", - variables: { - url: chapterUrl, - }, - }), - }); - const json = (await result.json()) as response; - - const baseUrl = this.site; - const loadedCheerio = parseHTML(json.data.chapter?.text?.text || ""); - loadedCheerio("p > a[href]").each(function () { - let src = baseUrl + loadedCheerio(this).attr("href"); - if (!src.startsWith("http")) { - src = baseUrl + src; - } - loadedCheerio(this).find("picture").remove(); - loadedCheerio(this).removeAttr("href"); - loadedCheerio(``).appendTo(this); - }); - - const chapterText = loadedCheerio.html(); - return chapterText; - } - - async searchNovels( - searchTerm: string, - page: number | undefined = 1, - ): Promise { - const result = await fetchApi(this.site + "/api/site/v2/graphql", { - method: "post", - headers: { - Accept: "application/json, text/plain, */*", - "Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3", - "Content-Type": "application/json", - }, - referrer: this.site, - body: JSON.stringify({ - operationName: "Projects", - query: - "query Projects($hostname:String! $filter:SearchFilter $page:Int $limit:Int){projects(section:{fullUrl:$hostname}filter:$filter page:{pageSize:$limit,pageNumber:$page}){content{title fullUrl covers{url}}}}", - variables: { - filter: { - query: searchTerm, - }, - hostname: "novel.tl", - limit: 40, - page, - }, - }), - }); - const json = (await result.json()) as response; - const novels: Plugin.NovelItem[] = []; - - json?.data?.projects?.content?.forEach((novel) => - novels.push({ - name: novel.title, - url: "https://" + novel.fullUrl, - cover: novel?.covers?.[0]?.url - ? this.site + novel.covers[0].url - : defaultCover, - }), - ); - - return novels; - } - - fetchImage = fetchFile; - - filters = { - tags: { - label: "Тэги", - value: [], - options: [ - { label: "Автоматы", value: "64" }, - { label: "Агрессивные персонажи", value: "26" }, - { label: "Ад", value: "338" }, - { label: "Адаптация манги", value: "8" }, - { label: "Академия", value: "5" }, - { label: "Актёрское искусство", value: "7" }, - { label: "Актеры озвучки", value: "746" }, - { label: "Алхимия", value: "27" }, - { label: "Альтернативная реальность", value: "30" }, - { label: "Амнезия", value: "31" }, - { label: "Анабиоз", value: "170" }, - { label: "Анал", value: "33" }, - { label: "Ангелы", value: "38" }, - { label: "Андроиды", value: "37" }, - { label: "Анти-рост персонажа", value: "25" }, - { label: "Антигерой", value: "43" }, - { label: "Антикварный магазин", value: "44" }, - { label: "Антимагия", value: "41" }, - { label: "Антиутопия", value: "225" }, - { label: "Апатичный главный герой", value: "46" }, - { label: "Апокалипсис", value: "47" }, - { label: "Аристократия", value: "51" }, - { label: "Армия", value: "53" }, - { label: "Артефакты", value: "58" }, - { label: "Асоциальный главный герой", value: "42" }, - { label: "Ассасины", value: "61" }, - { label: "Атмосфера Европы", value: "245" }, - { label: "Банды", value: "302" }, - { label: "БДСМ", value: "77" }, - { label: "Бедный главный герой", value: "541" }, - { label: "Бездушный главный герой", value: "143" }, - { label: "Беззаботный герой", value: "115" }, - { label: "Безответная любовь", value: "213" }, - { label: "Безответная любовь", value: "740" }, - { label: "Безработные", value: "377" }, - { label: "Беременность", value: "551" }, - { label: "Бескорыстная любовь", value: "733" }, - { label: "Бессмертные", value: "359" }, - { label: "Бесстрашный главный герой", value: "274" }, - { label: "Библиотека", value: "393" }, - { label: "Биография", value: "86" }, - { label: "Близнецы", value: "729" }, - { label: "Богачи", value: "753" }, - { label: "Боги", value: "318" }, - { label: "Богини", value: "316" }, - { label: "Богоподобный протагонист", value: "314" }, - { label: "Боевая академия", value: "75" }, - { label: "Боевая парочка", value: "547" }, - { label: "Божественная защита", value: "208" }, - { label: "Божественная сила", value: "317" }, - { label: "Болеющие персонажи", value: "648" }, - { label: "Борьба за власть", value: "548" }, - { label: "Брак по расчету", value: "55" }, - { label: "Братство", value: "106" }, - { label: "Братья или сёстры", value: "646" }, - { label: "Брошенные дети", value: "1" }, - { label: "Бывший герой", value: "290" }, - { label: "Вампиры", value: "742" }, - { label: "Ваншот", value: "496" }, - { label: "Вдали от родителей", value: "3" }, - { label: "Ведьмы", value: "756" }, - { label: "Везучий главный герой", value: "411" }, - { label: "Верные подданные", value: "410" }, - { label: "Вероломные герои/плетение интриг", value: "424" }, - { label: "Взросление персонажа", value: "24" }, - { label: "Видит то, чего остальной люд не в силах", value: "617" }, - { label: "Виртуальная реальность", value: "744" }, - { label: "Владелец магазина", value: "677" }, - { label: "Владелец уникального оружия", value: "736" }, - { label: "Властные персонажи", value: "545" }, - { label: "Влияние прошлого", value: "517" }, - { label: "Внезапное обогащение", value: "691" }, - { label: "Внезапное обретение силы", value: "690" }, - { label: "Военные хроники", value: "749" }, - { label: "Возвращение в родной мир", value: "587" }, - { label: "Война", value: "750" }, - { label: "Вокалоид", value: "745" }, - { label: "Волшебные твари", value: "413" }, - { label: "Воскрешение", value: "586" }, - { label: "Воспитанный главный герой", value: "537" }, - { label: "Воспоминания прошлого", value: "283" }, - { label: "Враги становятся союзниками", value: "238" }, - { label: "Врата в иной мир", value: "303" }, - { label: "Временной парадокс", value: "711" }, - { label: "Вторжение на Землю", value: "228" }, - { label: "Второй шанс", value: "608" }, - { label: "Выживание", value: "695" }, - { label: "Вынужденные условия проживания", value: "287" }, - { label: "Высокомерный герой", value: "56" }, - { label: "Гадание", value: "207" }, - { label: "Галлюцинации", value: "189" }, - { label: "Гарем рабов", value: "656" }, - { label: "Геймеры", value: "301" }, - { label: "Генетические модификации", value: "306" }, - { label: "Герои", value: "341" }, - { label: "Герои непонятного пола", value: "36" }, - { label: "Героиня девушка-сорванец", value: "715" }, - { label: "Героиня красавица", value: "81" }, - { label: "Герой влюбляется первым", value: "561" }, - { label: "Герой использует щит", value: "636" }, - { label: "Герой красавец", value: "329" }, - { label: "Герой со множеством тел", value: "564" }, - { label: "Герой-бесстыдник", value: "632" }, - { label: "Герой-извращенец", value: "521" }, - { label: "Герой-трудяга", value: "330" }, - { label: "Герой-яндере", value: "421" }, - { label: "Гетерохромия", value: "342" }, - { label: "Гильдии", value: "324" }, - { label: "Гиперопека", value: "509" }, - { label: "Главная героиня — женщина", value: "277" }, - { label: "Главный герой - гений", value: "308" }, - { label: "Главный герой - Моб", value: "446" }, - { label: "Главный герой — знаменитость", value: "263" }, - { label: "Главный герой — мужчина", value: "419" }, - { label: "Главный герой в поисках гарема", value: "331" }, - { label: "Главный герой заранее готовится", value: "557" }, - { label: "Главный герой не человек", value: "488" }, - { label: "Главный герой приёмный", value: "20" }, - { label: "Главный герой сразу силён", value: "563" }, - { label: "Гладиаторы", value: "310" }, - { label: "Глухой к любви герой", value: "194" }, - { label: "Гоблины", value: "313" }, - { label: "Големы", value: "319" }, - { label: "Гомункул", value: "347" }, - { label: "Горничные", value: "418" }, - { label: "Государственные интриги", value: "212" }, - { label: "Гринд", value: "322" }, - { label: "Давняя травма", value: "518" }, - { label: "Двойник", value: "97" }, - { label: "Дворецкие", value: "111" }, - { label: "Дворфы", value: "224" }, - { label: "Дворяне", value: "487" }, - { label: "Двоюродные братья или сёстры", value: "162" }, - { label: "Девочки-волшебницы", value: "415" }, - { label: "Демоны", value: "193" }, - { label: "Депрессия", value: "196" }, - { label: "Детектив", value: "198" }, - { label: "Детективное расследование", value: "473" }, - { label: "Детское насилие", value: "125" }, - { label: "Детское обещание", value: "130" }, - { label: "Дискриминация", value: "203" }, - { label: "Длительный разрыв отношений", value: "400" }, - { label: "Домашняя жизнь", value: "45" }, - { label: "Драконы", value: "218" }, - { label: "Древний Китай", value: "34" }, - { label: "Дружба", value: "293" }, - { label: "Друзья детства", value: "128" }, - { label: "Друзья становятся врагами", value: "292" }, - { label: "Духи", value: "673" }, - { label: "Духовная сила", value: "665" }, - { label: "Духовный наставник", value: "671" }, - { label: "Душещипательная история", value: "335" }, - { label: "Души", value: "666" }, - { label: "Дьявольский путь культивации", value: "192" }, - { label: "Ёкай", value: "763" }, - { label: "Есть аниме-адаптация", value: "10" }, - { label: "Есть видеоигра", value: "13" }, - { label: "Есть дорама-адаптация", value: "11" }, - { label: "Есть манга-адаптация", value: "14" }, - { label: "Есть манхва-адаптация", value: "16" }, - { label: "Есть маньхуа-адаптация", value: "15" }, - { label: "Есть фильм", value: "17" }, - { label: "Есть CD дорама-адаптация", value: "12" }, - { label: "Жёсткий главный герой", value: "597" }, - { label: "Жестокие персонажи", value: "4" }, - { label: "Жёстокие персонажи", value: "169" }, - { label: "Животное-спутник", value: "78" }, - { label: "Жизнь в одиночку", value: "396" }, - { label: "Жрецы", value: "555" }, - { label: "Жрицы", value: "554" }, - { label: "Забота о детях", value: "127" }, - { label: "Заботливый главный герой", value: "116" }, - { label: "Загадочное заболевание", value: "471" }, - { label: "Загадочное прошлое родителей", value: "470" }, - { label: "Заговоры", value: "154" }, - { label: "Закалка тела", value: "96" }, - { label: "Закон джунглей", value: "682" }, - { label: "Закулисная борба", value: "603" }, - { label: "Замкнутый главный герой", value: "371" }, - { label: "Запечатанная сила", value: "607" }, - { label: "Затворник", value: "346" }, - { label: "Звери", value: "80" }, - { label: "Зверолюди", value: "39" }, - { label: "Звероподобные", value: "79" }, - { label: "Земледелие", value: "268" }, - { label: "Злобные благородные девы", value: "743" }, - { label: "Зловещие организации", value: "247" }, - { label: "Злой главный герой", value: "248" }, - { label: "Злые боги", value: "246" }, - { label: "Злые религии", value: "249" }, - { label: "Знаменитости", value: "118" }, - { label: "Знания из прошлой жизни", value: "553" }, - { label: "Зомби", value: "767" }, - { label: "Игра на выживание", value: "696" }, - { label: "Игровая рейтинговая система", value: "300" }, - { label: "Из грязи в князи", value: "542" }, - { label: "Известные родители", value: "262" }, - { label: "Изменение внешнего вида", value: "48" }, - { label: "Изнасилование", value: "574" }, - { label: "Инвалидность", value: "202" }, - { label: "Индустриализация", value: "364" }, - { label: "Инженер", value: "241" }, - { label: "Инцест", value: "361" }, - { label: "Искажённая личность", value: "730" }, - { label: "Искусственный интеллект", value: "59" }, - { label: "Исполнение желаний", value: "755" }, - { label: "Каннибализм", value: "113" }, - { label: "Карточные игры", value: "114" }, - { label: "Кладбищенский смотритель", value: "321" }, - { label: "Классика", value: "134" }, - { label: "Книги", value: "99" }, - { label: "Книги навыков", value: "654" }, - { label: "Книжный червь", value: "100" }, - { label: "Коллеги", value: "140" }, - { label: "Колледж/Университет", value: "145" }, - { label: "Командная работа", value: "701" }, - { label: "Комедийный подтекст", value: "147" }, - { label: "Комплекс брата", value: "105" }, - { label: "Комплекс неполноценности", value: "365" }, - { label: "Контракты", value: "155" }, - { label: "Контроль разума", value: "441" }, - { label: "Конфликт в семье", value: "261" }, - { label: "Конфликт верности", value: "153" }, - { label: "Копейщик", value: "668" }, - { label: "Королевская особа", value: "596" }, - { label: "Королевства", value: "382" }, - { label: "Короткий рассказ", value: "638" }, - { label: "Коррупция", value: "157" }, - { label: "Космические Войны", value: "158" }, - { label: "Космос", value: "507" }, - { label: "Косплей", value: "159" }, - { label: "Кража способностей", value: "2" }, - { label: "Крафтинг", value: "164" }, - { label: "Кризис личности", value: "357" }, - { label: "Кровь", value: "320" }, - { label: "Кружки, клубы", value: "138" }, - { label: "Кудэрэ", value: "384" }, - { label: "Кузнец", value: "90" }, - { label: "Кукловоды", value: "567" }, - { label: "Куклы/Марионетки", value: "211" }, - { label: "Кулинария", value: "156" }, - { label: "Культивация", value: "171" }, - { label: "Куннилингус", value: "172" }, - { label: "Легенды", value: "391" }, - { label: "Легкая жизнь", value: "229" }, - { label: "Лекари", value: "334" }, - { label: "Ленивый главный герой", value: "389" }, - { label: "Лидерство", value: "390" }, - { label: "Любит персонажа младше себя", value: "765" }, - { label: "Любит персонажа старше себя", value: "494" }, - { label: "Любовное соперничество", value: "406" }, - { label: "Любовные треугольники", value: "407" }, - { label: "Любовь детства", value: "129" }, - { label: "Любовь с первого взгляда", value: "404" }, - { label: "Любящие родители", value: "215" }, - { label: "Маги", value: "757" }, - { label: "Магические технологии", value: "417" }, - { label: "Магия", value: "412" }, - { label: "Магия призыва", value: "694" }, - { label: "Маленькие девочки", value: "397" }, - { label: "Мангака", value: "423" }, - { label: "Маниакальная любовь", value: "492" }, - { label: "Марти Сью", value: "508" }, - { label: "Мастер подземелий", value: "222" }, - { label: "Мастурбация", value: "432" }, - { label: "Матриархат", value: "433" }, - { label: "Межпространственные путешествия", value: "370" }, - { label: "Менеджмент", value: "422" }, - { label: "Месть", value: "588" }, - { label: "Мечи и магия", value: "697" }, - { label: "Мечники", value: "698" }, - { label: "Миленькие дети", value: "176" }, - { label: "Милитаризм", value: "439" }, - { label: "Миловидная история", value: "178" }, - { label: "Милый главный герой", value: "177" }, - { label: "Минет", value: "275" }, - { label: "Мировое древо", value: "760" }, - { label: "Миролюбивый главный герой", value: "510" }, - { label: "Мифология", value: "475" }, - { label: "Младшие сёстры", value: "766" }, - { label: "ММОРПГ", value: "445" }, - { label: "Множество временных линий", value: "463" }, - { label: "Модели", value: "447" }, - { label: "Молод не по годам", value: "49" }, - { label: "Монстры", value: "454" }, - { label: "Мрачность", value: "183" }, - { label: "Музыка", value: "466" }, - { label: "Музыкальные группы", value: "68" }, - { label: "Мутации", value: "468" }, - { label: "На волосок от смерти", value: "479" }, - { label: "На все руки мастер", value: "374" }, - { label: "Навыки имеют стоимость", value: "150" }, - { label: "Нагота", value: "490" }, - { label: "Наездники на драконах", value: "216" }, - { label: "Наёмники", value: "437" }, - { label: "Наивный главный герой", value: "476" }, - { label: "Наследие", value: "366" }, - { label: "Настоящая любовь", value: "200" }, - { label: "Настоящий гарем", value: "540" }, - { label: "Небесное испытание", value: "337" }, - { label: "Невезучий главный герой", value: "738" }, - { label: "Недоверчивый протагонист", value: "206" }, - { label: "Недооцененный главный герой", value: "734" }, - { label: "Недопонимание", value: "444" }, - { label: "Некромант", value: "480" }, - { label: "Нелинейное повествование", value: "489" }, - { label: "Неловкий главный герой", value: "67" }, - { label: "Нелюбимый протагонист", value: "333" }, - { label: "Немой персонаж", value: "469" }, - { label: "Ненадежный рассказчик", value: "739" }, - { label: "Непримечательный герой", value: "65" }, - { label: "Нерешительный протагонист", value: "363" }, - { label: "Несгибаемый главный герой", value: "676" }, - { label: "Несколько главных героев", value: "460" }, - { label: "Несколько миров", value: "461" }, - { label: "Несколько перерожденцев", value: "462" }, - { label: "Несколько попаданцев", value: "464" }, - { label: "Несравненная скорость культивации", value: "269" }, - { label: "Нехватка/отсутствие здравого смысла", value: "385" }, - { label: "Нечестный протагонист", value: "205" }, - { label: "НИИТ", value: "481" }, - { label: "Ниндзя", value: "486" }, - { label: "Обмен телами", value: "95" }, - { label: "Оборотни", value: "754" }, - { label: "Объект любви влюбился первым", value: "405" }, - { label: "Объект любви популярен", value: "543" }, - { label: "Огнестрельное оружие", value: "280" }, - { label: "Ограничение на время жизни", value: "394" }, - { label: "Одержимость", value: "544" }, - { label: "Один родитель", value: "651" }, - { label: "Одинокий главный герой", value: "399" }, - { label: "Одиночество", value: "398" }, - { label: "Одно тело на двоих", value: "634" }, - { label: "Око силы", value: "253" }, - { label: "Оммёдзи", value: "498" }, - { label: "Организованная преступность", value: "500" }, - { label: "Оргия", value: "501" }, - { label: "Орки", value: "499" }, - { label: "Основано на аниме", value: "74" }, - { label: "Основано на видеоигре", value: "72" }, - { label: "Основано на визуальной новелле", value: "73" }, - { label: "Основано на песне", value: "70" }, - { label: "Основано на фильме", value: "69" }, - { label: "Осторожный главный герой", value: "117" }, - { label: "От ненависти до любви один шаг", value: "239" }, - { label: "От сильного с сильнейшему", value: "684" }, - { label: "От слабого к сильному", value: "752" }, - { label: "Отаку", value: "503" }, - { label: "Отзывчивая любовь", value: "380" }, - { label: "Отзывчивый главный герой", value: "339" }, - { label: "Отношение учитель-ученик", value: "430" }, - { label: "Отношения бог-человек", value: "315" }, - { label: "Отношения между учителем и учеником", value: "687" }, - { label: "Отношения начальник-подчинённый", value: "101" }, - { label: "Отношения против воли", value: "286" }, - { label: "Отношения с нечеловеком", value: "353" }, - { label: "Отношения сэмпай-кохай", value: "621" }, - { label: "Отношения хозяин-слуга", value: "431" }, - { label: "Отомэ-игры", value: "504" }, - { label: "ОТП", value: "562" }, - { label: "Отшельники", value: "505" }, - { label: "Офисный работник", value: "600" }, - { label: "Официанты", value: "748" }, - { label: "Охотники", value: "355" }, - { label: "Падшие ангелы", value: "255" }, - { label: "Падшие дворяне", value: "256" }, - { label: "Пайзури", value: "511" }, - { label: "Паразиты", value: "513" }, - { label: "Параллельные миры", value: "512" }, - { label: "Парк развлечений", value: "32" }, - { label: "Пародия", value: "515" }, - { label: "Перемещение между мирами", value: "758" }, - { label: "Переодевание", value: "167" }, - { label: "Переплавка пилюль", value: "529" }, - { label: "Перерождение в девушку", value: "420" }, - { label: "Перерождение в другом мире", value: "580" }, - { label: "Перерождение в другом мире", value: "720" }, - { label: "Перерождение в игровом мире", value: "579" }, - { label: "Перерождение в монстра", value: "577" }, - { label: "Персонажи с мазохистскими наклонностями", value: "429" }, - { label: "Персонажи с садистскими наклонностями", value: "598" }, - { label: "Петля времени", value: "709" }, - { label: "Петтинг", value: "328" }, - { label: "Писатели", value: "761" }, - { label: "Питомцы", value: "522" }, - { label: "Племенное общество", value: "726" }, - { label: "Повелитель демонов", value: "191" }, - { label: "Повествование от нескольких лиц", value: "459" }, - { label: "Поглощение навыков", value: "653" }, - { label: "Подземелья", value: "223" }, - { label: "Поздно начинающаяся романтическая линия", value: "387" }, - { label: "Политика", value: "538" }, - { label: "Полулюди", value: "190" }, - { label: "Помолвка", value: "240" }, - { label: "Попаданец в другой мир", value: "723" }, - { label: "Попаданец в игровой мир", value: "722" }, - { label: "Постапокалипсис", value: "546" }, - { label: "Постижение Дао", value: "181" }, - { label: "Потеря девственности", value: "282" }, - { label: "Потустороннее пространство", value: "416" }, - { label: "Похищения", value: "379" }, - { label: "Прагматичный главный герой", value: "549" }, - { label: "Предательство", value: "83" }, - { label: "Предвидение", value: "550" }, - { label: "Преследователи", value: "674" }, - { label: "Преступление", value: "165" }, - { label: "Преступность", value: "166" }, - { label: "Приёмные дети", value: "19" }, - { label: "Призванный герой", value: "693" }, - { label: "Призраки", value: "309" }, - { label: "Приключения", value: "22" }, - { label: "Принуждение", value: "108" }, - { label: "Приручатель", value: "453" }, - { label: "Прислуга", value: "625" }, - { label: "Приставучий любовник", value: "136" }, - { label: "Притворная любовь", value: "552" }, - { label: "Пришельцы", value: "28" }, - { label: "Программист", value: "558" }, - { label: "Произведение - обладатель наград", value: "66" }, - { label: "Проклятия", value: "175" }, - { label: "Промывка мозгов", value: "102" }, - { label: "Пропуск времени", value: "712" }, - { label: "Пророчества", value: "559" }, - { label: "Протагонист в очках", value: "312" }, - { label: "Протагонист раб", value: "657" }, - { label: "Психокинез", value: "565" }, - { label: "Психопаты", value: "566" }, - { label: "Путешествия", value: "759" }, - { label: "Путешествия во времени", value: "713" }, - { label: "Пытки", value: "716" }, - { label: "Рабы", value: "658" }, - { label: "Развитие отношений", value: "160" }, - { label: "Развитие персонажа", value: "119" }, - { label: "Раздвоение личности", value: "458" }, - { label: "Размеренная романтика", value: "661" }, - { label: "Разница в статусе", value: "201" }, - { label: "Разорванная помолвка", value: "104" }, - { label: "Разумные предметы", value: "622" }, - { label: "Рай", value: "336" }, - { label: "Раняя романтика", value: "227" }, - { label: "Расизм", value: "573" }, - { label: "Расследования", value: "372" }, - { label: "Ребенок — главный герой", value: "126" }, - { label: "Реверс-гарем", value: "589" }, - { label: "Реверс-изнасилование", value: "590" }, - { label: "Резкая смена характера", value: "520" }, - { label: "Реинкарнация", value: "581" }, - { label: "Религии", value: "582" }, - { label: "Ресторан", value: "585" }, - { label: "Решительный главный герой", value: "199" }, - { label: "Робкий протагонист", value: "714" }, - { label: "Родительсткий комплекс", value: "514" }, - { label: "Родные не связанные кровью", value: "647" }, - { label: "Родословные", value: "94" }, - { label: "РПГ-система", value: "392" }, - { label: "Рыцари", value: "383" }, - { label: "Самовлюбленный главный герой", value: "477" }, - { label: "Самоубийства", value: "692" }, - { label: "Самураи", value: "601" }, - { label: "Сборник коротких историй", value: "144" }, - { label: "Свадьба", value: "426" }, - { label: "Связанные сюжетные линии", value: "369" }, - { label: "Святые", value: "599" }, - { label: "Секреты", value: "614" }, - { label: "Секс втроём", value: "707" }, - { label: "Секс-рабыня", value: "629" }, - { label: "Семейная любовь", value: "257" }, - { label: "Семь добродетелей", value: "627" }, - { label: "Семь смертных грехов", value: "626" }, - { label: "Серийные убийцы", value: "624" }, - { label: "Сикигами", value: "637" }, - { label: "Сильная любовь от старших", value: "214" }, - { label: "Синдром восьмиклассника", value: "132" }, - { label: "Синдром сестры", value: "652" }, - { label: "Сироты", value: "502" }, - { label: "Системный администратор", value: "699" }, - { label: "Скромный главный герой", value: "409" }, - { label: "Скрытые романтические отношения", value: "23" }, - { label: "Скрытые способности", value: "343" }, - { label: "Скряга", value: "450" }, - { label: "Скульпторы", value: "606" }, - { label: "Слабая романтика", value: "688" }, - { label: "Слабый главный герой", value: "751" }, - { label: "Сложные семейные отношения", value: "149" }, - { label: "Смена расы", value: "572" }, - { label: "Смертельное заболевание", value: "704" }, - { label: "Смерть", value: "185" }, - { label: "Смерть любимых", value: "186" }, - { label: "Современность", value: "448" }, - { label: "Современные знания в слаборазвитых мирах", value: "449" }, - { label: "Сожительство", value: "141" }, - { label: "Создание артефактов", value: "57" }, - { label: "Создание клана", value: "133" }, - { label: "Создание королевства", value: "381" }, - { label: "Создание навыков", value: "655" }, - { label: "Сокрытие истинных способностей", value: "344" }, - { label: "Сокрытие личности", value: "345" }, - { label: "Солдаты", value: "664" }, - { label: "Сон", value: "659" }, - { label: "Соседи по комнате", value: "595" }, - { label: "Социальные изгои", value: "663" }, - { label: "Спасение мира", value: "602" }, - { label: "Спорящая пара", value: "84" }, - { label: "Способность перевоплощения", value: "719" }, - { label: "Средневековье", value: "436" }, - { label: "Стеснительные персонажи", value: "643" }, - { label: "Стокгольмский синдром", value: "575" }, - { label: "Стокгольмский синдром", value: "675" }, - { label: "Стратег", value: "681" }, - { label: "Стратегические битвы", value: "680" }, - { label: "Стрелки", value: "325" }, - { label: "Стрельба из лука", value: "50" }, - { label: "Студенческий совет", value: "686" }, - { label: "Судьба", value: "197" }, - { label: "Суккубы", value: "689" }, - { label: "Суперспособности", value: "669" }, - { label: "Суровая тренировка", value: "332" }, - { label: "Сцены насилия", value: "195" }, - { label: "Таинственное прошлое", value: "472" }, - { label: "Тайная личность", value: "610" }, - { label: "Тайные организации", value: "611" }, - { label: "Телохранители", value: "98" }, - { label: "Тентакли", value: "703" }, - { label: "Террористы", value: "705" }, - { label: "Технологический разрыв", value: "702" }, - { label: "Тихие герои", value: "568" }, - { label: "Толстый протагонист", value: "271" }, - { label: "Торговцы", value: "438" }, - { label: "Травник", value: "340" }, - { label: "Трагичное прошлое", value: "718" }, - { label: "Трап", value: "725" }, - { label: "Триллер", value: "708" }, - { label: "Турнир", value: "76" }, - { label: "Тюрьма", value: "556" }, - { label: "Убийства", value: "465" }, - { label: "Уверенный в себе главный герой", value: "151" }, - { label: "Умная пара", value: "662" }, - { label: "Умный главный герой", value: "135" }, - { label: "Уникальная техника культивации", value: "735" }, - { label: "Уникальные оружия", value: "737" }, - { label: "Управление бизнесом", value: "109" }, - { label: "Управление временем", value: "710" }, - { label: "Управление пространством", value: "667" }, - { label: "Ускоренный рост", value: "6" }, - { label: "Учёные", value: "605" }, - { label: "Учителя", value: "700" }, - { label: "Фамильяры", value: "258" }, - { label: "Фанатизм", value: "264" }, - { label: "Фармацевт", value: "523" }, - { label: "Феи", value: "254" }, - { label: "Фениксы", value: "526" }, - { label: "Фетиш на грудь", value: "103" }, - { label: "Фетиш на очки", value: "311" }, - { label: "Философия", value: "524" }, - { label: "Фобии/Страхи", value: "525" }, - { label: "Фольклор", value: "285" }, - { label: "Формирование армии", value: "54" }, - { label: "Футанари", value: "295" }, - { label: "Футуристический мир", value: "296" }, - { label: "Фэнтези", value: "267" }, - { label: "Фэнтезийные существа", value: "266" }, - { label: "Хакеры", value: "326" }, - { label: "Харизматичный протагонист", value: "120" }, - { label: "Хитрый главный герой", value: "173" }, - { label: "Хладнокровный главный герой", value: "112" }, - { label: "Художники", value: "60" }, - { label: "Хулиганы", value: "188" }, - { label: "Цундере", value: "728" }, - { label: "Чат", value: "122" }, - { label: "Человек-оружие", value: "352" }, - { label: "Человекоподобный главный герой", value: "354" }, - { label: "Честный главный герой", value: "348" }, - { label: "Четкие любовные убеждения", value: "683" }, - { label: "Читы", value: "123" }, - { label: "Чудаковатые герои", value: "569" }, - { label: "Чужие воспоминания", value: "721" }, - { label: "Шантаж", value: "89" }, - { label: "Шеф-повар", value: "124" }, - { label: "Школа для девочек", value: "29" }, - { label: "Шоу-бизнес", value: "642" }, - { label: "Шпионы", value: "670" }, - { label: "Эволюция", value: "250" }, - { label: "Эгоистичный главный герой", value: "618" }, - { label: "Экзорцизм", value: "252" }, - { label: "Экономика", value: "230" }, - { label: "Эксгибиционизм", value: "251" }, - { label: "Эксперименты на людях", value: "351" }, - { label: "Элементальная магия", value: "234" }, - { label: "Элементы игрового мира", value: "299" }, - { label: "Элементы романтики", value: "594" }, - { label: "Элементы юри", value: "640" }, - { label: "Элементы яоя", value: "641" }, - { label: "Эльфы", value: "235" }, - { label: "Эпизодичность", value: "243" }, - { label: "Язвительные персонажи", value: "635" }, - { label: "Языковой барьер", value: "386" }, - { label: "Яндере", value: "762" }, - { label: "Яойщица", value: "294" }, - { label: "Японские силы самообороны", value: "378" }, - { label: "R-15", value: "570" }, - { label: "R-18", value: "571" }, - ], - type: FilterTypes.CheckboxGroup, - }, - genres: { - label: "Жанры", - value: [], - options: [ - { label: "16+", value: "1" }, - { label: "18+", value: "2" }, - { label: "Боевик", value: "3" }, - { label: "Боевые искусства", value: "4" }, - { label: "Вампиры", value: "47" }, - { label: "Военное", value: "44" }, - { label: "Гарем", value: "5" }, - { label: "Демоны", value: "48" }, - { label: "Детектив", value: "6" }, - { label: "Дзёсей", value: "50" }, - { label: "Драма", value: "7" }, - { label: "Игры", value: "45" }, - { label: "Историческое", value: "8" }, - { label: "Киберпанк", value: "9" }, - { label: "Комедия", value: "10" }, - { label: "Космос", value: "46" }, - { label: "Магия", value: "42" }, - { label: "Махо-сёдзё", value: "12" }, - { label: "Меха", value: "13" }, - { label: "Мистика", value: "14" }, - { label: "Музыка", value: "49" }, - { label: "Научная фантастика", value: "15" }, - { label: "Пародия", value: "16" }, - { label: "Повседневность", value: "17" }, - { label: "Приключения", value: "18" }, - { label: "Психологическое", value: "19" }, - { label: "Романтика", value: "20" }, - { label: "Сверхъестественное", value: "21" }, - { label: "Сёдзё", value: "22" }, - { label: "Сёдзё-ай", value: "23" }, - { label: "Сёнэн", value: "24" }, - { label: "Сёнэн-ай", value: "25" }, - { label: "Смена пола", value: "26" }, - { label: "Современность", value: "27" }, - { label: "Спорт", value: "28" }, - { label: "Супер сила", value: "43" }, - { label: "Сэйнэн", value: "29" }, - { label: "Трагедия", value: "32" }, - { label: "Триллер", value: "33" }, - { label: "Ужасы", value: "34" }, - { label: "Уся", value: "35" }, - { label: "Фэнтези", value: "36" }, - { label: "Школьная жизнь", value: "37" }, - { label: "Этти", value: "38" }, - { label: "Юри", value: "39" }, - { label: "Яой", value: "40" }, - { label: "LitRPG", value: "41" }, - ], - type: FilterTypes.CheckboxGroup, - }, - } satisfies Filters; -} - -export default new TL(); - -interface response { - data: Data; -} - -interface Data { - projects?: Projects; - project?: Project; - chapter?: DataChapter; -} - -interface DataChapter { - text: Text; -} - -interface Text { - text: string; -} - -interface Project { - title: string; - translationStatus: string; - fullUrl: string; - covers: Cover[]; - persons: Person[]; - genres: Genre[]; - tags: Genre[]; - annotation: Text; - subprojects: Subprojects; -} - -interface Cover { - url: string; -} - -interface Genre { - nameRu: string; - nameEng: string; -} - -interface Person { - role: string; - name: Name; -} - -interface Name { - firstName: string; - lastName: string; -} - -interface Subprojects { - content: SubprojectsContent[]; -} - -interface SubprojectsContent { - title: null | string; - volumes: Volumes; -} - -interface Volumes { - content: VolumesContent[]; -} - -interface VolumesContent { - shortName: string; - chapters: ChapterElement[]; -} - -interface ChapterElement { - title: string; - publishDate: Date; - fullUrl: string; - published: boolean; -} - -interface Projects { - content: ProjectsContent[]; -} - -interface ProjectsContent { - title: string; - fullUrl: string; - covers: Cover[]; -} +import { Plugin } from "@typings/plugin"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { defaultCover } from "@libs/defaultCover"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { NovelStatus } from "@libs/novelStatus"; +import { load as parseHTML } from "cheerio"; +import dayjs from "dayjs"; + +class TL implements Plugin.PluginBase { + id = "TL"; + name = "NovelTL"; + site = "https://novel.tl"; + version = "1.0.0"; + icon = "src/ru/noveltl/icon.png"; + userAgent = ""; + + async popularNovels( + page: number, + { showLatestNovels, filters }: Plugin.PopularNovelsOptions, + ): Promise { + const result = await fetchApi(this.site + "/api/site/v2/graphql", { + method: "post", + headers: { + Accept: "application/json, text/plain, */*", + "Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3", + "Content-Type": "application/json", + }, + referrer: this.site, + body: JSON.stringify({ + operationName: "Projects", + query: + "query Projects($hostname:String! $filter:SearchFilter $page:Int $limit:Int){projects(section:{fullUrl:$hostname}filter:$filter page:{pageSize:$limit,pageNumber:$page}){content{title fullUrl covers{url}}}}", + variables: { + filter: { + tags: filters?.tags?.value || [], + genres: filters?.genres?.value || [], + }, + hostname: "novel.tl", + limit: 40, + page, + }, + }), + }); + + const json = (await result.json()) as response; + const novels: Plugin.NovelItem[] = []; + + json.data?.projects?.content?.forEach((novel) => + novels.push({ + name: novel.title, + url: "https://" + novel.fullUrl, + cover: novel?.covers?.[0]?.url + ? this.site + novel.covers[0].url + : defaultCover, + }), + ); + + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const result = await fetchApi(this.site + "/api/site/v2/graphql", { + method: "post", + headers: { + Accept: "application/json, text/plain, */*", + "Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3", + "Content-Type": "application/json", + }, + referrer: this.site, + body: JSON.stringify({ + operationName: "Book", + query: + 'query Book($url:String){project(project:{fullUrl:$url}){title translationStatus fullUrl covers{url}persons(langs:["ru","en","*"],roles:["author","illustrator"]){role name{firstName lastName}}genres{nameRu nameEng}tags{nameRu nameEng}annotation{text}subprojects{content{title volumes{content{shortName chapters{title publishDate fullUrl published}}}}}}}', + variables: { + url: novelUrl, + }, + }), + }); + const json = (await result.json()) as response; + const novel: Plugin.SourceNovel = { + url: novelUrl, + name: json.data.project?.title, + cover: json.data.project?.covers?.[0]?.url + ? this.site + json.data.project.covers[0].url + : defaultCover, + summary: json.data.project?.annotation?.text, + status: + json.data.project?.translationStatus === "active" + ? NovelStatus.Ongoing + : NovelStatus.Completed, + }; + + const genres = [json.data.project?.tags, json.data.project?.genres] + .flat() + .map((item) => item?.nameRu || item?.nameEng) + .filter((item) => item); + + if (genres?.length) { + novel.genres = genres.join(", "); + } + + json.data.project?.persons?.forEach((person) => { + if (person.role == "author" && person.name.firstName) { + novel.author = + person.name.firstName + " " + (person.name?.lastName || ""); + } + }); + + const chapters: Plugin.ChapterItem[] = []; + json.data.project?.subprojects?.content?.forEach((work) => + work.volumes.content.forEach((volume) => + volume.chapters.forEach( + (chapter) => + chapter?.published && + chapters.push({ + name: volume.shortName + " " + chapter.title, + url: "https://" + chapter.fullUrl, + releaseTime: dayjs(chapter.publishDate).format("LLL"), + }), + ), + ), + ); + + novel.chapters = chapters; + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const result = await fetchApi(this.site + "/api/site/v2/graphql", { + method: "post", + headers: { + Accept: "application/json, text/plain, */*", + "Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3", + "Content-Type": "application/json", + }, + referrer: this.site, + body: JSON.stringify({ + query: + "query($url:String){chapter(chapter:{fullUrl:$url}){text{text}}}", + variables: { + url: chapterUrl, + }, + }), + }); + const json = (await result.json()) as response; + + const baseUrl = this.site; + const loadedCheerio = parseHTML(json.data.chapter?.text?.text || ""); + loadedCheerio("p > a[href]").each(function () { + let src = baseUrl + loadedCheerio(this).attr("href"); + if (!src.startsWith("http")) { + src = baseUrl + src; + } + loadedCheerio(this).find("picture").remove(); + loadedCheerio(this).removeAttr("href"); + loadedCheerio(``).appendTo(this); + }); + + const chapterText = loadedCheerio.html(); + return chapterText; + } + + async searchNovels( + searchTerm: string, + page: number | undefined = 1, + ): Promise { + const result = await fetchApi(this.site + "/api/site/v2/graphql", { + method: "post", + headers: { + Accept: "application/json, text/plain, */*", + "Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3", + "Content-Type": "application/json", + }, + referrer: this.site, + body: JSON.stringify({ + operationName: "Projects", + query: + "query Projects($hostname:String! $filter:SearchFilter $page:Int $limit:Int){projects(section:{fullUrl:$hostname}filter:$filter page:{pageSize:$limit,pageNumber:$page}){content{title fullUrl covers{url}}}}", + variables: { + filter: { + query: searchTerm, + }, + hostname: "novel.tl", + limit: 40, + page, + }, + }), + }); + const json = (await result.json()) as response; + const novels: Plugin.NovelItem[] = []; + + json?.data?.projects?.content?.forEach((novel) => + novels.push({ + name: novel.title, + url: "https://" + novel.fullUrl, + cover: novel?.covers?.[0]?.url + ? this.site + novel.covers[0].url + : defaultCover, + }), + ); + + return novels; + } + + fetchImage = fetchFile; + + filters = { + tags: { + label: "Тэги", + value: [], + options: [ + { label: "Автоматы", value: "64" }, + { label: "Агрессивные персонажи", value: "26" }, + { label: "Ад", value: "338" }, + { label: "Адаптация манги", value: "8" }, + { label: "Академия", value: "5" }, + { label: "Актёрское искусство", value: "7" }, + { label: "Актеры озвучки", value: "746" }, + { label: "Алхимия", value: "27" }, + { label: "Альтернативная реальность", value: "30" }, + { label: "Амнезия", value: "31" }, + { label: "Анабиоз", value: "170" }, + { label: "Анал", value: "33" }, + { label: "Ангелы", value: "38" }, + { label: "Андроиды", value: "37" }, + { label: "Анти-рост персонажа", value: "25" }, + { label: "Антигерой", value: "43" }, + { label: "Антикварный магазин", value: "44" }, + { label: "Антимагия", value: "41" }, + { label: "Антиутопия", value: "225" }, + { label: "Апатичный главный герой", value: "46" }, + { label: "Апокалипсис", value: "47" }, + { label: "Аристократия", value: "51" }, + { label: "Армия", value: "53" }, + { label: "Артефакты", value: "58" }, + { label: "Асоциальный главный герой", value: "42" }, + { label: "Ассасины", value: "61" }, + { label: "Атмосфера Европы", value: "245" }, + { label: "Банды", value: "302" }, + { label: "БДСМ", value: "77" }, + { label: "Бедный главный герой", value: "541" }, + { label: "Бездушный главный герой", value: "143" }, + { label: "Беззаботный герой", value: "115" }, + { label: "Безответная любовь", value: "213" }, + { label: "Безответная любовь", value: "740" }, + { label: "Безработные", value: "377" }, + { label: "Беременность", value: "551" }, + { label: "Бескорыстная любовь", value: "733" }, + { label: "Бессмертные", value: "359" }, + { label: "Бесстрашный главный герой", value: "274" }, + { label: "Библиотека", value: "393" }, + { label: "Биография", value: "86" }, + { label: "Близнецы", value: "729" }, + { label: "Богачи", value: "753" }, + { label: "Боги", value: "318" }, + { label: "Богини", value: "316" }, + { label: "Богоподобный протагонист", value: "314" }, + { label: "Боевая академия", value: "75" }, + { label: "Боевая парочка", value: "547" }, + { label: "Божественная защита", value: "208" }, + { label: "Божественная сила", value: "317" }, + { label: "Болеющие персонажи", value: "648" }, + { label: "Борьба за власть", value: "548" }, + { label: "Брак по расчету", value: "55" }, + { label: "Братство", value: "106" }, + { label: "Братья или сёстры", value: "646" }, + { label: "Брошенные дети", value: "1" }, + { label: "Бывший герой", value: "290" }, + { label: "Вампиры", value: "742" }, + { label: "Ваншот", value: "496" }, + { label: "Вдали от родителей", value: "3" }, + { label: "Ведьмы", value: "756" }, + { label: "Везучий главный герой", value: "411" }, + { label: "Верные подданные", value: "410" }, + { label: "Вероломные герои/плетение интриг", value: "424" }, + { label: "Взросление персонажа", value: "24" }, + { label: "Видит то, чего остальной люд не в силах", value: "617" }, + { label: "Виртуальная реальность", value: "744" }, + { label: "Владелец магазина", value: "677" }, + { label: "Владелец уникального оружия", value: "736" }, + { label: "Властные персонажи", value: "545" }, + { label: "Влияние прошлого", value: "517" }, + { label: "Внезапное обогащение", value: "691" }, + { label: "Внезапное обретение силы", value: "690" }, + { label: "Военные хроники", value: "749" }, + { label: "Возвращение в родной мир", value: "587" }, + { label: "Война", value: "750" }, + { label: "Вокалоид", value: "745" }, + { label: "Волшебные твари", value: "413" }, + { label: "Воскрешение", value: "586" }, + { label: "Воспитанный главный герой", value: "537" }, + { label: "Воспоминания прошлого", value: "283" }, + { label: "Враги становятся союзниками", value: "238" }, + { label: "Врата в иной мир", value: "303" }, + { label: "Временной парадокс", value: "711" }, + { label: "Вторжение на Землю", value: "228" }, + { label: "Второй шанс", value: "608" }, + { label: "Выживание", value: "695" }, + { label: "Вынужденные условия проживания", value: "287" }, + { label: "Высокомерный герой", value: "56" }, + { label: "Гадание", value: "207" }, + { label: "Галлюцинации", value: "189" }, + { label: "Гарем рабов", value: "656" }, + { label: "Геймеры", value: "301" }, + { label: "Генетические модификации", value: "306" }, + { label: "Герои", value: "341" }, + { label: "Герои непонятного пола", value: "36" }, + { label: "Героиня девушка-сорванец", value: "715" }, + { label: "Героиня красавица", value: "81" }, + { label: "Герой влюбляется первым", value: "561" }, + { label: "Герой использует щит", value: "636" }, + { label: "Герой красавец", value: "329" }, + { label: "Герой со множеством тел", value: "564" }, + { label: "Герой-бесстыдник", value: "632" }, + { label: "Герой-извращенец", value: "521" }, + { label: "Герой-трудяга", value: "330" }, + { label: "Герой-яндере", value: "421" }, + { label: "Гетерохромия", value: "342" }, + { label: "Гильдии", value: "324" }, + { label: "Гиперопека", value: "509" }, + { label: "Главная героиня — женщина", value: "277" }, + { label: "Главный герой - гений", value: "308" }, + { label: "Главный герой - Моб", value: "446" }, + { label: "Главный герой — знаменитость", value: "263" }, + { label: "Главный герой — мужчина", value: "419" }, + { label: "Главный герой в поисках гарема", value: "331" }, + { label: "Главный герой заранее готовится", value: "557" }, + { label: "Главный герой не человек", value: "488" }, + { label: "Главный герой приёмный", value: "20" }, + { label: "Главный герой сразу силён", value: "563" }, + { label: "Гладиаторы", value: "310" }, + { label: "Глухой к любви герой", value: "194" }, + { label: "Гоблины", value: "313" }, + { label: "Големы", value: "319" }, + { label: "Гомункул", value: "347" }, + { label: "Горничные", value: "418" }, + { label: "Государственные интриги", value: "212" }, + { label: "Гринд", value: "322" }, + { label: "Давняя травма", value: "518" }, + { label: "Двойник", value: "97" }, + { label: "Дворецкие", value: "111" }, + { label: "Дворфы", value: "224" }, + { label: "Дворяне", value: "487" }, + { label: "Двоюродные братья или сёстры", value: "162" }, + { label: "Девочки-волшебницы", value: "415" }, + { label: "Демоны", value: "193" }, + { label: "Депрессия", value: "196" }, + { label: "Детектив", value: "198" }, + { label: "Детективное расследование", value: "473" }, + { label: "Детское насилие", value: "125" }, + { label: "Детское обещание", value: "130" }, + { label: "Дискриминация", value: "203" }, + { label: "Длительный разрыв отношений", value: "400" }, + { label: "Домашняя жизнь", value: "45" }, + { label: "Драконы", value: "218" }, + { label: "Древний Китай", value: "34" }, + { label: "Дружба", value: "293" }, + { label: "Друзья детства", value: "128" }, + { label: "Друзья становятся врагами", value: "292" }, + { label: "Духи", value: "673" }, + { label: "Духовная сила", value: "665" }, + { label: "Духовный наставник", value: "671" }, + { label: "Душещипательная история", value: "335" }, + { label: "Души", value: "666" }, + { label: "Дьявольский путь культивации", value: "192" }, + { label: "Ёкай", value: "763" }, + { label: "Есть аниме-адаптация", value: "10" }, + { label: "Есть видеоигра", value: "13" }, + { label: "Есть дорама-адаптация", value: "11" }, + { label: "Есть манга-адаптация", value: "14" }, + { label: "Есть манхва-адаптация", value: "16" }, + { label: "Есть маньхуа-адаптация", value: "15" }, + { label: "Есть фильм", value: "17" }, + { label: "Есть CD дорама-адаптация", value: "12" }, + { label: "Жёсткий главный герой", value: "597" }, + { label: "Жестокие персонажи", value: "4" }, + { label: "Жёстокие персонажи", value: "169" }, + { label: "Животное-спутник", value: "78" }, + { label: "Жизнь в одиночку", value: "396" }, + { label: "Жрецы", value: "555" }, + { label: "Жрицы", value: "554" }, + { label: "Забота о детях", value: "127" }, + { label: "Заботливый главный герой", value: "116" }, + { label: "Загадочное заболевание", value: "471" }, + { label: "Загадочное прошлое родителей", value: "470" }, + { label: "Заговоры", value: "154" }, + { label: "Закалка тела", value: "96" }, + { label: "Закон джунглей", value: "682" }, + { label: "Закулисная борба", value: "603" }, + { label: "Замкнутый главный герой", value: "371" }, + { label: "Запечатанная сила", value: "607" }, + { label: "Затворник", value: "346" }, + { label: "Звери", value: "80" }, + { label: "Зверолюди", value: "39" }, + { label: "Звероподобные", value: "79" }, + { label: "Земледелие", value: "268" }, + { label: "Злобные благородные девы", value: "743" }, + { label: "Зловещие организации", value: "247" }, + { label: "Злой главный герой", value: "248" }, + { label: "Злые боги", value: "246" }, + { label: "Злые религии", value: "249" }, + { label: "Знаменитости", value: "118" }, + { label: "Знания из прошлой жизни", value: "553" }, + { label: "Зомби", value: "767" }, + { label: "Игра на выживание", value: "696" }, + { label: "Игровая рейтинговая система", value: "300" }, + { label: "Из грязи в князи", value: "542" }, + { label: "Известные родители", value: "262" }, + { label: "Изменение внешнего вида", value: "48" }, + { label: "Изнасилование", value: "574" }, + { label: "Инвалидность", value: "202" }, + { label: "Индустриализация", value: "364" }, + { label: "Инженер", value: "241" }, + { label: "Инцест", value: "361" }, + { label: "Искажённая личность", value: "730" }, + { label: "Искусственный интеллект", value: "59" }, + { label: "Исполнение желаний", value: "755" }, + { label: "Каннибализм", value: "113" }, + { label: "Карточные игры", value: "114" }, + { label: "Кладбищенский смотритель", value: "321" }, + { label: "Классика", value: "134" }, + { label: "Книги", value: "99" }, + { label: "Книги навыков", value: "654" }, + { label: "Книжный червь", value: "100" }, + { label: "Коллеги", value: "140" }, + { label: "Колледж/Университет", value: "145" }, + { label: "Командная работа", value: "701" }, + { label: "Комедийный подтекст", value: "147" }, + { label: "Комплекс брата", value: "105" }, + { label: "Комплекс неполноценности", value: "365" }, + { label: "Контракты", value: "155" }, + { label: "Контроль разума", value: "441" }, + { label: "Конфликт в семье", value: "261" }, + { label: "Конфликт верности", value: "153" }, + { label: "Копейщик", value: "668" }, + { label: "Королевская особа", value: "596" }, + { label: "Королевства", value: "382" }, + { label: "Короткий рассказ", value: "638" }, + { label: "Коррупция", value: "157" }, + { label: "Космические Войны", value: "158" }, + { label: "Космос", value: "507" }, + { label: "Косплей", value: "159" }, + { label: "Кража способностей", value: "2" }, + { label: "Крафтинг", value: "164" }, + { label: "Кризис личности", value: "357" }, + { label: "Кровь", value: "320" }, + { label: "Кружки, клубы", value: "138" }, + { label: "Кудэрэ", value: "384" }, + { label: "Кузнец", value: "90" }, + { label: "Кукловоды", value: "567" }, + { label: "Куклы/Марионетки", value: "211" }, + { label: "Кулинария", value: "156" }, + { label: "Культивация", value: "171" }, + { label: "Куннилингус", value: "172" }, + { label: "Легенды", value: "391" }, + { label: "Легкая жизнь", value: "229" }, + { label: "Лекари", value: "334" }, + { label: "Ленивый главный герой", value: "389" }, + { label: "Лидерство", value: "390" }, + { label: "Любит персонажа младше себя", value: "765" }, + { label: "Любит персонажа старше себя", value: "494" }, + { label: "Любовное соперничество", value: "406" }, + { label: "Любовные треугольники", value: "407" }, + { label: "Любовь детства", value: "129" }, + { label: "Любовь с первого взгляда", value: "404" }, + { label: "Любящие родители", value: "215" }, + { label: "Маги", value: "757" }, + { label: "Магические технологии", value: "417" }, + { label: "Магия", value: "412" }, + { label: "Магия призыва", value: "694" }, + { label: "Маленькие девочки", value: "397" }, + { label: "Мангака", value: "423" }, + { label: "Маниакальная любовь", value: "492" }, + { label: "Марти Сью", value: "508" }, + { label: "Мастер подземелий", value: "222" }, + { label: "Мастурбация", value: "432" }, + { label: "Матриархат", value: "433" }, + { label: "Межпространственные путешествия", value: "370" }, + { label: "Менеджмент", value: "422" }, + { label: "Месть", value: "588" }, + { label: "Мечи и магия", value: "697" }, + { label: "Мечники", value: "698" }, + { label: "Миленькие дети", value: "176" }, + { label: "Милитаризм", value: "439" }, + { label: "Миловидная история", value: "178" }, + { label: "Милый главный герой", value: "177" }, + { label: "Минет", value: "275" }, + { label: "Мировое древо", value: "760" }, + { label: "Миролюбивый главный герой", value: "510" }, + { label: "Мифология", value: "475" }, + { label: "Младшие сёстры", value: "766" }, + { label: "ММОРПГ", value: "445" }, + { label: "Множество временных линий", value: "463" }, + { label: "Модели", value: "447" }, + { label: "Молод не по годам", value: "49" }, + { label: "Монстры", value: "454" }, + { label: "Мрачность", value: "183" }, + { label: "Музыка", value: "466" }, + { label: "Музыкальные группы", value: "68" }, + { label: "Мутации", value: "468" }, + { label: "На волосок от смерти", value: "479" }, + { label: "На все руки мастер", value: "374" }, + { label: "Навыки имеют стоимость", value: "150" }, + { label: "Нагота", value: "490" }, + { label: "Наездники на драконах", value: "216" }, + { label: "Наёмники", value: "437" }, + { label: "Наивный главный герой", value: "476" }, + { label: "Наследие", value: "366" }, + { label: "Настоящая любовь", value: "200" }, + { label: "Настоящий гарем", value: "540" }, + { label: "Небесное испытание", value: "337" }, + { label: "Невезучий главный герой", value: "738" }, + { label: "Недоверчивый протагонист", value: "206" }, + { label: "Недооцененный главный герой", value: "734" }, + { label: "Недопонимание", value: "444" }, + { label: "Некромант", value: "480" }, + { label: "Нелинейное повествование", value: "489" }, + { label: "Неловкий главный герой", value: "67" }, + { label: "Нелюбимый протагонист", value: "333" }, + { label: "Немой персонаж", value: "469" }, + { label: "Ненадежный рассказчик", value: "739" }, + { label: "Непримечательный герой", value: "65" }, + { label: "Нерешительный протагонист", value: "363" }, + { label: "Несгибаемый главный герой", value: "676" }, + { label: "Несколько главных героев", value: "460" }, + { label: "Несколько миров", value: "461" }, + { label: "Несколько перерожденцев", value: "462" }, + { label: "Несколько попаданцев", value: "464" }, + { label: "Несравненная скорость культивации", value: "269" }, + { label: "Нехватка/отсутствие здравого смысла", value: "385" }, + { label: "Нечестный протагонист", value: "205" }, + { label: "НИИТ", value: "481" }, + { label: "Ниндзя", value: "486" }, + { label: "Обмен телами", value: "95" }, + { label: "Оборотни", value: "754" }, + { label: "Объект любви влюбился первым", value: "405" }, + { label: "Объект любви популярен", value: "543" }, + { label: "Огнестрельное оружие", value: "280" }, + { label: "Ограничение на время жизни", value: "394" }, + { label: "Одержимость", value: "544" }, + { label: "Один родитель", value: "651" }, + { label: "Одинокий главный герой", value: "399" }, + { label: "Одиночество", value: "398" }, + { label: "Одно тело на двоих", value: "634" }, + { label: "Око силы", value: "253" }, + { label: "Оммёдзи", value: "498" }, + { label: "Организованная преступность", value: "500" }, + { label: "Оргия", value: "501" }, + { label: "Орки", value: "499" }, + { label: "Основано на аниме", value: "74" }, + { label: "Основано на видеоигре", value: "72" }, + { label: "Основано на визуальной новелле", value: "73" }, + { label: "Основано на песне", value: "70" }, + { label: "Основано на фильме", value: "69" }, + { label: "Осторожный главный герой", value: "117" }, + { label: "От ненависти до любви один шаг", value: "239" }, + { label: "От сильного с сильнейшему", value: "684" }, + { label: "От слабого к сильному", value: "752" }, + { label: "Отаку", value: "503" }, + { label: "Отзывчивая любовь", value: "380" }, + { label: "Отзывчивый главный герой", value: "339" }, + { label: "Отношение учитель-ученик", value: "430" }, + { label: "Отношения бог-человек", value: "315" }, + { label: "Отношения между учителем и учеником", value: "687" }, + { label: "Отношения начальник-подчинённый", value: "101" }, + { label: "Отношения против воли", value: "286" }, + { label: "Отношения с нечеловеком", value: "353" }, + { label: "Отношения сэмпай-кохай", value: "621" }, + { label: "Отношения хозяин-слуга", value: "431" }, + { label: "Отомэ-игры", value: "504" }, + { label: "ОТП", value: "562" }, + { label: "Отшельники", value: "505" }, + { label: "Офисный работник", value: "600" }, + { label: "Официанты", value: "748" }, + { label: "Охотники", value: "355" }, + { label: "Падшие ангелы", value: "255" }, + { label: "Падшие дворяне", value: "256" }, + { label: "Пайзури", value: "511" }, + { label: "Паразиты", value: "513" }, + { label: "Параллельные миры", value: "512" }, + { label: "Парк развлечений", value: "32" }, + { label: "Пародия", value: "515" }, + { label: "Перемещение между мирами", value: "758" }, + { label: "Переодевание", value: "167" }, + { label: "Переплавка пилюль", value: "529" }, + { label: "Перерождение в девушку", value: "420" }, + { label: "Перерождение в другом мире", value: "580" }, + { label: "Перерождение в другом мире", value: "720" }, + { label: "Перерождение в игровом мире", value: "579" }, + { label: "Перерождение в монстра", value: "577" }, + { label: "Персонажи с мазохистскими наклонностями", value: "429" }, + { label: "Персонажи с садистскими наклонностями", value: "598" }, + { label: "Петля времени", value: "709" }, + { label: "Петтинг", value: "328" }, + { label: "Писатели", value: "761" }, + { label: "Питомцы", value: "522" }, + { label: "Племенное общество", value: "726" }, + { label: "Повелитель демонов", value: "191" }, + { label: "Повествование от нескольких лиц", value: "459" }, + { label: "Поглощение навыков", value: "653" }, + { label: "Подземелья", value: "223" }, + { label: "Поздно начинающаяся романтическая линия", value: "387" }, + { label: "Политика", value: "538" }, + { label: "Полулюди", value: "190" }, + { label: "Помолвка", value: "240" }, + { label: "Попаданец в другой мир", value: "723" }, + { label: "Попаданец в игровой мир", value: "722" }, + { label: "Постапокалипсис", value: "546" }, + { label: "Постижение Дао", value: "181" }, + { label: "Потеря девственности", value: "282" }, + { label: "Потустороннее пространство", value: "416" }, + { label: "Похищения", value: "379" }, + { label: "Прагматичный главный герой", value: "549" }, + { label: "Предательство", value: "83" }, + { label: "Предвидение", value: "550" }, + { label: "Преследователи", value: "674" }, + { label: "Преступление", value: "165" }, + { label: "Преступность", value: "166" }, + { label: "Приёмные дети", value: "19" }, + { label: "Призванный герой", value: "693" }, + { label: "Призраки", value: "309" }, + { label: "Приключения", value: "22" }, + { label: "Принуждение", value: "108" }, + { label: "Приручатель", value: "453" }, + { label: "Прислуга", value: "625" }, + { label: "Приставучий любовник", value: "136" }, + { label: "Притворная любовь", value: "552" }, + { label: "Пришельцы", value: "28" }, + { label: "Программист", value: "558" }, + { label: "Произведение - обладатель наград", value: "66" }, + { label: "Проклятия", value: "175" }, + { label: "Промывка мозгов", value: "102" }, + { label: "Пропуск времени", value: "712" }, + { label: "Пророчества", value: "559" }, + { label: "Протагонист в очках", value: "312" }, + { label: "Протагонист раб", value: "657" }, + { label: "Психокинез", value: "565" }, + { label: "Психопаты", value: "566" }, + { label: "Путешествия", value: "759" }, + { label: "Путешествия во времени", value: "713" }, + { label: "Пытки", value: "716" }, + { label: "Рабы", value: "658" }, + { label: "Развитие отношений", value: "160" }, + { label: "Развитие персонажа", value: "119" }, + { label: "Раздвоение личности", value: "458" }, + { label: "Размеренная романтика", value: "661" }, + { label: "Разница в статусе", value: "201" }, + { label: "Разорванная помолвка", value: "104" }, + { label: "Разумные предметы", value: "622" }, + { label: "Рай", value: "336" }, + { label: "Раняя романтика", value: "227" }, + { label: "Расизм", value: "573" }, + { label: "Расследования", value: "372" }, + { label: "Ребенок — главный герой", value: "126" }, + { label: "Реверс-гарем", value: "589" }, + { label: "Реверс-изнасилование", value: "590" }, + { label: "Резкая смена характера", value: "520" }, + { label: "Реинкарнация", value: "581" }, + { label: "Религии", value: "582" }, + { label: "Ресторан", value: "585" }, + { label: "Решительный главный герой", value: "199" }, + { label: "Робкий протагонист", value: "714" }, + { label: "Родительсткий комплекс", value: "514" }, + { label: "Родные не связанные кровью", value: "647" }, + { label: "Родословные", value: "94" }, + { label: "РПГ-система", value: "392" }, + { label: "Рыцари", value: "383" }, + { label: "Самовлюбленный главный герой", value: "477" }, + { label: "Самоубийства", value: "692" }, + { label: "Самураи", value: "601" }, + { label: "Сборник коротких историй", value: "144" }, + { label: "Свадьба", value: "426" }, + { label: "Связанные сюжетные линии", value: "369" }, + { label: "Святые", value: "599" }, + { label: "Секреты", value: "614" }, + { label: "Секс втроём", value: "707" }, + { label: "Секс-рабыня", value: "629" }, + { label: "Семейная любовь", value: "257" }, + { label: "Семь добродетелей", value: "627" }, + { label: "Семь смертных грехов", value: "626" }, + { label: "Серийные убийцы", value: "624" }, + { label: "Сикигами", value: "637" }, + { label: "Сильная любовь от старших", value: "214" }, + { label: "Синдром восьмиклассника", value: "132" }, + { label: "Синдром сестры", value: "652" }, + { label: "Сироты", value: "502" }, + { label: "Системный администратор", value: "699" }, + { label: "Скромный главный герой", value: "409" }, + { label: "Скрытые романтические отношения", value: "23" }, + { label: "Скрытые способности", value: "343" }, + { label: "Скряга", value: "450" }, + { label: "Скульпторы", value: "606" }, + { label: "Слабая романтика", value: "688" }, + { label: "Слабый главный герой", value: "751" }, + { label: "Сложные семейные отношения", value: "149" }, + { label: "Смена расы", value: "572" }, + { label: "Смертельное заболевание", value: "704" }, + { label: "Смерть", value: "185" }, + { label: "Смерть любимых", value: "186" }, + { label: "Современность", value: "448" }, + { label: "Современные знания в слаборазвитых мирах", value: "449" }, + { label: "Сожительство", value: "141" }, + { label: "Создание артефактов", value: "57" }, + { label: "Создание клана", value: "133" }, + { label: "Создание королевства", value: "381" }, + { label: "Создание навыков", value: "655" }, + { label: "Сокрытие истинных способностей", value: "344" }, + { label: "Сокрытие личности", value: "345" }, + { label: "Солдаты", value: "664" }, + { label: "Сон", value: "659" }, + { label: "Соседи по комнате", value: "595" }, + { label: "Социальные изгои", value: "663" }, + { label: "Спасение мира", value: "602" }, + { label: "Спорящая пара", value: "84" }, + { label: "Способность перевоплощения", value: "719" }, + { label: "Средневековье", value: "436" }, + { label: "Стеснительные персонажи", value: "643" }, + { label: "Стокгольмский синдром", value: "575" }, + { label: "Стокгольмский синдром", value: "675" }, + { label: "Стратег", value: "681" }, + { label: "Стратегические битвы", value: "680" }, + { label: "Стрелки", value: "325" }, + { label: "Стрельба из лука", value: "50" }, + { label: "Студенческий совет", value: "686" }, + { label: "Судьба", value: "197" }, + { label: "Суккубы", value: "689" }, + { label: "Суперспособности", value: "669" }, + { label: "Суровая тренировка", value: "332" }, + { label: "Сцены насилия", value: "195" }, + { label: "Таинственное прошлое", value: "472" }, + { label: "Тайная личность", value: "610" }, + { label: "Тайные организации", value: "611" }, + { label: "Телохранители", value: "98" }, + { label: "Тентакли", value: "703" }, + { label: "Террористы", value: "705" }, + { label: "Технологический разрыв", value: "702" }, + { label: "Тихие герои", value: "568" }, + { label: "Толстый протагонист", value: "271" }, + { label: "Торговцы", value: "438" }, + { label: "Травник", value: "340" }, + { label: "Трагичное прошлое", value: "718" }, + { label: "Трап", value: "725" }, + { label: "Триллер", value: "708" }, + { label: "Турнир", value: "76" }, + { label: "Тюрьма", value: "556" }, + { label: "Убийства", value: "465" }, + { label: "Уверенный в себе главный герой", value: "151" }, + { label: "Умная пара", value: "662" }, + { label: "Умный главный герой", value: "135" }, + { label: "Уникальная техника культивации", value: "735" }, + { label: "Уникальные оружия", value: "737" }, + { label: "Управление бизнесом", value: "109" }, + { label: "Управление временем", value: "710" }, + { label: "Управление пространством", value: "667" }, + { label: "Ускоренный рост", value: "6" }, + { label: "Учёные", value: "605" }, + { label: "Учителя", value: "700" }, + { label: "Фамильяры", value: "258" }, + { label: "Фанатизм", value: "264" }, + { label: "Фармацевт", value: "523" }, + { label: "Феи", value: "254" }, + { label: "Фениксы", value: "526" }, + { label: "Фетиш на грудь", value: "103" }, + { label: "Фетиш на очки", value: "311" }, + { label: "Философия", value: "524" }, + { label: "Фобии/Страхи", value: "525" }, + { label: "Фольклор", value: "285" }, + { label: "Формирование армии", value: "54" }, + { label: "Футанари", value: "295" }, + { label: "Футуристический мир", value: "296" }, + { label: "Фэнтези", value: "267" }, + { label: "Фэнтезийные существа", value: "266" }, + { label: "Хакеры", value: "326" }, + { label: "Харизматичный протагонист", value: "120" }, + { label: "Хитрый главный герой", value: "173" }, + { label: "Хладнокровный главный герой", value: "112" }, + { label: "Художники", value: "60" }, + { label: "Хулиганы", value: "188" }, + { label: "Цундере", value: "728" }, + { label: "Чат", value: "122" }, + { label: "Человек-оружие", value: "352" }, + { label: "Человекоподобный главный герой", value: "354" }, + { label: "Честный главный герой", value: "348" }, + { label: "Четкие любовные убеждения", value: "683" }, + { label: "Читы", value: "123" }, + { label: "Чудаковатые герои", value: "569" }, + { label: "Чужие воспоминания", value: "721" }, + { label: "Шантаж", value: "89" }, + { label: "Шеф-повар", value: "124" }, + { label: "Школа для девочек", value: "29" }, + { label: "Шоу-бизнес", value: "642" }, + { label: "Шпионы", value: "670" }, + { label: "Эволюция", value: "250" }, + { label: "Эгоистичный главный герой", value: "618" }, + { label: "Экзорцизм", value: "252" }, + { label: "Экономика", value: "230" }, + { label: "Эксгибиционизм", value: "251" }, + { label: "Эксперименты на людях", value: "351" }, + { label: "Элементальная магия", value: "234" }, + { label: "Элементы игрового мира", value: "299" }, + { label: "Элементы романтики", value: "594" }, + { label: "Элементы юри", value: "640" }, + { label: "Элементы яоя", value: "641" }, + { label: "Эльфы", value: "235" }, + { label: "Эпизодичность", value: "243" }, + { label: "Язвительные персонажи", value: "635" }, + { label: "Языковой барьер", value: "386" }, + { label: "Яндере", value: "762" }, + { label: "Яойщица", value: "294" }, + { label: "Японские силы самообороны", value: "378" }, + { label: "R-15", value: "570" }, + { label: "R-18", value: "571" }, + ], + type: FilterTypes.CheckboxGroup, + }, + genres: { + label: "Жанры", + value: [], + options: [ + { label: "16+", value: "1" }, + { label: "18+", value: "2" }, + { label: "Боевик", value: "3" }, + { label: "Боевые искусства", value: "4" }, + { label: "Вампиры", value: "47" }, + { label: "Военное", value: "44" }, + { label: "Гарем", value: "5" }, + { label: "Демоны", value: "48" }, + { label: "Детектив", value: "6" }, + { label: "Дзёсей", value: "50" }, + { label: "Драма", value: "7" }, + { label: "Игры", value: "45" }, + { label: "Историческое", value: "8" }, + { label: "Киберпанк", value: "9" }, + { label: "Комедия", value: "10" }, + { label: "Космос", value: "46" }, + { label: "Магия", value: "42" }, + { label: "Махо-сёдзё", value: "12" }, + { label: "Меха", value: "13" }, + { label: "Мистика", value: "14" }, + { label: "Музыка", value: "49" }, + { label: "Научная фантастика", value: "15" }, + { label: "Пародия", value: "16" }, + { label: "Повседневность", value: "17" }, + { label: "Приключения", value: "18" }, + { label: "Психологическое", value: "19" }, + { label: "Романтика", value: "20" }, + { label: "Сверхъестественное", value: "21" }, + { label: "Сёдзё", value: "22" }, + { label: "Сёдзё-ай", value: "23" }, + { label: "Сёнэн", value: "24" }, + { label: "Сёнэн-ай", value: "25" }, + { label: "Смена пола", value: "26" }, + { label: "Современность", value: "27" }, + { label: "Спорт", value: "28" }, + { label: "Супер сила", value: "43" }, + { label: "Сэйнэн", value: "29" }, + { label: "Трагедия", value: "32" }, + { label: "Триллер", value: "33" }, + { label: "Ужасы", value: "34" }, + { label: "Уся", value: "35" }, + { label: "Фэнтези", value: "36" }, + { label: "Школьная жизнь", value: "37" }, + { label: "Этти", value: "38" }, + { label: "Юри", value: "39" }, + { label: "Яой", value: "40" }, + { label: "LitRPG", value: "41" }, + ], + type: FilterTypes.CheckboxGroup, + }, + } satisfies Filters; +} + +export default new TL(); + +interface response { + data: Data; +} + +interface Data { + projects?: Projects; + project?: Project; + chapter?: DataChapter; +} + +interface DataChapter { + text: Text; +} + +interface Text { + text: string; +} + +interface Project { + title: string; + translationStatus: string; + fullUrl: string; + covers: Cover[]; + persons: Person[]; + genres: Genre[]; + tags: Genre[]; + annotation: Text; + subprojects: Subprojects; +} + +interface Cover { + url: string; +} + +interface Genre { + nameRu: string; + nameEng: string; +} + +interface Person { + role: string; + name: Name; +} + +interface Name { + firstName: string; + lastName: string; +} + +interface Subprojects { + content: SubprojectsContent[]; +} + +interface SubprojectsContent { + title: null | string; + volumes: Volumes; +} + +interface Volumes { + content: VolumesContent[]; +} + +interface VolumesContent { + shortName: string; + chapters: ChapterElement[]; +} + +interface ChapterElement { + title: string; + publishDate: Date; + fullUrl: string; + published: boolean; +} + +interface Projects { + content: ProjectsContent[]; +} + +interface ProjectsContent { + title: string; + fullUrl: string; + covers: Cover[]; +} diff --git a/plugins/russian/ranobehub.ts b/plugins/russian/ranobehub.ts index 38ee106..3323007 100644 --- a/plugins/russian/ranobehub.ts +++ b/plugins/russian/ranobehub.ts @@ -1,1285 +1,1284 @@ -import { Plugin } from "@typings/plugin"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { NovelStatus } from "@libs/novelStatus"; -import { load as parseHTML } from "cheerio"; -import dayjs from "dayjs"; - -class RNBH implements Plugin.PluginBase { - id = "RNBH.org"; - name = "RanobeHub"; - version = "1.0.0"; - site = "https://ranobehub.org/"; - icon = "src/ru/ranobehub/icon.png"; - userAgent = ""; - cookieString = ""; - - async popularNovels( - pageNo: number, - { - showLatestNovels, - filters, - }: Plugin.PopularNovelsOptions, - ): Promise { - let url = this.site + `api/search?page=${pageNo}&sort=`; - url += showLatestNovels - ? "last_chapter_at" - : filters?.sort || "computed_rating"; - url += "&status=" + (filters?.status?.value ? filters?.status?.value : "0"); - - if (filters) { - if (filters.country?.value?.length) { - url += "&country=" + filters.country.value.join(","); - } - if ( - filters.tags?.value?.include?.length || - filters.events?.value?.include?.length - ) { - const tagsPositive = [ - filters.tags.value?.include, - filters.events.value?.include, - ] - .flat() - .filter((t) => t); - url += "&tags:positive=" + tagsPositive.join(","); - } - if ( - filters?.tags?.value?.exclude?.length || - filters?.events?.value?.exclude?.length - ) { - const tagsNegative = [ - filters.tags.value?.exclude, - filters.events.value?.exclude, - ] - .flat() - .filter((t) => t); - url += "&tags:negative=" + tagsNegative.join(","); - } - } - - url += "&take=40"; - const result = await fetchApi(url); - const body = (await result.json()) as { resource: responseNovels[] }; - - const novels: Plugin.NovelItem[] = []; - body.resource.forEach((novel) => - novels.push({ - name: novel.names?.rus || novel.names.eng, - cover: novel.poster.medium, - url: novel.url, - }), - ); - - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const novelId = novelUrl - .substring("https://ranobehub.org/ranobe/".length) - .split("-")[0]; - const result = await fetchApi(`${this.site}api/ranobe/${novelId}`); - const json = (await result.json()) as { data: responseNovel }; - - const novel: Plugin.SourceNovel = { - url: json.data.url, - name: json.data.names?.rus || json.data.names.eng, - cover: json.data.posters.medium, - summary: json.data.description, - author: json.data?.authors?.[0]?.name_eng || "", - status: json.data.status.title.includes("процессе") - ? NovelStatus.Ongoing - : NovelStatus.Completed, - }; - - const tags = [json.data.tags.events, json.data.tags.genres] - .flat() - .map((tags) => tags?.names?.rus || tags?.names?.eng || tags?.title) - .filter((tags) => tags); - - if (tags.length) { - novel.genres = tags.join(", "); - } - - const chapters: Plugin.ChapterItem[] = []; - const fetchChaptersUrl = `${this.site}api/ranobe/${novelId}/contents`; - - const chaptersRaw = await fetchApi(fetchChaptersUrl); - const chaptersJSON = (await chaptersRaw.json()) as { - volumes: VolumesEntity[]; - }; - - chaptersJSON.volumes.forEach( - (volume) => - volume.chapters?.forEach((chapter) => - chapters.push({ - name: chapter.name, - url: chapter.url, - releaseTime: dayjs(parseInt(chapter.changed_at, 10) * 1000).format( - "LLL", - ), - }), - ), - ); - - novel.chapters = chapters; - return novel; - } - - async parseChapter(chapterUrl: string): Promise { - const result = await fetchApi(chapterUrl); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - loadedCheerio(".chapter-hoticons").remove(); - const baseUrl = this.site; - loadedCheerio("div.text:nth-child(1) img").each(function () { - if (!loadedCheerio(this).attr("src")?.startsWith("http")) { - const dataMediaId = loadedCheerio(this).attr("data-media-id"); - loadedCheerio(this).attr("src", baseUrl + "api/media/" + dataMediaId); - } - }); - - let chapterText: string = - loadedCheerio("div.text:nth-child(1)").html() || ""; - - // Remove script tags - chapterText = chapterText?.replace( - /<\s*script[^>]*>[\s\S]*?<\/script>/gim, - "", - ); - return chapterText; - } - - async searchNovels( - searchTerm: string, - //pageNo: number | undefined = 1, - ): Promise { - const url = `${this.site}api/fulltext/global?query=${searchTerm}&take=10`; - - const result = await fetchApi(url); - const data = (await result.json()) as responseSearch[]; - - const novels: Plugin.NovelItem[] = []; - - data - ?.find((item) => item?.meta?.key === "ranobe") - ?.data?.forEach((novel) => - novels.push({ - name: novel?.names?.rus || novel?.names?.eng || novel?.name || "", - url: - "https://ranobehub.org/ranobe/" + - novel?.url?.match( - /https:\/\/ranobehub\.org\/ranobe\/(.*?)\?utm_source=search_name&utm_medium=search&utm_campaign=search_using/, - )?.[1], - cover: novel?.image?.replace("/small", "/medium"), - }), - ); - - return novels; - } - - fetchImage = fetchFile; - - filters = { - sort: { - label: "Сортировка", - value: "", - options: [ - { label: "по рейтингу", value: "computed_rating" }, - { label: "по дате обновления", value: "last_chapter_at" }, - { label: "по дате добавления", value: "created_at" }, - { label: "по названию", value: "name_rus" }, - { label: "по просмотрам", value: "views" }, - { label: "по количеству глав", value: "count_chapters" }, - { label: "по объему перевода", value: "count_of_symbols" }, - ], - type: FilterTypes.Picker, - }, - status: { - label: "Статус перевода", - value: "", - options: [ - { label: "Любой", value: "" }, - { label: "В процессе", value: "1" }, - { label: "Завершено", value: "2" }, - { label: "Заморожено", value: "3" }, - { label: "Неизвестно", value: "4" }, - ], - type: FilterTypes.Picker, - }, - country: { - label: "Тип", - value: [], - options: [ - { label: "Китай", value: "2" }, - { label: "Корея", value: "3" }, - { label: "США", value: "4" }, - { label: "Япония", value: "1" }, - ], - type: FilterTypes.CheckboxGroup, - }, - events: { - label: "События", - value: { include: [], exclude: [] }, - options: [ - { label: "[Награжденная работа]", value: "611" }, - { label: "18+", value: "338" }, - { label: "Авантюристы", value: "353" }, - { label: "Автоматоны", value: "538" }, - { label: "Агрессивные персонажи", value: "434" }, - { label: "Ад", value: "509" }, - { label: "Адаптация в радиопостановку", value: "522" }, - { label: "Академия", value: "25" }, - { label: "Актеры озвучки", value: "578" }, - { label: "Активный главный герой", value: "132" }, - { label: "Алхимия", value: "116" }, - { label: "Альтернативный мир", value: "28" }, - { label: "Амнезия/Потеря памяти", value: "247" }, - { label: "Анабиоз", value: "657" }, - { label: "Ангелы", value: "218" }, - { label: "Андрогинные персонажи", value: "217" }, - { label: "Андроиды", value: "82" }, - { label: "Анти-магия", value: "471" }, - { label: "Антигерой", value: "346" }, - { label: "Антикварный магазин", value: "572" }, - { label: "Антисоциальный главный герой", value: "562" }, - { label: "Антиутопия", value: "663" }, - { label: "Апатичный протагонист", value: "29" }, - { label: "Апокалипсис", value: "314" }, - { label: "Аранжированный брак", value: "285" }, - { label: "Армия", value: "598" }, - { label: "Артефакты", value: "117" }, - { label: "Артисты", value: "460" }, - { label: "Банды", value: "581" }, - { label: "БДСМ", value: "676" }, - { label: "Бедный главный герой", value: "309" }, - { label: "Безжалостный главный герой", value: "144" }, - { label: "Беззаботный главный герой", value: "355" }, - { label: "Безусловная любовь", value: "650" }, - { label: "Беременность", value: "131" }, - { label: "Бесполый главный герой", value: "222" }, - { label: "Бессмертные", value: "275" }, - { label: "Бесстрашный протагонист", value: "619" }, - { label: "Бесстыдный главный герой", value: "256" }, - { label: "Бесчестный главный герой", value: "699" }, - { label: "Библиотека", value: "342" }, - { label: "Бизнесмен ", value: "813" }, - { label: "Биочип", value: "120" }, - { label: "Бисексуальный главный герой", value: "822" }, - { label: "Близнецы", value: "148" }, - { label: "Боги", value: "211" }, - { label: "Богини", value: "356" }, - { label: "Боевая академия", value: "369" }, - { label: "Боевые духи", value: "347" }, - { label: "Боевые соревнования", value: "422" }, - { label: "Божественная защита", value: "336" }, - { label: "Божественные силы", value: "224" }, - { label: "Большая разница в возрасте между героем и его любовным интересом", value: "348" }, - { label: "Борьба за власть", value: "544" }, - { label: "Брак", value: "363" }, - { label: "Брак по расчету", value: "65" }, - { label: "Братский комплекс", value: "31" }, - { label: "Братство", value: "413" }, - { label: "Братья и сестры", value: "518" }, - { label: "Буддизм", value: "742" }, - { label: "Быстрая культивация", value: "273" }, - { label: "Быстрообучаемый", value: "221" }, - { label: "Валькирии", value: "667" }, - { label: "Вампиры", value: "266" }, - { label: "Ваншот", value: "679" }, - { label: "Ведьмы", value: "169" }, - { label: "Вежливый главный герой", value: "289" }, - { label: "Верные подчиненные", value: "225" }, - { label: "Взрослый главный герой", value: "183" }, - { label: "Видит то, чего не видят другие", value: "636" }, - { label: "Виртуальная реальность", value: "313" }, - { label: "Владелец магазина", value: "653" }, - { label: "Внезапная сила", value: "376" }, - { label: "Внезапное богатство", value: "802" }, - { label: "Внешний вид отличается от фактического возраста", value: "334" }, - { label: "Военные Летописи", value: "740" }, - { label: "Возвращение из другого мира", value: "673" }, - { label: "Войны", value: "58" }, - { label: "Вокалоид", value: "678" }, - { label: "Волшебники/Волшебницы", value: "477" }, - { label: "Волшебные звери", value: "201" }, - { label: "Воображаемый друг", value: "614" }, - { label: "Воры", value: "326" }, - { label: "Воскрешение", value: "78" }, - { label: "Враги становятся возлюбленными", value: "428" }, - { label: "Враги становятся союзниками", value: "502" }, - { label: "Врата в другой мир", value: "558" }, - { label: "Врачи", value: "286" }, - { label: "Временной парадокс", value: "163" }, - { label: "Всемогущий главный герой", value: "42" }, - { label: "Вторжение на землю", value: "77" }, - { label: "Второй шанс", value: "112" }, - { label: "Вуайеризм", value: "851" }, - { label: "Выживание", value: "290" }, - { label: "Высокомерные персонажи", value: "268" }, - { label: "Гадание", value: "540" }, - { label: "Гарем рабов", value: "828" }, - { label: "Геймеры", value: "302" }, - { label: "Генералы", value: "223" }, - { label: "Генетические модификации", value: "620" }, - { label: "Гениальный главный герой", value: "566" }, - { label: "Герои", value: "173" }, - { label: "Героиня — сорванец", value: "525" }, - { label: "Герой влюбляется первым", value: "64" }, - { label: "Гетерохромия", value: "510" }, - { label: "Гильдии", value: "323" }, - { label: "Гипнотизм", value: "768" }, - { label: "Главный герой — бог", value: "486" }, - { label: "Главный герой — гуманоид", value: "595" }, - { label: "Главный герой — женщина", value: "63" }, - { label: "Главный герой — мужчина", value: "39" }, - { label: "Главный герой — наполовину человек", value: "362" }, - { label: "Главный герой — отец", value: "859" }, - { label: "Главный герой — раб", value: "832" }, - { label: "Главный герой — ребенок", value: "415" }, - { label: "Главный герой — рубака", value: "400" }, - { label: "Главный герой — собиратель гарема", value: "439" }, - { label: "Главный герой влюбляется первым", value: "655" }, - { label: "Главный герой играет роль", value: "396" }, - { label: "Главный герой носит очки", value: "637" }, - { label: "Главный герой пацифист", value: "675" }, - { label: "Главный герой с несколькими телами", value: "628" }, - { label: "Главный герой силен с самого начала", value: "45" }, - { label: "Гладиаторы", value: "549" }, - { label: "Глуповатый главный герой", value: "295" }, - { label: "Гоблины", value: "529" }, - { label: "Големы", value: "569" }, - { label: "Гомункул", value: "850" }, - { label: "Горничные", value: "380" }, - { label: "Госпиталь", value: "1043" }, - { label: "Готовка", value: "193" }, - { label: "Гриндинг", value: "303" }, - { label: "Дао Компаньон", value: "384" }, - { label: "Даосизм", value: "792" }, - { label: "Дарк", value: "151" }, - { label: "Дварфы", value: "220" }, - { label: "Двойная личность", value: "601" }, - { label: "Двойник", value: "547" }, - { label: "Дворецкий", value: "623" }, - { label: "Дворяне", value: "41" }, - { label: "Дворянство/Аристократия", value: "354" }, - { label: "Девушки-монстры", value: "634" }, - { label: "Демоническая техника культивации", value: "706" }, - { label: "Демоны", value: "6" }, - { label: "Денежный долг", value: "841" }, - { label: "Депрессия", value: "494" }, - { label: "Детективы", value: "561" }, - { label: "Дискриминация", value: "34" }, - { label: "Добыча денег одно из основных стремлений главного героя", value: "307" }, - { label: "Долгая разлука", value: "200" }, - { label: "Домашние дела", value: "178" }, - { label: "Домогательство", value: "394" }, - { label: "Драконы", value: "195" }, - { label: "Драконьи всадники", value: "555" }, - { label: "Древние времена", value: "102" }, - { label: "Древний Китай", value: "284" }, - { label: "Дружба", value: "97" }, - { label: "Друзья детства", value: "170" }, - { label: "Друзья становятся врагами", value: "507" }, - { label: "Друиды", value: "427" }, - { label: "Дух лисы", value: "842" }, - { label: "Духи/Призраки", value: "46" }, - { label: "Духовный советник", value: "351" }, - { label: "Душевность", value: "587" }, - { label: "Души", value: "136" }, - { label: "Европейская атмосфера", value: "457" }, - { label: "Ёкаи", value: "516" }, - { label: "Есть аниме-адаптация", value: "26" }, - { label: "Есть видеоигра по мотивам", value: "491" }, - { label: "Есть манга-адаптация", value: "27" }, - { label: "Есть манхва-адаптация", value: "453" }, - { label: "Есть маньхуа-адаптация", value: "298" }, - { label: "Есть сериал-адаптация", value: "421" }, - { label: "Есть фильм по мотивам", value: "47" }, - { label: "Женища-наставник", value: "438" }, - { label: "Жертва изнасилования влюбляется в насильника", value: "414" }, - { label: "Жесткая, двуличная личность", value: "249" }, - { label: "Жестокие персонажи", value: "436" }, - { label: "Жестокое обращение с ребенком", value: "617" }, - { label: "Жестокость", value: "127" }, - { label: "Животноводство", value: "765" }, - { label: "Животные черты", value: "466" }, - { label: "Жизнь в квартире", value: "554" }, - { label: "Жрицы", value: "564" }, - { label: "Заботливый главный герой", value: "176" }, - { label: "Забывчивый главный герой", value: "579" }, - { label: "Заговоры", value: "177" }, - { label: "Закалка тела", value: "269" }, - { label: "Законники", value: "769" }, - { label: "Замкнутый главный герой", value: "533" }, - { label: "Запечатанная сила", value: "344" }, - { label: "Застенчивые персонажи", value: "443" }, - { label: "Звери", value: "119" }, - { label: "Звери-компаньоны", value: "192" }, - { label: "Злой протагонист", value: "125" }, - { label: "Злые боги", value: "437" }, - { label: "Злые организации", value: "503" }, - { label: "Злые религии", value: "725" }, - { label: "Знаменитости", value: "397" }, - { label: "Знаменитый главный герой", value: "469" }, - { label: "Знания современного мира", value: "185" }, - { label: "Зомби", value: "321" }, - { label: "Игра на выживание", value: "162" }, - { label: "Игривый протагонист", value: "700" }, - { label: "Игровая система рейтинга", value: "301" }, - { label: "Игровые элементы", value: "152" }, - { label: "Игрушки (18+)", value: "644" }, - { label: "Из грязи в князи", value: "330" }, - { label: "Из женщины в мужчину ", value: "688" }, - { label: "Из мужчины в женщину", value: "698" }, - { label: "Из полного в худого", value: "809" }, - { label: "Из слабого в сильного", value: "81" }, - { label: "Из страшно(го/й) в красиво(го/ю)", value: "810" }, - { label: "Извращенный главный герой", value: "349" }, - { label: "Изгои", value: "472" }, - { label: "Изменение расы", value: "627" }, - { label: "Изменения внешнего вида", value: "191" }, - { label: "Изменения личности", value: "99" }, - { label: "Изнасилование", value: "110" }, - { label: "Изображения жестокости", value: "260" }, - { label: "Империи", value: "749" }, - { label: "Инвалидность", value: "656" }, - { label: "Индустриализация", value: "180" }, - { label: "Инженер", value: "35" }, - { label: "Инцест", value: "463" }, - { label: "Искусственный интеллект", value: "118" }, - { label: "Исследования", value: "635" }, - { label: "Каннибализм", value: "377" }, - { label: "Карточные игры", value: "654" }, - { label: "Киберспорт", value: "658" }, - { label: "Кланы", value: "950" }, - { label: "Класс безработного [Игровой класс в игре]", value: "727" }, - { label: "Клоны", value: "365" }, - { label: "Клубы", value: "96" }, - { label: "Книги", value: "341" }, - { label: "Книги навыков", value: "312" }, - { label: "Книжный червь", value: "455" }, - { label: "Коварство", value: "111" }, - { label: "Коллеги", value: "683" }, - { label: "Колледж/Университет", value: "712" }, - { label: "Кома", value: "826" }, - { label: "Командная работа", value: "426" }, - { label: "Комедийный оттенок", value: "523" }, - { label: "Комплекс неполноценности", value: "489" }, - { label: "Комплекс семейных отношений", value: "104" }, - { label: "Конкуренция", value: "746" }, - { label: "Контракты", value: "483" }, - { label: "Контроль разума/сознания", value: "262" }, - { label: "Копейщик", value: "339" }, - { label: "Королевская битва", value: "1025" }, - { label: "Королевская власть", value: "53" }, - { label: "Королевства", value: "141" }, - { label: "Коррупция", value: "378" }, - { label: "Космические войны", value: "674" }, - { label: "Красивый герой", value: "107" }, - { label: "Крафт", value: "287" }, - { label: "Кризис личности", value: "599" }, - { label: "Кругосветное путешествие", value: "257" }, - { label: "Кудере", value: "440" }, - { label: "Кузены", value: "701" }, - { label: "Кузнец", value: "454" }, - { label: "Кукловоды", value: "431" }, - { label: "Куклы/марионетки", value: "573" }, - { label: "Культивация", value: "123" }, - { label: "Куннилингус", value: "632" }, - { label: "Легенды", value: "430" }, - { label: "Легкая жизнь", value: "604" }, - { label: "Ленивый главный герой", value: "570" }, - { label: "Лидерство", value: "424" }, - { label: "Лоли", value: "92" }, - { label: "Лотерея", value: "401" }, - { label: "Любовный интерес влюбляется первым", value: "647" }, - { label: "Любовный интерес главного героя носит очки", value: "576" }, - { label: "Любовный треугольник", value: "98" }, - { label: "Любовь детства", value: "500" }, - { label: "Любовь с первого взгляда", value: "730" }, - { label: "Магические надписи", value: "373" }, - { label: "Магические печати", value: "277" }, - { label: "Магические технологии", value: "357" }, - { label: "Магическое пространство/измерение, доступное не всем персонажам", value: "244" }, - { label: "Магия", value: "38" }, - { label: "Магия призыва", value: "333" }, - { label: "Мазохистские персонажи", value: "660" }, - { label: "Манипулятивные персонажи", value: "130" }, - { label: "Мания", value: "91" }, - { label: "Мастер на все руки", value: "390" }, - { label: "Мастурбация", value: "846" }, - { label: "Махо-сёдзё", value: "496" }, - { label: "Медицинские знания", value: "441" }, - { label: "Медленная романтическая линия", value: "113" }, - { label: "Медленное развитие на старте ", value: "670" }, - { label: "Межпространственные путешествия", value: "316" }, - { label: "Менеджмент", value: "182" }, - { label: "Мертвый главный герой", value: "794" }, - { label: "Месть", value: "88" }, - { label: "Метаморфы", value: "234" }, - { label: "Меч и магия", value: "55" }, - { label: "Мечник", value: "607" }, - { label: "Мечты", value: "733" }, - { label: "Милая история", value: "709" }, - { label: "Милое дитя", value: "596" }, - { label: "Милый главный герой", value: "697" }, - { label: "Мировое дерево", value: "385" }, - { label: "Мистический ореол вокруг семьи", value: "409" }, - { label: "Мифические звери", value: "418" }, - { label: "Мифология", value: "468" }, - { label: "Младшие братья", value: "843" }, - { label: "Младшие сестры", value: "465" }, - { label: "ММОРПГ (ЛитРПГ)", value: "306" }, - { label: "Множество перемещенных людей", value: "488" }, - { label: "Множество реальностей", value: "278" }, - { label: "Множество реинкарнированных людей", value: "227" }, - { label: "Модели", value: "649" }, - { label: "Молчаливый персонаж", value: "705" }, - { label: "Монстры", value: "69" }, - { label: "Мужская гей-пара", value: "685" }, - { label: "Мужчина-яндере", value: "155" }, - { label: "Музыка", value: "589" }, - { label: "Музыкальные группы", value: "588" }, - { label: "Мутации", value: "668" }, - { label: "Мутированные существа", value: "317" }, - { label: "Навык кражи", value: "626" }, - { label: "Навязчивая любовь", value: "85" }, - { label: "Наемники", value: "324" }, - { label: "Назойливый возлюбленный", value: "501" }, - { label: "Наивный главный герой", value: "66" }, - { label: "Наркотики", value: "661" }, - { label: "Нарциссический главный герой", value: "470" }, - { label: "Насилие сексуального характера", value: "824" }, - { label: "Наследование", value: "372" }, - { label: "Национализм", value: "318" }, - { label: "Не блещущий внешне главный герой", value: "328" }, - { label: "Не родные братья и сестры", value: "464" }, - { label: "Небеса", value: "508" }, - { label: "Небесное испытание", value: "274" }, - { label: "Негуманоидный главный герой", value: "622" }, - { label: "Недоверчивый главный герой", value: "485" }, - { label: "Недооцененный главный герой", value: "140" }, - { label: "Недоразумения", value: "202" }, - { label: "Неизлечимая болезнь", value: "75" }, - { label: "Некромант", value: "308" }, - { label: "Нелинейная история", value: "157" }, - { label: "Ненавистный главный герой", value: "487" }, - { label: "Ненадежный рассказчик", value: "165" }, - { label: "Нерезиденты", value: "821" }, - { label: "Нерешительный главный герой", value: "741" }, - { label: "Несерьезный главный герой", value: "294" }, - { label: "Несколько временных линий", value: "517" }, - { label: "Несколько главных героев", value: "475" }, - { label: "Несколько идентичностей", value: "615" }, - { label: "Несколько личностей", value: "474" }, - { label: "Нетораре", value: "721" }, - { label: "Нетори", value: "450" }, - { label: "Неудачливый главный герой", value: "567" }, - { label: "Ниндзя", value: "358" }, - { label: "Обещание из детства", value: "738" }, - { label: "Обманщик", value: "411" }, - { label: "Обмен телами", value: "94" }, - { label: "Обнаженка", value: "417" }, - { label: "Обольщение", value: "718" }, - { label: "Оборотни", value: "624" }, - { label: "Обратный гарем", value: "255" }, - { label: "Общество монстров", value: "226" }, - { label: "Обязательство", value: "548" }, - { label: "Огнестрельное оружие", value: "179" }, - { label: "Ограниченная продолжительность жизни", value: "73" }, - { label: "Одержимость", value: "447" }, - { label: "Одинокий главный герой", value: "304" }, - { label: "Одиночество", value: "199" }, - { label: "Одиночное проживание", value: "207" }, - { label: "Околосмертные переживания", value: "773" }, - { label: "Оммёдзи", value: "542" }, - { label: "Омоложение", value: "237" }, - { label: "Организованная преступность", value: "478" }, - { label: "Оргия", value: "825" }, - { label: "Орки", value: "228" }, - { label: "Освоение навыков", value: "235" }, - { label: "Основано на аниме", value: "553" }, - { label: "Основано на видео игре", value: "704" }, - { label: "Основано на визуальной новелле ", value: "861" }, - { label: "Основано на песне", value: "677" }, - { label: "Основано на фильме", value: "552" }, - { label: "Осторожный главный герой", value: "122" }, - { label: "Отаку", value: "512" }, - { label: "Открытый космос", value: "150" }, - { label: "Отношения в сети", value: "713" }, - { label: "Отношения между богом и человеком", value: "603" }, - { label: "Отношения между людьми и нелюдьми", value: "205" }, - { label: "Отношения на расстоянии", value: "852" }, - { label: "Отношения начальник-подчиненный", value: "590" }, - { label: "Отношения Сенпай-Коухай", value: "513" }, - { label: "Отношения ученика и учителя", value: "451" }, - { label: "Отношения учитель-ученик", value: "343" }, - { label: "Отношения хозяин-слуга", value: "206" }, - { label: "Отомэ игра", value: "723" }, - { label: "Отсутствие здравого смысла", value: "605" }, - { label: "Отсутствие родителей", value: "575" }, - { label: "Офисный роман", value: "686" }, - { label: "Официанты", value: "681" }, - { label: "Охотники", value: "288" }, - { label: "Очаровательный главный герой", value: "659" }, - { label: "Падшее дворянство", value: "618" }, - { label: "Падшие ангелы", value: "505" }, - { label: "Пайзури", value: "630" }, - { label: "Паразиты", value: "535" }, - { label: "Параллельные миры", value: "86" }, - { label: "Парк развлечений", value: "586" }, - { label: "Пародия", value: "319" }, - { label: "Певцы/Певицы", value: "734" }, - { label: "Первая любовь", value: "716" }, - { label: "Первоисточник новеллы — манга", value: "560" }, - { label: "Первый раз", value: "845" }, - { label: "Перемещение в другой мир, имея при себе современные достижения", value: "732" }, - { label: "Перемещение в игровой мир", value: "532" }, - { label: "Перемещение в иной мир", value: "754" }, - { label: "Перерождение в ином мире", value: "755" }, - { label: "Переселение души/Трансмиграция", value: "139" }, - { label: "Персонаж использует щит", value: "631" }, - { label: "Петля времени", value: "79" }, - { label: "Пираты", value: "817" }, - { label: "Писатели", value: "408" }, - { label: "Питомцы", value: "253" }, - { label: "Племенное общество", value: "291" }, - { label: "Повелитель демонов", value: "171" }, - { label: "Повествование от нескольких лиц/Несколько точек зрения", value: "156" }, - { label: "Подземелья", value: "219" }, - { label: "Пожелания", value: "166" }, - { label: "Познание Дао", value: "360" }, - { label: "Покинутое дитя", value: "534" }, - { label: "Полигамия", value: "296" }, - { label: "Политика", value: "43" }, - { label: "Полиция", value: "801" }, - { label: "Полулюди", value: "335" }, - { label: "Пользователь уникального оружия", value: "432" }, - { label: "Популярный любовный интерес", value: "241" }, - { label: "Постапокалиптика", value: "60" }, - { label: "Потерянные цивилизации", value: "751" }, - { label: "Похищения людей", value: "707" }, - { label: "Поэзия", value: "404" }, - { label: "Правонарушители", value: "694" }, - { label: "Прагматичный главный герой", value: "536" }, - { label: "Преданный любовный интерес", value: "106" }, - { label: "Предательство", value: "103" }, - { label: "Предвидение", value: "52" }, - { label: "Прекрасная героиня", value: "30" }, - { label: "Преступники", value: "728" }, - { label: "Преступность", value: "83" }, - { label: "Призванный герой", value: "147" }, - { label: "Призраки", value: "51" }, - { label: "Принуждение к отношениям", value: "602" }, - { label: "Принцессы", value: "933" }, - { label: "Притворная пара", value: "600" }, - { label: "Причудливые персонажи", value: "297" }, - { label: "Пришельцы/Инопланетяне", value: "76" }, - { label: "Программист", value: "666" }, - { label: "Проклятия", value: "48" }, - { label: "Промывание мозгов", value: "519" }, - { label: "Пропуск времени", value: "138" }, - { label: "Пророчества", value: "429" }, - { label: "Проститутки", value: "844" }, - { label: "Пространственное манипулирование", value: "375" }, - { label: "Прошлое играет большую роль", value: "215" }, - { label: "Прыжки между мирами", value: "737" }, - { label: "Психические силы", value: "265" }, - { label: "Психопаты", value: "158" }, - { label: "Путешествие во времени", value: "80" }, - { label: "Пытка", value: "168" }, - { label: "Рабы", value: "829" }, - { label: "Развод", value: "771" }, - { label: "Разумные предметы", value: "174" }, - { label: "Расизм", value: "320" }, - { label: "Рассказ", value: "61" }, - { label: "Расторжения помолвки", value: "243" }, - { label: "Расы зооморфов", value: "209" }, - { label: "Ревность", value: "717" }, - { label: "Редакторы", value: "684" }, - { label: "Реинкарнация", value: "281" }, - { label: "Реинкарнация в монстра", value: "204" }, - { label: "Реинкарнация в объект", value: "692" }, - { label: "Религии", value: "565" }, - { label: "Репортеры", value: "837" }, - { label: "Ресторан", value: "652" }, - { label: "Решительный главный герой", value: "124" }, - { label: "Робкий главный герой", value: "800" }, - { label: "Родитель одиночка", value: "687" }, - { label: "Родительский комплекс", value: "531" }, - { label: "Родословная", value: "121" }, - { label: "Романтический подсюжет ", value: "159" }, - { label: "Рост персонажа", value: "95" }, - { label: "Рыцари", value: "142" }, - { label: "Садистские персонажи", value: "642" }, - { label: "Самоотверженный главный герой", value: "748" }, - { label: "Самоубийства", value: "616" }, - { label: "Самурай", value: "646" }, - { label: "Сборник коротких историй", value: "456" }, - { label: "Связанные сюжетные линии", value: "473" }, - { label: "Святые", value: "761" }, - { label: "Священники", value: "325" }, - { label: "Сдержанный главный герой", value: "799" }, - { label: "Секретные организации", value: "331" }, - { label: "Секреты", value: "577" }, - { label: "Секс рабы", value: "830" }, - { label: "Семейный конфликт", value: "770" }, - { label: "Семь добродетелей", value: "233" }, - { label: "Семь смертных грехов", value: "134" }, - { label: "Семья", value: "251" }, - { label: "Сёнэн-ай подсюжет ", value: "664" }, - { label: "Серийные убийцы", value: "497" }, - { label: "Сестринский комплекс", value: "498" }, - { label: "Сила духа", value: "282" }, - { label: "Сила, требующая платы за пользование", value: "520" }, - { label: "Сильная пара", value: "109" }, - { label: "Сильный в сильнейшего", value: "359" }, - { label: "Сильный любовный интерес", value: "161" }, - { label: "Синдром восьмиклассника", value: "90" }, - { label: "Сироты", value: "214" }, - { label: "Система уровней", value: "198" }, - { label: "Системный администратор", value: "476" }, - { label: "Скрытие истинной личности", value: "371" }, - { label: "Скрытие истинных способностей", value: "252" }, - { label: "Скрытный главный герой", value: "133" }, - { label: "Скрытые способности", value: "128" }, - { label: "Скульпторы", value: "366" }, - { label: "Слабо выраженная романтическая линия", value: "213" }, - { label: "Слабый главный герой", value: "188" }, - { label: "Слепой главный герой", value: "864" }, - { label: "Слуги", value: "232" }, - { label: "Смерть", value: "49" }, - { label: "Смерть близких", value: "361" }, - { label: "Собственнические персонажи", value: "108" }, - { label: "Современность", value: "40" }, - { label: "Сожительство", value: "482" }, - { label: "Создание армии", value: "175" }, - { label: "Создание артефактов", value: "292" }, - { label: "Создание клана", value: "448" }, - { label: "Создание королевства", value: "181" }, - { label: "Создание навыков", value: "236" }, - { label: "Создание секты", value: "393" }, - { label: "Солдаты/Военные", value: "71" }, - { label: "Сон", value: "571" }, - { label: "Состоятельные персонажи", value: "67" }, - { label: "Социальная иерархия на основе силы", value: "137" }, - { label: "Социальные изгои", value: "480" }, - { label: "Спасение мира", value: "597" }, - { label: "Специальные способности", value: "54" }, - { label: "Спокойный главный герой", value: "32" }, - { label: "Справедливый главный герой", value: "702" }, - { label: "Средневековье", value: "184" }, - { label: "Ссорящаяся пара", value: "293" }, - { label: "Сталкеры", value: "689" }, - { label: "Старение", value: "190" }, - { label: "Стоические персонажи", value: "444" }, - { label: "Стокгольмский синдром", value: "643" }, - { label: "Стратег", value: "425" }, - { label: "Стратегические битвы", value: "160" }, - { label: "Стратегия", value: "1038" }, - { label: "Стрелки", value: "458" }, - { label: "Стрельба из лука", value: "383" }, - { label: "Студенческий совет", value: "490" }, - { label: "Судьба", value: "271" }, - { label: "Суккубы", value: "484" }, - { label: "Супер герои", value: "1039" }, - { label: "Суровая подготовка", value: "261" }, - { label: "Таинственная болезнь", value: "74" }, - { label: "Таинственное прошлое", value: "263" }, - { label: "Тайная личность", value: "310" }, - { label: "Тайные отношения", value: "812" }, - { label: "Танцоры", value: "840" }, - { label: "Телохранители", value: "452" }, - { label: "Тентакли", value: "693" }, - { label: "Террористы", value: "515" }, - { label: "Технологический разрыв", value: "621" }, - { label: "Тихие персонажи", value: "546" }, - { label: "Толстый главный герой", value: "299" }, - { label: "Торговцы", value: "416" }, - { label: "Травля/Буллинг", value: "89" }, - { label: "Травник", value: "708" }, - { label: "Трагическое прошлое", value: "164" }, - { label: "Трансплантация воспоминаний", value: "367" }, - { label: "Трап (Путаница с гендером персонажа)", value: "582" }, - { label: "Трудолюбивый главный герой", value: "37" }, - { label: "Тюрьма", value: "479" }, - { label: "Убийства", value: "84" }, - { label: "Убийцы", value: "248" }, - { label: "Убийцы драконов", value: "606" }, - { label: "Уверенный главный герой", value: "270" }, - { label: "Удачливый главный герой", value: "402" }, - { label: "Укротитель монстров", value: "337" }, - { label: "Умения из прошлой жизни", value: "280" }, - { label: "Умная пара", value: "493" }, - { label: "Умный главный герой", value: "33" }, - { label: "Уникальная техника Культивации", value: "254" }, - { label: "Уникальное оружие", value: "340" }, - { label: "Управление бизнесом", value: "315" }, - { label: "Управление временем", value: "167" }, - { label: "Управление кровью", value: "764" }, - { label: "Упрямый главный герой", value: "672" }, - { label: "Уродливый главный герой", value: "803" }, - { label: "Ускоренный рост", value: "300" }, - { label: "Усыновленные дети", value: "481" }, - { label: "Усыновленный главный герой", value: "412" }, - { label: "Уход за детьми", value: "398" }, - { label: "Учителя", value: "345" }, - { label: "Фамильяры", value: "541" }, - { label: "Фанатизм", value: "613" }, - { label: "Фантастические существа", value: "322" }, - { label: "Фанфикшн", value: "388" }, - { label: "Фармацевт", value: "715" }, - { label: "Фарминг", value: "379" }, - { label: "Феи", value: "210" }, - { label: "Фелляция", value: "584" }, - { label: "Фениксы", value: "374" }, - { label: "Фетиш груди", value: "499" }, - { label: "Философия", value: "87" }, - { label: "Фильмы", value: "403" }, - { label: "Флэшбэки", value: "528" }, - { label: "Фобии", value: "100" }, - { label: "Фольклор", value: "467" }, - { label: "Футанари", value: "568" }, - { label: "Футуристический сеттинг", value: "382" }, - { label: "Фэнтези мир", value: "126" }, - { label: "Хакеры", value: "399" }, - { label: "Харизматический герой", value: "391" }, - { label: "Хикикомори/Затворники", value: "462" }, - { label: "Хитроумный главный герой", value: "105" }, - { label: "Хозяин подземелий", value: "557" }, - { label: "Холодный главный герой", value: "259" }, - { label: "Хорошие отношения с семьей", value: "506" }, - { label: "Хранители могил", value: "68" }, - { label: "Целители", value: "389" }, - { label: "Цзянши", value: "735" }, - { label: "Цундэрэ", value: "445" }, - { label: "Чаты", value: "580" }, - { label: "Человеческое оружие", value: "521" }, - { label: "Честный главный герой", value: "240" }, - { label: "Читы", value: "238" }, - { label: "Шантаж", value: "526" }, - { label: "Шеф-повар", value: "239" }, - { label: "Шикигами", value: "543" }, - { label: "Школа только для девочек", value: "633" }, - { label: "Шота", value: "514" }, - { label: "Шоу-бизнес", value: "407" }, - { label: "Шпионы", value: "563" }, - { label: "Эволюция", value: "196" }, - { label: "Эгоистичный главный герой", value: "539" }, - { label: "Эйдетическая память", value: "392" }, - { label: "Экзорсизм", value: "504" }, - { label: "Экономика", value: "492" }, - { label: "Эксгибиционизм", value: "639" }, - { label: "Эксперименты с людьми", value: "129" }, - { label: "Элементальная магия", value: "395" }, - { label: "Эльфы", value: "172" }, - { label: "Эмоционально слабый главный герой", value: "816" }, - { label: "Эпизодический", value: "612" }, - { label: "Юный любовный интерес", value: "446" }, - { label: "Яды", value: "410" }, - { label: "Языкастые персонажи", value: "406" }, - { label: "Языковой барьер", value: "59" }, - { label: "Яндере", value: "208" }, - { label: "Японские силы самообороны", value: "559" }, - { label: "Ярко выраженная романтическая линия", value: "272" }, - { label: "Abusive Characters", value: "625" }, - { label: "Adapted to Visual Novel", value: "1046" }, - { label: "Adopted-lead", value: "886" }, - { label: "Adultery", value: "866" }, - { label: "Affair", value: "645" }, - { label: "Age-gap", value: "923" }, - { label: "Almost-human-lead", value: "975" }, - { label: "An*l", value: "780" }, - { label: "Androgynous-male-lead", value: "1002" }, - { label: "Anti-hero-lead", value: "976" }, - { label: "Apathetic-lead", value: "967" }, - { label: "Arms Dealers", value: "839" }, - { label: "Army-commander", value: "1003" }, - { label: "Artifact-refining", value: "924" }, - { label: "Autism", value: "855" }, - { label: "Awkward Protagonist", value: "669" }, - { label: "Awkward-lead", value: "892" }, - { label: "Bestiality", value: "796" }, - { label: "Big-breasts", value: "908" }, - { label: "Birth-of-a-nation", value: "1004" }, - { label: "Bisexual-lead", value: "1027" }, - { label: "Body-refining", value: "947" }, - { label: "Body-swap/s", value: "1036" }, - { label: "Boss-subordinate-relationship", value: "876" }, - { label: "Bride-kidnapping", value: "948" }, - { label: "Caring-lead", value: "879" }, - { label: "Cautious-lead", value: "1032" }, - { label: "Changed-man", value: "925" }, - { label: "Charismatic-lead", value: "1005" }, - { label: "Child-lead", value: "936" }, - { label: "Clever-lead", value: "880" }, - { label: "Clumsy Love Interests", value: "815" }, - { label: "Cold Love Interests", value: "651" }, - { label: "Coming of Age", value: "860" }, - { label: "Confident-lead", value: "894" }, - { label: "Confident-male-lead", value: "951" }, - { label: "Confinement", value: "767" }, - { label: "Conflicting Loyalties", value: "793" }, - { label: "Couple Growth", value: "690" }, - { label: "Court Official", value: "863" }, - { label: "Cowardly Protagonist", value: "703" }, - { label: "Cowardly-lead", value: "1000" }, - { label: "Cross-dressing", value: "250" }, - { label: "Cunnilingus", value: "867" }, - { label: "Cunning-lead", value: "952" }, - { label: "Cunning-male-lead", value: "953" }, - { label: "Curious Protagonist", value: "849" }, - { label: "Cute-lead", value: "887" }, - { label: "Dense-lead", value: "868" }, - { label: "Determined-lead", value: "972" }, - { label: "Developing-technology", value: "1006" }, - { label: "Devil/s", value: "1007" }, - { label: "Different Social Status", value: "758" }, - { label: "Disfigurement", value: "838" }, - { label: "Doting Love Interests", value: "665" }, - { label: "Doting Older Siblings", value: "609" }, - { label: "Doting Parents", value: "530" }, - { label: "Dungeon/s-exploring", value: "990" }, - { label: "Elderly Protagonist", value: "774" }, - { label: "Enlightenment", value: "729" }, - { label: "Eunuch", value: "1047" }, - { label: "Eye Powers", value: "386" }, - { label: "Family Business", value: "785" }, - { label: "Famous Parents", value: "790" }, - { label: "Famous-lead", value: "897" }, - { label: "Fanfic", value: "942" }, - { label: "Fated Lovers", value: "797" }, - { label: "Fellatio", value: "870" }, - { label: "Female-lead", value: "888" }, - { label: "First-time-intercourse", value: "871" }, - { label: "Fleet Battles", value: "836" }, - { label: "Forced Living Arrangements", value: "808" }, - { label: "Forced Marriage", value: "835" }, - { label: "Former Hero", value: "752" }, - { label: "Fujoshi", value: "1045" }, - { label: "Galge", value: "848" }, - { label: "Gambling", value: "1031" }, - { label: "Gamelit", value: "994" }, - { label: "Genderless-lead", value: "1008" }, - { label: "Glasses-wearing-lead", value: "995" }, - { label: "Guardian Relationship", value: "197" }, - { label: "H*ndjob", value: "629" }, - { label: "Half-human-lead", value: "956" }, - { label: "Hard-working-lead", value: "957" }, - { label: "Hard-working-male-lead", value: "958" }, - { label: "Hard-working-protagonist/s", value: "1030" }, - { label: "Harem-seeking-lead", value: "929" }, - { label: "Harem-subtext", value: "909" }, - { label: "Helpful Protagonist", value: "760" }, - { label: "Helpful-lead", value: "882" }, - { label: "Hidden-gem", value: "998" }, - { label: "High-fantasy", value: "991" }, - { label: "Human-becomes-demon/monster", value: "1009" }, - { label: "Human-nonhuman-relationship", value: "914" }, - { label: "Humanoid-lead", value: "915" }, - { label: "Imperial Harem", value: "805" }, - { label: "Incubus", value: "1049" }, - { label: "Insects", value: "831" }, - { label: "Interspatial-storage", value: "959" }, - { label: "Kind Love Interests", value: "757" }, - { label: "Large-number-of-skills", value: "1010" }, - { label: "Legendary-hero", value: "1011" }, - { label: "Litrpg", value: "977" }, - { label: "Love Rivals", value: "788" }, - { label: "Lovers Reunited", value: "853" }, - { label: "Low-fantasy", value: "1037" }, - { label: "Master-disciple-relationship", value: "899" }, - { label: "Matriarchy", value: "807" }, - { label: "Mature-lead", value: "900" }, - { label: "Mind Break", value: "862" }, - { label: "Mismatched Couple", value: "695" }, - { label: "Mob Protagonist", value: "648" }, - { label: "Mob-lead", value: "902" }, - { label: "Modern", value: "988" }, - { label: "Monster-pov", value: "1012" }, - { label: "Mpreg", value: "856" }, - { label: "Multiple-povs", value: "1013" }, - { label: "Mystery Solving", value: "710" }, - { label: "Naive-lead", value: "1014" }, - { label: "Near-death-experience", value: "996" }, - { label: "Neet", value: "495" }, - { label: "Nightmares", value: "833" }, - { label: "Nobility", value: "1015" }, - { label: "Non-human-lead", value: "978" }, - { label: "Non-humanoid-lead", value: "1016" }, - { label: "Not-so-secret-identity", value: "979" }, - { label: "Omegaverse", value: "857" }, - { label: "Online-game", value: "910" }, - { label: "Online-gaming", value: "911" }, - { label: "Outdoor Interc**rse", value: "847" }, - { label: "Outdoor-intercourse", value: "903" }, - { label: "Overprotective Siblings", value: "610" }, - { label: "Part-Time Job", value: "865" }, - { label: "Past Trauma", value: "726" }, - { label: "Past-memories", value: "1017" }, - { label: "Persistent Love Interests", value: "714" }, - { label: "Perverted-lead", value: "932" }, - { label: "Photography", value: "798" }, - { label: "Pill Based Cultivation", value: "279" }, - { label: "Pill Concocting", value: "245" }, - { label: "Pilots", value: "820" }, - { label: "Playboys", value: "795" }, - { label: "Playful-lead", value: "989" }, - { label: "Polyandry", value: "811" }, - { label: "Portal-fantasy-/-isekai", value: "992" }, - { label: "Pragmatic-lead", value: "884" }, - { label: "Profanity", value: "980" }, - { label: "Protagonist-loyal-to-love-interest", value: "905" }, - { label: "R-15 (Японское возрастное ограничение)", value: "230" }, - { label: "Rebellion", value: "834" }, - { label: "Reincarnated in a Game World", value: "804" }, - { label: "Reluctant Protagonist", value: "696" }, - { label: "Reverse R*pe", value: "776" }, - { label: "Reversible Couple", value: "854" }, - { label: "Ruling-class", value: "943" }, - { label: "S*x Friends", value: "750" }, - { label: "S*xual Cultivation Technique", value: "823" }, - { label: "Salaryman", value: "1035" }, - { label: "Satire", value: "981" }, - { label: "Schemes And Conspiracies", value: "745" }, - { label: "Scientist/s", value: "997" }, - { label: "Scientists", value: "1029" }, - { label: "Secret Crush", value: "827" }, - { label: "Seme Protagonist", value: "1034" }, - { label: "Seme-lead", value: "1028" }, - { label: "Sentimental Protagonist", value: "1048" }, - { label: "Seven-heavenly-virtues", value: "1018" }, - { label: "Sexual Cultivation Technique", value: "350" }, - { label: "Sexual-content", value: "982" }, - { label: "Sexuality", value: "983" }, - { label: "Sharing A Body", value: "814" }, - { label: "Shotacon", value: "858" }, - { label: "Shoujo-Ai Subplot", value: "777" }, - { label: "Sibling Rivalry", value: "766" }, - { label: "Sibling's Care", value: "1042" }, - { label: "Sickly Characters", value: "787" }, - { label: "Skills", value: "1019" }, - { label: "Slave-harem", value: "1001" }, - { label: "Slime", value: "1020" }, - { label: "Smart-male-lead", value: "963" }, - { label: "Spirit Users", value: "739" }, - { label: "Straight Seme", value: "819" }, - { label: "Straight Uke", value: "818" }, - { label: "Strength-based-social-hierarchy", value: "974" }, - { label: "Strong-lead", value: "944" }, - { label: "Strong-male-lead", value: "964" }, - { label: "Student-teacher-relationship", value: "1026" }, - { label: "Suspense", value: "987" }, - { label: "Sword-and-sorcery", value: "875" }, - { label: "Threesome", value: "574" }, - { label: "Transformation Ability", value: "736" }, - { label: "Traumatising-content", value: "1040" }, - { label: "Underestimated-lead", value: "919" }, - { label: "Underestimated-male-lead", value: "966" }, - { label: "Unique-abilities", value: "1021" }, - { label: "Unique-skill", value: "1022" }, - { label: "Unique-skills", value: "1023" }, - { label: "Unlimited Flow", value: "1044" }, - { label: "Unrequited Love", value: "806" }, - { label: "Urban-fantasy", value: "1041" }, - { label: "Villainess Noble Girls", value: "719" }, - { label: "Weak-lead", value: "885" }, - { label: "Web-novel", value: "1024" }, - ], - type: FilterTypes.ExcludableCheckboxGroup, - }, - tags: { - label: "Жанры", - value: { include: [], exclude: [] }, - options: [ - { label: "Боевые искусства", value: "22" }, - { label: "Гарем", value: "114" }, - { label: "Гендер бендер", value: "246" }, - { label: "Дзёсэй", value: "216" }, - { label: "Для взрослых", value: "115" }, - { label: "Для взрослых", value: "258" }, - { label: "Драма", value: "7" }, - { label: "Исторический", value: "101" }, - { label: "Комедия", value: "17" }, - { label: "Лоликон", value: "638" }, - { label: "Магический реализм", value: "922" }, - { label: "Меха", value: "24" }, - { label: "Милитари", value: "12" }, - { label: "Мистика", value: "2" }, - { label: "Научная фантастика", value: "13" }, - { label: "Непристойность", value: "747" }, - { label: "Повседневность", value: "93" }, - { label: "Приключение", value: "11" }, - { label: "Психология", value: "18" }, - { label: "Романтика", value: "9" }, - { label: "Сверхъестественное", value: "20" }, - { label: "Сёдзё", value: "15" }, - { label: "Сёдзё-ай", value: "23" }, - { label: "Сёнэн", value: "189" }, - { label: "Сёнэн-ай", value: "680" }, - { label: "Спорт", value: "420" }, - { label: "Сэйнэн", value: "5" }, - { label: "Сюаньхуа", value: "242" }, - { label: "Сянься", value: "364" }, - { label: "Трагедия", value: "19" }, - { label: "Триллер", value: "3" }, - { label: "Ужасы", value: "1" }, - { label: "Уся", value: "720" }, - { label: "Фэнтези", value: "8" }, - { label: "Школьная жизнь", value: "21" }, - { label: "Экшн", value: "14" }, - { label: "Эччи", value: "327" }, - { label: "Юри", value: "691" }, - { label: "Яой", value: "682" }, - { label: "Eastern fantasy", value: "907" }, - { label: "Isekai", value: "999" }, - { label: "Video games", value: "993" }, - ], - type: FilterTypes.ExcludableCheckboxGroup, - }, - } satisfies Filters; -} - -export default new RNBH(); - -interface responseNovels { - id: number; - names: Names; - rating: number; - synopsis: string; - url: string; - poster: Poster; - created_at: number; - status: string; - user?: User; - counts: Counts; -} -interface Names { - eng: string; - rus?: string; - original?: string; -} -interface Poster { - medium: string; - small: string; - color: string; -} -interface User { - status?: null; - liked: boolean; -} -interface Counts { - volumes: string; - chapters: string; -} - -interface responseNovel { - id: number; - names: Names; - rating: number; - year: number; - synopsis: string; - url: string; - posters: Posters; - isSpecial: boolean; - liked: boolean; - authors?: AuthorsEntity[] | null; - translators?: TranslatorsEntity[] | null; - description: string; - status: Status; - start_reading_url: string; - html: string; - tags: Tags; -} -interface Posters { - big: string; - medium: string; - small: string; - tiny: string; - color: string; -} -interface AuthorsEntity { - name_eng: string; - pivot: Pivot; -} -interface Pivot { - ranobe_id: number; - author_id?: number; - translator_id?: number; -} -interface TranslatorsEntity { - name: string; - pivot: Pivot; -} -interface Status { - id: number; - title: string; -} -interface Tags { - events?: GenresOrEntity[] | null; - genres?: GenresOrEntity[] | null; -} -interface GenresOrEntity { - id: number; - names: Names; - url: string; - title: string; - description?: string | null; -} - -interface responseChapters { - marked_chapters_ids?: null[] | null; - volumes?: VolumesEntity[] | null; -} -interface VolumesEntity { - id: number; - num: number; - name: string; - status: Status; - chapters?: ChaptersEntity[] | null; -} -interface Status { - id: number; - name: string; -} -interface ChaptersEntity { - id: number; - name: string; - num: number; - url: string; - is_new: boolean; - has_images: boolean; - changed_at: string; - comments_count: string; -} - -interface responseSearch { - meta: Meta; - collections?: DataEntity[] | null; - data?: DataEntity[] | null; -} -interface Meta { - key: string; - title: string; -} -interface DataEntity { - id: number; - names?: Names | null; - description?: string | null; - url: string; - image?: string | null; - name?: string | null; - level?: number | null; - evolution_scheme?: null; - roles?: null[] | null; - avatar?: Avatar | null; - has_plus?: boolean | null; -} -interface Avatar { - big: string; - color: string; - thumb: string; - is_default: boolean; -} +import { Plugin } from "@typings/plugin"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { NovelStatus } from "@libs/novelStatus"; +import { load as parseHTML } from "cheerio"; +import dayjs from "dayjs"; + +class RNBH implements Plugin.PluginBase { + id = "RNBH.org"; + name = "RanobeHub"; + version = "1.0.0"; + site = "https://ranobehub.org/"; + icon = "src/ru/ranobehub/icon.png"; + userAgent = ""; + + async popularNovels( + pageNo: number, + { + showLatestNovels, + filters, + }: Plugin.PopularNovelsOptions, + ): Promise { + let url = this.site + `api/search?page=${pageNo}&sort=`; + url += showLatestNovels + ? "last_chapter_at" + : filters?.sort || "computed_rating"; + url += "&status=" + (filters?.status?.value ? filters?.status?.value : "0"); + + if (filters) { + if (filters.country?.value?.length) { + url += "&country=" + filters.country.value.join(","); + } + if ( + filters.tags?.value?.include?.length || + filters.events?.value?.include?.length + ) { + const tagsPositive = [ + filters.tags.value?.include, + filters.events.value?.include, + ] + .flat() + .filter((t) => t); + url += "&tags:positive=" + tagsPositive.join(","); + } + if ( + filters?.tags?.value?.exclude?.length || + filters?.events?.value?.exclude?.length + ) { + const tagsNegative = [ + filters.tags.value?.exclude, + filters.events.value?.exclude, + ] + .flat() + .filter((t) => t); + url += "&tags:negative=" + tagsNegative.join(","); + } + } + + url += "&take=40"; + const result = await fetchApi(url); + const body = (await result.json()) as { resource: responseNovels[] }; + + const novels: Plugin.NovelItem[] = []; + body.resource.forEach((novel) => + novels.push({ + name: novel.names?.rus || novel.names.eng, + cover: novel.poster.medium, + url: novel.url, + }), + ); + + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const novelId = novelUrl + .substring("https://ranobehub.org/ranobe/".length) + .split("-")[0]; + const result = await fetchApi(`${this.site}api/ranobe/${novelId}`); + const json = (await result.json()) as { data: responseNovel }; + + const novel: Plugin.SourceNovel = { + url: json.data.url, + name: json.data.names?.rus || json.data.names.eng, + cover: json.data.posters.medium, + summary: json.data.description, + author: json.data?.authors?.[0]?.name_eng || "", + status: json.data.status.title.includes("процессе") + ? NovelStatus.Ongoing + : NovelStatus.Completed, + }; + + const tags = [json.data.tags.events, json.data.tags.genres] + .flat() + .map((tags) => tags?.names?.rus || tags?.names?.eng || tags?.title) + .filter((tags) => tags); + + if (tags.length) { + novel.genres = tags.join(", "); + } + + const chapters: Plugin.ChapterItem[] = []; + const fetchChaptersUrl = `${this.site}api/ranobe/${novelId}/contents`; + + const chaptersRaw = await fetchApi(fetchChaptersUrl); + const chaptersJSON = (await chaptersRaw.json()) as { + volumes: VolumesEntity[]; + }; + + chaptersJSON.volumes.forEach( + (volume) => + volume.chapters?.forEach((chapter) => + chapters.push({ + name: chapter.name, + url: chapter.url, + releaseTime: dayjs(parseInt(chapter.changed_at, 10) * 1000).format( + "LLL", + ), + }), + ), + ); + + novel.chapters = chapters; + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const result = await fetchApi(chapterUrl); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + loadedCheerio(".chapter-hoticons").remove(); + const baseUrl = this.site; + loadedCheerio("div.text:nth-child(1) img").each(function () { + if (!loadedCheerio(this).attr("src")?.startsWith("http")) { + const dataMediaId = loadedCheerio(this).attr("data-media-id"); + loadedCheerio(this).attr("src", baseUrl + "api/media/" + dataMediaId); + } + }); + + let chapterText: string = + loadedCheerio("div.text:nth-child(1)").html() || ""; + + // Remove script tags + chapterText = chapterText?.replace( + /<\s*script[^>]*>[\s\S]*?<\/script>/gim, + "", + ); + return chapterText; + } + + async searchNovels( + searchTerm: string, + //pageNo: number | undefined = 1, + ): Promise { + const url = `${this.site}api/fulltext/global?query=${searchTerm}&take=10`; + + const result = await fetchApi(url); + const data = (await result.json()) as responseSearch[]; + + const novels: Plugin.NovelItem[] = []; + + data + ?.find((item) => item?.meta?.key === "ranobe") + ?.data?.forEach((novel) => + novels.push({ + name: novel?.names?.rus || novel?.names?.eng || novel?.name || "", + url: + "https://ranobehub.org/ranobe/" + + novel?.url?.match( + /https:\/\/ranobehub\.org\/ranobe\/(.*?)\?utm_source=search_name&utm_medium=search&utm_campaign=search_using/, + )?.[1], + cover: novel?.image?.replace("/small", "/medium"), + }), + ); + + return novels; + } + + fetchImage = fetchFile; + + filters = { + sort: { + label: "Сортировка", + value: "", + options: [ + { label: "по рейтингу", value: "computed_rating" }, + { label: "по дате обновления", value: "last_chapter_at" }, + { label: "по дате добавления", value: "created_at" }, + { label: "по названию", value: "name_rus" }, + { label: "по просмотрам", value: "views" }, + { label: "по количеству глав", value: "count_chapters" }, + { label: "по объему перевода", value: "count_of_symbols" }, + ], + type: FilterTypes.Picker, + }, + status: { + label: "Статус перевода", + value: "", + options: [ + { label: "Любой", value: "" }, + { label: "В процессе", value: "1" }, + { label: "Завершено", value: "2" }, + { label: "Заморожено", value: "3" }, + { label: "Неизвестно", value: "4" }, + ], + type: FilterTypes.Picker, + }, + country: { + label: "Тип", + value: [], + options: [ + { label: "Китай", value: "2" }, + { label: "Корея", value: "3" }, + { label: "США", value: "4" }, + { label: "Япония", value: "1" }, + ], + type: FilterTypes.CheckboxGroup, + }, + events: { + label: "События", + value: { include: [], exclude: [] }, + options: [ + { label: "[Награжденная работа]", value: "611" }, + { label: "18+", value: "338" }, + { label: "Авантюристы", value: "353" }, + { label: "Автоматоны", value: "538" }, + { label: "Агрессивные персонажи", value: "434" }, + { label: "Ад", value: "509" }, + { label: "Адаптация в радиопостановку", value: "522" }, + { label: "Академия", value: "25" }, + { label: "Актеры озвучки", value: "578" }, + { label: "Активный главный герой", value: "132" }, + { label: "Алхимия", value: "116" }, + { label: "Альтернативный мир", value: "28" }, + { label: "Амнезия/Потеря памяти", value: "247" }, + { label: "Анабиоз", value: "657" }, + { label: "Ангелы", value: "218" }, + { label: "Андрогинные персонажи", value: "217" }, + { label: "Андроиды", value: "82" }, + { label: "Анти-магия", value: "471" }, + { label: "Антигерой", value: "346" }, + { label: "Антикварный магазин", value: "572" }, + { label: "Антисоциальный главный герой", value: "562" }, + { label: "Антиутопия", value: "663" }, + { label: "Апатичный протагонист", value: "29" }, + { label: "Апокалипсис", value: "314" }, + { label: "Аранжированный брак", value: "285" }, + { label: "Армия", value: "598" }, + { label: "Артефакты", value: "117" }, + { label: "Артисты", value: "460" }, + { label: "Банды", value: "581" }, + { label: "БДСМ", value: "676" }, + { label: "Бедный главный герой", value: "309" }, + { label: "Безжалостный главный герой", value: "144" }, + { label: "Беззаботный главный герой", value: "355" }, + { label: "Безусловная любовь", value: "650" }, + { label: "Беременность", value: "131" }, + { label: "Бесполый главный герой", value: "222" }, + { label: "Бессмертные", value: "275" }, + { label: "Бесстрашный протагонист", value: "619" }, + { label: "Бесстыдный главный герой", value: "256" }, + { label: "Бесчестный главный герой", value: "699" }, + { label: "Библиотека", value: "342" }, + { label: "Бизнесмен ", value: "813" }, + { label: "Биочип", value: "120" }, + { label: "Бисексуальный главный герой", value: "822" }, + { label: "Близнецы", value: "148" }, + { label: "Боги", value: "211" }, + { label: "Богини", value: "356" }, + { label: "Боевая академия", value: "369" }, + { label: "Боевые духи", value: "347" }, + { label: "Боевые соревнования", value: "422" }, + { label: "Божественная защита", value: "336" }, + { label: "Божественные силы", value: "224" }, + { label: "Большая разница в возрасте между героем и его любовным интересом", value: "348" }, + { label: "Борьба за власть", value: "544" }, + { label: "Брак", value: "363" }, + { label: "Брак по расчету", value: "65" }, + { label: "Братский комплекс", value: "31" }, + { label: "Братство", value: "413" }, + { label: "Братья и сестры", value: "518" }, + { label: "Буддизм", value: "742" }, + { label: "Быстрая культивация", value: "273" }, + { label: "Быстрообучаемый", value: "221" }, + { label: "Валькирии", value: "667" }, + { label: "Вампиры", value: "266" }, + { label: "Ваншот", value: "679" }, + { label: "Ведьмы", value: "169" }, + { label: "Вежливый главный герой", value: "289" }, + { label: "Верные подчиненные", value: "225" }, + { label: "Взрослый главный герой", value: "183" }, + { label: "Видит то, чего не видят другие", value: "636" }, + { label: "Виртуальная реальность", value: "313" }, + { label: "Владелец магазина", value: "653" }, + { label: "Внезапная сила", value: "376" }, + { label: "Внезапное богатство", value: "802" }, + { label: "Внешний вид отличается от фактического возраста", value: "334" }, + { label: "Военные Летописи", value: "740" }, + { label: "Возвращение из другого мира", value: "673" }, + { label: "Войны", value: "58" }, + { label: "Вокалоид", value: "678" }, + { label: "Волшебники/Волшебницы", value: "477" }, + { label: "Волшебные звери", value: "201" }, + { label: "Воображаемый друг", value: "614" }, + { label: "Воры", value: "326" }, + { label: "Воскрешение", value: "78" }, + { label: "Враги становятся возлюбленными", value: "428" }, + { label: "Враги становятся союзниками", value: "502" }, + { label: "Врата в другой мир", value: "558" }, + { label: "Врачи", value: "286" }, + { label: "Временной парадокс", value: "163" }, + { label: "Всемогущий главный герой", value: "42" }, + { label: "Вторжение на землю", value: "77" }, + { label: "Второй шанс", value: "112" }, + { label: "Вуайеризм", value: "851" }, + { label: "Выживание", value: "290" }, + { label: "Высокомерные персонажи", value: "268" }, + { label: "Гадание", value: "540" }, + { label: "Гарем рабов", value: "828" }, + { label: "Геймеры", value: "302" }, + { label: "Генералы", value: "223" }, + { label: "Генетические модификации", value: "620" }, + { label: "Гениальный главный герой", value: "566" }, + { label: "Герои", value: "173" }, + { label: "Героиня — сорванец", value: "525" }, + { label: "Герой влюбляется первым", value: "64" }, + { label: "Гетерохромия", value: "510" }, + { label: "Гильдии", value: "323" }, + { label: "Гипнотизм", value: "768" }, + { label: "Главный герой — бог", value: "486" }, + { label: "Главный герой — гуманоид", value: "595" }, + { label: "Главный герой — женщина", value: "63" }, + { label: "Главный герой — мужчина", value: "39" }, + { label: "Главный герой — наполовину человек", value: "362" }, + { label: "Главный герой — отец", value: "859" }, + { label: "Главный герой — раб", value: "832" }, + { label: "Главный герой — ребенок", value: "415" }, + { label: "Главный герой — рубака", value: "400" }, + { label: "Главный герой — собиратель гарема", value: "439" }, + { label: "Главный герой влюбляется первым", value: "655" }, + { label: "Главный герой играет роль", value: "396" }, + { label: "Главный герой носит очки", value: "637" }, + { label: "Главный герой пацифист", value: "675" }, + { label: "Главный герой с несколькими телами", value: "628" }, + { label: "Главный герой силен с самого начала", value: "45" }, + { label: "Гладиаторы", value: "549" }, + { label: "Глуповатый главный герой", value: "295" }, + { label: "Гоблины", value: "529" }, + { label: "Големы", value: "569" }, + { label: "Гомункул", value: "850" }, + { label: "Горничные", value: "380" }, + { label: "Госпиталь", value: "1043" }, + { label: "Готовка", value: "193" }, + { label: "Гриндинг", value: "303" }, + { label: "Дао Компаньон", value: "384" }, + { label: "Даосизм", value: "792" }, + { label: "Дарк", value: "151" }, + { label: "Дварфы", value: "220" }, + { label: "Двойная личность", value: "601" }, + { label: "Двойник", value: "547" }, + { label: "Дворецкий", value: "623" }, + { label: "Дворяне", value: "41" }, + { label: "Дворянство/Аристократия", value: "354" }, + { label: "Девушки-монстры", value: "634" }, + { label: "Демоническая техника культивации", value: "706" }, + { label: "Демоны", value: "6" }, + { label: "Денежный долг", value: "841" }, + { label: "Депрессия", value: "494" }, + { label: "Детективы", value: "561" }, + { label: "Дискриминация", value: "34" }, + { label: "Добыча денег одно из основных стремлений главного героя", value: "307" }, + { label: "Долгая разлука", value: "200" }, + { label: "Домашние дела", value: "178" }, + { label: "Домогательство", value: "394" }, + { label: "Драконы", value: "195" }, + { label: "Драконьи всадники", value: "555" }, + { label: "Древние времена", value: "102" }, + { label: "Древний Китай", value: "284" }, + { label: "Дружба", value: "97" }, + { label: "Друзья детства", value: "170" }, + { label: "Друзья становятся врагами", value: "507" }, + { label: "Друиды", value: "427" }, + { label: "Дух лисы", value: "842" }, + { label: "Духи/Призраки", value: "46" }, + { label: "Духовный советник", value: "351" }, + { label: "Душевность", value: "587" }, + { label: "Души", value: "136" }, + { label: "Европейская атмосфера", value: "457" }, + { label: "Ёкаи", value: "516" }, + { label: "Есть аниме-адаптация", value: "26" }, + { label: "Есть видеоигра по мотивам", value: "491" }, + { label: "Есть манга-адаптация", value: "27" }, + { label: "Есть манхва-адаптация", value: "453" }, + { label: "Есть маньхуа-адаптация", value: "298" }, + { label: "Есть сериал-адаптация", value: "421" }, + { label: "Есть фильм по мотивам", value: "47" }, + { label: "Женища-наставник", value: "438" }, + { label: "Жертва изнасилования влюбляется в насильника", value: "414" }, + { label: "Жесткая, двуличная личность", value: "249" }, + { label: "Жестокие персонажи", value: "436" }, + { label: "Жестокое обращение с ребенком", value: "617" }, + { label: "Жестокость", value: "127" }, + { label: "Животноводство", value: "765" }, + { label: "Животные черты", value: "466" }, + { label: "Жизнь в квартире", value: "554" }, + { label: "Жрицы", value: "564" }, + { label: "Заботливый главный герой", value: "176" }, + { label: "Забывчивый главный герой", value: "579" }, + { label: "Заговоры", value: "177" }, + { label: "Закалка тела", value: "269" }, + { label: "Законники", value: "769" }, + { label: "Замкнутый главный герой", value: "533" }, + { label: "Запечатанная сила", value: "344" }, + { label: "Застенчивые персонажи", value: "443" }, + { label: "Звери", value: "119" }, + { label: "Звери-компаньоны", value: "192" }, + { label: "Злой протагонист", value: "125" }, + { label: "Злые боги", value: "437" }, + { label: "Злые организации", value: "503" }, + { label: "Злые религии", value: "725" }, + { label: "Знаменитости", value: "397" }, + { label: "Знаменитый главный герой", value: "469" }, + { label: "Знания современного мира", value: "185" }, + { label: "Зомби", value: "321" }, + { label: "Игра на выживание", value: "162" }, + { label: "Игривый протагонист", value: "700" }, + { label: "Игровая система рейтинга", value: "301" }, + { label: "Игровые элементы", value: "152" }, + { label: "Игрушки (18+)", value: "644" }, + { label: "Из грязи в князи", value: "330" }, + { label: "Из женщины в мужчину ", value: "688" }, + { label: "Из мужчины в женщину", value: "698" }, + { label: "Из полного в худого", value: "809" }, + { label: "Из слабого в сильного", value: "81" }, + { label: "Из страшно(го/й) в красиво(го/ю)", value: "810" }, + { label: "Извращенный главный герой", value: "349" }, + { label: "Изгои", value: "472" }, + { label: "Изменение расы", value: "627" }, + { label: "Изменения внешнего вида", value: "191" }, + { label: "Изменения личности", value: "99" }, + { label: "Изнасилование", value: "110" }, + { label: "Изображения жестокости", value: "260" }, + { label: "Империи", value: "749" }, + { label: "Инвалидность", value: "656" }, + { label: "Индустриализация", value: "180" }, + { label: "Инженер", value: "35" }, + { label: "Инцест", value: "463" }, + { label: "Искусственный интеллект", value: "118" }, + { label: "Исследования", value: "635" }, + { label: "Каннибализм", value: "377" }, + { label: "Карточные игры", value: "654" }, + { label: "Киберспорт", value: "658" }, + { label: "Кланы", value: "950" }, + { label: "Класс безработного [Игровой класс в игре]", value: "727" }, + { label: "Клоны", value: "365" }, + { label: "Клубы", value: "96" }, + { label: "Книги", value: "341" }, + { label: "Книги навыков", value: "312" }, + { label: "Книжный червь", value: "455" }, + { label: "Коварство", value: "111" }, + { label: "Коллеги", value: "683" }, + { label: "Колледж/Университет", value: "712" }, + { label: "Кома", value: "826" }, + { label: "Командная работа", value: "426" }, + { label: "Комедийный оттенок", value: "523" }, + { label: "Комплекс неполноценности", value: "489" }, + { label: "Комплекс семейных отношений", value: "104" }, + { label: "Конкуренция", value: "746" }, + { label: "Контракты", value: "483" }, + { label: "Контроль разума/сознания", value: "262" }, + { label: "Копейщик", value: "339" }, + { label: "Королевская битва", value: "1025" }, + { label: "Королевская власть", value: "53" }, + { label: "Королевства", value: "141" }, + { label: "Коррупция", value: "378" }, + { label: "Космические войны", value: "674" }, + { label: "Красивый герой", value: "107" }, + { label: "Крафт", value: "287" }, + { label: "Кризис личности", value: "599" }, + { label: "Кругосветное путешествие", value: "257" }, + { label: "Кудере", value: "440" }, + { label: "Кузены", value: "701" }, + { label: "Кузнец", value: "454" }, + { label: "Кукловоды", value: "431" }, + { label: "Куклы/марионетки", value: "573" }, + { label: "Культивация", value: "123" }, + { label: "Куннилингус", value: "632" }, + { label: "Легенды", value: "430" }, + { label: "Легкая жизнь", value: "604" }, + { label: "Ленивый главный герой", value: "570" }, + { label: "Лидерство", value: "424" }, + { label: "Лоли", value: "92" }, + { label: "Лотерея", value: "401" }, + { label: "Любовный интерес влюбляется первым", value: "647" }, + { label: "Любовный интерес главного героя носит очки", value: "576" }, + { label: "Любовный треугольник", value: "98" }, + { label: "Любовь детства", value: "500" }, + { label: "Любовь с первого взгляда", value: "730" }, + { label: "Магические надписи", value: "373" }, + { label: "Магические печати", value: "277" }, + { label: "Магические технологии", value: "357" }, + { label: "Магическое пространство/измерение, доступное не всем персонажам", value: "244" }, + { label: "Магия", value: "38" }, + { label: "Магия призыва", value: "333" }, + { label: "Мазохистские персонажи", value: "660" }, + { label: "Манипулятивные персонажи", value: "130" }, + { label: "Мания", value: "91" }, + { label: "Мастер на все руки", value: "390" }, + { label: "Мастурбация", value: "846" }, + { label: "Махо-сёдзё", value: "496" }, + { label: "Медицинские знания", value: "441" }, + { label: "Медленная романтическая линия", value: "113" }, + { label: "Медленное развитие на старте ", value: "670" }, + { label: "Межпространственные путешествия", value: "316" }, + { label: "Менеджмент", value: "182" }, + { label: "Мертвый главный герой", value: "794" }, + { label: "Месть", value: "88" }, + { label: "Метаморфы", value: "234" }, + { label: "Меч и магия", value: "55" }, + { label: "Мечник", value: "607" }, + { label: "Мечты", value: "733" }, + { label: "Милая история", value: "709" }, + { label: "Милое дитя", value: "596" }, + { label: "Милый главный герой", value: "697" }, + { label: "Мировое дерево", value: "385" }, + { label: "Мистический ореол вокруг семьи", value: "409" }, + { label: "Мифические звери", value: "418" }, + { label: "Мифология", value: "468" }, + { label: "Младшие братья", value: "843" }, + { label: "Младшие сестры", value: "465" }, + { label: "ММОРПГ (ЛитРПГ)", value: "306" }, + { label: "Множество перемещенных людей", value: "488" }, + { label: "Множество реальностей", value: "278" }, + { label: "Множество реинкарнированных людей", value: "227" }, + { label: "Модели", value: "649" }, + { label: "Молчаливый персонаж", value: "705" }, + { label: "Монстры", value: "69" }, + { label: "Мужская гей-пара", value: "685" }, + { label: "Мужчина-яндере", value: "155" }, + { label: "Музыка", value: "589" }, + { label: "Музыкальные группы", value: "588" }, + { label: "Мутации", value: "668" }, + { label: "Мутированные существа", value: "317" }, + { label: "Навык кражи", value: "626" }, + { label: "Навязчивая любовь", value: "85" }, + { label: "Наемники", value: "324" }, + { label: "Назойливый возлюбленный", value: "501" }, + { label: "Наивный главный герой", value: "66" }, + { label: "Наркотики", value: "661" }, + { label: "Нарциссический главный герой", value: "470" }, + { label: "Насилие сексуального характера", value: "824" }, + { label: "Наследование", value: "372" }, + { label: "Национализм", value: "318" }, + { label: "Не блещущий внешне главный герой", value: "328" }, + { label: "Не родные братья и сестры", value: "464" }, + { label: "Небеса", value: "508" }, + { label: "Небесное испытание", value: "274" }, + { label: "Негуманоидный главный герой", value: "622" }, + { label: "Недоверчивый главный герой", value: "485" }, + { label: "Недооцененный главный герой", value: "140" }, + { label: "Недоразумения", value: "202" }, + { label: "Неизлечимая болезнь", value: "75" }, + { label: "Некромант", value: "308" }, + { label: "Нелинейная история", value: "157" }, + { label: "Ненавистный главный герой", value: "487" }, + { label: "Ненадежный рассказчик", value: "165" }, + { label: "Нерезиденты", value: "821" }, + { label: "Нерешительный главный герой", value: "741" }, + { label: "Несерьезный главный герой", value: "294" }, + { label: "Несколько временных линий", value: "517" }, + { label: "Несколько главных героев", value: "475" }, + { label: "Несколько идентичностей", value: "615" }, + { label: "Несколько личностей", value: "474" }, + { label: "Нетораре", value: "721" }, + { label: "Нетори", value: "450" }, + { label: "Неудачливый главный герой", value: "567" }, + { label: "Ниндзя", value: "358" }, + { label: "Обещание из детства", value: "738" }, + { label: "Обманщик", value: "411" }, + { label: "Обмен телами", value: "94" }, + { label: "Обнаженка", value: "417" }, + { label: "Обольщение", value: "718" }, + { label: "Оборотни", value: "624" }, + { label: "Обратный гарем", value: "255" }, + { label: "Общество монстров", value: "226" }, + { label: "Обязательство", value: "548" }, + { label: "Огнестрельное оружие", value: "179" }, + { label: "Ограниченная продолжительность жизни", value: "73" }, + { label: "Одержимость", value: "447" }, + { label: "Одинокий главный герой", value: "304" }, + { label: "Одиночество", value: "199" }, + { label: "Одиночное проживание", value: "207" }, + { label: "Околосмертные переживания", value: "773" }, + { label: "Оммёдзи", value: "542" }, + { label: "Омоложение", value: "237" }, + { label: "Организованная преступность", value: "478" }, + { label: "Оргия", value: "825" }, + { label: "Орки", value: "228" }, + { label: "Освоение навыков", value: "235" }, + { label: "Основано на аниме", value: "553" }, + { label: "Основано на видео игре", value: "704" }, + { label: "Основано на визуальной новелле ", value: "861" }, + { label: "Основано на песне", value: "677" }, + { label: "Основано на фильме", value: "552" }, + { label: "Осторожный главный герой", value: "122" }, + { label: "Отаку", value: "512" }, + { label: "Открытый космос", value: "150" }, + { label: "Отношения в сети", value: "713" }, + { label: "Отношения между богом и человеком", value: "603" }, + { label: "Отношения между людьми и нелюдьми", value: "205" }, + { label: "Отношения на расстоянии", value: "852" }, + { label: "Отношения начальник-подчиненный", value: "590" }, + { label: "Отношения Сенпай-Коухай", value: "513" }, + { label: "Отношения ученика и учителя", value: "451" }, + { label: "Отношения учитель-ученик", value: "343" }, + { label: "Отношения хозяин-слуга", value: "206" }, + { label: "Отомэ игра", value: "723" }, + { label: "Отсутствие здравого смысла", value: "605" }, + { label: "Отсутствие родителей", value: "575" }, + { label: "Офисный роман", value: "686" }, + { label: "Официанты", value: "681" }, + { label: "Охотники", value: "288" }, + { label: "Очаровательный главный герой", value: "659" }, + { label: "Падшее дворянство", value: "618" }, + { label: "Падшие ангелы", value: "505" }, + { label: "Пайзури", value: "630" }, + { label: "Паразиты", value: "535" }, + { label: "Параллельные миры", value: "86" }, + { label: "Парк развлечений", value: "586" }, + { label: "Пародия", value: "319" }, + { label: "Певцы/Певицы", value: "734" }, + { label: "Первая любовь", value: "716" }, + { label: "Первоисточник новеллы — манга", value: "560" }, + { label: "Первый раз", value: "845" }, + { label: "Перемещение в другой мир, имея при себе современные достижения", value: "732" }, + { label: "Перемещение в игровой мир", value: "532" }, + { label: "Перемещение в иной мир", value: "754" }, + { label: "Перерождение в ином мире", value: "755" }, + { label: "Переселение души/Трансмиграция", value: "139" }, + { label: "Персонаж использует щит", value: "631" }, + { label: "Петля времени", value: "79" }, + { label: "Пираты", value: "817" }, + { label: "Писатели", value: "408" }, + { label: "Питомцы", value: "253" }, + { label: "Племенное общество", value: "291" }, + { label: "Повелитель демонов", value: "171" }, + { label: "Повествование от нескольких лиц/Несколько точек зрения", value: "156" }, + { label: "Подземелья", value: "219" }, + { label: "Пожелания", value: "166" }, + { label: "Познание Дао", value: "360" }, + { label: "Покинутое дитя", value: "534" }, + { label: "Полигамия", value: "296" }, + { label: "Политика", value: "43" }, + { label: "Полиция", value: "801" }, + { label: "Полулюди", value: "335" }, + { label: "Пользователь уникального оружия", value: "432" }, + { label: "Популярный любовный интерес", value: "241" }, + { label: "Постапокалиптика", value: "60" }, + { label: "Потерянные цивилизации", value: "751" }, + { label: "Похищения людей", value: "707" }, + { label: "Поэзия", value: "404" }, + { label: "Правонарушители", value: "694" }, + { label: "Прагматичный главный герой", value: "536" }, + { label: "Преданный любовный интерес", value: "106" }, + { label: "Предательство", value: "103" }, + { label: "Предвидение", value: "52" }, + { label: "Прекрасная героиня", value: "30" }, + { label: "Преступники", value: "728" }, + { label: "Преступность", value: "83" }, + { label: "Призванный герой", value: "147" }, + { label: "Призраки", value: "51" }, + { label: "Принуждение к отношениям", value: "602" }, + { label: "Принцессы", value: "933" }, + { label: "Притворная пара", value: "600" }, + { label: "Причудливые персонажи", value: "297" }, + { label: "Пришельцы/Инопланетяне", value: "76" }, + { label: "Программист", value: "666" }, + { label: "Проклятия", value: "48" }, + { label: "Промывание мозгов", value: "519" }, + { label: "Пропуск времени", value: "138" }, + { label: "Пророчества", value: "429" }, + { label: "Проститутки", value: "844" }, + { label: "Пространственное манипулирование", value: "375" }, + { label: "Прошлое играет большую роль", value: "215" }, + { label: "Прыжки между мирами", value: "737" }, + { label: "Психические силы", value: "265" }, + { label: "Психопаты", value: "158" }, + { label: "Путешествие во времени", value: "80" }, + { label: "Пытка", value: "168" }, + { label: "Рабы", value: "829" }, + { label: "Развод", value: "771" }, + { label: "Разумные предметы", value: "174" }, + { label: "Расизм", value: "320" }, + { label: "Рассказ", value: "61" }, + { label: "Расторжения помолвки", value: "243" }, + { label: "Расы зооморфов", value: "209" }, + { label: "Ревность", value: "717" }, + { label: "Редакторы", value: "684" }, + { label: "Реинкарнация", value: "281" }, + { label: "Реинкарнация в монстра", value: "204" }, + { label: "Реинкарнация в объект", value: "692" }, + { label: "Религии", value: "565" }, + { label: "Репортеры", value: "837" }, + { label: "Ресторан", value: "652" }, + { label: "Решительный главный герой", value: "124" }, + { label: "Робкий главный герой", value: "800" }, + { label: "Родитель одиночка", value: "687" }, + { label: "Родительский комплекс", value: "531" }, + { label: "Родословная", value: "121" }, + { label: "Романтический подсюжет ", value: "159" }, + { label: "Рост персонажа", value: "95" }, + { label: "Рыцари", value: "142" }, + { label: "Садистские персонажи", value: "642" }, + { label: "Самоотверженный главный герой", value: "748" }, + { label: "Самоубийства", value: "616" }, + { label: "Самурай", value: "646" }, + { label: "Сборник коротких историй", value: "456" }, + { label: "Связанные сюжетные линии", value: "473" }, + { label: "Святые", value: "761" }, + { label: "Священники", value: "325" }, + { label: "Сдержанный главный герой", value: "799" }, + { label: "Секретные организации", value: "331" }, + { label: "Секреты", value: "577" }, + { label: "Секс рабы", value: "830" }, + { label: "Семейный конфликт", value: "770" }, + { label: "Семь добродетелей", value: "233" }, + { label: "Семь смертных грехов", value: "134" }, + { label: "Семья", value: "251" }, + { label: "Сёнэн-ай подсюжет ", value: "664" }, + { label: "Серийные убийцы", value: "497" }, + { label: "Сестринский комплекс", value: "498" }, + { label: "Сила духа", value: "282" }, + { label: "Сила, требующая платы за пользование", value: "520" }, + { label: "Сильная пара", value: "109" }, + { label: "Сильный в сильнейшего", value: "359" }, + { label: "Сильный любовный интерес", value: "161" }, + { label: "Синдром восьмиклассника", value: "90" }, + { label: "Сироты", value: "214" }, + { label: "Система уровней", value: "198" }, + { label: "Системный администратор", value: "476" }, + { label: "Скрытие истинной личности", value: "371" }, + { label: "Скрытие истинных способностей", value: "252" }, + { label: "Скрытный главный герой", value: "133" }, + { label: "Скрытые способности", value: "128" }, + { label: "Скульпторы", value: "366" }, + { label: "Слабо выраженная романтическая линия", value: "213" }, + { label: "Слабый главный герой", value: "188" }, + { label: "Слепой главный герой", value: "864" }, + { label: "Слуги", value: "232" }, + { label: "Смерть", value: "49" }, + { label: "Смерть близких", value: "361" }, + { label: "Собственнические персонажи", value: "108" }, + { label: "Современность", value: "40" }, + { label: "Сожительство", value: "482" }, + { label: "Создание армии", value: "175" }, + { label: "Создание артефактов", value: "292" }, + { label: "Создание клана", value: "448" }, + { label: "Создание королевства", value: "181" }, + { label: "Создание навыков", value: "236" }, + { label: "Создание секты", value: "393" }, + { label: "Солдаты/Военные", value: "71" }, + { label: "Сон", value: "571" }, + { label: "Состоятельные персонажи", value: "67" }, + { label: "Социальная иерархия на основе силы", value: "137" }, + { label: "Социальные изгои", value: "480" }, + { label: "Спасение мира", value: "597" }, + { label: "Специальные способности", value: "54" }, + { label: "Спокойный главный герой", value: "32" }, + { label: "Справедливый главный герой", value: "702" }, + { label: "Средневековье", value: "184" }, + { label: "Ссорящаяся пара", value: "293" }, + { label: "Сталкеры", value: "689" }, + { label: "Старение", value: "190" }, + { label: "Стоические персонажи", value: "444" }, + { label: "Стокгольмский синдром", value: "643" }, + { label: "Стратег", value: "425" }, + { label: "Стратегические битвы", value: "160" }, + { label: "Стратегия", value: "1038" }, + { label: "Стрелки", value: "458" }, + { label: "Стрельба из лука", value: "383" }, + { label: "Студенческий совет", value: "490" }, + { label: "Судьба", value: "271" }, + { label: "Суккубы", value: "484" }, + { label: "Супер герои", value: "1039" }, + { label: "Суровая подготовка", value: "261" }, + { label: "Таинственная болезнь", value: "74" }, + { label: "Таинственное прошлое", value: "263" }, + { label: "Тайная личность", value: "310" }, + { label: "Тайные отношения", value: "812" }, + { label: "Танцоры", value: "840" }, + { label: "Телохранители", value: "452" }, + { label: "Тентакли", value: "693" }, + { label: "Террористы", value: "515" }, + { label: "Технологический разрыв", value: "621" }, + { label: "Тихие персонажи", value: "546" }, + { label: "Толстый главный герой", value: "299" }, + { label: "Торговцы", value: "416" }, + { label: "Травля/Буллинг", value: "89" }, + { label: "Травник", value: "708" }, + { label: "Трагическое прошлое", value: "164" }, + { label: "Трансплантация воспоминаний", value: "367" }, + { label: "Трап (Путаница с гендером персонажа)", value: "582" }, + { label: "Трудолюбивый главный герой", value: "37" }, + { label: "Тюрьма", value: "479" }, + { label: "Убийства", value: "84" }, + { label: "Убийцы", value: "248" }, + { label: "Убийцы драконов", value: "606" }, + { label: "Уверенный главный герой", value: "270" }, + { label: "Удачливый главный герой", value: "402" }, + { label: "Укротитель монстров", value: "337" }, + { label: "Умения из прошлой жизни", value: "280" }, + { label: "Умная пара", value: "493" }, + { label: "Умный главный герой", value: "33" }, + { label: "Уникальная техника Культивации", value: "254" }, + { label: "Уникальное оружие", value: "340" }, + { label: "Управление бизнесом", value: "315" }, + { label: "Управление временем", value: "167" }, + { label: "Управление кровью", value: "764" }, + { label: "Упрямый главный герой", value: "672" }, + { label: "Уродливый главный герой", value: "803" }, + { label: "Ускоренный рост", value: "300" }, + { label: "Усыновленные дети", value: "481" }, + { label: "Усыновленный главный герой", value: "412" }, + { label: "Уход за детьми", value: "398" }, + { label: "Учителя", value: "345" }, + { label: "Фамильяры", value: "541" }, + { label: "Фанатизм", value: "613" }, + { label: "Фантастические существа", value: "322" }, + { label: "Фанфикшн", value: "388" }, + { label: "Фармацевт", value: "715" }, + { label: "Фарминг", value: "379" }, + { label: "Феи", value: "210" }, + { label: "Фелляция", value: "584" }, + { label: "Фениксы", value: "374" }, + { label: "Фетиш груди", value: "499" }, + { label: "Философия", value: "87" }, + { label: "Фильмы", value: "403" }, + { label: "Флэшбэки", value: "528" }, + { label: "Фобии", value: "100" }, + { label: "Фольклор", value: "467" }, + { label: "Футанари", value: "568" }, + { label: "Футуристический сеттинг", value: "382" }, + { label: "Фэнтези мир", value: "126" }, + { label: "Хакеры", value: "399" }, + { label: "Харизматический герой", value: "391" }, + { label: "Хикикомори/Затворники", value: "462" }, + { label: "Хитроумный главный герой", value: "105" }, + { label: "Хозяин подземелий", value: "557" }, + { label: "Холодный главный герой", value: "259" }, + { label: "Хорошие отношения с семьей", value: "506" }, + { label: "Хранители могил", value: "68" }, + { label: "Целители", value: "389" }, + { label: "Цзянши", value: "735" }, + { label: "Цундэрэ", value: "445" }, + { label: "Чаты", value: "580" }, + { label: "Человеческое оружие", value: "521" }, + { label: "Честный главный герой", value: "240" }, + { label: "Читы", value: "238" }, + { label: "Шантаж", value: "526" }, + { label: "Шеф-повар", value: "239" }, + { label: "Шикигами", value: "543" }, + { label: "Школа только для девочек", value: "633" }, + { label: "Шота", value: "514" }, + { label: "Шоу-бизнес", value: "407" }, + { label: "Шпионы", value: "563" }, + { label: "Эволюция", value: "196" }, + { label: "Эгоистичный главный герой", value: "539" }, + { label: "Эйдетическая память", value: "392" }, + { label: "Экзорсизм", value: "504" }, + { label: "Экономика", value: "492" }, + { label: "Эксгибиционизм", value: "639" }, + { label: "Эксперименты с людьми", value: "129" }, + { label: "Элементальная магия", value: "395" }, + { label: "Эльфы", value: "172" }, + { label: "Эмоционально слабый главный герой", value: "816" }, + { label: "Эпизодический", value: "612" }, + { label: "Юный любовный интерес", value: "446" }, + { label: "Яды", value: "410" }, + { label: "Языкастые персонажи", value: "406" }, + { label: "Языковой барьер", value: "59" }, + { label: "Яндере", value: "208" }, + { label: "Японские силы самообороны", value: "559" }, + { label: "Ярко выраженная романтическая линия", value: "272" }, + { label: "Abusive Characters", value: "625" }, + { label: "Adapted to Visual Novel", value: "1046" }, + { label: "Adopted-lead", value: "886" }, + { label: "Adultery", value: "866" }, + { label: "Affair", value: "645" }, + { label: "Age-gap", value: "923" }, + { label: "Almost-human-lead", value: "975" }, + { label: "An*l", value: "780" }, + { label: "Androgynous-male-lead", value: "1002" }, + { label: "Anti-hero-lead", value: "976" }, + { label: "Apathetic-lead", value: "967" }, + { label: "Arms Dealers", value: "839" }, + { label: "Army-commander", value: "1003" }, + { label: "Artifact-refining", value: "924" }, + { label: "Autism", value: "855" }, + { label: "Awkward Protagonist", value: "669" }, + { label: "Awkward-lead", value: "892" }, + { label: "Bestiality", value: "796" }, + { label: "Big-breasts", value: "908" }, + { label: "Birth-of-a-nation", value: "1004" }, + { label: "Bisexual-lead", value: "1027" }, + { label: "Body-refining", value: "947" }, + { label: "Body-swap/s", value: "1036" }, + { label: "Boss-subordinate-relationship", value: "876" }, + { label: "Bride-kidnapping", value: "948" }, + { label: "Caring-lead", value: "879" }, + { label: "Cautious-lead", value: "1032" }, + { label: "Changed-man", value: "925" }, + { label: "Charismatic-lead", value: "1005" }, + { label: "Child-lead", value: "936" }, + { label: "Clever-lead", value: "880" }, + { label: "Clumsy Love Interests", value: "815" }, + { label: "Cold Love Interests", value: "651" }, + { label: "Coming of Age", value: "860" }, + { label: "Confident-lead", value: "894" }, + { label: "Confident-male-lead", value: "951" }, + { label: "Confinement", value: "767" }, + { label: "Conflicting Loyalties", value: "793" }, + { label: "Couple Growth", value: "690" }, + { label: "Court Official", value: "863" }, + { label: "Cowardly Protagonist", value: "703" }, + { label: "Cowardly-lead", value: "1000" }, + { label: "Cross-dressing", value: "250" }, + { label: "Cunnilingus", value: "867" }, + { label: "Cunning-lead", value: "952" }, + { label: "Cunning-male-lead", value: "953" }, + { label: "Curious Protagonist", value: "849" }, + { label: "Cute-lead", value: "887" }, + { label: "Dense-lead", value: "868" }, + { label: "Determined-lead", value: "972" }, + { label: "Developing-technology", value: "1006" }, + { label: "Devil/s", value: "1007" }, + { label: "Different Social Status", value: "758" }, + { label: "Disfigurement", value: "838" }, + { label: "Doting Love Interests", value: "665" }, + { label: "Doting Older Siblings", value: "609" }, + { label: "Doting Parents", value: "530" }, + { label: "Dungeon/s-exploring", value: "990" }, + { label: "Elderly Protagonist", value: "774" }, + { label: "Enlightenment", value: "729" }, + { label: "Eunuch", value: "1047" }, + { label: "Eye Powers", value: "386" }, + { label: "Family Business", value: "785" }, + { label: "Famous Parents", value: "790" }, + { label: "Famous-lead", value: "897" }, + { label: "Fanfic", value: "942" }, + { label: "Fated Lovers", value: "797" }, + { label: "Fellatio", value: "870" }, + { label: "Female-lead", value: "888" }, + { label: "First-time-intercourse", value: "871" }, + { label: "Fleet Battles", value: "836" }, + { label: "Forced Living Arrangements", value: "808" }, + { label: "Forced Marriage", value: "835" }, + { label: "Former Hero", value: "752" }, + { label: "Fujoshi", value: "1045" }, + { label: "Galge", value: "848" }, + { label: "Gambling", value: "1031" }, + { label: "Gamelit", value: "994" }, + { label: "Genderless-lead", value: "1008" }, + { label: "Glasses-wearing-lead", value: "995" }, + { label: "Guardian Relationship", value: "197" }, + { label: "H*ndjob", value: "629" }, + { label: "Half-human-lead", value: "956" }, + { label: "Hard-working-lead", value: "957" }, + { label: "Hard-working-male-lead", value: "958" }, + { label: "Hard-working-protagonist/s", value: "1030" }, + { label: "Harem-seeking-lead", value: "929" }, + { label: "Harem-subtext", value: "909" }, + { label: "Helpful Protagonist", value: "760" }, + { label: "Helpful-lead", value: "882" }, + { label: "Hidden-gem", value: "998" }, + { label: "High-fantasy", value: "991" }, + { label: "Human-becomes-demon/monster", value: "1009" }, + { label: "Human-nonhuman-relationship", value: "914" }, + { label: "Humanoid-lead", value: "915" }, + { label: "Imperial Harem", value: "805" }, + { label: "Incubus", value: "1049" }, + { label: "Insects", value: "831" }, + { label: "Interspatial-storage", value: "959" }, + { label: "Kind Love Interests", value: "757" }, + { label: "Large-number-of-skills", value: "1010" }, + { label: "Legendary-hero", value: "1011" }, + { label: "Litrpg", value: "977" }, + { label: "Love Rivals", value: "788" }, + { label: "Lovers Reunited", value: "853" }, + { label: "Low-fantasy", value: "1037" }, + { label: "Master-disciple-relationship", value: "899" }, + { label: "Matriarchy", value: "807" }, + { label: "Mature-lead", value: "900" }, + { label: "Mind Break", value: "862" }, + { label: "Mismatched Couple", value: "695" }, + { label: "Mob Protagonist", value: "648" }, + { label: "Mob-lead", value: "902" }, + { label: "Modern", value: "988" }, + { label: "Monster-pov", value: "1012" }, + { label: "Mpreg", value: "856" }, + { label: "Multiple-povs", value: "1013" }, + { label: "Mystery Solving", value: "710" }, + { label: "Naive-lead", value: "1014" }, + { label: "Near-death-experience", value: "996" }, + { label: "Neet", value: "495" }, + { label: "Nightmares", value: "833" }, + { label: "Nobility", value: "1015" }, + { label: "Non-human-lead", value: "978" }, + { label: "Non-humanoid-lead", value: "1016" }, + { label: "Not-so-secret-identity", value: "979" }, + { label: "Omegaverse", value: "857" }, + { label: "Online-game", value: "910" }, + { label: "Online-gaming", value: "911" }, + { label: "Outdoor Interc**rse", value: "847" }, + { label: "Outdoor-intercourse", value: "903" }, + { label: "Overprotective Siblings", value: "610" }, + { label: "Part-Time Job", value: "865" }, + { label: "Past Trauma", value: "726" }, + { label: "Past-memories", value: "1017" }, + { label: "Persistent Love Interests", value: "714" }, + { label: "Perverted-lead", value: "932" }, + { label: "Photography", value: "798" }, + { label: "Pill Based Cultivation", value: "279" }, + { label: "Pill Concocting", value: "245" }, + { label: "Pilots", value: "820" }, + { label: "Playboys", value: "795" }, + { label: "Playful-lead", value: "989" }, + { label: "Polyandry", value: "811" }, + { label: "Portal-fantasy-/-isekai", value: "992" }, + { label: "Pragmatic-lead", value: "884" }, + { label: "Profanity", value: "980" }, + { label: "Protagonist-loyal-to-love-interest", value: "905" }, + { label: "R-15 (Японское возрастное ограничение)", value: "230" }, + { label: "Rebellion", value: "834" }, + { label: "Reincarnated in a Game World", value: "804" }, + { label: "Reluctant Protagonist", value: "696" }, + { label: "Reverse R*pe", value: "776" }, + { label: "Reversible Couple", value: "854" }, + { label: "Ruling-class", value: "943" }, + { label: "S*x Friends", value: "750" }, + { label: "S*xual Cultivation Technique", value: "823" }, + { label: "Salaryman", value: "1035" }, + { label: "Satire", value: "981" }, + { label: "Schemes And Conspiracies", value: "745" }, + { label: "Scientist/s", value: "997" }, + { label: "Scientists", value: "1029" }, + { label: "Secret Crush", value: "827" }, + { label: "Seme Protagonist", value: "1034" }, + { label: "Seme-lead", value: "1028" }, + { label: "Sentimental Protagonist", value: "1048" }, + { label: "Seven-heavenly-virtues", value: "1018" }, + { label: "Sexual Cultivation Technique", value: "350" }, + { label: "Sexual-content", value: "982" }, + { label: "Sexuality", value: "983" }, + { label: "Sharing A Body", value: "814" }, + { label: "Shotacon", value: "858" }, + { label: "Shoujo-Ai Subplot", value: "777" }, + { label: "Sibling Rivalry", value: "766" }, + { label: "Sibling's Care", value: "1042" }, + { label: "Sickly Characters", value: "787" }, + { label: "Skills", value: "1019" }, + { label: "Slave-harem", value: "1001" }, + { label: "Slime", value: "1020" }, + { label: "Smart-male-lead", value: "963" }, + { label: "Spirit Users", value: "739" }, + { label: "Straight Seme", value: "819" }, + { label: "Straight Uke", value: "818" }, + { label: "Strength-based-social-hierarchy", value: "974" }, + { label: "Strong-lead", value: "944" }, + { label: "Strong-male-lead", value: "964" }, + { label: "Student-teacher-relationship", value: "1026" }, + { label: "Suspense", value: "987" }, + { label: "Sword-and-sorcery", value: "875" }, + { label: "Threesome", value: "574" }, + { label: "Transformation Ability", value: "736" }, + { label: "Traumatising-content", value: "1040" }, + { label: "Underestimated-lead", value: "919" }, + { label: "Underestimated-male-lead", value: "966" }, + { label: "Unique-abilities", value: "1021" }, + { label: "Unique-skill", value: "1022" }, + { label: "Unique-skills", value: "1023" }, + { label: "Unlimited Flow", value: "1044" }, + { label: "Unrequited Love", value: "806" }, + { label: "Urban-fantasy", value: "1041" }, + { label: "Villainess Noble Girls", value: "719" }, + { label: "Weak-lead", value: "885" }, + { label: "Web-novel", value: "1024" }, + ], + type: FilterTypes.ExcludableCheckboxGroup, + }, + tags: { + label: "Жанры", + value: { include: [], exclude: [] }, + options: [ + { label: "Боевые искусства", value: "22" }, + { label: "Гарем", value: "114" }, + { label: "Гендер бендер", value: "246" }, + { label: "Дзёсэй", value: "216" }, + { label: "Для взрослых", value: "115" }, + { label: "Для взрослых", value: "258" }, + { label: "Драма", value: "7" }, + { label: "Исторический", value: "101" }, + { label: "Комедия", value: "17" }, + { label: "Лоликон", value: "638" }, + { label: "Магический реализм", value: "922" }, + { label: "Меха", value: "24" }, + { label: "Милитари", value: "12" }, + { label: "Мистика", value: "2" }, + { label: "Научная фантастика", value: "13" }, + { label: "Непристойность", value: "747" }, + { label: "Повседневность", value: "93" }, + { label: "Приключение", value: "11" }, + { label: "Психология", value: "18" }, + { label: "Романтика", value: "9" }, + { label: "Сверхъестественное", value: "20" }, + { label: "Сёдзё", value: "15" }, + { label: "Сёдзё-ай", value: "23" }, + { label: "Сёнэн", value: "189" }, + { label: "Сёнэн-ай", value: "680" }, + { label: "Спорт", value: "420" }, + { label: "Сэйнэн", value: "5" }, + { label: "Сюаньхуа", value: "242" }, + { label: "Сянься", value: "364" }, + { label: "Трагедия", value: "19" }, + { label: "Триллер", value: "3" }, + { label: "Ужасы", value: "1" }, + { label: "Уся", value: "720" }, + { label: "Фэнтези", value: "8" }, + { label: "Школьная жизнь", value: "21" }, + { label: "Экшн", value: "14" }, + { label: "Эччи", value: "327" }, + { label: "Юри", value: "691" }, + { label: "Яой", value: "682" }, + { label: "Eastern fantasy", value: "907" }, + { label: "Isekai", value: "999" }, + { label: "Video games", value: "993" }, + ], + type: FilterTypes.ExcludableCheckboxGroup, + }, + } satisfies Filters; +} + +export default new RNBH(); + +interface responseNovels { + id: number; + names: Names; + rating: number; + synopsis: string; + url: string; + poster: Poster; + created_at: number; + status: string; + user?: User; + counts: Counts; +} +interface Names { + eng: string; + rus?: string; + original?: string; +} +interface Poster { + medium: string; + small: string; + color: string; +} +interface User { + status?: null; + liked: boolean; +} +interface Counts { + volumes: string; + chapters: string; +} + +interface responseNovel { + id: number; + names: Names; + rating: number; + year: number; + synopsis: string; + url: string; + posters: Posters; + isSpecial: boolean; + liked: boolean; + authors?: AuthorsEntity[] | null; + translators?: TranslatorsEntity[] | null; + description: string; + status: Status; + start_reading_url: string; + html: string; + tags: Tags; +} +interface Posters { + big: string; + medium: string; + small: string; + tiny: string; + color: string; +} +interface AuthorsEntity { + name_eng: string; + pivot: Pivot; +} +interface Pivot { + ranobe_id: number; + author_id?: number; + translator_id?: number; +} +interface TranslatorsEntity { + name: string; + pivot: Pivot; +} +interface Status { + id: number; + title: string; +} +interface Tags { + events?: GenresOrEntity[] | null; + genres?: GenresOrEntity[] | null; +} +interface GenresOrEntity { + id: number; + names: Names; + url: string; + title: string; + description?: string | null; +} + +interface responseChapters { + marked_chapters_ids?: null[] | null; + volumes?: VolumesEntity[] | null; +} +interface VolumesEntity { + id: number; + num: number; + name: string; + status: Status; + chapters?: ChaptersEntity[] | null; +} +interface Status { + id: number; + name: string; +} +interface ChaptersEntity { + id: number; + name: string; + num: number; + url: string; + is_new: boolean; + has_images: boolean; + changed_at: string; + comments_count: string; +} + +interface responseSearch { + meta: Meta; + collections?: DataEntity[] | null; + data?: DataEntity[] | null; +} +interface Meta { + key: string; + title: string; +} +interface DataEntity { + id: number; + names?: Names | null; + description?: string | null; + url: string; + image?: string | null; + name?: string | null; + level?: number | null; + evolution_scheme?: null; + roles?: null[] | null; + avatar?: Avatar | null; + has_plus?: boolean | null; +} +interface Avatar { + big: string; + color: string; + thumb: string; + is_default: boolean; +} diff --git a/plugins/russian/ranobelib.ts b/plugins/russian/ranobelib.ts index 47d4c2c..6077877 100644 --- a/plugins/russian/ranobelib.ts +++ b/plugins/russian/ranobelib.ts @@ -1,513 +1,512 @@ -import { Plugin } from "@typings/plugin"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { NovelStatus } from "@libs/novelStatus"; -import { load as parseHTML } from "cheerio"; -import dayjs from "dayjs"; - -class RLIB implements Plugin.PluginBase { - id = "RLIB"; - name = "RanobeLib"; - site = "https://ranobelib.me"; - version = "1.0.0"; - icon = "src/ru/ranobelib/icon.png"; - ui: string | number | undefined = undefined; - userAgent = ""; - cookieString = ""; - - async popularNovels( - pageNo: number, - { - showLatestNovels, - filters, - }: Plugin.PopularNovelsOptions, - ): Promise { - let url = `${this.site}/manga-list?sort=`; - url += showLatestNovels - ? "last_chapter_at" - : filters?.sort?.value || "rate"; - url += "&dir=" + (filters?.order?.value || "desc"); - - if (filters.type?.value?.length) { - url += filters.type.value.map((i) => "&types[]=" + i).join(""); - } - - if (filters.format?.value?.include?.length) { - url += filters.format.value.include - .map((i) => "&format[include][]=" + i).join(""); - } - - if (filters.format?.value?.exclude?.length) { - url += filters.format.value.exclude - .map((i) => "&format[exclude][]=" + i).join(""); - } - - if (filters.status?.value?.length) { - url += filters.status.value.map((i) => "&status[]=" + i).join(""); - } - - if (filters.statuss?.value?.length) { - url += filters.statuss.value.map((i) => "&manga_status[]=" + i).join(""); - } - - if (filters.genres?.value?.include?.length) { - url += filters.genres.value.include - .map((i) => "&genres[include][]=" + i).join(""); - } - - if (filters.genres?.value?.exclude?.length) { - url += filters.genres.value.exclude - .map((i) => "&genres[exclude][]=" + i).join(""); - } - - if (filters.tags?.value?.include?.length) { - url += filters.tags.value.include - .map((i) => "&tags[include][]=" + i).join(""); - } - - if (filters.tags?.value?.exclude?.length) { - url += filters.tags.value.exclude - .map((i) => "&tags[exclude][]=" + i).join(""); - } - - url += "&page=" + pageNo; - - const result = await fetchApi(url); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - this.ui = loadedCheerio("a.header-right-menu__item") - .attr("href") - ?.replace?.(/[^0-9]/g, ""); - - const novels: Plugin.NovelItem[] = []; - loadedCheerio(".media-card-wrap").each(function () { - const name = loadedCheerio(this).find(".media-card__title").text(); - const cover = loadedCheerio(this).find("a.media-card").attr("data-src"); - const url = loadedCheerio(this).find("a.media-card").attr("href"); - if (!url) return; - novels.push({ name, cover, url }); - }); - - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const result = await fetchApi(novelUrl); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - const novel: Plugin.SourceNovel = { - url: novelUrl, - }; - - novel.name = loadedCheerio(".media-name__main").text().trim(); - - novel.cover = loadedCheerio(".media-sidebar__cover img").attr("src"); - - novel.summary = loadedCheerio(".media-description__text").text().trim(); - - novel.genres = loadedCheerio('div[class="media-tags"]') - .text() - .trim() - .replace(/[\n\r]+/g, ", ") - .replace(/ /g, ""); - - loadedCheerio( - 'div[class="media-info-list paper"] > [class="media-info-list__item"]', - ).each(function () { - let name = loadedCheerio(this) - .find('div[class="media-info-list__title"]') - .text(); - - if (name === "Статус перевода") { - novel.status = - loadedCheerio(this).find("div:nth-child(2)").text().trim() === - "Продолжается" - ? NovelStatus.Ongoing - : NovelStatus.Completed; - } else if (name === "Автор") { - novel.author = loadedCheerio(this) - .find("div:nth-child(2)") - .text() - .trim(); - } - }); - - const chapters: Plugin.ChapterItem[] = []; - - let chaptersRaw: any = body.match( - /window.__DATA__ = [\s\S]*?window._SITE_COLOR_/gm, - ); - - chaptersRaw = chaptersRaw?.[0] - ?.replace("window.__DATA__ = ", "") - ?.replace("window._SITE_COLOR_", "") - ?.trim() - ?.slice(0, -1); - - let chaptersJson: responseBook = JSON.parse(chaptersRaw); - this.ui = chaptersJson?.user?.id; - - chaptersJson.chapters?.list?.forEach((chapter) => - chapters.push({ - name: `Том ${chapter.chapter_volume} Глава ${chapter.chapter_number} ${chapter.chapter_name}`?.trim(), - releaseTime: dayjs(chapter.chapter_created_at).format("LLL"), - url: - `${this.site}/${chaptersJson.manga.slug}/v${chapter.chapter_volume}/c${chapter.chapter_number}?bid=` + - (chapter?.branch_id || ""), - }), - ); - - novel.chapters = chapters.reverse(); - return novel; - } - - async parseChapter(chapterUrl: string): Promise { - const result = await fetchApi( - chapterUrl + (this.ui ? "&ui=" + this.ui : ""), - ); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - const baseUrl = this.site; - loadedCheerio(".reader-container img").each(function () { - const src = - loadedCheerio(this).attr("data-src") || loadedCheerio(this).attr("src"); - if (!src?.startsWith("http")) { - loadedCheerio(this).attr("src", baseUrl + src); - } else { - loadedCheerio(this).attr("src", src); - } - loadedCheerio(this).removeAttr("data-src"); - }); - - const chapterText = loadedCheerio(".reader-container").html(); - - return chapterText || ""; - } - - async searchNovels( - searchTerm: string, - //pageNo: number | undefined = 1, - ): Promise { - const result = await fetchApi( - `${this.site}/search?q=${searchTerm}&type=manga`, - ); - const body = (await result.json()) as Manga[]; - const novels: Plugin.NovelItem[] = []; - - body.forEach((novel) => - novels.push({ - name: novel?.rus_name || novel.name, - cover: novel?.coverImage, - url: novel?.href || this.site + "/" + novel.slug, - }), - ); - - return novels; - } - - fetchImage = fetchFile; - - filters = { - sort: { - label: "Сортировка", - value: "", - options: [ - { label: "Рейтинг", value: "rate" }, - { label: "Имя", value: "name" }, - { label: "Просмотры", value: "views" }, - { label: "Дате добавления", value: "created_at" }, - { label: "Дате обновления", value: "last_chapter_at" }, - { label: "Количество глав", value: "chap_count" }, - ], - type: FilterTypes.Picker, - }, - order: { - label: "Порядок", - value: "", - options: [ - { label: "По убыванию", value: "desc" }, - { label: "По возрастанию", value: "asc" }, - ], - type: FilterTypes.Picker, - }, - type: { - label: "Тип", - value: [], - options: [ - { label: "Авторский", value: "14" }, - { label: "Английский", value: "13" }, - { label: "Китай", value: "12" }, - { label: "Корея", value: "11" }, - { label: "Фанфик", value: "15" }, - { label: "Япония", value: "10" }, - ], - type: FilterTypes.CheckboxGroup, - }, - format: { - label: "Формат выпуска", - value: { include: [], exclude: [] }, - options: [ - { label: "4-кома (Ёнкома)", value: "1" }, - { label: "В цвете", value: "4" }, - { label: "Веб", value: "6" }, - { label: "Вебтун", value: "7" }, - { label: "Додзинси", value: "3" }, - { label: "Сборник", value: "2" }, - { label: "Сингл", value: "5" }, - ], - type: FilterTypes.ExcludableCheckboxGroup, - }, - status: { - label: "Статус перевода", - value: [], - options: [ - { label: "Продолжается", value: "1" }, - { label: "Завершен", value: "2" }, - { label: "Заморожен", value: "3" }, - { label: "Заброшен", value: "4" }, - ], - type: FilterTypes.CheckboxGroup, - }, - statuss: { - label: "Статус тайтла", - value: [], - options: [ - { label: "Онгоинг", value: "1" }, - { label: "Завершён", value: "2" }, - { label: "Анонс", value: "3" }, - { label: "Приостановлен", value: "4" }, - { label: "Выпуск прекращён", value: "5" }, - ], - type: FilterTypes.CheckboxGroup, - }, - genres: { - label: "Жанры", - value: { include: [], exclude: [] }, - options: [ - { label: "Арт", value: "32" }, - { label: "Безумие", value: "91" }, - { label: "Боевик", value: "34" }, - { label: "Боевые искусства", value: "35" }, - { label: "Вампиры", value: "36" }, - { label: "Военное", value: "89" }, - { label: "Гарем", value: "37" }, - { label: "Гендерная интрига", value: "38" }, - { label: "Героическое фэнтези", value: "39" }, - { label: "Демоны", value: "81" }, - { label: "Детектив", value: "40" }, - { label: "Детское", value: "88" }, - { label: "Дзёсэй", value: "41" }, - { label: "Драма", value: "43" }, - { label: "Игра", value: "44" }, - { label: "Исекай", value: "79" }, - { label: "История", value: "45" }, - { label: "Киберпанк", value: "46" }, - { label: "Кодомо", value: "76" }, - { label: "Комедия", value: "47" }, - { label: "Космос", value: "83" }, - { label: "Магия", value: "85" }, - { label: "Махо-сёдзё", value: "48" }, - { label: "Машины", value: "90" }, - { label: "Меха", value: "49" }, - { label: "Мистика", value: "50" }, - { label: "Музыка", value: "80" }, - { label: "Научная фантастика", value: "51" }, - { label: "Омегаверс", value: "77" }, - { label: "Пародия", value: "86" }, - { label: "Повседневность", value: "52" }, - { label: "Полиция", value: "82" }, - { label: "Постапокалиптика", value: "53" }, - { label: "Приключения", value: "54" }, - { label: "Психология", value: "55" }, - { label: "Романтика", value: "56" }, - { label: "Самурайский боевик", value: "57" }, - { label: "Сверхъестественное", value: "58" }, - { label: "Сёдзё", value: "59" }, - { label: "Сёдзё-ай", value: "60" }, - { label: "Сёнэн", value: "61" }, - { label: "Сёнэн-ай", value: "62" }, - { label: "Спорт", value: "63" }, - { label: "Супер сила", value: "87" }, - { label: "Сэйнэн", value: "64" }, - { label: "Трагедия", value: "65" }, - { label: "Триллер", value: "66" }, - { label: "Ужасы", value: "67" }, - { label: "Фантастика", value: "68" }, - { label: "Фэнтези", value: "69" }, - { label: "Школа", value: "70" }, - { label: "Эротика", value: "71" }, - { label: "Этти", value: "72" }, - { label: "Юри", value: "73" }, - { label: "Яой", value: "74" }, - ], - type: FilterTypes.ExcludableCheckboxGroup, - }, - tags: { - label: "Теги", - value: { include: [], exclude: [] }, - options: [ - { label: "Авантюристы", value: "328" }, - { label: "Антигерой", value: "176" }, - { label: "Бессмертные", value: "333" }, - { label: "Боги", value: "218" }, - { label: "Борьба за власть", value: "309" }, - { label: "Брат и сестра", value: "360" }, - { label: "Ведьма", value: "339" }, - { label: "Видеоигры", value: "204" }, - { label: "Виртуальная реальность", value: "214" }, - { label: "Владыка демонов", value: "349" }, - { label: "Военные", value: "198" }, - { label: "Воспоминания из другого мира", value: "310" }, - { label: "Выживание", value: "212" }, - { label: "ГГ женщина", value: "294" }, - { label: "ГГ имба", value: "292" }, - { label: "ГГ мужчина", value: "295" }, - { label: "ГГ не ояш", value: "325" }, - { label: "ГГ не человек", value: "331" }, - { label: "ГГ ояш", value: "326" }, - { label: "Главный герой бог", value: "324" }, - { label: "Глупый ГГ", value: "298" }, - { label: "Горничные", value: "171" }, - { label: "Гуро", value: "306" }, - { label: "Гяру", value: "197" }, - { label: "Демоны", value: "157" }, - { label: "Драконы", value: "313" }, - { label: "Древний мир", value: "317" }, - { label: "Зверолюди", value: "163" }, - { label: "Зомби", value: "155" }, - { label: "Исторические фигуры", value: "323" }, - { label: "Кулинария", value: "158" }, - { label: "Культивация", value: "161" }, - { label: "ЛГБТ", value: "344" }, - { label: "ЛитРПГ", value: "319" }, - { label: "Лоли", value: "206" }, - { label: "Магия", value: "170" }, - { label: "Машинный перевод", value: "345" }, - { label: "Медицина", value: "159" }, - { label: "Межгалактическая война", value: "330" }, - { label: "Монстр Девушки", value: "207" }, - { label: "Монстры", value: "208" }, - { label: "Мрачный мир", value: "316" }, - { label: "Музыка", value: "358" }, - { label: "Музыка", value: "209" }, - { label: "Ниндзя", value: "199" }, - { label: "Обратный Гарем", value: "210" }, - { label: "Офисные Работники", value: "200" }, - { label: "Пираты", value: "341" }, - { label: "Подземелья", value: "314" }, - { label: "Политика", value: "311" }, - { label: "Полиция", value: "201" }, - { label: "Преступники / Криминал", value: "205" }, - { label: "Призраки / Духи", value: "196" }, - { label: "Призыватели", value: "329" }, - { label: "Прыжки между мирами", value: "321" }, - { label: "Путешествие в другой мир", value: "318" }, - { label: "Путешествие во времени", value: "213" }, - { label: "Рабы", value: "355" }, - { label: "Ранги силы", value: "312" }, - { label: "Реинкарнация", value: "154" }, - { label: "Самураи", value: "202" }, - { label: "Скрытие личности", value: "315" }, - { label: "Средневековье", value: "174" }, - { label: "Традиционные игры", value: "203" }, - { label: "Умный ГГ", value: "303" }, - { label: "Характерный рост", value: "332" }, - { label: "Хикикомори", value: "167" }, - { label: "Эволюция", value: "322" }, - { label: "Элементы РПГ", value: "327" }, - { label: "Эльфы", value: "217" }, - { label: "Якудза", value: "165" }, - ], - type: FilterTypes.ExcludableCheckboxGroup, - }, - } satisfies Filters; -} - -export default new RLIB(); - -interface responseBook { - hasStickyPermission: boolean; - bookmark?: null; - auth: boolean; - comments_version: string; - manga: Manga; - chapters: Chapters; - user?: User; -} -interface Manga { - id: number; - name: string; - rusName?: string; - rus_name?: string; - engName?: string; - slug: string; - status: number; - chapters_count: number; - altNames?: string[] | null; - coverImage?: string; - href?: string; -} -interface Chapters { - list?: ListEntity[] | null; - teams?: TeamsEntity[] | null; - branches?: BranchesEntity[] | null; - is_paid?: null[] | null; -} -interface ListEntity { - chapter_id: number; - chapter_slug: string; - chapter_name: string; - chapter_number: string; - chapter_volume: number; - chapter_moderated: number; - chapter_user_id: number; - chapter_expired_at: string; - chapter_scanlator_id: number; - chapter_created_at: string; - status?: null; - price: number; - branch_id: number; - username: string; -} -interface TeamsEntity { - name: string; - alt_name: string; - cover: string; - slug: string; - id: number; - branch_id: number; - sale: number; - href: string; - pivot: Pivot; -} -interface Pivot { - manga_id: number; - team_id: number; -} -interface BranchesEntity { - id: number; - manga_id: number; - name: string; - teams?: TeamsEntity1[] | null; - is_subscribed: boolean; -} -interface TeamsEntity1 { - id: number; - name: string; - slug: string; - cover: string; - branch_id: number; - is_active: number; -} -interface User { - id: number; - avatar: string; - access: boolean; - isAdmin: boolean; - paid: boolean; -} +import { Plugin } from "@typings/plugin"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { NovelStatus } from "@libs/novelStatus"; +import { load as parseHTML } from "cheerio"; +import dayjs from "dayjs"; + +class RLIB implements Plugin.PluginBase { + id = "RLIB"; + name = "RanobeLib"; + site = "https://ranobelib.me"; + version = "1.0.0"; + icon = "src/ru/ranobelib/icon.png"; + ui: string | number | undefined = undefined; + userAgent = ""; + + async popularNovels( + pageNo: number, + { + showLatestNovels, + filters, + }: Plugin.PopularNovelsOptions, + ): Promise { + let url = `${this.site}/manga-list?sort=`; + url += showLatestNovels + ? "last_chapter_at" + : filters?.sort?.value || "rate"; + url += "&dir=" + (filters?.order?.value || "desc"); + + if (filters.type?.value?.length) { + url += filters.type.value.map((i) => "&types[]=" + i).join(""); + } + + if (filters.format?.value?.include?.length) { + url += filters.format.value.include + .map((i) => "&format[include][]=" + i).join(""); + } + + if (filters.format?.value?.exclude?.length) { + url += filters.format.value.exclude + .map((i) => "&format[exclude][]=" + i).join(""); + } + + if (filters.status?.value?.length) { + url += filters.status.value.map((i) => "&status[]=" + i).join(""); + } + + if (filters.statuss?.value?.length) { + url += filters.statuss.value.map((i) => "&manga_status[]=" + i).join(""); + } + + if (filters.genres?.value?.include?.length) { + url += filters.genres.value.include + .map((i) => "&genres[include][]=" + i).join(""); + } + + if (filters.genres?.value?.exclude?.length) { + url += filters.genres.value.exclude + .map((i) => "&genres[exclude][]=" + i).join(""); + } + + if (filters.tags?.value?.include?.length) { + url += filters.tags.value.include + .map((i) => "&tags[include][]=" + i).join(""); + } + + if (filters.tags?.value?.exclude?.length) { + url += filters.tags.value.exclude + .map((i) => "&tags[exclude][]=" + i).join(""); + } + + url += "&page=" + pageNo; + + const result = await fetchApi(url); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + this.ui = loadedCheerio("a.header-right-menu__item") + .attr("href") + ?.replace?.(/[^0-9]/g, ""); + + const novels: Plugin.NovelItem[] = []; + loadedCheerio(".media-card-wrap").each(function () { + const name = loadedCheerio(this).find(".media-card__title").text(); + const cover = loadedCheerio(this).find("a.media-card").attr("data-src"); + const url = loadedCheerio(this).find("a.media-card").attr("href"); + if (!url) return; + novels.push({ name, cover, url }); + }); + + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const result = await fetchApi(novelUrl); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + const novel: Plugin.SourceNovel = { + url: novelUrl, + }; + + novel.name = loadedCheerio(".media-name__main").text().trim(); + + novel.cover = loadedCheerio(".media-sidebar__cover img").attr("src"); + + novel.summary = loadedCheerio(".media-description__text").text().trim(); + + novel.genres = loadedCheerio('div[class="media-tags"]') + .text() + .trim() + .replace(/[\n\r]+/g, ", ") + .replace(/ /g, ""); + + loadedCheerio( + 'div[class="media-info-list paper"] > [class="media-info-list__item"]', + ).each(function () { + let name = loadedCheerio(this) + .find('div[class="media-info-list__title"]') + .text(); + + if (name === "Статус перевода") { + novel.status = + loadedCheerio(this).find("div:nth-child(2)").text().trim() === + "Продолжается" + ? NovelStatus.Ongoing + : NovelStatus.Completed; + } else if (name === "Автор") { + novel.author = loadedCheerio(this) + .find("div:nth-child(2)") + .text() + .trim(); + } + }); + + const chapters: Plugin.ChapterItem[] = []; + + let chaptersRaw: any = body.match( + /window.__DATA__ = [\s\S]*?window._SITE_COLOR_/gm, + ); + + chaptersRaw = chaptersRaw?.[0] + ?.replace("window.__DATA__ = ", "") + ?.replace("window._SITE_COLOR_", "") + ?.trim() + ?.slice(0, -1); + + let chaptersJson: responseBook = JSON.parse(chaptersRaw); + this.ui = chaptersJson?.user?.id; + + chaptersJson.chapters?.list?.forEach((chapter) => + chapters.push({ + name: `Том ${chapter.chapter_volume} Глава ${chapter.chapter_number} ${chapter.chapter_name}`?.trim(), + releaseTime: dayjs(chapter.chapter_created_at).format("LLL"), + url: + `${this.site}/${chaptersJson.manga.slug}/v${chapter.chapter_volume}/c${chapter.chapter_number}?bid=` + + (chapter?.branch_id || ""), + }), + ); + + novel.chapters = chapters.reverse(); + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const result = await fetchApi( + chapterUrl + (this.ui ? "&ui=" + this.ui : ""), + ); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + const baseUrl = this.site; + loadedCheerio(".reader-container img").each(function () { + const src = + loadedCheerio(this).attr("data-src") || loadedCheerio(this).attr("src"); + if (!src?.startsWith("http")) { + loadedCheerio(this).attr("src", baseUrl + src); + } else { + loadedCheerio(this).attr("src", src); + } + loadedCheerio(this).removeAttr("data-src"); + }); + + const chapterText = loadedCheerio(".reader-container").html(); + + return chapterText || ""; + } + + async searchNovels( + searchTerm: string, + //pageNo: number | undefined = 1, + ): Promise { + const result = await fetchApi( + `${this.site}/search?q=${searchTerm}&type=manga`, + ); + const body = (await result.json()) as Manga[]; + const novels: Plugin.NovelItem[] = []; + + body.forEach((novel) => + novels.push({ + name: novel?.rus_name || novel.name, + cover: novel?.coverImage, + url: novel?.href || this.site + "/" + novel.slug, + }), + ); + + return novels; + } + + fetchImage = fetchFile; + + filters = { + sort: { + label: "Сортировка", + value: "", + options: [ + { label: "Рейтинг", value: "rate" }, + { label: "Имя", value: "name" }, + { label: "Просмотры", value: "views" }, + { label: "Дате добавления", value: "created_at" }, + { label: "Дате обновления", value: "last_chapter_at" }, + { label: "Количество глав", value: "chap_count" }, + ], + type: FilterTypes.Picker, + }, + order: { + label: "Порядок", + value: "", + options: [ + { label: "По убыванию", value: "desc" }, + { label: "По возрастанию", value: "asc" }, + ], + type: FilterTypes.Picker, + }, + type: { + label: "Тип", + value: [], + options: [ + { label: "Авторский", value: "14" }, + { label: "Английский", value: "13" }, + { label: "Китай", value: "12" }, + { label: "Корея", value: "11" }, + { label: "Фанфик", value: "15" }, + { label: "Япония", value: "10" }, + ], + type: FilterTypes.CheckboxGroup, + }, + format: { + label: "Формат выпуска", + value: { include: [], exclude: [] }, + options: [ + { label: "4-кома (Ёнкома)", value: "1" }, + { label: "В цвете", value: "4" }, + { label: "Веб", value: "6" }, + { label: "Вебтун", value: "7" }, + { label: "Додзинси", value: "3" }, + { label: "Сборник", value: "2" }, + { label: "Сингл", value: "5" }, + ], + type: FilterTypes.ExcludableCheckboxGroup, + }, + status: { + label: "Статус перевода", + value: [], + options: [ + { label: "Продолжается", value: "1" }, + { label: "Завершен", value: "2" }, + { label: "Заморожен", value: "3" }, + { label: "Заброшен", value: "4" }, + ], + type: FilterTypes.CheckboxGroup, + }, + statuss: { + label: "Статус тайтла", + value: [], + options: [ + { label: "Онгоинг", value: "1" }, + { label: "Завершён", value: "2" }, + { label: "Анонс", value: "3" }, + { label: "Приостановлен", value: "4" }, + { label: "Выпуск прекращён", value: "5" }, + ], + type: FilterTypes.CheckboxGroup, + }, + genres: { + label: "Жанры", + value: { include: [], exclude: [] }, + options: [ + { label: "Арт", value: "32" }, + { label: "Безумие", value: "91" }, + { label: "Боевик", value: "34" }, + { label: "Боевые искусства", value: "35" }, + { label: "Вампиры", value: "36" }, + { label: "Военное", value: "89" }, + { label: "Гарем", value: "37" }, + { label: "Гендерная интрига", value: "38" }, + { label: "Героическое фэнтези", value: "39" }, + { label: "Демоны", value: "81" }, + { label: "Детектив", value: "40" }, + { label: "Детское", value: "88" }, + { label: "Дзёсэй", value: "41" }, + { label: "Драма", value: "43" }, + { label: "Игра", value: "44" }, + { label: "Исекай", value: "79" }, + { label: "История", value: "45" }, + { label: "Киберпанк", value: "46" }, + { label: "Кодомо", value: "76" }, + { label: "Комедия", value: "47" }, + { label: "Космос", value: "83" }, + { label: "Магия", value: "85" }, + { label: "Махо-сёдзё", value: "48" }, + { label: "Машины", value: "90" }, + { label: "Меха", value: "49" }, + { label: "Мистика", value: "50" }, + { label: "Музыка", value: "80" }, + { label: "Научная фантастика", value: "51" }, + { label: "Омегаверс", value: "77" }, + { label: "Пародия", value: "86" }, + { label: "Повседневность", value: "52" }, + { label: "Полиция", value: "82" }, + { label: "Постапокалиптика", value: "53" }, + { label: "Приключения", value: "54" }, + { label: "Психология", value: "55" }, + { label: "Романтика", value: "56" }, + { label: "Самурайский боевик", value: "57" }, + { label: "Сверхъестественное", value: "58" }, + { label: "Сёдзё", value: "59" }, + { label: "Сёдзё-ай", value: "60" }, + { label: "Сёнэн", value: "61" }, + { label: "Сёнэн-ай", value: "62" }, + { label: "Спорт", value: "63" }, + { label: "Супер сила", value: "87" }, + { label: "Сэйнэн", value: "64" }, + { label: "Трагедия", value: "65" }, + { label: "Триллер", value: "66" }, + { label: "Ужасы", value: "67" }, + { label: "Фантастика", value: "68" }, + { label: "Фэнтези", value: "69" }, + { label: "Школа", value: "70" }, + { label: "Эротика", value: "71" }, + { label: "Этти", value: "72" }, + { label: "Юри", value: "73" }, + { label: "Яой", value: "74" }, + ], + type: FilterTypes.ExcludableCheckboxGroup, + }, + tags: { + label: "Теги", + value: { include: [], exclude: [] }, + options: [ + { label: "Авантюристы", value: "328" }, + { label: "Антигерой", value: "176" }, + { label: "Бессмертные", value: "333" }, + { label: "Боги", value: "218" }, + { label: "Борьба за власть", value: "309" }, + { label: "Брат и сестра", value: "360" }, + { label: "Ведьма", value: "339" }, + { label: "Видеоигры", value: "204" }, + { label: "Виртуальная реальность", value: "214" }, + { label: "Владыка демонов", value: "349" }, + { label: "Военные", value: "198" }, + { label: "Воспоминания из другого мира", value: "310" }, + { label: "Выживание", value: "212" }, + { label: "ГГ женщина", value: "294" }, + { label: "ГГ имба", value: "292" }, + { label: "ГГ мужчина", value: "295" }, + { label: "ГГ не ояш", value: "325" }, + { label: "ГГ не человек", value: "331" }, + { label: "ГГ ояш", value: "326" }, + { label: "Главный герой бог", value: "324" }, + { label: "Глупый ГГ", value: "298" }, + { label: "Горничные", value: "171" }, + { label: "Гуро", value: "306" }, + { label: "Гяру", value: "197" }, + { label: "Демоны", value: "157" }, + { label: "Драконы", value: "313" }, + { label: "Древний мир", value: "317" }, + { label: "Зверолюди", value: "163" }, + { label: "Зомби", value: "155" }, + { label: "Исторические фигуры", value: "323" }, + { label: "Кулинария", value: "158" }, + { label: "Культивация", value: "161" }, + { label: "ЛГБТ", value: "344" }, + { label: "ЛитРПГ", value: "319" }, + { label: "Лоли", value: "206" }, + { label: "Магия", value: "170" }, + { label: "Машинный перевод", value: "345" }, + { label: "Медицина", value: "159" }, + { label: "Межгалактическая война", value: "330" }, + { label: "Монстр Девушки", value: "207" }, + { label: "Монстры", value: "208" }, + { label: "Мрачный мир", value: "316" }, + { label: "Музыка", value: "358" }, + { label: "Музыка", value: "209" }, + { label: "Ниндзя", value: "199" }, + { label: "Обратный Гарем", value: "210" }, + { label: "Офисные Работники", value: "200" }, + { label: "Пираты", value: "341" }, + { label: "Подземелья", value: "314" }, + { label: "Политика", value: "311" }, + { label: "Полиция", value: "201" }, + { label: "Преступники / Криминал", value: "205" }, + { label: "Призраки / Духи", value: "196" }, + { label: "Призыватели", value: "329" }, + { label: "Прыжки между мирами", value: "321" }, + { label: "Путешествие в другой мир", value: "318" }, + { label: "Путешествие во времени", value: "213" }, + { label: "Рабы", value: "355" }, + { label: "Ранги силы", value: "312" }, + { label: "Реинкарнация", value: "154" }, + { label: "Самураи", value: "202" }, + { label: "Скрытие личности", value: "315" }, + { label: "Средневековье", value: "174" }, + { label: "Традиционные игры", value: "203" }, + { label: "Умный ГГ", value: "303" }, + { label: "Характерный рост", value: "332" }, + { label: "Хикикомори", value: "167" }, + { label: "Эволюция", value: "322" }, + { label: "Элементы РПГ", value: "327" }, + { label: "Эльфы", value: "217" }, + { label: "Якудза", value: "165" }, + ], + type: FilterTypes.ExcludableCheckboxGroup, + }, + } satisfies Filters; +} + +export default new RLIB(); + +interface responseBook { + hasStickyPermission: boolean; + bookmark?: null; + auth: boolean; + comments_version: string; + manga: Manga; + chapters: Chapters; + user?: User; +} +interface Manga { + id: number; + name: string; + rusName?: string; + rus_name?: string; + engName?: string; + slug: string; + status: number; + chapters_count: number; + altNames?: string[] | null; + coverImage?: string; + href?: string; +} +interface Chapters { + list?: ListEntity[] | null; + teams?: TeamsEntity[] | null; + branches?: BranchesEntity[] | null; + is_paid?: null[] | null; +} +interface ListEntity { + chapter_id: number; + chapter_slug: string; + chapter_name: string; + chapter_number: string; + chapter_volume: number; + chapter_moderated: number; + chapter_user_id: number; + chapter_expired_at: string; + chapter_scanlator_id: number; + chapter_created_at: string; + status?: null; + price: number; + branch_id: number; + username: string; +} +interface TeamsEntity { + name: string; + alt_name: string; + cover: string; + slug: string; + id: number; + branch_id: number; + sale: number; + href: string; + pivot: Pivot; +} +interface Pivot { + manga_id: number; + team_id: number; +} +interface BranchesEntity { + id: number; + manga_id: number; + name: string; + teams?: TeamsEntity1[] | null; + is_subscribed: boolean; +} +interface TeamsEntity1 { + id: number; + name: string; + slug: string; + cover: string; + branch_id: number; + is_active: number; +} +interface User { + id: number; + avatar: string; + access: boolean; + isAdmin: boolean; + paid: boolean; +} diff --git a/plugins/russian/ranoberf.ts b/plugins/russian/ranoberf.ts index cdfa58c..504dda7 100644 --- a/plugins/russian/ranoberf.ts +++ b/plugins/russian/ranoberf.ts @@ -1,409 +1,408 @@ -import { Plugin } from "@typings/plugin"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { defaultCover } from "@libs/defaultCover"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { NovelStatus } from "@libs/novelStatus"; -import { load as parseHTML } from "cheerio"; -import dayjs from "dayjs"; - -class RNRF implements Plugin.PluginBase { - id = "RNRF"; - name = "РанобэРФ"; - site = "https://ранобэ.рф"; - version = "1.0.0"; - icon = "src/ru/ranoberf/icon.png"; - userAgent = ""; - cookieString = ""; - - async popularNovels( - pageNo: number, - { showLatestNovels, filters }: Plugin.PopularNovelsOptions, - ): Promise { - let url = this.site + "/books?order="; - url += showLatestNovels - ? "lastPublishedChapter" - : filters?.sort?.value || "popular"; - url += "&page=" + pageNo; - - const result = await fetchApi(url); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - const jsonRaw = loadedCheerio("#__NEXT_DATA__").html(); - const json: response = JSON.parse(jsonRaw || "{}"); - - const novels: Plugin.NovelItem[] = []; - json.props.pageProps?.totalData?.items?.forEach((novel) => - novels.push({ - name: novel.title, - cover: novel?.verticalImage?.url - ? this.site + novel.verticalImage.url - : defaultCover, - url: this.site + "/" + novel.slug, - }), - ); - - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const result = await fetchApi(novelUrl); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - const jsonRaw = loadedCheerio("#__NEXT_DATA__").html(); - const book = (JSON.parse(jsonRaw || "{}") as response).props.pageProps.book; - - const novel: Plugin.SourceNovel = { - url: novelUrl, - name: book?.title, - cover: book?.verticalImage?.url - ? this.site + book.verticalImage.url - : defaultCover, - summary: book?.description, - author: book?.author || "", - genres: book?.genres.map((item) => item.title).join(", "), - status: book?.additionalInfo.includes("Активен") - ? NovelStatus.Ongoing - : NovelStatus.Completed, - }; - - const chapters: Plugin.ChapterItem[] = []; - - book?.chapters?.forEach((chapter) => { - if (!chapter.isDonate || chapter.isUserPaid) { - chapters.push({ - name: chapter.title, - releaseTime: dayjs(chapter.publishedAt).format("LLL"), - url: this.site + chapter.url, - }); - } - }); - - novel.chapters = chapters.reverse(); - return novel; - } - - async parseChapter(chapterUrl: string): Promise { - const result = await fetchApi(chapterUrl); - const body = await result.text(); - - let loadedCheerio = parseHTML(body); - const jsonRaw = loadedCheerio("#__NEXT_DATA__").html(); - const json: response = JSON.parse(jsonRaw || "{}"); - - loadedCheerio = parseHTML( - json.props.pageProps?.chapter?.content?.text || "", - ); - - const baseUrl = this.site; - loadedCheerio("img").each(function () { - if (!loadedCheerio(this).attr("src")?.startsWith("http")) { - const src = loadedCheerio(this).attr("src"); - loadedCheerio(this).attr("src", baseUrl + src); - } - }); - - const chapterText = loadedCheerio.html(); - return chapterText; - } - - async searchNovels( - searchTerm: string, - //pageNo: number | undefined = 1, - ): Promise { - const url = `${this.site}/v3/books?filter[or][0][title][like]=${searchTerm}&filter[or][1][titleEn][like]=${searchTerm}&filter[or][2][fullTitle][like]=${searchTerm}&filter[status][]=active&filter[status][]=abandoned&filter[status][]=completed&expand=verticalImage`; - const result = await fetchApi(url); - const body = (await result.json()) as { items: Item[] }; - const novels: Plugin.NovelItem[] = []; - - body.items.forEach((novel) => - novels.push({ - name: novel.title, - cover: novel?.verticalImage?.url - ? this.site + novel.verticalImage.url - : defaultCover, - url: this.site + "/" + novel.slug, - }), - ); - - return novels; - } - fetchImage = fetchFile; - - filters = { - sort: { - label: "Сортировка", - value: "", - options: [ - { label: "Рейтинг", value: "popular" }, - { label: "Дате добавления", value: "new" }, - { label: "Дате обновления", value: "lastPublishedChapter" }, - { label: "Законченные", value: "completed" }, - ], - type: FilterTypes.Picker, - }, - } satisfies Filters; -} -export default new RNRF(); - -interface response { - props: Props; - page: string; - query: Query; - buildId: string; - isFallback: boolean; - gssp: boolean; - appGip: boolean; - dynamicIds?: string[]; -} - -interface Props { - pageProps: PageProps; - isMobile: boolean; - theme: string; - __N_SSP: boolean; -} - -interface PageProps { - initialReduxState: InitialReduxState; - book?: PagePropsBook; - totalData?: TotalData; - layoutViewCookies?: string; - displayBanner?: boolean; - chapter?: PagePropsChapter; -} - -interface PagePropsBook { - id: number; - countryId: number; - creatorId: number; - lastChapterId: number; - sourceId: number; - imgVerticalId: number; - imgHorizontalId: number; - imgSquareId: number; - imageVerticalId: number; - imageHorizontalId: number; - title: string; - titleEn: string; - fullTitle: string; - fullTitleEn: string; - description: string; - slug: string; - author: string; - additionalInfo: string; - importantInfo: string; - status: string; - views: number; - likes: number; - dislikes: number; - intervalSec: number; - intervalForNextFreeChapterSec: number; - chapterCost: number; - chapterPrice: number; - chapterPriceCollected: number; - maxSubscriptionChapters: number; - maxDonateChapters: number; - isDisplayOnMainPage: boolean; - withoutSchedule: boolean; - nextFreeChapterPublishedAt: null; - createdAt: Date; - updatedAt: Date; - userRating: null; - userBookmark: null; - country: Country; - genres: Genre[]; - timer: Timer; - chapters: ChapterElement[]; - verticalImage: Image; - horizontalImage: Image; - squareImage: Image; -} - -interface ChapterElement { - id: number; - bookId: number; - title: string; - url: string; - numberChapter: string; - tom: number | null; - chapterShortNumber: number | null; - isDonate: boolean; - isSponsored: boolean; - isSubscription: boolean; - isEdited: boolean; - publishedAt: Date; - price: number; - views: number; - isUserPaid?: boolean; -} - -interface Country { - id: number; - title: string; - code: string; -} - -interface Genre { - id: number; - title: string; - slug: string; -} - -interface Image { - id: number; - alt: string; - path: string; - name: string; - url: string; -} - -interface Timer { - value: null; - for: null; - message: string; -} - -interface PagePropsChapter { - id: number; - bookId: number; - editorId: null; - translatorId: number; - parserChapterId: null; - title: string; - slug: string; - status: string; - tom: null; - views: number; - isSponsored: boolean; - isSubscription: boolean; - isDonate: boolean; - isIndexing: boolean; - publishedAt: Date; - changeAvailabilityAt: null; - createdAt: Date; - updatedAt: Date; - numberChapter: string; - price: number; - content: Content; - book: ChapterBook; - userBookmark: null; - isUserPaid: boolean; - nextChapter: null; - previousChapter: PreviousChapter; - corrections: null; -} - -interface ChapterBook { - id: number; - title: string; - titleEn: string; - url: string; -} - -interface Content { - id: number; - text: string; - symbolsCount: number; -} - -interface PreviousChapter { - id: number; - title: string; - views: number; - publishedAt: Date; - url: string; - numberChapter: string; -} - -interface InitialReduxState { - data?: Data; - auth: Auth; - banners?: Banners; - corrections?: Corrections; - readerSettings?: ReaderSettings; -} - -interface Auth { - isAuth: boolean; - user: null; -} - -interface Banners { - displaySale: boolean; -} - -interface Corrections { - items: null; - data: null; -} - -interface Data { - bookPage: BookPage | null; - chapterPage: ChapterPage | null; -} - -interface BookPage { - bookId: number; - bookmark: null; -} - -interface ChapterPage { - chapterId: number; - bookId: number; - bookmark: null; -} - -interface ReaderSettings { - fontSize: number; - lineHeight: number; - textIndent: number; - sidebarHide: number; - textAlign: string; -} - -interface TotalData { - items: Item[]; - _links: Links; - pagesData: PagesData; -} - -interface Links { - self: First; - first: First; - last: First; - next: First; -} - -interface First { - href: string; -} - -interface Item { - id: number; - title: string; - description: string; - slug: string; - url: string; - status: string; - dislikes: number; - likes: number; - userRating: null; - lastChapter: ChapterElement; - verticalImage: Image; - horizontalImage: Image; - squareImage: Image; -} - -interface PagesData { - totalCount: number; - pageCount: number; - currentPage: number; - perPage: number; -} - -interface Query { - book?: string; - chapter?: string; -} +import { Plugin } from "@typings/plugin"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { defaultCover } from "@libs/defaultCover"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { NovelStatus } from "@libs/novelStatus"; +import { load as parseHTML } from "cheerio"; +import dayjs from "dayjs"; + +class RNRF implements Plugin.PluginBase { + id = "RNRF"; + name = "РанобэРФ"; + site = "https://ранобэ.рф"; + version = "1.0.0"; + icon = "src/ru/ranoberf/icon.png"; + userAgent = ""; + + async popularNovels( + pageNo: number, + { showLatestNovels, filters }: Plugin.PopularNovelsOptions, + ): Promise { + let url = this.site + "/books?order="; + url += showLatestNovels + ? "lastPublishedChapter" + : filters?.sort?.value || "popular"; + url += "&page=" + pageNo; + + const result = await fetchApi(url); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + const jsonRaw = loadedCheerio("#__NEXT_DATA__").html(); + const json: response = JSON.parse(jsonRaw || "{}"); + + const novels: Plugin.NovelItem[] = []; + json.props.pageProps?.totalData?.items?.forEach((novel) => + novels.push({ + name: novel.title, + cover: novel?.verticalImage?.url + ? this.site + novel.verticalImage.url + : defaultCover, + url: this.site + "/" + novel.slug, + }), + ); + + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const result = await fetchApi(novelUrl); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + const jsonRaw = loadedCheerio("#__NEXT_DATA__").html(); + const book = (JSON.parse(jsonRaw || "{}") as response).props.pageProps.book; + + const novel: Plugin.SourceNovel = { + url: novelUrl, + name: book?.title, + cover: book?.verticalImage?.url + ? this.site + book.verticalImage.url + : defaultCover, + summary: book?.description, + author: book?.author || "", + genres: book?.genres.map((item) => item.title).join(", "), + status: book?.additionalInfo.includes("Активен") + ? NovelStatus.Ongoing + : NovelStatus.Completed, + }; + + const chapters: Plugin.ChapterItem[] = []; + + book?.chapters?.forEach((chapter) => { + if (!chapter.isDonate || chapter.isUserPaid) { + chapters.push({ + name: chapter.title, + releaseTime: dayjs(chapter.publishedAt).format("LLL"), + url: this.site + chapter.url, + }); + } + }); + + novel.chapters = chapters.reverse(); + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const result = await fetchApi(chapterUrl); + const body = await result.text(); + + let loadedCheerio = parseHTML(body); + const jsonRaw = loadedCheerio("#__NEXT_DATA__").html(); + const json: response = JSON.parse(jsonRaw || "{}"); + + loadedCheerio = parseHTML( + json.props.pageProps?.chapter?.content?.text || "", + ); + + const baseUrl = this.site; + loadedCheerio("img").each(function () { + if (!loadedCheerio(this).attr("src")?.startsWith("http")) { + const src = loadedCheerio(this).attr("src"); + loadedCheerio(this).attr("src", baseUrl + src); + } + }); + + const chapterText = loadedCheerio.html(); + return chapterText; + } + + async searchNovels( + searchTerm: string, + //pageNo: number | undefined = 1, + ): Promise { + const url = `${this.site}/v3/books?filter[or][0][title][like]=${searchTerm}&filter[or][1][titleEn][like]=${searchTerm}&filter[or][2][fullTitle][like]=${searchTerm}&filter[status][]=active&filter[status][]=abandoned&filter[status][]=completed&expand=verticalImage`; + const result = await fetchApi(url); + const body = (await result.json()) as { items: Item[] }; + const novels: Plugin.NovelItem[] = []; + + body.items.forEach((novel) => + novels.push({ + name: novel.title, + cover: novel?.verticalImage?.url + ? this.site + novel.verticalImage.url + : defaultCover, + url: this.site + "/" + novel.slug, + }), + ); + + return novels; + } + fetchImage = fetchFile; + + filters = { + sort: { + label: "Сортировка", + value: "", + options: [ + { label: "Рейтинг", value: "popular" }, + { label: "Дате добавления", value: "new" }, + { label: "Дате обновления", value: "lastPublishedChapter" }, + { label: "Законченные", value: "completed" }, + ], + type: FilterTypes.Picker, + }, + } satisfies Filters; +} +export default new RNRF(); + +interface response { + props: Props; + page: string; + query: Query; + buildId: string; + isFallback: boolean; + gssp: boolean; + appGip: boolean; + dynamicIds?: string[]; +} + +interface Props { + pageProps: PageProps; + isMobile: boolean; + theme: string; + __N_SSP: boolean; +} + +interface PageProps { + initialReduxState: InitialReduxState; + book?: PagePropsBook; + totalData?: TotalData; + layoutViewCookies?: string; + displayBanner?: boolean; + chapter?: PagePropsChapter; +} + +interface PagePropsBook { + id: number; + countryId: number; + creatorId: number; + lastChapterId: number; + sourceId: number; + imgVerticalId: number; + imgHorizontalId: number; + imgSquareId: number; + imageVerticalId: number; + imageHorizontalId: number; + title: string; + titleEn: string; + fullTitle: string; + fullTitleEn: string; + description: string; + slug: string; + author: string; + additionalInfo: string; + importantInfo: string; + status: string; + views: number; + likes: number; + dislikes: number; + intervalSec: number; + intervalForNextFreeChapterSec: number; + chapterCost: number; + chapterPrice: number; + chapterPriceCollected: number; + maxSubscriptionChapters: number; + maxDonateChapters: number; + isDisplayOnMainPage: boolean; + withoutSchedule: boolean; + nextFreeChapterPublishedAt: null; + createdAt: Date; + updatedAt: Date; + userRating: null; + userBookmark: null; + country: Country; + genres: Genre[]; + timer: Timer; + chapters: ChapterElement[]; + verticalImage: Image; + horizontalImage: Image; + squareImage: Image; +} + +interface ChapterElement { + id: number; + bookId: number; + title: string; + url: string; + numberChapter: string; + tom: number | null; + chapterShortNumber: number | null; + isDonate: boolean; + isSponsored: boolean; + isSubscription: boolean; + isEdited: boolean; + publishedAt: Date; + price: number; + views: number; + isUserPaid?: boolean; +} + +interface Country { + id: number; + title: string; + code: string; +} + +interface Genre { + id: number; + title: string; + slug: string; +} + +interface Image { + id: number; + alt: string; + path: string; + name: string; + url: string; +} + +interface Timer { + value: null; + for: null; + message: string; +} + +interface PagePropsChapter { + id: number; + bookId: number; + editorId: null; + translatorId: number; + parserChapterId: null; + title: string; + slug: string; + status: string; + tom: null; + views: number; + isSponsored: boolean; + isSubscription: boolean; + isDonate: boolean; + isIndexing: boolean; + publishedAt: Date; + changeAvailabilityAt: null; + createdAt: Date; + updatedAt: Date; + numberChapter: string; + price: number; + content: Content; + book: ChapterBook; + userBookmark: null; + isUserPaid: boolean; + nextChapter: null; + previousChapter: PreviousChapter; + corrections: null; +} + +interface ChapterBook { + id: number; + title: string; + titleEn: string; + url: string; +} + +interface Content { + id: number; + text: string; + symbolsCount: number; +} + +interface PreviousChapter { + id: number; + title: string; + views: number; + publishedAt: Date; + url: string; + numberChapter: string; +} + +interface InitialReduxState { + data?: Data; + auth: Auth; + banners?: Banners; + corrections?: Corrections; + readerSettings?: ReaderSettings; +} + +interface Auth { + isAuth: boolean; + user: null; +} + +interface Banners { + displaySale: boolean; +} + +interface Corrections { + items: null; + data: null; +} + +interface Data { + bookPage: BookPage | null; + chapterPage: ChapterPage | null; +} + +interface BookPage { + bookId: number; + bookmark: null; +} + +interface ChapterPage { + chapterId: number; + bookId: number; + bookmark: null; +} + +interface ReaderSettings { + fontSize: number; + lineHeight: number; + textIndent: number; + sidebarHide: number; + textAlign: string; +} + +interface TotalData { + items: Item[]; + _links: Links; + pagesData: PagesData; +} + +interface Links { + self: First; + first: First; + last: First; + next: First; +} + +interface First { + href: string; +} + +interface Item { + id: number; + title: string; + description: string; + slug: string; + url: string; + status: string; + dislikes: number; + likes: number; + userRating: null; + lastChapter: ChapterElement; + verticalImage: Image; + horizontalImage: Image; + squareImage: Image; +} + +interface PagesData { + totalCount: number; + pageCount: number; + currentPage: number; + perPage: number; +} + +interface Query { + book?: string; + chapter?: string; +} diff --git a/plugins/russian/renovels.ts b/plugins/russian/renovels.ts index bb156cf..83b070a 100644 --- a/plugins/russian/renovels.ts +++ b/plugins/russian/renovels.ts @@ -1,1106 +1,1105 @@ -import { Plugin } from "@typings/plugin"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { NovelStatus } from "@libs/novelStatus"; -import dayjs from "dayjs"; - -class ReN implements Plugin.PluginBase { - id = "ReN"; - name = "Renovels"; - site = "https://renovels.org"; - version = "1.0.0"; - icon = "src/ru/renovels/icon.png"; - userAgent = ""; - cookieString = ""; - - async popularNovels( - pageNo: number, - { - showLatestNovels, - filters, - }: Plugin.PopularNovelsOptions, - ): Promise { - let url = this.site + "/api/search/catalog/?count=30&ordering="; - url += filters?.order?.value - ? (filters.order.value as string).replace("+", "") - : "-"; - url += showLatestNovels ? "chapter_date" : filters?.sort?.value || "rating"; - - if (filters?.genres?.value?.include?.length) { - url += filters.genres.value.include.map((i) => "&genres=" + i).join(""); - } - - if (filters?.genres?.value?.exclude?.length) { - url += filters.genres.value.exclude - .map((i) => "&exclude_genres=" + i).join(""); - } - - if (filters?.status?.value?.length) { - url += filters.status.value.map((i) => "&status=" + i).join(""); - } - - if (filters?.types?.value?.length) { - url += filters.types.value.map((i) => "&types=" + i).join(""); - } - - if (filters?.categories?.value?.include?.length) { - url += filters.categories.value.include - .map((i) => "&categories=" + i).join(""); - } - - if (filters?.categories?.value?.exclude?.length) { - url += filters.categories.value.exclude - .map((i) => "&exclude_categories=" + i).join(""); - } - - if (filters?.age_limit?.value?.length) { - url += filters.age_limit.value.map((i) => "&age_limit=" + i).join(""); - } - - url += "&page=" + pageNo; - - const result = await fetchApi(url); - const body = (await result.json()) as { content: responseNovels[] }; - - const novels: Plugin.NovelItem[] = []; - - body.content.forEach((novel) => - novels.push({ - name: novel.main_name || novel.secondary_name, - cover: this.site + (novel.img?.high || novel.img?.mid || novel.img.low), - url: this.site + "/novel/" + novel.dir, - }), - ); - - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const novelID = novelUrl.split("/")[4]; - const result = await fetchApi(this.site + "/api/titles/" + novelID); - const body = (await result.json()) as { content: responseNovel }; - - const novel: Plugin.SourceNovel = { - url: novelUrl, - name: body.content.main_name || body.content.secondary_name, - summary: body.content.description, - cover: - this.site + - (body.content.img?.high || - body.content.img?.mid || - body.content.img.low), - status: - body.content.status.name === "Продолжается" - ? NovelStatus.Ongoing - : NovelStatus.Completed, - }; - - const tags = [body.content?.genres, body.content?.categories] - .flat() - .map((tags) => tags?.name) - .filter((tags) => tags); - - if (tags.length) { - novel.genres = tags.join(","); - } - - const all = Math.floor(body.content.count_chapters / 100 + 1); - const branch_id = body.content.branches?.[0]?.id || body.content.id; - const chapters: Plugin.ChapterItem[] = []; - - for (let i = 0; i < all; i++) { - const chapterResult = await fetchApi( - this.site + - "/api/titles/chapters/?branch_id=" + - branch_id + - "&count=100&page=" + - (i + 1), - ); - const volumes = (await chapterResult.json()) as { - content: responseСhapters[]; - }; - - volumes.content.forEach((chapter) => { - if (!chapter.is_paid || chapter.is_bought) { - chapters.push({ - name: `Том ${chapter.tome} Глава ${chapter.chapter} ${chapter.name}`?.trim(), - releaseTime: dayjs(chapter.upload_date).format("LLL"), - url: `${this.site}/novel/${novelID}/${chapter.id}/`, - }); - } - }); - } - - novel.chapters = chapters.reverse(); - return novel; - } - - async parseChapter(chapterUrl: string): Promise { - const url = this.site + "/api/titles/chapters/" + chapterUrl.split("/")[5]; - const result = await fetchApi(url); - const body = (await result.json()) as { content: responseСhapter }; - - return body.content.content; - } - - async searchNovels( - searchTerm: string, - pageNo: number | undefined = 1, - ): Promise { - const url = `${this.site}/api/search/?query=${searchTerm}&count=100&field=titles&page=${pageNo}`; - const result = await fetchApi(url); - const body = (await result.json()) as { content: responseNovels[] }; - const novels: Plugin.NovelItem[] = []; - - body.content.forEach((novel) => - novels.push({ - name: novel.main_name || novel.secondary_name, - cover: this.site + (novel.img?.high || novel.img?.mid || novel.img.low), - url: this.site + "/novel/" + novel.dir, - }), - ); - - return novels; - } - - fetchImage = fetchFile; - - filters = { - sort: { - label: "Сортировка", - value: "", - options: [ - { label: "Рейтинг", value: "rating" }, - { label: "Просмотры", value: "views" }, - { label: "Лайкам", value: "votes" }, - { label: "Дате добавления", value: "id" }, - { label: "Дате обновления", value: "chapter_date" }, - { label: "Количество глав", value: "count_chapters" }, - ], - type: FilterTypes.Picker, - }, - order: { - label: "Порядок", - value: "", - options: [ - { label: "По убыванию", value: "-" }, - { label: "По возрастанию", value: "+" }, - ], - type: FilterTypes.Picker, - }, - genres: { - label: "Жанры:", - value: { include: [], exclude: [] }, - options: [ - { label: "Боевик", value: "112" }, - { label: "Война", value: "123" }, - { label: "Детектив", value: "114" }, - { label: "Драма", value: "125" }, - { label: "Историческая проза", value: "115" }, - { label: "ЛитРПГ", value: "109" }, - { label: "Любовные романы", value: "116" }, - { label: "Мистика", value: "103" }, - { label: "Научная фантастика", value: "121" }, - { label: "Повседневность", value: "128" }, - { label: "Подростковая проза", value: "113" }, - { label: "Политический роман", value: "119" }, - { label: "Попаданцы", value: "108" }, - { label: "Поэзия", value: "105" }, - { label: "Разное", value: "111" }, - { label: "РеалРПГ", value: "117" }, - { label: "Романтика", value: "126" }, - { label: "Современная проза", value: "102" }, - { label: "Современная фантастика", value: "127" }, - { label: "Спорт", value: "122" }, - { label: "Трагедия", value: "129" }, - { label: "Триллер", value: "110" }, - { label: "Триллер и саспенс", value: "124" }, - { label: "Ужасы", value: "106" }, - { label: "Фантастика", value: "101" }, - { label: "Фанфик", value: "107" }, - { label: "Фьюжн роман", value: "120" }, - { label: "Фэнтези", value: "100" }, - { label: "Эротика", value: "118" }, - { label: "Юмор", value: "104" }, - ], - type: FilterTypes.ExcludableCheckboxGroup, - }, - categories: { - label: "Тэги:", - value: { include: [], exclude: [] }, - options: [ - { label: "[Награжденная работа]", value: "648" }, - { label: "18+", value: "423" }, - { label: "Абьюзеры", value: "663" }, - { label: "Авантюристы", value: "436" }, - { label: "Автоматоны", value: "593" }, - { label: "Агрессивные персонажи", value: "502" }, - { label: "Ад", value: "569" }, - { label: "Адаптация в радиопостановку", value: "581" }, - { label: "Академия", value: "153" }, - { label: "Актеры озвучки", value: "627" }, - { label: "Активный главный герой", value: "251" }, - { label: "Алхимия", value: "233" }, - { label: "Альтернативная история", value: "147" }, - { label: "Альтернативный мир", value: "156" }, - { label: "Амнезия/Потеря памяти", value: "344" }, - { label: "Анабиоз", value: "685" }, - { label: "Ангелы", value: "183" }, - { label: "Андрогинные персонажи", value: "321" }, - { label: "Андроиды", value: "203" }, - { label: "Анти-магия", value: "533" }, - { label: "Антигерой", value: "432" }, - { label: "Антикварный магазин", value: "623" }, - { label: "Антисоциальный главный герой", value: "613" }, - { label: "Антиутопия", value: "142" }, - { label: "Апатичный протагонист", value: "157" }, - { label: "Апокалипсис", value: "402" }, - { label: "Аранжированный брак", value: "375" }, - { label: "Армия", value: "639" }, - { label: "Артефакты", value: "234" }, - { label: "Артисты", value: "523" }, - { label: "Банды", value: "630" }, - { label: "БДСМ", value: "699" }, - { label: "Бедный главный герой", value: "398" }, - { label: "Безжалостный главный герой", value: "261" }, - { label: "Беззаботный главный герой", value: "438" }, - { label: "Безусловная любовь", value: "679" }, - { label: "Беременность", value: "250" }, - { label: "Бесполый главный герой", value: "325" }, - { label: "Бессмертные", value: "368" }, - { label: "Бесстрашный протагонист", value: "656" }, - { label: "Бесстыдный главный герой", value: "352" }, - { label: "Бесчестный главный герой", value: "716" }, - { label: "Библиотека", value: "428" }, - { label: "Бизнес-литература", value: "121" }, - { label: "Бизнесмен", value: "769" }, - { label: "Биочип", value: "238" }, - { label: "Бисексуальный главный герой", value: "773" }, - { label: "Близнецы", value: "263" }, - { label: "Боги", value: "317" }, - { label: "Богини", value: "439" }, - { label: "Боевая академия", value: "450" }, - { label: "Боевая фантастика", value: "149" }, - { label: "Боевое фэнтези", value: "104" }, - { label: "Боевые духи", value: "433" }, - { label: "Боевые соревнования", value: "493" }, - { label: "Божественная защита", value: "421" }, - { label: "Божественные силы", value: "327" }, - { label: "Борьба за власть", value: "599" }, - { label: "Брак", value: "446" }, - { label: "Брак по расчету", value: "188" }, - { label: "Братский комплекс", value: "159" }, - { label: "Братство", value: "487" }, - { label: "Братья и сестры", value: "577" }, - { label: "Буддизм", value: "745" }, - { label: "Быстрая культивация", value: "366" }, - { label: "Быстрообучаемый", value: "324" }, - { label: "Валькирии", value: "692" }, - { label: "Вампиры", value: "360" }, - { label: "Ваншот", value: "702" }, - { label: "Ведьмы", value: "281" }, - { label: "Вежливый главный герой", value: "379" }, - { label: "Верные подчиненные", value: "328" }, - { label: "Взрослый главный герой", value: "295" }, - { label: "Видит то, чего не видят другие", value: "671" }, - { label: "Виртуальная реальность", value: "401" }, - { label: "Владелец магазина", value: "681" }, - { label: "Внезапная сила", value: "455" }, - { label: "Внезапное богатство", value: "765" }, - { label: "Внешний вид отличается от факт", value: "419" }, - { label: "Военные Летописи", value: "743" }, - { label: "Возвращение из другого мира", value: "696" }, - { label: "Войны", value: "181" }, - { label: "Вокалоид", value: "701" }, - { label: "Волшебники/Волшебницы", value: "539" }, - { label: "Волшебные звери", value: "308" }, - { label: "Воображаемый друг", value: "651" }, - { label: "Воры", value: "414" }, - { label: "Воскрешение", value: "199" }, - { label: "Враги становятся возлюбленными", value: "498" }, - { label: "Враги становятся союзниками", value: "562" }, - { label: "Врата в другой мир", value: "609" }, - { label: "Врачи", value: "376" }, - { label: "Временной парадокс", value: "275" }, - { label: "Всемогущий главный герой", value: "169" }, - { label: "Вторжение на землю", value: "198" }, - { label: "Второй шанс", value: "231" }, - { label: "Вуайеризм", value: "790" }, - { label: "Выживание", value: "380" }, - { label: "Высокомерные персонажи", value: "361" }, - { label: "Гадание", value: "595" }, - { label: "Гарем рабов", value: "777" }, - { label: "Гг - женщина", value: "186" }, - { label: "Гг - мужчина", value: "166" }, - { label: "Гг силен с самого начала", value: "171" }, - { label: "Геймеры", value: "392" }, - { label: "Генералы", value: "326" }, - { label: "Генетические модификации", value: "657" }, - { label: "Гениальный главный герой", value: "617" }, - { label: "Герои", value: "285" }, - { label: "Героиня — сорванец", value: "583" }, - { label: "Героическая фантастика", value: "140" }, - { label: "Героическое фэнтези", value: "107" }, - { label: "Герой влюбляется первым", value: "187" }, - { label: "Гетерохромия", value: "570" }, - { label: "Гильдии", value: "411" }, - { label: "Гипнотизм", value: "755" }, - { label: "Главный герой — бог", value: "547" }, - { label: "Главный герой — гуманоид", value: "636" }, - { label: "Главный герой — наполовину чел", value: "445" }, - { label: "Главный герой — отец", value: "792" }, - { label: "Главный герой — раб", value: "780" }, - { label: "Главный герой — ребенок", value: "488" }, - { label: "Главный герой — рубака", value: "476" }, - { label: "Главный герой влюбляется первы", value: "683" }, - { label: "Главный герой играет роль", value: "472" }, - { label: "Главный герой носит очки", value: "672" }, - { label: "Главный герой пацифист", value: "698" }, - { label: "Гладиаторы", value: "603" }, - { label: "Глуповатый главный герой", value: "385" }, - { label: "Гоблины", value: "586" }, - { label: "Големы", value: "620" }, - { label: "Гомункул", value: "789" }, - { label: "Горничные", value: "459" }, - { label: "Городское фэнтези", value: "105" }, - { label: "Госпиталь", value: "800" }, - { label: "Готовка", value: "302" }, - { label: "Гриндинг", value: "393" }, - { label: "Дао Компаньон", value: "462" }, - { label: "Даосизм", value: "760" }, - { label: "Дварфы", value: "323" }, - { label: "Двойная личность", value: "642" }, - { label: "Двойник", value: "601" }, - { label: "Дворецкий", value: "660" }, - { label: "Дворяне", value: "168" }, - { label: "Дворянство/Аристократия", value: "437" }, - { label: "Девушки-монстры", value: "669" }, - { label: "Демоническая техника культивац", value: "722" }, - { label: "Демоны", value: "152" }, - { label: "Денежный долг", value: "783" }, - { label: "Депрессия", value: "555" }, - { label: "Детектив", value: "116" }, - { label: "Детективы", value: "612" }, - { label: "Детская литература", value: "123" }, - { label: "Дискриминация", value: "162" }, - { label: "Добыча денег в приоритете", value: "396" }, - { label: "Документальная проза", value: "122" }, - { label: "Долгая разлука", value: "307" }, - { label: "Домашние дела", value: "290" }, - { label: "Домогательство", value: "470" }, - { label: "Драконы", value: "303" }, - { label: "Драконьи всадники", value: "607" }, - { label: "Древние времена", value: "221" }, - { label: "Древний Китай", value: "374" }, - { label: "Дружба", value: "217" }, - { label: "Друзья детства", value: "282" }, - { label: "Друзья становятся врагами", value: "567" }, - { label: "Друиды", value: "497" }, - { label: "Дух лисы", value: "784" }, - { label: "Духи/призраки", value: "172" }, - { label: "Духовный советник", value: "435" }, - { label: "Душевность", value: "633" }, - { label: "Души", value: "254" }, - { label: "Европейская атмосфера", value: "521" }, - { label: "Ёкаи", value: "575" }, - { label: "Есть аниме-адаптация", value: "154" }, - { label: "Есть видеоигра по мотивам", value: "552" }, - { label: "Есть манга", value: "155" }, - { label: "Есть манхва-адаптация", value: "517" }, - { label: "Есть маньхуа-адаптация", value: "388" }, - { label: "Есть сериал-адаптация", value: "492" }, - { label: "Есть фильм", value: "173" }, - { label: "Женища-наставник", value: "505" }, - { label: "Жесткая, двуличная личность", value: "346" }, - { label: "Жестокие персонажи", value: "503" }, - { label: "Жестокое обращение с ребенком", value: "654" }, - { label: "Жестокость", value: "245" }, - { label: "Животноводство", value: "754" }, - { label: "Животные черты", value: "528" }, - { label: "Жизнь в квартире", value: "606" }, - { label: "Жрицы", value: "615" }, - { label: "Заботливый главный герой", value: "288" }, - { label: "Забывчивый главный герой", value: "628" }, - { label: "Заговоры", value: "289" }, - { label: "Закалка тела", value: "362" }, - { label: "Законники", value: "756" }, - { label: "Замкнутый главный герой", value: "589" }, - { label: "Запечатанная сила", value: "430" }, - { label: "Застенчивые персонажи", value: "508" }, - { label: "Звери", value: "237" }, - { label: "Звери-компаньоны", value: "301" }, - { label: "Злой протагонист", value: "243" }, - { label: "Злые боги", value: "504" }, - { label: "Злые организации", value: "563" }, - { label: "Злые религии", value: "734" }, - { label: "Знаменитости", value: "473" }, - { label: "Знаменитый главный герой", value: "531" }, - { label: "Знания современного мира", value: "297" }, - { label: "Зомби", value: "409" }, - { label: "Игра на выживание", value: "274" }, - { label: "Игривый протагонист", value: "717" }, - { label: "Игровая система рейтинга", value: "391" }, - { label: "Игровые элементы", value: "266" }, - { label: "Игрушки (18+)", value: "676" }, - { label: "Из грязи в князи", value: "416" }, - { label: "Из женщины в мужчину", value: "709" }, - { label: "Из мужчины в женщину", value: "715" }, - { label: "Из полного в худого", value: "767" }, - { label: "Из слабого в сильного", value: "202" }, - { label: "Извращенный главный герой", value: "434" }, - { label: "Изгои", value: "534" }, - { label: "Изменение расы", value: "664" }, - { label: "Изменения внешнего вида", value: "300" }, - { label: "Изменения личности", value: "219" }, - { label: "Изнасилование", value: "229" }, - { label: "Изображения жестокости", value: "355" }, - { label: "ИИ", value: "235" }, - { label: "Империи", value: "748" }, - { label: "Инвалидность", value: "684" }, - { label: "Индустриализация", value: "292" }, - { label: "Инженер", value: "163" }, - { label: "Инцест", value: "525" }, - { label: "Искусственный интеллект", value: "236" }, - { label: "Исследования", value: "670" }, - { label: "Историческая проза", value: "119" }, - { label: "Исторические приключения", value: "120" }, - { label: "Исторический детектив", value: "117" }, - { label: "Исторический любовный роман", value: "138" }, - { label: "Историческое фэнтези", value: "110" }, - { label: "Каннибализм", value: "456" }, - { label: "Карточные игры", value: "682" }, - { label: "Киберпанк", value: "141" }, - { label: "Киберспорт", value: "686" }, - { label: "Кланы", value: "796" }, - { label: "Класс безработного", value: "735" }, - { label: "Клоны", value: "447" }, - { label: "Клубы", value: "216" }, - { label: "Книги", value: "427" }, - { label: "Книги навыков", value: "400" }, - { label: "Книжный червь", value: "519" }, - { label: "Коварство", value: "230" }, - { label: "Коллеги", value: "704" }, - { label: "Колледж/Университет", value: "726" }, - { label: "Кома", value: "776" }, - { label: "Командная работа", value: "496" }, - { label: "Комедийный оттенок", value: "582" }, - { label: "Комплекс неполноценности", value: "550" }, - { label: "Комплекс семейных отношений", value: "223" }, - { label: "Конкуренция", value: "746" }, - { label: "Контроль разума/сознания", value: "357" }, - { label: "Копейщик", value: "425" }, - { label: "Королевская битва", value: "797" }, - { label: "Королевская власть", value: "178" }, - { label: "Королевства", value: "259" }, - { label: "Короткий любовный роман", value: "136" }, - { label: "Коррупция", value: "457" }, - { label: "Космическая фантастика", value: "146" }, - { label: "Космические войны", value: "697" }, - { label: "Красивый герой", value: "226" }, - { label: "Крафт", value: "377" }, - { label: "Кризис личности", value: "640" }, - { label: "Кругосветное путешествие", value: "353" }, - { label: "Кудере", value: "506" }, - { label: "Кузены", value: "718" }, - { label: "Кузнец", value: "518" }, - { label: "Кукловоды", value: "501" }, - { label: "Куклы/марионетки", value: "624" }, - { label: "Культивация", value: "241" }, - { label: "Куннилингус", value: "667" }, - { label: "Легенды", value: "500" }, - { label: "Легкая жизнь", value: "644" }, - { label: "Ленивый главный герой", value: "621" }, - { label: "Лидерство", value: "494" }, - { label: "Лоли", value: "213" }, - { label: "Лотерея", value: "477" }, - { label: "Любовное фэнтези", value: "135" }, - { label: "Любовный треугольник", value: "218" }, - { label: "Любовь детства", value: "560" }, - { label: "Любовь с первого взгляда", value: "737" }, - { label: "Магические надписи", value: "453" }, - { label: "Магические печати", value: "369" }, - { label: "Магические технологии", value: "440" }, - { label: "Магия", value: "165" }, - { label: "Магия призыва", value: "418" }, - { label: "Мазохистские персонажи", value: "688" }, - { label: "Манипулятивные персонажи", value: "249" }, - { label: "Мания", value: "212" }, - { label: "Мастер на все руки", value: "466" }, - { label: "Мастурбация", value: "788" }, - { label: "Махо-сёдзё", value: "556" }, - { label: "Медицинские знания", value: "507" }, - { label: "Медленная романтическая линия", value: "232" }, - { label: "Медленное развитие на старте", value: "694" }, - { label: "Межпространственные путешестви", value: "404" }, - { label: "Менеджмент", value: "294" }, - { label: "Мертвый главный герой", value: "761" }, - { label: "Месть", value: "209" }, - { label: "Метаморфы", value: "335" }, - { label: "Меч и магия", value: "180" }, - { label: "Мечник", value: "647" }, - { label: "Мечты", value: "738" }, - { label: "Милая история", value: "725" }, - { label: "Милое дитя", value: "637" }, - { label: "Милый главный герой", value: "714" }, - { label: "Мировое дерево", value: "463" }, - { label: "Мистика", value: "151" }, - { label: "Мистический ореол вокруг семьи", value: "483" }, - { label: "Мифические звери", value: "491" }, - { label: "Мифология", value: "530" }, - { label: "Младшие братья", value: "785" }, - { label: "Младшие сестры", value: "527" }, - { label: "ММОРПГ (ЛитРПГ)", value: "395" }, - { label: "Множество перемещенных людей", value: "549" }, - { label: "Множество реальностей", value: "370" }, - { label: "Множество реинкарнированных лю", value: "330" }, - { label: "Модели", value: "678" }, - { label: "Молчаливый персонаж", value: "721" }, - { label: "Монстры", value: "192" }, - { label: "Мужская гей-пара", value: "706" }, - { label: "Мужчина-яндере", value: "267" }, - { label: "Музыка", value: "635" }, - { label: "Музыкальные группы", value: "634" }, - { label: "Мутации", value: "693" }, - { label: "Мутированные существа", value: "405" }, - { label: "Навык кражи", value: "662" }, - { label: "Навязчивая любовь", value: "206" }, - { label: "Наемники", value: "412" }, - { label: "Назойливый возлюбленный", value: "561" }, - { label: "Наивный главный герой", value: "189" }, - { label: "Наркотики", value: "689" }, - { label: "Нарциссический главный герой", value: "532" }, - { label: "Насилие сексуального характера", value: "774" }, - { label: "Наследование", value: "452" }, - { label: "Научная фантастика", value: "144" }, - { label: "Национализм", value: "406" }, - { label: "Не блещущий внешне главный гер", value: "415" }, - { label: "Не родные братья и сестры", value: "526" }, - { label: "Небеса", value: "568" }, - { label: "Небесное испытание", value: "367" }, - { label: "Негуманоидный главный герой", value: "659" }, - { label: "Недоверчивый главный герой", value: "546" }, - { label: "Недооцененный главный герой", value: "258" }, - { label: "Недоразумения", value: "309" }, - { label: "Неизлечимая болезнь", value: "196" }, - { label: "Некромант", value: "397" }, - { label: "Нелинейная история", value: "269" }, - { label: "Ненавистный главный герой", value: "548" }, - { label: "Ненадежный рассказчик", value: "277" }, - { label: "Нерезиденты", value: "772" }, - { label: "Нерешительный главный герой", value: "744" }, - { label: "Несерьезный главный герой", value: "384" }, - { label: "Несколько временных линий", value: "576" }, - { label: "Несколько гг", value: "268" }, - { label: "Несколько главных героев", value: "537" }, - { label: "Несколько идентичностей", value: "652" }, - { label: "Несколько личностей", value: "536" }, - { label: "Нетораре", value: "732" }, - { label: "Нетори", value: "514" }, - { label: "Неудачливый главный герой", value: "618" }, - { label: "Ниндзя", value: "441" }, - { label: "Новелла", value: "103" }, - { label: "Обещание из детства", value: "742" }, - { label: "Обманщик", value: "485" }, - { label: "Обмен телами", value: "214" }, - { label: "Обнаженка", value: "490" }, - { label: "Обольщение", value: "731" }, - { label: "Оборотни", value: "661" }, - { label: "Обратный гарем", value: "351" }, - { label: "Общество монстров", value: "329" }, - { label: "Обязательство", value: "602" }, - { label: "Огнестрельное оружие", value: "291" }, - { label: "Ограниченное время жизни", value: "194" }, - { label: "Одержимость", value: "512" }, - { label: "Одинокий главный герой", value: "394" }, - { label: "Одиночество", value: "306" }, - { label: "Одиночное проживание", value: "313" }, - { label: "Околосмертные переживания", value: "759" }, - { label: "Оммёдзи", value: "597" }, - { label: "Омоложение", value: "338" }, - { label: "Организованная преступность", value: "540" }, - { label: "Оргия", value: "775" }, - { label: "Орки", value: "331" }, - { label: "Освоение навыков", value: "336" }, - { label: "Основано на аниме", value: "605" }, - { label: "Основано на видео игре", value: "720" }, - { label: "Основано на визуальной новелле", value: "793" }, - { label: "Основано на песне", value: "700" }, - { label: "Основано на фильме", value: "604" }, - { label: "Осторожный главный герой", value: "240" }, - { label: "Отаку", value: "571" }, - { label: "Открытый космос", value: "264" }, - { label: "Отношения в сети", value: "727" }, - { label: "Отношения между людьми и нелюд", value: "311" }, - { label: "Отношения на расстоянии", value: "791" }, - { label: "Отношения Сенпай-Коухай", value: "572" }, - { label: "Отношения ученика и учителя", value: "515" }, - { label: "Отношения учитель-ученик", value: "429" }, - { label: "Отношения хозяин-слуга", value: "312" }, - { label: "Отомэ игра", value: "733" }, - { label: "Отсутствие здравого смысла", value: "645" }, - { label: "Отсутствие родителей", value: "625" }, - { label: "Офисный роман", value: "707" }, - { label: "Официанты", value: "703" }, - { label: "Охотники", value: "378" }, - { label: "Очаровательный главный герой", value: "687" }, - { label: "Падшее дворянство", value: "655" }, - { label: "Падшие ангелы", value: "565" }, - { label: "Пайзури", value: "665" }, - { label: "Паразиты", value: "591" }, - { label: "Параллельные миры", value: "207" }, - { label: "Парк", value: "265" }, - { label: "Парк развлечений", value: "632" }, - { label: "Пародия", value: "407" }, - { label: "Певцы/Певицы", value: "739" }, - { label: "Первая любовь", value: "729" }, - { label: "Первоисточник новеллы — манга", value: "611" }, - { label: "Первый раз", value: "787" }, - { label: "Перемещение в игровой мир", value: "588" }, - { label: "Перемещение в иной мир", value: "750" }, - { label: "Перерождение в ином мире", value: "751" }, - { label: "Переселение души/трансмиграция", value: "257" }, - { label: "Персонаж использует щит", value: "666" }, - { label: "Петля времени", value: "200" }, - { label: "Пираты", value: "771" }, - { label: "Писатели", value: "482" }, - { label: "Питомцы", value: "349" }, - { label: "Племенное общество", value: "381" }, - { label: "Повелитель демонов", value: "283" }, - { label: "Подземелья", value: "322" }, - { label: "Подростковая проза", value: "114" }, - { label: "Пожелания", value: "278" }, - { label: "Познание Дао", value: "443" }, - { label: "Покинутое дитя", value: "590" }, - { label: "Полигамия", value: "386" }, - { label: "Политика", value: "170" }, - { label: "Политический роман", value: "133" }, - { label: "Полиция", value: "764" }, - { label: "Полулюди", value: "420" }, - { label: "Популярный любовный интерес", value: "342" }, - { label: "Постапокалипсис", value: "145" }, - { label: "Постапокалиптика", value: "184" }, - { label: "Потерянные цивилизации", value: "749" }, - { label: "Похищения людей", value: "723" }, - { label: "Поэзия", value: "111" }, - { label: "Правонарушители", value: "713" }, - { label: "Прагматичный главный герой", value: "592" }, - { label: "Преданный любовный интерес", value: "225" }, - { label: "Предательство", value: "222" }, - { label: "Предвидение", value: "177" }, - { label: "Прекрасная героиня", value: "158" }, - { label: "Преступники", value: "736" }, - { label: "Преступность", value: "204" }, - { label: "Призванный герой", value: "262" }, - { label: "Призраки", value: "176" }, - { label: "Принуждение к отношениям", value: "643" }, - { label: "Принцессы", value: "795" }, - { label: "Притворная пара", value: "641" }, - { label: "Причудливые персонажи", value: "387" }, - { label: "Пришельцы/инопланетяне", value: "197" }, - { label: "Программист", value: "691" }, - { label: "Проклятия", value: "174" }, - { label: "Промывание мозгов", value: "578" }, - { label: "Пропуск времени", value: "256" }, - { label: "Пророчества", value: "499" }, - { label: "Проститутки", value: "786" }, - { label: "Прошлое играет большую роль", value: "320" }, - { label: "Прыжки между мирами", value: "741" }, - { label: "Психические силы", value: "359" }, - { label: "Психопаты", value: "270" }, - { label: "Публицистика", value: "124" }, - { label: "Путешествие во времени", value: "201" }, - { label: "Пытка", value: "280" }, - { label: "Рабы", value: "778" }, - { label: "Развитие личности", value: "125" }, - { label: "Развод", value: "758" }, - { label: "Разумные предметы", value: "286" }, - { label: "Расизм", value: "408" }, - { label: "Рассказ", value: "185" }, - { label: "Расторжения помолвки", value: "343" }, - { label: "Расы зооморфов", value: "315" }, - { label: "Реализм", value: "246" }, - { label: "Ревность", value: "730" }, - { label: "Редакторы", value: "705" }, - { label: "Реинкарнация", value: "372" }, - { label: "Реинкарнация в монстра", value: "310" }, - { label: "Реинкарнация в объект", value: "711" }, - { label: "Религии", value: "616" }, - { label: "Репортеры", value: "781" }, - { label: "Ресторан", value: "680" }, - { label: "Решительный главный герой", value: "242" }, - { label: "Робкий главный герой", value: "763" }, - { label: "Родитель одиночка", value: "708" }, - { label: "Родительский комплекс", value: "587" }, - { label: "Родословная", value: "239" }, - { label: "Романтическая эротика", value: "132" }, - { label: "Романтический подсюжет", value: "271" }, - { label: "Рост персонажа", value: "215" }, - { label: "Рыцари", value: "260" }, - { label: "Садистские персонажи", value: "674" }, - { label: "Самоотверженный главный герой", value: "747" }, - { label: "Самурай", value: "677" }, - { label: "Сборник коротких историй", value: "520" }, - { label: "Связанные сюжетные линии", value: "535" }, - { label: "Святые", value: "752" }, - { label: "Священники", value: "413" }, - { label: "Сдержанный главный герой", value: "762" }, - { label: "Секретные организации", value: "417" }, - { label: "Секреты", value: "626" }, - { label: "Секс рабы", value: "779" }, - { label: "Семейный конфликт", value: "757" }, - { label: "Семь добродетелей", value: "334" }, - { label: "Семь смертных грехов", value: "253" }, - { label: "Семья", value: "347" }, - { label: "Сёнэн-ай подсюжет", value: "690" }, - { label: "Серийные убийцы", value: "557" }, - { label: "Сестринский комплекс", value: "558" }, - { label: "Сила духа", value: "373" }, - { label: "Сила, требующая платы за польз", value: "579" }, - { label: "Сильная пара", value: "228" }, - { label: "Сильный в сильнейшего", value: "442" }, - { label: "Сильный любовный интерес", value: "273" }, - { label: "Синдром восьмиклассника", value: "211" }, - { label: "Сироты", value: "319" }, - { label: "Система уровней", value: "305" }, - { label: "Системный администратор", value: "538" }, - { label: "Сказка", value: "126" }, - { label: "Скрытие истинной личности", value: "451" }, - { label: "Скрытие истинных способностей", value: "348" }, - { label: "Скрытный главный герой", value: "252" }, - { label: "Скрытые способности", value: "247" }, - { label: "Скульпторы", value: "448" }, - { label: "Слабо выраженная романтическая", value: "318" }, - { label: "Слабый главный герой", value: "298" }, - { label: "Слепой главный герой", value: "794" }, - { label: "Слуги", value: "333" }, - { label: "Слэш", value: "131" }, - { label: "Смерть", value: "175" }, - { label: "Смерть близких", value: "444" }, - { label: "Собственнические персонажи", value: "227" }, - { label: "Современная проза", value: "150" }, - { label: "Современность", value: "167" }, - { label: "Современный любовный роман", value: "137" }, - { label: "Сожительство", value: "544" }, - { label: "Создание армии", value: "287" }, - { label: "Создание артефактов", value: "382" }, - { label: "Создание клана", value: "513" }, - { label: "Создание королевства", value: "293" }, - { label: "Создание навыков", value: "337" }, - { label: "Создание секты", value: "469" }, - { label: "Солдаты/военные", value: "193" }, - { label: "Сон", value: "622" }, - { label: "Состоятельные персонажи", value: "190" }, - { label: "Социальная иерархия по силе", value: "255" }, - { label: "Социальная фантастика", value: "148" }, - { label: "Социальные изгои", value: "542" }, - { label: "Спасение мира", value: "638" }, - { label: "Специальные способности", value: "179" }, - { label: "Спокойный главный герой", value: "160" }, - { label: "Справедливый главный герой", value: "719" }, - { label: "Средневековье", value: "296" }, - { label: "Ссорящаяся пара", value: "383" }, - { label: "Сталкеры", value: "710" }, - { label: "Старение", value: "299" }, - { label: "Стимпанк", value: "139" }, - { label: "Стоические персонажи", value: "509" }, - { label: "Стокгольмский синдром", value: "675" }, - { label: "Стратег", value: "495" }, - { label: "Стратегические битвы", value: "272" }, - { label: "Стратегия", value: "798" }, - { label: "Стрелки", value: "522" }, - { label: "Стрельба из лука", value: "461" }, - { label: "Студенческий совет", value: "551" }, - { label: "Судьба", value: "364" }, - { label: "Суккубы", value: "545" }, - { label: "Супер герои", value: "799" }, - { label: "Суровая подготовка", value: "356" }, - { label: "Таинственная болезнь", value: "195" }, - { label: "Таинственное прошлое", value: "358" }, - { label: "Тайная личность", value: "399" }, - { label: "Тайные отношения", value: "768" }, - { label: "Танцоры", value: "782" }, - { label: "Телохранители", value: "516" }, - { label: "Темное фэнтези", value: "106" }, - { label: "Тентакли", value: "712" }, - { label: "Террористы", value: "574" }, - { label: "Технологический разрыв", value: "658" }, - { label: "Тихие персонажи", value: "600" }, - { label: "Толстый главный герой", value: "389" }, - { label: "Торговцы", value: "489" }, - { label: "Травля/буллинг", value: "210" }, - { label: "Травник", value: "724" }, - { label: "Трагическое прошлое", value: "276" }, - { label: "Трансплантация воспоминаний", value: "449" }, - { label: "Триллер", value: "134" }, - { label: "Трудолюбивый главный герой", value: "164" }, - { label: "Тюрьма", value: "541" }, - { label: "Убийства", value: "205" }, - { label: "Убийцы", value: "345" }, - { label: "Убийцы драконов", value: "646" }, - { label: "Уверенный главный герой", value: "363" }, - { label: "Удачливый главный герой", value: "478" }, - { label: "Ужасы", value: "112" }, - { label: "Укротитель монстров", value: "422" }, - { label: "Умения из прошлой жизни", value: "371" }, - { label: "Умная пара", value: "554" }, - { label: "Умный главный герой", value: "161" }, - { label: "Уникальная техника Культивации", value: "350" }, - { label: "Уникальное оружие", value: "426" }, - { label: "Управление бизнесом", value: "403" }, - { label: "Управление временем", value: "279" }, - { label: "Управление кровью", value: "753" }, - { label: "Упрямый главный герой", value: "695" }, - { label: "Уродливый главный герой", value: "766" }, - { label: "Ускоренный рост", value: "390" }, - { label: "Усыновленные дети", value: "543" }, - { label: "Усыновленный главный герой", value: "486" }, - { label: "Уход за детьми", value: "474" }, - { label: "Учителя", value: "431" }, - { label: "Фамильяры", value: "596" }, - { label: "Фанатизм", value: "650" }, - { label: "Фантастические существа", value: "410" }, - { label: "Фантастический детектив", value: "115" }, - { label: "Фанфик", value: "113" }, - { label: "Фанфикшн", value: "464" }, - { label: "Фармацевт", value: "728" }, - { label: "Фарминг", value: "458" }, - { label: "Феи", value: "316" }, - { label: "Фелляция", value: "631" }, - { label: "Фемслэш", value: "127" }, - { label: "Фениксы", value: "454" }, - { label: "Фетиш груди", value: "559" }, - { label: "Философия", value: "208" }, - { label: "Фильмы", value: "479" }, - { label: "Флэшбэки", value: "585" }, - { label: "Фобии", value: "220" }, - { label: "Фольклор", value: "529" }, - { label: "Футанари", value: "619" }, - { label: "Футуристический сеттинг", value: "460" }, - { label: "Фэнтези мир", value: "244" }, - { label: "Хакеры", value: "475" }, - { label: "Харизматический герой", value: "467" }, - { label: "Хикикомори/Затворники", value: "524" }, - { label: "Хитроумный главный герой", value: "224" }, - { label: "Хозяин подземелий", value: "608" }, - { label: "Холодный главный герой", value: "354" }, - { label: "Хорошие отношения с семьей", value: "566" }, - { label: "Хранители могил", value: "191" }, - { label: "Целители", value: "465" }, - { label: "Цзянши", value: "740" }, - { label: "Цундэрэ", value: "510" }, - { label: "Чаты", value: "629" }, - { label: "Человеческое оружие", value: "580" }, - { label: "Честный главный герой", value: "341" }, - { label: "Читы", value: "339" }, - { label: "Шантаж", value: "584" }, - { label: "Шеф-повар", value: "340" }, - { label: "Шикигами", value: "598" }, - { label: "Школа только для девочек", value: "668" }, - { label: "Шота", value: "573" }, - { label: "Шоу-бизнес", value: "481" }, - { label: "Шпионский детектив", value: "118" }, - { label: "Шпионы", value: "614" }, - { label: "Эволюция", value: "304" }, - { label: "Эгоистичный главный герой", value: "594" }, - { label: "Эйдетическая память", value: "468" }, - { label: "Экзорсизм", value: "564" }, - { label: "Экономика", value: "553" }, - { label: "Эксгибиционизм", value: "673" }, - { label: "Эксперименты с людьми", value: "248" }, - { label: "Элементальная магия", value: "471" }, - { label: "Эльфы", value: "284" }, - { label: "Эмоционально слабый гг", value: "770" }, - { label: "Эпизодический", value: "649" }, - { label: "Эпическое фэнтези", value: "109" }, - { label: "Эротическая фантастика", value: "129" }, - { label: "Эротический фанфик", value: "128" }, - { label: "Эротическое фэнтези", value: "130" }, - { label: "Юмористическая фантастика", value: "143" }, - { label: "Юмористическое фэнтези", value: "108" }, - { label: "Юный любовный интерес", value: "511" }, - { label: "Яды", value: "484" }, - { label: "Языкастые персонажи", value: "480" }, - { label: "Языковой барьер", value: "182" }, - { label: "Яндере", value: "314" }, - { label: "Японские силы самообороны", value: "610" }, - { label: "Ярко выраженная романтика", value: "365" }, - { label: "R-15 Японское возрастное огр.", value: "332" }, - { label: "R-18", value: "424" }, - ], - type: FilterTypes.ExcludableCheckboxGroup, - }, - types: { - label: "Типы:", - value: [], - options: [ - { label: "Авторское", value: "1" }, - { label: "Другое", value: "7" }, - { label: "Запад", value: "5" }, - { label: "Китай", value: "4" }, - { label: "Корея", value: "3" }, - { label: "Фанфики", value: "6" }, - { label: "Япония", value: "2" }, - ], - type: FilterTypes.CheckboxGroup, - }, - status: { - label: "Статус проекта:", - value: [], - options: [ - { label: "Анонс", value: "4" }, - { label: "Закончен", value: "0" }, - { label: "Заморожен", value: "2" }, - { label: "Лицензировано", value: "5" }, - { label: "Нет переводчика", value: "3" }, - { label: "Продолжается", value: "1" }, - ], - type: FilterTypes.CheckboxGroup, - }, - age_limit: { - label: "Возрастной рейтинг:", - value: [], - options: [ - { label: "16+", value: "1" }, - { label: "18+", value: "2" }, - { label: "Для всех", value: "0" }, - ], - type: FilterTypes.CheckboxGroup, - }, - } satisfies Filters; -} - -export default new ReN(); - -interface responseNovels { - id: number; - secondary_name: string; - main_name: string; - dir: string; - is_licensed: boolean; - issue_year: number; - avg_rating: string; - admin_rating: string; - type: string; - total_views: number; - total_votes: number; - cover_high: string; - cover_mid: string; - cover_low: string; - is_yaoi: boolean; - is_erotic: boolean; - bookmark_type?: null; - genres?: ItemEntity[] | null; - img: Img; - categories?: ItemEntity[] | null; - rus_name?: string; - en_name?: string; -} - -interface Img { - high?: string; - mid?: string; - low: string; -} - -interface responseNovel { - id: number; - img: Img; - secondary_name: string; - main_name: string; - another_name: string; - dir: string; - description: string; - issue_year: number; - avg_rating: string; - admin_rating: string; - count_rating: number; - age_limit: number; - status: ItemEntity; - count_bookmarks: number; - total_votes: number; - total_views: number; - type: ItemEntity; - genres?: ItemEntity[] | null; - categories?: ItemEntity[] | null; - publishers?: PublishersEntity[] | null; - bookmark_type?: null; - branches?: BranchesEntity[] | null; - count_chapters: number; - first_chapter: FirstChapter; - continue_reading?: null; - is_licensed: boolean; - newlate_id?: null; - newlate_title?: null; - related?: null; - uploaded: number; - can_post_comments: boolean; - adaptation: Adaptation | null; -} - -interface Adaptation { - main_name: string; - secondary_name: string; - dir: string; - img: string; -} - -interface ItemEntity { - id: number; - name: string; -} -interface PublishersEntity { - id: number; - name: string; - img: string; - dir: string; - tagline: string; - type: string; -} -interface BranchesEntity { - id: number; - img: string; - publishers?: PublishersEntity[] | null; - subscribed: boolean; - total_votes: number; - count_chapters: number; -} -interface FirstChapter { - id: number; - tome: number; - chapter: string; -} - -interface responseСhapters { - id: number; - rated?: null; - viewed?: null; - is_bought?: boolean | null; - publishers?: PublishersEntity[] | null; - tome: number; - index: number; - chapter: string; - name: string; - price?: null | number; - score: number; - pub_date?: null; - upload_date: string; - is_paid: boolean; -} - -interface responseСhapter { - id: number; - chapter: string; - name: string; - content: string; - score: number; - upload_date: string; - is_paid: boolean; - title_id: number; - volume_id: number; - branch_id: number; - price?: null; - pub_date?: null; - index: number; - publishers?: PublishersEntity[] | null; - tome: number; -} +import { Plugin } from "@typings/plugin"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { NovelStatus } from "@libs/novelStatus"; +import dayjs from "dayjs"; + +class ReN implements Plugin.PluginBase { + id = "ReN"; + name = "Renovels"; + site = "https://renovels.org"; + version = "1.0.0"; + icon = "src/ru/renovels/icon.png"; + userAgent = ""; + + async popularNovels( + pageNo: number, + { + showLatestNovels, + filters, + }: Plugin.PopularNovelsOptions, + ): Promise { + let url = this.site + "/api/search/catalog/?count=30&ordering="; + url += filters?.order?.value + ? (filters.order.value as string).replace("+", "") + : "-"; + url += showLatestNovels ? "chapter_date" : filters?.sort?.value || "rating"; + + if (filters?.genres?.value?.include?.length) { + url += filters.genres.value.include.map((i) => "&genres=" + i).join(""); + } + + if (filters?.genres?.value?.exclude?.length) { + url += filters.genres.value.exclude + .map((i) => "&exclude_genres=" + i).join(""); + } + + if (filters?.status?.value?.length) { + url += filters.status.value.map((i) => "&status=" + i).join(""); + } + + if (filters?.types?.value?.length) { + url += filters.types.value.map((i) => "&types=" + i).join(""); + } + + if (filters?.categories?.value?.include?.length) { + url += filters.categories.value.include + .map((i) => "&categories=" + i).join(""); + } + + if (filters?.categories?.value?.exclude?.length) { + url += filters.categories.value.exclude + .map((i) => "&exclude_categories=" + i).join(""); + } + + if (filters?.age_limit?.value?.length) { + url += filters.age_limit.value.map((i) => "&age_limit=" + i).join(""); + } + + url += "&page=" + pageNo; + + const result = await fetchApi(url); + const body = (await result.json()) as { content: responseNovels[] }; + + const novels: Plugin.NovelItem[] = []; + + body.content.forEach((novel) => + novels.push({ + name: novel.main_name || novel.secondary_name, + cover: this.site + (novel.img?.high || novel.img?.mid || novel.img.low), + url: this.site + "/novel/" + novel.dir, + }), + ); + + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const novelID = novelUrl.split("/")[4]; + const result = await fetchApi(this.site + "/api/titles/" + novelID); + const body = (await result.json()) as { content: responseNovel }; + + const novel: Plugin.SourceNovel = { + url: novelUrl, + name: body.content.main_name || body.content.secondary_name, + summary: body.content.description, + cover: + this.site + + (body.content.img?.high || + body.content.img?.mid || + body.content.img.low), + status: + body.content.status.name === "Продолжается" + ? NovelStatus.Ongoing + : NovelStatus.Completed, + }; + + const tags = [body.content?.genres, body.content?.categories] + .flat() + .map((tags) => tags?.name) + .filter((tags) => tags); + + if (tags.length) { + novel.genres = tags.join(","); + } + + const all = Math.floor(body.content.count_chapters / 100 + 1); + const branch_id = body.content.branches?.[0]?.id || body.content.id; + const chapters: Plugin.ChapterItem[] = []; + + for (let i = 0; i < all; i++) { + const chapterResult = await fetchApi( + this.site + + "/api/titles/chapters/?branch_id=" + + branch_id + + "&count=100&page=" + + (i + 1), + ); + const volumes = (await chapterResult.json()) as { + content: responseСhapters[]; + }; + + volumes.content.forEach((chapter) => { + if (!chapter.is_paid || chapter.is_bought) { + chapters.push({ + name: `Том ${chapter.tome} Глава ${chapter.chapter} ${chapter.name}`?.trim(), + releaseTime: dayjs(chapter.upload_date).format("LLL"), + url: `${this.site}/novel/${novelID}/${chapter.id}/`, + }); + } + }); + } + + novel.chapters = chapters.reverse(); + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const url = this.site + "/api/titles/chapters/" + chapterUrl.split("/")[5]; + const result = await fetchApi(url); + const body = (await result.json()) as { content: responseСhapter }; + + return body.content.content; + } + + async searchNovels( + searchTerm: string, + pageNo: number | undefined = 1, + ): Promise { + const url = `${this.site}/api/search/?query=${searchTerm}&count=100&field=titles&page=${pageNo}`; + const result = await fetchApi(url); + const body = (await result.json()) as { content: responseNovels[] }; + const novels: Plugin.NovelItem[] = []; + + body.content.forEach((novel) => + novels.push({ + name: novel.main_name || novel.secondary_name, + cover: this.site + (novel.img?.high || novel.img?.mid || novel.img.low), + url: this.site + "/novel/" + novel.dir, + }), + ); + + return novels; + } + + fetchImage = fetchFile; + + filters = { + sort: { + label: "Сортировка", + value: "", + options: [ + { label: "Рейтинг", value: "rating" }, + { label: "Просмотры", value: "views" }, + { label: "Лайкам", value: "votes" }, + { label: "Дате добавления", value: "id" }, + { label: "Дате обновления", value: "chapter_date" }, + { label: "Количество глав", value: "count_chapters" }, + ], + type: FilterTypes.Picker, + }, + order: { + label: "Порядок", + value: "", + options: [ + { label: "По убыванию", value: "-" }, + { label: "По возрастанию", value: "+" }, + ], + type: FilterTypes.Picker, + }, + genres: { + label: "Жанры:", + value: { include: [], exclude: [] }, + options: [ + { label: "Боевик", value: "112" }, + { label: "Война", value: "123" }, + { label: "Детектив", value: "114" }, + { label: "Драма", value: "125" }, + { label: "Историческая проза", value: "115" }, + { label: "ЛитРПГ", value: "109" }, + { label: "Любовные романы", value: "116" }, + { label: "Мистика", value: "103" }, + { label: "Научная фантастика", value: "121" }, + { label: "Повседневность", value: "128" }, + { label: "Подростковая проза", value: "113" }, + { label: "Политический роман", value: "119" }, + { label: "Попаданцы", value: "108" }, + { label: "Поэзия", value: "105" }, + { label: "Разное", value: "111" }, + { label: "РеалРПГ", value: "117" }, + { label: "Романтика", value: "126" }, + { label: "Современная проза", value: "102" }, + { label: "Современная фантастика", value: "127" }, + { label: "Спорт", value: "122" }, + { label: "Трагедия", value: "129" }, + { label: "Триллер", value: "110" }, + { label: "Триллер и саспенс", value: "124" }, + { label: "Ужасы", value: "106" }, + { label: "Фантастика", value: "101" }, + { label: "Фанфик", value: "107" }, + { label: "Фьюжн роман", value: "120" }, + { label: "Фэнтези", value: "100" }, + { label: "Эротика", value: "118" }, + { label: "Юмор", value: "104" }, + ], + type: FilterTypes.ExcludableCheckboxGroup, + }, + categories: { + label: "Тэги:", + value: { include: [], exclude: [] }, + options: [ + { label: "[Награжденная работа]", value: "648" }, + { label: "18+", value: "423" }, + { label: "Абьюзеры", value: "663" }, + { label: "Авантюристы", value: "436" }, + { label: "Автоматоны", value: "593" }, + { label: "Агрессивные персонажи", value: "502" }, + { label: "Ад", value: "569" }, + { label: "Адаптация в радиопостановку", value: "581" }, + { label: "Академия", value: "153" }, + { label: "Актеры озвучки", value: "627" }, + { label: "Активный главный герой", value: "251" }, + { label: "Алхимия", value: "233" }, + { label: "Альтернативная история", value: "147" }, + { label: "Альтернативный мир", value: "156" }, + { label: "Амнезия/Потеря памяти", value: "344" }, + { label: "Анабиоз", value: "685" }, + { label: "Ангелы", value: "183" }, + { label: "Андрогинные персонажи", value: "321" }, + { label: "Андроиды", value: "203" }, + { label: "Анти-магия", value: "533" }, + { label: "Антигерой", value: "432" }, + { label: "Антикварный магазин", value: "623" }, + { label: "Антисоциальный главный герой", value: "613" }, + { label: "Антиутопия", value: "142" }, + { label: "Апатичный протагонист", value: "157" }, + { label: "Апокалипсис", value: "402" }, + { label: "Аранжированный брак", value: "375" }, + { label: "Армия", value: "639" }, + { label: "Артефакты", value: "234" }, + { label: "Артисты", value: "523" }, + { label: "Банды", value: "630" }, + { label: "БДСМ", value: "699" }, + { label: "Бедный главный герой", value: "398" }, + { label: "Безжалостный главный герой", value: "261" }, + { label: "Беззаботный главный герой", value: "438" }, + { label: "Безусловная любовь", value: "679" }, + { label: "Беременность", value: "250" }, + { label: "Бесполый главный герой", value: "325" }, + { label: "Бессмертные", value: "368" }, + { label: "Бесстрашный протагонист", value: "656" }, + { label: "Бесстыдный главный герой", value: "352" }, + { label: "Бесчестный главный герой", value: "716" }, + { label: "Библиотека", value: "428" }, + { label: "Бизнес-литература", value: "121" }, + { label: "Бизнесмен", value: "769" }, + { label: "Биочип", value: "238" }, + { label: "Бисексуальный главный герой", value: "773" }, + { label: "Близнецы", value: "263" }, + { label: "Боги", value: "317" }, + { label: "Богини", value: "439" }, + { label: "Боевая академия", value: "450" }, + { label: "Боевая фантастика", value: "149" }, + { label: "Боевое фэнтези", value: "104" }, + { label: "Боевые духи", value: "433" }, + { label: "Боевые соревнования", value: "493" }, + { label: "Божественная защита", value: "421" }, + { label: "Божественные силы", value: "327" }, + { label: "Борьба за власть", value: "599" }, + { label: "Брак", value: "446" }, + { label: "Брак по расчету", value: "188" }, + { label: "Братский комплекс", value: "159" }, + { label: "Братство", value: "487" }, + { label: "Братья и сестры", value: "577" }, + { label: "Буддизм", value: "745" }, + { label: "Быстрая культивация", value: "366" }, + { label: "Быстрообучаемый", value: "324" }, + { label: "Валькирии", value: "692" }, + { label: "Вампиры", value: "360" }, + { label: "Ваншот", value: "702" }, + { label: "Ведьмы", value: "281" }, + { label: "Вежливый главный герой", value: "379" }, + { label: "Верные подчиненные", value: "328" }, + { label: "Взрослый главный герой", value: "295" }, + { label: "Видит то, чего не видят другие", value: "671" }, + { label: "Виртуальная реальность", value: "401" }, + { label: "Владелец магазина", value: "681" }, + { label: "Внезапная сила", value: "455" }, + { label: "Внезапное богатство", value: "765" }, + { label: "Внешний вид отличается от факт", value: "419" }, + { label: "Военные Летописи", value: "743" }, + { label: "Возвращение из другого мира", value: "696" }, + { label: "Войны", value: "181" }, + { label: "Вокалоид", value: "701" }, + { label: "Волшебники/Волшебницы", value: "539" }, + { label: "Волшебные звери", value: "308" }, + { label: "Воображаемый друг", value: "651" }, + { label: "Воры", value: "414" }, + { label: "Воскрешение", value: "199" }, + { label: "Враги становятся возлюбленными", value: "498" }, + { label: "Враги становятся союзниками", value: "562" }, + { label: "Врата в другой мир", value: "609" }, + { label: "Врачи", value: "376" }, + { label: "Временной парадокс", value: "275" }, + { label: "Всемогущий главный герой", value: "169" }, + { label: "Вторжение на землю", value: "198" }, + { label: "Второй шанс", value: "231" }, + { label: "Вуайеризм", value: "790" }, + { label: "Выживание", value: "380" }, + { label: "Высокомерные персонажи", value: "361" }, + { label: "Гадание", value: "595" }, + { label: "Гарем рабов", value: "777" }, + { label: "Гг - женщина", value: "186" }, + { label: "Гг - мужчина", value: "166" }, + { label: "Гг силен с самого начала", value: "171" }, + { label: "Геймеры", value: "392" }, + { label: "Генералы", value: "326" }, + { label: "Генетические модификации", value: "657" }, + { label: "Гениальный главный герой", value: "617" }, + { label: "Герои", value: "285" }, + { label: "Героиня — сорванец", value: "583" }, + { label: "Героическая фантастика", value: "140" }, + { label: "Героическое фэнтези", value: "107" }, + { label: "Герой влюбляется первым", value: "187" }, + { label: "Гетерохромия", value: "570" }, + { label: "Гильдии", value: "411" }, + { label: "Гипнотизм", value: "755" }, + { label: "Главный герой — бог", value: "547" }, + { label: "Главный герой — гуманоид", value: "636" }, + { label: "Главный герой — наполовину чел", value: "445" }, + { label: "Главный герой — отец", value: "792" }, + { label: "Главный герой — раб", value: "780" }, + { label: "Главный герой — ребенок", value: "488" }, + { label: "Главный герой — рубака", value: "476" }, + { label: "Главный герой влюбляется первы", value: "683" }, + { label: "Главный герой играет роль", value: "472" }, + { label: "Главный герой носит очки", value: "672" }, + { label: "Главный герой пацифист", value: "698" }, + { label: "Гладиаторы", value: "603" }, + { label: "Глуповатый главный герой", value: "385" }, + { label: "Гоблины", value: "586" }, + { label: "Големы", value: "620" }, + { label: "Гомункул", value: "789" }, + { label: "Горничные", value: "459" }, + { label: "Городское фэнтези", value: "105" }, + { label: "Госпиталь", value: "800" }, + { label: "Готовка", value: "302" }, + { label: "Гриндинг", value: "393" }, + { label: "Дао Компаньон", value: "462" }, + { label: "Даосизм", value: "760" }, + { label: "Дварфы", value: "323" }, + { label: "Двойная личность", value: "642" }, + { label: "Двойник", value: "601" }, + { label: "Дворецкий", value: "660" }, + { label: "Дворяне", value: "168" }, + { label: "Дворянство/Аристократия", value: "437" }, + { label: "Девушки-монстры", value: "669" }, + { label: "Демоническая техника культивац", value: "722" }, + { label: "Демоны", value: "152" }, + { label: "Денежный долг", value: "783" }, + { label: "Депрессия", value: "555" }, + { label: "Детектив", value: "116" }, + { label: "Детективы", value: "612" }, + { label: "Детская литература", value: "123" }, + { label: "Дискриминация", value: "162" }, + { label: "Добыча денег в приоритете", value: "396" }, + { label: "Документальная проза", value: "122" }, + { label: "Долгая разлука", value: "307" }, + { label: "Домашние дела", value: "290" }, + { label: "Домогательство", value: "470" }, + { label: "Драконы", value: "303" }, + { label: "Драконьи всадники", value: "607" }, + { label: "Древние времена", value: "221" }, + { label: "Древний Китай", value: "374" }, + { label: "Дружба", value: "217" }, + { label: "Друзья детства", value: "282" }, + { label: "Друзья становятся врагами", value: "567" }, + { label: "Друиды", value: "497" }, + { label: "Дух лисы", value: "784" }, + { label: "Духи/призраки", value: "172" }, + { label: "Духовный советник", value: "435" }, + { label: "Душевность", value: "633" }, + { label: "Души", value: "254" }, + { label: "Европейская атмосфера", value: "521" }, + { label: "Ёкаи", value: "575" }, + { label: "Есть аниме-адаптация", value: "154" }, + { label: "Есть видеоигра по мотивам", value: "552" }, + { label: "Есть манга", value: "155" }, + { label: "Есть манхва-адаптация", value: "517" }, + { label: "Есть маньхуа-адаптация", value: "388" }, + { label: "Есть сериал-адаптация", value: "492" }, + { label: "Есть фильм", value: "173" }, + { label: "Женища-наставник", value: "505" }, + { label: "Жесткая, двуличная личность", value: "346" }, + { label: "Жестокие персонажи", value: "503" }, + { label: "Жестокое обращение с ребенком", value: "654" }, + { label: "Жестокость", value: "245" }, + { label: "Животноводство", value: "754" }, + { label: "Животные черты", value: "528" }, + { label: "Жизнь в квартире", value: "606" }, + { label: "Жрицы", value: "615" }, + { label: "Заботливый главный герой", value: "288" }, + { label: "Забывчивый главный герой", value: "628" }, + { label: "Заговоры", value: "289" }, + { label: "Закалка тела", value: "362" }, + { label: "Законники", value: "756" }, + { label: "Замкнутый главный герой", value: "589" }, + { label: "Запечатанная сила", value: "430" }, + { label: "Застенчивые персонажи", value: "508" }, + { label: "Звери", value: "237" }, + { label: "Звери-компаньоны", value: "301" }, + { label: "Злой протагонист", value: "243" }, + { label: "Злые боги", value: "504" }, + { label: "Злые организации", value: "563" }, + { label: "Злые религии", value: "734" }, + { label: "Знаменитости", value: "473" }, + { label: "Знаменитый главный герой", value: "531" }, + { label: "Знания современного мира", value: "297" }, + { label: "Зомби", value: "409" }, + { label: "Игра на выживание", value: "274" }, + { label: "Игривый протагонист", value: "717" }, + { label: "Игровая система рейтинга", value: "391" }, + { label: "Игровые элементы", value: "266" }, + { label: "Игрушки (18+)", value: "676" }, + { label: "Из грязи в князи", value: "416" }, + { label: "Из женщины в мужчину", value: "709" }, + { label: "Из мужчины в женщину", value: "715" }, + { label: "Из полного в худого", value: "767" }, + { label: "Из слабого в сильного", value: "202" }, + { label: "Извращенный главный герой", value: "434" }, + { label: "Изгои", value: "534" }, + { label: "Изменение расы", value: "664" }, + { label: "Изменения внешнего вида", value: "300" }, + { label: "Изменения личности", value: "219" }, + { label: "Изнасилование", value: "229" }, + { label: "Изображения жестокости", value: "355" }, + { label: "ИИ", value: "235" }, + { label: "Империи", value: "748" }, + { label: "Инвалидность", value: "684" }, + { label: "Индустриализация", value: "292" }, + { label: "Инженер", value: "163" }, + { label: "Инцест", value: "525" }, + { label: "Искусственный интеллект", value: "236" }, + { label: "Исследования", value: "670" }, + { label: "Историческая проза", value: "119" }, + { label: "Исторические приключения", value: "120" }, + { label: "Исторический детектив", value: "117" }, + { label: "Исторический любовный роман", value: "138" }, + { label: "Историческое фэнтези", value: "110" }, + { label: "Каннибализм", value: "456" }, + { label: "Карточные игры", value: "682" }, + { label: "Киберпанк", value: "141" }, + { label: "Киберспорт", value: "686" }, + { label: "Кланы", value: "796" }, + { label: "Класс безработного", value: "735" }, + { label: "Клоны", value: "447" }, + { label: "Клубы", value: "216" }, + { label: "Книги", value: "427" }, + { label: "Книги навыков", value: "400" }, + { label: "Книжный червь", value: "519" }, + { label: "Коварство", value: "230" }, + { label: "Коллеги", value: "704" }, + { label: "Колледж/Университет", value: "726" }, + { label: "Кома", value: "776" }, + { label: "Командная работа", value: "496" }, + { label: "Комедийный оттенок", value: "582" }, + { label: "Комплекс неполноценности", value: "550" }, + { label: "Комплекс семейных отношений", value: "223" }, + { label: "Конкуренция", value: "746" }, + { label: "Контроль разума/сознания", value: "357" }, + { label: "Копейщик", value: "425" }, + { label: "Королевская битва", value: "797" }, + { label: "Королевская власть", value: "178" }, + { label: "Королевства", value: "259" }, + { label: "Короткий любовный роман", value: "136" }, + { label: "Коррупция", value: "457" }, + { label: "Космическая фантастика", value: "146" }, + { label: "Космические войны", value: "697" }, + { label: "Красивый герой", value: "226" }, + { label: "Крафт", value: "377" }, + { label: "Кризис личности", value: "640" }, + { label: "Кругосветное путешествие", value: "353" }, + { label: "Кудере", value: "506" }, + { label: "Кузены", value: "718" }, + { label: "Кузнец", value: "518" }, + { label: "Кукловоды", value: "501" }, + { label: "Куклы/марионетки", value: "624" }, + { label: "Культивация", value: "241" }, + { label: "Куннилингус", value: "667" }, + { label: "Легенды", value: "500" }, + { label: "Легкая жизнь", value: "644" }, + { label: "Ленивый главный герой", value: "621" }, + { label: "Лидерство", value: "494" }, + { label: "Лоли", value: "213" }, + { label: "Лотерея", value: "477" }, + { label: "Любовное фэнтези", value: "135" }, + { label: "Любовный треугольник", value: "218" }, + { label: "Любовь детства", value: "560" }, + { label: "Любовь с первого взгляда", value: "737" }, + { label: "Магические надписи", value: "453" }, + { label: "Магические печати", value: "369" }, + { label: "Магические технологии", value: "440" }, + { label: "Магия", value: "165" }, + { label: "Магия призыва", value: "418" }, + { label: "Мазохистские персонажи", value: "688" }, + { label: "Манипулятивные персонажи", value: "249" }, + { label: "Мания", value: "212" }, + { label: "Мастер на все руки", value: "466" }, + { label: "Мастурбация", value: "788" }, + { label: "Махо-сёдзё", value: "556" }, + { label: "Медицинские знания", value: "507" }, + { label: "Медленная романтическая линия", value: "232" }, + { label: "Медленное развитие на старте", value: "694" }, + { label: "Межпространственные путешестви", value: "404" }, + { label: "Менеджмент", value: "294" }, + { label: "Мертвый главный герой", value: "761" }, + { label: "Месть", value: "209" }, + { label: "Метаморфы", value: "335" }, + { label: "Меч и магия", value: "180" }, + { label: "Мечник", value: "647" }, + { label: "Мечты", value: "738" }, + { label: "Милая история", value: "725" }, + { label: "Милое дитя", value: "637" }, + { label: "Милый главный герой", value: "714" }, + { label: "Мировое дерево", value: "463" }, + { label: "Мистика", value: "151" }, + { label: "Мистический ореол вокруг семьи", value: "483" }, + { label: "Мифические звери", value: "491" }, + { label: "Мифология", value: "530" }, + { label: "Младшие братья", value: "785" }, + { label: "Младшие сестры", value: "527" }, + { label: "ММОРПГ (ЛитРПГ)", value: "395" }, + { label: "Множество перемещенных людей", value: "549" }, + { label: "Множество реальностей", value: "370" }, + { label: "Множество реинкарнированных лю", value: "330" }, + { label: "Модели", value: "678" }, + { label: "Молчаливый персонаж", value: "721" }, + { label: "Монстры", value: "192" }, + { label: "Мужская гей-пара", value: "706" }, + { label: "Мужчина-яндере", value: "267" }, + { label: "Музыка", value: "635" }, + { label: "Музыкальные группы", value: "634" }, + { label: "Мутации", value: "693" }, + { label: "Мутированные существа", value: "405" }, + { label: "Навык кражи", value: "662" }, + { label: "Навязчивая любовь", value: "206" }, + { label: "Наемники", value: "412" }, + { label: "Назойливый возлюбленный", value: "561" }, + { label: "Наивный главный герой", value: "189" }, + { label: "Наркотики", value: "689" }, + { label: "Нарциссический главный герой", value: "532" }, + { label: "Насилие сексуального характера", value: "774" }, + { label: "Наследование", value: "452" }, + { label: "Научная фантастика", value: "144" }, + { label: "Национализм", value: "406" }, + { label: "Не блещущий внешне главный гер", value: "415" }, + { label: "Не родные братья и сестры", value: "526" }, + { label: "Небеса", value: "568" }, + { label: "Небесное испытание", value: "367" }, + { label: "Негуманоидный главный герой", value: "659" }, + { label: "Недоверчивый главный герой", value: "546" }, + { label: "Недооцененный главный герой", value: "258" }, + { label: "Недоразумения", value: "309" }, + { label: "Неизлечимая болезнь", value: "196" }, + { label: "Некромант", value: "397" }, + { label: "Нелинейная история", value: "269" }, + { label: "Ненавистный главный герой", value: "548" }, + { label: "Ненадежный рассказчик", value: "277" }, + { label: "Нерезиденты", value: "772" }, + { label: "Нерешительный главный герой", value: "744" }, + { label: "Несерьезный главный герой", value: "384" }, + { label: "Несколько временных линий", value: "576" }, + { label: "Несколько гг", value: "268" }, + { label: "Несколько главных героев", value: "537" }, + { label: "Несколько идентичностей", value: "652" }, + { label: "Несколько личностей", value: "536" }, + { label: "Нетораре", value: "732" }, + { label: "Нетори", value: "514" }, + { label: "Неудачливый главный герой", value: "618" }, + { label: "Ниндзя", value: "441" }, + { label: "Новелла", value: "103" }, + { label: "Обещание из детства", value: "742" }, + { label: "Обманщик", value: "485" }, + { label: "Обмен телами", value: "214" }, + { label: "Обнаженка", value: "490" }, + { label: "Обольщение", value: "731" }, + { label: "Оборотни", value: "661" }, + { label: "Обратный гарем", value: "351" }, + { label: "Общество монстров", value: "329" }, + { label: "Обязательство", value: "602" }, + { label: "Огнестрельное оружие", value: "291" }, + { label: "Ограниченное время жизни", value: "194" }, + { label: "Одержимость", value: "512" }, + { label: "Одинокий главный герой", value: "394" }, + { label: "Одиночество", value: "306" }, + { label: "Одиночное проживание", value: "313" }, + { label: "Околосмертные переживания", value: "759" }, + { label: "Оммёдзи", value: "597" }, + { label: "Омоложение", value: "338" }, + { label: "Организованная преступность", value: "540" }, + { label: "Оргия", value: "775" }, + { label: "Орки", value: "331" }, + { label: "Освоение навыков", value: "336" }, + { label: "Основано на аниме", value: "605" }, + { label: "Основано на видео игре", value: "720" }, + { label: "Основано на визуальной новелле", value: "793" }, + { label: "Основано на песне", value: "700" }, + { label: "Основано на фильме", value: "604" }, + { label: "Осторожный главный герой", value: "240" }, + { label: "Отаку", value: "571" }, + { label: "Открытый космос", value: "264" }, + { label: "Отношения в сети", value: "727" }, + { label: "Отношения между людьми и нелюд", value: "311" }, + { label: "Отношения на расстоянии", value: "791" }, + { label: "Отношения Сенпай-Коухай", value: "572" }, + { label: "Отношения ученика и учителя", value: "515" }, + { label: "Отношения учитель-ученик", value: "429" }, + { label: "Отношения хозяин-слуга", value: "312" }, + { label: "Отомэ игра", value: "733" }, + { label: "Отсутствие здравого смысла", value: "645" }, + { label: "Отсутствие родителей", value: "625" }, + { label: "Офисный роман", value: "707" }, + { label: "Официанты", value: "703" }, + { label: "Охотники", value: "378" }, + { label: "Очаровательный главный герой", value: "687" }, + { label: "Падшее дворянство", value: "655" }, + { label: "Падшие ангелы", value: "565" }, + { label: "Пайзури", value: "665" }, + { label: "Паразиты", value: "591" }, + { label: "Параллельные миры", value: "207" }, + { label: "Парк", value: "265" }, + { label: "Парк развлечений", value: "632" }, + { label: "Пародия", value: "407" }, + { label: "Певцы/Певицы", value: "739" }, + { label: "Первая любовь", value: "729" }, + { label: "Первоисточник новеллы — манга", value: "611" }, + { label: "Первый раз", value: "787" }, + { label: "Перемещение в игровой мир", value: "588" }, + { label: "Перемещение в иной мир", value: "750" }, + { label: "Перерождение в ином мире", value: "751" }, + { label: "Переселение души/трансмиграция", value: "257" }, + { label: "Персонаж использует щит", value: "666" }, + { label: "Петля времени", value: "200" }, + { label: "Пираты", value: "771" }, + { label: "Писатели", value: "482" }, + { label: "Питомцы", value: "349" }, + { label: "Племенное общество", value: "381" }, + { label: "Повелитель демонов", value: "283" }, + { label: "Подземелья", value: "322" }, + { label: "Подростковая проза", value: "114" }, + { label: "Пожелания", value: "278" }, + { label: "Познание Дао", value: "443" }, + { label: "Покинутое дитя", value: "590" }, + { label: "Полигамия", value: "386" }, + { label: "Политика", value: "170" }, + { label: "Политический роман", value: "133" }, + { label: "Полиция", value: "764" }, + { label: "Полулюди", value: "420" }, + { label: "Популярный любовный интерес", value: "342" }, + { label: "Постапокалипсис", value: "145" }, + { label: "Постапокалиптика", value: "184" }, + { label: "Потерянные цивилизации", value: "749" }, + { label: "Похищения людей", value: "723" }, + { label: "Поэзия", value: "111" }, + { label: "Правонарушители", value: "713" }, + { label: "Прагматичный главный герой", value: "592" }, + { label: "Преданный любовный интерес", value: "225" }, + { label: "Предательство", value: "222" }, + { label: "Предвидение", value: "177" }, + { label: "Прекрасная героиня", value: "158" }, + { label: "Преступники", value: "736" }, + { label: "Преступность", value: "204" }, + { label: "Призванный герой", value: "262" }, + { label: "Призраки", value: "176" }, + { label: "Принуждение к отношениям", value: "643" }, + { label: "Принцессы", value: "795" }, + { label: "Притворная пара", value: "641" }, + { label: "Причудливые персонажи", value: "387" }, + { label: "Пришельцы/инопланетяне", value: "197" }, + { label: "Программист", value: "691" }, + { label: "Проклятия", value: "174" }, + { label: "Промывание мозгов", value: "578" }, + { label: "Пропуск времени", value: "256" }, + { label: "Пророчества", value: "499" }, + { label: "Проститутки", value: "786" }, + { label: "Прошлое играет большую роль", value: "320" }, + { label: "Прыжки между мирами", value: "741" }, + { label: "Психические силы", value: "359" }, + { label: "Психопаты", value: "270" }, + { label: "Публицистика", value: "124" }, + { label: "Путешествие во времени", value: "201" }, + { label: "Пытка", value: "280" }, + { label: "Рабы", value: "778" }, + { label: "Развитие личности", value: "125" }, + { label: "Развод", value: "758" }, + { label: "Разумные предметы", value: "286" }, + { label: "Расизм", value: "408" }, + { label: "Рассказ", value: "185" }, + { label: "Расторжения помолвки", value: "343" }, + { label: "Расы зооморфов", value: "315" }, + { label: "Реализм", value: "246" }, + { label: "Ревность", value: "730" }, + { label: "Редакторы", value: "705" }, + { label: "Реинкарнация", value: "372" }, + { label: "Реинкарнация в монстра", value: "310" }, + { label: "Реинкарнация в объект", value: "711" }, + { label: "Религии", value: "616" }, + { label: "Репортеры", value: "781" }, + { label: "Ресторан", value: "680" }, + { label: "Решительный главный герой", value: "242" }, + { label: "Робкий главный герой", value: "763" }, + { label: "Родитель одиночка", value: "708" }, + { label: "Родительский комплекс", value: "587" }, + { label: "Родословная", value: "239" }, + { label: "Романтическая эротика", value: "132" }, + { label: "Романтический подсюжет", value: "271" }, + { label: "Рост персонажа", value: "215" }, + { label: "Рыцари", value: "260" }, + { label: "Садистские персонажи", value: "674" }, + { label: "Самоотверженный главный герой", value: "747" }, + { label: "Самурай", value: "677" }, + { label: "Сборник коротких историй", value: "520" }, + { label: "Связанные сюжетные линии", value: "535" }, + { label: "Святые", value: "752" }, + { label: "Священники", value: "413" }, + { label: "Сдержанный главный герой", value: "762" }, + { label: "Секретные организации", value: "417" }, + { label: "Секреты", value: "626" }, + { label: "Секс рабы", value: "779" }, + { label: "Семейный конфликт", value: "757" }, + { label: "Семь добродетелей", value: "334" }, + { label: "Семь смертных грехов", value: "253" }, + { label: "Семья", value: "347" }, + { label: "Сёнэн-ай подсюжет", value: "690" }, + { label: "Серийные убийцы", value: "557" }, + { label: "Сестринский комплекс", value: "558" }, + { label: "Сила духа", value: "373" }, + { label: "Сила, требующая платы за польз", value: "579" }, + { label: "Сильная пара", value: "228" }, + { label: "Сильный в сильнейшего", value: "442" }, + { label: "Сильный любовный интерес", value: "273" }, + { label: "Синдром восьмиклассника", value: "211" }, + { label: "Сироты", value: "319" }, + { label: "Система уровней", value: "305" }, + { label: "Системный администратор", value: "538" }, + { label: "Сказка", value: "126" }, + { label: "Скрытие истинной личности", value: "451" }, + { label: "Скрытие истинных способностей", value: "348" }, + { label: "Скрытный главный герой", value: "252" }, + { label: "Скрытые способности", value: "247" }, + { label: "Скульпторы", value: "448" }, + { label: "Слабо выраженная романтическая", value: "318" }, + { label: "Слабый главный герой", value: "298" }, + { label: "Слепой главный герой", value: "794" }, + { label: "Слуги", value: "333" }, + { label: "Слэш", value: "131" }, + { label: "Смерть", value: "175" }, + { label: "Смерть близких", value: "444" }, + { label: "Собственнические персонажи", value: "227" }, + { label: "Современная проза", value: "150" }, + { label: "Современность", value: "167" }, + { label: "Современный любовный роман", value: "137" }, + { label: "Сожительство", value: "544" }, + { label: "Создание армии", value: "287" }, + { label: "Создание артефактов", value: "382" }, + { label: "Создание клана", value: "513" }, + { label: "Создание королевства", value: "293" }, + { label: "Создание навыков", value: "337" }, + { label: "Создание секты", value: "469" }, + { label: "Солдаты/военные", value: "193" }, + { label: "Сон", value: "622" }, + { label: "Состоятельные персонажи", value: "190" }, + { label: "Социальная иерархия по силе", value: "255" }, + { label: "Социальная фантастика", value: "148" }, + { label: "Социальные изгои", value: "542" }, + { label: "Спасение мира", value: "638" }, + { label: "Специальные способности", value: "179" }, + { label: "Спокойный главный герой", value: "160" }, + { label: "Справедливый главный герой", value: "719" }, + { label: "Средневековье", value: "296" }, + { label: "Ссорящаяся пара", value: "383" }, + { label: "Сталкеры", value: "710" }, + { label: "Старение", value: "299" }, + { label: "Стимпанк", value: "139" }, + { label: "Стоические персонажи", value: "509" }, + { label: "Стокгольмский синдром", value: "675" }, + { label: "Стратег", value: "495" }, + { label: "Стратегические битвы", value: "272" }, + { label: "Стратегия", value: "798" }, + { label: "Стрелки", value: "522" }, + { label: "Стрельба из лука", value: "461" }, + { label: "Студенческий совет", value: "551" }, + { label: "Судьба", value: "364" }, + { label: "Суккубы", value: "545" }, + { label: "Супер герои", value: "799" }, + { label: "Суровая подготовка", value: "356" }, + { label: "Таинственная болезнь", value: "195" }, + { label: "Таинственное прошлое", value: "358" }, + { label: "Тайная личность", value: "399" }, + { label: "Тайные отношения", value: "768" }, + { label: "Танцоры", value: "782" }, + { label: "Телохранители", value: "516" }, + { label: "Темное фэнтези", value: "106" }, + { label: "Тентакли", value: "712" }, + { label: "Террористы", value: "574" }, + { label: "Технологический разрыв", value: "658" }, + { label: "Тихие персонажи", value: "600" }, + { label: "Толстый главный герой", value: "389" }, + { label: "Торговцы", value: "489" }, + { label: "Травля/буллинг", value: "210" }, + { label: "Травник", value: "724" }, + { label: "Трагическое прошлое", value: "276" }, + { label: "Трансплантация воспоминаний", value: "449" }, + { label: "Триллер", value: "134" }, + { label: "Трудолюбивый главный герой", value: "164" }, + { label: "Тюрьма", value: "541" }, + { label: "Убийства", value: "205" }, + { label: "Убийцы", value: "345" }, + { label: "Убийцы драконов", value: "646" }, + { label: "Уверенный главный герой", value: "363" }, + { label: "Удачливый главный герой", value: "478" }, + { label: "Ужасы", value: "112" }, + { label: "Укротитель монстров", value: "422" }, + { label: "Умения из прошлой жизни", value: "371" }, + { label: "Умная пара", value: "554" }, + { label: "Умный главный герой", value: "161" }, + { label: "Уникальная техника Культивации", value: "350" }, + { label: "Уникальное оружие", value: "426" }, + { label: "Управление бизнесом", value: "403" }, + { label: "Управление временем", value: "279" }, + { label: "Управление кровью", value: "753" }, + { label: "Упрямый главный герой", value: "695" }, + { label: "Уродливый главный герой", value: "766" }, + { label: "Ускоренный рост", value: "390" }, + { label: "Усыновленные дети", value: "543" }, + { label: "Усыновленный главный герой", value: "486" }, + { label: "Уход за детьми", value: "474" }, + { label: "Учителя", value: "431" }, + { label: "Фамильяры", value: "596" }, + { label: "Фанатизм", value: "650" }, + { label: "Фантастические существа", value: "410" }, + { label: "Фантастический детектив", value: "115" }, + { label: "Фанфик", value: "113" }, + { label: "Фанфикшн", value: "464" }, + { label: "Фармацевт", value: "728" }, + { label: "Фарминг", value: "458" }, + { label: "Феи", value: "316" }, + { label: "Фелляция", value: "631" }, + { label: "Фемслэш", value: "127" }, + { label: "Фениксы", value: "454" }, + { label: "Фетиш груди", value: "559" }, + { label: "Философия", value: "208" }, + { label: "Фильмы", value: "479" }, + { label: "Флэшбэки", value: "585" }, + { label: "Фобии", value: "220" }, + { label: "Фольклор", value: "529" }, + { label: "Футанари", value: "619" }, + { label: "Футуристический сеттинг", value: "460" }, + { label: "Фэнтези мир", value: "244" }, + { label: "Хакеры", value: "475" }, + { label: "Харизматический герой", value: "467" }, + { label: "Хикикомори/Затворники", value: "524" }, + { label: "Хитроумный главный герой", value: "224" }, + { label: "Хозяин подземелий", value: "608" }, + { label: "Холодный главный герой", value: "354" }, + { label: "Хорошие отношения с семьей", value: "566" }, + { label: "Хранители могил", value: "191" }, + { label: "Целители", value: "465" }, + { label: "Цзянши", value: "740" }, + { label: "Цундэрэ", value: "510" }, + { label: "Чаты", value: "629" }, + { label: "Человеческое оружие", value: "580" }, + { label: "Честный главный герой", value: "341" }, + { label: "Читы", value: "339" }, + { label: "Шантаж", value: "584" }, + { label: "Шеф-повар", value: "340" }, + { label: "Шикигами", value: "598" }, + { label: "Школа только для девочек", value: "668" }, + { label: "Шота", value: "573" }, + { label: "Шоу-бизнес", value: "481" }, + { label: "Шпионский детектив", value: "118" }, + { label: "Шпионы", value: "614" }, + { label: "Эволюция", value: "304" }, + { label: "Эгоистичный главный герой", value: "594" }, + { label: "Эйдетическая память", value: "468" }, + { label: "Экзорсизм", value: "564" }, + { label: "Экономика", value: "553" }, + { label: "Эксгибиционизм", value: "673" }, + { label: "Эксперименты с людьми", value: "248" }, + { label: "Элементальная магия", value: "471" }, + { label: "Эльфы", value: "284" }, + { label: "Эмоционально слабый гг", value: "770" }, + { label: "Эпизодический", value: "649" }, + { label: "Эпическое фэнтези", value: "109" }, + { label: "Эротическая фантастика", value: "129" }, + { label: "Эротический фанфик", value: "128" }, + { label: "Эротическое фэнтези", value: "130" }, + { label: "Юмористическая фантастика", value: "143" }, + { label: "Юмористическое фэнтези", value: "108" }, + { label: "Юный любовный интерес", value: "511" }, + { label: "Яды", value: "484" }, + { label: "Языкастые персонажи", value: "480" }, + { label: "Языковой барьер", value: "182" }, + { label: "Яндере", value: "314" }, + { label: "Японские силы самообороны", value: "610" }, + { label: "Ярко выраженная романтика", value: "365" }, + { label: "R-15 Японское возрастное огр.", value: "332" }, + { label: "R-18", value: "424" }, + ], + type: FilterTypes.ExcludableCheckboxGroup, + }, + types: { + label: "Типы:", + value: [], + options: [ + { label: "Авторское", value: "1" }, + { label: "Другое", value: "7" }, + { label: "Запад", value: "5" }, + { label: "Китай", value: "4" }, + { label: "Корея", value: "3" }, + { label: "Фанфики", value: "6" }, + { label: "Япония", value: "2" }, + ], + type: FilterTypes.CheckboxGroup, + }, + status: { + label: "Статус проекта:", + value: [], + options: [ + { label: "Анонс", value: "4" }, + { label: "Закончен", value: "0" }, + { label: "Заморожен", value: "2" }, + { label: "Лицензировано", value: "5" }, + { label: "Нет переводчика", value: "3" }, + { label: "Продолжается", value: "1" }, + ], + type: FilterTypes.CheckboxGroup, + }, + age_limit: { + label: "Возрастной рейтинг:", + value: [], + options: [ + { label: "16+", value: "1" }, + { label: "18+", value: "2" }, + { label: "Для всех", value: "0" }, + ], + type: FilterTypes.CheckboxGroup, + }, + } satisfies Filters; +} + +export default new ReN(); + +interface responseNovels { + id: number; + secondary_name: string; + main_name: string; + dir: string; + is_licensed: boolean; + issue_year: number; + avg_rating: string; + admin_rating: string; + type: string; + total_views: number; + total_votes: number; + cover_high: string; + cover_mid: string; + cover_low: string; + is_yaoi: boolean; + is_erotic: boolean; + bookmark_type?: null; + genres?: ItemEntity[] | null; + img: Img; + categories?: ItemEntity[] | null; + rus_name?: string; + en_name?: string; +} + +interface Img { + high?: string; + mid?: string; + low: string; +} + +interface responseNovel { + id: number; + img: Img; + secondary_name: string; + main_name: string; + another_name: string; + dir: string; + description: string; + issue_year: number; + avg_rating: string; + admin_rating: string; + count_rating: number; + age_limit: number; + status: ItemEntity; + count_bookmarks: number; + total_votes: number; + total_views: number; + type: ItemEntity; + genres?: ItemEntity[] | null; + categories?: ItemEntity[] | null; + publishers?: PublishersEntity[] | null; + bookmark_type?: null; + branches?: BranchesEntity[] | null; + count_chapters: number; + first_chapter: FirstChapter; + continue_reading?: null; + is_licensed: boolean; + newlate_id?: null; + newlate_title?: null; + related?: null; + uploaded: number; + can_post_comments: boolean; + adaptation: Adaptation | null; +} + +interface Adaptation { + main_name: string; + secondary_name: string; + dir: string; + img: string; +} + +interface ItemEntity { + id: number; + name: string; +} +interface PublishersEntity { + id: number; + name: string; + img: string; + dir: string; + tagline: string; + type: string; +} +interface BranchesEntity { + id: number; + img: string; + publishers?: PublishersEntity[] | null; + subscribed: boolean; + total_votes: number; + count_chapters: number; +} +interface FirstChapter { + id: number; + tome: number; + chapter: string; +} + +interface responseСhapters { + id: number; + rated?: null; + viewed?: null; + is_bought?: boolean | null; + publishers?: PublishersEntity[] | null; + tome: number; + index: number; + chapter: string; + name: string; + price?: null | number; + score: number; + pub_date?: null; + upload_date: string; + is_paid: boolean; +} + +interface responseСhapter { + id: number; + chapter: string; + name: string; + content: string; + score: number; + upload_date: string; + is_paid: boolean; + title_id: number; + volume_id: number; + branch_id: number; + price?: null; + pub_date?: null; + index: number; + publishers?: PublishersEntity[] | null; + tome: number; +} diff --git a/plugins/spanish/tunovelaligera.ts b/plugins/spanish/tunovelaligera.ts index 0e0b98a..cc87f56 100644 --- a/plugins/spanish/tunovelaligera.ts +++ b/plugins/spanish/tunovelaligera.ts @@ -1,385 +1,375 @@ -import { CheerioAPI, load as parseHTML } from "cheerio"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { Plugin } from "@typings/plugin"; -import { showToast } from "@libs/showToast"; -import { defaultCover } from "@libs/defaultCover"; -import { NovelStatus } from "@libs/novelStatus"; - -class TuNovelaLigera implements Plugin.PluginBase { - id = "tunovelaligera"; - name = "TuNovelaLigera"; - icon = "src/es/tunovelaligera/icon.png"; - site = "https://tunovelaligera.com/"; - version = "1.0.0"; - userAgent = ""; - cookieString = ""; - - async popularNovels( - pageNo: number, - { filters }: Plugin.PopularNovelsOptions - ): Promise { - let link = `${this.site}`; - - link += filters.genres ? `genero/` + filters.genres : "novelas"; - - link += `/page/${pageNo}`; - - link += filters.order; - - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(link, { headers }); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - - loadedCheerio(".page-item-detail").each((i, el) => { - const novelName = loadedCheerio(el).find(".h5 > a").text(); - const novelCover = loadedCheerio(el).find("img").attr("src"); - const novelUrl = loadedCheerio(el).find(".h5 > a").attr("href"); - - if (!novelUrl) return; - const novel = { - name: novelName, - cover: novelCover, - url: novelUrl, - }; - - novels.push(novel); - }); - - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const url = novelUrl; - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(url, { headers }); - const body = await result.text(); - - let loadedCheerio = parseHTML(body); - - const novel: Plugin.SourceNovel = { - url, - chapters: [], - }; - - loadedCheerio(".manga-title-badges").remove(); - - novel.name = loadedCheerio(".post-title > h1").text().trim(); - - let novelCover = loadedCheerio(".summary_image > a > img"); - - novel.cover = - novelCover.attr("data-src") || - novelCover.attr("src") || - novelCover.attr("data-cfsrc") || - defaultCover; - - loadedCheerio(".post-content_item").each(function () { - const detailName = loadedCheerio(this) - .find(".summary-heading > h5") - .text() - .trim(); - const detail = loadedCheerio(this) - .find(".summary-content") - .text() - .trim(); - - switch (detailName) { - case "Generos": - novel.genres = detail.replace(/, /g, ","); - break; - case "Autores": - novel.author = detail; - break; - case "Estado": - novel.status = - detail.includes("OnGoing") || - detail.includes("Updating") - ? NovelStatus.Ongoing - : NovelStatus.Completed; - break; - } - }); - - novel.summary = loadedCheerio("div.summary__content > p").text().trim(); - - let chapter: Plugin.ChapterItem[] = []; - - const delay = (ms: number) => new Promise((res) => setTimeout(res, ms)); - let lastPage = 1; - lastPage = +loadedCheerio(".lcp_paginator li:last").prev().text(); - - const getChapters = async () => { - const n = url.split("/"); - const novelName = n[4]; - showToast("Cargando desde Archivo..."); - - const formData = new FormData(); - formData.append("action", "madara_load_more"); - formData.append("page", "0"); - formData.append("template", "html/loop/content"); - formData.append("vars[category_name]", novelName); - formData.append("vars[posts_per_page]", "10000"); - - const result = await fetchApi( - `${this.site}wp-admin/admin-ajax.php`, - { - method: "POST", - body: formData, - headers, - } - ); - const text = await result.text(); - - loadedCheerio = parseHTML(text); - - loadedCheerio(".heading").each((i, el) => { - const chapterName = loadedCheerio(el) - .text() - .replace(/[\t\n]/g, "") - .trim(); - const releaseDate = null; - const chapterUrl = - loadedCheerio(el).find("a").attr("href") || ""; - - chapter.push({ - name: chapterName, - url: chapterUrl, - releaseTime: releaseDate, - }); - }); - return chapter.reverse(); - }; - - const getPageChapters = async () => { - for (let i = 1; i <= lastPage; i++) { - const chaptersUrl = `${novelUrl}?lcp_page0=${i}`; - showToast(`Cargando desde la página ${i}/${lastPage}...`); - const result = await fetchApi(chaptersUrl, { headers }); - const chaptersHTML = await result.text(); - - let chapterCheerio = parseHTML(chaptersHTML); - - chapterCheerio(".lcp_catlist li").each((i, el) => { - const chapterName = chapterCheerio(el) - .find("a") - .text() - .replace(/[\t\n]/g, "") - .trim(); - - const releaseDate = chapterCheerio(el) - .find("span") - .text() - .trim(); - - const chapterUrl = - chapterCheerio(el).find("a").attr("href") || ""; - - chapter.push({ - name: chapterName, - releaseTime: releaseDate, - url: chapterUrl, - }); - }); - await delay(1000); - } - return chapter.reverse(); - }; - - novel.chapters = await getChapters(); - - if (!novel.chapters.length) { - showToast("¡Archivo no encontrado!"); - await delay(1000); - novel.chapters = await getPageChapters(); - } - - return novel; - } - - async parseChapter(chapterUrl: string): Promise { - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(chapterUrl); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - loadedCheerio("#hola_siguiente").next().find("div").remove(); - const chapterText = - loadedCheerio("#hola_siguiente").next().html() || ""; - - return chapterText; - } - - async searchNovels( - searchTerm: string, - pageNo?: number | undefined - ): Promise { - const url = `${this.site}?s=${searchTerm}&post_type=wp-manga`; - - const result = await fetchApi(url); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - - loadedCheerio(".c-tabs-item__content").each((i, el) => { - const novelName = loadedCheerio(el).find(".h4 > a").text(); - const novelCover = loadedCheerio(el).find("img").attr("src"); - const novelUrl = loadedCheerio(el).find(".h4 > a").attr("href"); - if (!novelUrl) return; - novels.push({ - name: novelName, - url: novelUrl, - cover: novelCover, - }); - }); - - return novels; - } - - async fetchImage(url: string): Promise { - return await fetchFile(url); - } - - filters = { - order: { - value: "?m_orderby=rating", - label: "Ordenado por", - options: [ - { label: "Lo mas reciente", value: "?m_orderby=latest" }, - - { label: "A-Z", value: "?m_orderby=alphabet" }, - - { label: "Clasificación", value: "?m_orderby=rating" }, - - { label: "Trending", value: "?m_orderby=trending" }, - - { label: "Mas visto", value: "?m_orderby=views" }, - - { label: "Nuevo", value: "?m_orderby=new-manga" }, - ], - type: FilterTypes.Picker, - }, - genres: { - value: "", - label: "Generos", - options: [ - { label: "None", value: "" }, - { label: "Acción", value: "accion" }, - - { label: "Adulto", value: "adulto" }, - - { label: "Artes Marciales", value: "artes-marciales" }, - - { label: "Aventura", value: "aventura" }, - - { label: "Ciencia Ficción", value: "ciencia-ficcion" }, - - { label: "Comedia", value: "comedia" }, - - { label: "Deportes", value: "deportes" }, - - { label: "Drama", value: "drama" }, - - { label: "Eastern Fantasy", value: "eastern-fantasy" }, - - { label: "Ecchi", value: "ecchi" }, - - { label: "FanFiction", value: "fan-fiction" }, - - { label: "Fantasía", value: "fantasia" }, - - { label: "Fantasía oriental", value: "fantasia-oriental" }, - - { label: "Ficción Romántica", value: "ficcion-romantica" }, - - { label: "Gender Bender", value: "gender-bender" }, - - { label: "Harem", value: "harem" }, - - { label: "Histórico", value: "historico" }, - - { label: "Horror", value: "horror" }, - - { label: "Josei", value: "josei" }, - - { label: "Maduro", value: "maduro" }, - - { label: "Mecha", value: "mecha" }, - - { label: "Misterio", value: "misterio" }, - - { label: "Novela China", value: "novela-china" }, - - { label: "Novela FanFiction", value: "novela-fanfiction" }, - - { label: "Novela Japonesa", value: "novela-japonesa" }, - - { label: "Novela Koreana", value: "novela-koreana" }, - - { label: "Novela ligera", value: "novela-ligera" }, - - { label: "Novela original", value: "novela-original" }, - - { label: "Novela Web", value: "web-novel" }, - - { label: "Psicológico", value: "psicologico" }, - - { label: "Realismo Mágico", value: "realismo-magico" }, - - { label: "Recuento de vida", value: "recuento-de-vida" }, - - { label: "Romance", value: "romance" }, - - { - label: "Romance contemporáneo", - value: "romance-contemporaneo", - }, - - { label: "Romance Moderno", value: "romance-moderno" }, - - { label: "Seinen", value: "seinen" }, - - { label: "Shoujo", value: "shoujo" }, - - { label: "Shounen", value: "shounen" }, - - { label: "Sobrenatural", value: "sobrenatural" }, - - { label: "Tragedia", value: "tragedia" }, - - { label: "Vampiros", value: "vampiros" }, - - { label: "Vida Escolar", value: "vida-escolar" }, - - { label: "Western Fantasy", value: "western-fantasy" }, - - { label: "Wuxia", value: "wuxia" }, - - { label: "Xianxia", value: "xianxia" }, - - { label: "Xuanhuan", value: "xuanhuan" }, - - { label: "Yaoi", value: "yaoi" }, - ], - type: FilterTypes.Picker, - }, - } satisfies Filters; -} - -export default new TuNovelaLigera(); +import { CheerioAPI, load as parseHTML } from "cheerio"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { Plugin } from "@typings/plugin"; +import { showToast } from "@libs/showToast"; +import { defaultCover } from "@libs/defaultCover"; +import { NovelStatus } from "@libs/novelStatus"; + +class TuNovelaLigera implements Plugin.PluginBase { + id = "tunovelaligera"; + name = "TuNovelaLigera"; + icon = "src/es/tunovelaligera/icon.png"; + site = "https://tunovelaligera.com/"; + version = "1.0.0"; + userAgent = ""; + + async popularNovels( + pageNo: number, + { filters }: Plugin.PopularNovelsOptions + ): Promise { + let link = `${this.site}`; + + link += filters.genres ? `genero/` + filters.genres : "novelas"; + + link += `/page/${pageNo}`; + + link += filters.order; + + const headers = new Headers(); + const result = await fetchApi(link, { headers }); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + loadedCheerio(".page-item-detail").each((i, el) => { + const novelName = loadedCheerio(el).find(".h5 > a").text(); + const novelCover = loadedCheerio(el).find("img").attr("src"); + const novelUrl = loadedCheerio(el).find(".h5 > a").attr("href"); + + if (!novelUrl) return; + const novel = { + name: novelName, + cover: novelCover, + url: novelUrl, + }; + + novels.push(novel); + }); + + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const url = novelUrl; + const headers = new Headers(); + const result = await fetchApi(url, { headers }); + const body = await result.text(); + + let loadedCheerio = parseHTML(body); + + const novel: Plugin.SourceNovel = { + url, + chapters: [], + }; + + loadedCheerio(".manga-title-badges").remove(); + + novel.name = loadedCheerio(".post-title > h1").text().trim(); + + let novelCover = loadedCheerio(".summary_image > a > img"); + + novel.cover = + novelCover.attr("data-src") || + novelCover.attr("src") || + novelCover.attr("data-cfsrc") || + defaultCover; + + loadedCheerio(".post-content_item").each(function () { + const detailName = loadedCheerio(this) + .find(".summary-heading > h5") + .text() + .trim(); + const detail = loadedCheerio(this) + .find(".summary-content") + .text() + .trim(); + + switch (detailName) { + case "Generos": + novel.genres = detail.replace(/, /g, ","); + break; + case "Autores": + novel.author = detail; + break; + case "Estado": + novel.status = + detail.includes("OnGoing") || + detail.includes("Updating") + ? NovelStatus.Ongoing + : NovelStatus.Completed; + break; + } + }); + + novel.summary = loadedCheerio("div.summary__content > p").text().trim(); + + let chapter: Plugin.ChapterItem[] = []; + + const delay = (ms: number) => new Promise((res) => setTimeout(res, ms)); + let lastPage = 1; + lastPage = +loadedCheerio(".lcp_paginator li:last").prev().text(); + + const getChapters = async () => { + const n = url.split("/"); + const novelName = n[4]; + showToast("Cargando desde Archivo..."); + + const formData = new FormData(); + formData.append("action", "madara_load_more"); + formData.append("page", "0"); + formData.append("template", "html/loop/content"); + formData.append("vars[category_name]", novelName); + formData.append("vars[posts_per_page]", "10000"); + + const result = await fetchApi( + `${this.site}wp-admin/admin-ajax.php`, + { + method: "POST", + body: formData, + headers, + } + ); + const text = await result.text(); + + loadedCheerio = parseHTML(text); + + loadedCheerio(".heading").each((i, el) => { + const chapterName = loadedCheerio(el) + .text() + .replace(/[\t\n]/g, "") + .trim(); + const releaseDate = null; + const chapterUrl = + loadedCheerio(el).find("a").attr("href") || ""; + + chapter.push({ + name: chapterName, + url: chapterUrl, + releaseTime: releaseDate, + }); + }); + return chapter.reverse(); + }; + + const getPageChapters = async () => { + for (let i = 1; i <= lastPage; i++) { + const chaptersUrl = `${novelUrl}?lcp_page0=${i}`; + showToast(`Cargando desde la página ${i}/${lastPage}...`); + const result = await fetchApi(chaptersUrl, { headers }); + const chaptersHTML = await result.text(); + + let chapterCheerio = parseHTML(chaptersHTML); + + chapterCheerio(".lcp_catlist li").each((i, el) => { + const chapterName = chapterCheerio(el) + .find("a") + .text() + .replace(/[\t\n]/g, "") + .trim(); + + const releaseDate = chapterCheerio(el) + .find("span") + .text() + .trim(); + + const chapterUrl = + chapterCheerio(el).find("a").attr("href") || ""; + + chapter.push({ + name: chapterName, + releaseTime: releaseDate, + url: chapterUrl, + }); + }); + await delay(1000); + } + return chapter.reverse(); + }; + + novel.chapters = await getChapters(); + + if (!novel.chapters.length) { + showToast("¡Archivo no encontrado!"); + await delay(1000); + novel.chapters = await getPageChapters(); + } + + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const headers = new Headers(); + const result = await fetchApi(chapterUrl); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + loadedCheerio("#hola_siguiente").next().find("div").remove(); + const chapterText = + loadedCheerio("#hola_siguiente").next().html() || ""; + + return chapterText; + } + + async searchNovels( + searchTerm: string, + pageNo?: number | undefined + ): Promise { + const url = `${this.site}?s=${searchTerm}&post_type=wp-manga`; + + const result = await fetchApi(url); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + loadedCheerio(".c-tabs-item__content").each((i, el) => { + const novelName = loadedCheerio(el).find(".h4 > a").text(); + const novelCover = loadedCheerio(el).find("img").attr("src"); + const novelUrl = loadedCheerio(el).find(".h4 > a").attr("href"); + if (!novelUrl) return; + novels.push({ + name: novelName, + url: novelUrl, + cover: novelCover, + }); + }); + + return novels; + } + + async fetchImage(url: string): Promise { + return await fetchFile(url); + } + + filters = { + order: { + value: "?m_orderby=rating", + label: "Ordenado por", + options: [ + { label: "Lo mas reciente", value: "?m_orderby=latest" }, + + { label: "A-Z", value: "?m_orderby=alphabet" }, + + { label: "Clasificación", value: "?m_orderby=rating" }, + + { label: "Trending", value: "?m_orderby=trending" }, + + { label: "Mas visto", value: "?m_orderby=views" }, + + { label: "Nuevo", value: "?m_orderby=new-manga" }, + ], + type: FilterTypes.Picker, + }, + genres: { + value: "", + label: "Generos", + options: [ + { label: "None", value: "" }, + { label: "Acción", value: "accion" }, + + { label: "Adulto", value: "adulto" }, + + { label: "Artes Marciales", value: "artes-marciales" }, + + { label: "Aventura", value: "aventura" }, + + { label: "Ciencia Ficción", value: "ciencia-ficcion" }, + + { label: "Comedia", value: "comedia" }, + + { label: "Deportes", value: "deportes" }, + + { label: "Drama", value: "drama" }, + + { label: "Eastern Fantasy", value: "eastern-fantasy" }, + + { label: "Ecchi", value: "ecchi" }, + + { label: "FanFiction", value: "fan-fiction" }, + + { label: "Fantasía", value: "fantasia" }, + + { label: "Fantasía oriental", value: "fantasia-oriental" }, + + { label: "Ficción Romántica", value: "ficcion-romantica" }, + + { label: "Gender Bender", value: "gender-bender" }, + + { label: "Harem", value: "harem" }, + + { label: "Histórico", value: "historico" }, + + { label: "Horror", value: "horror" }, + + { label: "Josei", value: "josei" }, + + { label: "Maduro", value: "maduro" }, + + { label: "Mecha", value: "mecha" }, + + { label: "Misterio", value: "misterio" }, + + { label: "Novela China", value: "novela-china" }, + + { label: "Novela FanFiction", value: "novela-fanfiction" }, + + { label: "Novela Japonesa", value: "novela-japonesa" }, + + { label: "Novela Koreana", value: "novela-koreana" }, + + { label: "Novela ligera", value: "novela-ligera" }, + + { label: "Novela original", value: "novela-original" }, + + { label: "Novela Web", value: "web-novel" }, + + { label: "Psicológico", value: "psicologico" }, + + { label: "Realismo Mágico", value: "realismo-magico" }, + + { label: "Recuento de vida", value: "recuento-de-vida" }, + + { label: "Romance", value: "romance" }, + + { + label: "Romance contemporáneo", + value: "romance-contemporaneo", + }, + + { label: "Romance Moderno", value: "romance-moderno" }, + + { label: "Seinen", value: "seinen" }, + + { label: "Shoujo", value: "shoujo" }, + + { label: "Shounen", value: "shounen" }, + + { label: "Sobrenatural", value: "sobrenatural" }, + + { label: "Tragedia", value: "tragedia" }, + + { label: "Vampiros", value: "vampiros" }, + + { label: "Vida Escolar", value: "vida-escolar" }, + + { label: "Western Fantasy", value: "western-fantasy" }, + + { label: "Wuxia", value: "wuxia" }, + + { label: "Xianxia", value: "xianxia" }, + + { label: "Xuanhuan", value: "xuanhuan" }, + + { label: "Yaoi", value: "yaoi" }, + ], + type: FilterTypes.Picker, + }, + } satisfies Filters; +} + +export default new TuNovelaLigera(); diff --git a/plugins/turkish/lshnovel.ts b/plugins/turkish/lshnovel.ts index 36a2d5c..a4ba337 100644 --- a/plugins/turkish/lshnovel.ts +++ b/plugins/turkish/lshnovel.ts @@ -1,323 +1,310 @@ -import { load as parseHTML } from "cheerio"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { FilterTypes, Filters } from "@libs/filterInputs"; -import { Plugin } from "@typings/plugin"; - -class LSHNovel implements Plugin.PluginBase { - id = "lshnovel"; - name = "Liebe Schnee Hiver Novel"; - icon = "multisrc/wpmangastream/icons/lshnovel.png"; - site = "https://lshnovel.com/"; - version = "1.0.0"; - userAgent = ""; - cookieString = ""; - async popularNovels( - pageNo: number, - { filters }: Plugin.PopularNovelsOptions - ): Promise { - let link = `${this.site}series/?page=${pageNo}`; - - if (filters.genres.value.length) { - link += filters.genres.value.map((i) => `&genre[]=${i}`).join(""); - } - - if (filters.type.value.length) - link += filters.type.value.map((i) => `&lang[]=${i}`).join(""); - - link += "&status=" + filters.status; - - link += "&order=" + filters.order; - - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const body = await fetchApi(link, { headers }).then((result) => - result.text() - ); - - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - - loadedCheerio("article.bs").each(function () { - const novelName = loadedCheerio(this).find(".ntitle").text().trim(); - let image = loadedCheerio(this).find("img"); - const novelCover = image.attr("data-src") || image.attr("src"); - - const novelUrl = loadedCheerio(this).find("a").attr("href"); - - if (!novelUrl) return; - - const novel = { - name: novelName, - cover: novelCover, - url: novelUrl, - }; - - novels.push(novel); - }); - - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const url = novelUrl; - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(url, { headers }); - const body = await result.text(); - - let loadedCheerio = parseHTML(body); - - const novel: Plugin.SourceNovel = { - url, - chapters: [], - }; - - novel.name = loadedCheerio(".entry-title").text(); - - novel.cover = - loadedCheerio("img.wp-post-image").attr("data-src") || - loadedCheerio("img.wp-post-image").attr("src"); - - loadedCheerio("div.spe > span").each(function () { - const detailName = loadedCheerio(this).find("b").text().trim(); - const detail = loadedCheerio(this) - .find("b") - .remove() - .end() - .text() - .trim(); - - switch (detailName) { - case "Yazar:": - novel.author = detail; - break; - case "Seviye:": - novel.status = detail; - break; - } - }); - - novel.genres = loadedCheerio(".genxed") - .text() - .trim() - .replace(/\s/g, ","); - - loadedCheerio('div[itemprop="description"] h3,p.a,strong').remove(); - novel.summary = loadedCheerio('div[itemprop="description"]') - .find("br") - .replaceWith("\n") - .end() - .text(); - - let chapter: Plugin.ChapterItem[] = []; - - loadedCheerio(".eplister") - .find("li") - .each(function () { - const chapterName = - loadedCheerio(this).find(".epl-num").text() + - " - " + - loadedCheerio(this).find(".epl-title").text(); - - const releaseDate = loadedCheerio(this) - .find(".epl-date") - .text() - .trim(); - - const chapterUrl = loadedCheerio(this).find("a").attr("href"); - - if (!chapterUrl) return; - - chapter.push({ - name: chapterName, - releaseTime: releaseDate, - url: chapterUrl, - }); - }); - - novel.chapters = chapter.reverse(); - - return novel; - } - async parseChapter(chapterUrl: string): Promise { - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(chapterUrl, { headers }); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - let chapterText = loadedCheerio(".epcontent").html() || ""; - - return chapterText; - } - - async searchNovels( - searchTerm: string, - pageNo?: number | undefined - ): Promise { - const url = `${this.site}?s=${searchTerm}`; - const headers = new Headers(); - if (this.cookieString) { - headers.append("cookie", this.cookieString); - } - const result = await fetchApi(url, { headers }); - const body = await result.text(); - - const loadedCheerio = parseHTML(body); - - const novels: Plugin.NovelItem[] = []; - - loadedCheerio("article.bs").each(function () { - const novelName = loadedCheerio(this).find(".ntitle").text().trim(); - const novelCover = loadedCheerio(this).find("img").attr("src"); - const novelUrl = loadedCheerio(this).find("a").attr("href"); - if (!novelUrl) return; - - novels.push({ - name: novelName, - url: novelUrl, - cover: novelCover, - }); - }); - - return novels; - } - async fetchImage(url: string): Promise { - return await fetchFile(url); - } - - filters = { - order: { - value: "popular", - label: "Önerilen", - options: [ - { label: "Varsayılan", value: "" }, - - { label: "A-Z", value: "title" }, - - { label: "Z-A", value: "titlereverse" }, - - { label: "Son Yüklemeler", value: "update" }, - - { label: "Son Eklenenler", value: "latest" }, - - { label: "Bestimiz", value: "popular" }, - ], - type: FilterTypes.Picker, - }, - status: { - label: "Statü", - value: "", - options: [ - { label: "Tümü", value: "" }, - - { label: "Ongoing", value: "ongoing" }, - - { label: "Hiatus", value: "hiatus" }, - - { label: "Completed", value: "completed" }, - ], - type: FilterTypes.Picker, - }, - type: { - value: [], - label: "Tür", - options: [ - { label: "Çeviri Novel", value: "ceviri-novel" }, - - { label: "Liz-Chan", value: "liz-chan" }, - - { label: "Manhwa", value: "manhwa" }, - - { label: "Orijinal Novel", value: "orijinal-novel" }, - - { label: "Web Novel", value: "web-novel" }, - ], - type: FilterTypes.CheckboxGroup, - }, - genres: { - value: [], - label: "Kategori", - options: [ - { label: "+18", value: "18" }, - - { label: "Action", value: "action" }, - - { label: "Adult", value: "adult" }, - - { label: "Aksiyon", value: "aksiyon" }, - - { label: "BL", value: "bl" }, - - { label: "Comedy", value: "comedy" }, - - { label: "Doğaüstü", value: "dogaustu" }, - - { label: "Dram", value: "dram" }, - - { label: "Drama", value: "drama" }, - - { label: "Ecchi", value: "ecchi" }, - - { label: "Fantastik", value: "fantastik" }, - - { label: "Fantasy", value: "fantasy" }, - - { label: "Gizem", value: "gizem" }, - - { label: "Harem", value: "harem" }, - - { label: "Historical", value: "historical" }, - - { label: "Josei", value: "josei" }, - - { label: "Macera", value: "macera" }, - - { label: "Manhwa", value: "manhwa" }, - - { label: "Martial Arts", value: "martial-arts" }, - - { label: "Mature", value: "mature" }, - - { label: "Novel", value: "novel" }, - - { label: "Okul", value: "okul" }, - - { label: "Psikolojik", value: "psikolojik" }, - - { label: "Psychological", value: "psychological" }, - - { label: "Reverse Harem", value: "reverse-harem" }, - - { label: "Romance", value: "romance" }, - - { label: "Romantik", value: "romantik" }, - - { label: "Shoujo", value: "shoujo" }, - - { label: "Slice Of Life", value: "slice-of-life" }, - - { label: "Smut", value: "smut" }, - - { label: "Supernatural", value: "supernatural" }, - - { label: "Tarihi", value: "tarihi" }, - - { label: "Tragedy", value: "tragedy" }, - - { label: "Yaoi", value: "yaoi" }, - ], - type: FilterTypes.CheckboxGroup, - }, - } satisfies Filters; -} - -export default new LSHNovel(); +import { load as parseHTML } from "cheerio"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { Plugin } from "@typings/plugin"; + +class LSHNovel implements Plugin.PluginBase { + id = "lshnovel"; + name = "Liebe Schnee Hiver Novel"; + icon = "multisrc/wpmangastream/icons/lshnovel.png"; + site = "https://lshnovel.com/"; + version = "1.0.0"; + userAgent = ""; + async popularNovels( + pageNo: number, + { filters }: Plugin.PopularNovelsOptions + ): Promise { + let link = `${this.site}series/?page=${pageNo}`; + + if (filters.genres.value.length) { + link += filters.genres.value.map((i) => `&genre[]=${i}`).join(""); + } + + if (filters.type.value.length) + link += filters.type.value.map((i) => `&lang[]=${i}`).join(""); + + link += "&status=" + filters.status; + + link += "&order=" + filters.order; + + const headers = new Headers(); + const body = await fetchApi(link, { headers }).then((result) => + result.text() + ); + + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + loadedCheerio("article.bs").each(function () { + const novelName = loadedCheerio(this).find(".ntitle").text().trim(); + let image = loadedCheerio(this).find("img"); + const novelCover = image.attr("data-src") || image.attr("src"); + + const novelUrl = loadedCheerio(this).find("a").attr("href"); + + if (!novelUrl) return; + + const novel = { + name: novelName, + cover: novelCover, + url: novelUrl, + }; + + novels.push(novel); + }); + + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const url = novelUrl; + const headers = new Headers(); + const result = await fetchApi(url, { headers }); + const body = await result.text(); + + let loadedCheerio = parseHTML(body); + + const novel: Plugin.SourceNovel = { + url, + chapters: [], + }; + + novel.name = loadedCheerio(".entry-title").text(); + + novel.cover = + loadedCheerio("img.wp-post-image").attr("data-src") || + loadedCheerio("img.wp-post-image").attr("src"); + + loadedCheerio("div.spe > span").each(function () { + const detailName = loadedCheerio(this).find("b").text().trim(); + const detail = loadedCheerio(this) + .find("b") + .remove() + .end() + .text() + .trim(); + + switch (detailName) { + case "Yazar:": + novel.author = detail; + break; + case "Seviye:": + novel.status = detail; + break; + } + }); + + novel.genres = loadedCheerio(".genxed") + .text() + .trim() + .replace(/\s/g, ","); + + loadedCheerio('div[itemprop="description"] h3,p.a,strong').remove(); + novel.summary = loadedCheerio('div[itemprop="description"]') + .find("br") + .replaceWith("\n") + .end() + .text(); + + let chapter: Plugin.ChapterItem[] = []; + + loadedCheerio(".eplister") + .find("li") + .each(function () { + const chapterName = + loadedCheerio(this).find(".epl-num").text() + + " - " + + loadedCheerio(this).find(".epl-title").text(); + + const releaseDate = loadedCheerio(this) + .find(".epl-date") + .text() + .trim(); + + const chapterUrl = loadedCheerio(this).find("a").attr("href"); + + if (!chapterUrl) return; + + chapter.push({ + name: chapterName, + releaseTime: releaseDate, + url: chapterUrl, + }); + }); + + novel.chapters = chapter.reverse(); + + return novel; + } + async parseChapter(chapterUrl: string): Promise { + const headers = new Headers(); + const result = await fetchApi(chapterUrl, { headers }); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + let chapterText = loadedCheerio(".epcontent").html() || ""; + + return chapterText; + } + + async searchNovels( + searchTerm: string, + pageNo?: number | undefined + ): Promise { + const url = `${this.site}?s=${searchTerm}`; + const headers = new Headers(); + const result = await fetchApi(url, { headers }); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + loadedCheerio("article.bs").each(function () { + const novelName = loadedCheerio(this).find(".ntitle").text().trim(); + const novelCover = loadedCheerio(this).find("img").attr("src"); + const novelUrl = loadedCheerio(this).find("a").attr("href"); + if (!novelUrl) return; + + novels.push({ + name: novelName, + url: novelUrl, + cover: novelCover, + }); + }); + + return novels; + } + async fetchImage(url: string): Promise { + return await fetchFile(url); + } + + filters = { + order: { + value: "popular", + label: "Önerilen", + options: [ + { label: "Varsayılan", value: "" }, + + { label: "A-Z", value: "title" }, + + { label: "Z-A", value: "titlereverse" }, + + { label: "Son Yüklemeler", value: "update" }, + + { label: "Son Eklenenler", value: "latest" }, + + { label: "Bestimiz", value: "popular" }, + ], + type: FilterTypes.Picker, + }, + status: { + label: "Statü", + value: "", + options: [ + { label: "Tümü", value: "" }, + + { label: "Ongoing", value: "ongoing" }, + + { label: "Hiatus", value: "hiatus" }, + + { label: "Completed", value: "completed" }, + ], + type: FilterTypes.Picker, + }, + type: { + value: [], + label: "Tür", + options: [ + { label: "Çeviri Novel", value: "ceviri-novel" }, + + { label: "Liz-Chan", value: "liz-chan" }, + + { label: "Manhwa", value: "manhwa" }, + + { label: "Orijinal Novel", value: "orijinal-novel" }, + + { label: "Web Novel", value: "web-novel" }, + ], + type: FilterTypes.CheckboxGroup, + }, + genres: { + value: [], + label: "Kategori", + options: [ + { label: "+18", value: "18" }, + + { label: "Action", value: "action" }, + + { label: "Adult", value: "adult" }, + + { label: "Aksiyon", value: "aksiyon" }, + + { label: "BL", value: "bl" }, + + { label: "Comedy", value: "comedy" }, + + { label: "Doğaüstü", value: "dogaustu" }, + + { label: "Dram", value: "dram" }, + + { label: "Drama", value: "drama" }, + + { label: "Ecchi", value: "ecchi" }, + + { label: "Fantastik", value: "fantastik" }, + + { label: "Fantasy", value: "fantasy" }, + + { label: "Gizem", value: "gizem" }, + + { label: "Harem", value: "harem" }, + + { label: "Historical", value: "historical" }, + + { label: "Josei", value: "josei" }, + + { label: "Macera", value: "macera" }, + + { label: "Manhwa", value: "manhwa" }, + + { label: "Martial Arts", value: "martial-arts" }, + + { label: "Mature", value: "mature" }, + + { label: "Novel", value: "novel" }, + + { label: "Okul", value: "okul" }, + + { label: "Psikolojik", value: "psikolojik" }, + + { label: "Psychological", value: "psychological" }, + + { label: "Reverse Harem", value: "reverse-harem" }, + + { label: "Romance", value: "romance" }, + + { label: "Romantik", value: "romantik" }, + + { label: "Shoujo", value: "shoujo" }, + + { label: "Slice Of Life", value: "slice-of-life" }, + + { label: "Smut", value: "smut" }, + + { label: "Supernatural", value: "supernatural" }, + + { label: "Tarihi", value: "tarihi" }, + + { label: "Tragedy", value: "tragedy" }, + + { label: "Yaoi", value: "yaoi" }, + ], + type: FilterTypes.CheckboxGroup, + }, + } satisfies Filters; +} + +export default new LSHNovel(); diff --git a/plugins/ukraine/smakolykytl.ts b/plugins/ukraine/smakolykytl.ts index e096934..de05261 100644 --- a/plugins/ukraine/smakolykytl.ts +++ b/plugins/ukraine/smakolykytl.ts @@ -1,256 +1,255 @@ -import { Plugin } from "@typings/plugin"; -import { fetchApi, fetchFile } from "@libs/fetch"; -import { NovelStatus } from "@libs/novelStatus"; -import dayjs from "dayjs"; - -class Smakolykytl implements Plugin.PluginBase { - id = "smakolykytl"; - name = "Смаколики"; - site = "https://smakolykytl.site/"; - version = "1.0.0"; - icon = "src/ua/smakolykytl/icon.png"; - userAgent = ""; - cookieString = ""; - - async popularNovels( - pageNo: number, - { showLatestNovels, filters }: Plugin.PopularNovelsOptions, - ): Promise { - const url = showLatestNovels - ? "https://api.smakolykytl.site/api/user/updates" - : "https://api.smakolykytl.site/api/user/projects"; - - const result = await fetchApi(url); - const json = (await result.json()) as response; - - const novels: Plugin.NovelItem[] = []; - (json?.projects || json?.updates)?.forEach((novel) => - novels.push({ - name: novel.title, - cover: novel.image.url, - url: this.site + "titles/" + novel.id, - }), - ); - - return novels; - } - - async parseNovelAndChapters(novelUrl: string): Promise { - const id = novelUrl.split("/").pop(); - const result = await fetchApi( - "https://api.smakolykytl.site/api/user/projects/" + id, - ); - const book = (await result.json()) as response; - - const novel: Plugin.SourceNovel = { - url: novelUrl, - name: book?.project?.title, - cover: book?.project?.image?.url, - summary: book?.project?.description, - author: book?.project?.author, - status: book?.project?.status_translate.includes("Триває") - ? NovelStatus.Ongoing - : NovelStatus.Completed, - }; - const tags = [book?.project?.genres, book?.project?.tags] - .flat() - .map((tags) => tags?.title) - .filter((tags) => tags); - - if (tags.length) { - novel.genres = tags.join(", "); - } - - const chapters: Plugin.ChapterItem[] = []; - const res = await fetchApi( - "https://api.smakolykytl.site/api/user/projects/" + id + "/books", - ); - const data = (await res.json()) as response; - data?.books?.forEach( - (volume) => - volume?.chapters?.map((chapter) => - chapters.push({ - name: volume.title + " " + chapter.title, - releaseTime: dayjs(chapter.modifiedAt).format("LLL"), - url: this.site + "read/" + chapter.id, - }), - ), - ); - - novel.chapters = chapters; - return novel; - } - - async parseChapter(chapterUrl: string): Promise { - const id = chapterUrl.split("/").pop(); - const result = await fetchApi( - "https://api.smakolykytl.site/api/user/chapters/" + id, - ); - const json = (await result.json()) as response; - const chapterRaw: HTML[] = JSON.parse(json?.chapter?.content || "[]"); - - const chapterText = jsonToHtml(chapterRaw); - return chapterText; - } - - async searchNovels( - searchTerm: string, - //pageNo: number | undefined = 1, - ): Promise { - const result = await fetchApi( - "https://api.smakolykytl.site/api/user/projects", - ); - const json = (await result.json()) as response; - const novels: Plugin.NovelItem[] = []; - - json?.projects - ?.filter( - (novel) => - novel.title.includes(searchTerm) || String(novel.id) === searchTerm, - ) - ?.forEach((novel) => - novels.push({ - name: novel.title, - cover: novel.image.url, - url: this.site + "titles/" + novel.id, - }), - ); - return novels; - } - - fetchImage = fetchFile; -} - -export default new Smakolykytl(); - -function jsonToHtml(json: HTML[], html: string = "") { - json.forEach((element) => { - switch (element.type) { - case "hardBreak": - html += "
"; - break; - case "horizontalRule": - html += "
"; - break; - case "image": - if (element.attrs) { - const attrs = Object.entries(element.attrs) - .filter((attr) => attr?.[1]) - .map((attr) => `${attr[0]}="${attr[1]}"`); - html += ""; - } - break; - case "paragraph": - html += - "

" + - (element.content ? jsonToHtml(element.content) : "
") + - "

"; - break; - case "text": - html += element.text; - break; - default: - html += JSON.stringify(element, null, "\t"); //maybe I missed something. - break; - } - }); - return html; -} - -interface response { - projects?: TopLevelProject[]; - updates?: Update[]; - project?: TopLevelProject; - books?: BookElement[]; - chapter?: TopLevelChapter; -} - -interface BookElement { - id: number; - rank: number; - title: string; - chapters: PurpleChapter[]; -} - -interface PurpleChapter { - id: number; - title: string; - rank: string; - modifiedAt: Date; -} - -interface TopLevelChapter { - id: number; - title: string; - rank: string; - content: string; - modifiedAt: Date; - book: ChapterBook; -} - -interface ChapterBook { - id: number; - rank: number; - title: string; - chapters: FluffyChapter[]; - project: BookProject; -} - -interface FluffyChapter { - id: number; - rank: string; -} - -interface BookProject { - id: number; -} - -interface TopLevelProject { - id: number; - title: string; - description: string; - author: string; - translator: string; - modifiedAt: Date; - alternatives: string; - release: string; - nation: string; - status: string; - status_translate: string; - image: Image; - tags?: Genre[]; - genres?: Genre[]; -} - -interface Genre { - id: number; - title: string; -} - -interface Image { - id: number; - url: string; - name: string; -} - -interface Update { - id: number; - title: string; - author: string; - translator: string; - modifiedAt: Date; - image: Image; -} - -interface HTML { - type: string; - content?: HTML[]; - attrs?: Attrs; - text?: string; -} - -interface Attrs { - src: string; - alt: string | null; - title: string | null; -} +import { Plugin } from "@typings/plugin"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { NovelStatus } from "@libs/novelStatus"; +import dayjs from "dayjs"; + +class Smakolykytl implements Plugin.PluginBase { + id = "smakolykytl"; + name = "Смаколики"; + site = "https://smakolykytl.site/"; + version = "1.0.0"; + icon = "src/ua/smakolykytl/icon.png"; + userAgent = ""; + + async popularNovels( + pageNo: number, + { showLatestNovels, filters }: Plugin.PopularNovelsOptions, + ): Promise { + const url = showLatestNovels + ? "https://api.smakolykytl.site/api/user/updates" + : "https://api.smakolykytl.site/api/user/projects"; + + const result = await fetchApi(url); + const json = (await result.json()) as response; + + const novels: Plugin.NovelItem[] = []; + (json?.projects || json?.updates)?.forEach((novel) => + novels.push({ + name: novel.title, + cover: novel.image.url, + url: this.site + "titles/" + novel.id, + }), + ); + + return novels; + } + + async parseNovelAndChapters(novelUrl: string): Promise { + const id = novelUrl.split("/").pop(); + const result = await fetchApi( + "https://api.smakolykytl.site/api/user/projects/" + id, + ); + const book = (await result.json()) as response; + + const novel: Plugin.SourceNovel = { + url: novelUrl, + name: book?.project?.title, + cover: book?.project?.image?.url, + summary: book?.project?.description, + author: book?.project?.author, + status: book?.project?.status_translate.includes("Триває") + ? NovelStatus.Ongoing + : NovelStatus.Completed, + }; + const tags = [book?.project?.genres, book?.project?.tags] + .flat() + .map((tags) => tags?.title) + .filter((tags) => tags); + + if (tags.length) { + novel.genres = tags.join(", "); + } + + const chapters: Plugin.ChapterItem[] = []; + const res = await fetchApi( + "https://api.smakolykytl.site/api/user/projects/" + id + "/books", + ); + const data = (await res.json()) as response; + data?.books?.forEach( + (volume) => + volume?.chapters?.map((chapter) => + chapters.push({ + name: volume.title + " " + chapter.title, + releaseTime: dayjs(chapter.modifiedAt).format("LLL"), + url: this.site + "read/" + chapter.id, + }), + ), + ); + + novel.chapters = chapters; + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const id = chapterUrl.split("/").pop(); + const result = await fetchApi( + "https://api.smakolykytl.site/api/user/chapters/" + id, + ); + const json = (await result.json()) as response; + const chapterRaw: HTML[] = JSON.parse(json?.chapter?.content || "[]"); + + const chapterText = jsonToHtml(chapterRaw); + return chapterText; + } + + async searchNovels( + searchTerm: string, + //pageNo: number | undefined = 1, + ): Promise { + const result = await fetchApi( + "https://api.smakolykytl.site/api/user/projects", + ); + const json = (await result.json()) as response; + const novels: Plugin.NovelItem[] = []; + + json?.projects + ?.filter( + (novel) => + novel.title.includes(searchTerm) || String(novel.id) === searchTerm, + ) + ?.forEach((novel) => + novels.push({ + name: novel.title, + cover: novel.image.url, + url: this.site + "titles/" + novel.id, + }), + ); + return novels; + } + + fetchImage = fetchFile; +} + +export default new Smakolykytl(); + +function jsonToHtml(json: HTML[], html: string = "") { + json.forEach((element) => { + switch (element.type) { + case "hardBreak": + html += "
"; + break; + case "horizontalRule": + html += "
"; + break; + case "image": + if (element.attrs) { + const attrs = Object.entries(element.attrs) + .filter((attr) => attr?.[1]) + .map((attr) => `${attr[0]}="${attr[1]}"`); + html += ""; + } + break; + case "paragraph": + html += + "

" + + (element.content ? jsonToHtml(element.content) : "
") + + "

"; + break; + case "text": + html += element.text; + break; + default: + html += JSON.stringify(element, null, "\t"); //maybe I missed something. + break; + } + }); + return html; +} + +interface response { + projects?: TopLevelProject[]; + updates?: Update[]; + project?: TopLevelProject; + books?: BookElement[]; + chapter?: TopLevelChapter; +} + +interface BookElement { + id: number; + rank: number; + title: string; + chapters: PurpleChapter[]; +} + +interface PurpleChapter { + id: number; + title: string; + rank: string; + modifiedAt: Date; +} + +interface TopLevelChapter { + id: number; + title: string; + rank: string; + content: string; + modifiedAt: Date; + book: ChapterBook; +} + +interface ChapterBook { + id: number; + rank: number; + title: string; + chapters: FluffyChapter[]; + project: BookProject; +} + +interface FluffyChapter { + id: number; + rank: string; +} + +interface BookProject { + id: number; +} + +interface TopLevelProject { + id: number; + title: string; + description: string; + author: string; + translator: string; + modifiedAt: Date; + alternatives: string; + release: string; + nation: string; + status: string; + status_translate: string; + image: Image; + tags?: Genre[]; + genres?: Genre[]; +} + +interface Genre { + id: number; + title: string; +} + +interface Image { + id: number; + url: string; + name: string; +} + +interface Update { + id: number; + title: string; + author: string; + translator: string; + modifiedAt: Date; + image: Image; +} + +interface HTML { + type: string; + content?: HTML[]; + attrs?: Attrs; + text?: string; +} + +interface Attrs { + src: string; + alt: string | null; + title: string | null; +} diff --git a/plugins/vietnamese/LNHako.ts b/plugins/vietnamese/LNHako.ts index 9cb5697..f2f96eb 100644 --- a/plugins/vietnamese/LNHako.ts +++ b/plugins/vietnamese/LNHako.ts @@ -9,7 +9,7 @@ class HakoPlugin implements Plugin.PluginBase { site: string; version: string; userAgent: string; - cookieString: string; + constructor() { this.id = "ln.hako"; this.name = "Hako"; @@ -17,7 +17,6 @@ class HakoPlugin implements Plugin.PluginBase { this.site = "https://ln.hako.vn"; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; } parseNovels(loadedCheerio: CheerioAPI) { const novels: Plugin.NovelItem[] = []; diff --git a/scripts/multisrc/ifreedom/template.ts b/scripts/multisrc/ifreedom/template.ts index bf58549..2adf137 100644 --- a/scripts/multisrc/ifreedom/template.ts +++ b/scripts/multisrc/ifreedom/template.ts @@ -19,7 +19,6 @@ class IfreedomPlugin implements Plugin.PluginBase { site: string; version: string; userAgent: string; - cookieString: string; filters?: Filters; constructor(metadata: IfreedomMetadata) { @@ -29,7 +28,6 @@ class IfreedomPlugin implements Plugin.PluginBase { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = metadata.filters; } diff --git a/scripts/multisrc/madara/template.ts b/scripts/multisrc/madara/template.ts index b72a315..8d40961 100644 --- a/scripts/multisrc/madara/template.ts +++ b/scripts/multisrc/madara/template.ts @@ -42,7 +42,6 @@ class MadaraPlugin implements Plugin.PluginBase { site: string; version: string; userAgent: string; - cookieString: string; options?: MadaraOptions; filters?: Filters | undefined; @@ -56,7 +55,6 @@ class MadaraPlugin implements Plugin.PluginBase { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; this.filters = metadata.filters; } diff --git a/scripts/multisrc/readwn/template.ts b/scripts/multisrc/readwn/template.ts index 8d6e411..18a84cd 100644 --- a/scripts/multisrc/readwn/template.ts +++ b/scripts/multisrc/readwn/template.ts @@ -20,7 +20,6 @@ class ReadwnPlugin implements Plugin.PluginBase { site: string; version: string; userAgent: string; - cookieString: string; filters?: Filters; constructor(metadata: ReadwnMetadata) { @@ -31,7 +30,6 @@ class ReadwnPlugin implements Plugin.PluginBase { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = metadata.filters; } diff --git a/scripts/multisrc/rulate/template.ts b/scripts/multisrc/rulate/template.ts index a474d1f..49b89bb 100644 --- a/scripts/multisrc/rulate/template.ts +++ b/scripts/multisrc/rulate/template.ts @@ -19,7 +19,6 @@ class RulatePlugin implements Plugin.PluginBase { site: string; version: string; userAgent: string; - cookieString: string; filters?: Filters | undefined; constructor(metadata: RulateMetadata) { @@ -30,7 +29,6 @@ class RulatePlugin implements Plugin.PluginBase { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.filters = metadata.filters; } diff --git a/scripts/multisrc/wpmangastream/template.ts b/scripts/multisrc/wpmangastream/template.ts index abd9330..1caecf4 100644 --- a/scripts/multisrc/wpmangastream/template.ts +++ b/scripts/multisrc/wpmangastream/template.ts @@ -22,7 +22,6 @@ class WPmangaStreamPlugin implements Plugin.PluginBase { site: string; version: string; userAgent: string; - cookieString: string; options?: WPmangaStreamOptions; constructor(metadata: WPmangaStreamMetadata) { @@ -32,7 +31,6 @@ class WPmangaStreamPlugin implements Plugin.PluginBase { this.site = metadata.sourceSite; this.version = "1.0.0"; this.userAgent = ""; - this.cookieString = ""; this.options = metadata.options; } diff --git a/test_web/index.html b/test_web/index.html index 8784008..509f552 100644 --- a/test_web/index.html +++ b/test_web/index.html @@ -50,6 +50,14 @@
+