|
@@ -0,0 +1,113 @@
|
|
|
+package eu.kanade.tachiyomi.data.track.suwayomi
|
|
|
+
|
|
|
+import android.app.Application
|
|
|
+import android.content.SharedPreferences
|
|
|
+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.GET
|
|
|
+import eu.kanade.tachiyomi.network.NetworkHelper
|
|
|
+import eu.kanade.tachiyomi.network.PUT
|
|
|
+import eu.kanade.tachiyomi.network.await
|
|
|
+import eu.kanade.tachiyomi.network.parseAs
|
|
|
+import eu.kanade.tachiyomi.util.lang.withIOContext
|
|
|
+import okhttp3.Credentials
|
|
|
+import okhttp3.Dns
|
|
|
+import okhttp3.FormBody
|
|
|
+import okhttp3.Headers
|
|
|
+import okhttp3.OkHttpClient
|
|
|
+import uy.kohesive.injekt.Injekt
|
|
|
+import uy.kohesive.injekt.api.get
|
|
|
+import uy.kohesive.injekt.injectLazy
|
|
|
+import java.nio.charset.Charset
|
|
|
+import java.security.MessageDigest
|
|
|
+
|
|
|
+class TachideskApi {
|
|
|
+ private val network by injectLazy<NetworkHelper>()
|
|
|
+ val client: OkHttpClient =
|
|
|
+ network.client.newBuilder()
|
|
|
+ .dns(Dns.SYSTEM) // don't use DNS over HTTPS as it breaks IP addressing
|
|
|
+ .build()
|
|
|
+ fun headersBuilder(): Headers.Builder = Headers.Builder().apply {
|
|
|
+ add("User-Agent", network.defaultUserAgent)
|
|
|
+ if (basePassword.isNotEmpty() && baseLogin.isNotEmpty()) {
|
|
|
+ val credentials = Credentials.basic(baseLogin, basePassword)
|
|
|
+ add("Authorization", credentials)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ val headers: Headers by lazy { headersBuilder().build() }
|
|
|
+
|
|
|
+ private val baseUrl by lazy { getPrefBaseUrl() }
|
|
|
+ private val baseLogin by lazy { getPrefBaseLogin() }
|
|
|
+ private val basePassword by lazy { getPrefBasePassword() }
|
|
|
+
|
|
|
+ suspend fun getTrackSearch(trackUrl: String): TrackSearch = withIOContext {
|
|
|
+ val url = try {
|
|
|
+ // test if getting api url or manga id
|
|
|
+ val mangaId = trackUrl.toLong()
|
|
|
+ "$baseUrl/api/v1/manga/$mangaId"
|
|
|
+ } catch (e: NumberFormatException) {
|
|
|
+ trackUrl
|
|
|
+ }
|
|
|
+
|
|
|
+ val manga = client.newCall(GET("$url/full", headers)).await().parseAs<MangaDataClass>()
|
|
|
+
|
|
|
+ TrackSearch.create(TrackManager.SUWAYOMI).apply {
|
|
|
+ title = manga.title
|
|
|
+ cover_url = "$url/thumbnail"
|
|
|
+ summary = manga.description
|
|
|
+ tracking_url = url
|
|
|
+ total_chapters = manga.chapterCount.toInt()
|
|
|
+ publishing_status = manga.status
|
|
|
+ last_chapter_read = manga.lastChapterRead?.chapterNumber ?: 0F
|
|
|
+ status = when (manga.unreadCount) {
|
|
|
+ manga.chapterCount -> Suwayomi.UNREAD
|
|
|
+ 0L -> Suwayomi.COMPLETED
|
|
|
+ else -> Suwayomi.READING
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ suspend fun updateProgress(track: Track): Track {
|
|
|
+ val url = track.tracking_url
|
|
|
+ val chapters = client.newCall(GET("$url/chapters", headers)).await().parseAs<List<ChapterDataClass>>()
|
|
|
+ val lastChapterIndex = chapters.first { it.chapterNumber == track.last_chapter_read }.index
|
|
|
+
|
|
|
+ client.newCall(
|
|
|
+ PUT(
|
|
|
+ "$url/chapter/$lastChapterIndex",
|
|
|
+ headers,
|
|
|
+ FormBody.Builder(Charset.forName("utf8"))
|
|
|
+ .add("markPrevRead", "true")
|
|
|
+ .add("read", "true")
|
|
|
+ .build(),
|
|
|
+ ),
|
|
|
+ ).await()
|
|
|
+
|
|
|
+ return getTrackSearch(track.tracking_url)
|
|
|
+ }
|
|
|
+
|
|
|
+ val tachideskExtensionId by lazy {
|
|
|
+ val key = "tachidesk/en/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
|
|
|
+ }
|
|
|
+
|
|
|
+ private val preferences: SharedPreferences by lazy {
|
|
|
+ Injekt.get<Application>().getSharedPreferences("source_$tachideskExtensionId", 0x0000)
|
|
|
+ }
|
|
|
+
|
|
|
+ companion object {
|
|
|
+ private const val ADDRESS_TITLE = "Server URL Address"
|
|
|
+ private const val ADDRESS_DEFAULT = ""
|
|
|
+ private const val LOGIN_TITLE = "Login (Basic Auth)"
|
|
|
+ private const val LOGIN_DEFAULT = ""
|
|
|
+ private const val PASSWORD_TITLE = "Password (Basic Auth)"
|
|
|
+ private const val PASSWORD_DEFAULT = ""
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun getPrefBaseUrl(): String = preferences.getString(ADDRESS_TITLE, ADDRESS_DEFAULT)!!
|
|
|
+ private fun getPrefBaseLogin(): String = preferences.getString(LOGIN_TITLE, LOGIN_DEFAULT)!!
|
|
|
+ private fun getPrefBasePassword(): String = preferences.getString(PASSWORD_TITLE, PASSWORD_DEFAULT)!!
|
|
|
+}
|