mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-06-30 17:34:39 -05:00
Add MangaUpdates (#834)
This commit is contained in:
@@ -54,3 +54,31 @@ fun POST(
|
|||||||
.cacheControl(cache)
|
.cacheControl(cache)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun PUT(
|
||||||
|
url: String,
|
||||||
|
headers: Headers = DEFAULT_HEADERS,
|
||||||
|
body: RequestBody = DEFAULT_BODY,
|
||||||
|
cache: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||||
|
): Request {
|
||||||
|
return Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.put(body)
|
||||||
|
.headers(headers)
|
||||||
|
.cacheControl(cache)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun DELETE(
|
||||||
|
url: String,
|
||||||
|
headers: Headers = DEFAULT_HEADERS,
|
||||||
|
body: RequestBody = DEFAULT_BODY,
|
||||||
|
cache: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||||
|
): Request {
|
||||||
|
return Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.delete(body)
|
||||||
|
.headers(headers)
|
||||||
|
.cacheControl(cache)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package suwayomi.tachidesk.manga.impl.track.tracker
|
package suwayomi.tachidesk.manga.impl.track.tracker
|
||||||
|
|
||||||
import suwayomi.tachidesk.manga.impl.track.tracker.anilist.Anilist
|
import suwayomi.tachidesk.manga.impl.track.tracker.anilist.Anilist
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.MangaUpdates
|
||||||
import suwayomi.tachidesk.manga.impl.track.tracker.myanimelist.MyAnimeList
|
import suwayomi.tachidesk.manga.impl.track.tracker.myanimelist.MyAnimeList
|
||||||
|
|
||||||
object TrackerManager {
|
object TrackerManager {
|
||||||
@@ -16,15 +17,16 @@ object TrackerManager {
|
|||||||
|
|
||||||
val myAnimeList = MyAnimeList(MYANIMELIST)
|
val myAnimeList = MyAnimeList(MYANIMELIST)
|
||||||
val aniList = Anilist(ANILIST)
|
val aniList = Anilist(ANILIST)
|
||||||
|
|
||||||
// val kitsu = Kitsu(KITSU)
|
// val kitsu = Kitsu(KITSU)
|
||||||
// val shikimori = Shikimori(SHIKIMORI)
|
// val shikimori = Shikimori(SHIKIMORI)
|
||||||
// val bangumi = Bangumi(BANGUMI)
|
// val bangumi = Bangumi(BANGUMI)
|
||||||
// val komga = Komga(KOMGA)
|
// val komga = Komga(KOMGA)
|
||||||
// val mangaUpdates = MangaUpdates(MANGA_UPDATES)
|
val mangaUpdates = MangaUpdates(MANGA_UPDATES)
|
||||||
// val kavita = Kavita(context, KAVITA)
|
// val kavita = Kavita(context, KAVITA)
|
||||||
// val suwayomi = Suwayomi(SUWAYOMI)
|
// val suwayomi = Suwayomi(SUWAYOMI)
|
||||||
|
|
||||||
val services: List<Tracker> = listOf(myAnimeList, aniList)
|
val services: List<Tracker> = listOf(myAnimeList, aniList, mangaUpdates)
|
||||||
|
|
||||||
fun getTracker(id: Int) = services.find { it.id == id }
|
fun getTracker(id: Int) = services.find { it.id == id }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates
|
||||||
|
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.Tracker
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto.ListItem
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto.Rating
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto.copyTo
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto.toTrackSearch
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.model.Track
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.model.TrackSearch
|
||||||
|
|
||||||
|
class MangaUpdates(id: Int) : Tracker(id, "MangaUpdates") {
|
||||||
|
// , DeletableTracker
|
||||||
|
companion object {
|
||||||
|
const val READING_LIST = 0
|
||||||
|
const val WISH_LIST = 1
|
||||||
|
const val COMPLETE_LIST = 2
|
||||||
|
const val UNFINISHED_LIST = 3
|
||||||
|
const val ON_HOLD_LIST = 4
|
||||||
|
|
||||||
|
private val SCORE_LIST =
|
||||||
|
(0..10)
|
||||||
|
.flatMap { decimal ->
|
||||||
|
when (decimal) {
|
||||||
|
0 -> listOf("-")
|
||||||
|
10 -> listOf("10.0")
|
||||||
|
else ->
|
||||||
|
(0..9).map { fraction ->
|
||||||
|
"$decimal.$fraction"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val interceptor by lazy { MangaUpdatesInterceptor(this) }
|
||||||
|
|
||||||
|
private val api by lazy { MangaUpdatesApi(interceptor, client) }
|
||||||
|
|
||||||
|
override fun getLogo(): String = "/static/tracker/manga_updates.png"
|
||||||
|
|
||||||
|
override fun getStatusList(): List<Int> {
|
||||||
|
return listOf(READING_LIST, COMPLETE_LIST, ON_HOLD_LIST, UNFINISHED_LIST, WISH_LIST)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getStatus(status: Int): String? =
|
||||||
|
when (status) {
|
||||||
|
READING_LIST -> "Reading List"
|
||||||
|
WISH_LIST -> "Wish List"
|
||||||
|
COMPLETE_LIST -> "Complete List"
|
||||||
|
ON_HOLD_LIST -> "On Hold List"
|
||||||
|
UNFINISHED_LIST -> "Unfinished List"
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getReadingStatus(): Int = READING_LIST
|
||||||
|
|
||||||
|
override fun getRereadingStatus(): Int = -1
|
||||||
|
|
||||||
|
override fun getCompletionStatus(): Int = COMPLETE_LIST
|
||||||
|
|
||||||
|
override fun getScoreList(): List<String> = SCORE_LIST
|
||||||
|
|
||||||
|
override fun indexToScore(index: Int): Float = if (index == 0) 0f else SCORE_LIST[index].toFloat()
|
||||||
|
|
||||||
|
override fun displayScore(track: Track): String = track.score.toString()
|
||||||
|
|
||||||
|
override suspend fun update(
|
||||||
|
track: Track,
|
||||||
|
didReadChapter: Boolean,
|
||||||
|
): Track {
|
||||||
|
if (track.status != COMPLETE_LIST && didReadChapter) {
|
||||||
|
track.status = READING_LIST
|
||||||
|
}
|
||||||
|
api.updateSeriesListItem(track)
|
||||||
|
return track
|
||||||
|
}
|
||||||
|
|
||||||
|
// override suspend fun delete(track: Track) {
|
||||||
|
// api.deleteSeriesFromList(track)
|
||||||
|
// }
|
||||||
|
|
||||||
|
override suspend fun bind(
|
||||||
|
track: Track,
|
||||||
|
hasReadChapters: Boolean,
|
||||||
|
): Track {
|
||||||
|
return try {
|
||||||
|
val (series, rating) = api.getSeriesListItem(track)
|
||||||
|
track.copyFrom(series, rating)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
track.score = 0f
|
||||||
|
api.addSeriesToList(track, hasReadChapters)
|
||||||
|
track
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun search(query: String): List<TrackSearch> {
|
||||||
|
return api.search(query)
|
||||||
|
.map {
|
||||||
|
it.toTrackSearch(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun refresh(track: Track): Track {
|
||||||
|
val (series, rating) = api.getSeriesListItem(track)
|
||||||
|
return track.copyFrom(series, rating)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Track.copyFrom(
|
||||||
|
item: ListItem,
|
||||||
|
rating: Rating?,
|
||||||
|
): Track =
|
||||||
|
apply {
|
||||||
|
item.copyTo(this)
|
||||||
|
score = rating?.rating ?: 0f
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun login(
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
) {
|
||||||
|
val authenticated = api.authenticate(username, password) ?: throw Throwable("Unable to login")
|
||||||
|
saveCredentials(authenticated.uid.toString(), authenticated.sessionToken)
|
||||||
|
interceptor.newAuth(authenticated.sessionToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun restoreSession(): String? {
|
||||||
|
return trackPreferences.getTrackPassword(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.network.DELETE
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.POST
|
||||||
|
import eu.kanade.tachiyomi.network.PUT
|
||||||
|
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||||
|
import eu.kanade.tachiyomi.network.parseAs
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
|
import kotlinx.serialization.json.add
|
||||||
|
import kotlinx.serialization.json.addJsonObject
|
||||||
|
import kotlinx.serialization.json.buildJsonArray
|
||||||
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
|
import kotlinx.serialization.json.decodeFromJsonElement
|
||||||
|
import kotlinx.serialization.json.jsonArray
|
||||||
|
import kotlinx.serialization.json.jsonObject
|
||||||
|
import kotlinx.serialization.json.put
|
||||||
|
import kotlinx.serialization.json.putJsonObject
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.MangaUpdates.Companion.READING_LIST
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.MangaUpdates.Companion.WISH_LIST
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto.Context
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto.ListItem
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto.Rating
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto.Record
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.model.Track
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
|
class MangaUpdatesApi(
|
||||||
|
interceptor: MangaUpdatesInterceptor,
|
||||||
|
private val client: OkHttpClient,
|
||||||
|
) {
|
||||||
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
|
private val baseUrl = "https://api.mangaupdates.com"
|
||||||
|
private val contentType = "application/vnd.api+json".toMediaType()
|
||||||
|
|
||||||
|
private val authClient by lazy {
|
||||||
|
client.newBuilder()
|
||||||
|
.addInterceptor(interceptor)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getSeriesListItem(track: Track): Pair<ListItem, Rating?> {
|
||||||
|
val listItem =
|
||||||
|
with(json) {
|
||||||
|
authClient.newCall(GET("$baseUrl/v1/lists/series/${track.media_id}"))
|
||||||
|
.awaitSuccess()
|
||||||
|
.parseAs<ListItem>()
|
||||||
|
}
|
||||||
|
|
||||||
|
val rating = getSeriesRating(track)
|
||||||
|
|
||||||
|
return listItem to rating
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun addSeriesToList(
|
||||||
|
track: Track,
|
||||||
|
hasReadChapters: Boolean,
|
||||||
|
) {
|
||||||
|
val status = if (hasReadChapters) READING_LIST else WISH_LIST
|
||||||
|
val body =
|
||||||
|
buildJsonArray {
|
||||||
|
addJsonObject {
|
||||||
|
putJsonObject("series") {
|
||||||
|
put("id", track.media_id)
|
||||||
|
}
|
||||||
|
put("list_id", status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
authClient.newCall(
|
||||||
|
POST(
|
||||||
|
url = "$baseUrl/v1/lists/series",
|
||||||
|
body = body.toString().toRequestBody(contentType),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.awaitSuccess()
|
||||||
|
.let {
|
||||||
|
if (it.code == 200) {
|
||||||
|
track.status = status
|
||||||
|
track.last_chapter_read = 1f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun updateSeriesListItem(track: Track) {
|
||||||
|
val body =
|
||||||
|
buildJsonArray {
|
||||||
|
addJsonObject {
|
||||||
|
putJsonObject("series") {
|
||||||
|
put("id", track.media_id)
|
||||||
|
}
|
||||||
|
put("list_id", track.status)
|
||||||
|
putJsonObject("status") {
|
||||||
|
put("chapter", track.last_chapter_read.toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
authClient.newCall(
|
||||||
|
POST(
|
||||||
|
url = "$baseUrl/v1/lists/series/update",
|
||||||
|
body = body.toString().toRequestBody(contentType),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.awaitSuccess()
|
||||||
|
|
||||||
|
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) {
|
||||||
|
authClient.newCall(GET("$baseUrl/v1/series/${track.media_id}/rating"))
|
||||||
|
.awaitSuccess()
|
||||||
|
.parseAs<Rating>()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun updateSeriesRating(track: Track) {
|
||||||
|
if (track.score != 0f) {
|
||||||
|
val body =
|
||||||
|
buildJsonObject {
|
||||||
|
put("rating", track.score)
|
||||||
|
}
|
||||||
|
authClient.newCall(
|
||||||
|
PUT(
|
||||||
|
url = "$baseUrl/v1/series/${track.media_id}/rating",
|
||||||
|
body = body.toString().toRequestBody(contentType),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.awaitSuccess()
|
||||||
|
} else {
|
||||||
|
authClient.newCall(
|
||||||
|
DELETE(
|
||||||
|
url = "$baseUrl/v1/series/${track.media_id}/rating",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.awaitSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun search(query: String): List<Record> {
|
||||||
|
val body =
|
||||||
|
buildJsonObject {
|
||||||
|
put("search", query)
|
||||||
|
put(
|
||||||
|
"filter_types",
|
||||||
|
buildJsonArray {
|
||||||
|
add("drama cd")
|
||||||
|
add("novel")
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return with(json) {
|
||||||
|
client.newCall(
|
||||||
|
POST(
|
||||||
|
url = "$baseUrl/v1/series/search",
|
||||||
|
body = body.toString().toRequestBody(contentType),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.awaitSuccess()
|
||||||
|
.parseAs<JsonObject>()
|
||||||
|
.let { obj ->
|
||||||
|
obj["results"]?.jsonArray?.map { element ->
|
||||||
|
json.decodeFromJsonElement<Record>(element.jsonObject["record"]!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.orEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun authenticate(
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
): Context? {
|
||||||
|
val body =
|
||||||
|
buildJsonObject {
|
||||||
|
put("username", username)
|
||||||
|
put("password", password)
|
||||||
|
}
|
||||||
|
return with(json) {
|
||||||
|
client.newCall(
|
||||||
|
PUT(
|
||||||
|
url = "$baseUrl/v1/account/login",
|
||||||
|
body = body.toString().toRequestBody(contentType),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.awaitSuccess()
|
||||||
|
.parseAs<JsonObject>()
|
||||||
|
.let { obj ->
|
||||||
|
try {
|
||||||
|
json.decodeFromJsonElement<Context>(obj["context"]!!)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// logcat(LogPriority.ERROR, e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates
|
||||||
|
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.Response
|
||||||
|
import suwayomi.tachidesk.server.generated.BuildConfig
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class MangaUpdatesInterceptor(
|
||||||
|
mangaUpdates: MangaUpdates,
|
||||||
|
) : Interceptor {
|
||||||
|
private var token: String? = mangaUpdates.restoreSession()
|
||||||
|
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
val originalRequest = chain.request()
|
||||||
|
|
||||||
|
val token = token ?: throw IOException("Not authenticated with MangaUpdates")
|
||||||
|
|
||||||
|
// Add the authorization header to the original request.
|
||||||
|
val authRequest =
|
||||||
|
originalRequest.newBuilder()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.header("User-Agent", "Suwayomi ${BuildConfig.VERSION} (${BuildConfig.REVISION})")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return chain.proceed(authRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newAuth(token: String?) {
|
||||||
|
this.token = token
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Context(
|
||||||
|
@SerialName("session_token")
|
||||||
|
val sessionToken: String,
|
||||||
|
val uid: Long,
|
||||||
|
)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Image(
|
||||||
|
val url: Url? = null,
|
||||||
|
val height: Int? = null,
|
||||||
|
val width: Int? = null,
|
||||||
|
)
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.MangaUpdates.Companion.READING_LIST
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.model.Track
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ListItem(
|
||||||
|
val series: Series? = null,
|
||||||
|
@SerialName("list_id")
|
||||||
|
val listId: Int? = null,
|
||||||
|
val status: Status? = null,
|
||||||
|
val priority: Int? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun ListItem.copyTo(track: Track): Track {
|
||||||
|
return track.apply {
|
||||||
|
this.status = listId ?: READING_LIST
|
||||||
|
this.last_chapter_read = this@copyTo.status?.chapter?.toFloat() ?: 0f
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.model.Track
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Rating(
|
||||||
|
val rating: Float? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun Rating.copyTo(track: Track): Track {
|
||||||
|
return track.apply {
|
||||||
|
this.score = rating ?: 0f
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
import suwayomi.tachidesk.manga.impl.track.tracker.model.TrackSearch
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Record(
|
||||||
|
@SerialName("series_id")
|
||||||
|
val seriesId: Long? = null,
|
||||||
|
val title: String? = null,
|
||||||
|
val url: String? = null,
|
||||||
|
val description: String? = null,
|
||||||
|
val image: Image? = null,
|
||||||
|
val type: String? = null,
|
||||||
|
val year: String? = null,
|
||||||
|
@SerialName("bayesian_rating")
|
||||||
|
val bayesianRating: Double? = null,
|
||||||
|
@SerialName("rating_votes")
|
||||||
|
val ratingVotes: Int? = null,
|
||||||
|
@SerialName("latest_chapter")
|
||||||
|
val latestChapter: Int? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun String.htmlDecode(): String {
|
||||||
|
return Jsoup.parse(this).wholeText()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Record.toTrackSearch(id: Int): TrackSearch {
|
||||||
|
return TrackSearch.create(id).apply {
|
||||||
|
media_id = this@toTrackSearch.seriesId ?: 0L
|
||||||
|
title = this@toTrackSearch.title?.htmlDecode() ?: ""
|
||||||
|
total_chapters = 0
|
||||||
|
cover_url = this@toTrackSearch.image?.url?.original ?: ""
|
||||||
|
summary = this@toTrackSearch.description?.htmlDecode() ?: ""
|
||||||
|
tracking_url = this@toTrackSearch.url ?: ""
|
||||||
|
publishing_status = ""
|
||||||
|
publishing_type = this@toTrackSearch.type.toString()
|
||||||
|
start_date = this@toTrackSearch.year.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Series(
|
||||||
|
val id: Long? = null,
|
||||||
|
val title: String? = null,
|
||||||
|
)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Status(
|
||||||
|
val volume: Int? = null,
|
||||||
|
val chapter: Int? = null,
|
||||||
|
)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Url(
|
||||||
|
val original: String? = null,
|
||||||
|
val thumb: String? = null,
|
||||||
|
)
|
||||||
BIN
server/src/main/resources/static/tracker/manga_updates.png
Normal file
BIN
server/src/main/resources/static/tracker/manga_updates.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
Reference in New Issue
Block a user