AralosBD: added new extension to add support for aralosbd.fr (#12581)
* AralosBD: added new extension to add support for aralosbd.fr * AralosBD: fixed pull request - Fixed icon format - Removed useless configuration file - Removed useless headers
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="eu.kanade.tachiyomi.extension" />
|
|
@ -0,0 +1,12 @@
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlinx-serialization'
|
||||||
|
|
||||||
|
ext {
|
||||||
|
extName = 'AralosBD'
|
||||||
|
pkgNameSuffix = 'fr.aralosbd'
|
||||||
|
extClass = '.AralosBD'
|
||||||
|
extVersionCode = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 43 KiB |
|
@ -0,0 +1,230 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.fr.aralosbd
|
||||||
|
|
||||||
|
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 kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import org.jsoup.parser.Parser
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AralosBDSearchManga(
|
||||||
|
val icon: String = "",
|
||||||
|
val title: String = "",
|
||||||
|
val id: String = "",
|
||||||
|
val read_count: String = "",
|
||||||
|
val chapter_count: String = "",
|
||||||
|
val is_favorite: Boolean = false,
|
||||||
|
val is_liked: Boolean = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AralosBDSearchResult(
|
||||||
|
val error: Int = 0,
|
||||||
|
val mangas: List<AralosBDSearchManga> = emptyList()
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AralosBDAlternativeTitle(
|
||||||
|
val title: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AralosBDAuthor(
|
||||||
|
val name: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AralosBDTranslator(
|
||||||
|
val name: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AralosBDTag(
|
||||||
|
val tag: String = "",
|
||||||
|
val color: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AralosBDManga(
|
||||||
|
val main_title: String = "",
|
||||||
|
val fulldescription: String? = "",
|
||||||
|
val description: String = "",
|
||||||
|
val year: String = "",
|
||||||
|
val id: Int = 0,
|
||||||
|
val alternative_titles: List<AralosBDAlternativeTitle>? = emptyList(),
|
||||||
|
val authors: List<AralosBDAuthor>? = emptyList(),
|
||||||
|
val translators: List<AralosBDTranslator>? = emptyList(),
|
||||||
|
val tags: List<AralosBDTag>? = emptyList(),
|
||||||
|
val banner: String = "",
|
||||||
|
val icon: String = "",
|
||||||
|
val error: Int = 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AralosBDChapter(
|
||||||
|
val chapter_number: String = "",
|
||||||
|
val chapter_user: String = "",
|
||||||
|
val chapter_title: String = "",
|
||||||
|
val chapter_translator: String? = "",
|
||||||
|
val chapter_view_count: String = "",
|
||||||
|
val chapter_like_count: String = "",
|
||||||
|
val chapter_date: String = "",
|
||||||
|
val chapter_id: String = "",
|
||||||
|
val chapter_read: Boolean = false,
|
||||||
|
val chapter_released: String = "0",
|
||||||
|
val chapter_release_time: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AralosBDPages(
|
||||||
|
val error: Int = 0,
|
||||||
|
val links: List<String> = emptyList()
|
||||||
|
)
|
||||||
|
|
||||||
|
class AralosBD : HttpSource() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.FRANCE)
|
||||||
|
|
||||||
|
val LINK_REGEX = "\\[([^]]+)\\]\\(([^)]+)\\)".toRegex()
|
||||||
|
val BOLD_REGEX = "\\*+\\s*([^\\*]*)\\s*\\*+".toRegex()
|
||||||
|
val ITALIC_REGEX = "_+\\s*([^_]*)\\s*_+".toRegex()
|
||||||
|
val ICON_REGEX = ":+[a-zA-Z]+:".toRegex()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cleanString(string: String): String {
|
||||||
|
return Parser.unescapeEntities(string, false)
|
||||||
|
.substringBefore("---")
|
||||||
|
.replace(LINK_REGEX, "$2")
|
||||||
|
.replace(BOLD_REGEX, "$1")
|
||||||
|
.replace(ITALIC_REGEX, "$1")
|
||||||
|
.replace(ICON_REGEX, "")
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val name = "AralosBD"
|
||||||
|
override val baseUrl = "https://aralosbd.fr"
|
||||||
|
override val lang = "fr"
|
||||||
|
override val supportsLatest = true
|
||||||
|
|
||||||
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
|
override fun popularMangaRequest(page: Int): Request {
|
||||||
|
// This is the search query used for the 'recommandations' in the front page
|
||||||
|
return GET("$baseUrl/manga/search?s=sort:allviews;limit:16;-id:3", headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popularMangaParse(response: Response): MangasPage {
|
||||||
|
val searchResult = json.decodeFromString<AralosBDSearchResult>(response.body!!.string())
|
||||||
|
|
||||||
|
return MangasPage(searchResult.mangas.map(::searchMangaToSManga), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException("Not used.")
|
||||||
|
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not used.")
|
||||||
|
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
// For a basic search, we call the appropriate endpoint
|
||||||
|
return GET("$baseUrl/manga/search?s=text:$query", headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response): MangasPage {
|
||||||
|
val searchResult = json.decodeFromString<AralosBDSearchResult>(response.body!!.string())
|
||||||
|
|
||||||
|
return MangasPage(searchResult.mangas.map(::searchMangaToSManga), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||||
|
// This is a needed call, so the Tachiyomi user behave like a regular user
|
||||||
|
// return GET(manga.url.replace("display?", "api?get=manga&"), headers)
|
||||||
|
return GET(manga.url, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mangaDetailsParse(response: Response): SManga {
|
||||||
|
val responseBody = client.newCall(GET(response.request.url.toString().replace("display?", "api?get=manga&"), headers)).execute().body
|
||||||
|
|
||||||
|
val manga = json.decodeFromString<AralosBDManga>(responseBody!!.string())
|
||||||
|
|
||||||
|
return SManga.create().apply {
|
||||||
|
url = "$baseUrl/manga/display?id=${manga.id}"
|
||||||
|
title = manga.main_title
|
||||||
|
artist = "" // manga.authors.joinToString(", ")
|
||||||
|
author = manga.authors?.joinToString(", ", transform = ::authorToString)
|
||||||
|
description = cleanString("${manga.description}\n\n" + (manga.fulldescription ?: ""))
|
||||||
|
status = 0 // This is not on the website
|
||||||
|
genre = manga.tags?.joinToString(", ", transform = ::tagToString)
|
||||||
|
thumbnail_url = "$baseUrl/${manga.icon}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chapterListRequest(manga: SManga): Request {
|
||||||
|
return GET(manga.url.replace("display?id", "api?get=chapters&manga"), headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
|
val searchResult = json.decodeFromString<List<AralosBDChapter>>(response.body!!.string())
|
||||||
|
|
||||||
|
val validSearchResults = mutableListOf<AralosBDChapter>()
|
||||||
|
searchResult.filterTo(validSearchResults) { it.chapter_released == "1" }
|
||||||
|
|
||||||
|
return validSearchResults.map(::chapterToSChapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pageListRequest(chapter: SChapter): Request {
|
||||||
|
return GET(chapter.url, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
|
val responseBody = client.newCall(GET(response.request.url.toString().replace("chapter?id", "api?get=pages&chapter"), headers)).execute().body
|
||||||
|
|
||||||
|
val pageResult = json.decodeFromString<AralosBDPages>(responseBody!!.string())
|
||||||
|
|
||||||
|
return pageResult.links.mapIndexed { index, link ->
|
||||||
|
Page(
|
||||||
|
index,
|
||||||
|
"$baseUrl/$link",
|
||||||
|
"$baseUrl/$link"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Not used.")
|
||||||
|
|
||||||
|
private fun authorToString(author: AralosBDAuthor) = author.name
|
||||||
|
private fun translatorToString(translator: AralosBDTranslator) = translator.name
|
||||||
|
private fun tagToString(tag: AralosBDTag) = tag.tag
|
||||||
|
|
||||||
|
private fun searchMangaToSManga(manga: AralosBDSearchManga): SManga {
|
||||||
|
return SManga.create().apply {
|
||||||
|
// No need to trim, it's already done by the server
|
||||||
|
title = manga.title
|
||||||
|
|
||||||
|
// Just need to append the base url to the relative link returned
|
||||||
|
thumbnail_url = "$baseUrl/${manga.icon}"
|
||||||
|
|
||||||
|
// The url of the manga is simply based on the manga ID for now
|
||||||
|
url = "$baseUrl/manga/display?id=${manga.id}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun chapterToSChapter(chapter: AralosBDChapter): SChapter {
|
||||||
|
return SChapter.create().apply {
|
||||||
|
url = "$baseUrl/manga/chapter?id=${chapter.chapter_id}"
|
||||||
|
name = chapter.chapter_number + " - " + chapter.chapter_title
|
||||||
|
date_upload = try { DATE_FORMAT.parse(chapter.chapter_release_time)!!.time } catch (e: Exception) { System.currentTimeMillis() }
|
||||||
|
// chapter_number = // This is a string and it can be 2.5.1 for example
|
||||||
|
scanlator = chapter.chapter_translator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|