diff --git a/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt b/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt index 67bdc60aa..01fda02e1 100644 --- a/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt +++ b/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt @@ -43,6 +43,13 @@ class TrackPreferences( AutoTrackState.ALWAYS, ) + // SY --> + fun resolveUsingSourceMetadata() = preferenceStore.getBoolean( + "pref_resolve_using_source_metadata_key", + true, + ) + // SY <-- + // KMK --> fun autoSyncProgressFromTrackers() = preferenceStore.getBoolean("pref_auto_sync_progress_from_trackers_key", true) // KMK <-- diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt index b235d17c9..3808c6116 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt @@ -63,6 +63,7 @@ import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.domain.source.service.SourceManager import tachiyomi.i18n.MR import tachiyomi.i18n.kmk.KMR +import tachiyomi.i18n.sy.SYMR import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource import uy.kohesive.injekt.Injekt @@ -146,6 +147,13 @@ object SettingsTrackingScreen : SearchableSettings { title = stringResource(KMR.strings.pref_auto_sync_progress_from_trackers), ), // KMK <-- + // SY --> + Preference.PreferenceItem.SwitchPreference( + preference = trackPreferences.resolveUsingSourceMetadata(), + title = stringResource(SYMR.strings.pref_tracker_resolve_using_source_metadata), + subtitle = stringResource(SYMR.strings.pref_tracker_resolve_using_source_metadata_summary), + ), + // SY <-- Preference.PreferenceGroup( title = stringResource(MR.strings.services), preferenceItems = persistentListOf( diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt index 0e587eb82..69ecb3010 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt @@ -7,6 +7,7 @@ import eu.kanade.domain.track.model.toDomainTrack import eu.kanade.domain.track.service.TrackPreferences import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.track.model.TrackMangaMetadata +import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.util.system.toast import kotlinx.coroutines.flow.Flow @@ -132,6 +133,12 @@ abstract class BaseTracker( throw NotImplementedError("Not implemented.") } + // SY --> + override suspend fun searchById(id: String): TrackSearch? { + throw NotImplementedError("Not implemented.") + } + // SY <-- + private suspend fun updateRemote(track: Track): Unit = withIOContext { try { update(track) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt index b6ca40316..bc718a02e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt @@ -90,6 +90,8 @@ interface Tracker { // SY --> suspend fun getMangaMetadata(track: DomainTrack): TrackMangaMetadata + + suspend fun searchById(id: String): TrackSearch? // SY <-- // KMK --> diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt index 873014398..11a9c38dc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt @@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.data.track.DeletableTracker import eu.kanade.tachiyomi.data.track.anilist.dto.ALOAuth import eu.kanade.tachiyomi.data.track.model.TrackMangaMetadata import eu.kanade.tachiyomi.data.track.model.TrackSearch +import exh.log.xLogW import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -238,6 +239,17 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker { return api.getMangaMetadata(track) } + // SY --> + override suspend fun searchById(id: String): TrackSearch? { + return try { + api.searchById(id) + } catch (e: Exception) { + xLogW("Error during searchById '$id': ${e.message}", e) + null + } + } + // SY <-- + fun saveOAuth(alOAuth: ALOAuth?) { trackPreferences.trackToken(this).set(json.encodeToString(alOAuth)) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt index f26bebd2d..a2acb6cda 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt @@ -5,6 +5,7 @@ import androidx.core.net.toUri import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.track.anilist.dto.ALAddMangaResult import eu.kanade.tachiyomi.data.track.anilist.dto.ALCurrentUserResult +import eu.kanade.tachiyomi.data.track.anilist.dto.ALIdSearchResult import eu.kanade.tachiyomi.data.track.anilist.dto.ALMangaMetadata import eu.kanade.tachiyomi.data.track.anilist.dto.ALOAuth import eu.kanade.tachiyomi.data.track.anilist.dto.ALSearchResult @@ -388,6 +389,56 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) { } } + // SY --> + suspend fun searchById(id: String): TrackSearch { + return withIOContext { + val query = """ + |query (${'$'}mangaId: Int!) { + |Media (id: ${'$'}mangaId) { + |id + |title { + |userPreferred + |} + |coverImage { + |large + |} + |format + |status + |chapters + |description + |startDate { + |year + |month + |day + |} + |averageScore + |} + |} + | + """.trimMargin() + val payload = buildJsonObject { + put("query", query) + putJsonObject("variables") { + put("mangaId", id) + } + } + with(json) { + authClient.newCall( + POST( + API_URL, + body = payload.toString().toRequestBody(jsonMime), + ), + ) + .awaitSuccess() + .parseAs() + .data.media + .toALManga() + .toTrack() + } + } + } + // SY <-- + private fun createDate(dateValue: Long): JsonObject { if (dateValue == 0L) { return buildJsonObject { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/dto/ALSearch.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/dto/ALSearch.kt index f13ebb400..c84d28c3e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/dto/ALSearch.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/dto/ALSearch.kt @@ -18,3 +18,16 @@ data class ALSearchPage( data class ALSearchMedia( val media: List, ) + +// SY --> +@Serializable +data class ALIdSearchResult( + val data: ALIdSearchMedia, +) + +@Serializable +data class ALIdSearchMedia( + @SerialName("Media") + val media: ALSearchItem, +) +// SY <-- diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdates.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdates.kt index f4e60044b..c0b12a1fd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdates.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdates.kt @@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.data.track.mangaupdates.dto.toTrackSearch import eu.kanade.tachiyomi.data.track.model.TrackMangaMetadata import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.util.lang.htmlDecode +import exh.log.xLogW import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import tachiyomi.i18n.MR @@ -135,6 +136,34 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker } } + // SY --> + override suspend fun searchById(id: String): TrackSearch? { + /* + * MangaUpdates uses newer base36 IDs (in URLs displayed as an encoded string, internally as a long) + * as well as older sequential numeric IDs, which were phased out to prevent heavy load caused by + * database scraping. Unfortunately, sites like MD sometimes still provides links with the old IDs, + * so we need to convert them. + * Because the API only accepts the newer IDs, we are forced to access the legacy non-API website + * (ex. https://www.mangaupdates.com/series.html?id=15), which is a permanent redirect (HTTP 308) to the new one. + */ + + val base36Id = if (id.matches(Regex("""^\d+$"""))) { + api.convertToNewId(id.toInt()) ?: return null + } else { + id + } + + return try { + base36Id.toLong(36).let { longId -> + api.getSeries(longId).toTrackSearch(this.id) + } + } catch (e: Exception) { + xLogW("Error during searchById '$id': ${e.message}", e) + null + } + } + // SY <-- + fun restoreSession(): String? { return trackPreferences.trackPassword(this).get().ifBlank { null } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdatesApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdatesApi.kt index fcb04c914..2e03ba985 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdatesApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdatesApi.kt @@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.network.DELETE import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.PUT +import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.awaitSuccess import eu.kanade.tachiyomi.network.parseAs import kotlinx.serialization.json.Json @@ -25,6 +26,8 @@ import kotlinx.serialization.json.putJsonObject import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import tachiyomi.core.common.util.lang.withIOContext import uy.kohesive.injekt.injectLazy import tachiyomi.domain.track.model.Track as DomainTrack @@ -190,14 +193,38 @@ class MangaUpdatesApi( } } - suspend fun getSeries(track: DomainTrack): MURecord { + suspend fun getSeries(track: DomainTrack): MURecord = + getSeries(track.remoteId) + + // SY --> + suspend fun getSeries(remoteId: Long): MURecord { return with(json) { - client.newCall(GET("$BASE_URL/v1/series/${track.remoteId}")) + client.newCall(GET("$BASE_URL/v1/series/$remoteId")) .awaitSuccess() .parseAs() } } + suspend fun convertToNewId(legacyId: Int): String? = withIOContext { + client.newBuilder() + .followRedirects(false) + .build() + .newCall(GET("https://www.mangaupdates.com/series.html?id=$legacyId")) + .await() + .use { response -> + response.takeIf(Response::isRedirect) + ?.header("Location") + ?.let { location -> + // Extract the new id from the redirected URL + Regex("""/series/(\w+)""") + .find(location) + ?.groups?.get(1) + ?.value + } + } + } + // SY <-- + companion object { private const val BASE_URL = "https://api.mangaupdates.com" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt index 14ac1ae27..7ba2a8019 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt @@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.data.track.DeletableTracker import eu.kanade.tachiyomi.data.track.model.TrackMangaMetadata import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.myanimelist.dto.MALOAuth +import exh.log.xLogW import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.serialization.json.Json @@ -160,6 +161,22 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker { return api.getMangaMetadata(track) } + // SY --> + override suspend fun searchById(id: String): TrackSearch? { + val searchId = id.toIntOrNull() + ?: run { + xLogW("Invalid ID format for searchById: $id") + return null + } + return try { + api.getMangaDetails(searchId) + } catch (e: Exception) { + xLogW("Error during searchById '$id': ${e.message}", e) + null + } + } + // SY <-- + fun getIfAuthExpired(): Boolean { return trackPreferences.trackAuthExpired(this).get() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt index 594222c70..03c3414b3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt @@ -2,17 +2,20 @@ package eu.kanade.tachiyomi.ui.manga.track import android.app.Application import android.content.Context +import androidx.compose.animation.animateContentSize import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -30,6 +33,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import cafe.adriel.voyager.core.model.ScreenModel import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.rememberScreenModel @@ -40,6 +45,7 @@ import cafe.adriel.voyager.navigator.currentOrThrow import dev.icerock.moko.resources.StringResource import eu.kanade.domain.track.interactor.RefreshTracks import eu.kanade.domain.track.model.toDbTrack +import eu.kanade.domain.track.service.TrackPreferences import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.track.TrackChapterSelector import eu.kanade.presentation.track.TrackDateSelector @@ -53,10 +59,13 @@ 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.source.online.MetadataSource import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.toast +import exh.metadata.metadata.base.TrackerIdMetadata +import exh.source.getMainSource import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collectLatest @@ -71,6 +80,7 @@ import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.logcat +import tachiyomi.domain.manga.interactor.GetFlatMetadataById import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.track.interactor.DeleteTrack @@ -103,78 +113,95 @@ data class TrackInfoDialogHomeScreen( val dateFormat = remember { UiPreferences.dateFormat(Injekt.get().dateFormat().get()) } val state by screenModel.state.collectAsState() - TrackInfoDialogHome( - trackItems = state.trackItems, - dateFormat = dateFormat, - onStatusClick = { - navigator.push( - TrackStatusSelectorScreen( - track = it.track!!, - serviceId = it.tracker.id, - ), - ) - }, - onChapterClick = { - navigator.push( - TrackChapterSelectorScreen( - track = it.track!!, - serviceId = it.tracker.id, - ), - ) - }, - onScoreClick = { - navigator.push( - TrackScoreSelectorScreen( - track = it.track!!, - serviceId = it.tracker.id, - ), - ) - }, - onStartDateEdit = { - navigator.push( - TrackDateSelectorScreen( - track = it.track!!, - serviceId = it.tracker.id, - start = true, - ), - ) - }, - onEndDateEdit = { - navigator.push( - TrackDateSelectorScreen( - track = it.track!!, - serviceId = it.tracker.id, - start = false, - ), - ) - }, - onNewSearch = { - if (it.tracker is EnhancedTracker) { - screenModel.registerEnhancedTracking(it) - } else { - navigator.push( - TrackerSearchScreen( - mangaId = mangaId, - initialQuery = it.track?.title ?: mangaTitle, - currentUrl = it.track?.remoteUrl, - serviceId = it.tracker.id, - ), + // SY --> + Column(modifier = Modifier.animateContentSize()) { + if (state.isLoading) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(32.dp) + .windowInsetsPadding(WindowInsets.systemBars), + verticalArrangement = Arrangement.spacedBy(24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + CircularProgressIndicator() + Text( + stringResource(MR.strings.loading), + fontSize = 14.sp, ) } - }, - onOpenInBrowser = { openTrackerInBrowser(context, it) }, - onRemoved = { - navigator.push( - TrackerRemoveScreen( - mangaId = mangaId, - track = it.track!!, - serviceId = it.tracker.id, - ), + } + // SY <-- + else { + TrackInfoDialogHome( + trackItems = state.trackItems, + dateFormat = dateFormat, + onStatusClick = { + navigator.push( + TrackStatusSelectorScreen( + track = it.track!!, + serviceId = it.tracker.id, + ), + ) + }, + onChapterClick = { + navigator.push( + TrackChapterSelectorScreen( + track = it.track!!, + serviceId = it.tracker.id, + ), + ) + }, + onScoreClick = { + navigator.push( + TrackScoreSelectorScreen( + track = it.track!!, + serviceId = it.tracker.id, + ), + ) + }, + onStartDateEdit = { + navigator.push( + TrackDateSelectorScreen( + track = it.track!!, + serviceId = it.tracker.id, + start = true, + ), + ) + }, + onEndDateEdit = { + navigator.push( + TrackDateSelectorScreen( + track = it.track!!, + serviceId = it.tracker.id, + start = false, + ), + ) + }, + onNewSearch = { + if (it.tracker is EnhancedTracker) { + screenModel.registerEnhancedTracking(it) + } else { + // SY --> + screenModel.newSearch(navigator, it, mangaTitle) + // SY <-- + } + }, + onOpenInBrowser = { openTrackerInBrowser(context, it) }, + onRemoved = { + navigator.push( + TrackerRemoveScreen( + mangaId = mangaId, + track = it.track!!, + serviceId = it.tracker.id, + ), + ) + }, + onCopyLink = { context.copyTrackerLink(it) }, + onTogglePrivate = screenModel::togglePrivate, ) - }, - onCopyLink = { context.copyTrackerLink(it) }, - onTogglePrivate = screenModel::togglePrivate, - ) + } + } } /** @@ -198,6 +225,14 @@ data class TrackInfoDialogHomeScreen( private val mangaId: Long, private val sourceId: Long, private val getTracks: GetTracks = Injekt.get(), + // SY --> + private val trackerManager: TrackerManager = Injekt.get(), + private val trackPreferences: TrackPreferences = Injekt.get(), + // SY <-- + // KMK --> + val sourceManager: SourceManager = Injekt.get(), + val getFlatMetadataById: GetFlatMetadataById = Injekt.get(), + // KMK <-- ) : StateScreenModel(State()) { init { @@ -227,6 +262,77 @@ data class TrackInfoDialogHomeScreen( } } + // SY --> + fun newSearch(navigator: Navigator, item: TrackItem, mangaTitle: String) { + screenModelScope.launchNonCancellable { + if (trackPreferences.resolveUsingSourceMetadata().get()) { + // Check if the tracker id is contained in the metadata + val result = getTrackerIdFromMetadata(item.tracker.id) + if (result != null) { + mutableState.update { it.copy(isLoading = true) } + + // Try to register tracking by id + val success = registerTrackingById(item.tracker.id, result) + + mutableState.update { it.copy(isLoading = false) } + + if (success) { + // Return on success + return@launchNonCancellable + } + } + } + + // Open search screen + navigator.push( + TrackerSearchScreen( + mangaId = mangaId, + initialQuery = item.track?.title ?: mangaTitle, + currentUrl = item.track?.remoteUrl, + serviceId = item.tracker.id, + ), + ) + } + } + + suspend fun getTrackerIdFromMetadata(trackerId: Long): String? { + try { + val metadataSource = sourceManager.get(sourceId) + ?.getMainSource>() ?: return null + + return getFlatMetadataById.await(mangaId)?.run { + // Use 'raise' to dynamically obtain the specific metadata type and then attempt to cast + raise(metadataSource.metaClass) as? TrackerIdMetadata + }?.let { metadata -> + when (trackerId) { + trackerManager.aniList.id -> metadata.anilistId + trackerManager.kitsu.id -> metadata.kitsuId + trackerManager.myAnimeList.id -> metadata.myAnimeListId + trackerManager.mangaUpdates.id -> metadata.mangaUpdatesId + else -> null + } + } + } catch (e: Throwable) { + logcat(LogPriority.ERROR, e) { "Failed to get tracker ID from metadata" } + return null + } + } + + suspend fun registerTrackingById(trackerId: Long, remoteId: String): Boolean { + trackerManager.get(trackerId)?.let { tracker -> + try { + tracker.searchById(remoteId)?.let { track -> + tracker.register(track, mangaId) + return true + } + } catch (e: Throwable) { + logcat(LogPriority.ERROR, e) { "Failed to register tracking by id" } + } + } + return false + } + // SY <-- + private suspend fun refreshTrackers() { val refreshTracks = Injekt.get() val context = Injekt.get() @@ -268,6 +374,9 @@ data class TrackInfoDialogHomeScreen( @Immutable data class State( val trackItems: List = emptyList(), + // SY --> + val isLoading: Boolean = false, + // SY <-- ) } } diff --git a/app/src/main/java/eu/kanade/test/DummyTracker.kt b/app/src/main/java/eu/kanade/test/DummyTracker.kt index 7ef68f30a..d5f87f84c 100644 --- a/app/src/main/java/eu/kanade/test/DummyTracker.kt +++ b/app/src/main/java/eu/kanade/test/DummyTracker.kt @@ -131,6 +131,8 @@ data class DummyTracker( override suspend fun getMangaMetadata( track: Track, ): TrackMangaMetadata = TrackMangaMetadata(0, "test", "test", "test", "test", "test") + + override suspend fun searchById(id: String) = null // SY <-- // KMK --> diff --git a/i18n-sy/src/commonMain/moko-resources/base/strings.xml b/i18n-sy/src/commonMain/moko-resources/base/strings.xml index 6bb5770b2..13c26e356 100644 --- a/i18n-sy/src/commonMain/moko-resources/base/strings.xml +++ b/i18n-sy/src/commonMain/moko-resources/base/strings.xml @@ -176,6 +176,10 @@ Show history in the nav Always show nav labels + + Select entries using source metadata + Automatically selects the matching title if the source provides links to trackers. Currently supported by MangaDex + Sorting Settings Use last saved pre-migration preferences and sources to mass migrate diff --git a/source-api/src/commonMain/kotlin/exh/metadata/metadata/MangaDexSearchMetadata.kt b/source-api/src/commonMain/kotlin/exh/metadata/metadata/MangaDexSearchMetadata.kt index 468058cae..d01c72f1c 100644 --- a/source-api/src/commonMain/kotlin/exh/metadata/metadata/MangaDexSearchMetadata.kt +++ b/source-api/src/commonMain/kotlin/exh/metadata/metadata/MangaDexSearchMetadata.kt @@ -4,13 +4,14 @@ import android.content.Context import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.copy import exh.md.utils.MangaDexRelation +import exh.metadata.metadata.base.TrackerIdMetadata import kotlinx.serialization.Serializable import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import tachiyomi.i18n.sy.SYMR @Serializable -class MangaDexSearchMetadata : RaisedSearchMetadata() { +class MangaDexSearchMetadata : RaisedSearchMetadata(), TrackerIdMetadata { var mdUuid: String? = null // var mdUrl: String? = null @@ -31,11 +32,11 @@ class MangaDexSearchMetadata : RaisedSearchMetadata() { var rating: Float? = null // var users: String? = null - var anilistId: String? = null - var kitsuId: String? = null - var myAnimeListId: String? = null - var mangaUpdatesId: String? = null - var animePlanetId: String? = null + override var anilistId: String? = null + override var kitsuId: String? = null + override var myAnimeListId: String? = null + override var mangaUpdatesId: String? = null + override var animePlanetId: String? = null var status: Int? = null diff --git a/source-api/src/commonMain/kotlin/exh/metadata/metadata/base/TrackerIdMetadata.kt b/source-api/src/commonMain/kotlin/exh/metadata/metadata/base/TrackerIdMetadata.kt new file mode 100644 index 000000000..388e518b4 --- /dev/null +++ b/source-api/src/commonMain/kotlin/exh/metadata/metadata/base/TrackerIdMetadata.kt @@ -0,0 +1,9 @@ +package exh.metadata.metadata.base + +interface TrackerIdMetadata { + var anilistId: String? + var kitsuId: String? + var myAnimeListId: String? + var mangaUpdatesId: String? + var animePlanetId: String? +}