mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-15 13:37:29 +01:00
Add Komga as an unattended track service (#5049)
* fix: prevent crash if TrackService.getScoreList() is empty * disabled track score button if service doesn't support scoring * first implementation of the Komga tracking this doesn't work for read lists * auto track when adding to library * handle refresh * 2-way sync of chapters for unattended tracking services * Update app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSheet.kt Co-authored-by: Andreas <andreas.everos@gmail.com> * group strings together * support for read lists * sync read chapters on bind * only mark local chapters as read during 2-way sync (incoming) * local progress from read chapters will be sent to remote tracker on bind/refresh this enables syncing after reading offline * remove unused variable * refactor the 2-way sync in a util function * handle auto add to track for unattended services from the browse source screen when long clicking this will also sync chapters, as it is possible to have read or marked as read chapters from there * 2-way sync when library update for TRACKING * refactor * better handling of what has been read server side * refactor: extract function * fix: localLastRead could be -1 when all chapters are read * refactor to rethrow exception so it can be shown in toast * extract strings * replace komga logo Co-authored-by: Andreas <andreas.everos@gmail.com>
This commit is contained in:
@@ -9,6 +9,9 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.UnattendedTrackService
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
@@ -30,6 +33,7 @@ import eu.kanade.tachiyomi.ui.browse.source.filter.TextSectionItem
|
||||
import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateItem
|
||||
import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem
|
||||
import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper
|
||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithTrackServiceTwoWay
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import eu.kanade.tachiyomi.util.removeCovers
|
||||
@@ -102,6 +106,8 @@ open class BrowseSourcePresenter(
|
||||
*/
|
||||
private var pageSubscription: Subscription? = null
|
||||
|
||||
private val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLogged } }
|
||||
|
||||
init {
|
||||
query = searchQuery ?: ""
|
||||
}
|
||||
@@ -260,11 +266,36 @@ open class BrowseSourcePresenter(
|
||||
manga.removeCovers(coverCache)
|
||||
} else {
|
||||
ChapterSettingsHelper.applySettingDefaults(manga)
|
||||
|
||||
if (prefs.autoAddTrack()) {
|
||||
autoAddTrack(manga)
|
||||
}
|
||||
}
|
||||
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
}
|
||||
|
||||
private fun autoAddTrack(manga: Manga) {
|
||||
loggedServices
|
||||
.filterIsInstance<UnattendedTrackService>()
|
||||
.filter { it.accept(source) }
|
||||
.forEach { service ->
|
||||
launchIO {
|
||||
try {
|
||||
service.match(manga)?.let { track ->
|
||||
track.manga_id = manga.id!!
|
||||
(service as TrackService).bind(track)
|
||||
db.insertTrack(track).executeAsBlocking()
|
||||
|
||||
syncChaptersWithTrackServiceTwoWay(db, db.getChapters(manga).executeAsBlocking(), track, service as TrackService)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.w(e, "Could not match manga: ${manga.title} with service $service")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the filter states for the current source.
|
||||
*
|
||||
|
||||
@@ -37,6 +37,8 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.download.DownloadService
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.UnattendedTrackService
|
||||
import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import eu.kanade.tachiyomi.databinding.MangaControllerBinding
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
@@ -72,6 +74,7 @@ import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.chapter.NoChaptersException
|
||||
import eu.kanade.tachiyomi.util.hasCustomCover
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
@@ -507,6 +510,24 @@ class MangaController :
|
||||
.showDialog(router)
|
||||
}
|
||||
}
|
||||
|
||||
if (source != null && preferences.autoAddTrack()) {
|
||||
presenter.trackList
|
||||
.map { it.service }
|
||||
.filterIsInstance<UnattendedTrackService>()
|
||||
.filter { it.accept(source!!) }
|
||||
.forEach { service ->
|
||||
launchIO {
|
||||
try {
|
||||
service.match(manga)?.let { track ->
|
||||
presenter.registerTracking(track, service as TrackService)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.w(e, "Could not match manga: ${manga.title} with service $service")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.UnattendedTrackService
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.model.toSChapter
|
||||
@@ -26,6 +27,7 @@ import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
|
||||
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
|
||||
import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper
|
||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithTrackServiceTwoWay
|
||||
import eu.kanade.tachiyomi.util.isLocal
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
@@ -709,6 +711,10 @@ class MangaPresenter(
|
||||
async {
|
||||
val track = it.service.refresh(it.track!!)
|
||||
db.insertTrack(track).executeAsBlocking()
|
||||
|
||||
if (it.service is UnattendedTrackService) {
|
||||
syncChaptersWithTrackServiceTwoWay(db, chapters, track, it.service)
|
||||
}
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
@@ -740,6 +746,10 @@ class MangaPresenter(
|
||||
try {
|
||||
service.bind(item)
|
||||
db.insertTrack(item).executeAsBlocking()
|
||||
|
||||
if (service is UnattendedTrackService) {
|
||||
syncChaptersWithTrackServiceTwoWay(db, chapters, item, service)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
withUIContext { view?.applicationContext?.toast(e.message) }
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.manga.track
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.kanade.tachiyomi.R.string
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.databinding.TrackItemBinding
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
@@ -49,6 +50,12 @@ class TrackHolder(private val binding: TrackItemBinding, adapter: TrackAdapter)
|
||||
if (track.total_chapters > 0) track.total_chapters else "-"
|
||||
binding.trackStatus.text = item.service.getStatus(track.status)
|
||||
binding.trackScore.text = if (track.score == 0f) "-" else item.service.displayScore(track)
|
||||
if (item.service.getScoreList().isEmpty()) {
|
||||
with(binding.trackScore) {
|
||||
text = context.getString(string.score_unsupported)
|
||||
isEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
if (item.service.supportsReadingDates) {
|
||||
binding.trackStartDate.text =
|
||||
|
||||
@@ -5,16 +5,25 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import eu.kanade.tachiyomi.R.string
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.track.UnattendedTrackService
|
||||
import eu.kanade.tachiyomi.databinding.TrackControllerBinding
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.ui.base.controller.openInBrowser
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class TrackSheet(
|
||||
val controller: MangaController,
|
||||
val manga: Manga
|
||||
val manga: Manga,
|
||||
private val sourceManager: SourceManager = Injekt.get()
|
||||
) : BaseBottomSheetDialog(controller.activity!!),
|
||||
TrackAdapter.OnClickListener,
|
||||
SetTrackStatusDialog.Listener,
|
||||
@@ -69,7 +78,31 @@ class TrackSheet(
|
||||
|
||||
override fun onSetClick(position: Int) {
|
||||
val item = adapter.getItem(position) ?: return
|
||||
TrackSearchDialog(controller, item.service).showDialog(controller.router, TAG_SEARCH_CONTROLLER)
|
||||
|
||||
if (item.service is UnattendedTrackService) {
|
||||
if (item.track != null) {
|
||||
controller.presenter.unregisterTracking(item.service)
|
||||
return
|
||||
}
|
||||
|
||||
if (!item.service.accept(sourceManager.getOrStub(manga.source))) {
|
||||
controller.presenter.view?.applicationContext?.toast(string.source_unsupported)
|
||||
return
|
||||
}
|
||||
|
||||
launchIO {
|
||||
try {
|
||||
item.service.match(manga)?.let { track ->
|
||||
controller.presenter.registerTracking(track, item.service)
|
||||
}
|
||||
?: withUIContext { controller.presenter.view?.applicationContext?.toast(string.error_no_match) }
|
||||
} catch (e: Exception) {
|
||||
withUIContext { controller.presenter.view?.applicationContext?.toast(string.error_no_match) }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TrackSearchDialog(controller, item.service).showDialog(controller.router, TAG_SEARCH_CONTROLLER)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTitleLongClick(position: Int) {
|
||||
@@ -94,7 +127,7 @@ class TrackSheet(
|
||||
|
||||
override fun onScoreClick(position: Int) {
|
||||
val item = adapter.getItem(position) ?: return
|
||||
if (item.track == null) return
|
||||
if (item.track == null || item.service.getScoreList().isEmpty()) return
|
||||
|
||||
SetTrackScoreDialog(controller, this, item).showDialog(controller.router)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.setting
|
||||
import android.app.Activity
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.track.NoLoginTrackService
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.data.track.anilist.AnilistApi
|
||||
@@ -38,6 +39,11 @@ class SettingsTrackingController :
|
||||
titleRes = R.string.pref_auto_update_manga_sync
|
||||
defaultValue = true
|
||||
}
|
||||
switchPreference {
|
||||
key = Keys.autoAddTrack
|
||||
titleRes = R.string.pref_auto_add_track
|
||||
defaultValue = true
|
||||
}
|
||||
preferenceCategory {
|
||||
titleRes = R.string.services
|
||||
|
||||
@@ -58,6 +64,10 @@ class SettingsTrackingController :
|
||||
trackPreference(trackManager.bangumi) {
|
||||
activity?.openInBrowser(BangumiApi.authUrl(), trackManager.bangumi.getLogoColor())
|
||||
}
|
||||
trackPreference(trackManager.komga) {
|
||||
trackManager.komga.loginNoop()
|
||||
updatePreference(trackManager.komga.id)
|
||||
}
|
||||
}
|
||||
preferenceCategory {
|
||||
infoPreference(R.string.tracking_info)
|
||||
@@ -76,9 +86,14 @@ class SettingsTrackingController :
|
||||
{
|
||||
onClick {
|
||||
if (service.isLogged) {
|
||||
val dialog = TrackLogoutDialog(service)
|
||||
dialog.targetController = this@SettingsTrackingController
|
||||
dialog.showDialog(router)
|
||||
if (service is NoLoginTrackService) {
|
||||
service.logout()
|
||||
updatePreference(service.id)
|
||||
} else {
|
||||
val dialog = TrackLogoutDialog(service)
|
||||
dialog.targetController = this@SettingsTrackingController
|
||||
dialog.showDialog(router)
|
||||
}
|
||||
} else {
|
||||
login()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user