Add MayoTune (#9106)
* Add MayoTune * Use fallback for text contents * Use DTO and refactor * Use `selectFirst` for status * Update fallback thumbnail URL * Lint code * Implement as single source * Encapsulate `ChapterDto` * Use relative URL * Correctly handle request endpoints
This commit is contained in:
parent
5406227f0f
commit
ef9d26cfe8
9
src/en/mayotune/build.gradle
Normal file
9
src/en/mayotune/build.gradle
Normal file
@ -0,0 +1,9 @@
|
||||
ext {
|
||||
extName = 'MayoTune'
|
||||
extClass = '.MayoTune'
|
||||
extVersionCode = 1
|
||||
baseUrl = 'https://mayotune.xyz'
|
||||
isNsfw = false
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
BIN
src/en/mayotune/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/en/mayotune/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
BIN
src/en/mayotune/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/en/mayotune/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
src/en/mayotune/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/en/mayotune/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
BIN
src/en/mayotune/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/en/mayotune/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.4 KiB |
BIN
src/en/mayotune/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/en/mayotune/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
@ -0,0 +1,35 @@
|
||||
package eu.kanade.tachiyomi.extension.en.mayotune
|
||||
|
||||
import keiyoushi.utils.tryParse
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
@Serializable
|
||||
data class ChapterDto(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val number: Float,
|
||||
val pageCount: Int,
|
||||
val date: String,
|
||||
) {
|
||||
@Contextual
|
||||
private val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.US)
|
||||
|
||||
fun getChapterURL(): String = "/chapter/${this.id}"
|
||||
|
||||
fun getNumberStr(): String = if (this.number % 1 == 0f) {
|
||||
this.number.toInt().toString()
|
||||
} else {
|
||||
this.number.toString()
|
||||
}
|
||||
|
||||
fun getChapterTitle(): String = if (!this.title.isEmpty()) {
|
||||
"Chapter ${this.getNumberStr()}: ${this.title}"
|
||||
} else {
|
||||
"Chapter ${this.getNumberStr()}"
|
||||
}
|
||||
|
||||
fun getDateTimestamp(): Long = this.sdf.tryParse(this.date)
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
package eu.kanade.tachiyomi.extension.en.mayotune
|
||||
|
||||
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.HttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import keiyoushi.utils.parseAs
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import rx.Observable
|
||||
|
||||
class MayoTune() : HttpSource() {
|
||||
override val name: String = "MayoTune"
|
||||
override val baseUrl: String = "https://mayotune.xyz"
|
||||
override val lang: String = "en"
|
||||
override val versionId: Int = 1
|
||||
|
||||
private val source = SManga.create().apply {
|
||||
title = "Mayonaka Heart Tune"
|
||||
url = "/"
|
||||
thumbnail_url = "$baseUrl/img/cover.jpg"
|
||||
author = "Masakuni Igarashi"
|
||||
}
|
||||
|
||||
// Popular
|
||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
||||
return Observable.just(MangasPage(listOf(source), false))
|
||||
}
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request = throw UnsupportedOperationException()
|
||||
override fun popularMangaParse(response: Response): MangasPage =
|
||||
throw UnsupportedOperationException()
|
||||
|
||||
// Latest
|
||||
override val supportsLatest: Boolean = true
|
||||
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
|
||||
return Observable.just(MangasPage(listOf(source), false))
|
||||
}
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException()
|
||||
override fun latestUpdatesParse(response: Response): MangasPage =
|
||||
throw UnsupportedOperationException()
|
||||
|
||||
// Search
|
||||
override fun fetchSearchManga(
|
||||
page: Int,
|
||||
query: String,
|
||||
filters: FilterList,
|
||||
): Observable<MangasPage> {
|
||||
val mangas = mutableListOf<SManga>()
|
||||
|
||||
if (source.title.lowercase().contains(query.lowercase()) ||
|
||||
source.author?.lowercase()?.contains(query.lowercase()) == true
|
||||
) {
|
||||
mangas.add(source)
|
||||
}
|
||||
|
||||
return Observable.just(MangasPage(mangas, false))
|
||||
}
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
|
||||
throw UnsupportedOperationException()
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage =
|
||||
throw UnsupportedOperationException()
|
||||
|
||||
// Get Override
|
||||
override fun chapterListRequest(manga: SManga): Request {
|
||||
return GET("$baseUrl/api/chapters", headers)
|
||||
}
|
||||
|
||||
// Details
|
||||
override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply {
|
||||
val document = response.asJsoup()
|
||||
val statusText =
|
||||
document.selectFirst("div.text-center:contains(Status)")?.text()
|
||||
?.substringBefore("Status")
|
||||
?.trim()
|
||||
|
||||
url = source.url
|
||||
title = source.title
|
||||
artist = source.artist
|
||||
author = source.author
|
||||
description = document.selectFirst(".text-lg")?.text()
|
||||
genre = document.selectFirst("span.text-sm:nth-child(2)")?.text()?.replace("•", ",")
|
||||
status = when (statusText) {
|
||||
"Ongoing" -> SManga.ONGOING
|
||||
"Completed" -> SManga.COMPLETED
|
||||
"Cancelled" -> SManga.CANCELLED
|
||||
"Hiatus" -> SManga.ON_HIATUS
|
||||
"Finished" -> SManga.PUBLISHING_FINISHED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
thumbnail_url = document.selectFirst("img.object-contain")?.absUrl("src")
|
||||
?.ifEmpty { source.thumbnail_url }
|
||||
}
|
||||
|
||||
// Chapters
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val chapters = response.parseAs<List<ChapterDto>>()
|
||||
return chapters.sortedByDescending { it.number }.map { chapter ->
|
||||
SChapter.create().apply {
|
||||
url = chapter.getChapterURL()
|
||||
name = chapter.getChapterTitle()
|
||||
chapter_number = chapter.number
|
||||
date_upload = chapter.getDateTimestamp()
|
||||
}
|
||||
}
|
||||
}
|
||||
// Pages
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val document = response.asJsoup()
|
||||
return document.select("div.w-full > img").mapIndexed { index, img ->
|
||||
Page(index, imageUrl = img.absUrl("src"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user