[RU]Newbie search fix (#11942)

* begin fix search

* filters

* filters+

* require_chapters

* search as match

* 18+

* structure Dto
This commit is contained in:
Ejan 2022-05-24 05:10:14 +05:00 committed by GitHub
parent 2236605211
commit b227fd2e39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 328 additions and 118 deletions

View File

@ -6,7 +6,7 @@ ext {
extName = 'NewManga(Newbie)'
pkgNameSuffix = 'ru.newbie'
extClass = '.Newbie'
extVersionCode = 8
extVersionCode = 9
}
dependencies {

View File

@ -6,11 +6,15 @@ import LibraryDto
import MangaDetDto
import PageDto
import PageWrapperDto
import SearchLibraryDto
import SearchWrapperDto
import SeriesWrapperDto
import SubSearchDto
import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.os.Build
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
@ -22,11 +26,12 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import org.jsoup.Jsoup
@ -73,13 +78,7 @@ class Newbie : HttpSource() {
override fun popularMangaRequest(page: Int) = GET("$API_URL/projects/popular?scale=month&size=$count&page=$page", headers)
override fun popularMangaParse(response: Response): MangasPage = searchMangaParse(response)
override fun latestUpdatesRequest(page: Int): Request = GET("$API_URL/projects/updates?only_bookmarks=false&size=$count&page=$page", headers)
override fun latestUpdatesParse(response: Response): MangasPage = searchMangaParse(response)
override fun searchMangaParse(response: Response): MangasPage {
override fun popularMangaParse(response: Response): MangasPage {
val page = json.decodeFromString<PageWrapperDto<LibraryDto>>(response.body!!.string())
val mangas = page.items.map {
it.toSManga()
@ -95,8 +94,30 @@ class Newbie : HttpSource() {
url = "$id"
thumbnail_url = if (image.srcset.large.isNotEmpty()) {
"$IMAGE_URL/${image.srcset.large}"
} else "" +
"$IMAGE_URL/${image.srcset.small}"
} else "$IMAGE_URL/${image.srcset.small}"
}
}
override fun latestUpdatesRequest(page: Int): Request = GET("$API_URL/projects/updates?only_bookmarks=false&size=$count&page=$page", headers)
override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response)
override fun searchMangaParse(response: Response): MangasPage {
val page = json.decodeFromString<SearchWrapperDto<SubSearchDto<SearchLibraryDto>>>(response.body!!.string())
val mangas = page.result.hits.map {
it.toSearchManga()
}
return MangasPage(mangas, mangas.isNotEmpty())
}
private fun SearchLibraryDto.toSearchManga(): SManga {
return SManga.create().apply {
// Do not change the title name to ensure work with a multilingual catalog!
title = document.title_en
url = document.id
thumbnail_url = if (document.image_large.isNotEmpty()) {
"$IMAGE_URL/${document.image_large}"
} else "$IMAGE_URL/${document.image_small}"
}
}
@ -112,35 +133,62 @@ class Newbie : HttpSource() {
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
var url = "$API_URL/projects/catalog?size=$count&page=$page".toHttpUrlOrNull()!!.newBuilder()
if (query.isNotEmpty()) {
url = "$API_URL/projects/search?size=$count&page=$page".toHttpUrlOrNull()!!.newBuilder()
url.addQueryParameter("query", query)
}
val mutableGenre = mutableListOf<String>()
val mutableExGenre = mutableListOf<String>()
val mutableTag = mutableListOf<String>()
val mutableExTag = mutableListOf<String>()
val mutableType = mutableListOf<String>()
val mutableStatus = mutableListOf<String>()
val mutableAge = mutableListOf<String>()
var orderBy = "MATCH"
var ascEnd = "DESC"
var requireChapters = true
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) {
is OrderBy -> {
val ord = arrayOf("rating", "fresh")[filter.state!!.index]
url.addQueryParameter("sorting", ord)
if (query.isEmpty()) {
orderBy = arrayOf("RATING", "VIEWS", "HEARTS", "COUNT_CHAPTERS", "CREATED_AT", "UPDATED_AT")[filter.state!!.index]
ascEnd = if (filter.state!!.ascending) "ASC" else "DESC"
}
}
is GenreList -> filter.state.forEach { genre ->
if (genre.state != Filter.TriState.STATE_IGNORE) {
if (genre.isIncluded()) mutableGenre += '"' + genre.name + '"' else mutableExGenre += '"' + genre.name + '"'
}
}
is TagsList -> filter.state.forEach { tag ->
if (tag.state != Filter.TriState.STATE_IGNORE) {
if (tag.isIncluded()) mutableTag += '"' + tag.name + '"' else mutableExTag += '"' + tag.name + '"'
}
}
is TypeList -> filter.state.forEach { type ->
if (type.state) {
url.addQueryParameter("types", type.id)
mutableType += '"' + type.id + '"'
}
}
is StatusList -> filter.state.forEach { status ->
if (status.state) {
url.addQueryParameter("statuses", status.id)
mutableStatus += '"' + status.id + '"'
}
}
is GenreList -> filter.state.forEach { genre ->
if (genre.state) {
url.addQueryParameter("genres", genre.id)
is AgeList -> filter.state.forEach { age ->
if (age.state) {
mutableAge += '"' + age.id + '"'
}
}
is RequireChapters -> {
if (filter.state == 1) {
requireChapters = false
}
}
}
}
return GET(url.toString(), headers)
return POST(
"https://neo.newmanga.org/catalogue",
body = """{"query":"$query","sort":{"kind":"$orderBy","dir":"$ascEnd"},"filter":{"hidden_projects":[],"genres":{"excluded":$mutableExGenre,"included":$mutableGenre},"tags":{"excluded":$mutableExTag,"included":$mutableTag},"type":{"allowed":$mutableType},"translation_status":{"allowed":[]},"released_year":{"min":null,"max":null},"require_chapters":$requireChapters,"original_status":{"allowed":$mutableStatus},"adult":{"allowed":$mutableAge}},"pagination":{"page":$page,"size":$count}}""".toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()),
headers = headers
)
}
private fun parseStatus(status: String): Int {
@ -324,90 +372,225 @@ class Newbie : HttpSource() {
}
private class CheckFilter(name: String, val id: String) : Filter.CheckBox(name)
private class SearchFilter(name: String) : Filter.TriState(name)
private class TypeList(types: List<CheckFilter>) : Filter.Group<CheckFilter>("Типы", types)
private class StatusList(statuses: List<CheckFilter>) : Filter.Group<CheckFilter>("Статус", statuses)
private class GenreList(genres: List<CheckFilter>) : Filter.Group<CheckFilter>("Жанры", genres)
private class GenreList(genres: List<SearchFilter>) : Filter.Group<SearchFilter>("Жанры", genres)
private class TagsList(tags: List<SearchFilter>) : Filter.Group<SearchFilter>("Теги", tags)
private class AgeList(ages: List<CheckFilter>) : Filter.Group<CheckFilter>("Возрастное ограничение", ages)
override fun getFilterList() = FilterList(
OrderBy(),
GenreList(getGenreList()),
TagsList(getTagsList()),
TypeList(getTypeList()),
StatusList(getStatusList())
StatusList(getStatusList()),
AgeList(getAgeList()),
RequireChapters()
)
private class OrderBy : Filter.Sort(
"Сортировка",
arrayOf("По рейтенгу", "По новизне"),
arrayOf("По рейтингу", "По просмотрам", "По лайкам", "По кол-ву глав", "По дате создания", "По дате обновления"),
Selection(0, false)
)
private class RequireChapters : Filter.Select<String>(
"Только проекты с главами",
arrayOf("Да", "Все")
)
private fun getTypeList() = listOf(
CheckFilter("Манга", "manga"),
CheckFilter("Манхва", "manhwa"),
CheckFilter("Маньхуа", "manhya"),
CheckFilter("Сингл", "single"),
CheckFilter("OEL-манга", "oel"),
CheckFilter("Комикс", "comics"),
CheckFilter("Руманга", "russian")
CheckFilter("Манга", "MANGA"),
CheckFilter("Манхва", "MANHWA"),
CheckFilter("Маньхуа", "MANHYA"),
CheckFilter("Сингл", "SINGLE"),
CheckFilter("OEL-манга", "OEL"),
CheckFilter("Комикс", "COMICS"),
CheckFilter("Руманга", "RUSSIAN")
)
private fun getStatusList() = listOf(
CheckFilter("Выпускается", "on_going"),
CheckFilter("Заброшен", "abandoned"),
CheckFilter("Завершён", "completed"),
CheckFilter("Приостановлен", "suspended")
CheckFilter("Выпускается", "ON_GOING"),
CheckFilter("Приостановлен", "SUSPENDED"),
CheckFilter("Завершён", "COMPLETED"),
CheckFilter("Анонс", "ANNOUNCEMENT"),
)
private fun getGenreList() = listOf(
CheckFilter("cёнэн-ай", "28"),
CheckFilter("боевик", "17"),
CheckFilter("боевые искусства", "33"),
CheckFilter("гарем", "34"),
CheckFilter("гендерная интрига", "3"),
CheckFilter("героическое фэнтези", "19"),
CheckFilter("детектив", "35"),
CheckFilter("дзёсэй", "4"),
CheckFilter("додзинси", "20"),
CheckFilter("драма", "36"),
CheckFilter("ёнкома", "5"),
CheckFilter("игра", "21"),
CheckFilter("драма", "36"),
CheckFilter("ёнкома", "5"),
CheckFilter("игра", "21"),
CheckFilter("исекай", "37"),
CheckFilter("история", "6"),
CheckFilter("киберпанк", "22"),
CheckFilter("кодомо", "38"),
CheckFilter("комедия", "7"),
CheckFilter("махо-сёдзё", "23"),
CheckFilter("меха", "39"),
CheckFilter("мистика", "8"),
CheckFilter("научная фантастика", "24"),
CheckFilter("омегаверс", "40"),
CheckFilter("повседневность", "9"),
CheckFilter("постапокалиптика", "25"),
CheckFilter("приключения", "41"),
CheckFilter("психология", "10"),
CheckFilter("романтика", "26"),
CheckFilter("самурайский боевик", "42"),
CheckFilter("сверхъестественное", "11"),
CheckFilter("сёдзё", "27"),
CheckFilter("сёдзё-ай", "43"),
CheckFilter("сёнэн", "13"),
CheckFilter("спорт", "44"),
CheckFilter("сэйнэн", "12"),
CheckFilter("трагедия", "29"),
CheckFilter("триллер", "45"),
CheckFilter("ужасы", "14"),
CheckFilter("фантастика", "30"),
CheckFilter("фэнтези", "46"),
CheckFilter("школа", "15"),
CheckFilter("элементы юмора", "1"),
CheckFilter("эротика", "31"),
CheckFilter("этти", "47"),
CheckFilter("юри", "16"),
CheckFilter("яой", "32"),
SearchFilter("cёнэн-ай"),
SearchFilter("боевик"),
SearchFilter("боевые искусства"),
SearchFilter("гарем"),
SearchFilter("гендерная интрига"),
SearchFilter("героическое фэнтези"),
SearchFilter("детектив"),
SearchFilter("дзёсэй"),
SearchFilter("додзинси"),
SearchFilter("драма"),
SearchFilter("ёнкома"),
SearchFilter("игра"),
SearchFilter("драма"),
SearchFilter("ёнкома"),
SearchFilter("игра"),
SearchFilter("исекай"),
SearchFilter("история"),
SearchFilter("киберпанк"),
SearchFilter("кодомо"),
SearchFilter("комедия"),
SearchFilter("махо-сёдзё"),
SearchFilter("меха"),
SearchFilter("мистика"),
SearchFilter("научная фантастика"),
SearchFilter("омегаверс"),
SearchFilter("повседневность"),
SearchFilter("постапокалиптика"),
SearchFilter("приключения"),
SearchFilter("психология"),
SearchFilter("романтика"),
SearchFilter("самурайский боевик"),
SearchFilter("сверхъестественное"),
SearchFilter("сёдзё"),
SearchFilter("сёдзё-ай"),
SearchFilter("сёнэн"),
SearchFilter("спорт"),
SearchFilter("сэйнэн"),
SearchFilter("трагедия"),
SearchFilter("триллер"),
SearchFilter("ужасы"),
SearchFilter("фантастика"),
SearchFilter("фэнтези"),
SearchFilter("школа"),
SearchFilter("элементы юмора"),
SearchFilter("эротика"),
SearchFilter("этти"),
SearchFilter("юри"),
SearchFilter("яой"),
)
private fun getTagsList() = listOf(
SearchFilter("веб"),
SearchFilter("в цвете"),
SearchFilter("сборник"),
SearchFilter("хентай"),
SearchFilter("азартные игры"),
SearchFilter("алхимия"),
SearchFilter("амнезия"),
SearchFilter("ангелы"),
SearchFilter("антигерой"),
SearchFilter("антиутопия"),
SearchFilter("апокалипсис"),
SearchFilter("аристократия"),
SearchFilter("армия"),
SearchFilter("артефакты"),
SearchFilter("боги"),
SearchFilter("бои на мечах"),
SearchFilter("борьба за власть"),
SearchFilter("брат и сестра"),
SearchFilter("будущее"),
SearchFilter("вампиры"),
SearchFilter("ведьма"),
SearchFilter("вестерн"),
SearchFilter("видеоигры"),
SearchFilter("виртуальная реальность"),
SearchFilter("военные"),
SearchFilter("война"),
SearchFilter("волшебники"),
SearchFilter("волшебные существа"),
SearchFilter("воспоминания из другого мира"),
SearchFilter("врачи / доктора"),
SearchFilter("выживание"),
SearchFilter("гг женщина"),
SearchFilter("гг имба"),
SearchFilter("гг мужчина"),
SearchFilter("гг не человек"),
SearchFilter("геймеры"),
SearchFilter("гильдии"),
SearchFilter("глупый гг"),
SearchFilter("гоблины"),
SearchFilter("горничные"),
SearchFilter("грузовик-сан"),
SearchFilter("гяру"),
SearchFilter("демоны"),
SearchFilter("драконы"),
SearchFilter("дружба"),
SearchFilter("ёнкома"),
SearchFilter("жестокий мир"),
SearchFilter("животные компаньоны"),
SearchFilter("завоевание мира"),
SearchFilter("зверолюди"),
SearchFilter("злые духи"),
SearchFilter("зомби"),
SearchFilter("игровые элементы"),
SearchFilter("империи"),
SearchFilter("исекай"),
SearchFilter("квесты"),
SearchFilter("космос"),
SearchFilter("кулинария"),
SearchFilter("культивация"),
SearchFilter("лгбт"),
SearchFilter("легендарное оружие"),
SearchFilter("лоли"),
SearchFilter("магическая академия"),
SearchFilter("магия"),
SearchFilter("мафия"),
SearchFilter("медицина"),
SearchFilter("месть"),
SearchFilter("монстродевушки"),
SearchFilter("монстры"),
SearchFilter("музыка"),
SearchFilter("навыки / способности"),
SearchFilter("наёмники"),
SearchFilter("насилие / жестокость"),
SearchFilter("нежить"),
SearchFilter("ниндзя"),
SearchFilter("обмен телами"),
SearchFilter("оборотни"),
SearchFilter("обратный гарем"),
SearchFilter("огнестрельное оружие"),
SearchFilter("офисные работники"),
SearchFilter("пародия"),
SearchFilter("пираты"),
SearchFilter("подземелье"),
SearchFilter("политика"),
SearchFilter("полиция"),
SearchFilter("преступники / криминал"),
SearchFilter("призраки / духи"),
SearchFilter("прокачка"),
SearchFilter("психодел"),
SearchFilter("путешествия во времени"),
SearchFilter("рабы"),
SearchFilter("разумные расы"),
SearchFilter("ранги силы"),
SearchFilter("реинкарнация"),
SearchFilter("роботы"),
SearchFilter("рыцари"),
SearchFilter("самураи"),
SearchFilter("система"),
SearchFilter("скрытие личности"),
SearchFilter("спасение мира"),
SearchFilter("спортивное тело"),
SearchFilter("средневековье"),
SearchFilter("стимпанк"),
SearchFilter("супергерои"),
SearchFilter("традиционные игры"),
SearchFilter("умный гг"),
SearchFilter("управление территорией"),
SearchFilter("учитель / ученик"),
SearchFilter("философия"),
SearchFilter("хикикомори"),
SearchFilter("холодное оружие"),
SearchFilter("шантаж"),
SearchFilter("эльфы"),
SearchFilter("якудза"),
SearchFilter("япония"),
)
private fun getAgeList() = listOf(
CheckFilter("13+", "ADULT_13"),
CheckFilter("16+", "ADULT_16"),
CheckFilter("18+", "ADULT_18")
)
companion object {

View File

@ -1,36 +1,9 @@
import kotlinx.serialization.Serializable
//Catalog API
@Serializable
data class TagsDto(
val title: TitleDto
)
@Serializable
data class BranchesDto(
val id: Long,
val is_default: Boolean
)
@Serializable
data class ImgsDto(
val large: String,
val small: String,
)
@Serializable
data class ImgDto(
val srcset: ImgsDto,
)
@Serializable
data class TitleDto(
val en: String,
val ru: String
)
@Serializable
data class AuthorDto(
val name: String?
data class PageWrapperDto<T>(
val items: List<T>,
)
@Serializable
@ -40,6 +13,7 @@ data class LibraryDto(
val image: ImgDto
)
//Manga Details
@Serializable
data class MangaDetDto(
val id: Long,
@ -57,10 +31,39 @@ data class MangaDetDto(
)
@Serializable
data class PageWrapperDto<T>(
val items: List<T>,
data class TitleDto(
val en: String,
val ru: String
)
@Serializable
data class AuthorDto(
val name: String?
)
@Serializable
data class ImgDto(
val srcset: ImgsDto,
)
@Serializable
data class ImgsDto(
val large: String,
val small: String,
)
@Serializable
data class TagsDto(
val title: TitleDto
)
@Serializable
data class BranchesDto(
val id: Long,
val is_default: Boolean
)
//Chapters
@Serializable
data class SeriesWrapperDto<T>(
val items: T
@ -82,3 +85,27 @@ data class PageDto(
val id: Int,
val slices: Int?
)
//Search NEO in POST Request
@Serializable
data class SearchWrapperDto<T>(
val result: T,
)
@Serializable
data class SubSearchDto<T>(
val hits: List<T>,
)
@Serializable
data class SearchLibraryDto(
val document: DocElementsDto,
)
@Serializable
data class DocElementsDto(
val id: String,
val title_en: String,
val image_large: String,
val image_small: String
)