Some domain Track model migrations

This commit is contained in:
arkon 2023-12-24 18:30:24 -05:00
parent 5908bd1930
commit 6d74a86711
28 changed files with 129 additions and 127 deletions

View File

@ -16,7 +16,7 @@ fun Track.copyPersonalFrom(other: Track): Track {
fun Track.toDbTrack(): DbTrack = DbTrack.create(syncId).also { fun Track.toDbTrack(): DbTrack = DbTrack.create(syncId).also {
it.id = id it.id = id
it.manga_id = mangaId it.manga_id = mangaId
it.media_id = remoteId it.remote_id = remoteId
it.library_id = libraryId it.library_id = libraryId
it.title = title it.title = title
it.last_chapter_read = lastChapterRead.toFloat() it.last_chapter_read = lastChapterRead.toFloat()
@ -34,7 +34,7 @@ fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? {
id = trackId, id = trackId,
mangaId = manga_id, mangaId = manga_id,
syncId = sync_id.toLong(), syncId = sync_id.toLong(),
remoteId = media_id, remoteId = remote_id,
libraryId = library_id, libraryId = library_id,
title = title, title = title,
lastChapterRead = last_chapter_read.toDouble(), lastChapterRead = last_chapter_read.toDouble(),

View File

@ -47,7 +47,6 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.presentation.components.DropdownMenu import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.presentation.theme.TachiyomiTheme import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.presentation.track.components.TrackLogoIcon import eu.kanade.presentation.track.components.TrackLogoIcon
@ -101,7 +100,7 @@ fun TrackInfoDialogHome(
} }
}, },
onChaptersClick = { onChapterClick(item) }, onChaptersClick = { onChapterClick(item) },
score = item.tracker.displayScore(item.track.toDbTrack()) score = item.tracker.displayScore(item.track)
.takeIf { supportsScoring && item.track.score != 0.0 }, .takeIf { supportsScoring && item.track.score != 0.0 },
onScoreClick = { onScoreClick(item) } onScoreClick = { onScoreClick(item) }
.takeIf { supportsScoring }, .takeIf { supportsScoring },

View File

@ -63,7 +63,7 @@ internal class TrackerSearchPreviewProvider : PreviewParameterProvider<@Composab
it.id = Random.nextLong() it.id = Random.nextLong()
it.manga_id = Random.nextLong() it.manga_id = Random.nextLong()
it.sync_id = Random.nextInt() it.sync_id = Random.nextInt()
it.media_id = Random.nextLong() it.remote_id = Random.nextLong()
it.library_id = Random.nextLong() it.library_id = Random.nextLong()
it.title = lorem((1..10).random()).joinToString() it.title = lorem((1..10).random()).joinToString()
it.last_chapter_read = (0..100).random().toFloat() it.last_chapter_read = (0..100).random().toFloat()

View File

@ -10,7 +10,7 @@ interface Track : Serializable {
var sync_id: Int var sync_id: Int
var media_id: Long var remote_id: Long
var library_id: Long? var library_id: Long?

View File

@ -8,7 +8,7 @@ class TrackImpl : Track {
override var sync_id: Int = 0 override var sync_id: Int = 0
override var media_id: Long = 0 override var remote_id: Long = 0
override var library_id: Long? = null override var library_id: Long? = null

View File

@ -1,11 +1,11 @@
package eu.kanade.tachiyomi.data.track package eu.kanade.tachiyomi.data.track
import eu.kanade.tachiyomi.data.database.models.Track import tachiyomi.domain.track.model.Track
/** /**
* Tracker that support deleting am entry from a user's list. * Tracker that support deleting am entry from a user's list.
*/ */
interface DeletableTracker { interface DeletableTracker {
suspend fun delete(track: Track): Track suspend fun delete(track: Track)
} }

View File

@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import tachiyomi.domain.track.model.Track as DomainTrack
interface Tracker { interface Tracker {
@ -39,11 +40,11 @@ interface Tracker {
fun getScoreList(): ImmutableList<String> fun getScoreList(): ImmutableList<String>
// TODO: Store all scores as 10 point in the future maybe? // TODO: Store all scores as 10 point in the future maybe?
fun get10PointScore(track: tachiyomi.domain.track.model.Track): Double fun get10PointScore(track: DomainTrack): Double
fun indexToScore(index: Int): Float fun indexToScore(index: Int): Float
fun displayScore(track: Track): String fun displayScore(track: DomainTrack): String
suspend fun update(track: Track, didReadChapter: Boolean = false): Track suspend fun update(track: Track, didReadChapter: Boolean = false): Track

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.track.anilist
import android.graphics.Color import android.graphics.Color
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.BaseTracker import eu.kanade.tachiyomi.data.track.BaseTracker
@ -120,16 +121,16 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
} }
} }
override fun displayScore(track: Track): String { override fun displayScore(track: DomainTrack): String {
val score = track.score val score = track.score
return when (scorePreference.get()) { return when (scorePreference.get()) {
POINT_5 -> when (score) { POINT_5 -> when (score) {
0f -> "0 ★" 0.0 -> "0 ★"
else -> "${((score + 10) / 20).toInt()}" else -> "${((score + 10) / 20).toInt()}"
} }
POINT_3 -> when { POINT_3 -> when {
score == 0f -> "0" score == 0.0 -> "0"
score <= 35 -> "😦" score <= 35 -> "😦"
score <= 60 -> "😐" score <= 60 -> "😐"
else -> "😊" else -> "😊"
@ -167,13 +168,13 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
return api.updateLibManga(track) return api.updateLibManga(track)
} }
override suspend fun delete(track: Track): Track { override suspend fun delete(track: DomainTrack) {
if (track.library_id == null || track.library_id!! == 0L) { if (track.libraryId == null || track.libraryId == 0L) {
val libManga = api.findLibManga(track, getUsername().toInt()) ?: return track val libManga = api.findLibManga(track.toDbTrack(), getUsername().toInt()) ?: return
track.library_id = libManga.library_id return api.deleteLibManga(track.copy(id = libManga.library_id!!))
} }
return api.deleteLibManga(track) api.deleteLibManga(track)
} }
override suspend fun bind(track: Track, hasReadChapters: Boolean): Track { override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {

View File

@ -31,6 +31,7 @@ import java.time.LocalDate
import java.time.ZoneId import java.time.ZoneId
import java.time.ZonedDateTime import java.time.ZonedDateTime
import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.minutes
import tachiyomi.domain.track.model.Track as DomainTrack
class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) { class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
@ -55,7 +56,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
val payload = buildJsonObject { val payload = buildJsonObject {
put("query", query) put("query", query)
putJsonObject("variables") { putJsonObject("variables") {
put("mangaId", track.media_id) put("mangaId", track.remote_id)
put("progress", track.last_chapter_read.toInt()) put("progress", track.last_chapter_read.toInt())
put("status", track.toAnilistStatus()) put("status", track.toAnilistStatus())
} }
@ -113,8 +114,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
} }
} }
suspend fun deleteLibManga(track: Track): Track { suspend fun deleteLibManga(track: DomainTrack) {
return withIOContext { withIOContext {
val query = """ val query = """
|mutation DeleteManga(${'$'}listId: Int) { |mutation DeleteManga(${'$'}listId: Int) {
|DeleteMediaListEntry(id: ${'$'}listId) { |DeleteMediaListEntry(id: ${'$'}listId) {
@ -126,12 +127,11 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
val payload = buildJsonObject { val payload = buildJsonObject {
put("query", query) put("query", query)
putJsonObject("variables") { putJsonObject("variables") {
put("listId", track.library_id) put("listId", track.libraryId)
} }
} }
authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime))) authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime)))
.awaitSuccess() .awaitSuccess()
track
} }
} }
suspend fun search(search: String): List<TrackSearch> { suspend fun search(search: String): List<TrackSearch> {
@ -235,7 +235,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
put("query", query) put("query", query)
putJsonObject("variables") { putJsonObject("variables") {
put("id", userid) put("id", userid)
put("manga_id", track.media_id) put("manga_id", track.remote_id)
} }
} }
with(json) { with(json) {
@ -258,8 +258,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
} }
} }
suspend fun getLibManga(track: Track, userid: Int): Track { suspend fun getLibManga(track: Track, userId: Int): Track {
return findLibManga(track, userid) ?: throw Exception("Could not find manga") return findLibManga(track, userId) ?: throw Exception("Could not find manga")
} }
fun createOAuth(token: String): OAuth { fun createOAuth(token: String): OAuth {

View File

@ -9,9 +9,10 @@ import kotlinx.serialization.Serializable
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import tachiyomi.domain.track.model.Track as DomainTrack
data class ALManga( data class ALManga(
val media_id: Long, val remote_id: Long,
val title_user_pref: String, val title_user_pref: String,
val image_url_lge: String, val image_url_lge: String,
val description: String?, val description: String?,
@ -23,13 +24,13 @@ data class ALManga(
) { ) {
fun toTrack() = TrackSearch.create(TrackerManager.ANILIST).apply { fun toTrack() = TrackSearch.create(TrackerManager.ANILIST).apply {
media_id = this@ALManga.media_id remote_id = this@ALManga.remote_id
title = title_user_pref title = title_user_pref
total_chapters = this@ALManga.total_chapters total_chapters = this@ALManga.total_chapters
cover_url = image_url_lge cover_url = image_url_lge
summary = description?.htmlDecode() ?: "" summary = description?.htmlDecode() ?: ""
score = average_score.toFloat() score = average_score.toFloat()
tracking_url = AnilistApi.mangaUrl(media_id) tracking_url = AnilistApi.mangaUrl(remote_id)
publishing_status = this@ALManga.publishing_status publishing_status = this@ALManga.publishing_status
publishing_type = format publishing_type = format
if (start_date_fuzzy != 0L) { if (start_date_fuzzy != 0L) {
@ -54,7 +55,7 @@ data class ALUserManga(
) { ) {
fun toTrack() = Track.create(TrackerManager.ANILIST).apply { fun toTrack() = Track.create(TrackerManager.ANILIST).apply {
media_id = manga.media_id remote_id = manga.remote_id
title = manga.title_user_pref title = manga.title_user_pref
status = toTrackStatus() status = toTrackStatus()
score = score_raw.toFloat() score = score_raw.toFloat()
@ -98,28 +99,28 @@ fun Track.toAnilistStatus() = when (status) {
private val preferences: TrackPreferences by injectLazy() private val preferences: TrackPreferences by injectLazy()
fun Track.toAnilistScore(): String = when (preferences.anilistScoreType().get()) { fun DomainTrack.toAnilistScore(): String = when (preferences.anilistScoreType().get()) {
// 10 point // 10 point
"POINT_10" -> (score.toInt() / 10).toString() "POINT_10" -> (score.toInt() / 10).toString()
// 100 point // 100 point
"POINT_100" -> score.toInt().toString() "POINT_100" -> score.toInt().toString()
// 5 stars // 5 stars
"POINT_5" -> when { "POINT_5" -> when {
score == 0f -> "0" score == 0.0 -> "0"
score < 30 -> "1" score < 30 -> "1"
score < 50 -> "2" score < 50 -> "2"
score < 70 -> "3" score < 70 -> "3"
score < 90 -> "4" score < 90 -> "4"
else -> "5" else -> "5"
} }
// Smiley // Smiley
"POINT_3" -> when { "POINT_3" -> when {
score == 0f -> "0" score == 0.0 -> "0"
score <= 35 -> ":(" score <= 35 -> ":("
score <= 60 -> ":|" score <= 60 -> ":|"
else -> ":)" else -> ":)"
} }
// 10 point decimal // 10 point decimal
"POINT_10_DECIMAL" -> (score / 10).toString() "POINT_10_DECIMAL" -> (score / 10).toString()
else -> throw NotImplementedError("Unknown score type") else -> throw NotImplementedError("Unknown score type")
} }

View File

@ -12,6 +12,7 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import tachiyomi.domain.track.model.Track as DomainTrack
class Bangumi(id: Long) : BaseTracker(id, "Bangumi") { class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
@ -23,7 +24,7 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
override fun getScoreList(): ImmutableList<String> = SCORE_LIST override fun getScoreList(): ImmutableList<String> = SCORE_LIST
override fun displayScore(track: Track): String { override fun displayScore(track: DomainTrack): String {
return track.score.toInt().toString() return track.score.toInt().toString()
} }

View File

@ -42,7 +42,7 @@ class BangumiApi(
.add("rating", track.score.toInt().toString()) .add("rating", track.score.toInt().toString())
.add("status", track.toBangumiStatus()) .add("status", track.toBangumiStatus())
.build() .build()
authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = body)) authClient.newCall(POST("$apiUrl/collection/${track.remote_id}/update", body = body))
.awaitSuccess() .awaitSuccess()
track track
} }
@ -55,7 +55,7 @@ class BangumiApi(
.add("rating", track.score.toInt().toString()) .add("rating", track.score.toInt().toString())
.add("status", track.toBangumiStatus()) .add("status", track.toBangumiStatus())
.build() .build()
authClient.newCall(POST("$apiUrl/collection/${track.media_id}/update", body = sbody)) authClient.newCall(POST("$apiUrl/collection/${track.remote_id}/update", body = sbody))
.awaitSuccess() .awaitSuccess()
// chapter update // chapter update
@ -64,7 +64,7 @@ class BangumiApi(
.build() .build()
authClient.newCall( authClient.newCall(
POST( POST(
"$apiUrl/subject/${track.media_id}/update/watched_eps", "$apiUrl/subject/${track.remote_id}/update/watched_eps",
body = body, body = body,
), ),
).awaitSuccess() ).awaitSuccess()
@ -111,7 +111,7 @@ class BangumiApi(
} }
val rating = obj["rating"]?.jsonObject?.get("score")?.jsonPrimitive?.floatOrNull ?: -1f val rating = obj["rating"]?.jsonObject?.get("score")?.jsonPrimitive?.floatOrNull ?: -1f
return TrackSearch.create(trackId).apply { return TrackSearch.create(trackId).apply {
media_id = obj["id"]!!.jsonPrimitive.long remote_id = obj["id"]!!.jsonPrimitive.long
title = obj["name_cn"]!!.jsonPrimitive.content title = obj["name_cn"]!!.jsonPrimitive.content
cover_url = coverUrl cover_url = coverUrl
summary = obj["name"]!!.jsonPrimitive.content summary = obj["name"]!!.jsonPrimitive.content
@ -124,7 +124,7 @@ class BangumiApi(
suspend fun findLibManga(track: Track): Track? { suspend fun findLibManga(track: Track): Track? {
return withIOContext { return withIOContext {
with(json) { with(json) {
authClient.newCall(GET("$apiUrl/subject/${track.media_id}")) authClient.newCall(GET("$apiUrl/subject/${track.remote_id}"))
.awaitSuccess() .awaitSuccess()
.parseAs<JsonObject>() .parseAs<JsonObject>()
.let { jsonToSearch(it) } .let { jsonToSearch(it) }
@ -134,7 +134,7 @@ class BangumiApi(
suspend fun statusLibManga(track: Track): Track? { suspend fun statusLibManga(track: Track): Track? {
return withIOContext { return withIOContext {
val urlUserRead = "$apiUrl/collection/${track.media_id}" val urlUserRead = "$apiUrl/collection/${track.remote_id}"
val requestUserRead = Request.Builder() val requestUserRead = Request.Builder()
.url(urlUserRead) .url(urlUserRead)
.cacheControl(CacheControl.FORCE_NETWORK) .cacheControl(CacheControl.FORCE_NETWORK)

View File

@ -55,7 +55,7 @@ class Kavita(id: Long) : BaseTracker(id, "Kavita"), EnhancedTracker {
override fun getScoreList(): ImmutableList<String> = persistentListOf() override fun getScoreList(): ImmutableList<String> = persistentListOf()
override fun displayScore(track: Track): String = "" override fun displayScore(track: DomainTrack): String = ""
override suspend fun update(track: Track, didReadChapter: Boolean): Track { override suspend fun update(track: Track, didReadChapter: Boolean): Track {
if (track.status != COMPLETED) { if (track.status != COMPLETED) {

View File

@ -14,6 +14,7 @@ import kotlinx.serialization.json.Json
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.DecimalFormat import java.text.DecimalFormat
import tachiyomi.domain.track.model.Track as DomainTrack
class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker { class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker {
@ -65,7 +66,7 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker {
return if (index > 0) (index + 1) / 2f else 0f return if (index > 0) (index + 1) / 2f else 0f
} }
override fun displayScore(track: Track): String { override fun displayScore(track: DomainTrack): String {
val df = DecimalFormat("0.#") val df = DecimalFormat("0.#")
return df.format(track.score) return df.format(track.score)
} }
@ -92,15 +93,15 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker {
return api.updateLibManga(track) return api.updateLibManga(track)
} }
override suspend fun delete(track: Track): Track { override suspend fun delete(track: DomainTrack) {
return api.removeLibManga(track) api.removeLibManga(track)
} }
override suspend fun bind(track: Track, hasReadChapters: Boolean): Track { override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {
val remoteTrack = api.findLibManga(track, getUserId()) val remoteTrack = api.findLibManga(track, getUserId())
return if (remoteTrack != null) { return if (remoteTrack != null) {
track.copyPersonalFrom(remoteTrack) track.copyPersonalFrom(remoteTrack)
track.media_id = remoteTrack.media_id track.remote_id = remoteTrack.remote_id
if (track.status != COMPLETED) { if (track.status != COMPLETED) {
track.status = if (hasReadChapters) READING else track.status track.status = if (hasReadChapters) READING else track.status

View File

@ -29,6 +29,7 @@ import tachiyomi.core.util.lang.withIOContext
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.net.URLEncoder import java.net.URLEncoder
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import tachiyomi.domain.track.model.Track as DomainTrack
class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor) { class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor) {
@ -54,7 +55,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
} }
putJsonObject("media") { putJsonObject("media") {
putJsonObject("data") { putJsonObject("data") {
put("id", track.media_id) put("id", track.remote_id)
put("type", "manga") put("type", "manga")
} }
} }
@ -77,7 +78,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
.awaitSuccess() .awaitSuccess()
.parseAs<JsonObject>() .parseAs<JsonObject>()
.let { .let {
track.media_id = it["data"]!!.jsonObject["id"]!!.jsonPrimitive.long track.remote_id = it["data"]!!.jsonObject["id"]!!.jsonPrimitive.long
track track
} }
} }
@ -89,7 +90,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
val data = buildJsonObject { val data = buildJsonObject {
putJsonObject("data") { putJsonObject("data") {
put("type", "libraryEntries") put("type", "libraryEntries")
put("id", track.media_id) put("id", track.remote_id)
putJsonObject("attributes") { putJsonObject("attributes") {
put("status", track.toKitsuStatus()) put("status", track.toKitsuStatus())
put("progress", track.last_chapter_read.toInt()) put("progress", track.last_chapter_read.toInt())
@ -103,7 +104,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
with(json) { with(json) {
authClient.newCall( authClient.newCall(
Request.Builder() Request.Builder()
.url("${baseUrl}library-entries/${track.media_id}") .url("${baseUrl}library-entries/${track.remote_id}")
.headers( .headers(
headersOf( headersOf(
"Content-Type", "Content-Type",
@ -124,19 +125,19 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
} }
} }
suspend fun removeLibManga(track: Track): Track { suspend fun removeLibManga(track: DomainTrack) {
return withIOContext { withIOContext {
authClient.newCall( authClient
DELETE( .newCall(
"${baseUrl}library-entries/${track.media_id}", DELETE(
headers = headersOf( "${baseUrl}library-entries/${track.remoteId}",
"Content-Type", headers = headersOf(
"application/vnd.api+json", "Content-Type",
"application/vnd.api+json",
),
), ),
), )
)
.awaitSuccess() .awaitSuccess()
track
} }
} }
suspend fun search(query: String): List<TrackSearch> { suspend fun search(query: String): List<TrackSearch> {
@ -187,7 +188,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
suspend fun findLibManga(track: Track, userId: String): Track? { suspend fun findLibManga(track: Track, userId: String): Track? {
return withIOContext { return withIOContext {
val url = "${baseUrl}library-entries".toUri().buildUpon() val url = "${baseUrl}library-entries".toUri().buildUpon()
.encodedQuery("filter[manga_id]=${track.media_id}&filter[user_id]=$userId") .encodedQuery("filter[manga_id]=${track.remote_id}&filter[user_id]=$userId")
.appendQueryParameter("include", "manga") .appendQueryParameter("include", "manga")
.build() .build()
with(json) { with(json) {
@ -210,7 +211,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
suspend fun getLibManga(track: Track): Track { suspend fun getLibManga(track: Track): Track {
return withIOContext { return withIOContext {
val url = "${baseUrl}library-entries".toUri().buildUpon() val url = "${baseUrl}library-entries".toUri().buildUpon()
.encodedQuery("filter[id]=${track.media_id}") .encodedQuery("filter[id]=${track.remote_id}")
.appendQueryParameter("include", "manga") .appendQueryParameter("include", "manga")
.build() .build()
with(json) { with(json) {

View File

@ -37,12 +37,12 @@ class KitsuSearchManga(obj: JsonObject) {
@CallSuper @CallSuper
fun toTrack() = TrackSearch.create(TrackerManager.KITSU).apply { fun toTrack() = TrackSearch.create(TrackerManager.KITSU).apply {
media_id = this@KitsuSearchManga.id remote_id = this@KitsuSearchManga.id
title = canonicalTitle title = canonicalTitle
total_chapters = chapterCount ?: 0 total_chapters = chapterCount ?: 0
cover_url = original ?: "" cover_url = original ?: ""
summary = synopsis ?: "" summary = synopsis ?: ""
tracking_url = KitsuApi.mangaUrl(media_id) tracking_url = KitsuApi.mangaUrl(remote_id)
score = rating ?: -1f score = rating ?: -1f
publishing_status = if (endDate == null) { publishing_status = if (endDate == null) {
"Publishing" "Publishing"
@ -70,12 +70,12 @@ class KitsuLibManga(obj: JsonObject, manga: JsonObject) {
val progress = obj["attributes"]!!.jsonObject["progress"]!!.jsonPrimitive.int val progress = obj["attributes"]!!.jsonObject["progress"]!!.jsonPrimitive.int
fun toTrack() = TrackSearch.create(TrackerManager.KITSU).apply { fun toTrack() = TrackSearch.create(TrackerManager.KITSU).apply {
media_id = libraryId remote_id = libraryId
title = canonicalTitle title = canonicalTitle
total_chapters = chapterCount ?: 0 total_chapters = chapterCount ?: 0
cover_url = original cover_url = original
summary = synopsis summary = synopsis
tracking_url = KitsuApi.mangaUrl(media_id) tracking_url = KitsuApi.mangaUrl(remote_id)
publishing_status = this@KitsuLibManga.status publishing_status = this@KitsuLibManga.status
publishing_type = type publishing_type = type
start_date = startDate start_date = startDate

View File

@ -52,7 +52,7 @@ class Komga(id: Long) : BaseTracker(id, "Komga"), EnhancedTracker {
override fun getScoreList(): ImmutableList<String> = persistentListOf() override fun getScoreList(): ImmutableList<String> = persistentListOf()
override fun displayScore(track: Track): String = "" override fun displayScore(track: DomainTrack): String = ""
override suspend fun update(track: Track, didReadChapter: Boolean): Track { override suspend fun update(track: Track, didReadChapter: Boolean): Track {
if (track.status != COMPLETED) { if (track.status != COMPLETED) {

View File

@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.domain.track.model.Track as DomainTrack
class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker { class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker {
@ -60,7 +61,7 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
override fun indexToScore(index: Int): Float = SCORE_LIST[index].toFloat() override fun indexToScore(index: Int): Float = SCORE_LIST[index].toFloat()
override fun displayScore(track: Track): String = track.score.toString() override fun displayScore(track: DomainTrack): String = track.score.toString()
override suspend fun update(track: Track, didReadChapter: Boolean): Track { override suspend fun update(track: Track, didReadChapter: Boolean): Track {
if (track.status != COMPLETE_LIST && didReadChapter) { if (track.status != COMPLETE_LIST && didReadChapter) {
@ -70,9 +71,8 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
return track return track
} }
override suspend fun delete(track: Track): Track { override suspend fun delete(track: DomainTrack) {
api.deleteSeriesFromList(track) api.deleteSeriesFromList(track)
return track
} }
override suspend fun bind(track: Track, hasReadChapters: Boolean): Track { override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {

View File

@ -30,6 +30,7 @@ import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import tachiyomi.domain.track.model.Track as DomainTrack
class MangaUpdatesApi( class MangaUpdatesApi(
interceptor: MangaUpdatesInterceptor, interceptor: MangaUpdatesInterceptor,
@ -48,7 +49,7 @@ class MangaUpdatesApi(
suspend fun getSeriesListItem(track: Track): Pair<ListItem, Rating?> { suspend fun getSeriesListItem(track: Track): Pair<ListItem, Rating?> {
val listItem = with(json) { val listItem = with(json) {
authClient.newCall(GET("$baseUrl/v1/lists/series/${track.media_id}")) authClient.newCall(GET("$baseUrl/v1/lists/series/${track.remote_id}"))
.awaitSuccess() .awaitSuccess()
.parseAs<ListItem>() .parseAs<ListItem>()
} }
@ -63,7 +64,7 @@ class MangaUpdatesApi(
val body = buildJsonArray { val body = buildJsonArray {
addJsonObject { addJsonObject {
putJsonObject("series") { putJsonObject("series") {
put("id", track.media_id) put("id", track.remote_id)
} }
put("list_id", status) put("list_id", status)
} }
@ -87,7 +88,7 @@ class MangaUpdatesApi(
val body = buildJsonArray { val body = buildJsonArray {
addJsonObject { addJsonObject {
putJsonObject("series") { putJsonObject("series") {
put("id", track.media_id) put("id", track.remote_id)
} }
put("list_id", track.status) put("list_id", track.status)
putJsonObject("status") { putJsonObject("status") {
@ -106,9 +107,9 @@ class MangaUpdatesApi(
updateSeriesRating(track) updateSeriesRating(track)
} }
suspend fun deleteSeriesFromList(track: Track) { suspend fun deleteSeriesFromList(track: DomainTrack) {
val body = buildJsonArray { val body = buildJsonArray {
add(track.media_id) add(track.remoteId)
} }
authClient.newCall( authClient.newCall(
POST( POST(
@ -122,7 +123,7 @@ class MangaUpdatesApi(
private suspend fun getSeriesRating(track: Track): Rating? { private suspend fun getSeriesRating(track: Track): Rating? {
return try { return try {
with(json) { with(json) {
authClient.newCall(GET("$baseUrl/v1/series/${track.media_id}/rating")) authClient.newCall(GET("$baseUrl/v1/series/${track.remote_id}/rating"))
.awaitSuccess() .awaitSuccess()
.parseAs<Rating>() .parseAs<Rating>()
} }
@ -138,7 +139,7 @@ class MangaUpdatesApi(
} }
authClient.newCall( authClient.newCall(
PUT( PUT(
url = "$baseUrl/v1/series/${track.media_id}/rating", url = "$baseUrl/v1/series/${track.remote_id}/rating",
body = body.toString().toRequestBody(contentType), body = body.toString().toRequestBody(contentType),
), ),
) )
@ -146,7 +147,7 @@ class MangaUpdatesApi(
} else { } else {
authClient.newCall( authClient.newCall(
DELETE( DELETE(
url = "$baseUrl/v1/series/${track.media_id}/rating", url = "$baseUrl/v1/series/${track.remote_id}/rating",
), ),
) )
.awaitSuccess() .awaitSuccess()

View File

@ -25,7 +25,7 @@ data class Record(
fun Record.toTrackSearch(id: Long): TrackSearch { fun Record.toTrackSearch(id: Long): TrackSearch {
return TrackSearch.create(id).apply { return TrackSearch.create(id).apply {
media_id = this@toTrackSearch.seriesId ?: 0L remote_id = this@toTrackSearch.seriesId ?: 0L
title = this@toTrackSearch.title?.htmlDecode() ?: "" title = this@toTrackSearch.title?.htmlDecode() ?: ""
total_chapters = 0 total_chapters = 0
cover_url = this@toTrackSearch.image?.url?.original ?: "" cover_url = this@toTrackSearch.image?.url?.original ?: ""

View File

@ -10,7 +10,7 @@ class TrackSearch : Track {
override var sync_id: Int = 0 override var sync_id: Int = 0
override var media_id: Long = 0 override var remote_id: Long = 0
override var library_id: Long? = null override var library_id: Long? = null
@ -48,7 +48,7 @@ class TrackSearch : Track {
if (manga_id != other.manga_id) return false if (manga_id != other.manga_id) return false
if (sync_id != other.sync_id) return false if (sync_id != other.sync_id) return false
if (media_id != other.media_id) return false if (remote_id != other.remote_id) return false
return true return true
} }
@ -56,7 +56,7 @@ class TrackSearch : Track {
override fun hashCode(): Int { override fun hashCode(): Int {
var result = manga_id.hashCode() var result = manga_id.hashCode()
result = 31 * result + sync_id result = 31 * result + sync_id
result = 31 * result + media_id.hashCode() result = 31 * result + remote_id.hashCode()
return result return result
} }

View File

@ -13,6 +13,7 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import tachiyomi.domain.track.model.Track as DomainTrack
class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker { class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
@ -65,7 +66,7 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
override fun getScoreList(): ImmutableList<String> = SCORE_LIST override fun getScoreList(): ImmutableList<String> = SCORE_LIST
override fun displayScore(track: Track): String { override fun displayScore(track: DomainTrack): String {
return track.score.toInt().toString() return track.score.toInt().toString()
} }
@ -91,15 +92,15 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
return api.updateItem(track) return api.updateItem(track)
} }
override suspend fun delete(track: Track): Track { override suspend fun delete(track: DomainTrack) {
return api.deleteItem(track) api.deleteItem(track)
} }
override suspend fun bind(track: Track, hasReadChapters: Boolean): Track { override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {
val remoteTrack = api.findListItem(track) val remoteTrack = api.findListItem(track)
return if (remoteTrack != null) { return if (remoteTrack != null) {
track.copyPersonalFrom(remoteTrack) track.copyPersonalFrom(remoteTrack)
track.media_id = remoteTrack.media_id track.remote_id = remoteTrack.remote_id
if (track.status != COMPLETED) { if (track.status != COMPLETED) {
val isRereading = track.status == REREADING val isRereading = track.status == REREADING

View File

@ -4,6 +4,7 @@ import android.net.Uri
import androidx.core.net.toUri import androidx.core.net.toUri
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.network.DELETE
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.awaitSuccess import eu.kanade.tachiyomi.network.awaitSuccess
@ -31,6 +32,7 @@ import tachiyomi.core.util.lang.withIOContext
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import tachiyomi.domain.track.model.Track as DomainTrack
class MyAnimeListApi( class MyAnimeListApi(
private val trackId: Long, private val trackId: Long,
@ -114,7 +116,7 @@ class MyAnimeListApi(
.let { .let {
val obj = it.jsonObject val obj = it.jsonObject
TrackSearch.create(trackId).apply { TrackSearch.create(trackId).apply {
media_id = obj["id"]!!.jsonPrimitive.long remote_id = obj["id"]!!.jsonPrimitive.long
title = obj["title"]!!.jsonPrimitive.content title = obj["title"]!!.jsonPrimitive.content
summary = obj["synopsis"]?.jsonPrimitive?.content ?: "" summary = obj["synopsis"]?.jsonPrimitive?.content ?: ""
total_chapters = obj["num_chapters"]!!.jsonPrimitive.int total_chapters = obj["num_chapters"]!!.jsonPrimitive.int
@ -122,7 +124,7 @@ class MyAnimeListApi(
cover_url = cover_url =
obj["main_picture"]?.jsonObject?.get("large")?.jsonPrimitive?.content obj["main_picture"]?.jsonObject?.get("large")?.jsonPrimitive?.content
?: "" ?: ""
tracking_url = "https://myanimelist.net/manga/$media_id" tracking_url = "https://myanimelist.net/manga/$remote_id"
publishing_status = publishing_status =
obj["status"]!!.jsonPrimitive.content.replace("_", " ") obj["status"]!!.jsonPrimitive.content.replace("_", " ")
publishing_type = publishing_type =
@ -154,7 +156,7 @@ class MyAnimeListApi(
} }
val request = Request.Builder() val request = Request.Builder()
.url(mangaUrl(track.media_id).toString()) .url(mangaUrl(track.remote_id).toString())
.put(formBodyBuilder.build()) .put(formBodyBuilder.build())
.build() .build()
with(json) { with(json) {
@ -166,24 +168,18 @@ class MyAnimeListApi(
} }
} }
suspend fun deleteItem(track: Track): Track { suspend fun deleteItem(track: DomainTrack) {
return withIOContext { withIOContext {
val request = Request.Builder() authClient
.url(mangaUrl(track.media_id).toString()) .newCall(DELETE(mangaUrl(track.remoteId).toString()))
.delete() .awaitSuccess()
.build()
with(json) {
authClient.newCall(request)
.awaitSuccess()
track
}
} }
} }
suspend fun findListItem(track: Track): Track? { suspend fun findListItem(track: Track): Track? {
return withIOContext { return withIOContext {
val uri = "$baseApiUrl/manga".toUri().buildUpon() val uri = "$baseApiUrl/manga".toUri().buildUpon()
.appendPath(track.media_id.toString()) .appendPath(track.remote_id.toString())
.appendQueryParameter("fields", "num_chapters,my_list_status{start_date,finish_date}") .appendQueryParameter("fields", "num_chapters,my_list_status{start_date,finish_date}")
.build() .build()
with(json) { with(json) {

View File

@ -13,6 +13,7 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import tachiyomi.domain.track.model.Track as DomainTrack
class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker { class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
@ -37,7 +38,7 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
override fun getScoreList(): ImmutableList<String> = SCORE_LIST override fun getScoreList(): ImmutableList<String> = SCORE_LIST
override fun displayScore(track: Track): String { override fun displayScore(track: DomainTrack): String {
return track.score.toInt().toString() return track.score.toInt().toString()
} }
@ -59,8 +60,8 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
return api.updateLibManga(track, getUsername()) return api.updateLibManga(track, getUsername())
} }
override suspend fun delete(track: Track): Track { override suspend fun delete(track: DomainTrack) {
return api.deleteLibManga(track) api.deleteLibManga(track)
} }
override suspend fun bind(track: Track, hasReadChapters: Boolean): Track { override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {

View File

@ -27,6 +27,7 @@ import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withIOContext
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import tachiyomi.domain.track.model.Track as DomainTrack
class ShikimoriApi( class ShikimoriApi(
private val trackId: Long, private val trackId: Long,
@ -44,7 +45,7 @@ class ShikimoriApi(
val payload = buildJsonObject { val payload = buildJsonObject {
putJsonObject("user_rate") { putJsonObject("user_rate") {
put("user_id", userId) put("user_id", userId)
put("target_id", track.media_id) put("target_id", track.remote_id)
put("target_type", "Manga") put("target_type", "Manga")
put("chapters", track.last_chapter_read.toInt()) put("chapters", track.last_chapter_read.toInt())
put("score", track.score.toInt()) put("score", track.score.toInt())
@ -69,14 +70,11 @@ class ShikimoriApi(
suspend fun updateLibManga(track: Track, userId: String): Track = addLibManga(track, userId) suspend fun updateLibManga(track: Track, userId: String): Track = addLibManga(track, userId)
suspend fun deleteLibManga(track: Track): Track { suspend fun deleteLibManga(track: DomainTrack) {
return withIOContext { withIOContext {
authClient.newCall( authClient
DELETE( .newCall(DELETE("$apiUrl/v2/user_rates/${track.libraryId}"))
"$apiUrl/v2/user_rates/${track.library_id}", .awaitSuccess()
),
).awaitSuccess()
track
} }
} }
@ -102,7 +100,7 @@ class ShikimoriApi(
private fun jsonToSearch(obj: JsonObject): TrackSearch { private fun jsonToSearch(obj: JsonObject): TrackSearch {
return TrackSearch.create(trackId).apply { return TrackSearch.create(trackId).apply {
media_id = obj["id"]!!.jsonPrimitive.long remote_id = obj["id"]!!.jsonPrimitive.long
title = obj["name"]!!.jsonPrimitive.content title = obj["name"]!!.jsonPrimitive.content
total_chapters = obj["chapters"]!!.jsonPrimitive.int total_chapters = obj["chapters"]!!.jsonPrimitive.int
cover_url = baseUrl + obj["image"]!!.jsonObject["preview"]!!.jsonPrimitive.content cover_url = baseUrl + obj["image"]!!.jsonObject["preview"]!!.jsonPrimitive.content
@ -118,7 +116,7 @@ class ShikimoriApi(
private fun jsonToTrack(obj: JsonObject, mangas: JsonObject): Track { private fun jsonToTrack(obj: JsonObject, mangas: JsonObject): Track {
return Track.create(trackId).apply { return Track.create(trackId).apply {
title = mangas["name"]!!.jsonPrimitive.content title = mangas["name"]!!.jsonPrimitive.content
media_id = obj["id"]!!.jsonPrimitive.long remote_id = obj["id"]!!.jsonPrimitive.long
total_chapters = mangas["chapters"]!!.jsonPrimitive.int total_chapters = mangas["chapters"]!!.jsonPrimitive.int
library_id = obj["id"]!!.jsonPrimitive.long library_id = obj["id"]!!.jsonPrimitive.long
last_chapter_read = obj["chapters"]!!.jsonPrimitive.float last_chapter_read = obj["chapters"]!!.jsonPrimitive.float
@ -131,7 +129,7 @@ class ShikimoriApi(
suspend fun findLibManga(track: Track, userId: String): Track? { suspend fun findLibManga(track: Track, userId: String): Track? {
return withIOContext { return withIOContext {
val urlMangas = "$apiUrl/mangas".toUri().buildUpon() val urlMangas = "$apiUrl/mangas".toUri().buildUpon()
.appendPath(track.media_id.toString()) .appendPath(track.remote_id.toString())
.build() .build()
val mangas = with(json) { val mangas = with(json) {
authClient.newCall(GET(urlMangas.toString())) authClient.newCall(GET(urlMangas.toString()))
@ -141,7 +139,7 @@ class ShikimoriApi(
val url = "$apiUrl/v2/user_rates".toUri().buildUpon() val url = "$apiUrl/v2/user_rates".toUri().buildUpon()
.appendQueryParameter("user_id", userId) .appendQueryParameter("user_id", userId)
.appendQueryParameter("target_id", track.media_id.toString()) .appendQueryParameter("target_id", track.remote_id.toString())
.appendQueryParameter("target_type", "Manga") .appendQueryParameter("target_type", "Manga")
.build() .build()
with(json) { with(json) {

View File

@ -45,7 +45,7 @@ class Suwayomi(id: Long) : BaseTracker(id, "Suwayomi"), EnhancedTracker {
override fun getScoreList(): ImmutableList<String> = persistentListOf() override fun getScoreList(): ImmutableList<String> = persistentListOf()
override fun displayScore(track: Track): String = "" override fun displayScore(track: DomainTrack): String = ""
override suspend fun update(track: Track, didReadChapter: Boolean): Track { override suspend fun update(track: Track, didReadChapter: Boolean): Track {
if (track.status != COMPLETED) { if (track.status != COMPLETED) {

View File

@ -399,7 +399,7 @@ private data class TrackScoreSelectorScreen(
private class Model( private class Model(
private val track: Track, private val track: Track,
private val tracker: Tracker, private val tracker: Tracker,
) : StateScreenModel<Model.State>(State(tracker.displayScore(track.toDbTrack()))) { ) : StateScreenModel<Model.State>(State(tracker.displayScore(track))) {
fun getSelections(): ImmutableList<String> { fun getSelections(): ImmutableList<String> {
return tracker.getScoreList() return tracker.getScoreList()
@ -816,7 +816,7 @@ private data class TrackerRemoveScreen(
fun deleteMangaFromService() { fun deleteMangaFromService() {
screenModelScope.launchNonCancellable { screenModelScope.launchNonCancellable {
(tracker as DeletableTracker).delete(track.toDbTrack()) (tracker as DeletableTracker).delete(track)
} }
} }

View File

@ -58,7 +58,7 @@ data class DummyTracker(
override fun indexToScore(index: Int): Float = getScoreList()[index].toFloat() override fun indexToScore(index: Int): Float = getScoreList()[index].toFloat()
override fun displayScore(track: eu.kanade.tachiyomi.data.database.models.Track): String = override fun displayScore(track: Track): String =
track.score.toString() track.score.toString()
override suspend fun update( override suspend fun update(