diff --git a/src/en/arcrelight/build.gradle b/src/all/mangadventure/build.gradle similarity index 50% rename from src/en/arcrelight/build.gradle rename to src/all/mangadventure/build.gradle index 53cb67c69..532050417 100644 --- a/src/en/arcrelight/build.gradle +++ b/src/all/mangadventure/build.gradle @@ -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' } diff --git a/src/all/mangadventure/res/mipmap-hdpi/ic_launcher.png b/src/all/mangadventure/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..2c3111603 Binary files /dev/null and b/src/all/mangadventure/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/all/mangadventure/res/mipmap-mdpi/ic_launcher.png b/src/all/mangadventure/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..3817c92e5 Binary files /dev/null and b/src/all/mangadventure/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/all/mangadventure/res/mipmap-xhdpi/ic_launcher.png b/src/all/mangadventure/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..ac0dbc4df Binary files /dev/null and b/src/all/mangadventure/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/all/mangadventure/res/mipmap-xxhdpi/ic_launcher.png b/src/all/mangadventure/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..9474a7d35 Binary files /dev/null and b/src/all/mangadventure/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/all/mangadventure/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/mangadventure/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..09f1bbf62 Binary files /dev/null and b/src/all/mangadventure/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/all/mangadventure/res/web_hi_res_512.png b/src/all/mangadventure/res/web_hi_res_512.png new file mode 100644 index 000000000..053dd0157 Binary files /dev/null and b/src/all/mangadventure/res/web_hi_res_512.png differ diff --git a/src/en/arcrelight/src/eu/kanade/tachiyomi/extension/en/arcrelight/ArcRelight.kt b/src/all/mangadventure/src/eu/kanade/tachiyomi/extension/all/mangadventure/MangAdventure.kt similarity index 53% rename from src/en/arcrelight/src/eu/kanade/tachiyomi/extension/en/arcrelight/ArcRelight.kt rename to src/all/mangadventure/src/eu/kanade/tachiyomi/extension/all/mangadventure/MangAdventure.kt index 21d225d1b..9710d82a4 100644 --- a/src/en/arcrelight/src/eu/kanade/tachiyomi/extension/en/arcrelight/ArcRelight.kt +++ b/src/all/mangadventure/src/eu/kanade/tachiyomi/extension/all/mangadventure/MangAdventure.kt @@ -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 = 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() 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 { 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("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( + "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") } diff --git a/src/en/arcrelight/src/eu/kanade/tachiyomi/extension/en/arcrelight/ARUtils.kt b/src/all/mangadventure/src/eu/kanade/tachiyomi/extension/all/mangadventure/MangAdventureExtensions.kt similarity index 76% rename from src/en/arcrelight/src/eu/kanade/tachiyomi/extension/en/arcrelight/ARUtils.kt rename to src/all/mangadventure/src/eu/kanade/tachiyomi/extension/all/mangadventure/MangAdventureExtensions.kt index a68f2f769..ebf1fb40c 100644 --- a/src/en/arcrelight/src/eu/kanade/tachiyomi/extension/en/arcrelight/ARUtils.kt +++ b/src/all/mangadventure/src/eu/kanade/tachiyomi/extension/all/mangadventure/MangAdventureExtensions.kt @@ -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 ") } diff --git a/src/all/mangadventure/src/eu/kanade/tachiyomi/extension/all/mangadventure/MangAdventureFactory.kt b/src/all/mangadventure/src/eu/kanade/tachiyomi/extension/all/mangadventure/MangAdventureFactory.kt new file mode 100644 index 000000000..32bfd0544 --- /dev/null +++ b/src/all/mangadventure/src/eu/kanade/tachiyomi/extension/all/mangadventure/MangAdventureFactory.kt @@ -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" + ) +) + diff --git a/src/en/arcrelight/res/mipmap-hdpi/ic_launcher.png b/src/en/arcrelight/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index c754bc9ec..000000000 Binary files a/src/en/arcrelight/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/arcrelight/res/mipmap-mdpi/ic_launcher.png b/src/en/arcrelight/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 356dff73f..000000000 Binary files a/src/en/arcrelight/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/arcrelight/res/mipmap-xhdpi/ic_launcher.png b/src/en/arcrelight/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 1a29ebb8f..000000000 Binary files a/src/en/arcrelight/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/arcrelight/res/mipmap-xxhdpi/ic_launcher.png b/src/en/arcrelight/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d38ac11d3..000000000 Binary files a/src/en/arcrelight/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/arcrelight/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/arcrelight/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index e8ae26054..000000000 Binary files a/src/en/arcrelight/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/arcrelight/res/web_hi_res_512.png b/src/en/arcrelight/res/web_hi_res_512.png deleted file mode 100644 index f08eadcf3..000000000 Binary files a/src/en/arcrelight/res/web_hi_res_512.png and /dev/null differ diff --git a/src/en/arcrelight/src/eu/kanade/tachiyomi/extension/en/arcrelight/ARFilters.kt b/src/en/arcrelight/src/eu/kanade/tachiyomi/extension/en/arcrelight/ARFilters.kt deleted file mode 100644 index 26da6bba7..000000000 --- a/src/en/arcrelight/src/eu/kanade/tachiyomi/extension/en/arcrelight/ARFilters.kt +++ /dev/null @@ -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("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("Categories", CATEGORIES) - -/** - * Filter representing the name of an author or artist. - * - * @constructor Creates a [Filter.Text] object. - */ -class Person : Filter.Text("Author/Artist") -