Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Hikka Tracker Integration #1386

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8ac5fd4
feat: add Hikka logo to resources
Lorg0n Oct 17, 2024
a357b5e
feat: add core Hikka tracker classes and DTOs for API requests
Lorg0n Oct 17, 2024
9444cee
feat: update AndroidManifest for auth support, modify auth-related cl…
Lorg0n Oct 17, 2024
b018928
feat: improve Hikka authorization
Lorg0n Oct 17, 2024
328dd96
feat: improve Hikka authorization
Lorg0n Oct 17, 2024
3d939ca
feat: more optimization Hikka authorization
Lorg0n Oct 17, 2024
3c8a60f
ref: added more specificity and removed unnecessary code in Hikka Api
Lorg0n Oct 17, 2024
16581de
fix: corrected function names and added reread logic
Lorg0n Oct 22, 2024
41e6d08
fix: slightly optimised the function of string to number conversion a…
Lorg0n Oct 26, 2024
94532a2
fix: recode the rereading logic in HikkaApi
Lorg0n Oct 26, 2024
f27c321
fix: fixed a bug where the server returned a 404 error due to receivi…
Lorg0n Oct 26, 2024
30a13be
ref: optimized imports and reduced the length of lines
Lorg0n Oct 27, 2024
f45c0a8
ref: optimized imports and refactor code
Lorg0n Oct 27, 2024
fe8f40f
feat: changed authorization principle, removed dependence on third-pa…
Lorg0n Oct 27, 2024
0fde5e7
fix: remove unused import
Lorg0n Oct 27, 2024
8ec826c
fix: fixed a bug in DTO that caused a data parsing error
Lorg0n Oct 28, 2024
eaf385a
fix: fixed a bug in DTO that caused a data parsing error in manga search
Lorg0n Oct 30, 2024
3445509
fix: update the API link by replacing the deprecated one
Lorg0n Jan 1, 2025
586fd38
feat: changed the buffer time to check the token for expiry
Lorg0n Jan 1, 2025
fcc8c76
ref: deleted unnecessary fields in the HKUser
Lorg0n Jan 1, 2025
a2f269d
ref: deleted deleted unnecessary imports
Lorg0n Jan 1, 2025
2047d48
feat: change logic of binding track from Hikka source
Lorg0n Jan 1, 2025
9ed5741
ref: deleted unnecessary comment
Lorg0n Jan 1, 2025
2f7509e
style: nothing change
Lorg0n Jan 1, 2025
04b3593
ref: simplify methods using expression bodies
Lorg0n Feb 26, 2025
ec9fa64
fix: changed the sorting in the search to from higher score to lower …
Lorg0n Feb 26, 2025
f10813a
ref: move annotations above properties and optimize string formatting…
Lorg0n Feb 26, 2025
5d5d640
fix: auth issue caused by null value
Lorg0n Feb 26, 2025
6612764
style: add support of Hikka.io to README.md
Lorg0n Feb 26, 2025
de772ea
Merge branch 'main' into Add-Hikka-Tracker
Lorg0n Feb 27, 2025
b347453
fix: resolved the Hikka title bug in SettingsTrackingScreen.kt
Lorg0n Feb 27, 2025
3030ac0
chore: apply spotless formatting
Lorg0n Feb 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Discover and read manga, webtoons, comics, and more – easier than ever on your

* Local reading of content.
* A configurable reader with multiple viewers, reading directions and other settings.
* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.app/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support.
* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.app/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), [Bangumi](https://bgm.tv/) and [Hikka](https://hikka.io/) support.
* Categories to organize your library.
* Light and dark themes.
* Schedule updating your library for new chapters.
Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@
<data android:host="bangumi-auth" />
<data android:host="myanimelist-auth" />
<data android:host="shikimori-auth" />
<data android:host="hikka-auth" />
</intent-filter>
</activity>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.data.track.anilist.AnilistApi
import eu.kanade.tachiyomi.data.track.bangumi.BangumiApi
import eu.kanade.tachiyomi.data.track.hikka.HikkaApi
import eu.kanade.tachiyomi.data.track.myanimelist.MyAnimeListApi
import eu.kanade.tachiyomi.data.track.shikimori.ShikimoriApi
import eu.kanade.tachiyomi.util.system.openInBrowser
Expand Down Expand Up @@ -168,6 +169,11 @@ object SettingsTrackingScreen : SearchableSettings {
login = { context.openInBrowser(BangumiApi.authUrl(), forceDefaultBrowser = true) },
logout = { dialog = LogoutDialog(trackerManager.bangumi) },
),
Preference.PreferenceItem.TrackerPreference(
tracker = trackerManager.hikka,
login = { context.openInBrowser(HikkaApi.authUrl(), forceDefaultBrowser = true) },
logout = { dialog = LogoutDialog(trackerManager.hikka) },
),
Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.tracking_info)),
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.track

import eu.kanade.tachiyomi.data.track.anilist.Anilist
import eu.kanade.tachiyomi.data.track.bangumi.Bangumi
import eu.kanade.tachiyomi.data.track.hikka.Hikka
import eu.kanade.tachiyomi.data.track.kavita.Kavita
import eu.kanade.tachiyomi.data.track.kitsu.Kitsu
import eu.kanade.tachiyomi.data.track.komga.Komga
Expand All @@ -28,8 +29,9 @@ class TrackerManager {
val mangaUpdates = MangaUpdates(7L)
val kavita = Kavita(KAVITA)
val suwayomi = Suwayomi(9L)
val hikka = Hikka(10L)

val trackers = listOf(myAnimeList, aniList, kitsu, shikimori, bangumi, komga, mangaUpdates, kavita, suwayomi)
val trackers = listOf(myAnimeList, aniList, kitsu, shikimori, bangumi, komga, mangaUpdates, kavita, suwayomi, hikka)

fun loggedInTrackers() = trackers.filter { it.isLoggedIn }

Expand Down
155 changes: 155 additions & 0 deletions app/src/main/java/eu/kanade/tachiyomi/data/track/hikka/Hikka.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package eu.kanade.tachiyomi.data.track.hikka

import android.graphics.Color
import dev.icerock.moko.resources.StringResource
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.BaseTracker
import eu.kanade.tachiyomi.data.track.DeletableTracker
import eu.kanade.tachiyomi.data.track.hikka.dto.HKOAuth
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.serialization.json.Json
import tachiyomi.i18n.MR
import uy.kohesive.injekt.injectLazy
import tachiyomi.domain.track.model.Track as DomainTrack

class Hikka(id: Long) : BaseTracker(id, "Hikka"), DeletableTracker {

companion object {
const val READING = 0L
const val COMPLETED = 1L
const val ON_HOLD = 2L
const val DROPPED = 3L
const val PLAN_TO_READ = 4L
const val REREADING = 5L

private val SCORE_LIST = IntRange(0, 10)
.map(Int::toString)
.toImmutableList()
}

private val json: Json by injectLazy()

private val interceptor by lazy { HikkaInterceptor(this) }
private val api by lazy { HikkaApi(id, client, interceptor) }

override fun getLogoColor(): Int = Color.rgb(0, 0, 0)

override fun getLogo(): Int = R.drawable.ic_tracker_hikka

override fun getStatusList(): List<Long> {
return listOf(
READING,
COMPLETED,
ON_HOLD,
DROPPED,
PLAN_TO_READ,
REREADING,
)
}

override fun getStatus(status: Long): StringResource? = when (status) {
READING -> MR.strings.reading
PLAN_TO_READ -> MR.strings.plan_to_read
COMPLETED -> MR.strings.completed
ON_HOLD -> MR.strings.on_hold
DROPPED -> MR.strings.dropped
REREADING -> MR.strings.repeating
else -> null
}

override fun getReadingStatus(): Long = READING

override fun getRereadingStatus(): Long = REREADING

override fun getCompletionStatus(): Long = COMPLETED

override fun getScoreList(): ImmutableList<String> = SCORE_LIST

override fun displayScore(track: DomainTrack): String {
return track.score.toInt().toString()
}

override suspend fun update(
track: Track,
didReadChapter: Boolean,
): Track {
if (track.status != COMPLETED) {
if (didReadChapter) {
if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) {
track.status = COMPLETED
} else if (track.status != REREADING) {
track.status = READING
}
}
}
return api.updateUserManga(track)
}

override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {
val readContent = api.getRead(track)
val remoteTrack = api.getManga(track)

track.copyPersonalFrom(remoteTrack)
track.library_id = remoteTrack.library_id

if (track.status != COMPLETED) {
val isRereading = track.status == REREADING
track.status = if (!isRereading && hasReadChapters) READING else track.status
}

return if (readContent != null) {
track.score = readContent.score.toDouble()
track.last_chapter_read = readContent.chapters.toDouble()
track.score = readContent.score.toDouble()
update(track)
} else {
track.score = 0.0
update(track)
}
}

override suspend fun search(query: String): List<TrackSearch> = api.searchManga(query)

override suspend fun refresh(track: Track): Track {
val remoteTrack = api.updateUserManga(track)
track.copyPersonalFrom(remoteTrack)
track.total_chapters = remoteTrack.total_chapters
return track
}

override suspend fun login(username: String, password: String) = login(password)

suspend fun login(reference: String) {
try {
val oauth = api.accessToken(reference)
interceptor.setAuth(oauth)
val user = api.getCurrentUser()
saveCredentials(user.reference, oauth.accessToken)
} catch (e: Throwable) {
logout()
}
}

override suspend fun delete(track: DomainTrack) = api.deleteUserManga(track)

override fun logout() {
super.logout()
trackPreferences.trackToken(this).delete()
interceptor.setAuth(null)
}

fun saveOAuth(oAuth: HKOAuth?) {
trackPreferences.trackToken(this).set(json.encodeToString(oAuth))
}

fun loadOAuth(): HKOAuth? {
return try {
json.decodeFromString<HKOAuth>(trackPreferences.trackToken(this).get())
} catch (e: Exception) {
null
}
}
}
Loading
Loading