diff --git a/lib-multisrc/mangathemesia/assets/i18n/messages_en.properties b/lib-multisrc/mangathemesia/assets/i18n/messages_en.properties new file mode 100644 index 000000000..91e24269f --- /dev/null +++ b/lib-multisrc/mangathemesia/assets/i18n/messages_en.properties @@ -0,0 +1,30 @@ +alt_names_heading=Alternative Names: +author_filter_title=Author +year_filter_title=Year +status_filter_title=Status +status_filter_option_all=All +status_filter_option_ongoing=Ongoing +status_filter_option_completed=Completed +status_filter_option_hiatus=Hiatus +status_filter_option_dropped=Dropped +type_filter_title=Type +type_filter_option_all=All +type_filter_option_manga=Manga +type_filter_option_manhwa=Manhwa +type_filter_option_manhua=Manhua +type_filter_option_comic=Comic +order_by_filter_title=Sort By +order_by_filter_default=Default +order_by_filter_az=A-Z +order_by_filter_za=Z-A +order_by_filter_latest_update=Latest Update +order_by_filter_latest_added=Latest Added +order_by_filter_popular=Popular +project_filter_title=Filter Project +project_filter_all_manga=Show all manga +project_filter_only_project=Show only project manga +genre_filter_title=Genre +genre_missing_warning=Press 'Reset' to attempt to show the genres +genre_exclusion_warning=Genre exclusion is not available for all sources +project_filter_warning=NOTE: Can't be used with other filter! +project_filter_name=%s Project List page diff --git a/lib-multisrc/mangathemesia/assets/i18n/messages_es.properties b/lib-multisrc/mangathemesia/assets/i18n/messages_es.properties new file mode 100644 index 000000000..883c132c0 --- /dev/null +++ b/lib-multisrc/mangathemesia/assets/i18n/messages_es.properties @@ -0,0 +1,23 @@ +alt_names_heading=Nombres alternativos: +author_filter_title=Autor +year_filter_title=Año +status_filter_title=Estado +status_filter_option_all=Todos +status_filter_option_ongoing=En curso +status_filter_option_completed=Completado +status_filter_option_hiatus=En pausa +status_filter_option_dropped=Abandonado +type_filter_title=Tipo +type_filter_option_all=Todos +order_by_filter_title=Ordenar por +order_by_filter_default=Por defecto +order_by_filter_latest_update=Última actualización +order_by_filter_latest_added=Último añadido +project_filter_title=Filtrar proyectos +project_filter_all_manga=Mostrar todos los mangas +project_filter_only_project=Mostrar solo los proyectos +genre_filter_title=Género +genre_missing_warning=Presione 'Restablecer' para intentar cargar los géneros +genre_exclusion_warning=La exclusión de géneros puede no funcionar correctamente +project_filter_warning=NOTA: ¡No se puede usar con otros filtros! +project_filter_name=%s Página de proyectos diff --git a/lib-multisrc/mangathemesia/build.gradle.kts b/lib-multisrc/mangathemesia/build.gradle.kts index 7863375ef..73f9e980b 100644 --- a/lib-multisrc/mangathemesia/build.gradle.kts +++ b/lib-multisrc/mangathemesia/build.gradle.kts @@ -2,8 +2,8 @@ plugins { id("lib-multisrc") } -baseVersionCode = 28 +baseVersionCode = 29 dependencies { - api(project(":lib:randomua")) + api(project(":lib:i18n")) } diff --git a/lib-multisrc/mangathemesia/src/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesia.kt b/lib-multisrc/mangathemesia/src/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesia.kt index 63a883321..2100a7941 100644 --- a/lib-multisrc/mangathemesia/src/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesia.kt +++ b/lib-multisrc/mangathemesia/src/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesia.kt @@ -1,15 +1,8 @@ package eu.kanade.tachiyomi.multisrc.mangathemesia -import android.app.Application -import android.content.SharedPreferences -import androidx.preference.PreferenceScreen -import eu.kanade.tachiyomi.lib.randomua.addRandomUAPreferenceToScreen -import eu.kanade.tachiyomi.lib.randomua.getPrefCustomUA -import eu.kanade.tachiyomi.lib.randomua.getPrefUAType -import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent +import eu.kanade.tachiyomi.lib.i18n.Intl import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST -import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage @@ -21,64 +14,56 @@ import eu.kanade.tachiyomi.util.asJsoup import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonPrimitive +import okhttp3.Call +import okhttp3.Callback import okhttp3.FormBody import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element import org.jsoup.select.Elements import rx.Observable -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy -import java.lang.IllegalArgumentException +import java.io.IOException import java.text.SimpleDateFormat import java.util.Locale -import java.util.concurrent.TimeUnit // Formerly WPMangaStream & WPMangaReader -> MangaThemesia abstract class MangaThemesia( override val name: String, override val baseUrl: String, - override val lang: String, + final override val lang: String, val mangaUrlDirectory: String = "/manga", val dateFormat: SimpleDateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.US), -) : ParsedHttpSource(), ConfigurableSource { - - private val preferences: SharedPreferences by lazy { - Injekt.get().getSharedPreferences("source_$id", 0x0000) - } +) : ParsedHttpSource() { protected open val json: Json by injectLazy() override val supportsLatest = true - override val client: OkHttpClient by lazy { - network.cloudflareClient.newBuilder() - .setRandomUserAgent( - preferences.getPrefUAType(), - preferences.getPrefCustomUA(), - ) - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .build() - } + override val client = network.cloudflareClient override fun headersBuilder() = super.headersBuilder() .set("Referer", "$baseUrl/") + protected val intl = Intl( + language = lang, + baseLanguage = "en", + availableLanguages = setOf("en", "es"), + classLoader = javaClass.classLoader!!, + ) + open val projectPageString = "/project" // Popular (Search with popular order and nothing else) - override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", FilterList(OrderByFilter("popular"))) + override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", popularFilter) override fun popularMangaParse(response: Response) = searchMangaParse(response) // Latest (Search with update order and nothing else) - override fun latestUpdatesRequest(page: Int) = searchMangaRequest(page, "", FilterList(OrderByFilter("update"))) + override fun latestUpdatesRequest(page: Int) = searchMangaRequest(page, "", latestFilter) override fun latestUpdatesParse(response: Response) = searchMangaParse(response) // Search @@ -166,22 +151,96 @@ abstract class MangaThemesia( override fun searchMangaNextPageSelector() = "div.pagination .next, div.hpage .r" // Manga details + private fun selector(selector: String, contains: List): String { + return contains.joinToString(", ") { selector.replace("%s", it) } + } + open val seriesDetailsSelector = "div.bigcontent, div.animefull, div.main-info, div.postbody" - open val seriesTitleSelector = "h1.entry-title" - open val seriesArtistSelector = ".infotable tr:contains(artist) td:last-child, .tsinfo .imptdt:contains(artist) i, .fmed b:contains(artist)+span, span:contains(artist)" - open val seriesAuthorSelector = ".infotable tr:contains(author) td:last-child, .tsinfo .imptdt:contains(author) i, .fmed b:contains(author)+span, span:contains(author)" + + open val seriesTitleSelector = "h1.entry-title, .ts-breadcrumb li:last-child span" + + open val seriesArtistSelector = selector( + ".infotable tr:contains(%s) td:last-child, .tsinfo .imptdt:contains(%s) i, .fmed b:contains(%s)+span, span:contains(%s)", + listOf( + "artist", + "Artiste", + "Artista", + "الرسام", + "الناشر", + "İllüstratör", + "Çizer", + ), + ) + + open val seriesAuthorSelector = selector( + ".infotable tr:contains(%s) td:last-child, .tsinfo .imptdt:contains(%s) i, .fmed b:contains(%s)+span, span:contains(%s)", + listOf( + "Author", + "Auteur", + "autor", + "المؤلف", + "Mangaka", + "seniman", + "Pengarang", + "Yazar", + ), + ) + open val seriesDescriptionSelector = ".desc, .entry-content[itemprop=description]" - open val seriesAltNameSelector = ".alternative, .wd-full:contains(alt) span, .alter, .seriestualt" - open val seriesGenreSelector = "div.gnr a, .mgen a, .seriestugenre a, span:contains(genre)" - open val seriesTypeSelector = ".infotable tr:contains(type) td:last-child, .tsinfo .imptdt:contains(type) i, .tsinfo .imptdt:contains(type) a, .fmed b:contains(type)+span, span:contains(type) a, a[href*=type\\=]" - open val seriesStatusSelector = ".infotable tr:contains(status) td:last-child, .tsinfo .imptdt:contains(status) i, .fmed b:contains(status)+span span:contains(status)" + + open val seriesAltNameSelector = ".alternative, .wd-full:contains(alt) span, .alter, .seriestualt, " + + selector( + ".infotable tr:contains(%s) td:last-child", + listOf( + "Alternative", + "Alternatif", + "الأسماء الثانوية", + ), + ) + + open val seriesGenreSelector = "div.gnr a, .mgen a, .seriestugenre a, " + + selector( + "span:contains(%s)", + listOf( + "genre", + "التصنيف", + ), + ) + + open val seriesTypeSelector = selector( + ".infotable tr:contains(%s) td:last-child, .tsinfo .imptdt:contains(%s) i, .tsinfo .imptdt:contains(%s) a, .fmed b:contains(%s)+span, span:contains(%s) a", + listOf( + "type", + "ประเภท", + "النوع", + "tipe", + "Türü", + ), + ) + ", a[href*=type\\=]" + + open val seriesStatusSelector = selector( + ".infotable tr:contains(%s) td:last-child, .tsinfo .imptdt:contains(%s) i, .fmed b:contains(%s)+span span:contains(%s)", + listOf( + "status", + "Statut", + "Durum", + "連載状況", + "Estado", + "الحالة", + "حالة العمل", + "สถานะ", + "stato", + "Statüsü", + ), + ) + open val seriesThumbnailSelector = ".infomanga > div[itemprop=image] img, .thumb img" - open val altNamePrefix = "Alternative Name: " + open val altNamePrefix = "${intl["alt_names_heading"]} " override fun mangaDetailsParse(document: Document) = SManga.create().apply { document.selectFirst(seriesDetailsSelector)?.let { seriesDetails -> - title = seriesDetails.selectFirst(seriesTitleSelector)?.text().orEmpty() + title = seriesDetails.selectFirst(seriesTitleSelector)!!.text() artist = seriesDetails.selectFirst(seriesArtistSelector)?.ownText().removeEmptyPlaceholder() author = seriesDetails.selectFirst(seriesAuthorSelector)?.ownText().removeEmptyPlaceholder() description = seriesDetails.select(seriesDescriptionSelector).joinToString("\n") { it.text() }.trim() @@ -210,16 +269,32 @@ abstract class MangaThemesia( } protected fun String?.removeEmptyPlaceholder(): String? { - return if (this.isNullOrBlank() || this == "-" || this == "N/A") null else this + return if (this.isNullOrBlank() || this == "-" || this == "N/A" || this == "n/a") null else this } - open fun String?.parseStatus(): Int = when { - this == null -> SManga.UNKNOWN - listOf("ongoing", "publishing").any { this.contains(it, ignoreCase = true) } -> SManga.ONGOING - this.contains("hiatus", ignoreCase = true) -> SManga.ON_HIATUS - this.contains("completed", ignoreCase = true) -> SManga.COMPLETED - listOf("dropped", "cancelled").any { this.contains(it, ignoreCase = true) } -> SManga.CANCELLED - else -> SManga.UNKNOWN + open fun String?.parseStatus(): Int { + if (this == null) return SManga.UNKNOWN + + return when (this.lowercase().trim()) { + "مستمرة", "en curso", "ongoing", "on going", "ativo", "en cours", + "en cours de publication", "đang tiến hành", "em lançamento", "онгоінг", "publishing", + "devam ediyor", "em andamento", "in corso", "güncel", "berjalan", "продолжается", "updating", "lançando", "in arrivo", "emision", + "en emision", "مستمر", "curso", "en marcha", "publicandose", "publicando", "连载中", "devam etmekte", "連載中", + -> SManga.ONGOING + + "completed", "completo", "complété", "fini", "achevé", "terminé", "tamamlandı", "đã hoàn thành", "hoàn thành", + "مكتملة", "завершено", "finished", "finalizado", "completata", "one-shot", "bitti", "tamat", "completado", "concluído", "完結", + "concluido", "已完结", "bitmiş", + -> SManga.COMPLETED + + "canceled", "cancelled", "cancelado", "cancellato", "cancelados", "dropped", "discontinued", "abandonné", + -> SManga.CANCELLED + + "hiatus", "on hold", "pausado", "en espera", "en pause", "en attente", + -> SManga.ON_HIATUS + + else -> SManga.UNKNOWN + } } // Chapter list @@ -227,6 +302,9 @@ abstract class MangaThemesia( override fun chapterListParse(response: Response): List { val document = response.asJsoup() + + countViews(document) + val chapters = document.select(chapterListSelector()).map { chapterFromElement(it) } // Add timestamp to latest chapter, taken from "Updated On". @@ -238,8 +316,6 @@ abstract class MangaThemesia( if (date.isNotEmpty()) chapters.first().date_upload = parseUpdatedOnDate(date) } - countViews(document) - return chapters } @@ -267,13 +343,13 @@ abstract class MangaThemesia( open val pageSelector = "div#readerarea img" override fun pageListParse(document: Document): List { + countViews(document) + val chapterUrl = document.location() val htmlPages = document.select(pageSelector) .filterNot { it.imgAttr().isEmpty() } .mapIndexed { i, img -> Page(i, chapterUrl, img.imgAttr()) } - countViews(document) - // Some sites also loads pages via javascript if (htmlPages.isNotEmpty()) { return htmlPages } @@ -320,8 +396,6 @@ abstract class MangaThemesia( .build() val newHeaders = headersBuilder() - .set("Content-Length", formBody.contentLength().toString()) - .set("Content-Type", formBody.contentType().toString()) .set("Referer", document.location()) .build() @@ -339,17 +413,22 @@ abstract class MangaThemesia( } val request = countViewsRequest(document) ?: return - runCatching { client.newCall(request).execute().close() } + val callback = object : Callback { + override fun onResponse(call: Call, response: Response) = response.close() + override fun onFailure(call: Call, e: IOException) = Unit + } + + client.newCall(request).enqueue(callback) } // Filters - protected class AuthorFilter : Filter.Text("Author") + protected class AuthorFilter(name: String) : Filter.Text(name) - protected class YearFilter : Filter.Text("Year") + protected class YearFilter(name: String) : Filter.Text(name) open class SelectFilter( displayName: String, - val vals: Array>, + private val vals: Array>, defaultValue: String? = null, ) : Filter.Select( displayName, @@ -359,63 +438,91 @@ abstract class MangaThemesia( fun selectedValue() = vals[state].second } - protected class StatusFilter : SelectFilter( - "Status", - arrayOf( - Pair("All", ""), - Pair("Ongoing", "ongoing"), - Pair("Completed", "completed"), - Pair("Hiatus", "hiatus"), - Pair("Dropped", "dropped"), - ), + protected class StatusFilter( + name: String, + options: Array>, + ) : SelectFilter( + name, + options, ) - protected class TypeFilter : SelectFilter( - "Type", - arrayOf( - Pair("All", ""), - Pair("Manga", "Manga"), - Pair("Manhwa", "Manhwa"), - Pair("Manhua", "Manhua"), - Pair("Comic", "Comic"), - ), + protected open val statusOptions = arrayOf( + Pair(intl["status_filter_option_all"], ""), + Pair(intl["status_filter_option_ongoing"], "ongoing"), + Pair(intl["status_filter_option_completed"], "completed"), + Pair(intl["status_filter_option_hiatus"], "hiatus"), + Pair(intl["status_filter_option_dropped"], "dropped"), ) - protected class OrderByFilter(defaultOrder: String? = null) : SelectFilter( - "Sort By", - arrayOf( - Pair("Default", ""), - Pair("A-Z", "title"), - Pair("Z-A", "titlereverse"), - Pair("Latest Update", "update"), - Pair("Latest Added", "latest"), - Pair("Popular", "popular"), - ), + protected class TypeFilter( + name: String, + options: Array>, + ) : SelectFilter( + name, + options, + ) + + protected open val typeFilterOptions = arrayOf( + Pair(intl["type_filter_option_all"], ""), + Pair(intl["type_filter_option_manga"], "Manga"), + Pair(intl["type_filter_option_manhwa"], "Manhwa"), + Pair(intl["type_filter_option_manhua"], "Manhua"), + Pair(intl["type_filter_option_comic"], "Comic"), + ) + + protected class OrderByFilter( + name: String, + options: Array>, + defaultOrder: String? = null, + ) : SelectFilter( + name, + options, defaultOrder, ) - protected class ProjectFilter : SelectFilter( - "Filter Project", - arrayOf( - Pair("Show all manga", ""), - Pair("Show only project manga", "project-filter-on"), - ), + protected open val orderByFilterOptions = arrayOf( + Pair(intl["order_by_filter_default"], ""), + Pair(intl["order_by_filter_az"], "title"), + Pair(intl["order_by_filter_za"], "titlereverse"), + Pair(intl["order_by_filter_latest_update"], "update"), + Pair(intl["order_by_filter_latest_added"], "latest"), + Pair(intl["order_by_filter_popular"], "popular"), + ) + + protected val popularFilter by lazy { FilterList(OrderByFilter("", orderByFilterOptions, "popular")) } + protected val latestFilter by lazy { FilterList(OrderByFilter("", orderByFilterOptions, "update")) } + + protected class ProjectFilter( + name: String, + options: Array>, + ) : SelectFilter( + name, + options, + ) + + protected open val projectFilterOptions = arrayOf( + Pair(intl["project_filter_all_manga"], ""), + Pair(intl["project_filter_only_project"], "project-filter-on"), + ) + + protected class GenreData( + val name: String, + val value: String, + val state: Int = Filter.TriState.STATE_IGNORE, ) protected class Genre( name: String, val value: String, - state: Int = STATE_IGNORE, + state: Int, ) : Filter.TriState(name, state) - protected class GenreListFilter(genres: List) : Filter.Group("Genre", genres) + protected class GenreListFilter(name: String, genres: List) : Filter.Group(name, genres) + + protected var genrelist: List? = null - private var genrelist: List? = null protected open fun getGenreList(): List { - // Filters are fetched immediately once an extension loads - // We're only able to get filters after a loading the manga directory, - // and resetting the filters is the only thing that seems to reinflate the view - return genrelist ?: listOf(Genre("Press reset to attempt to fetch genres", "")) + return genrelist?.map { Genre(it.name, it.value, it.state) }.orEmpty() } open val hasProjectPage = false @@ -423,21 +530,31 @@ abstract class MangaThemesia( override fun getFilterList(): FilterList { val filters = mutableListOf>( Filter.Separator(), - AuthorFilter(), - YearFilter(), - StatusFilter(), - TypeFilter(), - OrderByFilter(), - Filter.Header("Genre exclusion is not available for all sources"), - GenreListFilter(getGenreList()), + AuthorFilter(intl["author_filter_title"]), + YearFilter(intl["year_filter_title"]), + StatusFilter(intl["status_filter_title"], statusOptions), + TypeFilter(intl["type_filter_title"], typeFilterOptions), + OrderByFilter(intl["order_by_filter_title"], orderByFilterOptions), ) + if (!genrelist.isNullOrEmpty()) { + filters.addAll( + listOf( + Filter.Header(intl["genre_exclusion_warning"]), + GenreListFilter(intl["genre_filter_title"], getGenreList()), + ), + ) + } else { + filters.add( + Filter.Header(intl["genre_missing_warning"]), + ) + } if (hasProjectPage) { filters.addAll( mutableListOf>( Filter.Separator(), - Filter.Header("NOTE: Can't be used with other filter!"), - Filter.Header("$name Project List page"), - ProjectFilter(), + Filter.Header(intl["project_filter_warning"]), + Filter.Header(intl.format("project_filter_name", name)), + ProjectFilter(intl["project_filter_title"], projectFilterOptions), ), ) } @@ -485,9 +602,9 @@ abstract class MangaThemesia( (!strict && url.pathSegments.size == n + 1 && url.pathSegments[n].isEmpty()) } - private fun parseGenres(document: Document): List? { + private fun parseGenres(document: Document): List? { return document.selectFirst("ul.genrez")?.select("li")?.map { li -> - Genre( + GenreData( li.selectFirst("label")!!.text(), li.selectFirst("input[type=checkbox]")!!.attr("value"), ) @@ -514,15 +631,10 @@ abstract class MangaThemesia( override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException() - override fun setupPreferenceScreen(screen: PreferenceScreen) { - addRandomUAPreferenceToScreen(screen) - } - companion object { const val URL_SEARCH_PREFIX = "url:" // More info: https://issuetracker.google.com/issues/36970498 - @Suppress("RegExpRedundantEscape") private val MANGA_PAGE_ID_REGEX = "post_id\\s*:\\s*(\\d+)\\}".toRegex() private val CHAPTER_PAGE_ID_REGEX = "chapter_id\\s*=\\s*(\\d+);".toRegex() diff --git a/src/all/miauscan/src/eu/kanade/tachiyomi/extension/all/miauscan/MiauScanFactory.kt b/src/all/miauscan/src/eu/kanade/tachiyomi/extension/all/miauscan/MiauScanFactory.kt index 29849910f..a93917d66 100644 --- a/src/all/miauscan/src/eu/kanade/tachiyomi/extension/all/miauscan/MiauScanFactory.kt +++ b/src/all/miauscan/src/eu/kanade/tachiyomi/extension/all/miauscan/MiauScanFactory.kt @@ -33,10 +33,11 @@ open class MiauScan(lang: String) : MangaThemesia( override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { val genreFilterIndex = filters.indexOfFirst { it is GenreListFilter } val genreFilter = filters.getOrNull(genreFilterIndex) as? GenreListFilter - ?: GenreListFilter(emptyList()) + ?: GenreListFilter("", emptyList()) val overloadedGenreFilter = GenreListFilter( - genres = genreFilter.state + listOf( + genreFilter.name, + genreFilter.state + listOf( Genre("", PORTUGUESE_GENRE_ID, portugueseMode), ), ) diff --git a/src/all/mihentai/src/eu/kanade/tachiyomi/extension/all/mihentai/Mihentai.kt b/src/all/mihentai/src/eu/kanade/tachiyomi/extension/all/mihentai/Mihentai.kt index 8bdb74c0c..648a0cb46 100644 --- a/src/all/mihentai/src/eu/kanade/tachiyomi/extension/all/mihentai/Mihentai.kt +++ b/src/all/mihentai/src/eu/kanade/tachiyomi/extension/all/mihentai/Mihentai.kt @@ -31,8 +31,8 @@ class Mihentai : MangaThemesia("Mihentai", "https://mihentai.com", "all") { listOf( StatusFilter(), TypeFilter(), - OrderByFilter(), - GenreListFilter(getGenreList()), + OrderByFilter(intl["order_by_filter_title"], orderByFilterOptions), + GenreListFilter(intl["genre_filter_title"], getGenreList()), ), ) } diff --git a/src/ar/areamanga/src/eu/kanade/tachiyomi/extension/ar/areamanga/AreaManga.kt b/src/ar/areamanga/src/eu/kanade/tachiyomi/extension/ar/areamanga/AreaManga.kt index ea18cb04a..d6ae0d935 100644 --- a/src/ar/areamanga/src/eu/kanade/tachiyomi/extension/ar/areamanga/AreaManga.kt +++ b/src/ar/areamanga/src/eu/kanade/tachiyomi/extension/ar/areamanga/AreaManga.kt @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.extension.ar.areamanga import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia -import eu.kanade.tachiyomi.source.model.SManga import java.text.SimpleDateFormat import java.util.Locale @@ -10,21 +9,4 @@ class AreaManga : MangaThemesia( "https://www.areascans.net", "ar", dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")), -) { - override val seriesArtistSelector = - ".tsinfo .imptdt:contains(الرسام) i, ${super.seriesArtistSelector}" - override val seriesAuthorSelector = - ".tsinfo .imptdt:contains(المؤلف) i, ${super.seriesAuthorSelector}" - override val seriesStatusSelector = - ".tsinfo .imptdt:contains(الحالة) i, ${super.seriesStatusSelector}" - override val seriesTypeSelector = - ".tsinfo .imptdt:contains(النوع) i, ${super.seriesTypeSelector}" - - override fun String?.parseStatus() = when { - this == null -> SManga.UNKNOWN - this.contains("مستمر", ignoreCase = true) -> SManga.ONGOING - this.contains("مكتمل", ignoreCase = true) -> SManga.COMPLETED - this.contains("متوقف", ignoreCase = true) -> SManga.ON_HIATUS - else -> SManga.UNKNOWN - } -} +) diff --git a/src/ar/mangaswat/src/eu/kanade/tachiyomi/extension/ar/mangaswat/MangaSwat.kt b/src/ar/mangaswat/src/eu/kanade/tachiyomi/extension/ar/mangaswat/MangaSwat.kt index 639e5d319..8ebd85f3e 100644 --- a/src/ar/mangaswat/src/eu/kanade/tachiyomi/extension/ar/mangaswat/MangaSwat.kt +++ b/src/ar/mangaswat/src/eu/kanade/tachiyomi/extension/ar/mangaswat/MangaSwat.kt @@ -7,6 +7,7 @@ import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.extension.BuildConfig import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter @@ -23,12 +24,14 @@ import java.util.Locale private const val swatUrl = "https://swatmanhua.com" -class MangaSwat : MangaThemesia( - "MangaSwat", - swatUrl, - "ar", - dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")), -) { +class MangaSwat : + MangaThemesia( + "MangaSwat", + swatUrl, + "ar", + dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")), + ), + ConfigurableSource { private val defaultBaseUrl = swatUrl override val baseUrl by lazy { getPrefBaseUrl() } @@ -113,8 +116,6 @@ class MangaSwat : MangaThemesia( } } screen.addPreference(baseUrlPref) - - super.setupPreferenceScreen(screen) } private fun getPrefBaseUrl(): String = preferences.getString(BASE_URL_PREF, defaultBaseUrl)!! diff --git a/src/en/asurascans/src/eu/kanade/tachiyomi/extension/en/asurascans/AsuraScans.kt b/src/en/asurascans/src/eu/kanade/tachiyomi/extension/en/asurascans/AsuraScans.kt index 734ab4d98..0b8180239 100644 --- a/src/en/asurascans/src/eu/kanade/tachiyomi/extension/en/asurascans/AsuraScans.kt +++ b/src/en/asurascans/src/eu/kanade/tachiyomi/extension/en/asurascans/AsuraScans.kt @@ -6,6 +6,7 @@ import androidx.preference.PreferenceScreen import androidx.preference.SwitchPreferenceCompat import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page @@ -26,12 +27,14 @@ import java.text.SimpleDateFormat import java.util.Locale import java.util.concurrent.TimeUnit -class AsuraScans : MangaThemesia( - "Asura Scans", - "https://asuratoon.com", - "en", - dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US), -) { +class AsuraScans : + MangaThemesia( + "Asura Scans", + "https://asuratoon.com", + "en", + dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US), + ), + ConfigurableSource { private val preferences by lazy { Injekt.get().getSharedPreferences("source_$id", 0x0000) @@ -281,8 +284,6 @@ class AsuraScans : MangaThemesia( summary = PREF_PERM_MANGA_URL_SUMMARY setDefaultValue(true) }.also(screen::addPreference) - - super.setupPreferenceScreen(screen) } private val SharedPreferences.permaUrlPref diff --git a/src/en/constellarscans/build.gradle b/src/en/constellarscans/build.gradle index 9659c31ed..e8a779461 100644 --- a/src/en/constellarscans/build.gradle +++ b/src/en/constellarscans/build.gradle @@ -8,3 +8,7 @@ ext { } apply from: "$rootDir/common.gradle" + +dependencies { + implementation(project(":lib:randomua")) +} diff --git a/src/en/constellarscans/src/eu/kanade/tachiyomi/extension/en/constellarscans/ConstellarScans.kt b/src/en/constellarscans/src/eu/kanade/tachiyomi/extension/en/constellarscans/ConstellarScans.kt index e7c5027b4..71137c219 100644 --- a/src/en/constellarscans/src/eu/kanade/tachiyomi/extension/en/constellarscans/ConstellarScans.kt +++ b/src/en/constellarscans/src/eu/kanade/tachiyomi/extension/en/constellarscans/ConstellarScans.kt @@ -2,11 +2,14 @@ package eu.kanade.tachiyomi.extension.en.constellarscans import android.app.Application import android.content.SharedPreferences +import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.lib.randomua.addRandomUAPreferenceToScreen import eu.kanade.tachiyomi.lib.randomua.getPrefCustomUA import eu.kanade.tachiyomi.lib.randomua.getPrefUAType import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import kotlinx.serialization.json.jsonArray @@ -19,22 +22,29 @@ import okhttp3.Request import org.jsoup.nodes.Document import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.concurrent.TimeUnit -class ConstellarScans : MangaThemesia("Constellar Scans", "https://constellarcomic.com", "en") { +class ConstellarScans : + MangaThemesia( + "Constellar Scans", + "https://constellarcomic.com", + "en", + ), + ConfigurableSource { private val preferences: SharedPreferences by lazy { Injekt.get().getSharedPreferences("source_$id", 0x0000) } + override fun setupPreferenceScreen(screen: PreferenceScreen) { + addRandomUAPreferenceToScreen(screen) + } + override val client: OkHttpClient by lazy { network.cloudflareClient.newBuilder() .setRandomUserAgent( preferences.getPrefUAType(), preferences.getPrefCustomUA(), ) - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) .rateLimit(1, 1) .build() } @@ -61,6 +71,8 @@ class ConstellarScans : MangaThemesia("Constellar Scans", "https://constellarcom .build() override fun pageListParse(document: Document): List { + countViews(document) + val html = document.toString() if (!html.contains("ts_rea_der_._run(\"")) { return super.pageListParse(document) @@ -80,7 +92,6 @@ class ConstellarScans : MangaThemesia("Constellar Scans", "https://constellarcom } .joinToString("") - countViews(document) return json.parseToJsonElement(tsReaderRawData).jsonObject["sources"]!!.jsonArray[0].jsonObject["images"]!!.jsonArray.mapIndexed { idx, it -> Page(idx, imageUrl = it.jsonPrimitive.content) } diff --git a/src/en/flamecomics/src/eu/kanade/tachiyomi/extension/en/flamecomics/FlameComics.kt b/src/en/flamecomics/src/eu/kanade/tachiyomi/extension/en/flamecomics/FlameComics.kt index 8e7fe96e6..74a6fcbc7 100644 --- a/src/en/flamecomics/src/eu/kanade/tachiyomi/extension/en/flamecomics/FlameComics.kt +++ b/src/en/flamecomics/src/eu/kanade/tachiyomi/extension/en/flamecomics/FlameComics.kt @@ -9,6 +9,7 @@ import androidx.preference.PreferenceScreen import androidx.preference.SwitchPreferenceCompat import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page @@ -25,12 +26,14 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.io.ByteArrayOutputStream -class FlameComics : MangaThemesia( - "Flame Comics", - "https://flamecomics.com", - "en", - mangaUrlDirectory = "/series", -) { +class FlameComics : + MangaThemesia( + "Flame Comics", + "https://flamecomics.com", + "en", + mangaUrlDirectory = "/series", + ), + ConfigurableSource { // Flame Scans -> Flame Comics override val id = 6350607071566689772 diff --git a/src/en/luminousscans/src/eu/kanade/tachiyomi/extension/en/luminousscans/LuminousScans.kt b/src/en/luminousscans/src/eu/kanade/tachiyomi/extension/en/luminousscans/LuminousScans.kt index 05b336ce9..5b218aaf5 100644 --- a/src/en/luminousscans/src/eu/kanade/tachiyomi/extension/en/luminousscans/LuminousScans.kt +++ b/src/en/luminousscans/src/eu/kanade/tachiyomi/extension/en/luminousscans/LuminousScans.kt @@ -6,6 +6,7 @@ import androidx.preference.PreferenceScreen import androidx.preference.SwitchPreferenceCompat import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.SChapter @@ -20,7 +21,15 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.io.IOException -class LuminousScans : MangaThemesia("Luminous Scans", "https://lumitoon.com", "en", mangaUrlDirectory = "/series") { +class LuminousScans : + MangaThemesia( + "Luminous Scans", + "https://lumitoon.com", + "en", + mangaUrlDirectory = "/series", + ), + ConfigurableSource { + override val client = super.client.newBuilder() .addInterceptor(::urlChangeInterceptor) .rateLimit(2) @@ -201,8 +210,6 @@ class LuminousScans : MangaThemesia("Luminous Scans", "https://lumitoon.com", "e summary = PREF_PERM_MANGA_URL_SUMMARY setDefaultValue(true) }.also(screen::addPreference) - - super.setupPreferenceScreen(screen) } private val SharedPreferences.permaUrlPref diff --git a/src/en/lunarscans/src/eu/kanade/tachiyomi/extension/en/lunarscans/LunarScans.kt b/src/en/lunarscans/src/eu/kanade/tachiyomi/extension/en/lunarscans/LunarScans.kt index 6290d3091..f3a95f697 100644 --- a/src/en/lunarscans/src/eu/kanade/tachiyomi/extension/en/lunarscans/LunarScans.kt +++ b/src/en/lunarscans/src/eu/kanade/tachiyomi/extension/en/lunarscans/LunarScans.kt @@ -33,19 +33,29 @@ class LunarScans : MangaThemesia( val filters = mutableListOf>( Filter.Header("Note: Can't be used with text search!"), Filter.Separator(), - StatusFilter(), - TypeFilter(), - OrderByFilter(), - Filter.Header("Genre exclusion is not available for all sources"), - GenreListFilter(getGenreList()), + StatusFilter(intl["status_filter_title"], statusOptions), + TypeFilter(intl["type_filter_title"], typeFilterOptions), + OrderByFilter(intl["order_by_filter_title"], orderByFilterOptions), ) + if (!genrelist.isNullOrEmpty()) { + filters.addAll( + listOf( + Filter.Header(intl["genre_exclusion_warning"]), + GenreListFilter(intl["genre_filter_title"], getGenreList()), + ), + ) + } else { + filters.add( + Filter.Header(intl["genre_missing_warning"]), + ) + } if (hasProjectPage) { filters.addAll( mutableListOf>( Filter.Separator(), - Filter.Header("NOTE: Can't be used with other filter!"), - Filter.Header("$name Project List page"), - ProjectFilter(), + Filter.Header(intl["project_filter_warning"]), + Filter.Header(intl.format("project_filter_name", name)), + ProjectFilter(intl["project_filter_title"], projectFilterOptions), ), ) } diff --git a/src/es/carteldemanhwas/src/eu/kanade/tachiyomi/extension/es/carteldemanhwas/CarteldeManhwas.kt b/src/es/carteldemanhwas/src/eu/kanade/tachiyomi/extension/es/carteldemanhwas/CarteldeManhwas.kt index d7291369d..12c74a562 100644 --- a/src/es/carteldemanhwas/src/eu/kanade/tachiyomi/extension/es/carteldemanhwas/CarteldeManhwas.kt +++ b/src/es/carteldemanhwas/src/eu/kanade/tachiyomi/extension/es/carteldemanhwas/CarteldeManhwas.kt @@ -17,14 +17,4 @@ class CarteldeManhwas : MangaThemesia( override fun searchMangaSelector() = ".utao .uta .imgu:not(:has(span.novelabel)), " + ".listupd .bs .bsx:not(:has(span.novelabel)), " + ".listo .bs .bsx:not(:has(span.novelabel))" - - private class StatusFilter : SelectFilter( - "Status", - arrayOf( - Pair("All", ""), - Pair("Ongoing", "ongoing"), - Pair("Completed", "completed"), - Pair("Hiatus", "hiatus"), - ), - ) } diff --git a/src/es/gremorymangas/src/eu/kanade/tachiyomi/extension/es/gremorymangas/GremoryMangas.kt b/src/es/gremorymangas/src/eu/kanade/tachiyomi/extension/es/gremorymangas/GremoryMangas.kt index 0a09e395b..f0039bdd5 100644 --- a/src/es/gremorymangas/src/eu/kanade/tachiyomi/extension/es/gremorymangas/GremoryMangas.kt +++ b/src/es/gremorymangas/src/eu/kanade/tachiyomi/extension/es/gremorymangas/GremoryMangas.kt @@ -1,5 +1,12 @@ package eu.kanade.tachiyomi.extension.es.gremorymangas import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import java.text.SimpleDateFormat +import java.util.Locale -class GremoryMangas : MangaThemesia("Gremory Mangas", "https://gremorymangas.com", "es") +class GremoryMangas : MangaThemesia( + "Gremory Mangas", + "https://gremorymangas.com", + "es", + dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")), +) diff --git a/src/es/nekoscans/src/eu/kanade/tachiyomi/extension/es/nekoscans/NekoScans.kt b/src/es/nekoscans/src/eu/kanade/tachiyomi/extension/es/nekoscans/NekoScans.kt index 8e6ea4eb4..805cf6921 100644 --- a/src/es/nekoscans/src/eu/kanade/tachiyomi/extension/es/nekoscans/NekoScans.kt +++ b/src/es/nekoscans/src/eu/kanade/tachiyomi/extension/es/nekoscans/NekoScans.kt @@ -25,13 +25,13 @@ class NekoScans : MangaThemesia( override val seriesStatusSelector = ".tsinfo .imptdt:contains(estado) i" override fun pageListParse(document: Document): List { + countViews(document) + val chapterUrl = document.location() val htmlPages = document.select(pageSelector) .filterNot { it.imgAttr().isEmpty() } .mapIndexed { i, img -> Page(i, chapterUrl, img.imgAttr()) } - countViews(document) - // Some sites also loads pages via javascript if (htmlPages.isNotEmpty()) { return htmlPages } diff --git a/src/es/senpaiediciones/src/eu/kanade/tachiyomi/extension/es/senpaiediciones/SenpaiEdiciones.kt b/src/es/senpaiediciones/src/eu/kanade/tachiyomi/extension/es/senpaiediciones/SenpaiEdiciones.kt index e9fd36ff5..ac3bab3f1 100644 --- a/src/es/senpaiediciones/src/eu/kanade/tachiyomi/extension/es/senpaiediciones/SenpaiEdiciones.kt +++ b/src/es/senpaiediciones/src/eu/kanade/tachiyomi/extension/es/senpaiediciones/SenpaiEdiciones.kt @@ -1,26 +1,14 @@ package eu.kanade.tachiyomi.extension.es.senpaiediciones import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia -import eu.kanade.tachiyomi.source.model.SManga import java.text.SimpleDateFormat import java.util.Locale class SenpaiEdiciones : MangaThemesia( "Senpai Ediciones", - "http://senpaiediciones.com", + "https://senpaiediciones.com", "es", dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")), ) { - override val seriesAuthorSelector = ".imptdt:contains(Autor) i" - override val seriesStatusSelector = ".imptdt:contains(Estado) i" - override val pageSelector = "div#readerarea img:not(noscript img)" - - override fun String?.parseStatus(): Int = when { - this == null -> SManga.UNKNOWN - listOf("curso").any { this.contains(it, ignoreCase = true) } -> SManga.ONGOING - this.contains("hiatus", ignoreCase = true) -> SManga.ON_HIATUS - this.contains("finalizado", ignoreCase = true) -> SManga.COMPLETED - else -> SManga.UNKNOWN - } } diff --git a/src/id/cosmicscansid/src/eu/kanade/tachiyomi/extension/id/cosmicscansid/CosmicScansID.kt b/src/id/cosmicscansid/src/eu/kanade/tachiyomi/extension/id/cosmicscansid/CosmicScansID.kt index 371ff2700..945368ac0 100644 --- a/src/id/cosmicscansid/src/eu/kanade/tachiyomi/extension/id/cosmicscansid/CosmicScansID.kt +++ b/src/id/cosmicscansid/src/eu/kanade/tachiyomi/extension/id/cosmicscansid/CosmicScansID.kt @@ -19,7 +19,7 @@ class CosmicScansID : MangaThemesia("CosmicScans.id", "https://cosmicscans.id", override val hasProjectPage = true override val projectPageString = "/semua-komik" - override fun latestUpdatesRequest(page: Int) = GET("$baseUrl" + if (page > 1) "/page/$page" else "", headers) + override fun latestUpdatesRequest(page: Int) = GET(baseUrl + if (page > 1) "/page/$page" else "", headers) // search override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { @@ -47,8 +47,8 @@ class CosmicScansID : MangaThemesia("CosmicScans.id", "https://cosmicscans.id", val filters = mutableListOf>( Filter.Separator(), Filter.Header("$name Project List page"), - ProjectFilter(), - OrderByFilter(), + ProjectFilter(intl["project_filter_title"], projectFilterOptions), + OrderByFilter(intl["order_by_filter_title"], orderByFilterOptions), ) return FilterList(filters) } diff --git a/src/id/komikcast/src/eu/kanade/tachiyomi/extension/id/komikcast/KomikCast.kt b/src/id/komikcast/src/eu/kanade/tachiyomi/extension/id/komikcast/KomikCast.kt index 051108ed6..9a5e7486e 100644 --- a/src/id/komikcast/src/eu/kanade/tachiyomi/extension/id/komikcast/KomikCast.kt +++ b/src/id/komikcast/src/eu/kanade/tachiyomi/extension/id/komikcast/KomikCast.kt @@ -241,12 +241,12 @@ class KomikCast : MangaThemesia("Komik Cast", "https://komikcast.lol", "id", "/d StatusFilter(), TypeFilter(), OrderByFilter(), - Filter.Header("Genre exclusion is not available for all sources"), - GenreListFilter(getGenreList()), + Filter.Header(intl["genre_exclusion_warning"]), + GenreListFilter(intl["genre_filter_title"], getGenreList()), Filter.Separator(), - Filter.Header("NOTE: Can't be used with other filter!"), - Filter.Header("$name Project List page"), - ProjectFilter(), + Filter.Header(intl["project_filter_warning"]), + Filter.Header(intl.format("project_filter_name", name)), + ProjectFilter(intl["project_filter_title"], projectFilterOptions), ) return FilterList(filters) } diff --git a/src/ja/mangamate/src/eu/kanade/tachiyomi/extension/ja/mangamate/MangaMate.kt b/src/ja/mangamate/src/eu/kanade/tachiyomi/extension/ja/mangamate/MangaMate.kt index ae59864df..a0860337d 100644 --- a/src/ja/mangamate/src/eu/kanade/tachiyomi/extension/ja/mangamate/MangaMate.kt +++ b/src/ja/mangamate/src/eu/kanade/tachiyomi/extension/ja/mangamate/MangaMate.kt @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.extension.ja.mangamate import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia -import eu.kanade.tachiyomi.source.model.SManga import java.text.SimpleDateFormat import java.util.Locale @@ -12,12 +11,4 @@ class MangaMate : MangaThemesia( dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale("ja")), ) { override val seriesAuthorSelector = ".fmed b:contains(作者) + span" - override val seriesStatusSelector = ".tsinfo .imptdt:contains(連載状況) i" - - override fun String?.parseStatus(): Int = when (this) { - "連載中" -> SManga.ONGOING - "完結" -> SManga.COMPLETED - "人気" -> SManga.ON_HIATUS - else -> SManga.UNKNOWN - } }