mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	Merge branch 'master' into sync-part-final
This commit is contained in:
		| @@ -1,8 +1,8 @@ | ||||
| package eu.kanade.domain | ||||
|  | ||||
| import eu.kanade.domain.chapter.interactor.SetReadStatus | ||||
| import eu.kanade.domain.chapter.interactor.SyncChapterProgressWithTrack | ||||
| import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource | ||||
| import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay | ||||
| import eu.kanade.domain.download.interactor.DeleteDownload | ||||
| import eu.kanade.domain.extension.interactor.GetExtensionLanguages | ||||
| import eu.kanade.domain.extension.interactor.GetExtensionSources | ||||
| @@ -127,7 +127,7 @@ class DomainModule : InjektModule { | ||||
|         addFactory { SetReadStatus(get(), get(), get(), get()) } | ||||
|         addFactory { ShouldUpdateDbChapter() } | ||||
|         addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get()) } | ||||
|         addFactory { SyncChaptersWithTrackServiceTwoWay(get(), get(), get()) } | ||||
|         addFactory { SyncChapterProgressWithTrack(get(), get(), get()) } | ||||
|  | ||||
|         addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) } | ||||
|         addFactory { GetHistory(get()) } | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import tachiyomi.domain.chapter.model.toChapterUpdate | ||||
| import tachiyomi.domain.track.interactor.InsertTrack | ||||
| import tachiyomi.domain.track.model.Track | ||||
| 
 | ||||
| class SyncChaptersWithTrackServiceTwoWay( | ||||
| class SyncChapterProgressWithTrack( | ||||
|     private val updateChapter: UpdateChapter, | ||||
|     private val insertTrack: InsertTrack, | ||||
|     private val getChapterByMangaId: GetChapterByMangaId, | ||||
| @@ -1,14 +1,13 @@ | ||||
| package eu.kanade.domain.track.interactor | ||||
|  | ||||
| import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay | ||||
| import eu.kanade.domain.chapter.interactor.SyncChapterProgressWithTrack | ||||
| import eu.kanade.domain.track.model.toDbTrack | ||||
| import eu.kanade.domain.track.model.toDomainTrack | ||||
| import eu.kanade.tachiyomi.data.track.TrackManager | ||||
| import eu.kanade.tachiyomi.data.track.TrackService | ||||
| import kotlinx.coroutines.async | ||||
| import kotlinx.coroutines.awaitAll | ||||
| import kotlinx.coroutines.supervisorScope | ||||
| import logcat.LogPriority | ||||
| import tachiyomi.core.util.system.logcat | ||||
| import tachiyomi.domain.track.interactor.GetTracks | ||||
| import tachiyomi.domain.track.interactor.InsertTrack | ||||
|  | ||||
| @@ -16,28 +15,34 @@ class RefreshTracks( | ||||
|     private val getTracks: GetTracks, | ||||
|     private val trackManager: TrackManager, | ||||
|     private val insertTrack: InsertTrack, | ||||
|     private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay, | ||||
|     private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack, | ||||
| ) { | ||||
|  | ||||
|     suspend fun await(mangaId: Long) { | ||||
|         supervisorScope { | ||||
|             getTracks.await(mangaId) | ||||
|     /** | ||||
|      * Fetches updated tracking data from all logged in trackers. | ||||
|      * | ||||
|      * @return Failed updates. | ||||
|      */ | ||||
|     suspend fun await(mangaId: Long): List<Pair<TrackService?, Throwable>> { | ||||
|         return supervisorScope { | ||||
|             return@supervisorScope getTracks.await(mangaId) | ||||
|                 .map { track -> | ||||
|                     async { | ||||
|                         val service = trackManager.getService(track.syncId) | ||||
|                         if (service != null && service.isLoggedIn) { | ||||
|                             try { | ||||
|                         return@async try { | ||||
|                             if (service?.isLoggedIn == true) { | ||||
|                                 val updatedTrack = service.refresh(track.toDbTrack()) | ||||
|                                 insertTrack.await(updatedTrack.toDomainTrack()!!) | ||||
|                                 syncChaptersWithTrackServiceTwoWay.await(mangaId, track, service) | ||||
|                             } catch (e: Throwable) { | ||||
|                                 // Ignore errors and continue | ||||
|                                 logcat(LogPriority.ERROR, e) | ||||
|                                 syncChapterProgressWithTrack.await(mangaId, track, service) | ||||
|                             } | ||||
|                             null | ||||
|                         } catch (e: Throwable) { | ||||
|                             service to e | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 .awaitAll() | ||||
|                 .filterNotNull() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -127,7 +127,7 @@ private fun ColumnScope.FilterPage( | ||||
|             trackServices.map { service -> | ||||
|                 val filterTracker by screenModel.libraryPreferences.filterTracking(service.id.toInt()).collectAsState() | ||||
|                 TriStateItem( | ||||
|                     label = stringResource(service.nameRes()), | ||||
|                     label = service.name, | ||||
|                     state = filterTracker, | ||||
|                     onClick = { screenModel.toggleTracker(service.id.toInt()) }, | ||||
|                 ) | ||||
|   | ||||
| @@ -114,9 +114,7 @@ object SettingsTrackingScreen : SearchableSettings { | ||||
|         if (enhancedTrackers.second.isNotEmpty()) { | ||||
|             val missingSourcesInfo = stringResource( | ||||
|                 R.string.enhanced_services_not_installed, | ||||
|                 enhancedTrackers.second | ||||
|                     .map { stringResource(it.nameRes()) } | ||||
|                     .joinToString(), | ||||
|                 enhancedTrackers.second.joinToString { it.name }, | ||||
|             ) | ||||
|             enhancedTrackerInfo += "\n\n$missingSourcesInfo" | ||||
|         } | ||||
| @@ -130,37 +128,37 @@ object SettingsTrackingScreen : SearchableSettings { | ||||
|                 title = stringResource(R.string.services), | ||||
|                 preferenceItems = listOf( | ||||
|                     Preference.PreferenceItem.TrackingPreference( | ||||
|                         title = stringResource(trackManager.myAnimeList.nameRes()), | ||||
|                         title = trackManager.myAnimeList.name, | ||||
|                         service = trackManager.myAnimeList, | ||||
|                         login = { context.openInBrowser(MyAnimeListApi.authUrl(), forceDefaultBrowser = true) }, | ||||
|                         logout = { dialog = LogoutDialog(trackManager.myAnimeList) }, | ||||
|                     ), | ||||
|                     Preference.PreferenceItem.TrackingPreference( | ||||
|                         title = stringResource(trackManager.aniList.nameRes()), | ||||
|                         title = trackManager.aniList.name, | ||||
|                         service = trackManager.aniList, | ||||
|                         login = { context.openInBrowser(AnilistApi.authUrl(), forceDefaultBrowser = true) }, | ||||
|                         logout = { dialog = LogoutDialog(trackManager.aniList) }, | ||||
|                     ), | ||||
|                     Preference.PreferenceItem.TrackingPreference( | ||||
|                         title = stringResource(trackManager.kitsu.nameRes()), | ||||
|                         title = trackManager.kitsu.name, | ||||
|                         service = trackManager.kitsu, | ||||
|                         login = { dialog = LoginDialog(trackManager.kitsu, R.string.email) }, | ||||
|                         logout = { dialog = LogoutDialog(trackManager.kitsu) }, | ||||
|                     ), | ||||
|                     Preference.PreferenceItem.TrackingPreference( | ||||
|                         title = stringResource(trackManager.mangaUpdates.nameRes()), | ||||
|                         title = trackManager.mangaUpdates.name, | ||||
|                         service = trackManager.mangaUpdates, | ||||
|                         login = { dialog = LoginDialog(trackManager.mangaUpdates, R.string.username) }, | ||||
|                         logout = { dialog = LogoutDialog(trackManager.mangaUpdates) }, | ||||
|                     ), | ||||
|                     Preference.PreferenceItem.TrackingPreference( | ||||
|                         title = stringResource(trackManager.shikimori.nameRes()), | ||||
|                         title = trackManager.shikimori.name, | ||||
|                         service = trackManager.shikimori, | ||||
|                         login = { context.openInBrowser(ShikimoriApi.authUrl(), forceDefaultBrowser = true) }, | ||||
|                         logout = { dialog = LogoutDialog(trackManager.shikimori) }, | ||||
|                     ), | ||||
|                     Preference.PreferenceItem.TrackingPreference( | ||||
|                         title = stringResource(trackManager.bangumi.nameRes()), | ||||
|                         title = trackManager.bangumi.name, | ||||
|                         service = trackManager.bangumi, | ||||
|                         login = { context.openInBrowser(BangumiApi.authUrl(), forceDefaultBrowser = true) }, | ||||
|                         logout = { dialog = LogoutDialog(trackManager.bangumi) }, | ||||
| @@ -173,7 +171,7 @@ object SettingsTrackingScreen : SearchableSettings { | ||||
|                 preferenceItems = enhancedTrackers.first | ||||
|                     .map { service -> | ||||
|                         Preference.PreferenceItem.TrackingPreference( | ||||
|                             title = stringResource(service.nameRes()), | ||||
|                             title = service.name, | ||||
|                             service = service, | ||||
|                             login = { (service as EnhancedTrackService).loginNoop() }, | ||||
|                             logout = service::logout, | ||||
| @@ -202,7 +200,7 @@ object SettingsTrackingScreen : SearchableSettings { | ||||
|             title = { | ||||
|                 Row(verticalAlignment = Alignment.CenterVertically) { | ||||
|                     Text( | ||||
|                         text = stringResource(R.string.login_title, stringResource(service.nameRes())), | ||||
|                         text = stringResource(R.string.login_title, service.name), | ||||
|                         modifier = Modifier.weight(1f), | ||||
|                     ) | ||||
|                     IconButton(onClick = onDismissRequest) { | ||||
| @@ -310,7 +308,7 @@ object SettingsTrackingScreen : SearchableSettings { | ||||
|             onDismissRequest = onDismissRequest, | ||||
|             title = { | ||||
|                 Text( | ||||
|                     text = stringResource(R.string.logout_title, stringResource(service.nameRes())), | ||||
|                     text = stringResource(R.string.logout_title, service.name), | ||||
|                     textAlign = TextAlign.Center, | ||||
|                     modifier = Modifier.fillMaxWidth(), | ||||
|                 ) | ||||
|   | ||||
| @@ -40,7 +40,7 @@ fun TrackingPreferenceWidget( | ||||
|         ) { | ||||
|             TrackLogoIcon(service) | ||||
|             Text( | ||||
|                 text = stringResource(service.nameRes()), | ||||
|                 text = service.name, | ||||
|                 modifier = Modifier | ||||
|                     .weight(1f) | ||||
|                     .padding(horizontal = 16.dp), | ||||
|   | ||||
| @@ -11,7 +11,6 @@ import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import androidx.compose.ui.res.painterResource | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.unit.dp | ||||
| import eu.kanade.tachiyomi.data.track.TrackService | ||||
| import tachiyomi.presentation.core.util.clickableNoIndication | ||||
| @@ -36,7 +35,7 @@ fun TrackLogoIcon( | ||||
|     ) { | ||||
|         Image( | ||||
|             painter = painterResource(service.getLogo()), | ||||
|             contentDescription = stringResource(service.nameRes()), | ||||
|             contentDescription = service.name, | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -52,7 +52,7 @@ class BackupFileValidator( | ||||
|         val missingTrackers = trackers | ||||
|             .mapNotNull { trackManager.getService(it.toLong()) } | ||||
|             .filter { !it.isLoggedIn } | ||||
|             .map { context.getString(it.nameRes()) } | ||||
|             .map { it.name } | ||||
|             .sorted() | ||||
|  | ||||
|         return Results(missingSources, missingTrackers) | ||||
|   | ||||
| @@ -283,7 +283,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet | ||||
|                                     } | ||||
|  | ||||
|                                     if (libraryPreferences.autoUpdateTrackers().get()) { | ||||
|                                         refreshTracks.await(manga.id) | ||||
|                                         refreshTracks(manga.id) | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
| @@ -409,13 +409,20 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet | ||||
|  | ||||
|                 val manga = libraryManga.manga | ||||
|                 notifier.showProgressNotification(listOf(manga), progressCount++, mangaToUpdate.size) | ||||
|                 refreshTracks.await(manga.id) | ||||
|                 refreshTracks(manga.id) | ||||
|             } | ||||
|  | ||||
|             notifier.cancelProgressNotification() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private suspend fun refreshTracks(mangaId: Long) { | ||||
|         refreshTracks.await(mangaId).forEach { (_, e) -> | ||||
|             // Ignore errors and continue | ||||
|             logcat(LogPriority.ERROR, e) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private suspend fun withUpdateNotification( | ||||
|         updatingManga: CopyOnWriteArrayList<Manga>, | ||||
|         completed: AtomicInteger, | ||||
|   | ||||
| @@ -30,7 +30,7 @@ class TrackManager(context: Context) { | ||||
|     val kitsu = Kitsu(KITSU) | ||||
|     val shikimori = Shikimori(SHIKIMORI) | ||||
|     val bangumi = Bangumi(BANGUMI) | ||||
|     val komga = Komga(context, KOMGA) | ||||
|     val komga = Komga(KOMGA) | ||||
|     val mangaUpdates = MangaUpdates(MANGA_UPDATES) | ||||
|     val kavita = Kavita(context, KAVITA) | ||||
|     val suwayomi = Suwayomi(SUWAYOMI) | ||||
|   | ||||
| @@ -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.SyncChaptersWithTrackServiceTwoWay | ||||
| import eu.kanade.domain.chapter.interactor.SyncChapterProgressWithTrack | ||||
| import eu.kanade.domain.track.model.toDbTrack | ||||
| import eu.kanade.domain.track.model.toDomainTrack | ||||
| import eu.kanade.domain.track.service.TrackPreferences | ||||
| @@ -28,20 +28,16 @@ import uy.kohesive.injekt.injectLazy | ||||
| import java.time.ZoneOffset | ||||
| import tachiyomi.domain.track.model.Track as DomainTrack | ||||
|  | ||||
| abstract class TrackService(val id: Long) { | ||||
| abstract class TrackService(val id: Long, val name: String) { | ||||
|  | ||||
|     val trackPreferences: TrackPreferences by injectLazy() | ||||
|     val networkService: NetworkHelper by injectLazy() | ||||
|     private val insertTrack: InsertTrack by injectLazy() | ||||
|     private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay by injectLazy() | ||||
|     private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack by injectLazy() | ||||
|  | ||||
|     open val client: OkHttpClient | ||||
|         get() = networkService.client | ||||
|  | ||||
|     // Name of the manga sync service to display | ||||
|     @StringRes | ||||
|     abstract fun nameRes(): Int | ||||
|  | ||||
|     // Application and remote support for reading dates | ||||
|     open val supportsReadingDates: Boolean = false | ||||
|  | ||||
| @@ -103,7 +99,7 @@ abstract class TrackService(val id: Long) { | ||||
|     } | ||||
|  | ||||
|     // TODO: move this to an interactor, and update all trackers based on common data | ||||
|     suspend fun registerTracking(item: Track, mangaId: Long) { | ||||
|     suspend fun register(item: Track, mangaId: Long) { | ||||
|         item.manga_id = mangaId | ||||
|         try { | ||||
|             withIOContext { | ||||
| @@ -147,7 +143,7 @@ abstract class TrackService(val id: Long) { | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 syncChaptersWithTrackServiceTwoWay.await(mangaId, track, this@TrackService) | ||||
|                 syncChapterProgressWithTrack.await(mangaId, track, this@TrackService) | ||||
|             } | ||||
|         } catch (e: Throwable) { | ||||
|             withUIContext { Injekt.get<Application>().toast(e.message) } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import kotlinx.serialization.json.Json | ||||
| import uy.kohesive.injekt.injectLazy | ||||
| import tachiyomi.domain.track.model.Track as DomainTrack | ||||
|  | ||||
| class Anilist(id: Long) : TrackService(id), DeletableTrackService { | ||||
| class Anilist(id: Long) : TrackService(id, "AniList"), DeletableTrackService { | ||||
|  | ||||
|     companion object { | ||||
|         const val READING = 1 | ||||
| @@ -49,9 +49,6 @@ class Anilist(id: Long) : TrackService(id), DeletableTrackService { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @StringRes | ||||
|     override fun nameRes() = R.string.tracker_anilist | ||||
|  | ||||
|     override fun getLogo() = R.drawable.ic_tracker_anilist | ||||
|  | ||||
|     override fun getLogoColor() = Color.rgb(18, 25, 35) | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import kotlinx.serialization.encodeToString | ||||
| import kotlinx.serialization.json.Json | ||||
| import uy.kohesive.injekt.injectLazy | ||||
|  | ||||
| class Bangumi(id: Long) : TrackService(id) { | ||||
| class Bangumi(id: Long) : TrackService(id, "Bangumi") { | ||||
|  | ||||
|     private val json: Json by injectLazy() | ||||
|  | ||||
| @@ -18,9 +18,6 @@ class Bangumi(id: Long) : TrackService(id) { | ||||
|  | ||||
|     private val api by lazy { BangumiApi(client, interceptor) } | ||||
|  | ||||
|     @StringRes | ||||
|     override fun nameRes() = R.string.tracker_bangumi | ||||
|  | ||||
|     override fun getScoreList(): List<String> { | ||||
|         return IntRange(0, 10).map(Int::toString) | ||||
|     } | ||||
|   | ||||
| @@ -14,7 +14,7 @@ import tachiyomi.domain.manga.model.Manga | ||||
| import java.security.MessageDigest | ||||
| import tachiyomi.domain.track.model.Track as DomainTrack | ||||
|  | ||||
| class Kavita(private val context: Context, id: Long) : TrackService(id), EnhancedTrackService { | ||||
| class Kavita(private val context: Context, id: Long) : TrackService(id, "Kavita"), EnhancedTrackService { | ||||
|  | ||||
|     companion object { | ||||
|         const val UNREAD = 1 | ||||
| @@ -27,9 +27,6 @@ class Kavita(private val context: Context, id: Long) : TrackService(id), Enhance | ||||
|     private val interceptor by lazy { KavitaInterceptor(this) } | ||||
|     val api by lazy { KavitaApi(client, interceptor) } | ||||
|  | ||||
|     @StringRes | ||||
|     override fun nameRes() = R.string.tracker_kavita | ||||
|  | ||||
|     override fun getLogo(): Int = R.drawable.ic_tracker_kavita | ||||
|  | ||||
|     override fun getLogoColor() = Color.rgb(74, 198, 148) | ||||
|   | ||||
| @@ -115,8 +115,8 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor | ||||
|     } | ||||
|  | ||||
|     private fun getLatestChapterRead(url: String): Float { | ||||
|         val serieId = getIdFromUrl(url) | ||||
|         val requestUrl = "${getApiFromUrl(url)}/Tachiyomi/latest-chapter?seriesId=$serieId" | ||||
|         val seriesId = getIdFromUrl(url) | ||||
|         val requestUrl = "${getApiFromUrl(url)}/Tachiyomi/latest-chapter?seriesId=$seriesId" | ||||
|         try { | ||||
|             with(json) { | ||||
|                 authClient.newCall(GET(requestUrl)).execute().use { | ||||
| @@ -137,21 +137,21 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor | ||||
|  | ||||
|     suspend fun getTrackSearch(url: String): TrackSearch = withIOContext { | ||||
|         try { | ||||
|             val serieDto: SeriesDto = with(json) { | ||||
|             val seriesDto: SeriesDto = with(json) { | ||||
|                 authClient.newCall(GET(url)) | ||||
|                     .awaitSuccess() | ||||
|                     .parseAs() | ||||
|             } | ||||
|  | ||||
|             val track = serieDto.toTrack() | ||||
|             val track = seriesDto.toTrack() | ||||
|             track.apply { | ||||
|                 cover_url = serieDto.thumbnail_url.toString() | ||||
|                 cover_url = seriesDto.thumbnail_url.toString() | ||||
|                 tracking_url = url | ||||
|                 total_chapters = getTotalChapters(url) | ||||
|  | ||||
|                 title = serieDto.name | ||||
|                 status = when (serieDto.pagesRead) { | ||||
|                     serieDto.pages -> Kavita.COMPLETED | ||||
|                 title = seriesDto.name | ||||
|                 status = when (seriesDto.pagesRead) { | ||||
|                     seriesDto.pages -> Kavita.COMPLETED | ||||
|                     0 -> Kavita.UNREAD | ||||
|                     else -> Kavita.READING | ||||
|                 } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import kotlinx.serialization.json.Json | ||||
| import uy.kohesive.injekt.injectLazy | ||||
| import java.text.DecimalFormat | ||||
|  | ||||
| class Kitsu(id: Long) : TrackService(id), DeletableTrackService { | ||||
| class Kitsu(id: Long) : TrackService(id, "Kitsu"), DeletableTrackService { | ||||
|  | ||||
|     companion object { | ||||
|         const val READING = 1 | ||||
| @@ -22,9 +22,6 @@ class Kitsu(id: Long) : TrackService(id), DeletableTrackService { | ||||
|         const val PLAN_TO_READ = 5 | ||||
|     } | ||||
|  | ||||
|     @StringRes | ||||
|     override fun nameRes() = R.string.tracker_kitsu | ||||
|  | ||||
|     override val supportsReadingDates: Boolean = true | ||||
|  | ||||
|     private val json: Json by injectLazy() | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| package eu.kanade.tachiyomi.data.track.komga | ||||
|  | ||||
| import android.content.Context | ||||
| import android.graphics.Color | ||||
| import androidx.annotation.StringRes | ||||
| import eu.kanade.tachiyomi.R | ||||
| @@ -14,7 +13,7 @@ import okhttp3.OkHttpClient | ||||
| import tachiyomi.domain.manga.model.Manga | ||||
| import tachiyomi.domain.track.model.Track as DomainTrack | ||||
|  | ||||
| class Komga(private val context: Context, id: Long) : TrackService(id), EnhancedTrackService { | ||||
| class Komga(id: Long) : TrackService(id, "Komga"), EnhancedTrackService { | ||||
|  | ||||
|     companion object { | ||||
|         const val UNREAD = 1 | ||||
| @@ -29,9 +28,6 @@ class Komga(private val context: Context, id: Long) : TrackService(id), Enhanced | ||||
|  | ||||
|     val api by lazy { KomgaApi(client) } | ||||
|  | ||||
|     @StringRes | ||||
|     override fun nameRes() = R.string.tracker_komga | ||||
|  | ||||
|     override fun getLogo() = R.drawable.ic_tracker_komga | ||||
|  | ||||
|     override fun getLogoColor() = Color.rgb(51, 37, 50) | ||||
|   | ||||
| @@ -17,7 +17,7 @@ import tachiyomi.core.util.lang.withIOContext | ||||
| import tachiyomi.core.util.system.logcat | ||||
| import uy.kohesive.injekt.injectLazy | ||||
|  | ||||
| const val READLIST_API = "/api/v1/readlists" | ||||
| private const val READLIST_API = "/api/v1/readlists" | ||||
|  | ||||
| class KomgaApi(private val client: OkHttpClient) { | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,7 @@ 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), DeletableTrackService { | ||||
| class MangaUpdates(id: Long) : TrackService(id, "MangaUpdates"), DeletableTrackService { | ||||
|  | ||||
|     companion object { | ||||
|         const val READING_LIST = 0 | ||||
| @@ -24,9 +24,6 @@ class MangaUpdates(id: Long) : TrackService(id), DeletableTrackService { | ||||
|  | ||||
|     private val api by lazy { MangaUpdatesApi(interceptor, client) } | ||||
|  | ||||
|     @StringRes | ||||
|     override fun nameRes(): Int = R.string.tracker_manga_updates | ||||
|  | ||||
|     override fun getLogo(): Int = R.drawable.ic_manga_updates | ||||
|  | ||||
|     override fun getLogoColor(): Int = Color.rgb(146, 160, 173) | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import kotlinx.serialization.encodeToString | ||||
| import kotlinx.serialization.json.Json | ||||
| import uy.kohesive.injekt.injectLazy | ||||
|  | ||||
| class MyAnimeList(id: Long) : TrackService(id), DeletableTrackService { | ||||
| class MyAnimeList(id: Long) : TrackService(id, "MyAnimeList"), DeletableTrackService { | ||||
|  | ||||
|     companion object { | ||||
|         const val READING = 1 | ||||
| @@ -30,9 +30,6 @@ class MyAnimeList(id: Long) : TrackService(id), DeletableTrackService { | ||||
|     private val interceptor by lazy { MyAnimeListInterceptor(this, getPassword()) } | ||||
|     private val api by lazy { MyAnimeListApi(client, interceptor) } | ||||
|  | ||||
|     @StringRes | ||||
|     override fun nameRes() = R.string.tracker_myanimelist | ||||
|  | ||||
|     override val supportsReadingDates: Boolean = true | ||||
|  | ||||
|     override fun getLogo() = R.drawable.ic_tracker_mal | ||||
|   | ||||
| @@ -11,7 +11,7 @@ import kotlinx.serialization.encodeToString | ||||
| import kotlinx.serialization.json.Json | ||||
| import uy.kohesive.injekt.injectLazy | ||||
|  | ||||
| class Shikimori(id: Long) : TrackService(id), DeletableTrackService { | ||||
| class Shikimori(id: Long) : TrackService(id, "Shikimori"), DeletableTrackService { | ||||
|  | ||||
|     companion object { | ||||
|         const val READING = 1 | ||||
| @@ -28,9 +28,6 @@ class Shikimori(id: Long) : TrackService(id), DeletableTrackService { | ||||
|  | ||||
|     private val api by lazy { ShikimoriApi(client, interceptor) } | ||||
|  | ||||
|     @StringRes | ||||
|     override fun nameRes() = R.string.tracker_shikimori | ||||
|  | ||||
|     override fun getScoreList(): List<String> { | ||||
|         return IntRange(0, 10).map(Int::toString) | ||||
|     } | ||||
|   | ||||
| @@ -122,7 +122,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     suspend fun findLibManga(track: Track, user_id: String): Track? { | ||||
|     suspend fun findLibManga(track: Track, userId: String): Track? { | ||||
|         return withIOContext { | ||||
|             val urlMangas = "$apiUrl/mangas".toUri().buildUpon() | ||||
|                 .appendPath(track.media_id.toString()) | ||||
| @@ -134,7 +134,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter | ||||
|             } | ||||
|  | ||||
|             val url = "$apiUrl/v2/user_rates".toUri().buildUpon() | ||||
|                 .appendQueryParameter("user_id", user_id) | ||||
|                 .appendQueryParameter("user_id", userId) | ||||
|                 .appendQueryParameter("target_id", track.media_id.toString()) | ||||
|                 .appendQueryParameter("target_type", "Manga") | ||||
|                 .build() | ||||
|   | ||||
| @@ -11,13 +11,10 @@ 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), EnhancedTrackService { | ||||
| class Suwayomi(id: Long) : TrackService(id, "Suwayomi"), EnhancedTrackService { | ||||
|  | ||||
|     val api by lazy { TachideskApi() } | ||||
|  | ||||
|     @StringRes | ||||
|     override fun nameRes() = R.string.tracker_suwayomi | ||||
|  | ||||
|     override fun getLogo() = R.drawable.ic_tracker_suwayomi | ||||
|  | ||||
|     override fun getLogoColor() = Color.rgb(255, 35, 35) // TODO | ||||
|   | ||||
| @@ -28,19 +28,19 @@ class TachideskApi { | ||||
|     private val network: NetworkHelper by injectLazy() | ||||
|     private val json: Json by injectLazy() | ||||
|  | ||||
|     val client: OkHttpClient = | ||||
|     private val client: OkHttpClient = | ||||
|         network.client.newBuilder() | ||||
|             .dns(Dns.SYSTEM) // don't use DNS over HTTPS as it breaks IP addressing | ||||
|             .build() | ||||
|  | ||||
|     fun headersBuilder(): Headers.Builder = Headers.Builder().apply { | ||||
|     private fun headersBuilder(): Headers.Builder = Headers.Builder().apply { | ||||
|         if (basePassword.isNotEmpty() && baseLogin.isNotEmpty()) { | ||||
|             val credentials = Credentials.basic(baseLogin, basePassword) | ||||
|             add("Authorization", credentials) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     val headers: Headers by lazy { headersBuilder().build() } | ||||
|     private val headers: Headers by lazy { headersBuilder().build() } | ||||
|  | ||||
|     private val baseUrl by lazy { getPrefBaseUrl() } | ||||
|     private val baseLogin by lazy { getPrefBaseLogin() } | ||||
| @@ -100,7 +100,7 @@ class TachideskApi { | ||||
|         return getTrackSearch(track.tracking_url) | ||||
|     } | ||||
|  | ||||
|     val tachideskExtensionId by lazy { | ||||
|     private val tachideskExtensionId 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 | ||||
| @@ -110,6 +110,10 @@ class TachideskApi { | ||||
|         Injekt.get<Application>().getSharedPreferences("source_$tachideskExtensionId", 0x0000) | ||||
|     } | ||||
|  | ||||
|     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 = "" | ||||
| @@ -118,8 +122,4 @@ class TachideskApi { | ||||
|         private const val PASSWORD_TITLE = "Password (Basic Auth)" | ||||
|         private const val PASSWORD_DEFAULT = "" | ||||
|     } | ||||
|  | ||||
|     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)!! | ||||
| } | ||||
|   | ||||
| @@ -14,7 +14,7 @@ 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.SyncChaptersWithTrackServiceTwoWay | ||||
| 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 | ||||
| @@ -77,7 +77,7 @@ class BrowseSourceScreenModel( | ||||
|     private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), | ||||
|     private val updateManga: UpdateManga = Injekt.get(), | ||||
|     private val insertTrack: InsertTrack = Injekt.get(), | ||||
|     private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get(), | ||||
|     private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack = Injekt.get(), | ||||
| ) : StateScreenModel<BrowseSourceScreenModel.State>(State(Listing.valueOf(listingQuery))) { | ||||
|  | ||||
|     private val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLoggedIn } } | ||||
| @@ -297,7 +297,7 @@ class BrowseSourceScreenModel( | ||||
|                         (service as TrackService).bind(track) | ||||
|                         insertTrack.await(track.toDomainTrack()!!) | ||||
|  | ||||
|                         syncChaptersWithTrackServiceTwoWay.await(manga.id, track.toDomainTrack()!!, service) | ||||
|                         syncChapterProgressWithTrack.await(manga.id, track.toDomainTrack()!!, service) | ||||
|                     } | ||||
|                 } catch (e: Exception) { | ||||
|                     logcat(LogPriority.WARN, e) { "Could not match manga: ${manga.title} with service $service" } | ||||
|   | ||||
| @@ -323,7 +323,7 @@ class MangaScreenModel( | ||||
|                         launchIO { | ||||
|                             try { | ||||
|                                 service.match(manga)?.let { track -> | ||||
|                                     (service as TrackService).registerTracking(track, mangaId) | ||||
|                                     (service as TrackService).register(track, mangaId) | ||||
|                                 } | ||||
|                             } catch (e: Exception) { | ||||
|                                 logcat(LogPriority.WARN, e) { | ||||
|   | ||||
| @@ -39,9 +39,8 @@ import cafe.adriel.voyager.core.model.rememberScreenModel | ||||
| import cafe.adriel.voyager.navigator.LocalNavigator | ||||
| import cafe.adriel.voyager.navigator.Navigator | ||||
| import cafe.adriel.voyager.navigator.currentOrThrow | ||||
| import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay | ||||
| import eu.kanade.domain.track.interactor.RefreshTracks | ||||
| import eu.kanade.domain.track.model.toDbTrack | ||||
| import eu.kanade.domain.track.model.toDomainTrack | ||||
| import eu.kanade.domain.ui.UiPreferences | ||||
| import eu.kanade.presentation.track.TrackChapterSelector | ||||
| import eu.kanade.presentation.track.TrackDateSelector | ||||
| @@ -74,7 +73,6 @@ import tachiyomi.domain.manga.interactor.GetManga | ||||
| import tachiyomi.domain.source.service.SourceManager | ||||
| import tachiyomi.domain.track.interactor.DeleteTrack | ||||
| import tachiyomi.domain.track.interactor.GetTracks | ||||
| import tachiyomi.domain.track.interactor.InsertTrack | ||||
| import tachiyomi.domain.track.model.Track | ||||
| import tachiyomi.presentation.core.components.material.AlertDialogContent | ||||
| import tachiyomi.presentation.core.components.material.padding | ||||
| @@ -208,7 +206,7 @@ data class TrackInfoDialogHomeScreen( | ||||
|                 val manga = Injekt.get<GetManga>().await(mangaId) ?: return@launchNonCancellable | ||||
|                 try { | ||||
|                     val matchResult = item.service.match(manga) ?: throw Exception() | ||||
|                     item.service.registerTracking(matchResult, mangaId) | ||||
|                     item.service.register(matchResult, mangaId) | ||||
|                 } catch (e: Exception) { | ||||
|                     withUIContext { Injekt.get<Application>().toast(R.string.error_no_match) } | ||||
|                 } | ||||
| @@ -216,38 +214,25 @@ data class TrackInfoDialogHomeScreen( | ||||
|         } | ||||
|  | ||||
|         private suspend fun refreshTrackers() { | ||||
|             val insertTrack = Injekt.get<InsertTrack>() | ||||
|             val syncChaptersWithTrackServiceTwoWay = Injekt.get<SyncChaptersWithTrackServiceTwoWay>() | ||||
|             val refreshTracks = Injekt.get<RefreshTracks>() | ||||
|             val context = Injekt.get<Application>() | ||||
|  | ||||
|             try { | ||||
|                 val trackItems = getTracks.await(mangaId).mapToTrackItem() | ||||
|                 for (trackItem in trackItems) { | ||||
|                     try { | ||||
|                         val track = trackItem.track ?: continue | ||||
|                         val domainTrack = trackItem.service.refresh(track.toDbTrack()).toDomainTrack() ?: continue | ||||
|                         insertTrack.await(domainTrack) | ||||
|                         syncChaptersWithTrackServiceTwoWay.await(mangaId, domainTrack, trackItem.service) | ||||
|                     } catch (e: Exception) { | ||||
|                         logcat( | ||||
|                             LogPriority.ERROR, | ||||
|                             e, | ||||
|                         ) { "Failed to refresh track data mangaId=$mangaId for service ${trackItem.service.id}" } | ||||
|                         withUIContext { | ||||
|                             context.toast( | ||||
|                                 context.getString( | ||||
|                                     R.string.track_error, | ||||
|                                     context.getString(trackItem.service.nameRes()), | ||||
|                                     e.message, | ||||
|                                 ), | ||||
|                             ) | ||||
|                         } | ||||
|             refreshTracks.await(mangaId) | ||||
|                 .filter { it.first != null } | ||||
|                 .forEach { (track, e) -> | ||||
|                     logcat(LogPriority.ERROR, e) { | ||||
|                         "Failed to refresh track data mangaId=$mangaId for service ${track!!.id}" | ||||
|                     } | ||||
|                     withUIContext { | ||||
|                         context.toast( | ||||
|                             context.getString( | ||||
|                                 R.string.track_error, | ||||
|                                 track!!.name, | ||||
|                                 e.message, | ||||
|                             ), | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } catch (e: Exception) { | ||||
|                 logcat(LogPriority.ERROR, e) { "Failed to refresh track data mangaId=$mangaId" } | ||||
|                 withUIContext { context.toast(e.message) } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private fun List<Track>.mapToTrackItem(): List<TrackItem> { | ||||
| @@ -581,7 +566,7 @@ private data class TrackDateRemoverScreen( | ||||
|                 ) | ||||
|             }, | ||||
|             text = { | ||||
|                 val serviceName = stringResource(sm.getServiceNameRes()) | ||||
|                 val serviceName = sm.getServiceName() | ||||
|                 Text( | ||||
|                     text = if (start) { | ||||
|                         stringResource(R.string.track_remove_start_date_conf_text, serviceName) | ||||
| @@ -618,7 +603,7 @@ private data class TrackDateRemoverScreen( | ||||
|         private val start: Boolean, | ||||
|     ) : ScreenModel { | ||||
|  | ||||
|         fun getServiceNameRes() = service.nameRes() | ||||
|         fun getServiceName() = service.name | ||||
|  | ||||
|         fun removeDate() { | ||||
|             coroutineScope.launchNonCancellable { | ||||
| @@ -703,7 +688,7 @@ data class TrackServiceSearchScreen( | ||||
|         } | ||||
|  | ||||
|         fun registerTracking(item: TrackSearch) { | ||||
|             coroutineScope.launchNonCancellable { service.registerTracking(item, mangaId) } | ||||
|             coroutineScope.launchNonCancellable { service.register(item, mangaId) } | ||||
|         } | ||||
|  | ||||
|         fun updateSelection(selected: TrackSearch) { | ||||
| @@ -734,7 +719,7 @@ private data class TrackServiceRemoveScreen( | ||||
|                 service = Injekt.get<TrackManager>().getService(serviceId)!!, | ||||
|             ) | ||||
|         } | ||||
|         val serviceName = stringResource(sm.getServiceNameRes()) | ||||
|         val serviceName = sm.getServiceName() | ||||
|         var removeRemoteTrack by remember { mutableStateOf(false) } | ||||
|         AlertDialogContent( | ||||
|             modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars), | ||||
| @@ -799,7 +784,7 @@ private data class TrackServiceRemoveScreen( | ||||
|         private val deleteTrack: DeleteTrack = Injekt.get(), | ||||
|     ) : ScreenModel { | ||||
|  | ||||
|         fun getServiceNameRes() = service.nameRes() | ||||
|         fun getServiceName() = service.name | ||||
|  | ||||
|         fun isServiceDeletable() = service is DeletableTrackService | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user