999Hentai: update urls and some changes (#18953)
* update urls again * small update to filters * remove unnecessary function and add short title preference short title stolen from Nhentai ext * default off * add magazine info to description * add description from site * unify popular and search parsing * auto update cdn url
This commit is contained in:
parent
ff603c36d2
commit
850965dd3f
@ -6,7 +6,7 @@ ext {
|
|||||||
extName = '999Hentai'
|
extName = '999Hentai'
|
||||||
pkgNameSuffix = 'all.ninenineninehentai'
|
pkgNameSuffix = 'all.ninenineninehentai'
|
||||||
extClass = '.NineNineNineHentaiFactory'
|
extClass = '.NineNineNineHentaiFactory'
|
||||||
extVersionCode = 4
|
extVersionCode = 5
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,9 @@ import android.app.Application
|
|||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import eu.kanade.tachiyomi.extension.all.ninenineninehentai.Url.Companion.toAbsUrl
|
import eu.kanade.tachiyomi.extension.all.ninenineninehentai.Url.Companion.toAbsUrl
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.POST
|
import eu.kanade.tachiyomi.network.POST
|
||||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
@ -16,10 +18,11 @@ import eu.kanade.tachiyomi.source.model.Page
|
|||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.Headers
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
@ -39,15 +42,32 @@ open class NineNineNineHentai(
|
|||||||
|
|
||||||
override val name = "999Hentai"
|
override val name = "999Hentai"
|
||||||
|
|
||||||
override val baseUrl = "https://999hentai.to"
|
override val baseUrl = "https://999hentai.net"
|
||||||
|
|
||||||
private val apiUrl = "https://hapi.allanime.day/api"
|
private val apiUrl = "https://api.999hentai.net/api"
|
||||||
|
|
||||||
override val supportsLatest = true
|
override val supportsLatest = true
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
override val client = network.cloudflareClient.newBuilder()
|
override val client = network.cloudflareClient.newBuilder()
|
||||||
|
.addInterceptor { chain ->
|
||||||
|
val request = chain.request()
|
||||||
|
val url = request.url
|
||||||
|
|
||||||
|
if (url.host != "127.0.0.1") {
|
||||||
|
return@addInterceptor chain.proceed(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
val newRequest = request.newBuilder()
|
||||||
|
.url(
|
||||||
|
url.newBuilder()
|
||||||
|
.host(preference.cdnUrl)
|
||||||
|
.build(),
|
||||||
|
).build()
|
||||||
|
|
||||||
|
return@addInterceptor chain.proceed(newRequest)
|
||||||
|
}
|
||||||
.rateLimit(1)
|
.rateLimit(1)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@ -55,35 +75,21 @@ open class NineNineNineHentai(
|
|||||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun headersBuilder() = super.headersBuilder()
|
||||||
|
.set("Referer", "$baseUrl/")
|
||||||
|
|
||||||
override fun popularMangaRequest(page: Int): Request {
|
override fun popularMangaRequest(page: Int): Request {
|
||||||
val payload = GraphQL(
|
val payload = GraphQL(
|
||||||
PopularVariables(size, page, 1, siteLang),
|
PopularVariables(size, page, 1, siteLang),
|
||||||
POPULAR_QUERY,
|
POPULAR_QUERY,
|
||||||
)
|
).toJsonRequestBody()
|
||||||
|
|
||||||
val requestBody = payload.toJsonRequestBody()
|
return POST(apiUrl, headers, payload)
|
||||||
|
|
||||||
val apiHeaders = headersBuilder().buildApiHeaders(requestBody)
|
|
||||||
|
|
||||||
return POST(apiUrl, apiHeaders, requestBody)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun popularMangaParse(response: Response): MangasPage {
|
override fun popularMangaParse(response: Response) = browseMangaParse<PopularResponse>(response)
|
||||||
val res = response.parseAs<ApiPopularResponse>()
|
|
||||||
val mangas = res.data.popular.edges
|
|
||||||
val dateMap = preference.dateMap
|
|
||||||
val entries = mangas.map { manga ->
|
|
||||||
manga.uploadDate?.let { dateMap[manga.id] = it }
|
|
||||||
manga.toSManga()
|
|
||||||
}
|
|
||||||
preference.dateMap = dateMap
|
|
||||||
val hasNextPage = mangas.size == size
|
|
||||||
|
|
||||||
return MangasPage(entries, hasNextPage)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun latestUpdatesRequest(page: Int) = searchMangaRequest(page, "", FilterList())
|
override fun latestUpdatesRequest(page: Int) = searchMangaRequest(page, "", FilterList())
|
||||||
|
|
||||||
override fun latestUpdatesParse(response: Response) = searchMangaParse(response)
|
override fun latestUpdatesParse(response: Response) = searchMangaParse(response)
|
||||||
|
|
||||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||||
@ -114,42 +120,21 @@ open class NineNineNineHentai(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SEARCH_QUERY,
|
SEARCH_QUERY,
|
||||||
)
|
).toJsonRequestBody()
|
||||||
|
|
||||||
val requestBody = payload.toJsonRequestBody()
|
return POST(apiUrl, headers, payload)
|
||||||
|
|
||||||
val apiHeaders = headersBuilder().buildApiHeaders(requestBody)
|
|
||||||
|
|
||||||
return POST(apiUrl, apiHeaders, requestBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun searchMangaParse(response: Response): MangasPage {
|
|
||||||
val res = response.parseAs<ApiSearchResponse>()
|
|
||||||
val mangas = res.data.search.edges
|
|
||||||
val dateMap = preference.dateMap
|
|
||||||
val entries = mangas.map { manga ->
|
|
||||||
manga.uploadDate?.let { dateMap[manga.id] = it }
|
|
||||||
manga.toSManga()
|
|
||||||
}
|
|
||||||
preference.dateMap = dateMap
|
|
||||||
val hasNextPage = mangas.size == size
|
|
||||||
|
|
||||||
return MangasPage(entries, hasNextPage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response) = browseMangaParse<SearchResponse>(response)
|
||||||
override fun getFilterList() = getFilters()
|
override fun getFilterList() = getFilters()
|
||||||
|
|
||||||
private fun mangaFromIDRequest(id: String): Request {
|
private fun mangaFromIDRequest(id: String): Request {
|
||||||
val payload = GraphQL(
|
val payload = GraphQL(
|
||||||
IdVariables(id),
|
IdVariables(id),
|
||||||
DETAILS_QUERY,
|
DETAILS_QUERY,
|
||||||
)
|
).toJsonRequestBody()
|
||||||
|
|
||||||
val requestBody = payload.toJsonRequestBody()
|
return POST(apiUrl, headers, payload)
|
||||||
|
|
||||||
val apiHeaders = headersBuilder().buildApiHeaders(requestBody)
|
|
||||||
|
|
||||||
return POST(apiUrl, apiHeaders, requestBody)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun searchMangaFromIDParse(response: Response): MangasPage {
|
private fun searchMangaFromIDParse(response: Response): MangasPage {
|
||||||
@ -161,7 +146,7 @@ open class NineNineNineHentai(
|
|||||||
preference.dateMap = preference.dateMap.also { dateMap ->
|
preference.dateMap = preference.dateMap.also { dateMap ->
|
||||||
manga.uploadDate?.let { dateMap[manga.id] = it }
|
manga.uploadDate?.let { dateMap[manga.id] = it }
|
||||||
}
|
}
|
||||||
manga.toSManga()
|
manga.toSManga(preference.shortTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
return MangasPage(listOfNotNull(manga), false)
|
return MangasPage(listOfNotNull(manga), false)
|
||||||
@ -179,7 +164,7 @@ open class NineNineNineHentai(
|
|||||||
manga.uploadDate?.let { dateMap[manga.id] = it }
|
manga.uploadDate?.let { dateMap[manga.id] = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
return manga.toSManga()
|
return manga.toSManga(preference.shortTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMangaUrl(manga: SManga) = "$baseUrl/hchapter/${manga.url}"
|
override fun getMangaUrl(manga: SManga) = "$baseUrl/hchapter/${manga.url}"
|
||||||
@ -209,13 +194,9 @@ open class NineNineNineHentai(
|
|||||||
val payload = GraphQL(
|
val payload = GraphQL(
|
||||||
IdVariables(chapter.url),
|
IdVariables(chapter.url),
|
||||||
PAGES_QUERY,
|
PAGES_QUERY,
|
||||||
)
|
).toJsonRequestBody()
|
||||||
|
|
||||||
val requestBody = payload.toJsonRequestBody()
|
return POST(apiUrl, headers, payload)
|
||||||
|
|
||||||
val apiHeaders = headersBuilder().buildApiHeaders(requestBody)
|
|
||||||
|
|
||||||
return POST(apiUrl, apiHeaders, requestBody)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
@ -224,7 +205,8 @@ open class NineNineNineHentai(
|
|||||||
val pages = res.data.chapter.pages?.firstOrNull()
|
val pages = res.data.chapter.pages?.firstOrNull()
|
||||||
?: return emptyList()
|
?: return emptyList()
|
||||||
|
|
||||||
val cdn = pages.urlPart.toAbsUrl()
|
val cdnUrl = "https://${getUpdatedCdn(res.data.chapter.id)}/"
|
||||||
|
val cdn = pages.urlPart.toAbsUrl(cdnUrl)
|
||||||
|
|
||||||
val selectedImages = when (preference.getString(PREF_IMG_QUALITY_KEY, "original")) {
|
val selectedImages = when (preference.getString(PREF_IMG_QUALITY_KEY, "original")) {
|
||||||
"medium" -> pages.qualityMedium?.mapIndexed { i, it ->
|
"medium" -> pages.qualityMedium?.mapIndexed { i, it ->
|
||||||
@ -238,6 +220,21 @@ open class NineNineNineHentai(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getUpdatedCdn(chapterId: String): String {
|
||||||
|
val url = "$baseUrl/hchapter/$chapterId"
|
||||||
|
val document = client.newCall(GET(url, headers))
|
||||||
|
.execute().use { it.asJsoup() }
|
||||||
|
|
||||||
|
val cdnHost = document.selectFirst("meta[property=og:image]")
|
||||||
|
?.attr("content")
|
||||||
|
?.toHttpUrlOrNull()
|
||||||
|
?.host
|
||||||
|
|
||||||
|
return cdnHost?.also {
|
||||||
|
preference.cdnUrl = it
|
||||||
|
} ?: preference.cdnUrl
|
||||||
|
}
|
||||||
|
|
||||||
private inline fun <reified T> String.parseAs(): T =
|
private inline fun <reified T> String.parseAs(): T =
|
||||||
json.decodeFromString(this)
|
json.decodeFromString(this)
|
||||||
|
|
||||||
@ -251,17 +248,27 @@ open class NineNineNineHentai(
|
|||||||
json.encodeToString(this)
|
json.encodeToString(this)
|
||||||
.toRequestBody(JSON_MEDIA_TYPE)
|
.toRequestBody(JSON_MEDIA_TYPE)
|
||||||
|
|
||||||
private fun Headers.Builder.buildApiHeaders(requestBody: RequestBody) = this
|
|
||||||
.add("Content-Length", requestBody.contentLength().toString())
|
|
||||||
.add("Content-Type", requestBody.contentType().toString())
|
|
||||||
.build()
|
|
||||||
|
|
||||||
private fun String?.parseDate(): Long {
|
private fun String?.parseDate(): Long {
|
||||||
return runCatching {
|
return runCatching {
|
||||||
dateFormat.parse(this!!.trim())!!.time
|
dateFormat.parse(this!!.trim())!!.time
|
||||||
}.getOrDefault(0L)
|
}.getOrDefault(0L)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inline fun <reified T : BrowseResponse> browseMangaParse(response: Response): MangasPage {
|
||||||
|
val res = response.parseAs<Data<T>>()
|
||||||
|
val mangas = res.data.chapters.edges
|
||||||
|
val dateMap = preference.dateMap
|
||||||
|
val useShortTitle = preference.shortTitle
|
||||||
|
val entries = mangas.map { manga ->
|
||||||
|
manga.uploadDate?.let { dateMap[manga.id] = it }
|
||||||
|
manga.toSManga(useShortTitle)
|
||||||
|
}
|
||||||
|
preference.dateMap = dateMap
|
||||||
|
val hasNextPage = mangas.size == size
|
||||||
|
|
||||||
|
return MangasPage(entries, hasNextPage)
|
||||||
|
}
|
||||||
|
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
ListPreference(screen.context).apply {
|
ListPreference(screen.context).apply {
|
||||||
key = PREF_IMG_QUALITY_KEY
|
key = PREF_IMG_QUALITY_KEY
|
||||||
@ -271,6 +278,14 @@ open class NineNineNineHentai(
|
|||||||
setDefaultValue("original")
|
setDefaultValue("original")
|
||||||
summary = "%s"
|
summary = "%s"
|
||||||
}.also(screen::addPreference)
|
}.also(screen::addPreference)
|
||||||
|
|
||||||
|
SwitchPreferenceCompat(screen.context).apply {
|
||||||
|
key = PREF_SHORT_TITLE
|
||||||
|
title = "Display Short Titles"
|
||||||
|
summaryOff = "Showing Long Titles"
|
||||||
|
summaryOn = "Showing short Titles"
|
||||||
|
setDefaultValue(false)
|
||||||
|
}.also(screen::addPreference)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var SharedPreferences.dateMap: MutableMap<String, String>
|
private var SharedPreferences.dateMap: MutableMap<String, String>
|
||||||
@ -287,6 +302,16 @@ open class NineNineNineHentai(
|
|||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var SharedPreferences.cdnUrl: String
|
||||||
|
get() = getString(PREF_CDN_URL, DEFAULT_CDN) ?: DEFAULT_CDN
|
||||||
|
|
||||||
|
@SuppressLint("ApplySharedPref")
|
||||||
|
set(cdnUrl) {
|
||||||
|
edit().putString(PREF_CDN_URL, cdnUrl).commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val SharedPreferences.shortTitle get() = getBoolean(PREF_SHORT_TITLE, false)
|
||||||
|
|
||||||
override fun chapterListParse(response: Response) = throw UnsupportedOperationException("Not Used")
|
override fun chapterListParse(response: Response) = throw UnsupportedOperationException("Not Used")
|
||||||
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Not Used")
|
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Not Used")
|
||||||
|
|
||||||
@ -300,6 +325,10 @@ open class NineNineNineHentai(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private const val PREF_DATE_MAP_KEY = "pref_date_map"
|
private const val PREF_DATE_MAP_KEY = "pref_date_map"
|
||||||
|
private const val PREF_CDN_URL = "pref_cdn_url"
|
||||||
private const val PREF_IMG_QUALITY_KEY = "pref_image_quality"
|
private const val PREF_IMG_QUALITY_KEY = "pref_image_quality"
|
||||||
|
private const val PREF_SHORT_TITLE = "pref_short_title"
|
||||||
|
|
||||||
|
private const val DEFAULT_CDN = "edge.fast4speed.rsvp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,6 @@ import kotlinx.serialization.SerialName
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
typealias ApiPopularResponse = Data<PopularResponse>
|
|
||||||
|
|
||||||
typealias ApiSearchResponse = Data<SearchResponse>
|
|
||||||
|
|
||||||
typealias ApiDetailsResponse = Data<DetailsResponse>
|
typealias ApiDetailsResponse = Data<DetailsResponse>
|
||||||
|
|
||||||
typealias ApiPageListResponse = Data<PageList>
|
typealias ApiPageListResponse = Data<PageList>
|
||||||
@ -20,15 +16,19 @@ data class Data<T>(val data: T)
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class Edges<T>(val edges: List<T>)
|
data class Edges<T>(val edges: List<T>)
|
||||||
|
|
||||||
|
interface BrowseResponse {
|
||||||
|
val chapters: Edges<ChapterResponse>
|
||||||
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class PopularResponse(
|
data class PopularResponse(
|
||||||
@SerialName("queryPopularChapters") val popular: Edges<ChapterResponse>,
|
@SerialName("queryPopularChapters") override val chapters: Edges<ChapterResponse>,
|
||||||
)
|
) : BrowseResponse
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SearchResponse(
|
data class SearchResponse(
|
||||||
@SerialName("queryChapters") val search: Edges<ChapterResponse>,
|
@SerialName("queryChapters") override val chapters: Edges<ChapterResponse>,
|
||||||
)
|
) : BrowseResponse
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class DetailsResponse(
|
data class DetailsResponse(
|
||||||
@ -41,24 +41,27 @@ data class ChapterResponse(
|
|||||||
val name: String,
|
val name: String,
|
||||||
val uploadDate: String? = null,
|
val uploadDate: String? = null,
|
||||||
val format: String? = null,
|
val format: String? = null,
|
||||||
|
val description: String? = null,
|
||||||
val language: String? = null,
|
val language: String? = null,
|
||||||
val pages: Int? = null,
|
val pages: Int? = null,
|
||||||
@SerialName("firstPics") val cover: List<Url>? = emptyList(),
|
@SerialName("firstPics") val cover: List<Url>? = emptyList(),
|
||||||
val tags: List<Tag>? = emptyList(),
|
val tags: List<Tag>? = emptyList(),
|
||||||
) {
|
) {
|
||||||
fun toSManga() = SManga.create().apply {
|
fun toSManga(shortTitle: Boolean) = SManga.create().apply {
|
||||||
url = id
|
url = id
|
||||||
title = name
|
title = if (shortTitle) name.replace(shortenTitleRegex, "").trim() else name
|
||||||
thumbnail_url = cover?.firstOrNull()?.absUrl
|
thumbnail_url = cover?.firstOrNull()?.absUrl
|
||||||
author = this@ChapterResponse.author
|
author = this@ChapterResponse.author
|
||||||
artist = author
|
artist = author
|
||||||
genre = genres
|
genre = genres
|
||||||
description = buildString {
|
description = buildString {
|
||||||
|
if (!this@ChapterResponse.description.isNullOrEmpty()) append(this@ChapterResponse.description.trim(), "\n\n")
|
||||||
if (formatParsed != null) append("Format: ${formatParsed}\n")
|
if (formatParsed != null) append("Format: ${formatParsed}\n")
|
||||||
if (languageParsed != null) append("Language: $languageParsed\n")
|
if (languageParsed != null) append("Language: $languageParsed\n")
|
||||||
if (group != null) append("Group: $group\n")
|
if (group != null) append("Group: $group\n")
|
||||||
if (characters != null) append("Character(s): $characters\n")
|
if (characters != null) append("Character(s): $characters\n")
|
||||||
if (parody != null) append("Parody: $parody\n")
|
if (parody != null) append("Parody: $parody\n")
|
||||||
|
if (magazine != null) append("Magazine: $magazine\n")
|
||||||
if (pages != null) append("Pages: $pages\n")
|
if (pages != null) append("Pages: $pages\n")
|
||||||
}
|
}
|
||||||
status = SManga.COMPLETED
|
status = SManga.COMPLETED
|
||||||
@ -69,6 +72,7 @@ data class ChapterResponse(
|
|||||||
private val formatParsed = when (format) {
|
private val formatParsed = when (format) {
|
||||||
"artistcg" -> "ArtistCG"
|
"artistcg" -> "ArtistCG"
|
||||||
"gamecg" -> "GameCG"
|
"gamecg" -> "GameCG"
|
||||||
|
"imageset" -> "ImageSet"
|
||||||
else -> format?.capitalize()
|
else -> format?.capitalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,12 +98,17 @@ data class ChapterResponse(
|
|||||||
?.joinToString { it.tagName.capitalize() }
|
?.joinToString { it.tagName.capitalize() }
|
||||||
?.takeUnless { it.isEmpty() }
|
?.takeUnless { it.isEmpty() }
|
||||||
|
|
||||||
|
private val magazine = tags?.filter { it.tagType == "magazine" }
|
||||||
|
?.joinToString { it.tagName.capitalize() }
|
||||||
|
?.takeUnless { it.isEmpty() }
|
||||||
|
|
||||||
private val genres = tags?.filterNot { it.tagType in filterTags }
|
private val genres = tags?.filterNot { it.tagType in filterTags }
|
||||||
?.joinToString { it.tagName.capitalize() }
|
?.joinToString { it.tagName.capitalize() }
|
||||||
?.takeUnless { it.isEmpty() }
|
?.takeUnless { it.isEmpty() }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val filterTags = listOf("artist", "group", "character", "parody")
|
private val filterTags = listOf("artist", "group", "character", "parody", "magazine")
|
||||||
|
private val shortenTitleRegex = Regex("""(\[[^]]*]|[({][^)}]*[)}])""")
|
||||||
|
|
||||||
private fun String.capitalize(): String {
|
private fun String.capitalize(): String {
|
||||||
return this.trim().split(" ").joinToString(" ") { word ->
|
return this.trim().split(" ").joinToString(" ") { word ->
|
||||||
@ -122,15 +131,15 @@ data class Url(val url: String) {
|
|||||||
val absUrl get() = url.toAbsUrl()
|
val absUrl get() = url.toAbsUrl()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun String.toAbsUrl(): String {
|
fun String.toAbsUrl(baseUrl: String = loUrl): String {
|
||||||
return if (this.matches(urlRegex)) {
|
return if (this.matches(urlRegex)) {
|
||||||
this
|
this
|
||||||
} else {
|
} else {
|
||||||
cdnUrl + this
|
baseUrl + this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val cdnUrl = "https://edge.fast4speed.rsvp/"
|
private const val loUrl = "https://127.0.0.1/"
|
||||||
private val urlRegex = Regex("^https?://.*")
|
private val urlRegex = Regex("^https?://.*")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,6 +157,7 @@ data class PageList(
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class PageUrl(
|
data class PageUrl(
|
||||||
|
@SerialName("_id") val id: String,
|
||||||
@SerialName("pictureUrls") val pages: List<Pages?>? = emptyList(),
|
@SerialName("pictureUrls") val pages: List<Pages?>? = emptyList(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@ class SortFilter : SelectFilter(
|
|||||||
arrayOf(
|
arrayOf(
|
||||||
Pair("Update", ""),
|
Pair("Update", ""),
|
||||||
Pair("Popular", "Popular"),
|
Pair("Popular", "Popular"),
|
||||||
Pair("Top", "Top"),
|
|
||||||
Pair("Name Ascending", "Name_ASC"),
|
Pair("Name Ascending", "Name_ASC"),
|
||||||
Pair("Name Descending", "Name_DESC"),
|
Pair("Name Descending", "Name_DESC"),
|
||||||
),
|
),
|
||||||
@ -45,6 +44,7 @@ class FormatFilter : SelectFilter(
|
|||||||
Pair("Doujinshi", "doujinshi"),
|
Pair("Doujinshi", "doujinshi"),
|
||||||
Pair("ArtistCG", "artistcg"),
|
Pair("ArtistCG", "artistcg"),
|
||||||
Pair("GameCG", "gamecg"),
|
Pair("GameCG", "gamecg"),
|
||||||
|
Pair("ImageSet", "imageset"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -25,6 +25,7 @@ val POPULAR_QUERY: String = buildQuery {
|
|||||||
name
|
name
|
||||||
uploadDate
|
uploadDate
|
||||||
format
|
format
|
||||||
|
description
|
||||||
language
|
language
|
||||||
pages
|
pages
|
||||||
firstPics
|
firstPics
|
||||||
@ -52,6 +53,7 @@ val SEARCH_QUERY: String = buildQuery {
|
|||||||
name
|
name
|
||||||
uploadDate
|
uploadDate
|
||||||
format
|
format
|
||||||
|
description
|
||||||
language
|
language
|
||||||
pages
|
pages
|
||||||
firstPics
|
firstPics
|
||||||
@ -74,6 +76,7 @@ val DETAILS_QUERY: String = buildQuery {
|
|||||||
name
|
name
|
||||||
uploadDate
|
uploadDate
|
||||||
format
|
format
|
||||||
|
description
|
||||||
language
|
language
|
||||||
pages
|
pages
|
||||||
firstPics
|
firstPics
|
||||||
@ -91,6 +94,7 @@ val PAGES_QUERY: String = buildQuery {
|
|||||||
queryChapter(
|
queryChapter(
|
||||||
chapterId: %id
|
chapterId: %id
|
||||||
) {
|
) {
|
||||||
|
_id
|
||||||
pictureUrls {
|
pictureUrls {
|
||||||
picCdn
|
picCdn
|
||||||
pics
|
pics
|
||||||
|
Loading…
x
Reference in New Issue
Block a user