komikku/app/src/main/java/exh/md/handlers/FollowsHandler.kt

256 lines
9.6 KiB
Kotlin
Raw Normal View History

package exh.md.handlers
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.mdlist.MdList
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
2020-10-28 00:52:32 +01:00
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.parseAs
import eu.kanade.tachiyomi.source.model.MetadataMangasPage
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.util.lang.withIOContext
import exh.md.handlers.serializers.MangaListResponse
import exh.md.handlers.serializers.MangaResponse
2021-05-09 23:37:19 +02:00
import exh.md.handlers.serializers.MangaStatusListResponse
import exh.md.handlers.serializers.MangaStatusResponse
2021-05-17 18:45:33 +02:00
import exh.md.handlers.serializers.ResultResponse
import exh.md.handlers.serializers.UpdateReadingStatus
import exh.md.utils.FollowStatus
import exh.md.utils.MdUtil
2021-05-11 19:06:55 +02:00
import exh.md.utils.mdListCall
import exh.metadata.metadata.MangaDexSearchMetadata
import exh.util.under
import kotlinx.serialization.encodeToString
import okhttp3.CacheControl
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import java.util.Locale
class FollowsHandler(
val client: OkHttpClient,
val headers: Headers,
val preferences: PreferencesHelper,
private val lang: String,
private val mdList: MdList
) {
/**
2021-05-09 23:37:19 +02:00
* fetch follows page
*/
2021-05-09 23:37:19 +02:00
suspend fun fetchFollows(page: Int): MetadataMangasPage {
return withIOContext {
2021-05-09 23:37:19 +02:00
val response = client.newCall(followsListRequest(MdUtil.mangaLimit * page - 1)).await()
if (response.code == 204) {
return@withIOContext MetadataMangasPage(emptyList(), false, emptyList())
}
val mangaListResponse = response.parseAs<MangaListResponse>(MdUtil.jsonParser)
2021-05-09 23:37:19 +02:00
if (mangaListResponse.results.isEmpty()) {
return@withIOContext MetadataMangasPage(emptyList(), false, emptyList())
}
2021-05-09 23:37:19 +02:00
val hasMoreResults = mangaListResponse.limit + mangaListResponse.offset under mangaListResponse.total
2021-05-12 22:17:39 +02:00
val statusListResponse = client.newCall(mangaStatusListRequest()).await()
.parseAs<MangaStatusListResponse>()
2021-05-11 04:01:42 +02:00
val results = followsParseMangaPage(mangaListResponse.results, statusListResponse.statuses)
2021-05-09 23:37:19 +02:00
MetadataMangasPage(results.map { it.first }, hasMoreResults, results.map { it.second })
}
}
/**
* Parse follows api to manga page
* used when multiple follows
*/
2021-05-22 20:38:47 +02:00
private suspend fun followsParseMangaPage(response: List<MangaResponse>, statuses: Map<String, String?>): List<Pair<SManga, MangaDexSearchMetadata>> {
2021-05-09 23:37:19 +02:00
val comparator = compareBy<Pair<SManga, MangaDexSearchMetadata>> { it.second.followStatus }
.thenBy { it.first.title }
2021-05-09 23:37:19 +02:00
return response.map {
val coverUrl = MdUtil.formThumbUrl(it.data.id)
/*val coverUrlId = it.relationships.firstOrNull { it.type == "cover_art" }?.id
2021-05-22 20:38:47 +02:00
if (coverUrlId != null) {
runCatching {
val covers = client.newCall(GET(MdUtil.coverUrl(it.data.id, coverUrlId))).await()
.parseAs<CoverListResponse>()
covers.results.firstOrNull()?.data?.attributes?.fileName?.let { fileName ->
coverUrl = "${MdUtil.cdnUrl}/covers/${it.data.id}/$fileName"
}
}
}*/
2021-05-22 20:38:47 +02:00
2021-05-09 23:37:19 +02:00
MdUtil.createMangaEntry(
it,
2021-05-22 20:38:47 +02:00
lang,
coverUrl
2021-05-09 23:37:19 +02:00
).toSManga() to MangaDexSearchMetadata().apply {
2021-05-11 04:01:42 +02:00
followStatus = FollowStatus.fromDex(statuses[it.data.id]).int
}
}.sortedWith(comparator)
}
2020-12-12 18:36:29 +01:00
/**
* fetch follow status used when fetching status for 1 manga
*/
2021-05-09 23:37:19 +02:00
private fun followStatusParse(response: Response, sResponse: Response): Track {
val mangaResponse = response.parseAs<MangaResponse>(MdUtil.jsonParser)
2021-05-09 23:37:19 +02:00
val statusResponse = sResponse.parseAs<MangaStatusResponse>()
val track = Track.create(TrackManager.MDLIST)
2021-05-09 23:37:19 +02:00
track.status = FollowStatus.fromDex(statusResponse.status).int
track.tracking_url = MdUtil.baseUrl + "/manga/" + mangaResponse.data.id
track.title = mangaResponse.data.attributes.title[lang] ?: mangaResponse.data.attributes.title["en"]!!
/* if (follow.chapter.isNotBlank()) {
track.last_chapter_read = follow.chapter.toFloat().floor()
}*/
return track
}
2020-12-12 18:36:29 +01:00
/**
* build Request for follows page
*/
private fun followsListRequest(offset: Int): Request {
val tempUrl = MdUtil.userFollows.toHttpUrl().newBuilder()
tempUrl.apply {
addQueryParameter("limit", MdUtil.mangaLimit.toString())
addQueryParameter("offset", offset.toString())
}
return GET(tempUrl.build().toString(), MdUtil.getAuthHeaders(headers, preferences, mdList), CacheControl.FORCE_NETWORK)
}
/**
* Change the status of a manga
*/
suspend fun updateFollowStatus(mangaId: String, followStatus: FollowStatus): Boolean {
return withIOContext {
val status = when (followStatus == FollowStatus.UNFOLLOWED) {
true -> null
2021-06-02 03:09:03 +02:00
false -> followStatus.name.lowercase(Locale.US)
}
val jsonString = MdUtil.jsonParser.encodeToString(UpdateReadingStatus(status))
val postResult = client.newCall(
POST(
MdUtil.updateReadingStatusUrl(mangaId),
MdUtil.getAuthHeaders(headers, preferences, mdList),
jsonString.toRequestBody("application/json".toMediaType())
2021-03-19 00:48:06 +01:00
)
).await()
2021-05-17 18:45:33 +02:00
val body = postResult.parseAs<ResultResponse>(MdUtil.jsonParser)
body.result == "ok"
}
}
suspend fun updateReadingProgress(track: Track): Boolean {
return true
/*return withIOContext {
val mangaID = getMangaId(track.tracking_url)
val formBody = FormBody.Builder()
.add("volume", "0")
.add("chapter", track.last_chapter_read.toString())
XLog.d("chapter to update %s", track.last_chapter_read.toString())
val result = runCatching {
client.newCall(
POST(
"$baseUrl/ajax/actions.ajax.php?function=edit_progress&id=$mangaID",
headers,
formBody.build()
)
).execute()
}
result.exceptionOrNull()?.let {
if (it is EOFException) {
return@withIOContext true
} else {
XLog.e("error updating reading progress", it)
return@withIOContext false
}
}
result.isSuccess
}*/
}
suspend fun updateRating(track: Track): Boolean {
return true
/*return withIOContext {
val mangaID = getMangaId(track.tracking_url)
val result = runCatching {
client.newCall(
GET(
"$baseUrl/ajax/actions.ajax.php?function=manga_rating&id=$mangaID&rating=${track.score.toInt()}",
headers
)
)
.execute()
}
result.exceptionOrNull()?.let {
if (it is EOFException) {
return@withIOContext true
} else {
XLog.e("error updating rating", it)
return@withIOContext false
}
}
result.isSuccess
}*/
}
/**
* fetch all manga from all possible pages
*/
suspend fun fetchAllFollows(): List<Pair<SManga, MangaDexSearchMetadata>> {
return withIOContext {
2021-05-11 19:06:55 +02:00
val results = client.mdListCall<MangaResponse> {
followsListRequest(it)
}
2021-05-12 22:17:39 +02:00
val statuses = client.newCall(mangaStatusListRequest()).await()
.parseAs<MangaStatusListResponse>().statuses
2021-05-11 04:01:42 +02:00
followsParseMangaPage(results, statuses)
}
}
suspend fun fetchTrackingInfo(url: String): Track {
return withIOContext {
2021-05-09 23:37:19 +02:00
val mangaId = MdUtil.getMangaId(url)
val request = GET(
2021-05-09 23:37:19 +02:00
MdUtil.mangaUrl + "/" + mangaId,
MdUtil.getAuthHeaders(headers, preferences, mdList),
CacheControl.FORCE_NETWORK
)
val statusRequest = GET(
MdUtil.mangaUrl + "/" + mangaId + "/status",
MdUtil.getAuthHeaders(headers, preferences, mdList),
CacheControl.FORCE_NETWORK
)
val response = client.newCall(request).await()
2021-05-09 23:37:19 +02:00
val statusResponse = client.newCall(statusRequest).await()
followStatusParse(response, statusResponse)
}
}
2021-05-12 22:17:39 +02:00
private fun mangaStatusListRequest(status: FollowStatus? = null): Request {
val mangaStatusUrl = MdUtil.mangaStatus.toHttpUrl().newBuilder()
if (status != null) {
2021-06-02 03:09:03 +02:00
mangaStatusUrl.addQueryParameter("status", status.name.lowercase(Locale.US))
2021-05-12 22:17:39 +02:00
}
return GET(mangaStatusUrl.build().toString(), MdUtil.getAuthHeaders(headers, preferences, mdList), CacheControl.FORCE_NETWORK)
}
}