Transfer scrobbling information within migration #930

master
Koitharu 2 years ago
parent c01d80f7da
commit a379604974
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -12,25 +12,34 @@ import org.koitharu.kotatsu.history.data.toMangaHistory
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingStatus
import org.koitharu.kotatsu.tracker.data.TrackEntity import org.koitharu.kotatsu.tracker.data.TrackEntity
import javax.inject.Inject import javax.inject.Inject
class MigrateUseCase @Inject constructor( class MigrateUseCase
@Inject
constructor(
private val mangaRepositoryFactory: MangaRepository.Factory, private val mangaRepositoryFactory: MangaRepository.Factory,
private val mangaDataRepository: MangaDataRepository, private val mangaDataRepository: MangaDataRepository,
private val database: MangaDatabase, private val database: MangaDatabase,
private val progressUpdateUseCase: ProgressUpdateUseCase, private val progressUpdateUseCase: ProgressUpdateUseCase,
private val scrobblers: Set<@JvmSuppressWildcards Scrobbler>,
) { ) {
suspend operator fun invoke(
suspend operator fun invoke(oldManga: Manga, newManga: Manga) { oldManga: Manga,
val oldDetails = if (oldManga.chapters.isNullOrEmpty()) { newManga: Manga,
) {
val oldDetails =
if (oldManga.chapters.isNullOrEmpty()) {
runCatchingCancellable { runCatchingCancellable {
mangaRepositoryFactory.create(oldManga.source).getDetails(oldManga) mangaRepositoryFactory.create(oldManga.source).getDetails(oldManga)
}.getOrDefault(oldManga) }.getOrDefault(oldManga)
} else { } else {
oldManga oldManga
} }
val newDetails = if (newManga.chapters.isNullOrEmpty()) { val newDetails =
if (newManga.chapters.isNullOrEmpty()) {
mangaRepositoryFactory.create(newManga.source).getDetails(newManga) mangaRepositoryFactory.create(newManga.source).getDetails(newManga)
} else { } else {
newManga newManga
@ -43,7 +52,8 @@ class MigrateUseCase @Inject constructor(
if (oldFavourites.isNotEmpty()) { if (oldFavourites.isNotEmpty()) {
favoritesDao.delete(oldManga.id) favoritesDao.delete(oldManga.id)
for (f in oldFavourites) { for (f in oldFavourites) {
val e = f.copy( val e =
f.copy(
mangaId = newManga.id, mangaId = newManga.id,
) )
favoritesDao.upsert(e) favoritesDao.upsert(e)
@ -52,17 +62,22 @@ class MigrateUseCase @Inject constructor(
// replace history // replace history
val historyDao = database.getHistoryDao() val historyDao = database.getHistoryDao()
val oldHistory = historyDao.find(oldDetails.id) val oldHistory = historyDao.find(oldDetails.id)
val newHistory =
if (oldHistory != null) { if (oldHistory != null) {
val newHistory = makeNewHistory(oldDetails, newDetails, oldHistory) val newHistory = makeNewHistory(oldDetails, newDetails, oldHistory)
historyDao.delete(oldDetails.id) historyDao.delete(oldDetails.id)
historyDao.upsert(newHistory) historyDao.upsert(newHistory)
newHistory
} else {
null
} }
// track // track
val tracksDao = database.getTracksDao() val tracksDao = database.getTracksDao()
val oldTrack = tracksDao.find(oldDetails.id) val oldTrack = tracksDao.find(oldDetails.id)
if (oldTrack != null) { if (oldTrack != null) {
val lastChapter = newDetails.chapters?.lastOrNull() val lastChapter = newDetails.chapters?.lastOrNull()
val newTrack = TrackEntity( val newTrack =
TrackEntity(
mangaId = newDetails.id, mangaId = newDetails.id,
lastChapterId = lastChapter?.id ?: 0L, lastChapterId = lastChapter?.id ?: 0L,
newChapters = 0, newChapters = 0,
@ -74,6 +89,32 @@ class MigrateUseCase @Inject constructor(
tracksDao.delete(oldDetails.id) tracksDao.delete(oldDetails.id)
tracksDao.upsert(newTrack) tracksDao.upsert(newTrack)
} }
// scrobbling
for (scrobbler in scrobblers) {
if (!scrobbler.isEnabled) {
continue
}
val prevInfo = scrobbler.getScrobblingInfoOrNull(oldDetails.id) ?: continue
scrobbler.unregisterScrobbling(oldDetails.id)
scrobbler.linkManga(newDetails.id, prevInfo.targetId)
scrobbler.updateScrobblingInfo(
mangaId = newDetails.id,
rating = prevInfo.rating,
status =
prevInfo.status ?: when {
newHistory == null -> ScrobblingStatus.PLANNED
newHistory.percent == 1f -> ScrobblingStatus.COMPLETED
else -> ScrobblingStatus.READING
},
comment = prevInfo.comment,
)
if (newHistory != null) {
scrobbler.scrobble(
manga = newDetails,
chapterId = newHistory.chapterId,
)
}
}
} }
progressUpdateUseCase(newManga) progressUpdateUseCase(newManga)
} }
@ -86,7 +127,8 @@ class MigrateUseCase @Inject constructor(
if (oldManga.chapters.isNullOrEmpty()) { // probably broken manga/source if (oldManga.chapters.isNullOrEmpty()) { // probably broken manga/source
val branch = newManga.getPreferredBranch(null) val branch = newManga.getPreferredBranch(null)
val chapters = checkNotNull(newManga.getChapters(branch)) val chapters = checkNotNull(newManga.getChapters(branch))
val currentChapter = if (history.percent in 0f..1f) { val currentChapter =
if (history.percent in 0f..1f) {
chapters[(chapters.lastIndex * history.percent).toInt()] chapters[(chapters.lastIndex * history.percent).toInt()]
} else { } else {
chapters.first() chapters.first()
@ -107,19 +149,23 @@ class MigrateUseCase @Inject constructor(
val oldChapters = checkNotNull(oldManga.getChapters(branch)) val oldChapters = checkNotNull(oldManga.getChapters(branch))
var index = oldChapters.indexOfFirst { it.id == history.chapterId } var index = oldChapters.indexOfFirst { it.id == history.chapterId }
if (index < 0) { if (index < 0) {
index = if (history.percent in 0f..1f) { index =
if (history.percent in 0f..1f) {
(oldChapters.lastIndex * history.percent).toInt() (oldChapters.lastIndex * history.percent).toInt()
} else { } else {
0 0
} }
} }
val newChapters = checkNotNull(newManga.chapters).groupBy { it.branch } val newChapters = checkNotNull(newManga.chapters).groupBy { it.branch }
val newBranch = if (newChapters.containsKey(branch)) { val newBranch =
if (newChapters.containsKey(branch)) {
branch branch
} else { } else {
newManga.getPreferredBranch(null) newManga.getPreferredBranch(null)
} }
val newChapterId = checkNotNull(newChapters[newBranch]).let { val newChapterId =
checkNotNull(newChapters[newBranch])
.let {
val oldChapter = oldChapters[index] val oldChapter = oldChapters[index]
it.findByNumber(oldChapter.volume, oldChapter.number) ?: it.getOrNull(index) ?: it.last() it.findByNumber(oldChapter.volume, oldChapter.number) ?: it.getOrNull(index) ?: it.last()
}.id }.id
@ -137,11 +183,13 @@ class MigrateUseCase @Inject constructor(
) )
} }
private fun List<MangaChapter>.findByNumber(volume: Int, number: Float): MangaChapter? { private fun List<MangaChapter>.findByNumber(
return if (number <= 0f) { volume: Int,
number: Float,
): MangaChapter? =
if (number <= 0f) {
null null
} else { } else {
firstOrNull { it.volume == volume && it.number == number } firstOrNull { it.volume == volume && it.number == number }
} }
} }
}

@ -162,7 +162,7 @@ class DetailsViewModel @Inject constructor(
val onMangaRemoved = MutableEventFlow<Manga>() val onMangaRemoved = MutableEventFlow<Manga>()
val isScrobblingAvailable: Boolean val isScrobblingAvailable: Boolean
get() = scrobblers.any { it.isAvailable } get() = scrobblers.any { it.isEnabled }
val scrobblingInfo: StateFlow<List<ScrobblingInfo>> = interactor.observeScrobblingInfo(mangaId) val scrobblingInfo: StateFlow<List<ScrobblingInfo>> = interactor.observeScrobblingInfo(mangaId)
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList()) .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList())
@ -393,7 +393,7 @@ class DetailsViewModel @Inject constructor(
private fun getScrobbler(index: Int): Scrobbler? { private fun getScrobbler(index: Int): Scrobbler? {
val info = scrobblingInfo.value.getOrNull(index) val info = scrobblingInfo.value.getOrNull(index)
val scrobbler = if (info != null) { val scrobbler = if (info != null) {
scrobblers.find { it.scrobblerService == info.scrobbler && it.isAvailable } scrobblers.find { it.scrobblerService == info.scrobbler && it.isEnabled }
} else { } else {
null null
} }

@ -51,7 +51,7 @@ abstract class Scrobbler(
} }
} }
val isAvailable: Boolean val isEnabled: Boolean
get() = repository.isAuthorized get() = repository.isAuthorized
suspend fun authorize(authCode: String): ScrobblerUser { suspend fun authorize(authCode: String): ScrobblerUser {

@ -42,7 +42,7 @@ class ScrobblingSelectorViewModel @Inject constructor(
val manga = savedStateHandle.require<ParcelableManga>(MangaIntent.KEY_MANGA).manga val manga = savedStateHandle.require<ParcelableManga>(MangaIntent.KEY_MANGA).manga
val availableScrobblers = scrobblers.filter { it.isAvailable } val availableScrobblers = scrobblers.filter { it.isEnabled }
val selectedScrobblerIndex = MutableStateFlow(0) val selectedScrobblerIndex = MutableStateFlow(0)

Loading…
Cancel
Save