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
literal 0
HcmV?d00001
diff --git a/src/pt/blackscans/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/blackscans/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000000000000000000000000000000000..8e355c5c3fe4ce29b74008b4cce809b58cb65660
GIT binary patch
literal 9450
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
J#cPX@{{db!T8RJv
literal 0
HcmV?d00001
diff --git a/src/pt/blackscans/src/eu/kanade/tachiyomi/extension/pt/blackscans/BlackScans.kt b/src/pt/blackscans/src/eu/kanade/tachiyomi/extension/pt/blackscans/BlackScans.kt
new file mode 100644
index 000000000..d908b87af
--- /dev/null
+++ b/src/pt/blackscans/src/eu/kanade/tachiyomi/extension/pt/blackscans/BlackScans.kt
@@ -0,0 +1,179 @@
+package eu.kanade.tachiyomi.extension.pt.blackscans
+
+import android.annotation.SuppressLint
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.network.POST
+import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
+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.SerialName
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.decodeFromStream
+import okhttp3.HttpUrl.Companion.toHttpUrl
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.Request
+import okhttp3.RequestBody
+import okhttp3.RequestBody.Companion.toRequestBody
+import okhttp3.Response
+import okio.Buffer
+import rx.Observable
+import uy.kohesive.injekt.injectLazy
+import java.text.SimpleDateFormat
+
+class BlackScans : HttpSource() {
+
+ override val name = "Black Scans"
+
+ override val baseUrl = "https://blackscans.site"
+
+ override val lang = "pt-BR"
+
+ override val supportsLatest = true
+
+ override val client = network.cloudflareClient.newBuilder()
+ .rateLimitHost(API_URL.toHttpUrl(), 2)
+ .build()
+
+ private val json: Json by injectLazy()
+
+ // ============================== Popular ==============================
+
+ override fun popularMangaRequest(page: Int) = GET("$API_URL/api/series/", headers)
+
+ override fun popularMangaParse(response: Response): MangasPage {
+ val mangas = response.parseAs>().map { manga ->
+ SManga.create().apply {
+ title = manga.title
+ thumbnail_url = "$API_URL/media/${manga.cover}"
+ url = "/series/${manga.code}"
+ }
+ }
+ return MangasPage(mangas, false)
+ }
+
+ // ============================== Latest ==============================
+
+ override fun latestUpdatesRequest(page: Int) = GET("$API_URL/api/series/updates/", headers)
+
+ override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
+
+ // ============================== Search ==============================
+
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = popularMangaRequest(page)
+
+ override fun searchMangaParse(response: Response) = popularMangaParse(response)
+
+ override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable {
+ if (query.startsWith(PREFIX_SEARCH)) {
+ val mangaCode = query.substringAfter(PREFIX_SEARCH)
+ return fetchMangaDetails(SManga.create().apply { url = "/series/$mangaCode" })
+ .map { manga -> MangasPage(listOf(manga), false) }
+ }
+
+ return super.fetchSearchManga(page, query, filters).map { mangasPage ->
+ val mangas = mangasPage.mangas.filter { manga -> manga.title.contains(query, true) }
+ mangasPage.copy(mangas)
+ }
+ }
+
+ // ============================== Details =============================
+
+ override fun getMangaUrl(manga: SManga) = "$baseUrl${manga.url}"
+
+ override fun mangaDetailsRequest(manga: SManga) =
+ POST("$API_URL/api/serie/", headers, manga.createPostPayload())
+
+ override fun mangaDetailsParse(response: Response): SManga {
+ return response.parseAs().let { dto ->
+ SManga.create().apply {
+ title = dto.title
+ description = dto.synopsis
+ thumbnail_url = "$API_URL/media/${dto.cover}"
+ author = dto.author
+ artist = dto.artist
+ genre = dto.genres.joinToString()
+ url = "/series/${dto.code}"
+ status = dto.status.toMangaStatus()
+ }
+ }
+ }
+
+ private fun String.toMangaStatus(): Int {
+ return when (this.lowercase()) {
+ "ongoing" -> SManga.ONGOING
+ "completed" -> SManga.COMPLETED
+ else -> SManga.UNKNOWN
+ }
+ }
+
+ // ============================== Chapters ============================
+
+ override fun getChapterUrl(chapter: SChapter) = "$baseUrl${chapter.url}"
+
+ override fun chapterListRequest(manga: SManga): Request {
+ val payload = manga.createPostPayload("series_code")
+ return POST("$API_URL/api/series/chapters/", headers, payload)
+ }
+
+ override fun chapterListParse(response: Response): List {
+ val series = response.request.body!!.parseAs()
+
+ return response.parseAs().chapters.map { chapter ->
+ SChapter.create().apply {
+ name = chapter.name
+ date_upload = chapter.uploadAt.toDate()
+ url = "/series/${series.code}/${chapter.code}"
+ }
+ }
+ }
+
+ // ============================== Pages ===============================
+
+ override fun imageUrlParse(response: Response) = ""
+
+ override fun pageListRequest(chapter: SChapter): Request {
+ val chapterCode = chapter.url.substringAfterLast("/")
+ val payload = """{"chapter_code":"$chapterCode"}"""
+ .toRequestBody("application/json".toMediaType())
+ return POST("$API_URL/api/chapter/info/", headers, payload)
+ }
+
+ override fun pageListParse(response: Response): List {
+ return response.parseAs().images.mapIndexed { index, imageUrl ->
+ Page(index, imageUrl = "$API_URL//media/$imageUrl")
+ }
+ }
+
+ // ============================== Utils ===============================
+
+ @Serializable
+ private class SeriesDto(@SerialName("series_code") val code: String)
+
+ private fun SManga.createPostPayload(field: String = "code"): RequestBody {
+ val mangaCode = url.substringAfterLast("/")
+ return """{"$field": "$mangaCode"}""".toRequestBody("application/json".toMediaType())
+ }
+
+ private inline fun Response.parseAs(): T = use {
+ json.decodeFromStream(it.body.byteStream())
+ }
+ private inline fun RequestBody.parseAs(): T =
+ json.decodeFromString(Buffer().also { writeTo(it) }.readUtf8())
+
+ private fun String.toDate() =
+ try { dateFormat.parse(this)!!.time } catch (_: Exception) { 0 }
+
+ companion object {
+ const val API_URL = "https://api.blackscans.site"
+ const val PREFIX_SEARCH = "id:"
+
+ @SuppressLint("SimpleDateFormat")
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'")
+ }
+}
diff --git a/src/pt/blackscans/src/eu/kanade/tachiyomi/extension/pt/blackscans/BlackScansUrlActivity.kt b/src/pt/blackscans/src/eu/kanade/tachiyomi/extension/pt/blackscans/BlackScansUrlActivity.kt
new file mode 100644
index 000000000..3f2b02e16
--- /dev/null
+++ b/src/pt/blackscans/src/eu/kanade/tachiyomi/extension/pt/blackscans/BlackScansUrlActivity.kt
@@ -0,0 +1,37 @@
+package eu.kanade.tachiyomi.extension.pt.blackscans
+
+import android.app.Activity
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import kotlin.system.exitProcess
+
+class BlackScansUrlActivity : Activity() {
+
+ private val tag = javaClass.simpleName
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val pathSegments = intent?.data?.pathSegments
+ if (pathSegments != null && pathSegments.size > 1) {
+ val item = pathSegments[1]
+ val mainIntent = Intent().apply {
+ action = "eu.kanade.tachiyomi.SEARCH"
+ putExtra("query", "${BlackScans.PREFIX_SEARCH}$item")
+ putExtra("filter", packageName)
+ }
+
+ try {
+ startActivity(mainIntent)
+ } catch (e: ActivityNotFoundException) {
+ Log.e(tag, e.toString())
+ }
+ } else {
+ Log.e(tag, "could not parse uri from intent $intent")
+ }
+
+ finish()
+ exitProcess(0)
+ }
+}
diff --git a/src/pt/blackscans/src/eu/kanade/tachiyomi/extension/pt/blackscans/MangaDetailsDto.kt b/src/pt/blackscans/src/eu/kanade/tachiyomi/extension/pt/blackscans/MangaDetailsDto.kt
new file mode 100644
index 000000000..14b6cb167
--- /dev/null
+++ b/src/pt/blackscans/src/eu/kanade/tachiyomi/extension/pt/blackscans/MangaDetailsDto.kt
@@ -0,0 +1,44 @@
+package eu.kanade.tachiyomi.extension.pt.blackscans
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.JsonNames
+
+@Serializable
+class MangaDetailsDto(
+ val title: String,
+ val artist: String,
+ val author: String,
+ val code: String,
+ val genres: List,
+ @SerialName("path_cover")
+ val cover: String,
+ val status: String,
+ val synopsis: String,
+)
+
+@Serializable
+class MangaDto(
+ val code: String,
+ val title: String,
+ @JsonNames("path_cover")
+ val cover: String,
+)
+
+@Serializable
+class ChapterList(
+ val chapters: List,
+)
+
+@Serializable
+data class Chapter(
+ val code: String,
+ val name: String,
+ @SerialName("upload_date")
+ val uploadAt: String,
+)
+
+@Serializable
+class PagesDto(
+ val images: List,
+)