[RU]NewManga(Newbie) closed (#6620)
This commit is contained in:
parent
dfc8f73cb5
commit
f94b827056
|
@ -1,11 +0,0 @@
|
|||
ext {
|
||||
extName = 'NewManga(Newbie)'
|
||||
extClass = '.Newbie'
|
||||
extVersionCode = 19
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation project(':lib:dataimage')
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 5.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 8.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
|
@ -1,678 +0,0 @@
|
|||
package eu.kanade.tachiyomi.extension.ru.newbie
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import androidx.preference.ListPreference
|
||||
import eu.kanade.tachiyomi.extension.ru.newbie.dto.BookDto
|
||||
import eu.kanade.tachiyomi.extension.ru.newbie.dto.BranchesDto
|
||||
import eu.kanade.tachiyomi.extension.ru.newbie.dto.LibraryDto
|
||||
import eu.kanade.tachiyomi.extension.ru.newbie.dto.MangaDetDto
|
||||
import eu.kanade.tachiyomi.extension.ru.newbie.dto.PageDto
|
||||
import eu.kanade.tachiyomi.extension.ru.newbie.dto.PageWrapperDto
|
||||
import eu.kanade.tachiyomi.extension.ru.newbie.dto.SearchLibraryDto
|
||||
import eu.kanade.tachiyomi.extension.ru.newbie.dto.SearchWrapperDto
|
||||
import eu.kanade.tachiyomi.extension.ru.newbie.dto.SeriesWrapperDto
|
||||
import eu.kanade.tachiyomi.extension.ru.newbie.dto.SubSearchDto
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
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
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.DecimalFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.random.Random
|
||||
|
||||
class Newbie : ConfigurableSource, HttpSource() {
|
||||
override val name = "NewManga(Newbie)"
|
||||
|
||||
override val id: Long = 8033757373676218584
|
||||
|
||||
override val baseUrl = "https://newmanga.org"
|
||||
|
||||
override val lang = "ru"
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private var branches = mutableMapOf<String, List<BranchesDto>>()
|
||||
|
||||
private val userAgentRandomizer = "${Random.nextInt().absoluteValue}"
|
||||
|
||||
override fun headersBuilder(): Headers.Builder = Headers.Builder()
|
||||
.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/100.0.$userAgentRandomizer")
|
||||
.add("Referer", baseUrl)
|
||||
|
||||
private fun imageContentTypeIntercept(chain: Interceptor.Chain): Response {
|
||||
if (chain.request().url.queryParameter("slice").isNullOrEmpty()) {
|
||||
return chain.proceed(chain.request())
|
||||
}
|
||||
|
||||
val response = chain.proceed(chain.request())
|
||||
val image = response.body.byteString().toResponseBody("image/*".toMediaType())
|
||||
return response.newBuilder().body(image).build()
|
||||
}
|
||||
|
||||
override val client: OkHttpClient =
|
||||
network.cloudflareClient.newBuilder()
|
||||
.rateLimitHost(API_URL.toHttpUrl(), 2)
|
||||
.addInterceptor { imageContentTypeIntercept(it) }
|
||||
.build()
|
||||
|
||||
private val count = 30
|
||||
|
||||
override fun popularMangaRequest(page: Int) = GET("$API_URL/projects/popular?scale=month&size=$count&page=$page", headers)
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val page = json.decodeFromString<PageWrapperDto<LibraryDto>>(response.body.string())
|
||||
val mangas = page.items.map {
|
||||
it.toSManga()
|
||||
}
|
||||
return MangasPage(mangas, mangas.isNotEmpty())
|
||||
}
|
||||
|
||||
private fun LibraryDto.toSManga(): SManga {
|
||||
val o = this
|
||||
return SManga.create().apply {
|
||||
// Do not change the title name to ensure work with a multilingual catalog!
|
||||
title = if (isEng.equals("rus")) o.title.ru else o.title.en
|
||||
url = "$id"
|
||||
thumbnail_url = "$IMAGE_URL/${image.name}"
|
||||
}
|
||||
}
|
||||
|
||||
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 = if (isEng.equals("rus")) document.title_ru else document.title_en
|
||||
url = document.id
|
||||
thumbnail_url = if (document.image_large.isNotEmpty()) {
|
||||
"$IMAGE_URL/${document.image_large}"
|
||||
} else {
|
||||
"$IMAGE_URL/${document.image_small}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val simpleDateFormat by lazy { SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US) }
|
||||
|
||||
private fun parseDate(date: String?): Long {
|
||||
date ?: return 0L
|
||||
return try {
|
||||
simpleDateFormat.parse(date)!!.time
|
||||
} catch (_: Exception) {
|
||||
Date().time
|
||||
}
|
||||
}
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
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 mutableTitleStatus = 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 -> {
|
||||
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) {
|
||||
mutableType += '"' + type.id + '"'
|
||||
}
|
||||
}
|
||||
is StatusList -> filter.state.forEach { status ->
|
||||
if (status.state) {
|
||||
mutableStatus += '"' + status.id + '"'
|
||||
}
|
||||
}
|
||||
is StatusTitleList -> filter.state.forEach { status ->
|
||||
if (status.state) {
|
||||
mutableTitleStatus += '"' + status.id + '"'
|
||||
}
|
||||
}
|
||||
is AgeList -> filter.state.forEach { age ->
|
||||
if (age.state) {
|
||||
mutableAge += '"' + age.id + '"'
|
||||
}
|
||||
}
|
||||
is RequireChapters -> {
|
||||
if (filter.state == 1) {
|
||||
requireChapters = false
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
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":$mutableStatus},"released_year":{"min":null,"max":null},"require_chapters":$requireChapters,"original_status":{"allowed":$mutableTitleStatus},"adult":{"allowed":$mutableAge}},"pagination":{"page":$page,"size":$count}}""".toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()),
|
||||
headers = headers,
|
||||
)
|
||||
}
|
||||
|
||||
private fun parseStatus(status: String): Int {
|
||||
return when (status) {
|
||||
"completed" -> SManga.COMPLETED
|
||||
"on_going" -> SManga.ONGOING
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseType(type: String): String {
|
||||
return when (type) {
|
||||
"manga" -> "Манга"
|
||||
"manhwa" -> "Манхва"
|
||||
"manhya" -> "Маньхуа"
|
||||
"single" -> "Сингл"
|
||||
"comics" -> "Комикс"
|
||||
"russian" -> "Руманга"
|
||||
else -> type
|
||||
}
|
||||
}
|
||||
private fun parseAge(adult: String): String {
|
||||
return when (adult) {
|
||||
"" -> "0+"
|
||||
else -> "$adult+"
|
||||
}
|
||||
}
|
||||
|
||||
private fun MangaDetDto.toSManga(): SManga {
|
||||
val ratingValue = DecimalFormat("#,###.##").format(rating * 2).replace(",", ".").toFloat()
|
||||
val ratingStar = when {
|
||||
ratingValue > 9.5 -> "★★★★★"
|
||||
ratingValue > 8.5 -> "★★★★✬"
|
||||
ratingValue > 7.5 -> "★★★★☆"
|
||||
ratingValue > 6.5 -> "★★★✬☆"
|
||||
ratingValue > 5.5 -> "★★★☆☆"
|
||||
ratingValue > 4.5 -> "★★✬☆☆"
|
||||
ratingValue > 3.5 -> "★★☆☆☆"
|
||||
ratingValue > 2.5 -> "★✬☆☆☆"
|
||||
ratingValue > 1.5 -> "★☆☆☆☆"
|
||||
ratingValue > 0.5 -> "✬☆☆☆☆"
|
||||
else -> "☆☆☆☆☆"
|
||||
}
|
||||
val o = this
|
||||
return SManga.create().apply {
|
||||
// Do not change the title name to ensure work with a multilingual catalog!
|
||||
title = if (isEng.equals("rus")) o.title.ru else o.title.en
|
||||
url = "$id"
|
||||
thumbnail_url = "$IMAGE_URL/${image.name}"
|
||||
author = o.author?.name
|
||||
artist = o.artist?.name
|
||||
val mediaNameLanguage = if (isEng.equals("rus")) o.title.en else o.title.ru
|
||||
description = mediaNameLanguage + "\n" + ratingStar + " " + ratingValue + " [♡" + hearts + "]\n" + Jsoup.parse(o.description).text()
|
||||
genre = parseType(type) + ", " + adult?.let { parseAge(it) } + ", " + genres.joinToString { it.title.ru.capitalize() }
|
||||
status = parseStatus(o.status)
|
||||
}
|
||||
}
|
||||
|
||||
private fun titleDetailsRequest(manga: SManga): Request {
|
||||
return GET(API_URL + "/projects/" + manga.url, headers)
|
||||
}
|
||||
|
||||
// Workaround to allow "Open in browser" use the real URL.
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||
return client.newCall(titleDetailsRequest(manga))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
mangaDetailsParse(response).apply { initialized = true }
|
||||
}
|
||||
}
|
||||
|
||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||
return GET(baseUrl + "/p/" + manga.url, headers)
|
||||
}
|
||||
|
||||
override fun mangaDetailsParse(response: Response): SManga {
|
||||
val series = json.decodeFromString<MangaDetDto>(response.body.string())
|
||||
branches[series.id.toString()] = series.branches
|
||||
return series.toSManga()
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
private fun chapterName(book: BookDto): String {
|
||||
var chapterName = "${book.tom}. Глава ${DecimalFormat("#,###.##").format(book.number).replace(",", ".")}"
|
||||
if (!book.is_available) {
|
||||
chapterName += " \uD83D\uDCB2 "
|
||||
}
|
||||
if (book.name?.isNotBlank() == true) {
|
||||
chapterName += " ${book.name.capitalize()}"
|
||||
}
|
||||
return chapterName
|
||||
}
|
||||
|
||||
private fun mangaBranches(manga: SManga): List<BranchesDto> {
|
||||
val response = client.newCall(titleDetailsRequest(manga)).execute()
|
||||
val series = json.decodeFromString<MangaDetDto>(response.body.string())
|
||||
branches[series.id.toString()] = series.branches
|
||||
return series.branches
|
||||
}
|
||||
|
||||
private fun selector(b: BranchesDto): Boolean = b.is_default
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
val branch = branches.getOrElse(manga.url) { mangaBranches(manga) }
|
||||
return when {
|
||||
branch.isEmpty() -> {
|
||||
return Observable.just(listOf())
|
||||
}
|
||||
manga.status == SManga.LICENSED -> {
|
||||
Observable.error(Exception("Лицензировано - Нет глав"))
|
||||
}
|
||||
else -> {
|
||||
val branchId = branch.first { selector(it) }.id
|
||||
client.newCall(chapterListRequest(branchId))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
chapterListParse(response, manga, branchId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response) = throw UnsupportedOperationException()
|
||||
|
||||
private fun chapterListParse(response: Response, manga: SManga, branch: Long): List<SChapter> {
|
||||
var chapters = json.decodeFromString<SeriesWrapperDto<List<BookDto>>>(response.body.string()).items
|
||||
if (!preferences.getBoolean(PAID_PREF, false)) {
|
||||
chapters = chapters.filter { it.is_available }
|
||||
}
|
||||
return chapters.map { chapter ->
|
||||
SChapter.create().apply {
|
||||
chapter_number = chapter.number
|
||||
name = chapterName(chapter)
|
||||
url = "/p/${manga.url}/$branch/r/${chapter.id}"
|
||||
date_upload = parseDate(chapter.created_at)
|
||||
scanlator = chapter.translator
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun chapterListRequest(manga: SManga): Request = throw UnsupportedOperationException()
|
||||
private fun chapterListRequest(branch: Long): Request {
|
||||
return GET(
|
||||
"$API_URL/branches/$branch/chapters?reverse=true&size=1000000",
|
||||
headers,
|
||||
)
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
return GET(API_URL + "/chapters/${chapter.url.substringAfterLast("/")}/pages", headers)
|
||||
}
|
||||
|
||||
override fun getChapterUrl(chapter: SChapter): String {
|
||||
return baseUrl + chapter.url
|
||||
}
|
||||
|
||||
private fun pageListParse(response: Response, urlRequest: String): List<Page> {
|
||||
val pages = json.decodeFromString<List<PageDto>>(response.body.string())
|
||||
val result = mutableListOf<Page>()
|
||||
pages.forEach { page ->
|
||||
(1..page.slices!!).map { i ->
|
||||
result.add(Page(result.size, urlRequest + "/${page.id}?slice=$i"))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> = throw UnsupportedOperationException()
|
||||
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
||||
return client.newCall(pageListRequest(chapter))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
pageListParse(response, pageListRequest(chapter).url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
override fun fetchImageUrl(page: Page): Observable<String> {
|
||||
return Observable.just(page.url)
|
||||
}
|
||||
|
||||
override fun imageUrlRequest(page: Page): Request = throw NotImplementedError("Unused")
|
||||
|
||||
override fun imageUrlParse(response: Response): String = throw NotImplementedError("Unused")
|
||||
|
||||
override fun imageRequest(page: Page): Request {
|
||||
val refererHeaders = headersBuilder().build()
|
||||
return GET(page.imageUrl!!, refererHeaders)
|
||||
}
|
||||
|
||||
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 StatusTitleList(titles: List<CheckFilter>) : Filter.Group<CheckFilter>("Статус оригинала", titles)
|
||||
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()),
|
||||
StatusTitleList(getStatusTitleList()),
|
||||
AgeList(getAgeList()),
|
||||
RequireChapters(),
|
||||
)
|
||||
|
||||
private class OrderBy : Filter.Sort(
|
||||
"Сортировка",
|
||||
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"),
|
||||
)
|
||||
|
||||
private fun getStatusList() = listOf(
|
||||
CheckFilter("Выпускается", "ON_GOING"),
|
||||
CheckFilter("Заброшен", "ABANDONED"),
|
||||
CheckFilter("Завершён", "COMPLETED"),
|
||||
)
|
||||
|
||||
private fun getStatusTitleList() = listOf(
|
||||
CheckFilter("Выпускается", "ON_GOING"),
|
||||
CheckFilter("Приостановлен", "SUSPENDED"),
|
||||
CheckFilter("Завершён", "COMPLETED"),
|
||||
CheckFilter("Анонс", "ANNOUNCEMENT"),
|
||||
)
|
||||
|
||||
private fun getGenreList() = listOf(
|
||||
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"),
|
||||
)
|
||||
|
||||
private var isEng: String? = preferences.getString(LANGUAGE_PREF, "eng")
|
||||
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) {
|
||||
val titleLanguagePref = ListPreference(screen.context).apply {
|
||||
key = LANGUAGE_PREF
|
||||
title = LANGUAGE_PREF_Title
|
||||
entries = arrayOf("Английский", "Русский")
|
||||
entryValues = arrayOf("eng", "rus")
|
||||
summary = "%s"
|
||||
setDefaultValue("eng")
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val titleLanguage = preferences.edit().putString(LANGUAGE_PREF, newValue as String).commit()
|
||||
val warning = "Если язык обложки не изменился очистите базу данных в приложении (Настройки -> Дополнительно -> Очистить базу данных)"
|
||||
Toast.makeText(screen.context, warning, Toast.LENGTH_LONG).show()
|
||||
titleLanguage
|
||||
}
|
||||
}
|
||||
val paidChapterShow = androidx.preference.CheckBoxPreference(screen.context).apply {
|
||||
key = PAID_PREF
|
||||
title = PAID_PREF_Title
|
||||
summary = "Показывает не купленные\uD83D\uDCB2 главы(может вызвать ошибки при обновлении/автозагрузке)"
|
||||
setDefaultValue(false)
|
||||
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val checkValue = newValue as Boolean
|
||||
preferences.edit().putBoolean(key, checkValue).commit()
|
||||
}
|
||||
}
|
||||
screen.addPreference(titleLanguagePref)
|
||||
screen.addPreference(paidChapterShow)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val API_URL = "https://api.newmanga.org/v2"
|
||||
private const val IMAGE_URL = "https://storage.newmanga.org"
|
||||
|
||||
private const val LANGUAGE_PREF = "NewMangaTitleLanguage"
|
||||
private const val LANGUAGE_PREF_Title = "Выбор языка на обложке"
|
||||
|
||||
private const val PAID_PREF = "PaidChapter"
|
||||
private const val PAID_PREF_Title = "Показывать платные главы"
|
||||
}
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
package eu.kanade.tachiyomi.extension.ru.newbie.dto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
// Catalog API
|
||||
@Serializable
|
||||
data class PageWrapperDto<T>(
|
||||
val items: List<T>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LibraryDto(
|
||||
val id: Long,
|
||||
val title: TitleDto,
|
||||
val image: ImgDto,
|
||||
)
|
||||
|
||||
// Manga Details
|
||||
@Serializable
|
||||
data class MangaDetDto(
|
||||
val id: Long,
|
||||
val title: TitleDto,
|
||||
val author: AuthorDto?,
|
||||
val artist: AuthorDto?,
|
||||
val description: String,
|
||||
val image: ImgDto,
|
||||
val genres: List<TagsDto>,
|
||||
val type: String,
|
||||
val status: String,
|
||||
val rating: Float,
|
||||
val hearts: Long,
|
||||
val adult: String?,
|
||||
val branches: List<BranchesDto>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class TitleDto(
|
||||
val en: String,
|
||||
val ru: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class AuthorDto(
|
||||
val name: String?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ImgDto(
|
||||
val name: 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,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class BookDto(
|
||||
val id: Long,
|
||||
val tom: Int?,
|
||||
val name: String?,
|
||||
val number: Float,
|
||||
val created_at: String,
|
||||
val translator: String?,
|
||||
val is_available: Boolean,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
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 title_ru: String,
|
||||
val image_large: String,
|
||||
val image_small: String,
|
||||
)
|
Loading…
Reference in New Issue