Remove Kouhai Work (#525)
This commit is contained in:
parent
a4c0420bf7
commit
fa3fc7188f
|
@ -1,18 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<application>
|
|
||||||
<activity android:name=".en.kouhaiwork.KouhaiActivity"
|
|
||||||
android:theme="@android:style/Theme.NoDisplay"
|
|
||||||
android:exported="true"
|
|
||||||
android:excludeFromRecents="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
|
||||||
<data android:host="kouhai.work"
|
|
||||||
android:pathPattern="/series/..*"
|
|
||||||
android:scheme="https" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
</application>
|
|
||||||
</manifest>
|
|
|
@ -1,7 +0,0 @@
|
||||||
ext {
|
|
||||||
extName = 'Kouhai Work'
|
|
||||||
extClass = '.KouhaiWork'
|
|
||||||
extVersionCode = 8
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
|
Binary file not shown.
Before Width: | Height: | Size: 5.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 8.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
Before Width: | Height: | Size: 22 KiB |
|
@ -1,116 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.kouhaiwork
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import java.text.DecimalFormat
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
const val ID_QUERY = "id:"
|
|
||||||
|
|
||||||
const val API_URL = "https://api.kouhai.work/v3"
|
|
||||||
|
|
||||||
const val STORAGE_URL = "https://api.kouhai.work/storage/"
|
|
||||||
|
|
||||||
private const val ISO_DATE = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'"
|
|
||||||
|
|
||||||
private val dateFormat = SimpleDateFormat(ISO_DATE, Locale.ROOT)
|
|
||||||
|
|
||||||
private val decimalFormat = DecimalFormat("#.##")
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class KouhaiSeries(
|
|
||||||
val id: Int,
|
|
||||||
val title: String,
|
|
||||||
val cover: String,
|
|
||||||
) {
|
|
||||||
inline val url get() = id.toString()
|
|
||||||
|
|
||||||
inline val thumbnail get() = STORAGE_URL + cover
|
|
||||||
|
|
||||||
override fun toString() = title.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class KouhaiSeriesDetails(
|
|
||||||
val synopsis: String,
|
|
||||||
val status: String,
|
|
||||||
val alternative_titles: List<String>? = null,
|
|
||||||
val artists: List<KouhaiTag>? = null,
|
|
||||||
val authors: List<KouhaiTag>? = null,
|
|
||||||
val genres: List<KouhaiTag>? = null,
|
|
||||||
val themes: List<KouhaiTag>? = null,
|
|
||||||
val demographics: List<KouhaiTag>? = null,
|
|
||||||
val chapters: List<KouhaiChapter>,
|
|
||||||
) {
|
|
||||||
val tags by lazy {
|
|
||||||
genres.orEmpty() + themes.orEmpty() + demographics.orEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString() = buildString {
|
|
||||||
append(synopsis)
|
|
||||||
alternative_titles?.joinTo(
|
|
||||||
this,
|
|
||||||
"\n",
|
|
||||||
"\n\nAlternative Names:\n",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class KouhaiChapter(
|
|
||||||
val id: Int,
|
|
||||||
val volume: Int? = null,
|
|
||||||
val number: Float,
|
|
||||||
val name: String? = null,
|
|
||||||
val groups: List<KouhaiTag>,
|
|
||||||
val updated_at: String,
|
|
||||||
) {
|
|
||||||
inline val url get() = id.toString()
|
|
||||||
|
|
||||||
val timestamp by lazy {
|
|
||||||
dateFormat.parse(updated_at)?.time ?: 0L
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString() = buildString {
|
|
||||||
volume?.let {
|
|
||||||
append("[Vol. ").append(it).append("] ")
|
|
||||||
}
|
|
||||||
append("Chapter ")
|
|
||||||
append(decimalFormat.format(number))
|
|
||||||
name?.let { append(" - ").append(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class KouhaiTag(
|
|
||||||
private val id: Int,
|
|
||||||
private val name: String,
|
|
||||||
) {
|
|
||||||
override fun toString() = name
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class KouhaiTagList(
|
|
||||||
val genres: List<KouhaiTag>,
|
|
||||||
val themes: List<KouhaiTag>,
|
|
||||||
val demographics: List<KouhaiTag>,
|
|
||||||
val status: KouhaiTag?,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class KouhaiPages(
|
|
||||||
private val pages: List<KouhaiMedia>,
|
|
||||||
) : Iterable<KouhaiMedia> by pages
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class KouhaiMedia(private val media: String) {
|
|
||||||
override fun toString() = STORAGE_URL + media
|
|
||||||
}
|
|
||||||
|
|
||||||
typealias KouhaiSearch = List<String>
|
|
||||||
|
|
||||||
inline val KouhaiSearch.url get() = this[0]
|
|
||||||
|
|
||||||
inline val KouhaiSearch.title get() = this[1].trim()
|
|
||||||
|
|
||||||
inline val KouhaiSearch.thumbnail get() = STORAGE_URL + last()
|
|
|
@ -1,35 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.kouhaiwork
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.util.Log
|
|
||||||
import kotlin.system.exitProcess
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Springboard that accepts `{baseUrl}/series/{id}`
|
|
||||||
* intents and redirects them to the main Tachiyomi process.
|
|
||||||
*/
|
|
||||||
class KouhaiActivity : Activity() {
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
val segments = intent?.data?.pathSegments
|
|
||||||
if (segments != null && segments.size > 1) {
|
|
||||||
val activity = Intent().apply {
|
|
||||||
action = "eu.kanade.tachiyomi.SEARCH"
|
|
||||||
putExtra("query", ID_QUERY + segments[1])
|
|
||||||
putExtra("filter", packageName)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
startActivity(activity)
|
|
||||||
} catch (ex: ActivityNotFoundException) {
|
|
||||||
Log.e("KouhaiActivity", ex.message, ex)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.e("KouhaiActivity", "Failed to parse URI from intent: $intent")
|
|
||||||
}
|
|
||||||
finish()
|
|
||||||
exitProcess(0)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.kouhaiwork
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
|
||||||
|
|
||||||
class Genre(val id: Int, name: String) : Filter.CheckBox(name)
|
|
||||||
|
|
||||||
private val genres: List<Genre>
|
|
||||||
get() = listOf(
|
|
||||||
Genre(1, "Romance"),
|
|
||||||
Genre(2, "Comedy"),
|
|
||||||
Genre(3, "Slice of Life"),
|
|
||||||
Genre(4, "Fantasy"),
|
|
||||||
Genre(5, "Sci-Fi"),
|
|
||||||
Genre(6, "Psychological"),
|
|
||||||
Genre(7, "Horror"),
|
|
||||||
Genre(8, "Mystery"),
|
|
||||||
Genre(9, "Girls' Love"),
|
|
||||||
Genre(10, "Drama"),
|
|
||||||
Genre(11, "Action"),
|
|
||||||
Genre(12, "Ecchi"),
|
|
||||||
Genre(13, "Adventure"),
|
|
||||||
)
|
|
||||||
|
|
||||||
class GenresFilter(values: List<Genre> = genres) :
|
|
||||||
Filter.Group<Genre>("Genres", values)
|
|
||||||
|
|
||||||
class Theme(val id: Int, name: String) : Filter.CheckBox(name)
|
|
||||||
|
|
||||||
private val themes: List<Theme>
|
|
||||||
get() = listOf(
|
|
||||||
Theme(1, "Office Workers"),
|
|
||||||
Theme(2, "Family"),
|
|
||||||
Theme(3, "Supernatural"),
|
|
||||||
Theme(4, "Demons"),
|
|
||||||
Theme(5, "Magic"),
|
|
||||||
Theme(6, "Aliens"),
|
|
||||||
Theme(7, "Suggestive"),
|
|
||||||
Theme(8, "Doujinshi"),
|
|
||||||
Theme(9, "School Life"),
|
|
||||||
Theme(10, "Police"),
|
|
||||||
)
|
|
||||||
|
|
||||||
class ThemesFilter(values: List<Theme> = themes) :
|
|
||||||
Filter.Group<Theme>("Themes", values)
|
|
||||||
|
|
||||||
private val demographics: Array<String>
|
|
||||||
get() = arrayOf("Any", "Shounen", "Shoujo", "Seinen")
|
|
||||||
|
|
||||||
class DemographicsFilter(values: Array<String> = demographics) :
|
|
||||||
Filter.Select<String>("Demographic", values)
|
|
||||||
|
|
||||||
private val statuses: Array<String>
|
|
||||||
get() = arrayOf("Any", "Ongoing", "Finished", "Axed/Dropped")
|
|
||||||
|
|
||||||
class StatusFilter(values: Array<String> = statuses) :
|
|
||||||
Filter.Select<String>("Status", values)
|
|
||||||
|
|
||||||
inline fun <reified T> List<Filter<*>>.find() = find { it is T } as? T
|
|
|
@ -1,157 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.kouhaiwork
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
|
||||||
import eu.kanade.tachiyomi.network.POST
|
|
||||||
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.json.Json
|
|
||||||
import kotlinx.serialization.json.decodeFromJsonElement
|
|
||||||
import kotlinx.serialization.json.encodeToJsonElement
|
|
||||||
import kotlinx.serialization.json.jsonObject
|
|
||||||
import okhttp3.FormBody
|
|
||||||
import okhttp3.Response
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
|
|
||||||
class KouhaiWork : HttpSource() {
|
|
||||||
override val name = "Kouhai Work"
|
|
||||||
|
|
||||||
override val baseUrl = "https://kouhai.work"
|
|
||||||
|
|
||||||
override val lang = "en"
|
|
||||||
|
|
||||||
override val supportsLatest = true
|
|
||||||
|
|
||||||
override val id = 1273675602267580928L
|
|
||||||
|
|
||||||
private val json by lazy {
|
|
||||||
Json(Injekt.get()) { isLenient = true }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun latestUpdatesRequest(page: Int) =
|
|
||||||
GET("$API_URL/manga/recent", headers)
|
|
||||||
|
|
||||||
override fun latestUpdatesParse(response: Response) =
|
|
||||||
response.decode<List<KouhaiSeries>>().map {
|
|
||||||
SManga.create().apply {
|
|
||||||
url = it.url
|
|
||||||
title = it.toString()
|
|
||||||
thumbnail_url = it.thumbnail
|
|
||||||
}
|
|
||||||
}.let { MangasPage(it, false) }
|
|
||||||
|
|
||||||
override fun popularMangaRequest(page: Int) =
|
|
||||||
GET("$API_URL/manga/all", headers)
|
|
||||||
|
|
||||||
override fun popularMangaParse(response: Response) =
|
|
||||||
latestUpdatesParse(response)
|
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
|
|
||||||
FormBody.Builder().add("search", query).add("tags", filters.json())
|
|
||||||
.let { POST("$API_URL/search/manga", headers, it.build()) }
|
|
||||||
|
|
||||||
override fun searchMangaParse(response: Response) =
|
|
||||||
response.decode<List<KouhaiSearch>>().map {
|
|
||||||
SManga.create().apply {
|
|
||||||
url = it.url
|
|
||||||
title = it.title
|
|
||||||
thumbnail_url = it.thumbnail
|
|
||||||
}
|
|
||||||
}.let { MangasPage(it, false) }
|
|
||||||
|
|
||||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList) =
|
|
||||||
if (!query.startsWith(ID_QUERY)) {
|
|
||||||
super.fetchSearchManga(page, query, filters)
|
|
||||||
} else {
|
|
||||||
val id = query.substringAfter(ID_QUERY)
|
|
||||||
val req = GET("$API_URL/manga/get/$id", headers)
|
|
||||||
client.newCall(req).asObservableSuccess().map {
|
|
||||||
val series = it.decode<KouhaiSeries>()
|
|
||||||
val manga = SManga.create().apply {
|
|
||||||
url = series.url
|
|
||||||
title = series.title
|
|
||||||
thumbnail_url = series.thumbnail
|
|
||||||
}
|
|
||||||
MangasPage(listOf(manga), false)
|
|
||||||
}!!
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun mangaDetailsRequest(manga: SManga) =
|
|
||||||
GET("$API_URL/manga/get/${manga.url}", headers)
|
|
||||||
|
|
||||||
override fun mangaDetailsParse(response: Response) =
|
|
||||||
SManga.create().apply {
|
|
||||||
val series = response.decode<KouhaiSeriesDetails>()
|
|
||||||
description = series.toString()
|
|
||||||
author = series.authors?.joinToString()
|
|
||||||
artist = series.artists?.joinToString()
|
|
||||||
genre = series.tags.joinToString()
|
|
||||||
status = when (series.status) {
|
|
||||||
"ongoing" -> SManga.ONGOING
|
|
||||||
"finished" -> SManga.COMPLETED
|
|
||||||
else -> SManga.UNKNOWN
|
|
||||||
}
|
|
||||||
initialized = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga)
|
|
||||||
|
|
||||||
override fun chapterListParse(response: Response) =
|
|
||||||
response.decode<KouhaiSeriesDetails>().chapters.map {
|
|
||||||
SChapter.create().apply {
|
|
||||||
url = it.url
|
|
||||||
name = it.toString()
|
|
||||||
chapter_number = it.number
|
|
||||||
date_upload = it.timestamp
|
|
||||||
scanlator = it.groups.joinToString()
|
|
||||||
}
|
|
||||||
}.reversed()
|
|
||||||
|
|
||||||
override fun pageListRequest(chapter: SChapter) =
|
|
||||||
GET("$API_URL/chapters/get/${chapter.url}", headers)
|
|
||||||
|
|
||||||
override fun pageListParse(response: Response) =
|
|
||||||
response.decode<KouhaiPages>("chapter")
|
|
||||||
.mapIndexed { idx, img -> Page(idx, "", img.toString()) }
|
|
||||||
|
|
||||||
override fun getMangaUrl(manga: SManga) = "$baseUrl/series/${manga.url}"
|
|
||||||
|
|
||||||
override fun getChapterUrl(chapter: SChapter) = "$baseUrl/read/${chapter.url}"
|
|
||||||
|
|
||||||
override fun getFilterList() = FilterList(
|
|
||||||
GenresFilter(),
|
|
||||||
ThemesFilter(),
|
|
||||||
DemographicsFilter(),
|
|
||||||
StatusFilter(),
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun imageUrlParse(response: Response) =
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
|
|
||||||
private fun FilterList.json() = json.encodeToJsonElement(
|
|
||||||
KouhaiTagList(
|
|
||||||
find<GenresFilter>()?.state?.filter { it.state }?.map {
|
|
||||||
KouhaiTag(it.id, it.name)
|
|
||||||
} ?: emptyList(),
|
|
||||||
find<ThemesFilter>()?.state?.filter { it.state }?.map {
|
|
||||||
KouhaiTag(it.id, it.name)
|
|
||||||
} ?: emptyList(),
|
|
||||||
find<DemographicsFilter>()?.takeIf { it.state != 0 }?.let {
|
|
||||||
listOf(KouhaiTag(it.state, it.values[it.state]))
|
|
||||||
} ?: emptyList(),
|
|
||||||
find<StatusFilter>()?.takeIf { it.state != 0 }?.let {
|
|
||||||
KouhaiTag(it.state - 1, it.values[it.state])
|
|
||||||
},
|
|
||||||
),
|
|
||||||
).toString()
|
|
||||||
|
|
||||||
private inline fun <reified T> Response.decode(key: String = "data") =
|
|
||||||
json.decodeFromJsonElement<T>(
|
|
||||||
json.parseToJsonElement(body.string()).jsonObject[key]!!,
|
|
||||||
)
|
|
||||||
}
|
|
Loading…
Reference in New Issue