Add Web Comic Gamma and Plus (#11982)

This commit is contained in:
kasperskier 2022-05-26 18:26:49 +08:00 committed by GitHub
parent bdeafa7883
commit e472d7972d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 230 additions and 0 deletions

View File

@ -0,0 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ComicGammaGenerator" type="JetRunConfigurationType" nameIsGenerated="true">
<module name="tachiyomi-extensions.multisrc" />
<option name="VM_PARAMETERS" value="" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="MAIN_CLASS_NAME" value="eu.kanade.tachiyomi.multisrc.comicgamma.ComicGammaGenerator" />
<option name="WORKING_DIRECTORY" value="" />
<method v="2">
<option name="Make" enabled="true" />
<option name="Gradle.BeforeRunTask" enabled="true" tasks="ktFormat" externalProjectPath="$PROJECT_DIR$/multisrc" vmOptions="" scriptParameters="" />
<option name="Gradle.BeforeRunTask" enabled="true" tasks="ktLint" externalProjectPath="$PROJECT_DIR$/multisrc" vmOptions="" scriptParameters="" />
</method>
</configuration>
</component>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

View File

@ -0,0 +1,100 @@
package eu.kanade.tachiyomi.multisrc.comicgamma
import eu.kanade.tachiyomi.network.GET
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.ParsedHttpSource
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import java.text.DateFormat.getDateInstance
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.TimeZone
open class ComicGamma(
override val name: String,
override val baseUrl: String,
override val lang: String = "ja",
) : ParsedHttpSource() {
override val supportsLatest = true
override val client = network.client.newBuilder().addInterceptor(PtImgInterceptor()).build()
override fun popularMangaRequest(page: Int) = GET("$baseUrl/manga/", headers)
override fun popularMangaNextPageSelector(): String? = null
override fun popularMangaSelector() = ".work_list li"
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.select("a").attr("abs:href"))
val image = element.select("img")
title = image.attr("alt").removePrefix("").removeSuffix("』作品ページへ")
thumbnail_url = image.attr("abs:src")
}
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/", headers)
override fun latestUpdatesNextPageSelector(): String? = null
override fun latestUpdatesSelector() = ".whatsnew li:contains(読む)"
override fun latestUpdatesFromElement(element: Element) = SManga.create().apply {
val url = element.select(".show_detail").attr("abs:href")
setUrlWithoutDomain(url)
title = element.select("h3").textNodes()[0].text()
thumbnail_url = url + "main.jpg"
}
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> =
fetchPopularManga(page).map { p -> MangasPage(p.mangas.filter { it.title.contains(query) }, false) }
override fun searchMangaNextPageSelector() = throw UnsupportedOperationException("Not used.")
override fun searchMangaSelector() = throw UnsupportedOperationException("Not used.")
override fun searchMangaFromElement(element: Element) = throw UnsupportedOperationException("Not used.")
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
throw UnsupportedOperationException("Not used.")
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
title = document.select("h1").text()
author = document.select(".author").text()
description = document.select(".work_sammary").text()
val nextDate = document.select(".episode_caption:contains(【次回更新】)")
if (nextDate.isNotEmpty()) {
val dateStr = nextDate.textNodes()
.filter { it.text().contains("【次回更新】") }[0]
.text().trim().removePrefix("【次回更新】")
val localDate = JST_FORMAT.parse(dateStr)?.let { LOCAL_FORMAT.format(it) }
if (localDate != null) description = "【Next/Repeat: $localDate\n$description"
}
}
override fun chapterListSelector() =
".box_episode > .box_episode_L:contains(読む), .box_episode > .box_episode_M:contains(読む)" // filter out purchase links
override fun chapterFromElement(element: Element) = SChapter.create().apply {
val url = element.select("a[id^=read]").attr("abs:href")
setUrlWithoutDomain(url)
val chapterNumber = url.removeSuffix("/").substringAfterLast('/').replace('_', '.')
val title = element.select(".episode_title").textNodes().filterNot {
it.text().contains("集中連載") || it.text().contains("配信中!!")
}.joinToString("") { it.text() }
name = "$chapterNumber $title"
val date = element.select(".episode_caption").textNodes().firstOrNull { it.text().contains("【公開日】") }
?.text()?.trim()?.removePrefix("【公開日】")
date_upload = date?.let { JST_FORMAT.parse(it)?.time } ?: -1L // hide unknown ones
}
override fun pageListParse(document: Document) =
document.select("#content > div[data-ptimg]").mapIndexed { i, e ->
Page(i, imageUrl = e.attr("abs:data-ptimg"))
}
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used.")
companion object {
private val JST_FORMAT by lazy {
SimpleDateFormat("yyyy年M月dd日(E)", Locale.JAPANESE).apply { timeZone = TimeZone.getTimeZone("JST") }
}
private val LOCAL_FORMAT by lazy { getDateInstance() }
}
}

View File

@ -0,0 +1,39 @@
package eu.kanade.tachiyomi.multisrc.comicgamma
import generator.ThemeSourceData.SingleLang
import generator.ThemeSourceGenerator
class ComicGammaGenerator : ThemeSourceGenerator {
override val themeClass = "ComicGamma"
override val themePkg = "comicgamma"
override val baseVersionCode = 1
override val sources = listOf(
SingleLang(
name = "Web Comic Gamma",
baseUrl = "https://webcomicgamma.takeshobo.co.jp",
lang = "ja",
isNsfw = false,
className = "WebComicGamma",
pkgName = "webcomicgamma",
sourceName = "Web Comic Gamma",
overrideVersionCode = 0,
),
SingleLang(
name = "Web Comic Gamma Plus",
baseUrl = "https://gammaplus.takeshobo.co.jp",
lang = "ja",
isNsfw = true,
className = "WebComicGammaPlus",
pkgName = "webcomicgammaplus",
sourceName = "Web Comic Gamma Plus",
overrideVersionCode = 0,
),
)
companion object {
@JvmStatic
fun main(args: Array<String>) {
ComicGammaGenerator().createAll()
}
}
}

View File

@ -0,0 +1,28 @@
package eu.kanade.tachiyomi.multisrc.comicgamma
import kotlinx.serialization.Serializable
val COORD_REGEX = Regex("""^i:(\d+),(\d+)\+(\d+),(\d+)>(\d+),(\d+)$""")
@Serializable
data class PtImg(val resources: Resource, val views: List<View>) {
fun getFilename() = resources.i.src
fun getViewSize() = Pair(views[0].width, views[0].height)
fun getTranslations() = views[0].coords.map(::toTranslation)
private fun toTranslation(coord: String): Translation {
val v = COORD_REGEX.matchEntire(coord)!!.destructured.toList().map(String::toInt)
return Translation(v[0], v[1], v[2], v[3], v[4], v[5])
}
}
@Serializable
data class Resource(val i: Image)
@Serializable
data class Image(val src: String, val width: Int, val height: Int)
@Serializable
data class View(val width: Int, val height: Int, val coords: List<String>)
data class Translation(val ix: Int, val iy: Int, val w: Int, val h: Int, val vx: Int, val vy: Int)

View File

@ -0,0 +1,46 @@
package eu.kanade.tachiyomi.multisrc.comicgamma
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Rect
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import uy.kohesive.injekt.injectLazy
import java.io.ByteArrayOutputStream
class PtImgInterceptor : Interceptor {
private val json: Json by injectLazy()
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
val url = request.url.toString()
if (!url.endsWith(".ptimg.json")) return response
val metadata = json.decodeFromString<PtImg>(response.body!!.string())
val imgRequest = request.newBuilder()
.url(url.replaceAfterLast('/', metadata.getFilename())).build()
val imgResponse = chain.proceed(imgRequest)
val image = BitmapFactory.decodeStream(imgResponse.body!!.byteStream())
val (width, height) = metadata.getViewSize()
val result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val view = Canvas(result)
metadata.getTranslations().forEach {
val src = Rect(it.ix, it.iy, it.ix + it.w, it.iy + it.h)
val dst = Rect(it.vx, it.vy, it.vx + it.w, it.vy + it.h)
view.drawBitmap(image, src, dst, null)
}
val output = ByteArrayOutputStream()
result.compress(Bitmap.CompressFormat.JPEG, 90, output)
val responseBody = output.toByteArray().toResponseBody("image/jpeg".toMediaType())
return imgResponse.newBuilder().body(responseBody).build()
}
}