League of Legends: new extension (#9463)
This commit is contained in:
parent
abf7fe4284
commit
64ba1e6ba4
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="eu.kanade.tachiyomi.extension"/>
|
|
@ -0,0 +1,12 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
ext {
|
||||
extName = 'League of Legends'
|
||||
pkgNameSuffix = 'all.leagueoflegends'
|
||||
extClass = '.LOLFactory'
|
||||
extVersionCode = 1
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Binary file not shown.
After Width: | Height: | Size: 7.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
Binary file not shown.
After Width: | Height: | Size: 220 KiB |
|
@ -0,0 +1,30 @@
|
|||
package eu.kanade.tachiyomi.extension.all.leagueoflegends
|
||||
|
||||
import eu.kanade.tachiyomi.source.SourceFactory
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
|
||||
@ExperimentalSerializationApi
|
||||
class LOLFactory : SourceFactory {
|
||||
override fun createSources() = listOf(
|
||||
LOLUniverse("en_us"),
|
||||
// LOLUniverse("en_gb"),
|
||||
LOLUniverse("de_de"),
|
||||
LOLUniverse("es_es"),
|
||||
LOLUniverse("fr_fr"),
|
||||
LOLUniverse("it_it"),
|
||||
// LOLUniverse("en_pl"),
|
||||
LOLUniverse("pl_pl"),
|
||||
LOLUniverse("el_gr"),
|
||||
LOLUniverse("ro_ro"),
|
||||
LOLUniverse("hu_hu"),
|
||||
LOLUniverse("cs_cz"),
|
||||
LOLUniverse("es_mx", "es-419"),
|
||||
// LOLUniverse("es_ar", "es-419"),
|
||||
LOLUniverse("pt_br", "pt-BR"),
|
||||
LOLUniverse("ja_jp"),
|
||||
LOLUniverse("ru_ru"),
|
||||
LOLUniverse("tr_tr"),
|
||||
// LOLUniverse("en_au"),
|
||||
LOLUniverse("ko_kr"),
|
||||
)
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package eu.kanade.tachiyomi.extension.all.leagueoflegends
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class LOLHub(
|
||||
private val sections: LOLSections
|
||||
) : Iterable<LOLComic> by sections
|
||||
|
||||
@Serializable
|
||||
data class LOLSections(
|
||||
val series: LOLData,
|
||||
@SerialName("one-shots")
|
||||
private val oneShots: LOLData
|
||||
) : Iterable<LOLComic> {
|
||||
override fun iterator() = (series + oneShots).iterator()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class LOLData(
|
||||
private val data: List<LOLComic>
|
||||
) : Iterable<LOLComic> by data
|
||||
|
||||
@Serializable
|
||||
data class LOLComic(
|
||||
val title: String,
|
||||
val subtitle: String? = null,
|
||||
val index: Float? = null,
|
||||
private val url: String,
|
||||
val description: String,
|
||||
val background: LOLImage,
|
||||
@SerialName("featured-champions")
|
||||
val champions: List<LOLChampion>? = null
|
||||
) {
|
||||
override fun toString() = url.substringAfter("/comic/")
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class LOLIssues(
|
||||
private val issues: List<LOLComic>
|
||||
) : Iterable<LOLComic> by issues
|
||||
|
||||
@Serializable
|
||||
data class LOLPages(
|
||||
@SerialName("staging-date")
|
||||
val date: String,
|
||||
@SerialName("desktop-pages")
|
||||
private val pages: List<List<LOLImage>>
|
||||
) : Iterable<LOLImage> {
|
||||
override fun iterator() = pages.flatten().iterator()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class LOLImage(private val uri: String) {
|
||||
override fun toString() = uri
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class LOLChampion(private val name: String) {
|
||||
override fun toString() = name
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
package eu.kanade.tachiyomi.extension.all.leagueoflegends
|
||||
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
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.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Response
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
@ExperimentalSerializationApi
|
||||
class LOLUniverse(
|
||||
private val siteLang: String,
|
||||
override val lang: String = siteLang.substring(0, 2)
|
||||
) : HttpSource() {
|
||||
override val baseUrl = "$UNIVERSE_URL/$siteLang/comic/"
|
||||
|
||||
override val name = "League of Legends"
|
||||
|
||||
override val supportsLatest = false
|
||||
|
||||
private val json by injectLazy<Json>()
|
||||
|
||||
private val pageCache = mutableMapOf<String, List<Page>>()
|
||||
|
||||
override fun headersBuilder() = super.headersBuilder()
|
||||
.set("Origin", UNIVERSE_URL).set("Referer", "$UNIVERSE_URL/")
|
||||
|
||||
override fun popularMangaRequest(page: Int) =
|
||||
GET("$MEEPS_URL/$siteLang/comics/index.json", headers)
|
||||
|
||||
// Request the actual manga URL for the webview
|
||||
override fun mangaDetailsRequest(manga: SManga) =
|
||||
GET("$UNIVERSE_URL/$siteLang/comic/${manga.url}")
|
||||
|
||||
override fun chapterListRequest(manga: SManga) =
|
||||
GET("$MEEPS_URL/$siteLang/comics/${manga.url}/index.json", headers)
|
||||
|
||||
override fun pageListRequest(chapter: SChapter) =
|
||||
GET("$COMICS_URL/$siteLang/${chapter.url}/index.json", headers)
|
||||
|
||||
override fun popularMangaParse(response: Response) =
|
||||
response.decode<LOLHub>().map {
|
||||
SManga.create().apply {
|
||||
title = it.title
|
||||
url = it.toString()
|
||||
description = it.description.clean()
|
||||
thumbnail_url = it.background.toString()
|
||||
genre = it.subtitle ?: it.champions?.joinToString()
|
||||
}
|
||||
}.run { MangasPage(this, false) }
|
||||
|
||||
override fun chapterListParse(response: Response) =
|
||||
response.decode<LOLIssues>().map {
|
||||
SChapter.create().apply {
|
||||
name = it.title
|
||||
url = it.toString()
|
||||
chapter_number = it.index ?: -1f
|
||||
fetchPageList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList) =
|
||||
client.newCall(popularMangaRequest(page)).asObservableSuccess()
|
||||
.map { popularMangaParse(it).filter(query) }!!
|
||||
|
||||
override fun fetchMangaDetails(manga: SManga) =
|
||||
Observable.just(manga.apply { initialized = true })!!
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
if ('/' !in manga.url) return super.fetchChapterList(manga)
|
||||
val chapter = SChapter.create().apply {
|
||||
url = manga.url
|
||||
name = "One Shot"
|
||||
chapter_number = 0f
|
||||
fetchPageList()
|
||||
}
|
||||
return Observable.just(listOf(chapter))
|
||||
}
|
||||
|
||||
override fun fetchPageList(chapter: SChapter) =
|
||||
Observable.just(pageCache[chapter.url].orEmpty())!!
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) =
|
||||
throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
|
||||
throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun latestUpdatesParse(response: Response) =
|
||||
throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun searchMangaParse(response: Response) =
|
||||
throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun mangaDetailsParse(response: Response) =
|
||||
throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun pageListParse(response: Response) =
|
||||
throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun imageUrlParse(response: Response) =
|
||||
throw UnsupportedOperationException("Not used")
|
||||
|
||||
private inline fun <reified T> Response.decode() =
|
||||
json.decodeFromString<T>(body!!.string())
|
||||
|
||||
private fun String.clean() =
|
||||
replace("</p> ", "</p>").replace("</p>", "\n").replace("<p>", "")
|
||||
|
||||
private fun SChapter.fetchPageList() {
|
||||
client.newCall(pageListRequest(this)).execute().decode<LOLPages>().let {
|
||||
// The chapter date is only available in the page list
|
||||
date_upload = isoDate.parse(it.date)?.time ?: 0L
|
||||
pageCache[url] = it.mapIndexed { idx, img ->
|
||||
Page(idx, "", img.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun MangasPage.filter(query: String) = copy(
|
||||
mangas.filter {
|
||||
it.title.contains(query, true) ||
|
||||
it.genre?.contains(query, true) ?: false
|
||||
}
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val UNIVERSE_URL = "https://universe.leagueoflegends.com"
|
||||
|
||||
private const val MEEPS_URL = "https://universe-meeps.leagueoflegends.com/v1"
|
||||
|
||||
private const val COMICS_URL = "https://universe-comics.leagueoflegends.com/comics"
|
||||
|
||||
private val isoDate by lazy {
|
||||
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ROOT)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue