Zero Scans migrate to new site. (#11358)
* Initial Commit * Update * Add space in extension name. * Review updates
@ -13,7 +13,6 @@ class GenkanGenerator : ThemeSourceGenerator {
|
||||
|
||||
override val sources = listOf(
|
||||
SingleLang("Hunlight Scans", "https://hunlight-scans.info", "en"),
|
||||
SingleLang("ZeroScans", "https://zeroscans.com", "en"),
|
||||
SingleLang("The Nonames Scans", "https://the-nonames.com", "en"),
|
||||
SingleLang("Edelgarde Scans", "https://edelgardescans.com", "en"),
|
||||
SingleLang("LynxScans", "https://lynxscans.com", "en", overrideVersionCode = 3),
|
||||
|
2
src/en/zeroscans/AndroidManifest.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="eu.kanade.tachiyomi.extension" />
|
3
src/en/zeroscans/CHANGELOG.md
Normal file
@ -0,0 +1,3 @@
|
||||
## 1.2.4
|
||||
|
||||
Migrated to the new site.
|
12
src/en/zeroscans/build.gradle
Normal file
@ -0,0 +1,12 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
ext {
|
||||
extName = 'Zero Scans'
|
||||
pkgNameSuffix = 'en.zeroscans'
|
||||
extClass = '.ZeroScans'
|
||||
extVersionCode = 4
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
@ -0,0 +1,349 @@
|
||||
package eu.kanade.tachiyomi.extension.en.zeroscans
|
||||
|
||||
import android.util.Log
|
||||
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.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.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import rx.Observable
|
||||
import rx.Single
|
||||
import rx.schedulers.Schedulers
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class ZeroScans : HttpSource() {
|
||||
|
||||
override val name: String = "Zero Scans"
|
||||
|
||||
override val lang: String = "en"
|
||||
|
||||
override val baseUrl: String = "https://beta.zeroscans.com"
|
||||
|
||||
override val supportsLatest: Boolean = true
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
private lateinit var comicList: List<ZeroScansComicDto>
|
||||
|
||||
private lateinit var rankings: ZeroScansRankingsDto
|
||||
|
||||
private val zsHelper = ZeroScansHelper()
|
||||
|
||||
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
|
||||
if (page == 1) runCatching { updateComicsData() }
|
||||
return super.fetchLatestUpdates(page)
|
||||
}
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request {
|
||||
return GET("$baseUrl/$API_PATH/new-chapters")
|
||||
}
|
||||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||
val newChapters = response.parseAs<NewChaptersResponseDto>()
|
||||
|
||||
val titlesList = newChapters.all.mapNotNull {
|
||||
comicList.firstOrNull { comic -> comic.slug == it.slug }
|
||||
}.map { comic -> zsHelper.zsComicEntryToSManga(comic) }
|
||||
|
||||
return MangasPage(titlesList, false)
|
||||
}
|
||||
|
||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
||||
return fetchSearchManga(page, query = "", filters = getFilterList())
|
||||
}
|
||||
|
||||
override fun fetchSearchManga(
|
||||
page: Int,
|
||||
query: String,
|
||||
filters: FilterList
|
||||
): Observable<MangasPage> {
|
||||
if (page == 1) runCatching { updateComicsData() }
|
||||
|
||||
filters.filterIsInstance<RankingsFilter>().firstOrNull()?.let { rankingFilter ->
|
||||
val type = rankingList[rankingFilter.state!!.index].type
|
||||
val ascending = rankingFilter.state!!.ascending
|
||||
getRankingsIfNeeded(type, ascending)?.let {
|
||||
return Observable.just(MangasPage(it, false))
|
||||
}
|
||||
}
|
||||
|
||||
var filteredComics = comicList
|
||||
|
||||
if (query.isNotBlank()) {
|
||||
filteredComics = filteredComics.filter { it.name.contains(query, ignoreCase = true) }
|
||||
}
|
||||
|
||||
filters.forEach { filter ->
|
||||
when (filter) {
|
||||
is StatusFilter -> {
|
||||
filteredComics = filteredComics.filter { zsHelper.checkStatusFilter(filter, it) }
|
||||
}
|
||||
is GenreFilter -> {
|
||||
filteredComics = filteredComics.filter { zsHelper.checkGenreFilter(filter, it) }
|
||||
}
|
||||
is SortFilter -> {
|
||||
filter.state?.let {
|
||||
val type = sortList[it.index].type
|
||||
val ascending = it.ascending
|
||||
filteredComics = zsHelper.applySortFilter(type, ascending, filteredComics)
|
||||
}
|
||||
}
|
||||
else -> { /* Do Nothing */ }
|
||||
}
|
||||
}
|
||||
|
||||
// Get 20 comics at a time
|
||||
val chunkedFilteredComics = filteredComics.chunked(20)
|
||||
if (chunkedFilteredComics.isEmpty()) {
|
||||
return Observable.just(MangasPage(emptyList(), false))
|
||||
}
|
||||
|
||||
val comics = chunkedFilteredComics[page - 1].map { comic -> zsHelper.zsComicEntryToSManga(comic) }
|
||||
val hasNextPage = page < chunkedFilteredComics.size
|
||||
|
||||
return Observable.just(MangasPage(comics, hasNextPage))
|
||||
}
|
||||
|
||||
private fun getRankingsIfNeeded(type: String?, ascending: Boolean): List<SManga>? {
|
||||
if (type.isNullOrBlank()) return null
|
||||
|
||||
val rankingEntries = when (type) {
|
||||
"weekly" -> {
|
||||
if (!ascending) rankings.weekly.reversed()
|
||||
else rankings.weekly
|
||||
}
|
||||
"monthly" -> {
|
||||
if (!ascending) rankings.monthly.reversed()
|
||||
else rankings.monthly
|
||||
}
|
||||
else -> {
|
||||
if (!ascending) rankings.allTime.reversed()
|
||||
else rankings.allTime
|
||||
}
|
||||
}
|
||||
|
||||
val titlesList = rankingEntries.mapNotNull { rankingEntry ->
|
||||
comicList.firstOrNull { rankingEntry.slug == it.slug }
|
||||
}.map { comic -> zsHelper.zsComicEntryToSManga(comic) }
|
||||
|
||||
return titlesList
|
||||
}
|
||||
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||
runCatching { updateComicsData() }
|
||||
val mangaSlug = "$baseUrl${manga.url}".toHttpUrl().pathSegments[1]
|
||||
|
||||
try {
|
||||
val comic = comicList.first { comic -> comic.slug == mangaSlug }
|
||||
.let { zsHelper.zsComicEntryToSManga(it) }
|
||||
|
||||
return Observable.just(comic)
|
||||
} catch (e: NoSuchElementException) {
|
||||
throw Exception("Migrate from Zero Scans to Zero Scans")
|
||||
}
|
||||
}
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
try {
|
||||
var page = 1
|
||||
|
||||
val response = client.newCall(zsChapterListRequest(page, manga)).execute()
|
||||
|
||||
val zsChapterPage = zsChapterListParse(response)
|
||||
|
||||
val zsChapters = zsChapterPage.chapters.toMutableList()
|
||||
|
||||
var hasMoreResult = zsChapterPage.hasNextPage
|
||||
|
||||
while (hasMoreResult) {
|
||||
page++
|
||||
val newResponse = client.newCall(zsChapterListRequest(page, manga)).execute()
|
||||
val newZSChapterPage = zsChapterListParse(newResponse)
|
||||
zsChapters.addAll(newZSChapterPage.chapters)
|
||||
hasMoreResult = newZSChapterPage.hasNextPage
|
||||
}
|
||||
|
||||
zsChapters.map { it.toSChapter(manga) }.let {
|
||||
return Observable.just(it)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("Zero Scans", "Error parsing chapter list", e)
|
||||
throw(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun zsChapterListRequest(page: Int, manga: SManga): Request {
|
||||
val mangaId = "$baseUrl${manga.url}".toHttpUrl().queryParameter("id")
|
||||
return GET("$baseUrl/$API_PATH/comic/$mangaId/chapters?sort=desc&page=$page")
|
||||
}
|
||||
|
||||
private fun zsChapterListParse(response: Response): ZeroScansChapterPage {
|
||||
return response.parseAs<ZeroScansResponseDto<ZeroScansChaptersResponseDto>>()
|
||||
.data.let {
|
||||
ZeroScansChapterPage(
|
||||
it.data,
|
||||
it.currentPage < it.lastPage
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ZeroScansChapterPage(
|
||||
val chapters: List<ZeroScansChapterDto>,
|
||||
val hasNextPage: Boolean
|
||||
)
|
||||
|
||||
private fun ZeroScansChapterDto.toSChapter(manga: SManga): SChapter {
|
||||
val comicSlug = "$baseUrl${manga.url}".toHttpUrl().pathSegments[1]
|
||||
val zsChapter = this
|
||||
return SChapter.create().apply {
|
||||
name = "Chapter ${zsChapter.name}"
|
||||
scanlator = zsChapter.group
|
||||
chapter_number = zsChapter.name.toFloat()
|
||||
date_upload = zsHelper.parseChapterUploadDate(zsChapter.createdAt)
|
||||
url = "/comics/$comicSlug/${zsChapter.id}"
|
||||
}
|
||||
}
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
val chapterUrlPaths = "$baseUrl${chapter.url}".toHttpUrl().pathSegments
|
||||
val mangaSlug = chapterUrlPaths[1]
|
||||
val chapterId = chapterUrlPaths[2]
|
||||
return GET("$baseUrl/$API_PATH/comic/$mangaSlug/chapters/$chapterId")
|
||||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val allQualityZSPages = response.parseAs<ZeroScansResponseDto<ZeroScansPageResponseDto>>().data.chapter
|
||||
|
||||
val highResZSPages = allQualityZSPages.highQuality.takeIf { it.isNotEmpty() } ?: allQualityZSPages.goodQuality
|
||||
val pages = highResZSPages.mapIndexed { index, url ->
|
||||
Page(index, imageUrl = url)
|
||||
}
|
||||
|
||||
return pages
|
||||
}
|
||||
|
||||
// Fetch Comics, Genres, Statuses and Rankings on creating source
|
||||
init {
|
||||
Single.fromCallable {
|
||||
runCatching { updateComicsData() }
|
||||
}.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe({}, {})
|
||||
}
|
||||
|
||||
// Filters
|
||||
override fun getFilterList(): FilterList {
|
||||
val filters = mutableListOf<Filter<*>>()
|
||||
if (genreList.isNotEmpty()) {
|
||||
filters.add(GenreFilter(genreList))
|
||||
}
|
||||
if (statusList.isNotEmpty()) {
|
||||
filters.add(StatusFilter(statusList))
|
||||
}
|
||||
filters += listOf(
|
||||
SortFilter(sortList),
|
||||
RankingsHeader(),
|
||||
RankingsHeader2(),
|
||||
RankingsFilter(rankingList)
|
||||
)
|
||||
|
||||
return FilterList(filters)
|
||||
}
|
||||
|
||||
class GenreFilter(genres: List<Genre>) : Filter.Group<Genre>("Genre", genres)
|
||||
class Genre(name: String, val id: Int) : Filter.TriState(name)
|
||||
|
||||
private var genreList: List<Genre> = emptyList()
|
||||
|
||||
class StatusFilter(statuses: List<Status>) : Filter.Group<Status>("Status", statuses)
|
||||
class Status(name: String, val id: Int) : Filter.TriState(name)
|
||||
|
||||
private var statusList: List<Status> = emptyList()
|
||||
|
||||
class SortFilter(sorts: List<ZeroScans.Sort>) :
|
||||
Filter.Sort("Sort by", sorts.map { it.name }.toTypedArray(), Selection(3, false))
|
||||
|
||||
class Sort(val name: String, val type: String)
|
||||
|
||||
private val sortList = listOf(
|
||||
Sort("Alphabetic", "alphabetic"),
|
||||
Sort("Rating", "rating"),
|
||||
Sort("Chapter Count", "chapter_count"),
|
||||
Sort("Bookmark Count", "bookmark_count"),
|
||||
Sort("View Count", "view_count")
|
||||
)
|
||||
|
||||
class RankingsHeader :
|
||||
Filter.Header("Note: Genre, Sort, Status filter and Search query")
|
||||
class RankingsHeader2 :
|
||||
Filter.Header("are not applied to rankings")
|
||||
|
||||
class RankingsFilter(rankings: List<Ranking>) :
|
||||
Filter.Sort("Rankings", rankings.map { it.name }.toTypedArray(), Selection(0, false))
|
||||
|
||||
class Ranking(val name: String, val type: String? = null)
|
||||
|
||||
private val rankingList = listOf(
|
||||
Ranking("None"),
|
||||
Ranking("All Time", "all-time"),
|
||||
Ranking("Weekly", "weekly"),
|
||||
Ranking("Monthly", "monthly")
|
||||
)
|
||||
|
||||
// Helpers
|
||||
private inline fun <reified T> Response.parseAs(): T = use {
|
||||
json.decodeFromString(it.body?.string().orEmpty())
|
||||
}
|
||||
|
||||
private fun comicsDataRequest(): Request {
|
||||
return GET("$baseUrl/$API_PATH/comics")
|
||||
}
|
||||
|
||||
private fun comicsDataParse(response: Response): ZeroScansComicsDataDto {
|
||||
return response.parseAs<ZeroScansResponseDto<ZeroScansComicsDataDto>>().data
|
||||
}
|
||||
|
||||
private fun updateComicsData() {
|
||||
val response = client.newCall(comicsDataRequest()).execute()
|
||||
comicsDataParse(response).let {
|
||||
genreList = it.genres.map { genreDto ->
|
||||
Genre(genreDto.name, genreDto.id)
|
||||
}
|
||||
statusList = it.statuses.map { statusDto ->
|
||||
Status(statusDto.name, statusDto.id)
|
||||
}
|
||||
comicList = it.comics
|
||||
rankings = it.rankings
|
||||
}
|
||||
}
|
||||
|
||||
// Unused Stuff
|
||||
override fun imageUrlParse(response: Response): String = ""
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request = throw UnsupportedOperationException("Not Used")
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage = throw UnsupportedOperationException("Not Used")
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
|
||||
throw UnsupportedOperationException("Not Used")
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage = throw UnsupportedOperationException("Not Used")
|
||||
|
||||
override fun chapterListRequest(manga: SManga): Request = throw UnsupportedOperationException("Not Used")
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> = throw UnsupportedOperationException("Not Used")
|
||||
|
||||
override fun mangaDetailsParse(response: Response): SManga = throw UnsupportedOperationException("Not Used")
|
||||
|
||||
companion object {
|
||||
const val API_PATH = "swordflake"
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package eu.kanade.tachiyomi.extension.en.zeroscans
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
|
||||
@Serializable
|
||||
data class NewChaptersResponseDto(
|
||||
val all: List<NewChaptersMangaDto>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class NewChaptersMangaDto(
|
||||
val slug: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ZeroScansResponseDto<T>(
|
||||
val success: Boolean? = null,
|
||||
val data: T,
|
||||
val message: String? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ZeroScansComicsDataDto(
|
||||
val comics: List<ZeroScansComicDto>,
|
||||
val genres: List<ZeroScansGenreDto>,
|
||||
val statuses: List<ZeroScansStatusDto>,
|
||||
val rankings: ZeroScansRankingsDto
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ZeroScansComicDto(
|
||||
val name: String,
|
||||
val slug: String,
|
||||
val id: Int,
|
||||
val cover: ZeroScansCoverDto,
|
||||
val summary: String,
|
||||
val statuses: List<ZeroScansStatusDto>,
|
||||
val genres: List<ZeroScansGenreDto>,
|
||||
@SerialName("chapter_count") val chapterCount: Int,
|
||||
@SerialName("bookmark_count") val bookmarkCount: Int,
|
||||
@SerialName("view_count") val viewCount: Int,
|
||||
val rating: JsonElement
|
||||
) {
|
||||
fun getRating(): Float {
|
||||
return this.rating.toString().toFloatOrNull() ?: 0F
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ZeroScansGenreDto(
|
||||
val name: String,
|
||||
val id: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ZeroScansStatusDto(
|
||||
val name: String,
|
||||
val id: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ZeroScansRankingsDto(
|
||||
@SerialName("all_time") val allTime: List<ZeroScansRankingsEntryDto>,
|
||||
@SerialName("weekly_comics") val weekly: List<ZeroScansRankingsEntryDto>,
|
||||
@SerialName("monthly_comics") val monthly: List<ZeroScansRankingsEntryDto>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ZeroScansRankingsEntryDto(
|
||||
val slug: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ZeroScansCoverDto(
|
||||
val horizontal: String? = null,
|
||||
val vertical: String? = null,
|
||||
val full: String? = null
|
||||
) {
|
||||
fun getHighResCover(): String {
|
||||
return when {
|
||||
!this.full.isNullOrBlank() -> this.full
|
||||
!this.horizontal.isNullOrBlank() -> this.horizontal.replace("-horizontal", "-full")
|
||||
!this.vertical.isNullOrBlank() -> this.vertical.replace("-vertical", "-full")
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ZeroScansChaptersResponseDto(
|
||||
val data: List<ZeroScansChapterDto> = emptyList(),
|
||||
@SerialName("current_page") val currentPage: Int,
|
||||
@SerialName("last_page") val lastPage: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ZeroScansChapterDto(
|
||||
val id: Int,
|
||||
val name: Int,
|
||||
val group: String?,
|
||||
@SerialName("created_at") val createdAt: String
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ZeroScansPageResponseDto(
|
||||
val chapter: ZeroScansChapterPagesDto
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ZeroScansChapterPagesDto(
|
||||
@SerialName("high_quality") val highQuality: List<String> = emptyList(),
|
||||
@SerialName("good_quality") val goodQuality: List<String> = emptyList()
|
||||
)
|
@ -0,0 +1,122 @@
|
||||
package eu.kanade.tachiyomi.extension.en.zeroscans
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
|
||||
class ZeroScansHelper {
|
||||
|
||||
// Search Related
|
||||
fun checkStatusFilter(
|
||||
filter: ZeroScans.StatusFilter,
|
||||
comic: ZeroScansComicDto
|
||||
): Boolean {
|
||||
val includedStatusIds = filter.state.filter { it.isIncluded() }.map { it.id }
|
||||
val excludedStatusIds = filter.state.filter { it.isExcluded() }.map { it.id }
|
||||
|
||||
val comicStatusesId = comic.statuses.map { it.id }
|
||||
|
||||
if (includedStatusIds.isEmpty() && excludedStatusIds.isEmpty()) return true
|
||||
|
||||
return includedStatusIds.any { it in comicStatusesId } && excludedStatusIds.any { it !in comicStatusesId }
|
||||
}
|
||||
|
||||
fun checkGenreFilter(
|
||||
filter: ZeroScans.GenreFilter,
|
||||
comic: ZeroScansComicDto
|
||||
): Boolean {
|
||||
val includedGenreIds = filter.state.filter { it.isIncluded() }.map { it.id }
|
||||
val excludedGenreIds = filter.state.filter { it.isExcluded() }.map { it.id }
|
||||
|
||||
val comicStatusesId = comic.genres.map { it.id }
|
||||
|
||||
if (includedGenreIds.isEmpty() && excludedGenreIds.isEmpty()) return true
|
||||
|
||||
return includedGenreIds.any { it in comicStatusesId } && excludedGenreIds.any { it !in comicStatusesId }
|
||||
}
|
||||
|
||||
fun applySortFilter(
|
||||
type: String,
|
||||
ascending: Boolean,
|
||||
comics: List<ZeroScansComicDto>
|
||||
): List<ZeroScansComicDto> {
|
||||
var sortedList = when (type) {
|
||||
"alphabetic" -> comics.sortedBy { it.name.toLowerCase(Locale.ROOT) }
|
||||
"rating" -> comics.sortedBy { it.getRating() }
|
||||
"chapter_count" -> comics.sortedBy { it.chapterCount }
|
||||
"bookmark_count" -> comics.sortedBy { it.bookmarkCount }
|
||||
"view_count" -> comics.sortedBy { it.viewCount }
|
||||
else -> comics
|
||||
}
|
||||
|
||||
if (!ascending) {
|
||||
sortedList = sortedList.reversed()
|
||||
}
|
||||
|
||||
return sortedList
|
||||
}
|
||||
|
||||
// Chapter Related
|
||||
fun parseChapterUploadDate(date: String): Long {
|
||||
val value = date.split(' ')[0].toInt()
|
||||
|
||||
return when (date.split(' ')[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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Manga Related
|
||||
fun zsComicEntryToSManga(comic: ZeroScansComicDto): SManga {
|
||||
var comicDescription = comic.summary
|
||||
if (comic.statuses.any { it.id == 4 }) {
|
||||
comicDescription = "The series has been dropped.\n\n$comicDescription"
|
||||
}
|
||||
return SManga.create().apply {
|
||||
title = comic.name
|
||||
url = "/comics/${comic.slug}?id=${comic.id}"
|
||||
thumbnail_url = comic.cover.getHighResCover()
|
||||
description = comicDescription
|
||||
genre = comic.genres.joinToString { it.name }
|
||||
status = comic.getTachiyomiStatus()
|
||||
initialized = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun ZeroScansComicDto.getTachiyomiStatus(): Int {
|
||||
// 1 = New & 4 = Dropped
|
||||
val compatibleStatus = statuses.filterNot { it.id in listOf(1, 4) }
|
||||
|
||||
// TODO Apply 6 to ON_HIATUS after ext-lib 1.3 merge
|
||||
compatibleStatus.firstOrNull { it.id in listOf(5, 6) }
|
||||
?.also { return SManga.ONGOING }
|
||||
|
||||
compatibleStatus.firstOrNull { it.id == 3 }
|
||||
?.also { return SManga.COMPLETED }
|
||||
|
||||
// Nothing Matched
|
||||
return SManga.UNKNOWN
|
||||
}
|
||||
}
|