parent
b449a1eaea
commit
65f0403ccb
12
src/all/comicake/build.gradle
Normal file
12
src/all/comicake/build.gradle
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
|
ext {
|
||||||
|
appName = 'Tachiyomi: ComiCake'
|
||||||
|
pkgNameSuffix = "all.comicake"
|
||||||
|
extClass = '.ComiCakeFactory'
|
||||||
|
extVersionCode = 1
|
||||||
|
libVersion = '1.2'
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
@ -0,0 +1,156 @@
|
|||||||
|
package eu.kanade.tachiyomi.extension.all.comicake
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import eu.kanade.tachiyomi.extension.BuildConfig
|
||||||
|
import eu.kanade.tachiyomi.source.model.*
|
||||||
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import okhttp3.Headers
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
const val COMICAKE_DEFAULT_API_ENDPOINT = "/api" // Highly unlikely to change
|
||||||
|
const val COMICAKE_DEFAULT_READER_ENDPOINT = "/r" // Can change based on CC config
|
||||||
|
|
||||||
|
open class ComiCake(override val name: String, override val baseUrl: String, override val lang: String, val readerEndpoint: String = COMICAKE_DEFAULT_READER_ENDPOINT, val apiEndpoint: String = COMICAKE_DEFAULT_API_ENDPOINT) : HttpSource() {
|
||||||
|
override val versionId = 1
|
||||||
|
override val supportsLatest = true
|
||||||
|
private val readerBase = baseUrl + readerEndpoint
|
||||||
|
private var apiBase = baseUrl + apiEndpoint
|
||||||
|
|
||||||
|
|
||||||
|
private val userAgent = "Mozilla/5.0 (" +
|
||||||
|
"Android ${Build.VERSION.RELEASE}; Mobile) " +
|
||||||
|
"Tachiyomi/${BuildConfig.VERSION_NAME}"
|
||||||
|
|
||||||
|
override fun headersBuilder() = Headers.Builder().apply {
|
||||||
|
add("User-Agent", userAgent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popularMangaRequest(page: Int): Request {
|
||||||
|
return GET("$apiBase/comics.json?ordering=-created_at&page=$page") // Not actually popular, just latest added to system
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popularMangaParse(response: Response): MangasPage {
|
||||||
|
val res = response.body()!!.string()
|
||||||
|
return getMangasPageFromComicsResponse(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMangasPageFromComicsResponse(json: String, nested: Boolean = false) : MangasPage {
|
||||||
|
var response = JSONObject(json)
|
||||||
|
var results = response.getJSONArray("results")
|
||||||
|
val mangas = ArrayList<SManga>()
|
||||||
|
val ids = mutableListOf<Int>();
|
||||||
|
|
||||||
|
for (i in 0 until results.length()) {
|
||||||
|
val obj = results.getJSONObject(i)
|
||||||
|
if(nested) {
|
||||||
|
val nestedComic = obj.getJSONObject("comic");
|
||||||
|
val id = nestedComic.getInt("id")
|
||||||
|
if(ids.contains(id))
|
||||||
|
continue
|
||||||
|
ids.add(id)
|
||||||
|
val manga = SManga.create()
|
||||||
|
manga.url = id.toString()
|
||||||
|
manga.title = nestedComic.getString("name")
|
||||||
|
mangas.add(manga)
|
||||||
|
} else {
|
||||||
|
val id = obj.getInt("id")
|
||||||
|
if(ids.contains(id))
|
||||||
|
continue
|
||||||
|
ids.add(id)
|
||||||
|
mangas.add(parseComicJson(obj))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MangasPage(mangas, if (response.getString("next").isNullOrEmpty() || response.getString("next") == "null") false else true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||||
|
return GET("$apiBase/comics/${manga.url}.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mangaDetailsParse(response: Response): SManga {
|
||||||
|
val comicJson = JSONObject(response.body()!!.string())
|
||||||
|
return parseComicJson(comicJson, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseComicJson(obj: JSONObject, human: Boolean = false) = SManga.create().apply {
|
||||||
|
if(human) {
|
||||||
|
url = "$readerBase/series/${obj.getString("slug")}/"
|
||||||
|
} else {
|
||||||
|
url = obj.getInt("id").toString() // Yeah, I know... Feel free to improve on this
|
||||||
|
}
|
||||||
|
title = obj.getString("name")
|
||||||
|
thumbnail_url = obj.getString("cover")
|
||||||
|
author = parseListNames(obj.getJSONArray("author"))
|
||||||
|
artist = parseListNames(obj.getJSONArray("artist"))
|
||||||
|
description = obj.getString("description")
|
||||||
|
genre = parseListNames(obj.getJSONArray("tags"))
|
||||||
|
status = SManga.UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseListNames(arr: JSONArray) : String {
|
||||||
|
var hold = ArrayList<String>(arr.length())
|
||||||
|
for(i in 0 until arr.length())
|
||||||
|
hold.add(arr.getJSONObject(i).getString("name"))
|
||||||
|
return hold.sorted().joinToString(", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
// TODO filters
|
||||||
|
return GET("$apiBase/comics.json?page=$page&search=$query")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response): MangasPage {
|
||||||
|
val res = response.body()!!.string()
|
||||||
|
return getMangasPageFromComicsResponse(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesRequest(page: Int): Request {
|
||||||
|
return GET("$apiBase/chapters.json?page=$page&expand=comic")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||||
|
val res = response.body()!!.string()
|
||||||
|
return getMangasPageFromComicsResponse(res, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseChapterJson(obj: JSONObject) = SChapter.create().apply {
|
||||||
|
name = obj.getString("title") // title will always have content, vs. name that's an optional field
|
||||||
|
chapter_number = (obj.getInt("chapter") + (obj.getInt("subchapter") / 10.0)).toFloat()
|
||||||
|
date_upload = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").parse(obj.getString("published_at")).time
|
||||||
|
// TODO scanlator field by adding team to expandable in CC (low priority given the use case of CC)
|
||||||
|
url = obj.getString("manifest")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chapterListRequest(manga: SManga): Request {
|
||||||
|
return GET("$apiBase/chapters.json?comic=${manga.url}&ordering=-volume%2C-chapter%2C-subchapter&n=1000", headers) // There's no pagination in Tachiyomi for chapters so we get 1k chapters
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
|
val chapterJson = JSONObject(response.body()!!.string())
|
||||||
|
var results = chapterJson.getJSONArray("results")
|
||||||
|
val ret = ArrayList<SChapter>()
|
||||||
|
for (i in 0 until results.length()) {
|
||||||
|
ret.add(parseChapterJson(results.getJSONObject(i)))
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
|
val webPub = JSONObject(response.body()!!.string())
|
||||||
|
val readingOrder = webPub.getJSONArray("readingOrder")
|
||||||
|
val ret = ArrayList<Page>();
|
||||||
|
for (i in 0 until readingOrder.length()) {
|
||||||
|
var pageUrl = readingOrder.getJSONObject(i).getString("href")
|
||||||
|
ret.add(Page(i, "", pageUrl))
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("This method should not be called!")
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package eu.kanade.tachiyomi.extension.all.comicake
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.Source
|
||||||
|
import eu.kanade.tachiyomi.source.SourceFactory
|
||||||
|
|
||||||
|
class ComiCakeFactory : SourceFactory {
|
||||||
|
override fun createSources(): List<Source> = getAllComiCake()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllComiCake(): List<Source> {
|
||||||
|
return listOf(
|
||||||
|
WhimSubs(),
|
||||||
|
ChampionScans()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
class WhimSubs : ComiCake("WhimSubs", "https://whimsubs.xyz", "en")
|
||||||
|
|
||||||
|
class ChampionScans : ComiCake("Champion Scans", "https://reader.championscans.com", "en", "/")
|
@ -5,7 +5,7 @@ ext {
|
|||||||
appName = 'Tachiyomi: FoolSlide'
|
appName = 'Tachiyomi: FoolSlide'
|
||||||
pkgNameSuffix = "all.foolslide"
|
pkgNameSuffix = "all.foolslide"
|
||||||
extClass = '.FoolSlideFactory'
|
extClass = '.FoolSlideFactory'
|
||||||
extVersionCode = 10
|
extVersionCode = 11
|
||||||
libVersion = '1.2'
|
libVersion = '1.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ class FoolSlideFactory : SourceFactory {
|
|||||||
fun getAllFoolSlide(): List<Source> {
|
fun getAllFoolSlide(): List<Source> {
|
||||||
return listOf(
|
return listOf(
|
||||||
JaminisBox(),
|
JaminisBox(),
|
||||||
ChampionScans(),
|
|
||||||
HelveticaScans(),
|
HelveticaScans(),
|
||||||
SenseScans(),
|
SenseScans(),
|
||||||
SeaOtterScans(),
|
SeaOtterScans(),
|
||||||
@ -70,8 +69,6 @@ class JaminisBox : FoolSlide("Jaimini's Box", "https://jaiminisbox.com", "en", "
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChampionScans : FoolSlide("Champion Scans", "http://reader.championscans.com", "en")
|
|
||||||
|
|
||||||
class HelveticaScans : FoolSlide("Helvetica Scans", "https://helveticascans.com", "en", "/r")
|
class HelveticaScans : FoolSlide("Helvetica Scans", "https://helveticascans.com", "en", "/r")
|
||||||
|
|
||||||
class SenseScans : FoolSlide("Sense-Scans", "https://sensescans.com", "en", "/reader")
|
class SenseScans : FoolSlide("Sense-Scans", "https://sensescans.com", "en", "/reader")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user