mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-01 22:58:57 +01:00
Remove manga from trackers (#9535)
* Dialog for service tracker removal added, anilist query prepared * added API delete requests for Mal and Kitsu * implement and fix tracker delete for anilist, shikimori, mangaupdates * implement and test mal delete request * Update to dialog text to reflect current tracker * finish kitsu api request and block bangumi tracker removal * Change delete flag into interface, localise strings, clean up logs * Add shikimori delete compatibility for already existing entries * update track delete dialog prompt to include checkbox, update strings * Update i18n/src/main/res/values/strings.xml Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com> * Update i18n/src/main/res/values/strings.xml --------- Co-authored-by: unknown <zaghdane@fireflow.de> Co-authored-by: arkon <arkon@users.noreply.github.com> Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
package eu.kanade.tachiyomi.data.track
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
|
||||
/**
|
||||
* For track services api that support deleting a manga entry for a user's list
|
||||
*/
|
||||
interface DeletableTrackService {
|
||||
|
||||
suspend fun delete(track: Track): Track
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.DeletableTrackService
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import kotlinx.serialization.decodeFromString
|
||||
@@ -12,7 +13,7 @@ import kotlinx.serialization.json.Json
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import tachiyomi.domain.track.model.Track as DomainTrack
|
||||
|
||||
class Anilist(id: Long) : TrackService(id) {
|
||||
class Anilist(id: Long) : TrackService(id), DeletableTrackService {
|
||||
|
||||
companion object {
|
||||
const val READING = 1
|
||||
@@ -167,6 +168,15 @@ class Anilist(id: Long) : TrackService(id) {
|
||||
return api.updateLibManga(track)
|
||||
}
|
||||
|
||||
override suspend fun delete(track: Track): Track {
|
||||
if (track.library_id == null || track.library_id!! == 0L) {
|
||||
val libManga = api.findLibManga(track, getUsername().toInt()) ?: return track
|
||||
track.library_id = libManga.library_id
|
||||
}
|
||||
|
||||
return api.deleteLibManga(track)
|
||||
}
|
||||
|
||||
override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {
|
||||
val remoteTrack = api.findLibManga(track, getUsername().toInt())
|
||||
return if (remoteTrack != null) {
|
||||
|
||||
@@ -110,6 +110,27 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteLibManga(track: Track): Track {
|
||||
return withIOContext {
|
||||
val query = """
|
||||
|mutation DeleteManga(${'$'}listId: Int) {
|
||||
|DeleteMediaListEntry(id: ${'$'}listId) {
|
||||
|deleted
|
||||
|}
|
||||
|}
|
||||
|
|
||||
""".trimMargin()
|
||||
val payload = buildJsonObject {
|
||||
put("query", query)
|
||||
putJsonObject("variables") {
|
||||
put("listId", track.library_id)
|
||||
}
|
||||
}
|
||||
authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime)))
|
||||
.awaitSuccess()
|
||||
track
|
||||
}
|
||||
}
|
||||
suspend fun search(search: String): List<TrackSearch> {
|
||||
return withIOContext {
|
||||
val query = """
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.DeletableTrackService
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import kotlinx.serialization.decodeFromString
|
||||
@@ -12,7 +13,7 @@ import kotlinx.serialization.json.Json
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.DecimalFormat
|
||||
|
||||
class Kitsu(id: Long) : TrackService(id) {
|
||||
class Kitsu(id: Long) : TrackService(id), DeletableTrackService {
|
||||
|
||||
companion object {
|
||||
const val READING = 1
|
||||
@@ -93,6 +94,10 @@ class Kitsu(id: Long) : TrackService(id) {
|
||||
return api.updateLibManga(track)
|
||||
}
|
||||
|
||||
override suspend fun delete(track: Track): Track {
|
||||
return api.removeLibManga(track)
|
||||
}
|
||||
|
||||
override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {
|
||||
val remoteTrack = api.findLibManga(track, getUserId())
|
||||
return if (remoteTrack != null) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.track.kitsu
|
||||
import androidx.core.net.toUri
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
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.POST
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
@@ -123,6 +124,21 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun removeLibManga(track: Track): Track {
|
||||
return withIOContext {
|
||||
authClient.newCall(
|
||||
DELETE(
|
||||
"${baseUrl}library-entries/${track.media_id}",
|
||||
headers = headersOf(
|
||||
"Content-Type",
|
||||
"application/vnd.api+json",
|
||||
),
|
||||
),
|
||||
)
|
||||
.awaitSuccess()
|
||||
track
|
||||
}
|
||||
}
|
||||
suspend fun search(query: String): List<TrackSearch> {
|
||||
return withIOContext {
|
||||
with(json) {
|
||||
|
||||
@@ -4,12 +4,13 @@ import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.DeletableTrackService
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.mangaupdates.dto.copyTo
|
||||
import eu.kanade.tachiyomi.data.track.mangaupdates.dto.toTrackSearch
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
|
||||
class MangaUpdates(id: Long) : TrackService(id) {
|
||||
class MangaUpdates(id: Long) : TrackService(id), DeletableTrackService {
|
||||
|
||||
companion object {
|
||||
const val READING_LIST = 0
|
||||
@@ -66,6 +67,11 @@ class MangaUpdates(id: Long) : TrackService(id) {
|
||||
return track
|
||||
}
|
||||
|
||||
override suspend fun delete(track: Track): Track {
|
||||
api.deleteSeriesFromList(track)
|
||||
return track
|
||||
}
|
||||
|
||||
override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {
|
||||
return try {
|
||||
val (series, rating) = api.getSeriesListItem(track)
|
||||
|
||||
@@ -106,6 +106,19 @@ class MangaUpdatesApi(
|
||||
updateSeriesRating(track)
|
||||
}
|
||||
|
||||
suspend fun deleteSeriesFromList(track: Track) {
|
||||
val body = buildJsonArray {
|
||||
add(track.media_id)
|
||||
}
|
||||
authClient.newCall(
|
||||
POST(
|
||||
url = "$baseUrl/v1/lists/series/delete",
|
||||
body = body.toString().toRequestBody(contentType),
|
||||
),
|
||||
)
|
||||
.awaitSuccess()
|
||||
}
|
||||
|
||||
private suspend fun getSeriesRating(track: Track): Rating? {
|
||||
return try {
|
||||
with(json) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.DeletableTrackService
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import kotlinx.serialization.decodeFromString
|
||||
@@ -11,7 +12,7 @@ import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class MyAnimeList(id: Long) : TrackService(id) {
|
||||
class MyAnimeList(id: Long) : TrackService(id), DeletableTrackService {
|
||||
|
||||
companion object {
|
||||
const val READING = 1
|
||||
@@ -90,6 +91,10 @@ class MyAnimeList(id: Long) : TrackService(id) {
|
||||
return api.updateItem(track)
|
||||
}
|
||||
|
||||
override suspend fun delete(track: Track): Track {
|
||||
return api.deleteItem(track)
|
||||
}
|
||||
|
||||
override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {
|
||||
val remoteTrack = api.findListItem(track)
|
||||
return if (remoteTrack != null) {
|
||||
|
||||
@@ -158,6 +158,20 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteItem(track: Track): Track {
|
||||
return withIOContext {
|
||||
val request = Request.Builder()
|
||||
.url(mangaUrl(track.media_id).toString())
|
||||
.delete()
|
||||
.build()
|
||||
with(json) {
|
||||
authClient.newCall(request)
|
||||
.awaitSuccess()
|
||||
track
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun findListItem(track: Track): Track? {
|
||||
return withIOContext {
|
||||
val uri = "$baseApiUrl/manga".toUri().buildUpon()
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.graphics.Color
|
||||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.DeletableTrackService
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import kotlinx.serialization.decodeFromString
|
||||
@@ -11,7 +12,7 @@ import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class Shikimori(id: Long) : TrackService(id) {
|
||||
class Shikimori(id: Long) : TrackService(id), DeletableTrackService {
|
||||
|
||||
companion object {
|
||||
const val READING = 1
|
||||
@@ -57,6 +58,10 @@ class Shikimori(id: Long) : TrackService(id) {
|
||||
return api.updateLibManga(track, getUsername())
|
||||
}
|
||||
|
||||
override suspend fun delete(track: Track): Track {
|
||||
return api.deleteLibManga(track)
|
||||
}
|
||||
|
||||
override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {
|
||||
val remoteTrack = api.findLibManga(track, getUsername())
|
||||
return if (remoteTrack != null) {
|
||||
@@ -83,6 +88,7 @@ class Shikimori(id: Long) : TrackService(id) {
|
||||
|
||||
override suspend fun refresh(track: Track): Track {
|
||||
api.findLibManga(track, getUsername())?.let { remoteTrack ->
|
||||
track.library_id = remoteTrack.library_id
|
||||
track.copyPersonalFrom(remoteTrack)
|
||||
track.total_chapters = remoteTrack.total_chapters
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import androidx.core.net.toUri
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
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.POST
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
@@ -35,28 +36,45 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
|
||||
|
||||
suspend fun addLibManga(track: Track, user_id: String): Track {
|
||||
return withIOContext {
|
||||
val payload = buildJsonObject {
|
||||
putJsonObject("user_rate") {
|
||||
put("user_id", user_id)
|
||||
put("target_id", track.media_id)
|
||||
put("target_type", "Manga")
|
||||
put("chapters", track.last_chapter_read.toInt())
|
||||
put("score", track.score.toInt())
|
||||
put("status", track.toShikimoriStatus())
|
||||
with(json) {
|
||||
val payload = buildJsonObject {
|
||||
putJsonObject("user_rate") {
|
||||
put("user_id", user_id)
|
||||
put("target_id", track.media_id)
|
||||
put("target_type", "Manga")
|
||||
put("chapters", track.last_chapter_read.toInt())
|
||||
put("score", track.score.toInt())
|
||||
put("status", track.toShikimoriStatus())
|
||||
}
|
||||
}
|
||||
authClient.newCall(
|
||||
POST(
|
||||
"$apiUrl/v2/user_rates",
|
||||
body = payload.toString().toRequestBody(jsonMime),
|
||||
),
|
||||
).awaitSuccess()
|
||||
.parseAs<JsonObject>()
|
||||
.let {
|
||||
track.library_id = it["id"]!!.jsonPrimitive.long // save id of the entry for possible future delete request
|
||||
}
|
||||
track
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateLibManga(track: Track, user_id: String): Track = addLibManga(track, user_id)
|
||||
|
||||
suspend fun deleteLibManga(track: Track): Track {
|
||||
return withIOContext {
|
||||
authClient.newCall(
|
||||
POST(
|
||||
"$apiUrl/v2/user_rates",
|
||||
body = payload.toString().toRequestBody(jsonMime),
|
||||
DELETE(
|
||||
"$apiUrl/v2/user_rates/${track.library_id}",
|
||||
),
|
||||
).awaitSuccess()
|
||||
track
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateLibManga(track: Track, user_id: String): Track = addLibManga(track, user_id)
|
||||
|
||||
suspend fun search(search: String): List<TrackSearch> {
|
||||
return withIOContext {
|
||||
val url = "$apiUrl/mangas".toUri().buildUpon()
|
||||
@@ -96,6 +114,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
|
||||
title = mangas["name"]!!.jsonPrimitive.content
|
||||
media_id = obj["id"]!!.jsonPrimitive.long
|
||||
total_chapters = mangas["chapters"]!!.jsonPrimitive.int
|
||||
library_id = obj["id"]!!.jsonPrimitive.long
|
||||
last_chapter_read = obj["chapters"]!!.jsonPrimitive.float
|
||||
score = (obj["score"]!!.jsonPrimitive.int).toFloat()
|
||||
status = toTrackStatus(obj["status"]!!.jsonPrimitive.content)
|
||||
|
||||
Reference in New Issue
Block a user