Convert Arc-Relight extension to MangAdventure bundle (#905)

Convert Arc-Relight extension to MangAdventure bundle
This commit is contained in:
ObserverOfTime 2019-03-15 10:14:50 +00:00 committed by Carlos
parent 3b82504fc0
commit 6591683ec2
17 changed files with 164 additions and 120 deletions

View File

@ -2,10 +2,10 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
appName = 'Tachiyomi: Arc-Relight'
pkgNameSuffix = 'en.arcrelight'
extClass = '.ArcRelight'
extVersionCode = 2
appName = 'Tachiyomi: MangAdventure'
pkgNameSuffix = 'all.mangadventure'
extClass = '.MangAdventureFactory'
extVersionCode = 1
libVersion = '1.2'
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -1,9 +1,10 @@
package eu.kanade.tachiyomi.extension.en.arcrelight
package eu.kanade.tachiyomi.extension.all.mangadventure
import android.net.Uri
import android.os.Build.VERSION
import eu.kanade.tachiyomi.extension.BuildConfig
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
@ -15,16 +16,20 @@ import okhttp3.Request
import okhttp3.Response
import org.json.JSONArray
import org.json.JSONObject
import java.text.SimpleDateFormat
import java.util.Locale
/** Arc-Relight source */
class ArcRelight : HttpSource() {
override val versionId = 1
/** MangAdventure source. */
open class MangAdventure(
override val name: String,
override val baseUrl: String,
val categories: Array<String> = DEFAULT_CATEGORIES,
override val lang: String = "en",
override val versionId: Int = 1,
apiPath: String = "/api") : HttpSource() {
override val name = "Arc-Relight"
override val baseUrl = "https://arc-relight.site/api/v$versionId"
override val lang = "en"
/** The URL to the site's API. */
open val apiUrl by lazy { "$baseUrl/$apiPath/v$versionId" }
override val supportsLatest = true
@ -43,31 +48,22 @@ class ArcRelight : HttpSource() {
}
override fun latestUpdatesRequest(page: Int) = GET(
"$baseUrl/releases/", headers
"$apiUrl/releases/", headers
)
override fun pageListRequest(chapter: SChapter) = GET(
"$baseUrl/series/${chapter.url.substringAfter("/reader/")}", headers
"$apiUrl/series/${chapter.url.substringAfter("/reader/")}", headers
)
override fun chapterListRequest(manga: SManga) = GET(
"$baseUrl/series/${Uri.parse(manga.url).lastPathSegment}/", headers
"$apiUrl/series/${Uri.parse(manga.url).lastPathSegment}/", headers
)
override fun mangaDetailsRequest(manga: SManga): Request {
// Workaround to get the proper URL in openInBrowser
val method = Thread.currentThread()
.stackTrace.getOrNull(2)?.methodName ?: ""
return if (method == "openInBrowser") {
GET(manga.url, headers)
} else {
chapterListRequest(manga)
}
}
override fun mangaDetailsRequest(manga: SManga) = chapterListRequest(manga)
override fun searchMangaRequest(page: Int, query: String,
filters: FilterList): Request {
val uri = Uri.parse("$baseUrl/series/").buildUpon()
val uri = Uri.parse("$apiUrl/series/").buildUpon()
uri.appendQueryParameter("q", query)
val cat = mutableListOf<String>()
filters.forEach {
@ -107,19 +103,19 @@ class ArcRelight : HttpSource() {
volumes.keys().forEach { vol ->
val chapters = volumes.getJSONObject(vol)
chapters.keys().forEach { ch ->
ret.add(SChapter.create().apply {
fromJSON(chapters.getJSONObject(ch).also {
ret.add(SChapter.create().fromJSON(
chapters.getJSONObject(ch).also {
it.put("volume", vol)
it.put("chapter", ch)
})
})
}
))
}
}
return ret.sortedByDescending { it.name }
}
override fun mangaDetailsParse(response: Response) = SManga.create()
.apply { fromJSON(JSONObject(response.body()!!.string())) }
override fun mangaDetailsParse(response: Response) =
SManga.create().fromJSON(JSONObject(response.body()!!.string()))
override fun pageListParse(response: Response): List<Page> {
val obj = JSONObject(response.body()!!.string())
@ -165,5 +161,103 @@ class ArcRelight : HttpSource() {
throw UnsupportedOperationException(
"This method should not be called!"
)
companion object {
/** The possible statuses of a manga. */
private val STATUSES = arrayOf("Any", "Completed", "Ongoing")
/** Manga categories from MangAdventure `categories.xml` fixture. */
internal val DEFAULT_CATEGORIES = arrayOf(
"4-Koma",
"Action",
"Adventure",
"Comedy",
"Doujinshi",
"Drama",
"Ecchi",
"Fantasy",
"Gender Bender",
"Harem",
"Hentai",
"Historical",
"Horror",
"Josei",
"Martial Arts",
"Mecha",
"Mystery",
"Psychological",
"Romance",
"School Life",
"Sci-Fi",
"Seinen",
"Shoujo",
"Shoujo Ai",
"Shounen",
"Shounen Ai",
"Slice of Life",
"Smut",
"Sports",
"Supernatural",
"Tragedy",
"Yaoi",
"Yuri"
)
/**
* The HTTP date format specified in
* [RFC 1123](https://tools.ietf.org/html/rfc1123#page-55).
*/
private const val HTTP_DATE = "EEE, dd MMM yyyy HH:mm:ss zzz"
/**
* Converts a date in the [HTTP_DATE] format to a Unix timestamp.
*
* @param date The date to convert.
* @return The timestamp of the date.
*/
fun httpDateToTimestamp(date: String) =
SimpleDateFormat(HTTP_DATE, Locale.US).parse(date).time
}
/**
* Filter representing the status of a manga.
*
* @constructor Creates a [Filter.Select] object with [STATUSES].
*/
inner class Status : Filter.Select<String>("Status", STATUSES) {
/** Returns the [state] as a string. */
fun string() = values[state].toLowerCase()
}
/**
* Filter representing a manga category.
*
* @property name The display name of the category.
* @constructor Creates a [Filter.TriState] object using [name].
*/
inner class Category(name: String) : Filter.TriState(name) {
/** Returns the [state] as a string, or null if [isIgnored]. */
fun optString() = when (state) {
STATE_INCLUDE -> name.toLowerCase()
STATE_EXCLUDE -> "-" + name.toLowerCase()
else -> null
}
}
/**
* Filter representing the [categories][Category] of a manga.
*
* @constructor Creates a [Filter.Group] object with categories.
*/
inner class CategoryList : Filter.Group<Category>(
"Categories", categories.map { Category(it) }
)
/**
* Filter representing the name of an author or artist.
*
* @constructor Creates a [Filter.Text] object.
*/
inner class Person : Filter.Text("Author/Artist")
}

View File

@ -1,27 +1,10 @@
package eu.kanade.tachiyomi.extension.en.arcrelight
package eu.kanade.tachiyomi.extension.all.mangadventure
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import org.json.JSONArray
import org.json.JSONObject
import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.util.Locale
/**
* The HTTP date format specified in
* [RFC 1123](https://tools.ietf.org/html/rfc1123#page-55).
*/
private const val HTTP_DATE = "EEE, dd MMM yyyy HH:mm:ss zzz"
/**
* Converts a date in the [HTTP_DATE] format to a Unix timestamp.
*
* @param date The date to convert.
* @return The timestamp of the date.
*/
fun httpDateToTimestamp(date: String) =
SimpleDateFormat(HTTP_DATE, Locale.US).parse(date).time
/**
* Joins each value of a given [field] of the array using [sep].
@ -52,7 +35,7 @@ fun JSONArray.joinField(field: Any, sep: String = ", "): String? {
*
* @param obj The object containing the manga info.
*/
fun SManga.fromJSON(obj: JSONObject) {
fun SManga.fromJSON(obj: JSONObject) = apply {
url = obj.getString("url")
title = obj.getString("title")
description = obj.getString("description")
@ -71,10 +54,10 @@ fun SManga.fromJSON(obj: JSONObject) {
*
* @param obj The object containing the chapter info.
*/
fun SChapter.fromJSON(obj: JSONObject) {
fun SChapter.fromJSON(obj: JSONObject) = apply {
url = obj.getString("url")
chapter_number = obj.optString("chapter", "0").toFloat()
date_upload = httpDateToTimestamp(obj.getString("date"))
date_upload = MangAdventure.httpDateToTimestamp(obj.getString("date"))
scanlator = obj.getJSONArray("groups")?.joinField("name", " & ")
name = buildString {
obj.optInt("volume").let { if (it != 0) append("Vol.$it ") }

View File

@ -0,0 +1,33 @@
package eu.kanade.tachiyomi.extension.all.mangadventure
import eu.kanade.tachiyomi.source.SourceFactory
/** [MangAdventure] source factory. */
class MangAdventureFactory : SourceFactory {
override fun createSources() = listOf(
ArcRelight()
)
}
/** Arc-Relight source. */
class ArcRelight : MangAdventure(
"Arc-Relight", "https://arc-relight.site", arrayOf(
"4-Koma",
"Chaos;Head",
"Collection",
"Comedy",
"Drama",
"Jubilee",
"Mystery",
"Psychological",
"Robotics;Notes",
"Romance",
"Sci-Fi",
"Seinen",
"Shounen",
"Steins;Gate",
"Supernatural",
"Tragedy"
)
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

View File

@ -1,66 +0,0 @@
package eu.kanade.tachiyomi.extension.en.arcrelight
import eu.kanade.tachiyomi.source.model.Filter
/** Array containing the possible statuses of a manga */
private val STATUSES = arrayOf("Any", "Completed", "Ongoing")
/** List containing the possible categories of a manga */
private val CATEGORIES = listOf(
Category("4-Koma"),
Category("Chaos;Head"),
Category("Collection"),
Category("Comedy"),
Category("Drama"),
Category("Jubilee"),
Category("Mystery"),
Category("Psychological"),
Category("Robotics;Notes"),
Category("Romance"),
Category("Sci-Fi"),
Category("Seinen"),
Category("Shounen"),
Category("Steins;Gate"),
Category("Supernatural"),
Category("Tragedy")
)
/**
* Filter representing the status of a manga.
*
* @constructor Creates a [Filter.Select] object with [STATUSES].
*/
class Status : Filter.Select<String>("Status", STATUSES) {
/** Returns the [state] as a string. */
fun string() = values[state].toLowerCase()
}
/**
* Filter representing a manga category.
*
* @property name The display name of the category.
* @constructor Creates a [Filter.TriState] object using [name].
*/
class Category(name: String) : Filter.TriState(name) {
/** Returns the [state] as a string, or null if [isIgnored]. */
fun optString() = when (state) {
STATE_INCLUDE -> name.toLowerCase()
STATE_EXCLUDE -> "-" + name.toLowerCase()
else -> null
}
}
/**
* Filter representing the [categories][Category] of a manga.
*
* @constructor Creates a [Filter.Group] object with [CATEGORIES].
*/
class CategoryList : Filter.Group<Category>("Categories", CATEGORIES)
/**
* Filter representing the name of an author or artist.
*
* @constructor Creates a [Filter.Text] object.
*/
class Person : Filter.Text("Author/Artist")