NGB-Was-Taken fd120c5081
Get manga info from tracker (#1271)
* Barebones setup (only AniList works)

* Show tracker selection dialog when entry has more than one tracker

* MangaUpdates implementation

* Add logging and toast on error.

* MyAnimeList implementation

* Kitsu implementation

* Fix MAL authors and artists

* Decode AL description

* Throw NotImplementedError instead of returning null

* Use logcat from LogcatExtensions

* Replace strings with MR strings

* Missed a string

* Delete unused Author class.

* Add Bangumi & Shikimori support for info edit (#2)

This adds the necessary API calls and DTOs to allow for editing an
entry's data to the data from a tracker, specifically adding support
for Bangumi and Shikimori.

* Exclude enhanced trackers from tracker select dialog

* MdList implementation

* Remember getTracks and trackerManager

Co-authored-by: jobobby04 <jobobby04@users.noreply.github.com>

---------

Co-authored-by: MajorTanya <39014446+MajorTanya@users.noreply.github.com>
Co-authored-by: jobobby04 <jobobby04@users.noreply.github.com>
2024-12-08 15:25:26 -05:00

174 lines
5.9 KiB
Kotlin

package exh.md.handlers
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import exh.md.dto.ChapterDataDto
import exh.md.service.MangaDexService
import exh.md.utils.MdConstants
import exh.md.utils.MdUtil
import exh.md.utils.mdListCall
import exh.metadata.metadata.MangaDexSearchMetadata
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import rx.Observable
import tachiyomi.core.common.util.lang.runAsObservable
import tachiyomi.core.common.util.lang.withIOContext
class MangaHandler(
private val lang: String,
private val service: MangaDexService,
private val apiMangaParser: ApiMangaParser,
private val followsHandler: FollowsHandler,
) {
suspend fun getMangaDetails(
manga: SManga,
sourceId: Long,
coverQuality: String,
tryUsingFirstVolumeCover: Boolean,
altTitlesInDesc: Boolean,
): SManga {
return coroutineScope {
val mangaId = MdUtil.getMangaId(manga.url)
val response = async(Dispatchers.IO) { service.viewManga(mangaId) }
val simpleChapters = async(Dispatchers.IO) { getSimpleChapters(manga) }
val statistics =
async(Dispatchers.IO) {
kotlin.runCatching { service.mangasRating(mangaId) }.getOrNull()?.statistics?.get(mangaId)
}
val responseData = response.await()
val coverFileName = if (tryUsingFirstVolumeCover) {
async(Dispatchers.IO) {
service.fetchFirstVolumeCover(responseData)
}
} else {
null
}
apiMangaParser.parseToManga(
manga,
sourceId,
responseData,
simpleChapters.await(),
statistics.await(),
coverFileName?.await(),
coverQuality,
altTitlesInDesc,
)
}
}
fun fetchMangaDetailsObservable(manga: SManga, sourceId: Long, coverQuality: String, tryUsingFirstVolumeCover: Boolean, altTitlesInDesc: Boolean): Observable<SManga> {
return runAsObservable {
getMangaDetails(manga, sourceId, coverQuality, tryUsingFirstVolumeCover, altTitlesInDesc)
}
}
fun fetchChapterListObservable(
manga: SManga,
blockedGroups: String,
blockedUploaders: String,
): Observable<List<SChapter>> = runAsObservable {
getChapterList(manga, blockedGroups, blockedUploaders)
}
suspend fun getChapterList(manga: SManga, blockedGroups: String, blockedUploaders: String): List<SChapter> {
return withIOContext {
val results = mdListCall {
service.viewChapters(
MdUtil.getMangaId(manga.url),
lang,
it,
blockedGroups,
blockedUploaders,
)
}
val groupMap = getGroupMap(results)
apiMangaParser.chapterListParse(results, groupMap)
}
}
private fun getGroupMap(results: List<ChapterDataDto>): Map<String, String> {
return results.map { chapter -> chapter.relationships }
.flatten()
.filter { it.type == MdConstants.Types.scanlator }
.map { it.id to it.attributes!!.name!! }
.toMap()
}
suspend fun fetchRandomMangaId(): String {
return withIOContext {
service.randomManga().data.id
}
}
suspend fun getTrackingInfo(track: Track): Pair<Track, MangaDexSearchMetadata?> {
return withIOContext {
/*val metadata = async {
val mangaUrl = MdUtil.buildMangaUrl(MdUtil.getMangaId(track.tracking_url))
val manga = MangaInfo(mangaUrl, track.title)
val response = client.newCall(mangaRequest(manga)).await()
val metadata = MangaDexSearchMetadata()
apiMangaParser.parseIntoMetadata(metadata, response, emptyList())
metadata
}*/
val remoteTrack = async {
followsHandler.fetchTrackingInfo(track.tracking_url)
}
remoteTrack.await() to null
}
}
suspend fun getMangaFromChapterId(chapterId: String): String? {
return withIOContext {
apiMangaParser.chapterParseForMangaId(service.viewChapter(chapterId))
}
}
suspend fun getMangaMetadata(
track: Track,
sourceId: Long,
coverQuality: String,
tryUsingFirstVolumeCover: Boolean,
altTitlesInDesc: Boolean,
): SManga? {
return withIOContext {
val mangaId = MdUtil.getMangaId(track.tracking_url)
val response = service.viewManga(mangaId)
val coverFileName = if (tryUsingFirstVolumeCover) {
service.fetchFirstVolumeCover(response)
} else {
null
}
apiMangaParser.parseToManga(
SManga.create().apply {
url = track.tracking_url
},
sourceId,
response,
emptyList(),
null,
coverFileName,
coverQuality,
altTitlesInDesc,
)
}
}
private suspend fun getSimpleChapters(manga: SManga): List<String> {
return runCatching { service.aggregateChapters(MdUtil.getMangaId(manga.url), lang) }
.onFailure {
if (it is CancellationException) throw it
}
.map { dto ->
dto.volumes.values
.flatMap { it.chapters.values }
.map { it.chapter }
}
.getOrElse { emptyList() }
}
}