Add new sources + fixes (#6680)

* add source fr FuryoSquad

* add source en 24hRomance

* add source en SetsuScans

* fix Kangaryu
This commit is contained in:
nzoba 2021-04-23 22:27:46 +02:00 committed by GitHub
parent 7729362ed5
commit 0c3581c87c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 294 additions and 8 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,5 @@
package eu.kanade.tachiyomi.extension.en.romance24h
import eu.kanade.tachiyomi.multisrc.madara.Madara
class Romance24h : Madara("24hRomance", "https://24hromance.com", "en")

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@ -0,0 +1,5 @@
package eu.kanade.tachiyomi.extension.en.setsuscans
import eu.kanade.tachiyomi.multisrc.madara.Madara
class SetsuScans : Madara("Setsu Scans", "https://setsuscans.com", "en")

View File

@ -13,6 +13,7 @@ class MadaraGenerator : ThemeSourceGenerator {
override val baseVersionCode: Int = 3 override val baseVersionCode: Int = 3
override val sources = listOf( override val sources = listOf(
SingleLang("24hRomance", "https://24hromance.com", "en", className = "Romance24h"),
SingleLang("Adonis Fansub", "https://manga.adonisfansub.com", "tr"), SingleLang("Adonis Fansub", "https://manga.adonisfansub.com", "tr"),
SingleLang("AkuManga", "https://akumanga.com", "ar"), SingleLang("AkuManga", "https://akumanga.com", "ar"),
SingleLang("AlianzaMarcial", "https://alianzamarcial.xyz", "es"), SingleLang("AlianzaMarcial", "https://alianzamarcial.xyz", "es"),
@ -197,6 +198,7 @@ class MadaraGenerator : ThemeSourceGenerator {
SingleLang("S2Manga", "https://s2manga.com", "en"), SingleLang("S2Manga", "https://s2manga.com", "en"),
SingleLang("SamuraiScan", "https://samuraiscan.com", "es"), SingleLang("SamuraiScan", "https://samuraiscan.com", "es"),
SingleLang("Sekte Doujin", "https://sektedoujin.xyz", "id", isNsfw = true), SingleLang("Sekte Doujin", "https://sektedoujin.xyz", "id", isNsfw = true),
SingleLang("Setsu Scans", "https://setsuscans.com", "en"),
SingleLang("Shield Manga", "https://shieldmanga.club", "en", overrideVersionCode = 2), SingleLang("Shield Manga", "https://shieldmanga.club", "en", overrideVersionCode = 2),
SingleLang("Shinzoo Scan", "https://shinzooscan.xyz", "pt-BR", overrideVersionCode = 1), SingleLang("Shinzoo Scan", "https://shinzooscan.xyz", "pt-BR", overrideVersionCode = 1),
SingleLang("ShoujoHearts", "https://shoujohearts.com", "en", overrideVersionCode = 1), SingleLang("ShoujoHearts", "https://shoujohearts.com", "en", overrideVersionCode = 1),

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.extension" />

View File

@ -0,0 +1,16 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'FuryoSquad'
pkgNameSuffix = 'fr.furyosquad'
extClass = '.FuryoSquad'
extVersionCode = 1
libVersion = '1.2'
}
dependencies {
implementation project(':lib-ratelimit')
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

View File

@ -0,0 +1,254 @@
package eu.kanade.tachiyomi.extension.fr.furyosquad
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
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.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import java.util.concurrent.TimeUnit
class FuryoSquad : ParsedHttpSource() {
override val name = "FuryoSquad"
override val baseUrl = "https://www.furyosquad.com/"
override val lang = "fr"
override val supportsLatest = true
private val rateLimitInterceptor = RateLimitInterceptor(1)
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addNetworkInterceptor(rateLimitInterceptor)
.build()
// Popular
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/mangas", headers)
}
override fun popularMangaSelector() = "div#fs-tous div.fs-card-body"
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
with(element) {
manga.url = select("div.fs-card-img-container a").attr("href")
manga.title = select("span.fs-comic-title a").text()
manga.thumbnail_url = select("div.fs-card-img-container img").attr("abs:src")
}
return manga
}
override fun popularMangaNextPageSelector() = "Not needed"
// Latest
override fun latestUpdatesRequest(page: Int): Request {
return GET(baseUrl, headers)
}
override fun latestUpdatesParse(response: Response): MangasPage {
val document = response.asJsoup()
val mangas = mutableListOf<SManga>()
document.select(latestUpdatesSelector()).map { mangas.add(latestUpdatesFromElement(it)) }
return MangasPage(mangas.distinctBy { it.url }, false)
}
override fun latestUpdatesSelector() = "table.table-striped tr"
override fun latestUpdatesFromElement(element: Element): SManga {
val manga = SManga.create()
with(element) {
manga.url = select("span.fs-comic-title a").attr("href")
manga.title = select("span.fs-comic-title a").text()
manga.thumbnail_url = select("img.fs-chap-img").attr("abs:src")
}
return manga
}
override fun latestUpdatesNextPageSelector() = "not needed"
// Search
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return client.newCall(searchMangaRequest(page, query, filters))
.asObservableSuccess()
.map { response ->
searchMangaParse(response, query)
}
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = popularMangaRequest(1)
private fun searchMangaParse(response: Response, query: String): MangasPage {
return MangasPage(popularMangaParse(response).mangas.filter { it.title.contains(query, ignoreCase = true) }, false)
}
override fun searchMangaSelector() = throw UnsupportedOperationException("Not used")
override fun searchMangaFromElement(element: Element): SManga = throw UnsupportedOperationException("Not used")
override fun searchMangaNextPageSelector() = throw UnsupportedOperationException("Not used")
// Details
override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create()
document.select("div.comic-info").let {
it.select("p.fs-comic-label").forEach { el ->
when (el.text().toLowerCase(Locale.ROOT)) {
"scénario" -> manga.author = el.nextElementSibling().text()
"dessins" -> manga.artist = el.nextElementSibling().text()
"genre" -> manga.genre = el.nextElementSibling().text()
}
}
manga.description = it.select("div.fs-comic-description").text()
manga.thumbnail_url = it.select("img.comic-cover").attr("abs:src")
}
return manga
}
// Chapters
override fun chapterListSelector() = "div.fs-chapter-list div.element"
override fun chapterFromElement(element: Element): SChapter {
val chapter = SChapter.create()
chapter.url = element.select("div.title a").attr("href")
chapter.name = element.select("div.title a").attr("title")
chapter.date_upload = parseChapterDate(element.select("div.meta_r").text())
return chapter
}
private fun parseChapterDate(date: String): Long {
val lcDate = date.toLowerCase(Locale.ROOT)
if (lcDate.startsWith("il y a"))
parseRelativeDate(lcDate).let { return it }
// Handle 'day before yesterday', yesterday' and 'today', using midnight
var relativeDate: Calendar? = null
// Result parsed but no year, copy current year over
when {
lcDate.startsWith("avant-hier") -> {
relativeDate = Calendar.getInstance()
relativeDate.add(Calendar.DAY_OF_MONTH, -2) // day before yesterday
relativeDate.set(Calendar.HOUR_OF_DAY, 0)
relativeDate.set(Calendar.MINUTE, 0)
relativeDate.set(Calendar.SECOND, 0)
relativeDate.set(Calendar.MILLISECOND, 0)
}
lcDate.startsWith("hier") -> {
relativeDate = Calendar.getInstance()
relativeDate.add(Calendar.DAY_OF_MONTH, -1) // yesterday
relativeDate.set(Calendar.HOUR_OF_DAY, 0)
relativeDate.set(Calendar.MINUTE, 0)
relativeDate.set(Calendar.SECOND, 0)
relativeDate.set(Calendar.MILLISECOND, 0)
}
lcDate.startsWith("aujourd'hui") -> {
relativeDate = Calendar.getInstance()
relativeDate.set(Calendar.HOUR_OF_DAY, 0) // today
relativeDate.set(Calendar.MINUTE, 0)
relativeDate.set(Calendar.SECOND, 0)
relativeDate.set(Calendar.MILLISECOND, 0)
}
}
return relativeDate?.timeInMillis ?: 0L
}
private fun parseRelativeDate(date: String): Long {
val value = date.split(" ")[3].toIntOrNull()
return if (value != null) {
when (date.split(" ")[4]) {
"minute", "minutes" -> Calendar.getInstance().apply {
add(Calendar.MINUTE, value * -1)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}.timeInMillis
"heure", "heures" -> Calendar.getInstance().apply {
add(Calendar.HOUR_OF_DAY, value * -1)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}.timeInMillis
"jour", "jours" -> Calendar.getInstance().apply {
add(Calendar.DATE, value * -1)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}.timeInMillis
"semaine", "semaines" -> Calendar.getInstance().apply {
add(Calendar.DATE, value * 7 * -1)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}.timeInMillis
"mois" -> Calendar.getInstance().apply {
add(Calendar.MONTH, value * -1)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}.timeInMillis
"an", "ans", "année", "années" -> Calendar.getInstance().apply {
add(Calendar.YEAR, value * -1)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}.timeInMillis
else -> {
return 0L
}
}
} else {
try {
SimpleDateFormat("dd MMM yyyy", Locale.FRENCH).parse(date.substringAfter("le "))?.time ?: 0
} catch (_: Exception) {
0L
}
}
}
// Pages
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
document.select("div.fs-read img[id]").forEachIndexed { i, img ->
pages.add(Page(i, "", img.attr("abs:src")))
}
return pages
}
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used")
override fun getFilterList() = FilterList()
}

View File

@ -42,15 +42,16 @@ class Kangaryu : ParsedHttpSource() {
return GET("$baseUrl/manga-list", headers) return GET("$baseUrl/manga-list", headers)
} }
override fun popularMangaSelector() = "div.profile-card-2" override fun popularMangaSelector() = "div.l-card"
override fun popularMangaFromElement(element: Element): SManga { override fun popularMangaFromElement(element: Element): SManga {
return SManga.create().apply { return SManga.create().apply {
element.select("div.profile-name a").let { element.select("div.card-image").let {
setUrlWithoutDomain(it.attr("href")) setUrlWithoutDomain(it.select("a").attr("href"))
title = it.text() thumbnail_url = it.select("img").attr("abs:src")
} }
thumbnail_url = element.select("img").attr("abs:src")
title = element.select("a.chart-title").text()
} }
} }
@ -114,12 +115,13 @@ class Kangaryu : ParsedHttpSource() {
artist = select("dd a[href*=artist]").text() artist = select("dd a[href*=artist]").text()
genre = select("dd a[href*=category]").joinToString { it.text() } genre = select("dd a[href*=category]").joinToString { it.text() }
} }
description = document.select("div.col-lg-12 p").text()
} }
} }
private fun String.toStatus() = when { private fun String.toStatus() = when {
this.contains("Ongoing", ignoreCase = true) -> SManga.ONGOING this.contains("En cours", ignoreCase = true) -> SManga.ONGOING
this.contains("Completed", ignoreCase = true) -> SManga.COMPLETED this.contains("Terminé", ignoreCase = true) -> SManga.COMPLETED
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
@ -129,7 +131,7 @@ class Kangaryu : ParsedHttpSource() {
override fun chapterFromElement(element: Element): SChapter { override fun chapterFromElement(element: Element): SChapter {
return SChapter.create().apply { return SChapter.create().apply {
name = element.select("h5").text() name = element.select("h5.chapter-title-rtl").text()
setUrlWithoutDomain(element.select("h5 a").attr("href")) setUrlWithoutDomain(element.select("h5 a").attr("href"))
date_upload = element.select("div.date-chapter-title-rtl").text().toDate() date_upload = element.select("div.date-chapter-title-rtl").text().toDate()
} }