Tracker-related cleanup

This commit is contained in:
arkon
2023-09-25 23:22:16 -04:00
parent 86edce0d87
commit c8e226acb2
54 changed files with 417 additions and 395 deletions
@@ -19,7 +19,7 @@ import eu.kanade.tachiyomi.data.download.DownloadCache
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.DownloadProvider
import eu.kanade.tachiyomi.data.saver.ImageSaver
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.network.JavaScriptEngine
import eu.kanade.tachiyomi.network.NetworkHelper
@@ -132,7 +132,7 @@ class AppModule(val app: Application) : InjektModule {
addSingletonFactory { DownloadManager(app) }
addSingletonFactory { DownloadCache(app) }
addSingletonFactory { TrackManager(app) }
addSingletonFactory { TrackerManager() }
addSingletonFactory { DelayedTrackingStore(app) }
addSingletonFactory { ImageSaver(app) }
@@ -9,7 +9,7 @@ import eu.kanade.domain.ui.UiPreferences
import eu.kanade.tachiyomi.core.security.SecurityPreferences
import eu.kanade.tachiyomi.data.backup.BackupCreateJob
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.network.NetworkPreferences
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
@@ -47,7 +47,7 @@ object Migrations {
libraryPreferences: LibraryPreferences,
readerPreferences: ReaderPreferences,
backupPreferences: BackupPreferences,
trackManager: TrackManager,
trackerManager: TrackerManager,
): Boolean {
val lastVersionCode = preferenceStore.getInt("last_version_code", 0)
val oldVersion = lastVersionCode.get()
@@ -135,8 +135,8 @@ object Migrations {
// Force MAL log out due to login flow change
// v52: switched from scraping to WebView
// v53: switched from WebView to OAuth
if (trackManager.myAnimeList.isLoggedIn) {
trackManager.myAnimeList.logout()
if (trackerManager.myAnimeList.isLoggedIn) {
trackerManager.myAnimeList.logout()
context.toast(R.string.myanimelist_relogin)
}
}
@@ -342,7 +342,7 @@ object Migrations {
"pref_filter_library_started",
"pref_filter_library_bookmarked",
"pref_filter_library_completed",
) + trackManager.services.map { "pref_filter_library_tracked_${it.id}" }
) + trackerManager.trackers.map { "pref_filter_library_tracked_${it.id}" }
prefKeys.forEach { key ->
val pref = preferenceStore.getInt(key, 0)
@@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.data.backup
import android.content.Context
import android.net.Uri
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.util.BackupUtil
import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.Injekt
@@ -11,7 +11,7 @@ import uy.kohesive.injekt.api.get
class BackupFileValidator(
private val sourceManager: SourceManager = Injekt.get(),
private val trackManager: TrackManager = Injekt.get(),
private val trackerManager: TrackerManager = Injekt.get(),
) {
/**
@@ -50,7 +50,7 @@ class BackupFileValidator(
.map { it.syncId }
.distinct()
val missingTrackers = trackers
.mapNotNull { trackManager.getService(it.toLong()) }
.mapNotNull { trackerManager.get(it.toLong()) }
.filter { !it.isLoggedIn }
.map { it.name }
.sorted()
@@ -3,9 +3,9 @@ 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
* Tracker that support deleting am entry from a user's list.
*/
interface DeletableTrackService {
interface DeletableTracker {
suspend fun delete(track: Track): Track
}
@@ -6,31 +6,32 @@ import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.track.model.Track
/**
* An Enhanced Track Service will never prompt the user to match a manga with the remote.
* It is expected that such Track Service can only work with specific sources and unique IDs.
* A tracker that will never prompt the user to manually bind an entry.
* It is expected that such tracker can only work with specific sources and unique IDs.
*/
interface EnhancedTrackService {
interface EnhancedTracker {
/**
* This TrackService will only work with the sources that are accepted by this filter function.
* This tracker will only work with the sources that are accepted by this filter function.
*/
fun accept(source: Source): Boolean {
return source::class.qualifiedName in getAcceptedSources()
}
/**
* Fully qualified source classes that this track service is compatible with.
* Fully qualified source classes that this tracker is compatible with.
*/
fun getAcceptedSources(): List<String>
fun loginNoop()
/**
* match is similar to TrackService.search, but only return zero or one match.
* Similar to [Tracker].search, but only returns zero or one match.
*/
suspend fun match(manga: Manga): TrackSearch?
/**
* Checks whether the provided source/track/manga triplet is from this TrackService
* Checks whether the provided source/track/manga triplet is from this [Tracker]
*/
fun isTrackFrom(track: Track, manga: Manga, source: Source?): Boolean
@@ -5,7 +5,7 @@ import androidx.annotation.CallSuper
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import eu.kanade.domain.chapter.interactor.SyncChapterProgressWithTrack
import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack
import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.domain.track.model.toDomainTrack
import eu.kanade.domain.track.service.TrackPreferences
@@ -28,7 +28,7 @@ import uy.kohesive.injekt.injectLazy
import java.time.ZoneOffset
import tachiyomi.domain.track.model.Track as DomainTrack
abstract class TrackService(val id: Long, val name: String) {
abstract class Tracker(val id: Long, val name: String) {
val trackPreferences: TrackPreferences by injectLazy()
val networkService: NetworkHelper by injectLazy()
@@ -83,7 +83,7 @@ abstract class TrackService(val id: Long, val name: String) {
@CallSuper
open fun logout() {
trackPreferences.setTrackCredentials(this, "", "")
trackPreferences.setCredentials(this, "", "")
}
open val isLoggedIn: Boolean
@@ -95,7 +95,7 @@ abstract class TrackService(val id: Long, val name: String) {
fun getPassword() = trackPreferences.trackPassword(this).get()
fun saveCredentials(username: String, password: String) {
trackPreferences.setTrackCredentials(this, username, password)
trackPreferences.setCredentials(this, username, password)
}
// TODO: move this to an interactor, and update all trackers based on common data
@@ -111,7 +111,7 @@ abstract class TrackService(val id: Long, val name: String) {
insertTrack.await(track)
// TODO: merge into SyncChaptersWithTrackServiceTwoWay?
// TODO: merge into [SyncChapterProgressWithTrack]?
// Update chapter progress if newer chapters marked read locally
if (hasReadChapters) {
val latestLocalReadChapterNumber = allChapters
@@ -143,7 +143,7 @@ abstract class TrackService(val id: Long, val name: String) {
}
}
syncChapterProgressWithTrack.await(mangaId, track, this@TrackService)
syncChapterProgressWithTrack.await(mangaId, track, this@Tracker)
}
} catch (e: Throwable) {
withUIContext { Injekt.get<Application>().toast(e.message) }
@@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.data.track
import android.content.Context
import eu.kanade.tachiyomi.data.track.anilist.Anilist
import eu.kanade.tachiyomi.data.track.bangumi.Bangumi
import eu.kanade.tachiyomi.data.track.kavita.Kavita
@@ -11,33 +10,27 @@ import eu.kanade.tachiyomi.data.track.myanimelist.MyAnimeList
import eu.kanade.tachiyomi.data.track.shikimori.Shikimori
import eu.kanade.tachiyomi.data.track.suwayomi.Suwayomi
class TrackManager(context: Context) {
class TrackerManager {
companion object {
const val MYANIMELIST = 1L
const val ANILIST = 2L
const val KITSU = 3L
const val SHIKIMORI = 4L
const val BANGUMI = 5L
const val KOMGA = 6L
const val MANGA_UPDATES = 7L
const val KAVITA = 8L
const val SUWAYOMI = 9L
}
val myAnimeList = MyAnimeList(MYANIMELIST)
val myAnimeList = MyAnimeList(1L)
val aniList = Anilist(ANILIST)
val kitsu = Kitsu(KITSU)
val shikimori = Shikimori(SHIKIMORI)
val bangumi = Bangumi(BANGUMI)
val komga = Komga(KOMGA)
val mangaUpdates = MangaUpdates(MANGA_UPDATES)
val kavita = Kavita(context, KAVITA)
val suwayomi = Suwayomi(SUWAYOMI)
val shikimori = Shikimori(4L)
val bangumi = Bangumi(5L)
val komga = Komga(6L)
val mangaUpdates = MangaUpdates(7L)
val kavita = Kavita(KAVITA)
val suwayomi = Suwayomi(9L)
val services = listOf(myAnimeList, aniList, kitsu, shikimori, bangumi, komga, mangaUpdates, kavita, suwayomi)
val trackers = listOf(myAnimeList, aniList, kitsu, shikimori, bangumi, komga, mangaUpdates, kavita, suwayomi)
fun getService(id: Long) = services.find { it.id == id }
fun get(id: Long) = trackers.find { it.id == id }
fun hasLoggedServices() = services.any { it.isLoggedIn }
fun hasLoggedIn() = trackers.any { it.isLoggedIn }
}
@@ -4,15 +4,15 @@ 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.DeletableTracker
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy
import tachiyomi.domain.track.model.Track as DomainTrack
class Anilist(id: Long) : TrackService(id, "AniList"), DeletableTrackService {
class Anilist(id: Long) : Tracker(id, "AniList"), DeletableTracker {
companion object {
const val READING = 1
@@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.data.track.anilist
import eu.kanade.domain.track.service.TrackPreferences
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.serialization.Serializable
import uy.kohesive.injekt.injectLazy
@@ -20,7 +20,7 @@ data class ALManga(
val total_chapters: Int,
) {
fun toTrack() = TrackSearch.create(TrackManager.ANILIST).apply {
fun toTrack() = TrackSearch.create(TrackerManager.ANILIST).apply {
media_id = this@ALManga.media_id
title = title_user_pref
total_chapters = this@ALManga.total_chapters
@@ -50,7 +50,7 @@ data class ALUserManga(
val manga: ALManga,
) {
fun toTrack() = Track.create(TrackManager.ANILIST).apply {
fun toTrack() = Track.create(TrackerManager.ANILIST).apply {
media_id = manga.media_id
title = manga.title_user_pref
status = toTrackStatus()
@@ -62,7 +62,7 @@ data class ALUserManga(
total_chapters = manga.total_chapters
}
fun toTrackStatus() = when (list_status) {
private fun toTrackStatus() = when (list_status) {
"CURRENT" -> Anilist.READING
"COMPLETED" -> Anilist.COMPLETED
"PAUSED" -> Anilist.ON_HOLD
@@ -4,19 +4,19 @@ 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.TrackService
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy
class Bangumi(id: Long) : TrackService(id, "Bangumi") {
class Bangumi(id: Long) : Tracker(id, "Bangumi") {
private val json: Json by injectLazy()
private val interceptor by lazy { BangumiInterceptor(this) }
private val api by lazy { BangumiApi(client, interceptor) }
private val api by lazy { BangumiApi(id, client, interceptor) }
override fun getScoreList(): List<String> {
return IntRange(0, 10).map(Int::toString)
@@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.track.bangumi
import android.net.Uri
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.GET
import eu.kanade.tachiyomi.network.POST
@@ -26,7 +25,11 @@ import uy.kohesive.injekt.injectLazy
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
class BangumiApi(private val client: OkHttpClient, interceptor: BangumiInterceptor) {
class BangumiApi(
private val trackId: Long,
private val client: OkHttpClient,
interceptor: BangumiInterceptor,
) {
private val json: Json by injectLazy()
@@ -105,7 +108,7 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
} else {
0
}
return TrackSearch.create(TrackManager.BANGUMI).apply {
return TrackSearch.create(trackId).apply {
media_id = obj["id"]!!.jsonPrimitive.long
title = obj["name_cn"]!!.jsonPrimitive.content
cover_url = coverUrl
@@ -1,20 +1,21 @@
package eu.kanade.tachiyomi.data.track.kavita
import android.content.Context
import android.content.SharedPreferences
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.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.Source
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.injectLazy
import java.security.MessageDigest
import tachiyomi.domain.track.model.Track as DomainTrack
class Kavita(private val context: Context, id: Long) : TrackService(id, "Kavita"), EnhancedTrackService {
class Kavita(id: Long) : Tracker(id, "Kavita"), EnhancedTracker {
companion object {
const val UNREAD = 1
@@ -27,6 +28,8 @@ class Kavita(private val context: Context, id: Long) : TrackService(id, "Kavita"
private val interceptor by lazy { KavitaInterceptor(this) }
val api by lazy { KavitaApi(client, interceptor) }
private val sourceManager: SourceManager by injectLazy()
override fun getLogo(): Int = R.drawable.ic_tracker_kavita
override fun getLogoColor() = Color.rgb(74, 198, 148)
@@ -83,7 +86,7 @@ class Kavita(private val context: Context, id: Long) : TrackService(id, "Kavita"
saveCredentials("user", "pass")
}
// TrackService.isLogged works by checking that credentials are saved.
// [Tracker].isLogged works by checking that credentials are saved.
// By saving dummy, unused credentials, we can activate the tracker simply by login/logout
override fun loginNoop() {
saveCredentials("user", "pass")
@@ -110,28 +113,29 @@ class Kavita(private val context: Context, id: Long) : TrackService(id, "Kavita"
fun loadOAuth() {
val oauth = OAuth()
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
for (id in 1..3) {
val authentication = oauth.authentications[id - 1]
val sourceId by lazy {
val key = "kavita_$id/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 {
context.getSharedPreferences("source_$sourceSuffixID", Context.MODE_PRIVATE)
}
val prefApiUrl = preferences.getString("APIURL", "")!!
if (prefApiUrl.isEmpty()) {
val preferences = (sourceManager.get(sourceId) as ConfigurableSource).getPreferences()
val prefApiUrl = preferences.getString("APIURL", "")
val prefApiKey = preferences.getString("APIKEY", "")
if (prefApiUrl.isNullOrEmpty() || prefApiKey.isNullOrEmpty()) {
// Source not configured. Skip
continue
}
val prefApiKey = preferences.getString("APIKEY", "")!!
val token = api.getNewToken(apiUrl = prefApiUrl, apiKey = prefApiKey)
if (token.isNullOrEmpty()) {
// Source is not accessible. Skip
continue
}
authentication.apiUrl = prefApiUrl
authentication.jwtToken = token.toString()
}
@@ -1,6 +1,6 @@
package eu.kanade.tachiyomi.data.track.kavita
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.serialization.Serializable
@@ -22,7 +22,7 @@ data class SeriesDto(
val libraryId: Int,
val libraryName: String? = "",
) {
fun toTrack(): TrackSearch = TrackSearch.create(TrackManager.KAVITA).also {
fun toTrack(): TrackSearch = TrackSearch.create(TrackerManager.KAVITA).also {
it.title = name
it.summary = ""
}
@@ -4,15 +4,15 @@ 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.DeletableTracker
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy
import java.text.DecimalFormat
class Kitsu(id: Long) : TrackService(id, "Kitsu"), DeletableTrackService {
class Kitsu(id: Long) : Tracker(id, "Kitsu"), DeletableTracker {
companion object {
const val READING = 1
@@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.data.track.kitsu
import androidx.annotation.CallSuper
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject
@@ -35,7 +35,7 @@ class KitsuSearchManga(obj: JsonObject) {
private val endDate = obj["endDate"]?.jsonPrimitive?.contentOrNull
@CallSuper
fun toTrack() = TrackSearch.create(TrackManager.KITSU).apply {
fun toTrack() = TrackSearch.create(TrackerManager.KITSU).apply {
media_id = this@KitsuSearchManga.id
title = canonicalTitle
total_chapters = chapterCount ?: 0
@@ -67,7 +67,7 @@ class KitsuLibManga(obj: JsonObject, manga: JsonObject) {
private val ratingTwenty = obj["attributes"]!!.jsonObject["ratingTwenty"]?.jsonPrimitive?.contentOrNull
val progress = obj["attributes"]!!.jsonObject["progress"]!!.jsonPrimitive.int
fun toTrack() = TrackSearch.create(TrackManager.KITSU).apply {
fun toTrack() = TrackSearch.create(TrackerManager.KITSU).apply {
media_id = libraryId
title = canonicalTitle
total_chapters = chapterCount ?: 0
@@ -4,8 +4,8 @@ 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.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.source.Source
import okhttp3.Dns
@@ -13,7 +13,7 @@ import okhttp3.OkHttpClient
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.track.model.Track as DomainTrack
class Komga(id: Long) : TrackService(id, "Komga"), EnhancedTrackService {
class Komga(id: Long) : Tracker(id, "Komga"), EnhancedTracker {
companion object {
const val UNREAD = 1
@@ -26,7 +26,7 @@ class Komga(id: Long) : TrackService(id, "Komga"), EnhancedTrackService {
.dns(Dns.SYSTEM) // don't use DNS over HTTPS as it breaks IP addressing
.build()
val api by lazy { KomgaApi(client) }
val api by lazy { KomgaApi(id, client) }
override fun getLogo() = R.drawable.ic_tracker_komga
@@ -85,7 +85,7 @@ class Komga(id: Long) : TrackService(id, "Komga"), EnhancedTrackService {
saveCredentials("user", "pass")
}
// TrackService.isLogged works by checking that credentials are saved.
// [Tracker].isLogged works by checking that credentials are saved.
// By saving dummy, unused credentials, we can activate the tracker simply by login/logout
override fun loginNoop() {
saveCredentials("user", "pass")
@@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.data.track.komga
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.awaitSuccess
@@ -19,7 +18,10 @@ import uy.kohesive.injekt.injectLazy
private const val READLIST_API = "/api/v1/readlists"
class KomgaApi(private val client: OkHttpClient) {
class KomgaApi(
private val trackId: Long,
private val client: OkHttpClient,
) {
private val json: Json by injectLazy()
@@ -85,13 +87,13 @@ class KomgaApi(private val client: OkHttpClient) {
return getTrackSearch(track.tracking_url)
}
private fun SeriesDto.toTrack(): TrackSearch = TrackSearch.create(TrackManager.KOMGA).also {
private fun SeriesDto.toTrack(): TrackSearch = TrackSearch.create(trackId).also {
it.title = metadata.title
it.summary = metadata.summary
it.publishing_status = metadata.status
}
private fun ReadListDto.toTrack(): TrackSearch = TrackSearch.create(TrackManager.KOMGA).also {
private fun ReadListDto.toTrack(): TrackSearch = TrackSearch.create(trackId).also {
it.title = name
}
}
@@ -4,13 +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.DeletableTracker
import eu.kanade.tachiyomi.data.track.Tracker
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, "MangaUpdates"), DeletableTrackService {
class MangaUpdates(id: Long) : Tracker(id, "MangaUpdates"), DeletableTracker {
companion object {
const val READING_LIST = 0
@@ -4,14 +4,14 @@ 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.DeletableTracker
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy
class MyAnimeList(id: Long) : TrackService(id, "MyAnimeList"), DeletableTrackService {
class MyAnimeList(id: Long) : Tracker(id, "MyAnimeList"), DeletableTracker {
companion object {
const val READING = 1
@@ -28,7 +28,7 @@ class MyAnimeList(id: Long) : TrackService(id, "MyAnimeList"), DeletableTrackSer
private val json: Json by injectLazy()
private val interceptor by lazy { MyAnimeListInterceptor(this, getPassword()) }
private val api by lazy { MyAnimeListApi(client, interceptor) }
private val api by lazy { MyAnimeListApi(id, client, interceptor) }
override val supportsReadingDates: Boolean = true
@@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.track.myanimelist
import android.net.Uri
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.GET
import eu.kanade.tachiyomi.network.POST
@@ -32,7 +31,11 @@ import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat
import java.util.Locale
class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListInterceptor) {
class MyAnimeListApi(
private val trackId: Long,
private val client: OkHttpClient,
interceptor: MyAnimeListInterceptor,
) {
private val json: Json by injectLazy()
@@ -106,7 +109,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
.parseAs<JsonObject>()
.let {
val obj = it.jsonObject
TrackSearch.create(TrackManager.MYANIMELIST).apply {
TrackSearch.create(trackId).apply {
media_id = obj["id"]!!.jsonPrimitive.long
title = obj["title"]!!.jsonPrimitive.content
summary = obj["synopsis"]?.jsonPrimitive?.content ?: ""
@@ -4,14 +4,14 @@ 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.DeletableTracker
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.injectLazy
class Shikimori(id: Long) : TrackService(id, "Shikimori"), DeletableTrackService {
class Shikimori(id: Long) : Tracker(id, "Shikimori"), DeletableTracker {
companion object {
const val READING = 1
@@ -26,7 +26,7 @@ class Shikimori(id: Long) : TrackService(id, "Shikimori"), DeletableTrackService
private val interceptor by lazy { ShikimoriInterceptor(this) }
private val api by lazy { ShikimoriApi(client, interceptor) }
private val api by lazy { ShikimoriApi(id, client, interceptor) }
override fun getScoreList(): List<String> {
return IntRange(0, 10).map(Int::toString)
@@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.data.track.shikimori
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
@@ -28,7 +27,11 @@ import okhttp3.RequestBody.Companion.toRequestBody
import tachiyomi.core.util.lang.withIOContext
import uy.kohesive.injekt.injectLazy
class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInterceptor) {
class ShikimoriApi(
private val trackId: Long,
private val client: OkHttpClient,
interceptor: ShikimoriInterceptor,
) {
private val json: Json by injectLazy()
@@ -96,7 +99,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
}
private fun jsonToSearch(obj: JsonObject): TrackSearch {
return TrackSearch.create(TrackManager.SHIKIMORI).apply {
return TrackSearch.create(trackId).apply {
media_id = obj["id"]!!.jsonPrimitive.long
title = obj["name"]!!.jsonPrimitive.content
total_chapters = obj["chapters"]!!.jsonPrimitive.int
@@ -110,7 +113,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
}
private fun jsonToTrack(obj: JsonObject, mangas: JsonObject): Track {
return Track.create(TrackManager.SHIKIMORI).apply {
return Track.create(trackId).apply {
title = mangas["name"]!!.jsonPrimitive.content
media_id = obj["id"]!!.jsonPrimitive.long
total_chapters = mangas["chapters"]!!.jsonPrimitive.int
@@ -4,16 +4,16 @@ 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.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.source.Source
import tachiyomi.domain.manga.model.Manga as DomainManga
import tachiyomi.domain.track.model.Track as DomainTrack
class Suwayomi(id: Long) : TrackService(id, "Suwayomi"), EnhancedTrackService {
class Suwayomi(id: Long) : Tracker(id, "Suwayomi"), EnhancedTracker {
val api by lazy { TachideskApi() }
val api by lazy { SuwayomiApi(id) }
override fun getLogo() = R.drawable.ic_tracker_suwayomi
@@ -4,7 +4,6 @@ import android.app.Application
import android.content.Context
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
@@ -24,7 +23,7 @@ import uy.kohesive.injekt.injectLazy
import java.nio.charset.Charset
import java.security.MessageDigest
class TachideskApi {
class SuwayomiApi(private val trackId: Long) {
private val network: NetworkHelper by injectLazy()
private val json: Json by injectLazy()
@@ -62,7 +61,7 @@ class TachideskApi {
.parseAs<MangaDataClass>()
}
TrackSearch.create(TrackManager.SUWAYOMI).apply {
TrackSearch.create(trackId).apply {
title = manga.title
cover_url = "$url/thumbnail"
summary = manga.description.orEmpty()
@@ -101,26 +100,24 @@ class TachideskApi {
return getTrackSearch(track.tracking_url)
}
private val tachideskExtensionId by lazy {
private val sourceId 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", Context.MODE_PRIVATE)
Injekt.get<Application>().getSharedPreferences("source_$sourceId", Context.MODE_PRIVATE)
}
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)!!
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 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 = ""
@@ -35,8 +35,8 @@ import eu.kanade.domain.manga.model.toSManga
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags
@@ -177,7 +177,7 @@ internal class MigrateDialogScreenModel(
}
private val enhancedServices by lazy {
Injekt.get<TrackManager>().services.filterIsInstance<EnhancedTrackService>()
Injekt.get<TrackerManager>().trackers.filterIsInstance<EnhancedTracker>()
}
suspend fun migrateManga(
@@ -15,16 +15,12 @@ import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.core.preference.asState
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.chapter.interactor.SyncChapterProgressWithTrack
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.toDomainManga
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.track.model.toDomainTrack
import eu.kanade.domain.track.interactor.AddTracks
import eu.kanade.presentation.util.ioCoroutineScope
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.util.removeCovers
@@ -37,11 +33,9 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import logcat.LogPriority
import tachiyomi.core.preference.CheckboxState
import tachiyomi.core.preference.mapAsCheckboxState
import tachiyomi.core.util.lang.launchIO
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.category.interactor.SetMangaCategories
import tachiyomi.domain.category.model.Category
@@ -54,7 +48,6 @@ import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.toMangaUpdate
import tachiyomi.domain.source.interactor.GetRemoteManga
import tachiyomi.domain.source.service.SourceManager
import tachiyomi.domain.track.interactor.InsertTrack
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.Date
@@ -76,12 +69,9 @@ class BrowseSourceScreenModel(
private val getManga: GetManga = Injekt.get(),
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(),
private val insertTrack: InsertTrack = Injekt.get(),
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack = Injekt.get(),
private val addTracks: AddTracks = Injekt.get(),
) : StateScreenModel<BrowseSourceScreenModel.State>(State(Listing.valueOf(listingQuery))) {
private val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLoggedIn } }
var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope)
val source = sourceManager.getOrStub(sourceId)
@@ -243,8 +233,7 @@ class BrowseSourceScreenModel(
new = new.removeCovers(coverCache)
} else {
setMangaDefaultChapterFlags.await(manga)
autoAddTrack(manga)
addTracks.bindEnhancedTracks(manga, source)
}
updateManga.await(new.toMangaUpdate())
@@ -281,25 +270,6 @@ class BrowseSourceScreenModel(
}
}
private suspend fun autoAddTrack(manga: Manga) {
loggedServices
.filterIsInstance<EnhancedTrackService>()
.filter { it.accept(source) }
.forEach { service ->
try {
service.match(manga)?.let { track ->
track.manga_id = manga.id
(service as TrackService).bind(track)
insertTrack.await(track.toDomainTrack()!!)
syncChapterProgressWithTrack.await(manga.id, track.toDomainTrack()!!, service)
}
} catch (e: Exception) {
logcat(LogPriority.WARN, e) { "Could not match manga: ${manga.title} with service $service" }
}
}
}
/**
* Get user categories.
*
@@ -23,7 +23,7 @@ import eu.kanade.presentation.manga.DownloadAction
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.download.DownloadCache
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.chapter.getNextUnread
@@ -88,7 +88,7 @@ class LibraryScreenModel(
private val sourceManager: SourceManager = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get(),
private val downloadCache: DownloadCache = Injekt.get(),
private val trackManager: TrackManager = Injekt.get(),
private val trackerManager: TrackerManager = Injekt.get(),
) : StateScreenModel<LibraryScreenModel.State>(State()) {
var activeCategoryIndex: Int by libraryPreferences.lastUsedCategory().asState(coroutineScope)
@@ -101,9 +101,9 @@ class LibraryScreenModel(
getTracksPerManga.subscribe(),
getTrackingFilterFlow(),
downloadCache.changes,
) { searchQuery, library, tracks, loggedInTrackServices, _ ->
) { searchQuery, library, tracks, loggedInTrackers, _ ->
library
.applyFilters(tracks, loggedInTrackServices)
.applyFilters(tracks, loggedInTrackers)
.applySort()
.mapValues { (_, value) ->
if (searchQuery != null) {
@@ -169,7 +169,7 @@ class LibraryScreenModel(
*/
private suspend fun LibraryMap.applyFilters(
trackMap: Map<Long, List<Long>>,
loggedInTrackServices: Map<Long, TriState>,
loggedInTrackers: Map<Long, TriState>,
): LibraryMap {
val prefs = getLibraryItemPreferencesFlow().first()
val downloadedOnly = prefs.globalFilterDownloaded
@@ -180,10 +180,10 @@ class LibraryScreenModel(
val filterBookmarked = prefs.filterBookmarked
val filterCompleted = prefs.filterCompleted
val isNotLoggedInAnyTrack = loggedInTrackServices.isEmpty()
val isNotLoggedInAnyTrack = loggedInTrackers.isEmpty()
val excludedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriState.ENABLED_NOT) it.key else null }
val includedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriState.ENABLED_IS) it.key else null }
val excludedTracks = loggedInTrackers.mapNotNull { if (it.value == TriState.ENABLED_NOT) it.key else null }
val includedTracks = loggedInTrackers.mapNotNull { if (it.value == TriState.ENABLED_IS) it.key else null }
val trackFiltersIsIgnored = includedTracks.isEmpty() && excludedTracks.isEmpty()
val filterFnDownloaded: (LibraryItem) -> Boolean = {
@@ -366,14 +366,14 @@ class LibraryScreenModel(
* @return map of track id with the filter value
*/
private fun getTrackingFilterFlow(): Flow<Map<Long, TriState>> {
val loggedServices = trackManager.services.filter { it.isLoggedIn }
return if (loggedServices.isNotEmpty()) {
val prefFlows = loggedServices
val loggedInTrackers = trackerManager.trackers.filter { it.isLoggedIn }
return if (loggedInTrackers.isNotEmpty()) {
val prefFlows = loggedInTrackers
.map { libraryPreferences.filterTracking(it.id.toInt()).changes() }
.toTypedArray()
combine(*prefFlows) {
loggedServices
.mapIndexed { index, trackService -> trackService.id to it[index] }
loggedInTrackers
.mapIndexed { index, tracker -> tracker.id to it[index] }
.toMap()
}
} else {
@@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.ui.library
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackerManager
import tachiyomi.core.preference.Preference
import tachiyomi.core.preference.TriState
import tachiyomi.core.preference.getAndSet
@@ -22,11 +22,11 @@ class LibrarySettingsScreenModel(
val libraryPreferences: LibraryPreferences = Injekt.get(),
private val setDisplayMode: SetDisplayMode = Injekt.get(),
private val setSortModeForCategory: SetSortModeForCategory = Injekt.get(),
private val trackManager: TrackManager = Injekt.get(),
private val trackerManager: TrackerManager = Injekt.get(),
) : ScreenModel {
val trackServices
get() = trackManager.services.filter { it.isLoggedIn }
val trackers
get() = trackerManager.trackers.filter { it.isLoggedIn }
fun toggleFilter(preference: (LibraryPreferences) -> Preference<TriState>) {
preference(libraryPreferences).getAndSet {
@@ -138,7 +138,7 @@ class MainActivity : BaseActivity() {
libraryPreferences = libraryPreferences,
readerPreferences = Injekt.get(),
backupPreferences = Injekt.get(),
trackManager = Injekt.get(),
trackerManager = Injekt.get(),
)
} else {
false
@@ -23,9 +23,9 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.DownloadCache
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.network.HttpException
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
@@ -84,7 +84,7 @@ class MangaScreenModel(
private val libraryPreferences: LibraryPreferences = Injekt.get(),
readerPreferences: ReaderPreferences = Injekt.get(),
uiPreferences: UiPreferences = Injekt.get(),
private val trackManager: TrackManager = Injekt.get(),
private val trackerManager: TrackerManager = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get(),
private val downloadCache: DownloadCache = Injekt.get(),
private val getMangaAndChapters: GetMangaWithChapters = Injekt.get(),
@@ -105,7 +105,7 @@ class MangaScreenModel(
private val successState: State.Success?
get() = state.value as? State.Success
private val loggedServices by lazy { trackManager.services.filter { it.isLoggedIn } }
private val loggedInTrackers by lazy { trackerManager.trackers.filter { it.isLoggedIn } }
val manga: Manga?
get() = successState?.manga
@@ -317,14 +317,14 @@ class MangaScreenModel(
// Finally match with enhanced tracking when available
val source = state.source
state.trackItems
.map { it.service }
.filterIsInstance<EnhancedTrackService>()
.map { it.tracker }
.filterIsInstance<EnhancedTracker>()
.filter { it.accept(source) }
.forEach { service ->
launchIO {
try {
service.match(manga)?.let { track ->
(service as TrackService).register(track, mangaId)
(service as Tracker).register(track, mangaId)
}
} catch (e: Exception) {
logcat(LogPriority.WARN, e) {
@@ -949,11 +949,11 @@ class MangaScreenModel(
getTracks.subscribe(manga.id)
.catch { logcat(LogPriority.ERROR, it) }
.map { tracks ->
loggedServices
loggedInTrackers
// Map to TrackItem
.map { service -> TrackItem(tracks.find { it.syncId == service.id }, service) }
// Show only if the service supports this manga's source
.filter { (it.service as? EnhancedTrackService)?.accept(source!!) ?: true }
.filter { (it.tracker as? EnhancedTracker)?.accept(source!!) ?: true }
}
.distinctUntilChanged()
.collectLatest { trackItems ->
@@ -46,14 +46,14 @@ import eu.kanade.presentation.track.TrackChapterSelector
import eu.kanade.presentation.track.TrackDateSelector
import eu.kanade.presentation.track.TrackInfoDialogHome
import eu.kanade.presentation.track.TrackScoreSelector
import eu.kanade.presentation.track.TrackServiceSearch
import eu.kanade.presentation.track.TrackStatusSelector
import eu.kanade.presentation.track.TrackerSearch
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.DeletableTrackService
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.DeletableTracker
import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone
import eu.kanade.tachiyomi.util.system.openInBrowser
@@ -105,7 +105,7 @@ data class TrackInfoDialogHomeScreen(
navigator.push(
TrackStatusSelectorScreen(
track = it.track!!,
serviceId = it.service.id,
serviceId = it.tracker.id,
),
)
},
@@ -113,7 +113,7 @@ data class TrackInfoDialogHomeScreen(
navigator.push(
TrackChapterSelectorScreen(
track = it.track!!,
serviceId = it.service.id,
serviceId = it.tracker.id,
),
)
},
@@ -121,7 +121,7 @@ data class TrackInfoDialogHomeScreen(
navigator.push(
TrackScoreSelectorScreen(
track = it.track!!,
serviceId = it.service.id,
serviceId = it.tracker.id,
),
)
},
@@ -129,7 +129,7 @@ data class TrackInfoDialogHomeScreen(
navigator.push(
TrackDateSelectorScreen(
track = it.track!!,
serviceId = it.service.id,
serviceId = it.tracker.id,
start = true,
),
)
@@ -138,21 +138,21 @@ data class TrackInfoDialogHomeScreen(
navigator.push(
TrackDateSelectorScreen(
track = it.track!!,
serviceId = it.service.id,
serviceId = it.tracker.id,
start = false,
),
)
},
onNewSearch = {
if (it.service is EnhancedTrackService) {
if (it.tracker is EnhancedTracker) {
sm.registerEnhancedTracking(it)
} else {
navigator.push(
TrackServiceSearchScreen(
TrackerSearchScreen(
mangaId = mangaId,
initialQuery = it.track?.title ?: mangaTitle,
currentUrl = it.track?.remoteUrl,
serviceId = it.service.id,
serviceId = it.tracker.id,
),
)
}
@@ -160,10 +160,10 @@ data class TrackInfoDialogHomeScreen(
onOpenInBrowser = { openTrackerInBrowser(context, it) },
onRemoved = {
navigator.push(
TrackServiceRemoveScreen(
TrackerRemoveScreen(
mangaId = mangaId,
track = it.track!!,
serviceId = it.service.id,
serviceId = it.tracker.id,
),
)
},
@@ -201,12 +201,12 @@ data class TrackInfoDialogHomeScreen(
}
fun registerEnhancedTracking(item: TrackItem) {
item.service as EnhancedTrackService
item.tracker as EnhancedTracker
coroutineScope.launchNonCancellable {
val manga = Injekt.get<GetManga>().await(mangaId) ?: return@launchNonCancellable
try {
val matchResult = item.service.match(manga) ?: throw Exception()
item.service.register(matchResult, mangaId)
val matchResult = item.tracker.match(manga) ?: throw Exception()
item.tracker.register(matchResult, mangaId)
} catch (e: Exception) {
withUIContext { Injekt.get<Application>().toast(R.string.error_no_match) }
}
@@ -236,13 +236,13 @@ data class TrackInfoDialogHomeScreen(
}
private fun List<Track>.mapToTrackItem(): List<TrackItem> {
val loggedServices = Injekt.get<TrackManager>().services.filter { it.isLoggedIn }
val loggedInTrackers = Injekt.get<TrackerManager>().trackers.filter { it.isLoggedIn }
val source = Injekt.get<SourceManager>().getOrStub(sourceId)
return loggedServices
return loggedInTrackers
// Map to TrackItem
.map { service -> TrackItem(find { it.syncId == service.id }, service) }
// Show only if the service supports this manga's source
.filter { (it.service as? EnhancedTrackService)?.accept(source) ?: true }
.filter { (it.tracker as? EnhancedTracker)?.accept(source) ?: true }
}
@Immutable
@@ -263,7 +263,7 @@ private data class TrackStatusSelectorScreen(
val sm = rememberScreenModel {
Model(
track = track,
service = Injekt.get<TrackManager>().getService(serviceId)!!,
tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
)
}
val state by sm.state.collectAsState()
@@ -281,11 +281,11 @@ private data class TrackStatusSelectorScreen(
private class Model(
private val track: Track,
private val service: TrackService,
private val tracker: Tracker,
) : StateScreenModel<Model.State>(State(track.status.toInt())) {
fun getSelections(): Map<Int, Int?> {
return service.getStatusList().associateWith { service.getStatus(it) }
return tracker.getStatusList().associateWith { tracker.getStatus(it) }
}
fun setSelection(selection: Int) {
@@ -294,7 +294,7 @@ private data class TrackStatusSelectorScreen(
fun setStatus() {
coroutineScope.launchNonCancellable {
service.setRemoteStatus(track.toDbTrack(), state.value.selection)
tracker.setRemoteStatus(track.toDbTrack(), state.value.selection)
}
}
@@ -316,7 +316,7 @@ private data class TrackChapterSelectorScreen(
val sm = rememberScreenModel {
Model(
track = track,
service = Injekt.get<TrackManager>().getService(serviceId)!!,
tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
)
}
val state by sm.state.collectAsState()
@@ -335,7 +335,7 @@ private data class TrackChapterSelectorScreen(
private class Model(
private val track: Track,
private val service: TrackService,
private val tracker: Tracker,
) : StateScreenModel<Model.State>(State(track.lastChapterRead.toInt())) {
fun getRange(): Iterable<Int> {
@@ -353,7 +353,7 @@ private data class TrackChapterSelectorScreen(
fun setChapter() {
coroutineScope.launchNonCancellable {
service.setRemoteLastChapterRead(track.toDbTrack(), state.value.selection)
tracker.setRemoteLastChapterRead(track.toDbTrack(), state.value.selection)
}
}
@@ -375,7 +375,7 @@ private data class TrackScoreSelectorScreen(
val sm = rememberScreenModel {
Model(
track = track,
service = Injekt.get<TrackManager>().getService(serviceId)!!,
tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
)
}
val state by sm.state.collectAsState()
@@ -394,11 +394,11 @@ private data class TrackScoreSelectorScreen(
private class Model(
private val track: Track,
private val service: TrackService,
) : StateScreenModel<Model.State>(State(service.displayScore(track.toDbTrack()))) {
private val tracker: Tracker,
) : StateScreenModel<Model.State>(State(tracker.displayScore(track.toDbTrack()))) {
fun getSelections(): List<String> {
return service.getScoreList()
return tracker.getScoreList()
}
fun setSelection(selection: String) {
@@ -407,7 +407,7 @@ private data class TrackScoreSelectorScreen(
fun setScore() {
coroutineScope.launchNonCancellable {
service.setRemoteScore(track.toDbTrack(), state.value.selection)
tracker.setRemoteScore(track.toDbTrack(), state.value.selection)
}
}
@@ -486,7 +486,7 @@ private data class TrackDateSelectorScreen(
val sm = rememberScreenModel {
Model(
track = track,
service = Injekt.get<TrackManager>().getService(serviceId)!!,
tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
start = start,
)
}
@@ -515,7 +515,7 @@ private data class TrackDateSelectorScreen(
private class Model(
private val track: Track,
private val service: TrackService,
private val tracker: Tracker,
private val start: Boolean,
) : ScreenModel {
@@ -534,15 +534,15 @@ private data class TrackDateSelectorScreen(
val localMillis = millis.convertEpochMillisZone(ZoneOffset.UTC, ZoneOffset.systemDefault())
coroutineScope.launchNonCancellable {
if (start) {
service.setRemoteStartDate(track.toDbTrack(), localMillis)
tracker.setRemoteStartDate(track.toDbTrack(), localMillis)
} else {
service.setRemoteFinishDate(track.toDbTrack(), localMillis)
tracker.setRemoteFinishDate(track.toDbTrack(), localMillis)
}
}
}
fun confirmRemoveDate(navigator: Navigator) {
navigator.push(TrackDateRemoverScreen(track, service.id, start))
navigator.push(TrackDateRemoverScreen(track, tracker.id, start))
}
}
}
@@ -559,7 +559,7 @@ private data class TrackDateRemoverScreen(
val sm = rememberScreenModel {
Model(
track = track,
service = Injekt.get<TrackManager>().getService(serviceId)!!,
tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
start = start,
)
}
@@ -614,25 +614,25 @@ private data class TrackDateRemoverScreen(
private class Model(
private val track: Track,
private val service: TrackService,
private val tracker: Tracker,
private val start: Boolean,
) : ScreenModel {
fun getServiceName() = service.name
fun getServiceName() = tracker.name
fun removeDate() {
coroutineScope.launchNonCancellable {
if (start) {
service.setRemoteStartDate(track.toDbTrack(), 0)
tracker.setRemoteStartDate(track.toDbTrack(), 0)
} else {
service.setRemoteFinishDate(track.toDbTrack(), 0)
tracker.setRemoteFinishDate(track.toDbTrack(), 0)
}
}
}
}
}
data class TrackServiceSearchScreen(
data class TrackerSearchScreen(
private val mangaId: Long,
private val initialQuery: String,
private val currentUrl: String?,
@@ -647,14 +647,14 @@ data class TrackServiceSearchScreen(
mangaId = mangaId,
currentUrl = currentUrl,
initialQuery = initialQuery,
service = Injekt.get<TrackManager>().getService(serviceId)!!,
tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
)
}
val state by sm.state.collectAsState()
var textFieldValue by remember { mutableStateOf(TextFieldValue(initialQuery)) }
TrackServiceSearch(
TrackerSearch(
query = textFieldValue,
onQueryChange = { textFieldValue = it },
onDispatchQuery = { sm.trackingSearch(textFieldValue.text) },
@@ -673,7 +673,7 @@ data class TrackServiceSearchScreen(
private val mangaId: Long,
private val currentUrl: String? = null,
initialQuery: String,
private val service: TrackService,
private val tracker: Tracker,
) : StateScreenModel<Model.State>(State()) {
init {
@@ -690,7 +690,7 @@ data class TrackServiceSearchScreen(
val result = withIOContext {
try {
val results = service.search(query)
val results = tracker.search(query)
Result.success(results)
} catch (e: Throwable) {
Result.failure(e)
@@ -706,7 +706,7 @@ data class TrackServiceSearchScreen(
}
fun registerTracking(item: TrackSearch) {
coroutineScope.launchNonCancellable { service.register(item, mangaId) }
coroutineScope.launchNonCancellable { tracker.register(item, mangaId) }
}
fun updateSelection(selected: TrackSearch) {
@@ -721,7 +721,7 @@ data class TrackServiceSearchScreen(
}
}
private data class TrackServiceRemoveScreen(
private data class TrackerRemoveScreen(
private val mangaId: Long,
private val track: Track,
private val serviceId: Long,
@@ -734,10 +734,10 @@ private data class TrackServiceRemoveScreen(
Model(
mangaId = mangaId,
track = track,
service = Injekt.get<TrackManager>().getService(serviceId)!!,
tracker = Injekt.get<TrackerManager>().get(serviceId)!!,
)
}
val serviceName = sm.getServiceName()
val serviceName = sm.getName()
var removeRemoteTrack by remember { mutableStateOf(false) }
AlertDialogContent(
modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars),
@@ -758,7 +758,7 @@ private data class TrackServiceRemoveScreen(
Text(
text = stringResource(R.string.track_delete_text, serviceName),
)
if (sm.isServiceDeletable()) {
if (sm.isDeletable()) {
Row(verticalAlignment = Alignment.CenterVertically) {
Checkbox(checked = removeRemoteTrack, onCheckedChange = { removeRemoteTrack = it })
Text(text = stringResource(R.string.track_delete_remote_text, serviceName))
@@ -798,17 +798,17 @@ private data class TrackServiceRemoveScreen(
private class Model(
private val mangaId: Long,
private val track: Track,
private val service: TrackService,
private val tracker: Tracker,
private val deleteTrack: DeleteTrack = Injekt.get(),
) : ScreenModel {
fun getServiceName() = service.name
fun getName() = tracker.name
fun isServiceDeletable() = service is DeletableTrackService
fun isDeletable() = tracker is DeletableTracker
fun deleteMangaFromService() {
coroutineScope.launchNonCancellable {
(service as DeletableTrackService).delete(track.toDbTrack())
(tracker as DeletableTracker).delete(track.toDbTrack())
}
}
@@ -1,6 +1,6 @@
package eu.kanade.tachiyomi.ui.manga.track
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.Tracker
import tachiyomi.domain.track.model.Track
data class TrackItem(val track: Track?, val service: TrackService)
data class TrackItem(val track: Track?, val tracker: Tracker)
@@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.ui.setting.track
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.util.view.setComposeContent
@@ -12,7 +12,7 @@ import uy.kohesive.injekt.injectLazy
abstract class BaseOAuthLoginActivity : BaseActivity() {
internal val trackManager: TrackManager by injectLazy()
internal val trackerManager: TrackerManager by injectLazy()
abstract fun handleResult(data: Uri?)
@@ -20,11 +20,11 @@ class TrackLoginActivity : BaseOAuthLoginActivity() {
val matchResult = regex.find(data.fragment.toString())
if (matchResult?.groups?.get(1) != null) {
lifecycleScope.launchIO {
trackManager.aniList.login(matchResult.groups[1]!!.value)
trackerManager.aniList.login(matchResult.groups[1]!!.value)
returnToSettings()
}
} else {
trackManager.aniList.logout()
trackerManager.aniList.logout()
returnToSettings()
}
}
@@ -33,11 +33,11 @@ class TrackLoginActivity : BaseOAuthLoginActivity() {
val code = data.getQueryParameter("code")
if (code != null) {
lifecycleScope.launchIO {
trackManager.bangumi.login(code)
trackerManager.bangumi.login(code)
returnToSettings()
}
} else {
trackManager.bangumi.logout()
trackerManager.bangumi.logout()
returnToSettings()
}
}
@@ -46,11 +46,11 @@ class TrackLoginActivity : BaseOAuthLoginActivity() {
val code = data.getQueryParameter("code")
if (code != null) {
lifecycleScope.launchIO {
trackManager.myAnimeList.login(code)
trackerManager.myAnimeList.login(code)
returnToSettings()
}
} else {
trackManager.myAnimeList.logout()
trackerManager.myAnimeList.logout()
returnToSettings()
}
}
@@ -59,11 +59,11 @@ class TrackLoginActivity : BaseOAuthLoginActivity() {
val code = data.getQueryParameter("code")
if (code != null) {
lifecycleScope.launchIO {
trackManager.shikimori.login(code)
trackerManager.shikimori.login(code)
returnToSettings()
}
} else {
trackManager.shikimori.logout()
trackerManager.shikimori.logout()
returnToSettings()
}
}
@@ -10,7 +10,7 @@ import eu.kanade.core.util.fastMapNotNull
import eu.kanade.presentation.more.stats.StatsScreenState
import eu.kanade.presentation.more.stats.data.StatsData
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.coroutines.flow.update
import tachiyomi.core.util.lang.launchIO
@@ -33,10 +33,10 @@ class StatsScreenModel(
private val getTotalReadDuration: GetTotalReadDuration = Injekt.get(),
private val getTracks: GetTracks = Injekt.get(),
private val preferences: LibraryPreferences = Injekt.get(),
private val trackManager: TrackManager = Injekt.get(),
private val trackerManager: TrackerManager = Injekt.get(),
) : StateScreenModel<StatsScreenState>(StatsScreenState.Loading) {
private val loggedServices by lazy { trackManager.services.fastFilter { it.isLoggedIn } }
private val loggedInTrackers by lazy { trackerManager.trackers.fastFilter { it.isLoggedIn } }
init {
coroutineScope.launchIO {
@@ -72,7 +72,7 @@ class StatsScreenModel(
val trackersStatData = StatsData.Trackers(
trackedTitleCount = mangaTrackMap.count { it.value.isNotEmpty() },
meanScore = meanScore,
trackerCount = loggedServices.size,
trackerCount = loggedInTrackers.size,
)
mutableState.update {
@@ -115,10 +115,10 @@ class StatsScreenModel(
}
private suspend fun getMangaTrackMap(libraryManga: List<LibraryManga>): Map<Long, List<Track>> {
val loggedServicesIds = loggedServices.map { it.id }.toHashSet()
val loggedInTrackerIds = loggedInTrackers.map { it.id }.toHashSet()
return libraryManga.associate { manga ->
val tracks = getTracks.await(manga.id)
.fastFilter { it.syncId in loggedServicesIds }
.fastFilter { it.syncId in loggedInTrackerIds }
manga.id to tracks
}
@@ -144,7 +144,7 @@ class StatsScreenModel(
}
private fun get10PointScore(track: Track): Double {
val service = trackManager.getService(track.syncId)!!
val service = trackerManager.get(track.syncId)!!
return service.get10PointScore(track)
}
}