Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/pt/onereader/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import io.github.keiyoushi.gradle.api.ContentWarning

plugins {
alias(kei.plugins.extension)
}

keiyoushi {
name = "OneReader"
versionCode = 1
contentWarning = ContentWarning.MIXED
libVersion = "1.6"

source {
lang = "pt-BR"
baseUrl = "https://onereader.net"
}

deeplink {
path("/manga-details")
path("/leitor")
}
}
Binary file added src/pt/onereader/res/mipmap-hdpi/ic_launcher.png

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use icon generator linked in contributing guide, and follow the style convention. i.e square icon with rounded corners, with solid background

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/pt/onereader/res/mipmap-mdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/pt/onereader/res/mipmap-xhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
151 changes: 151 additions & 0 deletions src/pt/onereader/src/eu/kanade/tachiyomi/extension/pt/onereader/Dto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package eu.kanade.tachiyomi.extension.pt.onereader

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 keiyoushi.utils.jsonInstance
import keiyoushi.utils.tryParse
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.JsonTransformingSerializer
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.put
import okhttp3.HttpUrl
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.TimeZone

@Serializable
internal class SearchDto(
private val data: List<MangaDto>,
private val page: Int,
private val totalPages: Int,
) {
fun toMangasPage() = MangasPage(
data.map(MangaDto::toSManga),
page < totalPages,
)
}

@Serializable
internal class MangaDto(
@SerialName("manga_key")
private val mangaKey: String,
@SerialName("display_name")
private val displayName: String,
private val name: String,
@SerialName("japanese_name")
private val japaneseName: String?,
private val author: String? = null,
private val artist: String? = null,
private val synopsis: String?,
private val status: String,
private val type: String,
@SerialName("origin_country")
private val originCountry: String,
private val year: Int,
@Serializable(with = TagsSerializer::class)
private val tags: List<String>,
@SerialName("cover_image")
private val coverImage: String,
@SerialName("publisher_name")
private val publisherName: String? = null,
) {
fun toSManga(details: Boolean = false): SManga = SManga.create().apply {
url = mangaKey
title = displayName
thumbnail_url = coverImage
author = this@MangaDto.author?.takeIf(String::isNotBlank)
artist = this@MangaDto.artist?.takeIf(String::isNotBlank)
genre = tags.takeIf(List<String>::isNotEmpty)?.joinToString()
status = this@MangaDto.status.toStatus()
description = buildDescription()
initialized = details
}

private fun buildDescription(): String? {
val metadata = buildList {
name.takeIf { it.isNotBlank() && !it.equals(displayName, ignoreCase = true) }
?.let { add("Título original: $it") }
japaneseName?.takeIf {
it.isNotBlank() &&
!it.equals(displayName, ignoreCase = true) &&
!it.equals(name, ignoreCase = true)
}?.let { add("Título nativo: $it") }
type.takeIf(String::isNotBlank)?.let { add("Tipo: $it") }
originCountry.takeIf(String::isNotBlank)?.let { add("País: $it") }
add("Ano: $year")
publisherName?.takeIf(String::isNotBlank)?.let { add("Editora: $it") }
}

return buildString {
synopsis?.takeIf(String::isNotBlank)?.let(::append)
if (isNotEmpty() && metadata.isNotEmpty()) append("\n\n")
append(metadata.joinToString("\n"))
}.ifBlank { null }
}
}

@Serializable
internal class ChapterDto(
@SerialName("manga_key")
private val mangaKey: String,
@SerialName("posted_date")
private val postedDate: String,
) {
fun toSChapter(number: String): SChapter = SChapter.create().apply {
url = "$mangaKey/$number"
memo = buildJsonObject {
put("id", mangaKey)
put("number", number)
}
name = "Capítulo $number"
chapter_number = number.toFloatOrNull() ?: -1F
date_upload = chapterDateFormat.tryParse(postedDate)
}
}

@Serializable
internal class PagesDto(
private val pages: List<String>,
) {
fun toPages(apiBaseUrl: HttpUrl): List<Page> = pages.mapIndexed { index, path ->
Page(
index = index,
imageUrl = requireNotNull(apiBaseUrl.resolve(path)).toString(),
)
}
}

private object TagsSerializer : JsonTransformingSerializer<List<String>>(
ListSerializer(String.serializer()),
) {
override fun transformDeserialize(element: JsonElement): JsonElement = when (element) {
JsonNull -> JsonArray(emptyList())
is JsonPrimitive ->
element.contentOrNull
?.let(jsonInstance::parseToJsonElement)
?: element
else -> element
}
}

private fun String.toStatus(): Int = when (trim().lowercase(Locale.ROOT)) {
"em lançamento", "lançando" -> SManga.ONGOING
"completo" -> SManga.COMPLETED
"hiatus", "hiato" -> SManga.ON_HIATUS
"cancelado" -> SManga.CANCELLED
else -> SManga.UNKNOWN
}

private val chapterDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ROOT).apply {
timeZone = TimeZone.getTimeZone("UTC")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package eu.kanade.tachiyomi.extension.pt.onereader

import eu.kanade.tachiyomi.source.model.Filter
import kotlinx.serialization.Serializable

@Serializable
internal class FilterData(
val tags: List<String>,
val types: List<String>,
)

internal open class SelectFilter(
name: String,
private val options: List<Pair<String, String>>,
) : Filter.Select<String>(name, options.map { it.first }.toTypedArray()) {
fun selected(): String = options[state].second
}

internal class OrderFilter :
SelectFilter(
"Ordenar por",
listOf(
"A-Z" to "az",
"Atualizados recentemente" to "recent",
),
)

internal class StatusFilter :
SelectFilter(
"Status",
listOf(
"Todos" to "",
"Em lançamento" to "Em Lançamento",
"Completo" to "Completo",
"Hiato" to "Hiatus",
"Cancelado" to "Cancelado",
),
)

internal class TypeFilter(types: List<String>) :
SelectFilter(
"Tipo",
listOf("Todos" to "") + types.map { it to it },
)

internal class TagFilter(tags: List<String>) :
SelectFilter(
"Tag",
listOf("Todas" to "") + tags.map { it to it },
)
Loading