UnattendedTrackService -> EnhancedTrackService

This commit is contained in:
Jays2Kings 2022-04-03 21:13:43 -04:00
parent a39510d08e
commit ad8871aff5
8 changed files with 106 additions and 20 deletions

View file

@ -25,8 +25,8 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateRanker.rankingScheme
import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.UnattendedTrackService
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.UnmeteredSource import eu.kanade.tachiyomi.source.UnmeteredSource
@ -524,7 +524,7 @@ class LibraryUpdateService(
val newTrack = service.refresh(track) val newTrack = service.refresh(track)
db.insertTrack(newTrack).executeAsBlocking() db.insertTrack(newTrack).executeAsBlocking()
if (service is UnattendedTrackService) { if (service is EnhancedTrackService) {
syncChaptersWithTrackServiceTwoWay(db, db.getChapters(manga).executeAsBlocking(), track, service) syncChaptersWithTrackServiceTwoWay(db, db.getChapters(manga).executeAsBlocking(), track, service)
} }
} catch (e: Exception) { } catch (e: Exception) {

View file

@ -0,0 +1,39 @@
package eu.kanade.tachiyomi.data.track
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.source.Source
/**
* 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.
*/
interface EnhancedTrackService {
/**
* This TrackService 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.
*/
fun getAcceptedSources(): List<String>
/**
* match is similar to TrackService.search, but only return zero or one match.
*/
suspend fun match(manga: Manga): TrackSearch?
/**
* Checks whether the provided source/track/manga triplet is from this TrackService
*/
fun isTrackFrom(track: Track, manga: Manga, source: Source?): Boolean
/**
* Migrates the given track for the manga to the newSource, if possible
*/
fun migrateTrack(track: Track, manga: Manga, newSource: Source): Track?
}

View file

@ -6,16 +6,16 @@ import androidx.annotation.StringRes
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.NoLoginTrackService import eu.kanade.tachiyomi.data.track.NoLoginTrackService
import eu.kanade.tachiyomi.data.track.TrackService 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.data.track.model.TrackSearch
import eu.kanade.tachiyomi.data.track.updateNewTrackInfo import eu.kanade.tachiyomi.data.track.updateNewTrackInfo
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import okhttp3.Dns import okhttp3.Dns
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
class Komga(private val context: Context, id: Int) : TrackService(id), UnattendedTrackService, NoLoginTrackService { class Komga(private val context: Context, id: Int) : TrackService(id), EnhancedTrackService, NoLoginTrackService {
companion object { companion object {
const val UNREAD = 1 const val UNREAD = 1
@ -107,6 +107,18 @@ class Komga(private val context: Context, id: Int) : TrackService(id), Unattende
override fun accept(source: Source): Boolean = source::class.qualifiedName == ACCEPTED_SOURCE override fun accept(source: Source): Boolean = source::class.qualifiedName == ACCEPTED_SOURCE
override fun getAcceptedSources() = listOf("eu.kanade.tachiyomi.extension.all.komga.Komga")
override fun isTrackFrom(track: Track, manga: Manga, source: Source?): Boolean =
track.tracking_url == manga.url && source?.let { accept(it) } == true
override fun migrateTrack(track: Track, manga: Manga, newSource: Source): Track? =
if (accept(newSource)) {
track.also { track.tracking_url = manga.url }
} else {
null
}
override suspend fun match(manga: Manga): TrackSearch? = override suspend fun match(manga: Manga): TrackSearch? =
try { try {
api.getTrackSearch(manga.url) api.getTrackSearch(manga.url)

View file

@ -27,9 +27,9 @@ import eu.kanade.tachiyomi.data.library.CustomMangaManager
import eu.kanade.tachiyomi.data.library.LibraryServiceListener import eu.kanade.tachiyomi.data.library.LibraryServiceListener
import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService 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.LocalSource
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceNotFoundException import eu.kanade.tachiyomi.source.SourceNotFoundException
@ -879,7 +879,7 @@ class MangaDetailsPresenter(
} }
if (trackItem != null) { if (trackItem != null) {
db.insertTrack(trackItem).executeAsBlocking() db.insertTrack(trackItem).executeAsBlocking()
if (item.service is UnattendedTrackService) { if (item.service is EnhancedTrackService) {
syncChaptersWithTrackServiceTwoWay(db, chapters, trackItem, item.service) syncChaptersWithTrackServiceTwoWay(db, chapters, trackItem, item.service)
} }
trackItem trackItem
@ -922,7 +922,7 @@ class MangaDetailsPresenter(
db.insertTrack(binding).executeAsBlocking() db.insertTrack(binding).executeAsBlocking()
} }
if (service is UnattendedTrackService) { if (service is EnhancedTrackService) {
syncChaptersWithTrackServiceTwoWay(db, chapters, item, service) syncChaptersWithTrackServiceTwoWay(db, chapters, item, service)
} }
} }

View file

@ -29,8 +29,8 @@ import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.adapters.ItemAdapter import com.mikepenz.fastadapter.adapters.ItemAdapter
import com.mikepenz.fastadapter.listeners.addClickListener import com.mikepenz.fastadapter.listeners.addClickListener
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackService 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.data.track.model.TrackSearch
import eu.kanade.tachiyomi.databinding.TrackChaptersDialogBinding import eu.kanade.tachiyomi.databinding.TrackChaptersDialogBinding
import eu.kanade.tachiyomi.databinding.TrackScoreDialogBinding import eu.kanade.tachiyomi.databinding.TrackScoreDialogBinding
@ -202,7 +202,7 @@ class TrackingBottomSheet(private val controller: MangaDetailsController) :
return return
} }
if (item.service is UnattendedTrackService) { if (item.service is EnhancedTrackService) {
if (item.track != null) { if (item.track != null) {
controller.presenter.removeTracker(item, false) controller.presenter.removeTracker(item, false)
return return

View file

@ -6,6 +6,8 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
@ -34,6 +36,8 @@ class MigrationPresenter(
private val stateRelay = BehaviorRelay.create(state) private val stateRelay = BehaviorRelay.create(state)
private val enhancedServices by lazy { Injekt.get<TrackManager>().services.filterIsInstance<EnhancedTrackService>() }
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState) super.onCreate(savedState)
@ -76,17 +80,19 @@ class MigrationPresenter(
fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean) { fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean) {
val source = sourceManager.get(manga.source) ?: return val source = sourceManager.get(manga.source) ?: return
val prevSource = sourceManager.get(prevManga.source)
state = state.copy(isReplacingManga = true) state = state.copy(isReplacingManga = true)
Observable.defer { source.fetchChapterList(manga) }.onErrorReturn { emptyList() } Observable.defer { source.fetchChapterList(manga) }.onErrorReturn { emptyList() }
.doOnNext { migrateMangaInternal(source, it, prevManga, manga, replace) } .doOnNext { migrateMangaInternal(prevSource, source, it, prevManga, manga, replace) }
.onErrorReturn { emptyList() }.subscribeOn(Schedulers.io()) .onErrorReturn { emptyList() }.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnUnsubscribe { state = state.copy(isReplacingManga = false) }.subscribe() .doOnUnsubscribe { state = state.copy(isReplacingManga = false) }.subscribe()
} }
private fun migrateMangaInternal( private fun migrateMangaInternal(
prevSource: Source?,
source: Source, source: Source,
sourceChapters: List<SChapter>, sourceChapters: List<SChapter>,
prevManga: Manga, prevManga: Manga,
@ -128,12 +134,16 @@ class MigrationPresenter(
} }
// Update track // Update track
if (migrateTracks) { if (migrateTracks) {
val tracks = db.getTracks(prevManga).executeAsBlocking() val tracksToUpdate = db.getTracks(prevManga).executeAsBlocking().mapNotNull { track ->
for (track in tracks) {
track.id = null track.id = null
track.manga_id = manga.id!! track.manga_id = manga.id!!
val service = enhancedServices
.firstOrNull { it.isTrackFrom(track, prevManga, prevSource) }
if (service != null) service.migrateTrack(track, manga, source)
else track
} }
db.insertTracks(tracks).executeAsBlocking() db.insertTracks(tracksToUpdate).executeAsBlocking()
} }
// Update favorite status // Update favorite status
if (replace) { if (replace) {

View file

@ -7,11 +7,17 @@ import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.migration.MigrationFlags import eu.kanade.tachiyomi.ui.migration.MigrationFlags
import eu.kanade.tachiyomi.util.system.launchUI import eu.kanade.tachiyomi.util.system.launchUI
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.Date import java.util.Date
@ -22,10 +28,13 @@ class MigrationProcessAdapter(
private val db: DatabaseHelper by injectLazy() private val db: DatabaseHelper by injectLazy()
var items: List<MigrationProcessItem> = emptyList() var items: List<MigrationProcessItem> = emptyList()
val preferences: PreferencesHelper by injectLazy() val preferences: PreferencesHelper by injectLazy()
val sourceManager: SourceManager by injectLazy()
var showOutline = preferences.outlineOnCovers().get() var showOutline = preferences.outlineOnCovers().get()
val menuItemListener: MigrationProcessInterface = controller val menuItemListener: MigrationProcessInterface = controller
private val enhancedServices by lazy { Injekt.get<TrackManager>().services.filterIsInstance<EnhancedTrackService>() }
override fun updateDataSet(items: List<MigrationProcessItem>?) { override fun updateDataSet(items: List<MigrationProcessItem>?) {
this.items = items ?: emptyList() this.items = items ?: emptyList()
super.updateDataSet(items) super.updateDataSet(items)
@ -63,8 +72,13 @@ class MigrationProcessAdapter(
val toMangaObj = val toMangaObj =
db.getManga(manga.searchResult.get() ?: return@forEach).executeAsBlocking() db.getManga(manga.searchResult.get() ?: return@forEach).executeAsBlocking()
?: return@forEach ?: return@forEach
val prevManga = manga.manga() ?: return@forEach
val source = sourceManager.get(toMangaObj.source) ?: return@forEach
val prevSource = sourceManager.get(prevManga.source)
migrateMangaInternal( migrateMangaInternal(
manga.manga() ?: return@forEach, prevSource,
source,
prevManga,
toMangaObj, toMangaObj,
!copy !copy
) )
@ -81,8 +95,13 @@ class MigrationProcessAdapter(
val toMangaObj = val toMangaObj =
db.getManga(manga.searchResult.get() ?: return@launchUI).executeAsBlocking() db.getManga(manga.searchResult.get() ?: return@launchUI).executeAsBlocking()
?: return@launchUI ?: return@launchUI
val prevManga = manga.manga() ?: return@launchUI
val source = sourceManager.get(toMangaObj.source) ?: return@launchUI
val prevSource = sourceManager.get(prevManga.source)
migrateMangaInternal( migrateMangaInternal(
manga.manga() ?: return@launchUI, prevSource,
source,
prevManga,
toMangaObj, toMangaObj,
!copy !copy
) )
@ -101,6 +120,8 @@ class MigrationProcessAdapter(
} }
private fun migrateMangaInternal( private fun migrateMangaInternal(
prevSource: Source?,
source: Source,
prevManga: Manga, prevManga: Manga,
manga: Manga, manga: Manga,
replace: Boolean replace: Boolean
@ -143,12 +164,16 @@ class MigrationProcessAdapter(
} }
// Update track // Update track
if (MigrationFlags.hasTracks(flags)) { if (MigrationFlags.hasTracks(flags)) {
val tracks = db.getTracks(prevManga).executeAsBlocking() val tracksToUpdate = db.getTracks(prevManga).executeAsBlocking().mapNotNull { track ->
for (track in tracks) {
track.id = null track.id = null
track.manga_id = manga.id!! track.manga_id = manga.id!!
val service = enhancedServices
.firstOrNull { it.isTrackFrom(track, prevManga, prevSource) }
if (service != null) service.migrateTrack(track, manga, source)
else track
} }
db.insertTracks(tracks).executeAsBlocking() db.insertTracks(tracksToUpdate).executeAsBlocking()
} }
// Update favorite status // Update favorite status
if (replace) { if (replace) {

View file

@ -10,9 +10,9 @@ import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService 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.LocalSource
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.category.addtolibrary.SetCategoriesSheet import eu.kanade.tachiyomi.ui.category.addtolibrary.SetCategoriesSheet
@ -192,7 +192,7 @@ fun Manga.autoAddTrack(db: DatabaseHelper, onMangaMoved: () -> Unit) {
val loggedServices = Injekt.get<TrackManager>().services.filter { it.isLogged } val loggedServices = Injekt.get<TrackManager>().services.filter { it.isLogged }
val source = Injekt.get<SourceManager>().getOrStub(this.source) val source = Injekt.get<SourceManager>().getOrStub(this.source)
loggedServices loggedServices
.filterIsInstance<UnattendedTrackService>() .filterIsInstance<EnhancedTrackService>()
.filter { it.accept(source) } .filter { it.accept(source) }
.forEach { service -> .forEach { service ->
launchIO { launchIO {