Remove sources (#5846)

This commit is contained in:
Chopper 2024-11-04 10:16:05 -03:00 committed by Draff
parent df88c6535c
commit a7600082ab
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
47 changed files with 0 additions and 1016 deletions

View File

@ -1,8 +0,0 @@
ext {
extName = 'Pururin'
extClass = '.PururinFactory'
extVersionCode = 9
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -1,264 +0,0 @@
package eu.kanade.tachiyomi.extension.all.pururin
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.FilterList
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.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
abstract class Pururin(
override val lang: String = "all",
private val searchLang: Pair<String, String>? = null,
private val langPath: String = "",
) : ParsedHttpSource() {
override val name = "Pururin"
final override val baseUrl = "https://pururin.to"
override val supportsLatest = true
override val client = network.cloudflareClient
private val json: Json by injectLazy()
// Popular
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/browse$langPath?sort=most-popular&page=$page", headers)
}
override fun popularMangaSelector(): String = "a.card"
override fun popularMangaFromElement(element: Element): SManga {
return SManga.create().apply {
title = element.attr("title")
setUrlWithoutDomain(element.attr("abs:href"))
thumbnail_url = element.select("img").attr("abs:src")
}
}
override fun popularMangaNextPageSelector(): String = ".page-item [rel=next]"
// Latest
override fun latestUpdatesRequest(page: Int): Request {
return GET("$baseUrl/browse$langPath?page=$page", headers)
}
override fun latestUpdatesSelector(): String = popularMangaSelector()
override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
override fun latestUpdatesNextPageSelector(): String = popularMangaNextPageSelector()
// Search
private fun List<Pair<String, String>>.toValue(): String {
return "[${this.joinToString(",") { "{\"id\":${it.first},\"name\":\"${it.second}\"}" }}]"
}
private fun parsePageRange(query: String, minPages: Int = 1, maxPages: Int = 9999): Pair<Int, Int> {
val num = query.filter(Char::isDigit).toIntOrNull() ?: -1
fun limitedNum(number: Int = num): Int = number.coerceIn(minPages, maxPages)
if (num < 0) return minPages to maxPages
return when (query.firstOrNull()) {
'<' -> 1 to if (query[1] == '=') limitedNum() else limitedNum(num + 1)
'>' -> limitedNum(if (query[1] == '=') num else num + 1) to maxPages
'=' -> when (query[1]) {
'>' -> limitedNum() to maxPages
'<' -> 1 to limitedNum(maxPages)
else -> limitedNum() to limitedNum()
}
else -> limitedNum() to limitedNum()
}
}
@Serializable
class Tag(
val id: Int,
val name: String,
)
private fun findTagByNameSubstring(tags: List<Tag>, substring: String): Pair<String, String>? {
val tag = tags.find { it.name.contains(substring, ignoreCase = true) }
return tag?.let { Pair(tag.id.toString(), tag.name) }
}
private fun tagSearch(tag: String, type: String): Pair<String, String>? {
val requestBody = FormBody.Builder()
.add("text", tag)
.build()
val request = Request.Builder()
.url("$baseUrl/api/get/tags/search")
.headers(headers)
.post(requestBody)
.build()
val response = client.newCall(request).execute()
return findTagByNameSubstring(response.parseAs<List<Tag>>(), type)
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val includeTags = mutableListOf<Pair<String, String>>()
val excludeTags = mutableListOf<Pair<String, String>>()
var pagesMin = 1
var pagesMax = 9999
var sortBy = "newest"
if (searchLang != null) includeTags.add(searchLang)
filters.forEach {
when (it) {
is SelectFilter -> sortBy = it.getValue()
is TypeFilter -> {
val (_, inactiveFilters) = it.state.partition { stIt -> stIt.state }
excludeTags += inactiveFilters.map { fil -> Pair(fil.value, "${fil.name} [Category]") }
}
is PageFilter -> {
if (it.state.isNotEmpty()) {
val (min, max) = parsePageRange(it.state)
pagesMin = min
pagesMax = max
}
}
is TextFilter -> {
if (it.state.isNotEmpty()) {
it.state.split(",").filter(String::isNotBlank).map { tag ->
val trimmed = tag.trim()
if (trimmed.startsWith('-')) {
tagSearch(trimmed.lowercase().removePrefix("-"), it.type)?.let { tagInfo ->
excludeTags.add(tagInfo)
}
} else {
tagSearch(trimmed.lowercase(), it.type)?.let { tagInfo ->
includeTags.add(tagInfo)
}
}
}
}
}
else -> {}
}
}
// Searching with just one tag usually gives wrong results
if (query.isEmpty()) {
when {
excludeTags.size == 1 && includeTags.isEmpty() -> excludeTags.addAll(excludeTags)
includeTags.size == 1 && excludeTags.isEmpty() -> {
val url = baseUrl.toHttpUrl().newBuilder().apply {
addPathSegment("browse")
addPathSegment("tags")
addPathSegment("content")
addPathSegment(includeTags[0].first)
addQueryParameter("sort", sortBy)
addQueryParameter("start_page", pagesMin.toString())
addQueryParameter("last_page", pagesMax.toString())
if (page > 1) addQueryParameter("page", page.toString())
}.build()
return GET(url, headers)
}
}
}
val url = baseUrl.toHttpUrl().newBuilder().apply {
addPathSegment("search")
addQueryParameter("q", query)
addQueryParameter("sort", sortBy)
addQueryParameter("start_page", pagesMin.toString())
addQueryParameter("last_page", pagesMax.toString())
if (includeTags.isNotEmpty()) addQueryParameter("included_tags", includeTags.toValue())
if (excludeTags.isNotEmpty()) addQueryParameter("excluded_tags", excludeTags.toValue())
if (page > 1) addQueryParameter("page", page.toString())
}.build()
return GET(url, headers)
}
override fun searchMangaSelector(): String = popularMangaSelector()
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
override fun searchMangaNextPageSelector(): String = popularMangaNextPageSelector()
// Details
override fun mangaDetailsParse(document: Document): SManga {
return SManga.create().apply {
document.select(".box-gallery").let { e ->
initialized = true
title = e.select(".title").text()
author = e.select("a[href*=/circle/]").text().ifEmpty { e.select("[itemprop=author]").text() }
artist = e.select("[itemprop=author]").text()
genre = e.select("a[href*=/content/]").text()
description = e.select(".box-gallery .table-info tr")
.filter { tr ->
tr.select("td").none { it.text().contains("content", ignoreCase = true) || it.text().contains("ratings", ignoreCase = true) }
}
.joinToString("\n") { tr ->
tr.select("td")
.joinToString(": ") { it.text() }
}
thumbnail_url = e.select("img").attr("abs:src")
}
}
}
// Chapters
override fun chapterListSelector(): String = ".table-collection tbody tr a"
override fun chapterFromElement(element: Element): SChapter {
return SChapter.create().apply {
name = element.text()
setUrlWithoutDomain(element.attr("abs:href"))
}
}
override fun chapterListParse(response: Response): List<SChapter> {
return response.asJsoup().select(chapterListSelector())
.map { chapterFromElement(it) }
.reversed()
.let { list ->
list.ifEmpty {
listOf(
SChapter.create().apply {
setUrlWithoutDomain(response.request.url.toString())
name = "Chapter"
},
)
}
}
}
// Pages
override fun pageListParse(document: Document): List<Page> {
return document.select(".gallery-preview a img")
.mapIndexed { i, img ->
Page(i, "", (if (img.hasAttr("abs:src")) img.attr("abs:src") else img.attr("abs:data-src")).replace("t.", "."))
}
}
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException()
private inline fun <reified T> Response.parseAs(): T {
return json.decodeFromString(body.string())
}
override fun getFilterList() = getFilters()
}

View File

@ -1,24 +0,0 @@
package eu.kanade.tachiyomi.extension.all.pururin
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
class PururinFactory : SourceFactory {
override fun createSources(): List<Source> = listOf(
PururinAll(),
PururinEN(),
PururinJA(),
)
}
class PururinAll : Pururin()
class PururinEN : Pururin(
"en",
Pair("13010", "english"),
"/tags/language/13010/english",
)
class PururinJA : Pururin(
"ja",
Pair("13011", "japanese"),
"/tags/language/13011/japanese",
)

View File

@ -1,57 +0,0 @@
package eu.kanade.tachiyomi.extension.all.pururin
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
fun getFilters(): FilterList {
return FilterList(
SelectFilter("Sort by", getSortsList),
TypeFilter("Types"),
Filter.Separator(),
Filter.Header("Separate tags with commas (,)"),
Filter.Header("Prepend with dash (-) to exclude"),
TextFilter("Tags", "[Content]"),
TextFilter("Artists", "[Artist]"),
TextFilter("Circles", "[Circle]"),
TextFilter("Parodies", "[Parody]"),
TextFilter("Languages", "[Language]"),
TextFilter("Scanlators", "[Scanlator]"),
TextFilter("Conventions", "[Convention]"),
TextFilter("Collections", "[Collections]"),
TextFilter("Categories", "[Category]"),
TextFilter("Uploaders", "[Uploader]"),
Filter.Separator(),
Filter.Header("Filter by pages, for example: (>20)"),
PageFilter("Pages"),
)
}
internal class TypeFilter(name: String) :
Filter.Group<CheckBoxFilter>(
name,
listOf(
Pair("Artbook", "17783"),
Pair("Artist CG", "13004"),
Pair("Doujinshi", "13003"),
Pair("Game CG", "13008"),
Pair("Manga", "13004"),
Pair("Webtoon", "27939"),
).map { CheckBoxFilter(it.first, it.second, true) },
)
internal open class CheckBoxFilter(name: String, val value: String, state: Boolean) : Filter.CheckBox(name, state)
internal open class PageFilter(name: String) : Filter.Text(name)
internal open class TextFilter(name: String, val type: String) : Filter.Text(name)
internal open class SelectFilter(name: String, val vals: List<Pair<String, String>>, state: Int = 0) :
Filter.Select<String>(name, vals.map { it.first }.toTypedArray(), state) {
fun getValue() = vals[state].second
}
private val getSortsList: List<Pair<String, String>> = listOf(
Pair("Newest", "newest"),
Pair("Most Popular", "most-popular"),
Pair("Highest Rated", "highest-rated"),
Pair("Most Viewed", "most-viewed"),
Pair("Title", "title"),
)

View File

@ -1,10 +0,0 @@
ext {
extName = 'Radiant Scans'
extClass = '.RadiantScans'
themePkg = 'mangathemesia'
baseUrl = 'https://radiantscans.com'
overrideVersionCode = 9
isNsfw = false
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

View File

@ -1,46 +0,0 @@
package eu.kanade.tachiyomi.extension.en.luminousscans
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesiaAlt
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.FilterList
import okhttp3.Request
class RadiantScans : MangaThemesiaAlt(
"Radiant Scans",
"https://radiantscans.com",
"en",
mangaUrlDirectory = "/series",
randomUrlPrefKey = "pref_permanent_manga_url_2_en",
) {
// Luminous Scans -> Radiant Scans
override val id = 1019556752273106311
init {
// remove legacy preferences
preferences.run {
if (contains("pref_url_map")) {
edit().remove("pref_url_map").apply()
}
}
}
override val client = super.client.newBuilder()
.rateLimit(2)
.build()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val request = super.searchMangaRequest(page, query, filters)
if (query.isBlank()) return request
val url = request.url.newBuilder()
.addPathSegment("page/$page/")
.removeAllQueryParameters("page")
.removeAllQueryParameters("title")
.addQueryParameter("s", query)
.build()
return request.newBuilder()
.url(url)
.build()
}
}

View File

@ -1,10 +0,0 @@
ext {
extName = 'Manga Tx.to'
extClass = '.MangaTxTo'
themePkg = 'madara'
baseUrl = 'https://mangatx.to'
overrideVersionCode = 0
isNsfw = false
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

View File

@ -1,14 +0,0 @@
package eu.kanade.tachiyomi.extension.en.mangatxto
import eu.kanade.tachiyomi.multisrc.madara.Madara
class MangaTxTo : Madara(
"Manga Tx.to",
"https://mangatx.to",
"en",
) {
override val useLoadMoreRequest = LoadMoreStrategy.Never
override val useNewChapterEndpoint = false
override val mangaSubString = "manhua"
}

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".en.ninehentai.NineHentaiUrlActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="9hentai.com"
android:pathPattern="/g/..*"
android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -1,8 +0,0 @@
ext {
extName = 'NineHentai'
extClass = '.NineHentai'
extVersionCode = 3
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,379 +0,0 @@
package eu.kanade.tachiyomi.extension.en.ninehentai
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess
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
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.put
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import okio.Buffer
import org.jsoup.nodes.Element
import rx.Observable
import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy
import java.util.Calendar
class NineHentai : HttpSource() {
override val baseUrl = "https://9hentai.com"
override val name = "NineHentai"
override val lang = "en"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val json: Json by injectLazy()
// Builds request for /api/getBooks endpoint
private fun buildSearchRequest(
searchText: String = "",
page: Int,
sort: Int = 0,
range: List<Int> = listOf(0, 2000),
includedTags: List<Tag> = listOf(),
excludedTags: List<Tag> = listOf(),
): Request {
val searchRequest = SearchRequest(
text = searchText,
page = page - 1, // Source starts counting from 0, not 1
sort = sort,
pages = Range(range),
tag = Items(
items = TagArrays(
included = includedTags,
excluded = excludedTags,
),
),
)
val jsonString = json.encodeToString(SearchRequestPayload(search = searchRequest))
return POST("$baseUrl$SEARCH_URL", headers, jsonString.toRequestBody(MEDIA_TYPE))
}
private fun parseSearchResponse(response: Response): MangasPage {
return response.use {
val page = json.decodeFromString<SearchRequestPayload>(it.request.bodyString).search.page
json.decodeFromString<SearchResponse>(it.body.string()).let { searchResponse ->
MangasPage(
searchResponse.results.map {
SManga.create().apply {
url = "/g/${it.id}"
title = it.title
// Cover is the compressed first page (cover might change if page count changes)
thumbnail_url = "${it.image_server}${it.id}/1.jpg?${it.total_page}"
}
},
searchResponse.totalCount - 1 > page,
)
}
}
}
// Builds request for /api/getBookById endpoint
private fun buildDetailRequest(id: Int): Request {
val jsonString = buildJsonObject { put("id", id) }.toString()
return POST("$baseUrl$MANGA_URL", headers, jsonString.toRequestBody(MEDIA_TYPE))
}
// Popular
override fun popularMangaRequest(page: Int): Request = buildSearchRequest(page = page, sort = 1)
override fun popularMangaParse(response: Response): MangasPage = parseSearchResponse(response)
// Latest
override fun latestUpdatesRequest(page: Int): Request = buildSearchRequest(page = page)
override fun latestUpdatesParse(response: Response): MangasPage = parseSearchResponse(response)
// Search
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
if (query.startsWith("id:")) {
val id = query.substringAfter("id:").toInt()
return client.newCall(buildDetailRequest(id))
.asObservableSuccess()
.map { response ->
fetchSingleManga(response)
}
}
return super.fetchSearchManga(page, query, filters)
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val filterList = if (filters.isEmpty()) getFilterList() else filters
var sort = 0
val range = mutableListOf(0, 2000)
val includedTags = mutableListOf<Tag>()
val excludedTags = mutableListOf<Tag>()
for (filter in filterList) {
when (filter) {
is SortFilter -> {
sort = filter.state
}
is MinPagesFilter -> {
try {
range[0] = filter.state.toInt()
} catch (_: NumberFormatException) {
// Suppress and retain default value
}
}
is MaxPagesFilter -> {
try {
range[1] = filter.state.toInt()
} catch (_: NumberFormatException) {
// Suppress and retain default value
}
}
is IncludedFilter -> {
includedTags += getTags(filter.state, 1)
}
is ExcludedFilter -> {
excludedTags += getTags(filter.state, 1)
}
is GroupFilter -> {
includedTags += getTags(filter.state, 2)
}
is ParodyFilter -> {
includedTags += getTags(filter.state, 3)
}
is ArtistFilter -> {
includedTags += getTags(filter.state, 4)
}
is CharacterFilter -> {
includedTags += getTags(filter.state, 5)
}
is CategoryFilter -> {
includedTags += getTags(filter.state, 6)
}
else -> { /* Do nothing */ }
}
}
return buildSearchRequest(
searchText = query,
page = page,
sort = sort,
range = range,
includedTags = includedTags,
excludedTags = excludedTags,
)
}
override fun searchMangaParse(response: Response): MangasPage = parseSearchResponse(response)
// Manga Details
override fun mangaDetailsParse(response: Response): SManga {
return SManga.create().apply {
response.asJsoup().selectFirst("div#bigcontainer")!!.let { info ->
title = info.select("h1").text()
thumbnail_url = info.selectFirst("div#cover v-lazy-image")!!.attr("abs:src")
status = SManga.COMPLETED
artist = info.selectTextOrNull("div.field-name:contains(Artist:) a.tag")
author = info.selectTextOrNull("div.field-name:contains(Group:) a.tag") ?: "Unknown circle"
genre = info.selectTextOrNull("div.field-name:contains(Tag:) a.tag")
// Additional details
description = listOf(
Pair("Alternative Title", info.selectTextOrNull("h2")),
Pair("Pages", info.selectTextOrNull("div#info > div:contains(pages)")),
Pair("Parody", info.selectTextOrNull("div.field-name:contains(Parody:) a.tag")),
Pair("Category", info.selectTextOrNull("div.field-name:contains(Category:) a.tag")),
Pair("Language", info.selectTextOrNull("div.field-name:contains(Language:) a.tag")),
).filterNot { it.second.isNullOrEmpty() }.joinToString("\n\n") { "${it.first}: ${it.second}" }
}
}
}
// Ensures no exceptions are thrown when scraping additional details
private fun Element.selectTextOrNull(selector: String): String? {
val list = this.select(selector)
return if (list.isEmpty()) {
null
} else {
list.joinToString(", ") { it.text() }
}
}
// Chapter
override fun chapterListParse(response: Response): List<SChapter> {
val time = response.asJsoup().select("div#info div time").text()
return listOf(
SChapter.create().apply {
name = "Chapter"
date_upload = parseChapterDate(time)
url = response.request.url.encodedPath
},
)
}
private fun parseChapterDate(date: String): Long {
val dateStringSplit = date.split(" ")
val value = dateStringSplit[0].toInt()
return when (dateStringSplit[1].removeSuffix("s")) {
"sec" -> Calendar.getInstance().apply {
add(Calendar.SECOND, value * -1)
}.timeInMillis
"min" -> Calendar.getInstance().apply {
add(Calendar.MINUTE, value * -1)
}.timeInMillis
"hour" -> Calendar.getInstance().apply {
add(Calendar.HOUR_OF_DAY, value * -1)
}.timeInMillis
"day" -> Calendar.getInstance().apply {
add(Calendar.DATE, value * -1)
}.timeInMillis
"week" -> Calendar.getInstance().apply {
add(Calendar.DATE, value * 7 * -1)
}.timeInMillis
"month" -> Calendar.getInstance().apply {
add(Calendar.MONTH, value * -1)
}.timeInMillis
"year" -> Calendar.getInstance().apply {
add(Calendar.YEAR, value * -1)
}.timeInMillis
else -> {
return 0
}
}
}
// Page List
override fun pageListRequest(chapter: SChapter): Request {
val mangaId = chapter.url.substringAfter("/g/").toInt()
return buildDetailRequest(mangaId)
}
override fun pageListParse(response: Response): List<Page> {
val resultsObj = json.parseToJsonElement(response.body.string()).jsonObject["results"]!!
val manga = json.decodeFromJsonElement<Manga>(resultsObj)
val imageUrl = manga.image_server + manga.id
var totalPages = manga.total_page
client.newCall(
GET(
"$imageUrl/preview/${totalPages}t.jpg",
headersBuilder().build(),
),
).execute().code.let { code ->
if (code == 404) totalPages--
}
return (1..totalPages).map {
Page(it - 1, "", "$imageUrl/$it.jpg")
}
}
private fun getTags(queries: String, type: Int): List<Tag> {
return queries.split(",").map(String::trim)
.filterNot(String::isBlank).mapNotNull { query ->
val jsonString = buildJsonObject {
put("tag_name", query)
put("tag_type", type)
}.toString()
lookupTags(jsonString)
}
}
// Based on HentaiHand ext
private fun lookupTags(request: String): Tag? {
return client.newCall(POST("$baseUrl$TAG_URL", headers, request.toRequestBody(MEDIA_TYPE)))
.asObservableSuccess()
.subscribeOn(Schedulers.io())
.map { response ->
// Returns the first matched tag, or null if there are no results
val tagList = json.parseToJsonElement(response.body.string()).jsonObject["results"]!!.jsonArray.map {
json.decodeFromJsonElement<Tag>(it)
}
if (tagList.isEmpty()) {
return@map null
} else {
tagList.first()
}
}.toBlocking().first()
}
private fun fetchSingleManga(response: Response): MangasPage {
val resultsObj = json.parseToJsonElement(response.body.string()).jsonObject["results"]!!
val manga = json.decodeFromJsonElement<Manga>(resultsObj)
val list = listOf(
SManga.create().apply {
setUrlWithoutDomain("/g/${manga.id}")
title = manga.title
thumbnail_url = "${manga.image_server + manga.id}/cover.jpg"
},
)
return MangasPage(list, false)
}
// Filters
private class SortFilter : Filter.Select<String>(
"Sort by",
arrayOf("Newest", "Popular Right now", "Most Fapped", "Most Viewed", "By Title"),
)
private class MinPagesFilter : Filter.Text("Minimum Pages")
private class MaxPagesFilter : Filter.Text("Maximum Pages")
private class IncludedFilter : Filter.Text("Included Tags")
private class ExcludedFilter : Filter.Text("Excluded Tags")
private class ArtistFilter : Filter.Text("Artist")
private class GroupFilter : Filter.Text("Group")
private class ParodyFilter : Filter.Text("Parody")
private class CharacterFilter : Filter.Text("Character")
private class CategoryFilter : Filter.Text("Category")
override fun getFilterList() = FilterList(
Filter.Header("Search by id with \"id:\" in front of query"),
Filter.Separator(),
SortFilter(),
MinPagesFilter(),
MaxPagesFilter(),
IncludedFilter(),
ExcludedFilter(),
ArtistFilter(),
GroupFilter(),
ParodyFilter(),
CharacterFilter(),
CategoryFilter(),
)
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
private val Request.bodyString: String
get() {
val requestCopy = newBuilder().build()
val buffer = Buffer()
return runCatching { buffer.apply { requestCopy.body!!.writeTo(this) }.readUtf8() }
.getOrNull() ?: ""
}
companion object {
private val MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull()
private const val SEARCH_URL = "/api/getBook"
private const val MANGA_URL = "/api/getBookByID"
private const val TAG_URL = "/api/getTag"
}
}

View File

@ -1,84 +0,0 @@
package eu.kanade.tachiyomi.extension.en.ninehentai
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class Manga(
val id: Int,
val title: String,
val image_server: String,
val total_page: Int,
)
/*
The basic search request JSON object looks like this:
{
"search": {
"text": "",
"page": 1,
"sort": 1,
"pages": {
"range": [0, 2000]
},
"tag": {
"items": {
"included": [],
"excluded": []
}
}
}
}
*/
/*
Sort = 0, Newest
Sort = 1, Popular right now
Sort = 2, Most Fapped
Sort = 3, Most Viewed
Sort = 4, By title
*/
@Serializable
data class SearchRequest(
val text: String,
val page: Int,
val sort: Int,
val pages: Range,
val tag: Items,
)
@Serializable
data class SearchRequestPayload(
val search: SearchRequest,
)
@Serializable
data class SearchResponse(
@SerialName("total_count") val totalCount: Int,
val results: List<Manga>,
)
@Serializable
data class Range(
val range: List<Int>,
)
@Serializable
data class Items(
val items: TagArrays,
)
@Serializable
data class TagArrays(
val included: List<Tag>,
val excluded: List<Tag>,
)
@Serializable
data class Tag(
val id: Int,
val name: String,
val description: String? = null,
val type: Int = 1,
)

View File

@ -1,38 +0,0 @@
package eu.kanade.tachiyomi.extension.en.ninehentai
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.util.Log
import kotlin.system.exitProcess
/**
* Springboard that accepts https://9hentai.com/g/xxxxxx intents and redirects them to
* the main Tachiyomi process.
*/
class NineHentaiUrlActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val pathSegments = intent?.data?.pathSegments
if (pathSegments != null && pathSegments.size > 1) {
val id = pathSegments[1]
val mainIntent = Intent().apply {
action = "eu.kanade.tachiyomi.SEARCH"
putExtra("query", "id:$id")
putExtra("filter", packageName)
}
try {
startActivity(mainIntent)
} catch (e: ActivityNotFoundException) {
Log.e("NineHentaiUrlActivity", e.toString())
}
} else {
Log.e("NineHentaiUrlActivity", "could not parse uri from intent $intent")
}
finish()
exitProcess(0)
}
}

View File

@ -1,9 +0,0 @@
ext {
extName = 'ReaderGen'
extClass = '.ReaderGen'
themePkg = 'madara'
baseUrl = 'https://fr.readergen.fr'
overrideVersionCode = 0
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,7 +0,0 @@
package eu.kanade.tachiyomi.extension.fr.readergen
import eu.kanade.tachiyomi.multisrc.madara.Madara
class ReaderGen : Madara("ReaderGen", "https://fr.readergen.fr", "fr") {
override val useNewChapterEndpoint = true
}

View File

@ -1,10 +0,0 @@
ext {
extName = 'Zanman Manga'
extClass = '.ZanmanManga'
themePkg = 'madara'
baseUrl = 'https://zamanmanga.com'
overrideVersionCode = 0
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

View File

@ -1,25 +0,0 @@
package eu.kanade.tachiyomi.extension.tr.zanmanmanga
import eu.kanade.tachiyomi.multisrc.madara.Madara
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Locale
class ZanmanManga : Madara(
"Zanman Manga",
"https://zamanmanga.com",
"tr",
dateFormat = SimpleDateFormat("d MMMM yyyy", Locale("tr")),
) {
override val mangaDetailsSelectorAuthor = "div.manga-authors > a"
override val mangaDetailsSelectorDescription = "div.manga-summary"
override val mangaDetailsSelectorThumbnail = "head meta[property='og:image']" // Same as browse
override val useLoadMoreRequest = LoadMoreStrategy.Never
override val useNewChapterEndpoint = true
override fun imageFromElement(element: Element): String? {
return super.imageFromElement(element)?.takeIf { it.isNotEmpty() }
?: element.attr("content") // Thumbnail from <head>
}
}