GANMA: remove unused code (#1407)
This commit is contained in:
parent
fdc57d60a4
commit
9ef0b03211
@ -1,6 +1,6 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'GANMA!'
|
extName = 'GANMA!'
|
||||||
extClass = '.GanmaFactory'
|
extClass = '.Ganma'
|
||||||
extVersionCode = 2
|
extVersionCode = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
package eu.kanade.tachiyomi.extension.ja.ganma
|
package eu.kanade.tachiyomi.extension.ja.ganma
|
||||||
|
|
||||||
import androidx.preference.EditTextPreference
|
|
||||||
import androidx.preference.PreferenceScreen
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.asObservable
|
import eu.kanade.tachiyomi.network.asObservable
|
||||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
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,16 +10,16 @@ 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.json.Json
|
||||||
import kotlinx.serialization.json.decodeFromStream
|
import kotlinx.serialization.json.decodeFromStream
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
open class Ganma : HttpSource(), ConfigurableSource {
|
class Ganma : HttpSource() {
|
||||||
override val id = sourceId
|
override val name = "GANMA!"
|
||||||
override val name = sourceName
|
override val lang = "ja"
|
||||||
override val lang = sourceLang
|
|
||||||
override val versionId = sourceVersionId
|
|
||||||
override val baseUrl = "https://ganma.jp"
|
override val baseUrl = "https://ganma.jp"
|
||||||
override val supportsLatest = true
|
override val supportsLatest = true
|
||||||
|
|
||||||
@ -112,11 +109,5 @@ open class Ganma : HttpSource(), ConfigurableSource {
|
|||||||
json.decodeFromStream<Result<T>>(it.body.byteStream()).root
|
json.decodeFromStream<Result<T>>(it.body.byteStream()).root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
val json: Json by injectLazy()
|
||||||
EditTextPreference(screen.context).apply {
|
|
||||||
key = METADATA_PREF
|
|
||||||
title = "Metadata (Debug)"
|
|
||||||
setDefaultValue("")
|
|
||||||
}.let { screen.addPreference(it) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,133 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.extension.ja.ganma
|
|
||||||
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.preference.PreferenceScreen
|
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
|
||||||
import eu.kanade.tachiyomi.network.POST
|
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
|
||||||
import okhttp3.Cookie
|
|
||||||
import okhttp3.CookieJar
|
|
||||||
import okhttp3.FormBody
|
|
||||||
import okhttp3.Headers
|
|
||||||
import okhttp3.HttpUrl
|
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
|
||||||
|
|
||||||
class GanmaApp(private val metadata: Metadata) : Ganma() {
|
|
||||||
|
|
||||||
override val client = network.client.newBuilder()
|
|
||||||
.cookieJar(Cookies(metadata.baseUrl.toHttpUrl().host, metadata.cookieName))
|
|
||||||
.build()
|
|
||||||
|
|
||||||
private val appHeaders: Headers = Headers.Builder().apply {
|
|
||||||
add("User-Agent", metadata.userAgent)
|
|
||||||
add("X-From", metadata.baseUrl)
|
|
||||||
}.build()
|
|
||||||
|
|
||||||
override fun chapterListRequest(manga: SManga): Request {
|
|
||||||
checkSession()
|
|
||||||
return GET(metadata.baseUrl + String.format(metadata.magazineUrl, manga.url.mangaId()), appHeaders)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun List<SChapter>.sortedDescending() = this
|
|
||||||
|
|
||||||
override fun pageListRequest(chapter: SChapter): Request {
|
|
||||||
checkSession()
|
|
||||||
val (mangaId, chapterId) = chapter.url.chapterDir()
|
|
||||||
return GET(metadata.baseUrl + String.format(metadata.storyUrl, mangaId, chapterId), appHeaders)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pageListParse(chapter: SChapter, response: Response): List<Page> =
|
|
||||||
try {
|
|
||||||
response.parseAs<AppStory>().toPageList()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw Exception("Chapter not available!")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkSession() {
|
|
||||||
val expiration = preferences.getLong(SESSION_EXPIRATION_PREF, 0)
|
|
||||||
if (System.currentTimeMillis() + 60 * 1000 <= expiration) return // at least 1 minute
|
|
||||||
var field1 = preferences.getString(TOKEN_FIELD1_PREF, "")!!
|
|
||||||
var field2 = preferences.getString(TOKEN_FIELD2_PREF, "")!!
|
|
||||||
if (field1.isEmpty() || field2.isEmpty()) {
|
|
||||||
val response = client.newCall(POST(metadata.baseUrl + metadata.tokenUrl, appHeaders)).execute()
|
|
||||||
val token: Map<String, String> = response.parseAs()
|
|
||||||
field1 = token[metadata.tokenField1]!!
|
|
||||||
field2 = token[metadata.tokenField2]!!
|
|
||||||
}
|
|
||||||
val requestBody = FormBody.Builder().apply {
|
|
||||||
add(metadata.tokenField1, field1)
|
|
||||||
add(metadata.tokenField2, field2)
|
|
||||||
}.build()
|
|
||||||
val response = client.newCall(POST(metadata.baseUrl + metadata.sessionUrl, appHeaders, requestBody)).execute()
|
|
||||||
val session: Session = response.parseAs()
|
|
||||||
preferences.edit().apply {
|
|
||||||
putString(TOKEN_FIELD1_PREF, field1)
|
|
||||||
putString(TOKEN_FIELD2_PREF, field2)
|
|
||||||
putLong(SESSION_EXPIRATION_PREF, session.expire)
|
|
||||||
}.apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun clearSession(clearToken: Boolean) {
|
|
||||||
preferences.edit().apply {
|
|
||||||
putString(SESSION_PREF, "")
|
|
||||||
putLong(SESSION_EXPIRATION_PREF, 0)
|
|
||||||
if (clearToken) {
|
|
||||||
putString(TOKEN_FIELD1_PREF, "")
|
|
||||||
putString(TOKEN_FIELD2_PREF, "")
|
|
||||||
}
|
|
||||||
}.apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
|
||||||
super.setupPreferenceScreen(screen)
|
|
||||||
SwitchPreferenceCompat(screen.context).apply {
|
|
||||||
title = "Clear session"
|
|
||||||
setOnPreferenceChangeListener { _, _ ->
|
|
||||||
clearSession(clearToken = false)
|
|
||||||
Toast.makeText(screen.context, "Session cleared", Toast.LENGTH_SHORT).show()
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}.let { screen.addPreference(it) }
|
|
||||||
SwitchPreferenceCompat(screen.context).apply {
|
|
||||||
title = "Clear token"
|
|
||||||
setOnPreferenceChangeListener { _, _ ->
|
|
||||||
clearSession(clearToken = true)
|
|
||||||
Toast.makeText(screen.context, "Token cleared", Toast.LENGTH_SHORT).show()
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}.let { screen.addPreference(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
class Cookies(private val host: String, private val name: String) : CookieJar {
|
|
||||||
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
|
||||||
if (url.host != host) return emptyList()
|
|
||||||
val cookie = Cookie.Builder().apply {
|
|
||||||
name(name)
|
|
||||||
value(preferences.getString(SESSION_PREF, "")!!)
|
|
||||||
domain(host)
|
|
||||||
}.build()
|
|
||||||
return listOf(cookie)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
|
||||||
if (url.host != host) return
|
|
||||||
for (cookie in cookies) {
|
|
||||||
if (cookie.name == name) {
|
|
||||||
preferences.edit().putString(SESSION_PREF, cookie.value).apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val TOKEN_FIELD1_PREF = "TOKEN_FIELD1"
|
|
||||||
private const val TOKEN_FIELD2_PREF = "TOKEN_FIELD2"
|
|
||||||
private const val SESSION_PREF = "SESSION"
|
|
||||||
private const val SESSION_EXPIRATION_PREF = "SESSION_EXPIRATION"
|
|
||||||
}
|
|
||||||
}
|
|
@ -166,42 +166,3 @@ class Directory(
|
|||||||
Page(i, imageUrl = "$baseUrl$file?$token")
|
Page(i, imageUrl = "$baseUrl$file?$token")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
|
||||||
class AppStory(val pages: List<AppPage>) {
|
|
||||||
fun toPageList(): List<Page> {
|
|
||||||
val result = ArrayList<Page>(pages.size)
|
|
||||||
pages.forEach {
|
|
||||||
if (it.imageURL != null) {
|
|
||||||
result.add(Page(result.size, imageUrl = it.imageURL.url))
|
|
||||||
} else if (it.afterwordImageURL != null) {
|
|
||||||
result.add(Page(result.size, imageUrl = it.afterwordImageURL.url))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
class AppPage(
|
|
||||||
val imageURL: File? = null,
|
|
||||||
val afterwordImageURL: File? = null,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Please keep the data private to support the site,
|
|
||||||
// otherwise they might change their APIs.
|
|
||||||
@Serializable
|
|
||||||
class Metadata(
|
|
||||||
val userAgent: String,
|
|
||||||
val baseUrl: String,
|
|
||||||
val tokenUrl: String,
|
|
||||||
val tokenField1: String,
|
|
||||||
val tokenField2: String,
|
|
||||||
val sessionUrl: String,
|
|
||||||
val cookieName: String,
|
|
||||||
val magazineUrl: String,
|
|
||||||
val storyUrl: String,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
class Session(val expire: Long)
|
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.extension.ja.ganma
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.util.Base64
|
|
||||||
import eu.kanade.tachiyomi.source.Source
|
|
||||||
import eu.kanade.tachiyomi.source.SourceFactory
|
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import java.security.MessageDigest
|
|
||||||
|
|
||||||
// source ID needed before class construction
|
|
||||||
// generated by running main() below
|
|
||||||
const val sourceId = 8045942616403978870
|
|
||||||
const val sourceName = "GANMA!"
|
|
||||||
const val sourceLang = "ja"
|
|
||||||
const val sourceVersionId = 1 // != extension version code
|
|
||||||
const val METADATA_PREF = "METADATA"
|
|
||||||
|
|
||||||
val json: Json = Injekt.get()
|
|
||||||
val preferences: SharedPreferences =
|
|
||||||
Injekt.get<Application>().getSharedPreferences("source_$sourceId", 0x0000)
|
|
||||||
|
|
||||||
class GanmaFactory : SourceFactory {
|
|
||||||
override fun createSources(): List<Source> {
|
|
||||||
val source = try {
|
|
||||||
val metadata = preferences.getString(METADATA_PREF, "")!!
|
|
||||||
.also { if (it.isEmpty()) throw Exception() }
|
|
||||||
.let { Base64.decode(it.toByteArray(), Base64.DEFAULT) }
|
|
||||||
GanmaApp(json.decodeFromString(String(metadata)))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Ganma()
|
|
||||||
}
|
|
||||||
return listOf(source)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
println(getSourceId()) // unfortunately there's no constexpr in Kotlin
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSourceId() = run { // copied from HttpSource
|
|
||||||
val key = "${sourceName.lowercase()}/$sourceLang/$sourceVersionId"
|
|
||||||
val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray())
|
|
||||||
(0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }.reduce(Long::or) and Long.MAX_VALUE
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user