From 04c5941ed1c2abbbece0ca0cfc4a6995b932fc9f Mon Sep 17 00:00:00 2001 From: UmbrellaalAcademy <165912201+UmbrellaalAcademy@users.noreply.github.com> Date: Tue, 16 Apr 2024 10:53:21 +0530 Subject: [PATCH] Update MangaUpdates.kt --- .../connections/bakaupdates/MangaUpdates.kt | 158 +++++++++++++++--- 1 file changed, 136 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/ani/dantotsu/connections/bakaupdates/MangaUpdates.kt b/app/src/main/java/ani/dantotsu/connections/bakaupdates/MangaUpdates.kt index 426abc51..c2e59000 100644 --- a/app/src/main/java/ani/dantotsu/connections/bakaupdates/MangaUpdates.kt +++ b/app/src/main/java/ani/dantotsu/connections/bakaupdates/MangaUpdates.kt @@ -3,8 +3,9 @@ package ani.dantotsu.connections.bakaupdates import android.content.Context import ani.dantotsu.R import ani.dantotsu.client -import ani.dantotsu.connections.anilist.api.FuzzyDate +import ani.dantotsu.media.Media import ani.dantotsu.tryWithSuspend +import ani.dantotsu.utf8 import ani.dantotsu.util.Logger import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async @@ -12,24 +13,24 @@ import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import okio.ByteString.Companion.encode import org.json.JSONException import org.json.JSONObject -import java.nio.charset.Charset - +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale +import java.util.TimeZone +import java.util.concurrent.TimeUnit class MangaUpdates { private val Int?.dateFormat get() = String.format("%02d", this) - private val apiUrl = "https://api.mangaupdates.com/v1/releases/search" - - suspend fun search(title: String, startDate: FuzzyDate?) : MangaUpdatesResponse.Results? { + suspend fun findLatestRelease(media: Media) : ReleaseResponse.Results? { return tryWithSuspend { val query = JSONObject().apply { try { - put("search", title.encode(Charset.forName("UTF-8"))) - startDate?.let { + put("search", media.mangaName().utf8) + media.startDate?.let { put( "start_date", "${it.year}-${it.month.dateFormat}-${it.day.dateFormat}" @@ -40,7 +41,10 @@ class MangaUpdates { e.printStackTrace() } } - val res = client.post(apiUrl, json = query).parsed() + val res = client.post( + "https://api.mangaupdates.com/v1/releases/search", + json = query + ).parsed() coroutineScope { res.results?.map { async(Dispatchers.IO) { @@ -56,20 +60,106 @@ class MangaUpdates { } } - companion object { - fun getLatestChapter(context: Context, results: MangaUpdatesResponse.Results): String { - return results.metadata.series.latestChapter?.let { - context.getString(R.string.chapter_number, it) - } ?: results.record.chapter!!.substringAfterLast("-").trim().let { chapter -> - chapter.takeIf { - it.toIntOrNull() == null - } ?: context.getString(R.string.chapter_number, chapter.toInt()) + suspend fun getSeries(results: ReleaseResponse.Results) : SeriesResponse? { + return results.metadata.series.seriesId?.let { + tryWithSuspend { + val res = client.get( + "https://api.mangaupdates.com/v1/series/$it" + ).parsed() + Logger.log(res.toString()) + res.latestChapter?.let { res } + } + } + } + + fun getLatestChapter(context: Context, results: ReleaseResponse.Results): String { + return results.metadata.series.latestChapter?.let { + context.getString(R.string.chapter_number, it) + } ?: results.record.chapter!!.substringAfterLast("-").trim().let { chapter -> + chapter.takeIf { + it.toIntOrNull() == null + } ?: context.getString(R.string.chapter_number, chapter.toInt()) + } + } + + private suspend fun findReleaseDates(media: Media) : List { + val releaseList = hashMapOf() + return tryWithSuspend { + val query = JSONObject().apply { + try { + put("search", media.mangaName().utf8) + media.startDate?.let { + put( + "start_date", + "${it.year}-${it.month.dateFormat}-${it.day.dateFormat}" + ) + } + put("include_metadata", true) + } catch (e: JSONException) { + e.printStackTrace() + } + } + val res = client.post( + "https://api.mangaupdates.com/v1/releases/search", + json = query + ).parsed() + res.results?.filter { + it.record.volume.isNullOrBlank() && it.record.chapter != null + }?.sortedByDescending { it.record.releaseDate }?.forEach { + releaseList[it.record.chapter!!] = it.record.releaseDate + Logger.log(it.toString()) + } + releaseList.values.toList().sortedDescending() + } ?: releaseList.values.toList() + } + + private fun getCalendarInstance(releaseDate: String): Calendar { + val calendar: Calendar = Calendar.getInstance() + val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT) + calendar.timeZone = TimeZone.getDefault() + dateFormat.parse(releaseDate)?.let { calendar.time = it } + return calendar + } + + suspend fun predictRelease(media: Media, latest: Long): Long? { + val releaseDates = findReleaseDates(media) + if (releaseDates.size < 3) return null + releaseDates.forEach { + Logger.log(it) + } + val dateLatest = getCalendarInstance(releaseDates[0]) + val dateMiddle = getCalendarInstance(releaseDates[1]) + val dateOldest = getCalendarInstance(releaseDates[2]) + val daysNew: Long = TimeUnit.MILLISECONDS.toDays(dateLatest.timeInMillis - dateMiddle.timeInMillis) + val daysOld: Long = TimeUnit.MILLISECONDS.toDays(dateMiddle.timeInMillis - dateOldest.timeInMillis) + + val date: Calendar = Calendar.getInstance() + date.timeInMillis = latest + + return when { + daysNew in 5..14 && daysOld in 5..14 -> { + latest + 604800000 // 7 days + } + daysNew in 28..36 && daysOld in 28..36 -> { + date.add(Calendar.MONTH, 1) + date.timeInMillis + } + daysNew in 84..98 && daysOld in 84..98 -> { + date.add(Calendar.MONTH, 3) + date.timeInMillis + } + daysNew >= 358 && daysOld >= 358 -> { + date.add(Calendar.YEAR, 1) + date.timeInMillis + } + else -> { + null } } } @Serializable - data class MangaUpdatesResponse( + data class ReleaseResponse( @SerialName("total_hits") val totalHits: Int?, @SerialName("page") @@ -86,7 +176,7 @@ class MangaUpdates { @Serializable data class Record( @SerialName("id") - val id: Int, + val id: Long, @SerialName("title") val title: String, @SerialName("volume") @@ -116,12 +206,36 @@ class MangaUpdates { @SerialName("timestamp") val timestamp: Long, @SerialName("as_rfc3339") - val asRfc3339: String, + val asRfc3339: String?, @SerialName("as_string") - val asString: String + val asString: String? ) } } } } + + @Serializable + data class SeriesResponse( + @SerialName("series_id") + val seriesId: Long, + @SerialName("title") + val title: String, + @SerialName("description") + val description: String?, + @SerialName("latest_chapter") + val latestChapter: Int?, + @SerialName("last_updated") + val lastUpdated: LastUpdated? + ) { + @Serializable + data class LastUpdated( + @SerialName("timestamp") + val timestamp: Long, + @SerialName("as_rfc3339") + val asRfc3339: String?, + @SerialName("as_string") + val asString: String? + ) + } }