FlixScans: rewrite for new site (#3808)

* FlixScans: rewrite for new site

* remove log

* filters

* remove commented

* dates

* rebrand, remove multisrc
This commit is contained in:
AwkwardPeak7 2024-07-02 11:46:07 +05:00 committed by Draff
parent 5942e0944e
commit b5b65b7be4
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
27 changed files with 381 additions and 522 deletions

View File

@ -1,5 +0,0 @@
plugins {
id("lib-multisrc")
}
baseVersionCode = 6

View File

@ -1,251 +0,0 @@
package eu.kanade.tachiyomi.multisrc.flixscans
import android.util.Log
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimit
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 kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Call
import okhttp3.Callback
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.injectLazy
import java.io.IOException
abstract class FlixScans(
override val name: String,
override val baseUrl: String,
override val lang: String,
protected val apiUrl: String = "$baseUrl/api/v1",
protected val cdnUrl: String = baseUrl.replace("://", "://media.").plus("/"),
) : HttpSource() {
override val supportsLatest = true
protected open val json: Json by injectLazy()
override val client = network.cloudflareClient.newBuilder()
.rateLimit(2)
.build()
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
override fun popularMangaRequest(page: Int): Request {
return GET("$apiUrl/webtoon/pages/home/romance", headers)
}
override fun popularMangaParse(response: Response): MangasPage {
val result = response.parseAs<HomeDto>()
val entries = (result.hot + result.topAll + result.topMonth + result.topWeek)
.distinctBy { it.id }
.map { it.toSManga(cdnUrl) }
return MangasPage(entries, false)
}
override fun latestUpdatesRequest(page: Int): Request {
return GET("$apiUrl/search/advance?page=$page&serie_type=webtoon", headers)
}
override fun latestUpdatesParse(response: Response): MangasPage {
val result = response.parseAs<ApiResponse<BrowseSeries>>()
val entries = result.data.map { it.toSManga(cdnUrl) }
val hasNextPage = result.lastPage > result.currentPage
return MangasPage(entries, hasNextPage)
}
private var fetchGenreList: List<GenreHolder> = emptyList()
private var fetchGenreCallOngoing = false
private var fetchGenreFailed = false
private var fetchGenreAttempt = 0
private fun fetchGenre() {
if (fetchGenreAttempt < 3 && (fetchGenreList.isEmpty() || fetchGenreFailed) && !fetchGenreCallOngoing) {
fetchGenreCallOngoing = true
// fetch genre asynchronously as it sometimes hangs
client.newCall(fetchGenreRequest()).enqueue(fetchGenreCallback)
}
}
private val fetchGenreCallback = object : Callback {
override fun onFailure(call: Call, e: IOException) {
fetchGenreAttempt++
fetchGenreFailed = true
fetchGenreCallOngoing = false
e.message?.let { Log.e("$name Filters", it) }
}
override fun onResponse(call: Call, response: Response) {
fetchGenreCallOngoing = false
fetchGenreAttempt++
if (!response.isSuccessful) {
fetchGenreFailed = true
response.close()
return
}
val parsed = runCatching {
response.use(::fetchGenreParse)
}
fetchGenreFailed = parsed.isFailure
fetchGenreList = parsed.getOrElse {
Log.e("$name Filters", it.stackTraceToString())
emptyList()
}
}
}
private fun fetchGenreRequest(): Request {
return GET("$apiUrl/search/genres", headers)
}
private fun fetchGenreParse(response: Response): List<GenreHolder> {
return response.parseAs<List<GenreHolder>>()
}
override fun getFilterList(): FilterList {
fetchGenre()
val filters: MutableList<Filter<*>> = mutableListOf(
Filter.Header("Ignored when using Text Search"),
MainGenreFilter(),
TypeFilter(),
StatusFilter(),
)
filters += if (fetchGenreList.isNotEmpty()) {
listOf(
GenreFilter("Genre", fetchGenreList),
)
} else {
listOf(
Filter.Separator(),
Filter.Header("Press 'reset' to attempt to show Genres"),
)
}
return FilterList(filters)
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.isNotEmpty()) {
val url = "$apiUrl/search/serie".toHttpUrl().newBuilder()
.addPathSegment(query.trim())
.addQueryParameter("page", page.toString())
.build()
return GET(url, headers)
}
val advSearchUrl = apiUrl.toHttpUrl().newBuilder().apply {
addPathSegments("search/advance")
addQueryParameter("page", page.toString())
addQueryParameter("serie_type", "webtoon")
filters.forEach { filter ->
when (filter) {
is GenreFilter -> {
filter.checked.let {
if (it.isNotEmpty()) {
addQueryParameter("genres", it.joinToString(","))
}
}
}
is MainGenreFilter -> {
if (filter.state > 0) {
addQueryParameter("main_genres", filter.selected)
}
}
is TypeFilter -> {
if (filter.state > 0) {
addQueryParameter("type", filter.selected)
}
}
is StatusFilter -> {
if (filter.state > 0) {
addQueryParameter("status", filter.selected)
}
}
else -> {}
}
}
}.build()
return GET(advSearchUrl, headers)
}
override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
override fun mangaDetailsRequest(manga: SManga): Request {
val (prefix, id) = getPrefixIdFromUrl(manga.url)
return GET("$apiUrl/webtoon/series/$id/$prefix", headers)
}
override fun getMangaUrl(manga: SManga) = baseUrl + manga.url
override fun mangaDetailsParse(response: Response): SManga {
val result = response.parseAs<SeriesResponse>()
return result.serie.toSManga(cdnUrl)
}
override fun chapterListRequest(manga: SManga): Request {
val (prefix, id) = getPrefixIdFromUrl(manga.url)
return GET("$apiUrl/webtoon/chapters/$id-desc#$prefix", headers)
}
override fun chapterListParse(response: Response): List<SChapter> {
val chapters = response.parseAs<List<Chapter>>()
val prefix = response.request.url.fragment!!
return chapters.map { it.toSChapter(prefix) }
}
override fun pageListRequest(chapter: SChapter): Request {
val (prefix, id) = getPrefixIdFromUrl(chapter.url)
return GET("$apiUrl/webtoon/chapters/chapter/$id/$prefix", headers)
}
protected fun getPrefixIdFromUrl(url: String): Pair<String, String> {
return with(url.substringAfterLast("/")) {
val split = split("-")
split[0] to split[1]
}
}
override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url
override fun pageListParse(response: Response): List<Page> {
val result = response.parseAs<PageListResponse>()
return result.chapter.chapterData.webtoon.mapIndexed { i, img ->
Page(i, "", cdnUrl + img)
}
}
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException()
protected inline fun <reified T> Response.parseAs(): T =
use { body.string() }.let(json::decodeFromString)
}

View File

@ -1,142 +0,0 @@
package eu.kanade.tachiyomi.multisrc.flixscans
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.jsoup.Jsoup
import java.text.SimpleDateFormat
import java.util.Locale
@Serializable
data class ApiResponse<T>(
val data: List<T>,
@SerialName("current_page") val currentPage: Int,
@SerialName("last_page") val lastPage: Int,
)
@Serializable
data class HomeDto(
val hot: List<BrowseSeries>,
val topWeek: List<BrowseSeries>,
val topMonth: List<BrowseSeries>,
val topAll: List<BrowseSeries>,
)
@Serializable
data class BrowseSeries(
val id: Int,
val title: String,
val slug: String,
val prefix: Int,
val thumbnail: String?,
) {
fun toSManga(cdnUrl: String) = SManga.create().apply {
title = this@BrowseSeries.title
url = "/series/$prefix-$id-$slug"
thumbnail_url = thumbnail?.let { cdnUrl + it }
}
}
@Serializable
data class SearchInput(
val title: String,
)
@Serializable
data class GenreHolder(
val name: String,
val id: Int,
)
@Serializable
data class SeriesResponse(
val serie: Series,
)
@Serializable
data class Series(
val id: Int,
val title: String,
val slug: String,
val prefix: Int,
val thumbnail: String?,
val story: String?,
val serieType: String?,
val mainGenres: String?,
val otherNames: List<String>? = emptyList(),
val status: String?,
val type: String?,
val authors: List<GenreHolder>? = emptyList(),
val artists: List<GenreHolder>? = emptyList(),
val genres: List<GenreHolder>? = emptyList(),
) {
fun toSManga(cdnUrl: String) = SManga.create().apply {
title = this@Series.title
url = "/series/$prefix-$id-$slug"
thumbnail_url = cdnUrl + thumbnail
author = authors?.joinToString { it.name.trim() }
artist = artists?.joinToString { it.name.trim() }
genre = (otherGenres + genres?.map { it.name.trim() }.orEmpty())
.distinct().joinToString { it.trim() }
description = story?.let { Jsoup.parse(it).text() }
if (otherNames?.isNotEmpty() == true) {
if (description.isNullOrEmpty()) {
description = "Alternative Names:\n"
} else {
description += "\n\nAlternative Names:\n"
}
description += otherNames.joinToString("\n") { "${it.trim()}" }
}
status = when (this@Series.status?.trim()) {
"ongoing" -> SManga.ONGOING
"completed" -> SManga.COMPLETED
"onhold" -> SManga.ON_HIATUS
else -> SManga.UNKNOWN
}
}
private val otherGenres = listOfNotNull(serieType, mainGenres, type)
.map { word ->
word.trim().replaceFirstChar {
if (it.isLowerCase()) {
it.titlecase(Locale.getDefault())
} else {
it.toString()
}
}
}
}
@Serializable
data class Chapter(
val id: Int,
val name: String,
val slug: String,
val createdAt: String? = null,
) {
fun toSChapter(prefix: String) = SChapter.create().apply {
url = "/read/webtoon/$prefix-$id-$slug"
name = this@Chapter.name
date_upload = runCatching { dateFormat.parse(createdAt!!)!!.time }.getOrDefault(0L)
}
companion object {
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'", Locale.ENGLISH)
}
}
@Serializable
data class PageListResponse(
val chapter: ChapterPages,
)
@Serializable
data class ChapterPages(
val chapterData: ChapterPageData,
)
@Serializable
data class ChapterPageData(
val webtoon: List<String>,
)

View File

@ -1,62 +0,0 @@
package eu.kanade.tachiyomi.multisrc.flixscans
import eu.kanade.tachiyomi.source.model.Filter
abstract class SelectFilter(
name: String,
private val options: List<String>,
) : Filter.Select<String>(
name,
options.toTypedArray(),
) {
val selected get() = options[state]
}
class CheckBoxFilter(
name: String,
val id: String,
) : Filter.CheckBox(name)
class GenreFilter(
name: String,
private val genres: List<GenreHolder>,
) : Filter.Group<CheckBoxFilter>(
name,
genres.map { CheckBoxFilter(it.name.trim(), it.id.toString()) },
) {
val checked get() = state.filter { it.state }.map { it.id }
}
class MainGenreFilter : SelectFilter(
"Main Genre",
listOf(
"",
"fantasy",
"romance",
"action",
"drama",
),
)
class TypeFilter : SelectFilter(
"Type",
listOf(
"",
"manhwa",
"manhua",
"manga",
"comic",
),
)
class StatusFilter : SelectFilter(
"Status",
listOf(
"",
"ongoing",
"completed",
"droped",
"onhold",
"soon",
),
)

View File

@ -0,0 +1,7 @@
ext {
extName = 'Galaxy'
extClass = '.GalaxyFactory'
extVersionCode = 1
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,327 @@
package eu.kanade.tachiyomi.extension.all.galaxy
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimit
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.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import java.util.Calendar
abstract class Galaxy(
override val name: String,
override val baseUrl: String,
override val lang: String,
) : HttpSource() {
override val supportsLatest = true
override val client = network.cloudflareClient.newBuilder()
.rateLimit(2)
.build()
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
override fun popularMangaRequest(page: Int): Request {
return if (page == 1) {
GET("$baseUrl/webtoons/romance/home", headers)
} else {
GET("$baseUrl/webtoons/action/home", headers)
}
}
override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
val entries = document.select(
"""div.tabs div[wire:snapshot*=App\\Models\\Serie], main div:has(h2:matches(Today\'s Hot|الرائج اليوم)) a[wire:snapshot*=App\\Models\\Serie]""",
).map { element ->
SManga.create().apply {
setUrlWithoutDomain(
if (element.tagName().equals("a")) {
element.absUrl("href")
} else {
element.selectFirst("a")!!.absUrl("href")
},
)
thumbnail_url = element.selectFirst("img")?.absUrl("src")
title = element.selectFirst("div.text-sm")!!.text()
}
}.distinctBy { it.url }
return MangasPage(entries, response.request.url.pathSegments.getOrNull(1) == "romance")
}
override fun latestUpdatesRequest(page: Int): Request {
val url = "$baseUrl/latest?serie_type=webtoon&main_genres=romance" +
if (page > 1) {
"&page=$page"
} else {
""
}
return GET(url, headers)
}
override fun latestUpdatesParse(response: Response): MangasPage {
val document = response.asJsoup()
val entries = document.select("div[wire:snapshot*=App\\\\Models\\\\Serie]").map { element ->
SManga.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href"))
thumbnail_url = element.selectFirst("img")?.absUrl("src")
title = element.select("div.flex a[href*=/series/]").last()!!.text()
}
}
val hasNextPage = document.selectFirst("[role=navigation] button[wire:click*=nextPage]") != null
return MangasPage(entries, hasNextPage)
}
private var filters: List<FilterData> = emptyList()
private val scope = CoroutineScope(Dispatchers.IO)
protected fun launchIO(block: () -> Unit) = scope.launch {
try {
block()
} catch (_: Exception) { }
}
override fun getFilterList(): FilterList {
launchIO {
if (filters.isEmpty()) {
val document = client.newCall(GET("$baseUrl/search", headers)).execute().asJsoup()
val mainGenre = FilterData(
displayName = document.select("label[for$=main_genres]").text(),
options = document.select("select[wire:model.live=main_genres] option").map {
it.text() to it.attr("value")
},
queryParameter = "main_genres",
)
val typeFilter = FilterData(
displayName = document.select("label[for$=type]").text(),
options = document.select("select[wire:model.live=type] option").map {
it.text() to it.attr("value")
},
queryParameter = "type",
)
val statusFilter = FilterData(
displayName = document.select("label[for$=status]").text(),
options = document.select("select[wire:model.live=status] option").map {
it.text() to it.attr("value")
},
queryParameter = "status",
)
val genreFilter = FilterData(
displayName = if (lang == "ar") {
"التصنيفات"
} else {
"Genre"
},
options = document.select("div[x-data*=genre] > div").map {
it.text() to it.attr("wire:key")
},
queryParameter = "genre",
)
filters = listOf(mainGenre, typeFilter, statusFilter, genreFilter)
}
}
val filters: List<Filter<*>> = filters.map {
SelectFilter(
it.displayName,
it.options,
it.queryParameter,
)
}.ifEmpty {
listOf(
Filter.Header("Press 'reset' to load filters"),
)
}
return FilterList(filters)
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/search".toHttpUrl().newBuilder().apply {
addQueryParameter("serie_type", "webtoon")
addQueryParameter("title", query.trim())
filters.filterIsInstance<SelectFilter>().forEach {
it.addFilterParameter(this)
}
if (page > 1) {
addQueryParameter("page", page.toString())
}
}.build()
return GET(url, headers)
}
override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
override fun mangaDetailsParse(response: Response): SManga {
val document = response.asJsoup()
return SManga.create().apply {
title = document.select("#full_model h3").text()
thumbnail_url = document.selectFirst("main img[src*=series/webtoon]")?.absUrl("src")
status = when (document.getQueryParam("status")) {
"ongoing", "soon" -> SManga.ONGOING
"completed", "droped" -> SManga.COMPLETED
"onhold" -> SManga.ON_HIATUS
else -> SManga.UNKNOWN
}
genre = buildList {
document.getQueryParam("type")
?.capitalize()?.let(::add)
document.select("#full_model a[href*=search?genre]")
.eachText().let(::addAll)
}.joinToString()
author = document.select("#full_model [wire:key^=a-]").eachText().joinToString()
artist = document.select("#full_model [wire:key^=r-]").eachText().joinToString()
description = buildString {
append(document.select("#full_model p").text().trim())
append("\n\nAlternative Names:\n")
document.select("#full_model [wire:key^=n-]")
.joinToString("\n") { "${it.text().trim().removeMdEscaped()}" }
.let(::append)
}.trim()
}
}
private fun Document.getQueryParam(queryParam: String): String? {
return selectFirst("#full_model a[href*=search?$queryParam]")
?.absUrl("href")?.toHttpUrlOrNull()?.queryParameter(queryParam)
}
private fun String.capitalize(): String {
val result = StringBuilder(length)
var capitalize = true
for (char in this) {
result.append(
if (capitalize) {
char.uppercase()
} else {
char.lowercase()
},
)
capitalize = char.isWhitespace()
}
return result.toString()
}
private val mdRegex = Regex("""&amp;#(\d+);""")
private fun String.removeMdEscaped(): String {
val char = mdRegex.find(this)?.groupValues?.get(1)?.toIntOrNull()
?: return this
return replaceFirst(mdRegex, Char(char).toString())
}
override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup()
return document.select("a[href*=/read/]:not([type=button])").map { element ->
SChapter.create().apply {
setUrlWithoutDomain(element.absUrl("href"))
name = element.select("span.font-normal").text()
date_upload = element.selectFirst("div:not(:has(> svg)) > span.text-xs")
?.text().parseRelativeDate()
}
}
}
protected open fun String?.parseRelativeDate(): Long {
this ?: return 0L
val number = Regex("""(\d+)""").find(this)?.value?.toIntOrNull() ?: 0
val cal = Calendar.getInstance()
return when {
listOf("second", "ثانية").any { contains(it, true) } -> {
cal.apply { add(Calendar.SECOND, -number) }.timeInMillis
}
contains("دقيقتين", true) -> {
cal.apply { add(Calendar.MINUTE, -2) }.timeInMillis
}
listOf("minute", "دقائق").any { contains(it, true) } -> {
cal.apply { add(Calendar.MINUTE, -number) }.timeInMillis
}
contains("ساعتان", true) -> {
cal.apply { add(Calendar.HOUR, -2) }.timeInMillis
}
listOf("hour", "ساعات").any { contains(it, true) } -> {
cal.apply { add(Calendar.HOUR, -number) }.timeInMillis
}
contains("يوم", true) -> {
cal.apply { add(Calendar.DAY_OF_YEAR, -1) }.timeInMillis
}
contains("يومين", true) -> {
cal.apply { add(Calendar.DAY_OF_YEAR, -2) }.timeInMillis
}
listOf("day", "أيام").any { contains(it, true) } -> {
cal.apply { add(Calendar.DAY_OF_YEAR, -number) }.timeInMillis
}
contains("أسبوع", true) -> {
cal.apply { add(Calendar.WEEK_OF_YEAR, -1) }.timeInMillis
}
contains("أسبوعين", true) -> {
cal.apply { add(Calendar.WEEK_OF_YEAR, -2) }.timeInMillis
}
listOf("week", "أسابيع").any { contains(it, true) } -> {
cal.apply { add(Calendar.WEEK_OF_YEAR, -number) }.timeInMillis
}
contains("شهر", true) -> {
cal.apply { add(Calendar.MONTH, -1) }.timeInMillis
}
contains("شهرين", true) -> {
cal.apply { add(Calendar.MONTH, -2) }.timeInMillis
}
listOf("month", "أشهر").any { contains(it, true) } -> {
cal.apply { add(Calendar.MONTH, -number) }.timeInMillis
}
contains("سنة", true) -> {
cal.apply { add(Calendar.YEAR, -1) }.timeInMillis
}
contains("سنتان", true) -> {
cal.apply { add(Calendar.YEAR, -2) }.timeInMillis
}
listOf("year", "سنوات").any { contains(it, true) } -> {
cal.apply { add(Calendar.YEAR, -number) }.timeInMillis
}
else -> 0L
}
}
override fun pageListParse(response: Response): List<Page> {
val document = response.asJsoup()
return document.select("[wire:key^=image] img").mapIndexed { idx, img ->
Page(idx, imageUrl = img.absUrl("src"))
}
}
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException()
}

View File

@ -0,0 +1,19 @@
package eu.kanade.tachiyomi.extension.all.galaxy
import eu.kanade.tachiyomi.source.SourceFactory
class GalaxyFactory : SourceFactory {
class GalaxyWebtoon : Galaxy("Galaxy Webtoon", "https://galaxyaction.net", "en") {
override val id = 2602904659965278831
}
class GalaxyManga : Galaxy("Galaxy Manga", "https://galaxymanga.net", "ar") {
override val id = 2729515745226258240
}
override fun createSources() = listOf(
GalaxyWebtoon(),
GalaxyManga(),
)
}

View File

@ -0,0 +1,28 @@
package eu.kanade.tachiyomi.extension.all.galaxy
import eu.kanade.tachiyomi.source.model.Filter
import okhttp3.HttpUrl
class SelectFilter(
name: String,
private val options: List<Pair<String, String>>,
private val queryParam: String,
) : Filter.Select<String>(
name,
buildList {
add("")
addAll(options.map { it.first })
}.toTypedArray(),
) {
fun addFilterParameter(url: HttpUrl.Builder) {
if (state == 0) return
url.addQueryParameter(queryParam, options[state - 1].second)
}
}
class FilterData(
val displayName: String,
val options: List<Pair<String, String>>,
val queryParameter: String,
)

View File

@ -1,9 +0,0 @@
ext {
extName = 'Galaxy Manga'
extClass = '.GalaxyManga'
themePkg = 'flixscans'
baseUrl = 'https://flixscans.net'
overrideVersionCode = 28
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@ -1,34 +0,0 @@
package eu.kanade.tachiyomi.extension.ar.galaxymanga
import eu.kanade.tachiyomi.multisrc.flixscans.FlixScans
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.Request
class GalaxyManga : FlixScans(
"جالاكسي مانجا",
"https://flixscans.net",
"ar",
"https://ar.flixscans.site/api/v1",
) {
override val versionId = 2
override fun mangaDetailsRequest(manga: SManga): Request {
val (prefix, id) = getPrefixIdFromUrl(manga.url)
return GET("$apiUrl/series/$id/$prefix", headers)
}
override fun chapterListRequest(manga: SManga): Request {
val (prefix, id) = getPrefixIdFromUrl(manga.url)
return GET("$apiUrl/chapters/$id-desc#$prefix", headers)
}
override fun pageListRequest(chapter: SChapter): Request {
val (prefix, id) = getPrefixIdFromUrl(chapter.url)
return GET("$apiUrl/chapters/webtoon/$id/$prefix", headers)
}
}

View File

@ -1,9 +0,0 @@
ext {
extName = 'Flix Scans'
extClass = '.FlixScansNet'
themePkg = 'flixscans'
baseUrl = 'https://flixscans.org'
overrideVersionCode = 0
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,10 +0,0 @@
package eu.kanade.tachiyomi.extension.en.flixscans
import eu.kanade.tachiyomi.multisrc.flixscans.FlixScans
class FlixScansNet : FlixScans(
"Flix Scans",
"https://flixscans.org",
"en",
"https://flixscans.site/api/v1",
)