all/SimplyHentai: Fix http 404 (#1008)

* fix: Fix popular manga page

* fix: Fix latest manga page

* fix: Fix search manga page

* chore: Minor changes to prevent future issues

* refactor: Minor refactoration

* chore: Bump version

* chore: Follow suggestion - re-add cloudflareClient

* refactor: Commit suggestion - remove unnecessary chapter_number

Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>

---------

Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>
This commit is contained in:
Claudemirovsky 2024-02-05 04:34:40 -03:00 committed by Draff
parent ac1575dd81
commit 00e4bed8ab
4 changed files with 48 additions and 74 deletions

View File

@ -1,7 +1,7 @@
ext { ext {
extName = 'Simply Hentai' extName = 'Simply Hentai'
extClass = '.SimplyHentaiFactory' extClass = '.SimplyHentaiFactory'
extVersionCode = 6 extVersionCode = 7
isNsfw = true isNsfw = true
} }

View File

@ -5,7 +5,6 @@ import android.net.Uri
import androidx.preference.EditTextPreference import androidx.preference.EditTextPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
@ -13,15 +12,20 @@ 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 kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import okhttp3.Response import okhttp3.Response
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
open class SimplyHentai(override val lang: String) : ConfigurableSource, HttpSource() { open class SimplyHentai(
override val lang: String,
private val langName: String,
) : ConfigurableSource, HttpSource() {
override val name = "Simply Hentai" override val name = "Simply Hentai"
override val baseUrl = "https://www.simply-hentai.com" override val baseUrl = "https://www.simply-hentai.com"
@ -34,47 +38,32 @@ open class SimplyHentai(override val lang: String) : ConfigurableSource, HttpSou
private val apiUrl = "https://api.simply-hentai.com/v3" private val apiUrl = "https://api.simply-hentai.com/v3"
private val langName by lazy { private val json: Json by injectLazy()
Locale.forLanguageTag(lang).displayName
}
private val json by lazy { Injekt.get<Json>() }
private val preferences by lazy { private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)!! Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)!!
} }
override fun popularMangaRequest(page: Int) = override fun popularMangaRequest(page: Int) =
Uri.parse("$apiUrl/albums").buildUpon().run { Uri.parse("$apiUrl/tag/$langName").buildUpon().run {
appendQueryParameter("si", "0") appendQueryParameter("type", "language")
appendQueryParameter("locale", lang)
appendQueryParameter("language", langName)
appendQueryParameter("sort", "spotlight")
appendQueryParameter("page", page.toString()) appendQueryParameter("page", page.toString())
GET(build().toString(), headers) GET(build().toString(), headers)
} }
override fun popularMangaParse(response: Response) = override fun popularMangaParse(response: Response) =
response.decode<SHList<SHObject>>().run { response.decode<SHList<SHDataAlbum>>().run {
MangasPage( MangasPage(
data.map { data.albums.map(SHObject::toSManga),
SManga.create().apply {
url = it.path
title = it.title
thumbnail_url = it.preview.sizes.thumb
}
},
pagination.next != null, pagination.next != null,
) )
} }
override fun latestUpdatesRequest(page: Int) = override fun latestUpdatesRequest(page: Int) =
Uri.parse("$apiUrl/albums").buildUpon().run { Uri.parse("$apiUrl/tag/$langName").buildUpon().run {
appendQueryParameter("si", "0") appendQueryParameter("type", "language")
appendQueryParameter("locale", lang)
appendQueryParameter("language", langName)
appendQueryParameter("sort", "newest")
appendQueryParameter("page", page.toString()) appendQueryParameter("page", page.toString())
appendQueryParameter("sort", "newest")
GET(build().toString(), headers) GET(build().toString(), headers)
} }
@ -83,18 +72,16 @@ open class SimplyHentai(override val lang: String) : ConfigurableSource, HttpSou
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
Uri.parse("$apiUrl/search/complex").buildUpon().run { Uri.parse("$apiUrl/search/complex").buildUpon().run {
appendQueryParameter("si", "0")
appendQueryParameter("locale", lang)
appendQueryParameter("query", query) appendQueryParameter("query", query)
appendQueryParameter("page", page.toString()) appendQueryParameter("page", page.toString())
appendQueryParameter("blacklist", blacklist) appendQueryParameter("blacklist", blacklist)
appendQueryParameter("filter[languages][0]", langName) appendQueryParameter("filter[language][0]", langName.replaceFirstChar(Char::uppercase))
filters.forEach { filter -> filters.forEach { filter ->
when (filter) { when (filter) {
is SortFilter -> { is SortFilter -> {
appendQueryParameter("sort", filter.orders[filter.state]) appendQueryParameter("sort", filter.orders[filter.state])
} }
is SeriesFilter -> filter.value?.let { is SeriesFilter -> filter.value?.also {
appendQueryParameter("filter[series_title][0]", it) appendQueryParameter("filter[series_title][0]", it)
} }
is TagsFilter -> filter.value?.forEachIndexed { idx, tag -> is TagsFilter -> filter.value?.forEachIndexed { idx, tag ->
@ -116,25 +103,14 @@ open class SimplyHentai(override val lang: String) : ConfigurableSource, HttpSou
} }
override fun searchMangaParse(response: Response) = override fun searchMangaParse(response: Response) =
response.decode<SHList<SHWrapper>>().run { response.decode<SHList<List<SHWrapper>>>().run {
MangasPage( MangasPage(
data.map { data.map { it.`object`.toSManga() },
SManga.create().apply {
url = it.`object`.path
title = it.`object`.title
thumbnail_url = it.`object`.preview.sizes.thumb
}
},
pagination.next != null, pagination.next != null,
) )
} }
override fun mangaDetailsRequest(manga: SManga) = override fun mangaDetailsRequest(manga: SManga) = chapterListRequest(manga)
GET(baseUrl + manga.url, headers)
override fun fetchMangaDetails(manga: SManga) =
client.newCall(chapterListRequest(manga))
.asObservableSuccess().map(::mangaDetailsParse)!!
override fun mangaDetailsParse(response: Response) = override fun mangaDetailsParse(response: Response) =
SManga.create().apply { SManga.create().apply {
@ -143,9 +119,9 @@ open class SimplyHentai(override val lang: String) : ConfigurableSource, HttpSou
title = album.title title = album.title
description = buildString { description = buildString {
if (!album.description.isNullOrEmpty()) { if (!album.description.isNullOrEmpty()) {
append("${album.description}\n\n") append(album.description, "\n\n")
} }
append("Series: ${album.series.title}\n") append("Series: ", album.series.title, "\n")
album.characters.joinTo(this, prefix = "Characters: ") { it.title } album.characters.joinTo(this, prefix = "Characters: ") { it.title }
} }
thumbnail_url = album.preview.sizes.thumb thumbnail_url = album.preview.sizes.thumb
@ -156,10 +132,8 @@ open class SimplyHentai(override val lang: String) : ConfigurableSource, HttpSou
} }
override fun chapterListRequest(manga: SManga) = override fun chapterListRequest(manga: SManga) =
Uri.parse("$apiUrl/album").buildUpon().run { Uri.parse("$apiUrl/manga").buildUpon().run {
appendEncodedPath(manga.url.split('/')[2]) appendEncodedPath(manga.url.split('/')[2])
appendQueryParameter("si", "0")
appendQueryParameter("locale", lang)
GET(build().toString(), headers) GET(build().toString(), headers)
} }
@ -167,18 +141,15 @@ open class SimplyHentai(override val lang: String) : ConfigurableSource, HttpSou
SChapter.create().apply { SChapter.create().apply {
val album = response.decode<SHAlbum>().data val album = response.decode<SHAlbum>().data
name = "Chapter" name = "Chapter"
chapter_number = -1f
url = "${album.path}/all-pages" url = "${album.path}/all-pages"
scanlator = album.translators.joinToString { it.title } scanlator = album.translators.joinToString { it.title }
date_upload = dateFormat.parse(album.created_at)?.time ?: 0L date_upload = dateFormat.parse(album.created_at)?.time ?: 0L
}.let(::listOf) }.let(::listOf)
override fun pageListRequest(chapter: SChapter) = override fun pageListRequest(chapter: SChapter) =
Uri.parse("$apiUrl/album").buildUpon().run { Uri.parse("$apiUrl/manga").buildUpon().run {
appendEncodedPath(chapter.url.split('/')[2]) appendEncodedPath(chapter.url.split('/')[2])
appendEncodedPath("/pages") appendEncodedPath("pages")
appendQueryParameter("si", "0")
appendQueryParameter("locale", lang)
GET(build().toString(), headers) GET(build().toString(), headers)
} }
@ -215,8 +186,8 @@ open class SimplyHentai(override val lang: String) : ConfigurableSource, HttpSou
private inline val blacklist: String private inline val blacklist: String
get() = preferences.getString("blacklist", "")!! get() = preferences.getString("blacklist", "")!!
private inline fun <reified T> Response.decode() = private inline fun <reified T> Response.decode(): T =
json.decodeFromString<T>(body.string()) json.decodeFromStream(body.byteStream())
override fun imageUrlParse(response: Response) = override fun imageUrlParse(response: Response) =
throw UnsupportedOperationException() throw UnsupportedOperationException()

View File

@ -1,9 +1,10 @@
package eu.kanade.tachiyomi.extension.all.simplyhentai package eu.kanade.tachiyomi.extension.all.simplyhentai
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
data class SHList<T>(val pagination: SHPagination, val data: List<T>) data class SHList<T>(val pagination: SHPagination, val data: T)
@Serializable @Serializable
data class SHPagination(val next: Int?) data class SHPagination(val next: Int?)
@ -11,6 +12,9 @@ data class SHPagination(val next: Int?)
@Serializable @Serializable
data class SHWrapper(val `object`: SHObject) data class SHWrapper(val `object`: SHObject)
@Serializable
data class SHDataAlbum(val albums: List<SHObject>)
@Serializable @Serializable
data class SHObject( data class SHObject(
val preview: SHImage, val preview: SHImage,
@ -18,7 +22,11 @@ data class SHObject(
val slug: String, val slug: String,
val title: String, val title: String,
) { ) {
val path by lazy { "/${series.slug}/$slug" } fun toSManga() = SManga.create().apply {
url = "/${series.slug}/$slug"
title = this@SHObject.title
thumbnail_url = preview.sizes.thumb
}
} }
@Serializable @Serializable

View File

@ -4,20 +4,15 @@ import eu.kanade.tachiyomi.source.SourceFactory
class SimplyHentaiFactory : SourceFactory { class SimplyHentaiFactory : SourceFactory {
override fun createSources() = listOf( override fun createSources() = listOf(
SimplyHentai("en"), SimplyHentai("en", "english"),
SimplyHentai("ja"), SimplyHentai("ja", "japanese"),
SimplyHentai("zh"), SimplyHentai("zh", "chinese"),
SimplyHentai("ko"), SimplyHentai("ko", "korean"),
SimplyHentai("es"), SimplyHentai("es", "spanish"),
SimplyHentai("ru"), SimplyHentai("ru", "russian"),
SimplyHentai("fr"), SimplyHentai("fr", "french"),
SimplyHentai("de"), SimplyHentai("de", "german"),
object : SimplyHentai("pt-BR") { SimplyHentai("it", "italian"),
// The site uses a Portugal flag for the language, SimplyHentai("pl", "polish"),
// but the contents are in Brazilian Portuguese.
override val id = 23032005200449651
},
SimplyHentai("it"),
SimplyHentai("pl"),
) )
} }