Add new source "Frelein Books" (#16708)

* Add frelein books

add new source from books.frelein.my.id

* Update src/all/freleinbooks/src/eu/kanade/tachiyomi/extension/all/freleinbooks/FreleinBooks.kt

Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>

* Update src/all/freleinbooks/src/eu/kanade/tachiyomi/extension/all/freleinbooks/FreleinBooks.kt

Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>

---------

Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
This commit is contained in:
Yusuf Bhaskara Adji 2023-06-17 21:02:00 +07:00 committed by GitHub
parent 4309aff8f5
commit a84ba89743
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 285 additions and 0 deletions

View File

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

View File

@ -0,0 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'Frelein Books'
pkgNameSuffix = 'all.freleinbooks'
extClass = '.FreleinBooks'
extVersionCode = 1
isNsfw = false
}
apply from: "$rootDir/common.gradle"

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: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,271 @@
package eu.kanade.tachiyomi.extension.all.freleinbooks
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
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 okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
class FreleinBooks() : ParsedHttpSource() {
override val baseUrl = "https://books.frelein.my.id"
override val lang = "all"
override val name = "Frelein Books"
override val supportsLatest = true
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
private val Element.imgSrc: String
get() = attr("data-lazy-src")
.ifEmpty { attr("data-src") }
.ifEmpty { attr("src") }
// Latest
override fun latestUpdatesFromElement(element: Element): SManga {
val manga = SManga.create()
manga.thumbnail_url = element.selectFirst("img")!!.imgSrc
manga.title = element.select(".postTitle").text()
manga.setUrlWithoutDomain(element.select(".postTitle > a").attr("abs:href"))
return manga
}
override fun latestUpdatesNextPageSelector() = ".olderLink"
override fun latestUpdatesRequest(page: Int): Request {
return if (page == 1) {
GET(baseUrl)
} else {
val dateParam = page * 7 * 2
// Calendar set to the current date
val calendar: Calendar = Calendar.getInstance()
// rollback 14 days
calendar.add(Calendar.DAY_OF_YEAR, -dateParam)
val formatter = SimpleDateFormat("yyyy-MM-dd", Locale.US)
// now the date is 14 days back
GET("$baseUrl/search?updated-max=${formatter.format(calendar.time)}T12:38:00%2B07:00&max-results=12&start=12&by-date=false")
}
}
override fun latestUpdatesSelector() = ".blogPosts > article"
// Popular
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
manga.thumbnail_url = element.selectFirst("img")!!.imgSrc
manga.title = element.select("h3").text()
manga.setUrlWithoutDomain(element.select("h3 > a").attr("abs:href"))
return manga
}
override fun popularMangaNextPageSelector(): String? = null
override fun popularMangaRequest(page: Int) = latestUpdatesRequest(page)
override fun popularMangaSelector() = ".itemPopulars article"
// Search
override fun searchMangaFromElement(element: Element) = latestUpdatesFromElement(element)
override fun searchMangaNextPageSelector() = latestUpdatesNextPageSelector()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val filterList = if (filters.isEmpty()) getFilterList() else filters
val tagFilter = filterList.findInstance<TagFilter>()!!
val groupFilter = filterList.findInstance<GroupFilter>()!!
val magazineFilter = filterList.findInstance<MagazineFilter>()!!
val fashionMagazineFilter = filterList.findInstance<FashionMagazineFilter>()!!
return when {
query.isEmpty() && groupFilter.state != 0 -> GET("$baseUrl/search/label/${groupFilter.toUriPart()}")
query.isEmpty() && magazineFilter.state != 0 -> GET("$baseUrl/search/label/${magazineFilter.toUriPart()}")
query.isEmpty() && fashionMagazineFilter.state != 0 -> GET("$baseUrl/search/label/${fashionMagazineFilter.toUriPart()}")
query.isEmpty() && tagFilter.state.isNotEmpty() -> GET("$baseUrl/search/label/${tagFilter.state}")
query.isNotEmpty() -> GET("$baseUrl/search?q=$query")
else -> latestUpdatesRequest(page)
}
}
override fun searchMangaSelector() = latestUpdatesSelector()
// Details
override fun mangaDetailsParse(document: Document): SManga {
val manga = SManga.create()
manga.title = document.select(".postTitle").text()
manga.description = "Read ${document.select(".postTitle").text()} \n \nNote: If you encounters error when opening the magazine, please press the WebView button then leave a comment on our web so we can update it soon."
manga.genre = document.select(".labelLink > a")
.joinToString(", ") { it.text() }
manga.status = SManga.COMPLETED
return manga
}
override fun chapterFromElement(element: Element): SChapter {
val chapter = SChapter.create()
chapter.setUrlWithoutDomain(element.select("link[rel=\"canonical\"]").attr("href"))
chapter.name = "Gallery"
chapter.date_upload = getDate(element.select("link[rel=\"canonical\"]").attr("href"))
return chapter
}
override fun chapterListSelector() = "html"
// Pages
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
document.select("noscript").remove()
document.select(".gallerybox a > img").forEachIndexed { i, it ->
// format new img/b/
if (it.imgSrc.contains("img/b/")) {
if (it.imgSrc.contains("/w768-rw/")) {
val itUrl = it.imgSrc.replace("/w768-rw/", "/s0/")
pages.add(Page(i, itUrl, itUrl))
}
if (it.imgSrc.contains("/w480-rw/")) {
val itUrl = it.imgSrc.replace("/w480-rw/", "/s0/")
pages.add(Page(i, itUrl, itUrl))
}
}
// format new img/b/
else {
if (it.imgSrc.contains("=w768-rw")) {
val itUrl = it.imgSrc.replace("=w768-rw", "")
pages.add(Page(i, itUrl, itUrl))
} else if (it.imgSrc.contains("=w480-rw")) {
val itUrl = it.imgSrc.replace("=w480-rw", "")
pages.add(Page(i, itUrl, itUrl))
} else {
val itUrl = it.imgSrc
pages.add(Page(i, itUrl, itUrl))
}
}
}
return pages
}
override fun imageUrlParse(document: Document): String =
throw UnsupportedOperationException("Not used")
// Filters
override fun getFilterList(): FilterList = FilterList(
Filter.Header("NOTE: Only one filter will be applied!"),
Filter.Separator(),
GroupFilter(),
MagazineFilter(),
FashionMagazineFilter(),
TagFilter(),
)
open class UriPartFilter(
displayName: String,
private val valuePair: Array<Pair<String, String>>,
) : Filter.Select<String>(displayName, valuePair.map { it.first }.toTypedArray()) {
fun toUriPart() = valuePair[state].second
}
class MagazineFilter : UriPartFilter(
"Magazine",
arrayOf(
Pair("Any", ""),
Pair("B.L.T.", "B.L.T."),
Pair("BIG ONE GIRLS", "BIG ONE GIRLS"),
Pair("BOMB!", "BOMB!"),
Pair("BRODY", "BRODY"),
Pair("BUBKA", "BUBKA"),
Pair("ENTAME", "ENTAME"),
Pair("EX Taishu", "EX Taishu"),
Pair("FINEBOYS", "FINEBOYS"),
Pair("FLASH", "FLASH"),
Pair("Fine", "Fine"),
Pair("Friday", "Friday"),
Pair("HINA_SATSU", "HINA_SATSU"),
Pair("IDOL AND READ", "IDOL AND READ"),
Pair("Kadokawa Scene 07", "Kadokawa Scene 07"),
Pair("Monthly Basketball", "Monthly Basketball"),
Pair("Monthly Young Magazine", "Monthly Young Magazine"),
Pair("NOGI_SATSU", "NOGI_SATSU"),
Pair("Nylon Japan", "Nylon Japan"),
Pair("Platinum FLASH", "Platinum FLASH"),
Pair("Shonen Magazine", "Shonen Magazine"),
Pair("Shukan Post", "Shukan Post"),
Pair("TOKYO NEWS MOOK", "TOKYO NEWS MOOK"),
Pair("TV LIFE,Tarzan", "TV LIFE,Tarzan"),
Pair("Tokyo Calendar", "Tokyo Calendar"),
Pair("Top Yell NEO", "Top Yell NEO"),
Pair("UTB", "UTB"),
Pair("Weekly Playboy", "Weekly Playboy"),
Pair("Weekly SPA", "Weekly SPA"),
Pair("Weekly SPA!", "Weekly SPA!"),
Pair("Weekly Shonen Champion", "Weekly Shonen Champion"),
Pair("Weekly Shonen Magazine", "Weekly Shonen Magazine"),
Pair("Weekly Shonen Sunday", "Weekly Shonen Sunday"),
Pair("Weekly Shounen Magazine", "Weekly Shounen Magazine"),
Pair("Weekly The Television Plus", "Weekly The Television Plus"),
Pair("Weekly Zero Jump", "Weekly Zero Jump"),
Pair("Yanmaga Web", "Yanmaga Web"),
Pair("Young Animal", "Young Animal"),
Pair("Young Champion", "Young Champion"),
Pair("Young Gangan", "Young Gangan"),
Pair("Young Jump", "Young Jump"),
Pair("Young Magazine", "Young Magazine"),
Pair("blt graph.", "blt graph."),
Pair("mini", "mini"),
),
)
class FashionMagazineFilter : UriPartFilter(
"Fashion Magazine",
arrayOf(
Pair("Any", ""),
Pair("BAILA", "BAILA"),
Pair("Biteki", "Biteki"),
Pair("CLASSY", "CLASSY"),
Pair("CanCam", "CanCam"),
Pair("JJ", "JJ"),
Pair("LARME", "LARME"),
Pair("MARQUEE", "MARQUEE"),
Pair("Maquia", "Maquia"),
Pair("Men's non-no", "Men's non-no"),
Pair("More", "More"),
Pair("Oggi", "Oggi"),
Pair("Ray", "Ray"),
Pair("Seventeen", "Seventeen"),
Pair("Sweet", "Sweet"),
Pair("VOCE", "VOCE"),
Pair("ViVi", "ViVi"),
Pair("With", "With"),
Pair("aR", "aR"),
Pair("anan", "anan"),
Pair("bis", "bis"),
Pair("non-no", "non-no"),
),
)
class GroupFilter : UriPartFilter(
"Group",
arrayOf(
Pair("Any", ""),
Pair("Hinatazaka46", "Hinatazaka46"),
Pair("Nogizaka46", "Nogizaka46"),
Pair("Sakurazaka46", "Sakurazaka46"),
Pair("Keyakizaka46", "Keyakizaka46"),
),
)
class TagFilter : Filter.Text("Tag")
private inline fun <reified T> Iterable<*>.findInstance() = find { it is T } as? T
private fun getDate(str: String): Long {
val regex = "[0-9]{4}\\/[0-9]{2}\\/[0-9]{2}".toRegex()
val match = regex.find(str)
return runCatching { DATE_FORMAT.parse(match!!.value)?.time }.getOrNull() ?: 0L
}
companion object {
private val DATE_FORMAT by lazy {
SimpleDateFormat("yyyy/MM/dd", Locale.US)
}
}
}