From a54d9912d0eb2c0677a8ca1b759bad65c6141f51 Mon Sep 17 00:00:00 2001 From: arkon Date: Fri, 25 Nov 2022 23:03:33 -0500 Subject: [PATCH] Fix Kavita interceptor crashing app + minor cleanup --- .../tachiyomi/data/track/kavita/Kavita.kt | 28 ++-- .../tachiyomi/data/track/kavita/KavitaApi.kt | 121 +++++++++--------- .../data/track/kavita/KavitaModels.kt | 18 ++- .../tachiyomi/data/track/kavita/OAuth.kt | 19 --- 4 files changed, 93 insertions(+), 93 deletions(-) delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/OAuth.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt index 6a308e9db..f719c1a45 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.data.track.kavita -import android.app.Application import android.content.Context import android.content.SharedPreferences import android.graphics.Color @@ -13,18 +12,20 @@ import eu.kanade.tachiyomi.data.track.NoLoginTrackService import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.source.Source -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.security.MessageDigest +import eu.kanade.domain.manga.model.Manga as DomainManga +import eu.kanade.domain.track.model.Track as DomainTrack class Kavita(private val context: Context, id: Long) : TrackService(id), EnhancedTrackService, NoLoginTrackService { - var authentications: OAuth? = null + companion object { const val UNREAD = 1 const val READING = 2 const val COMPLETED = 3 } + var authentications: OAuth? = null + private val interceptor by lazy { KavitaInterceptor(this) } val api by lazy { KavitaApi(client, interceptor) } @@ -39,18 +40,18 @@ class Kavita(private val context: Context, id: Long) : TrackService(id), Enhance override fun getStatus(status: Int): String = with(context) { when (status) { - Kavita.UNREAD -> getString(R.string.unread) - Kavita.READING -> getString(R.string.reading) - Kavita.COMPLETED -> getString(R.string.completed) + UNREAD -> getString(R.string.unread) + READING -> getString(R.string.reading) + COMPLETED -> getString(R.string.completed) else -> "" } } - override fun getReadingStatus(): Int = Kavita.READING + override fun getReadingStatus(): Int = READING override fun getRereadingStatus(): Int = -1 - override fun getCompletionStatus(): Int = Kavita.COMPLETED + override fun getCompletionStatus(): Int = COMPLETED override fun getScoreList(): List = emptyList() @@ -103,10 +104,10 @@ class Kavita(private val context: Context, id: Long) : TrackService(id), Enhance null } - override fun isTrackFrom(track: eu.kanade.domain.track.model.Track, manga: eu.kanade.domain.manga.model.Manga, source: Source?): Boolean = + override fun isTrackFrom(track: DomainTrack, manga: DomainManga, source: Source?): Boolean = track.remoteUrl == manga.url && source?.let { accept(it) } == true - override fun migrateTrack(track: eu.kanade.domain.track.model.Track, manga: eu.kanade.domain.manga.model.Manga, newSource: Source): eu.kanade.domain.track.model.Track? = + override fun migrateTrack(track: DomainTrack, manga: DomainManga, newSource: Source): DomainTrack? = if (accept(newSource)) { track.copy(remoteUrl = manga.url) } else { @@ -118,13 +119,13 @@ class Kavita(private val context: Context, id: Long) : TrackService(id), Enhance for (sourceId in 1..3) { val authentication = oauth.authentications[sourceId - 1] val sourceSuffixID by lazy { - val key = "${"kavita_$sourceId"}/all/1" // Hardcoded versionID to 1 + val key = "kavita_$sourceId/all/1" // Hardcoded versionID to 1 val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray()) (0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) } .reduce(Long::or) and Long.MAX_VALUE } val preferences: SharedPreferences by lazy { - Injekt.get().getSharedPreferences("source_$sourceSuffixID", 0x0000) + context.getSharedPreferences("source_$sourceSuffixID", 0x0000) } val prefApiUrl = preferences.getString("APIURL", "")!! if (prefApiUrl.isEmpty()) { @@ -133,7 +134,6 @@ class Kavita(private val context: Context, id: Long) : TrackService(id), Enhance } val prefApiKey = preferences.getString("APIKEY", "")!! val token = api.getNewToken(apiUrl = prefApiUrl, apiKey = prefApiKey) - if (token.isNullOrEmpty()) { // Source is not accessible. Skip continue diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaApi.kt index fb351a12c..c3c9b3147 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaApi.kt @@ -13,51 +13,57 @@ import okhttp3.Dns import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.RequestBody.Companion.toRequestBody +import java.io.IOException import java.net.SocketTimeoutException class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor) { - private val authClient = client.newBuilder().dns(Dns.SYSTEM).addInterceptor(interceptor).build() + + private val authClient = client.newBuilder() + .dns(Dns.SYSTEM) + .addInterceptor(interceptor) + .build() + fun getApiFromUrl(url: String): String { return url.split("/api/").first() + "/api" } + /* + * Uses url to compare against each source APIURL's to get the correct custom source preference. + * Now having source preference we can do getString("APIKEY") + * Authenticates to get the token + * Saves the token in the var jwtToken + */ fun getNewToken(apiUrl: String, apiKey: String): String? { - /* - * Uses url to compare against each source APIURL's to get the correct custom source preference. - * Now having source preference we can do getString("APIKEY") - * Authenticates to get the token - * Saves the token in the var jwtToken - */ - val request = POST( "$apiUrl/Plugin/authenticate?apiKey=$apiKey&pluginName=Tachiyomi-Kavita", body = "{}".toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()), ) try { client.newCall(request).execute().use { - if (it.code == 200) { - return it.parseAs().token - } - if (it.code == 401) { - logcat(LogPriority.WARN) { "Unauthorized / api key not valid:Cleaned api URL:${apiUrl}Api key is empty:${apiKey.isEmpty()}" } - throw Exception("Unauthorized / api key not valid") - } - if (it.code == 500) { - logcat(LogPriority.WARN) { "Error fetching jwt token. Cleaned api URL:$apiUrl Api key is empty:${apiKey.isEmpty()}" } - throw Exception("Error fetching jwt token") + when (it.code) { + 200 -> return it.parseAs().token + 401 -> { + logcat(LogPriority.WARN) { "Unauthorized / api key not valid: Cleaned api URL: $apiUrl, Api key is empty: ${apiKey.isEmpty()}" } + throw IOException("Unauthorized / api key not valid") + } + 500 -> { + logcat(LogPriority.WARN) { "Error fetching JWT token. Cleaned api URL: $apiUrl, Api key is empty: ${apiKey.isEmpty()}" } + throw IOException("Error fetching JWT token") + } + else -> {} } } - // Not sure which one to cathc + // Not sure which one to catch } catch (e: SocketTimeoutException) { logcat(LogPriority.WARN) { - "Could not fetch jwt token. Probably due to connectivity issue or the url '$apiUrl' is not available. Skipping" + "Could not fetch JWT token. Probably due to connectivity issue or the url '$apiUrl' is not available, skipping" } return null } catch (e: Exception) { logcat(LogPriority.ERROR) { - "Unhandled Exception fetching jwt token for url: '$apiUrl'" + "Unhandled exception fetching JWT token for url: '$apiUrl'" } - throw e + throw IOException(e) } return null @@ -67,16 +73,17 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor return "${getApiFromUrl(url)}/Series/volumes?seriesId=${getIdFromUrl(url)}" } + /* Strips serie id from URL */ private fun getIdFromUrl(url: String): Int { - /*Strips serie id from Url*/ return url.substringAfterLast("/").toInt() } + /* + * Returns total chapters in the series. + * Ignores volumes. + * Volumes consisting of 1 file treated as chapter + */ private fun getTotalChapters(url: String): Int { - /*Returns total chapters in the series. - * Ignores volumes. - * Volumes consisting of 1 file treated as chapter - */ val requestUrl = getApiVolumesUrl(url) try { val listVolumeDto = authClient.newCall(GET(requestUrl)) @@ -103,50 +110,46 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor val serieId = getIdFromUrl(url) val requestUrl = "${getApiFromUrl(url)}/Tachiyomi/latest-chapter?seriesId=$serieId" try { - authClient.newCall(GET(requestUrl)) - .execute().use { - if (it.code == 200) { - return it.parseAs().number!!.replace(",", ".").toFloat() - } - if (it.code == 204) { - return 0F - } + authClient.newCall(GET(requestUrl)).execute().use { + if (it.code == 200) { + return it.parseAs().number!!.replace(",", ".").toFloat() } + if (it.code == 204) { + return 0F + } + } } catch (e: Exception) { - logcat(LogPriority.WARN, e) { "Exception getting latest chapter read. Could not get itemRequest:$requestUrl" } + logcat(LogPriority.WARN, e) { "Exception getting latest chapter read. Could not get itemRequest: $requestUrl" } throw e } return 0F } - suspend fun getTrackSearch(url: String): TrackSearch = - withIOContext { - try { - val serieDto: SeriesDto = - authClient.newCall(GET(url)) - .await() - .parseAs() + suspend fun getTrackSearch(url: String): TrackSearch = withIOContext { + try { + val serieDto: SeriesDto = authClient.newCall(GET(url)) + .await() + .parseAs() - val track = serieDto.toTrack() + val track = serieDto.toTrack() + track.apply { + cover_url = serieDto.thumbnail_url.toString() + tracking_url = url + total_chapters = getTotalChapters(url) - track.apply { - cover_url = serieDto.thumbnail_url.toString() - tracking_url = url - total_chapters = getTotalChapters(url) - - title = serieDto.name - status = when (serieDto.pagesRead) { - serieDto.pages -> Kavita.COMPLETED - 0 -> Kavita.UNREAD - else -> Kavita.READING - } - last_chapter_read = getLatestChapterRead(url) + title = serieDto.name + status = when (serieDto.pagesRead) { + serieDto.pages -> Kavita.COMPLETED + 0 -> Kavita.UNREAD + else -> Kavita.READING } - } catch (e: Exception) { - logcat(LogPriority.WARN, e) { "Could not get item: $url" } - throw e + last_chapter_read = getLatestChapterRead(url) } + } catch (e: Exception) { + logcat(LogPriority.WARN, e) { "Could not get item: $url" } + throw e } + } suspend fun updateProgress(track: Track): Track { val requestUrl = "${getApiFromUrl(track.tracking_url)}/Tachiyomi/mark-chapter-until-as-read?seriesId=${getIdFromUrl(track.tracking_url)}&chapterNumber=${track.last_chapter_read}" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaModels.kt index abcc4d9c1..3900c3f39 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaModels.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaModels.kt @@ -21,7 +21,6 @@ data class SeriesDto( val created: String? = "", val libraryId: Int, val libraryName: String? = "", - ) { fun toTrack(): TrackSearch = TrackSearch.create(TrackManager.KAVITA).also { it.title = name @@ -63,6 +62,23 @@ data class AuthenticationDto( val apiKey: String, ) +class OAuth( + val authentications: List = listOf( + SourceAuth(1), + SourceAuth(2), + SourceAuth(3), + ), +) { + fun getToken(apiUrl: String): String? { + for (authentication in authentications) { + if (authentication.apiUrl == apiUrl) { + return authentication.jwtToken + } + } + return null + } +} + data class SourceAuth( var sourceId: Int, var apiUrl: String = "", diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/OAuth.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/OAuth.kt deleted file mode 100644 index 426ba99b0..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/OAuth.kt +++ /dev/null @@ -1,19 +0,0 @@ -package eu.kanade.tachiyomi.data.track.kavita - -class OAuth( - val authentications: List = listOf( - SourceAuth(1), - SourceAuth(2), - SourceAuth(3), - ), -) { - - fun getToken(apiUrl: String): String? { - for (authentication in authentications) { - if (authentication.apiUrl == apiUrl) { - return authentication.jwtToken - } - } - return null - } -}