Last commit merged: 6a558ad119
This commit is contained in:
LuftVerbot 2023-11-18 16:01:50 +01:00
parent 1b6301cc95
commit a2a445fdc5
69 changed files with 445 additions and 325 deletions

View file

@ -177,6 +177,7 @@ dependencies {
implementation(androidx.paging.compose)
implementation(libs.bundles.sqlite)
implementation(libs.sqldelight.primitive.adapters)
implementation(kotlinx.reflect)

View file

@ -81,7 +81,7 @@ import tachiyomi.domain.entries.anime.interactor.GetLibraryAnime
import tachiyomi.domain.entries.anime.interactor.NetworkToLocalAnime
import tachiyomi.domain.entries.anime.interactor.ResetAnimeViewerFlags
import tachiyomi.domain.entries.anime.interactor.SetAnimeEpisodeFlags
import tachiyomi.domain.entries.anime.interactor.SetAnimeUpdateInterval
import tachiyomi.domain.entries.anime.interactor.SetAnimeFetchInterval
import tachiyomi.domain.entries.anime.repository.AnimeRepository
import tachiyomi.domain.entries.manga.interactor.GetDuplicateLibraryManga
import tachiyomi.domain.entries.manga.interactor.GetLibraryManga
@ -91,7 +91,7 @@ import tachiyomi.domain.entries.manga.interactor.GetMangaWithChapters
import tachiyomi.domain.entries.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.entries.manga.interactor.ResetMangaViewerFlags
import tachiyomi.domain.entries.manga.interactor.SetMangaChapterFlags
import tachiyomi.domain.entries.manga.interactor.SetMangaUpdateInterval
import tachiyomi.domain.entries.manga.interactor.SetMangaFetchInterval
import tachiyomi.domain.entries.manga.repository.MangaRepository
import tachiyomi.domain.history.anime.interactor.GetAnimeHistory
import tachiyomi.domain.history.anime.interactor.GetNextEpisodes
@ -184,7 +184,7 @@ class DomainModule : InjektModule {
addFactory { GetNextEpisodes(get(), get(), get()) }
addFactory { ResetAnimeViewerFlags(get()) }
addFactory { SetAnimeEpisodeFlags(get()) }
addFactory { SetAnimeUpdateInterval(get()) }
addFactory { SetAnimeFetchInterval(get()) }
addFactory { SetAnimeDefaultEpisodeFlags(get(), get(), get()) }
addFactory { SetAnimeViewerFlags(get()) }
addFactory { NetworkToLocalAnime(get()) }
@ -200,7 +200,7 @@ class DomainModule : InjektModule {
addFactory { GetNextChapters(get(), get(), get()) }
addFactory { ResetMangaViewerFlags(get()) }
addFactory { SetMangaChapterFlags(get()) }
addFactory { SetMangaUpdateInterval(get()) }
addFactory { SetMangaFetchInterval(get()) }
addFactory {
SetMangaDefaultChapterFlags(
get(),

View file

@ -3,7 +3,7 @@ package eu.kanade.domain.entries.anime.interactor
import eu.kanade.domain.entries.anime.model.hasCustomCover
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
import tachiyomi.domain.entries.anime.interactor.SetAnimeUpdateInterval
import tachiyomi.domain.entries.anime.interactor.SetAnimeFetchInterval
import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.entries.anime.model.AnimeUpdate
import tachiyomi.domain.entries.anime.repository.AnimeRepository
@ -16,7 +16,7 @@ import java.util.Date
class UpdateAnime(
private val animeRepository: AnimeRepository,
private val setAnimeUpdateInterval: SetAnimeUpdateInterval,
private val setAnimeFetchInterval: SetAnimeFetchInterval,
) {
suspend fun await(animeUpdate: AnimeUpdate): Boolean {
@ -81,9 +81,9 @@ class UpdateAnime(
anime: Anime,
episodes: List<Episode>,
zonedDateTime: ZonedDateTime = ZonedDateTime.now(),
fetchRange: Pair<Long, Long> = setAnimeUpdateInterval.getCurrentFetchRange(zonedDateTime),
fetchRange: Pair<Long, Long> = setAnimeFetchInterval.getCurrent(zonedDateTime),
): Boolean {
val updateAnime = setAnimeUpdateInterval.updateInterval(anime, episodes, zonedDateTime, fetchRange)
val updateAnime = setAnimeFetchInterval.update(anime, episodes, zonedDateTime, fetchRange)
return if (updateAnime != null) {
animeRepository.updateAnime(updateAnime)
} else {

View file

@ -3,7 +3,7 @@ package eu.kanade.domain.entries.manga.interactor
import eu.kanade.domain.entries.manga.model.hasCustomCover
import eu.kanade.tachiyomi.data.cache.MangaCoverCache
import eu.kanade.tachiyomi.source.model.SManga
import tachiyomi.domain.entries.manga.interactor.SetMangaUpdateInterval
import tachiyomi.domain.entries.manga.interactor.SetMangaFetchInterval
import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.entries.manga.model.MangaUpdate
import tachiyomi.domain.entries.manga.repository.MangaRepository
@ -16,7 +16,7 @@ import java.util.Date
class UpdateManga(
private val mangaRepository: MangaRepository,
private val setMangaUpdateInterval: SetMangaUpdateInterval,
private val setMangaFetchInterval: SetMangaFetchInterval,
) {
suspend fun await(mangaUpdate: MangaUpdate): Boolean {
@ -81,9 +81,9 @@ class UpdateManga(
manga: Manga,
chapters: List<Chapter>,
zonedDateTime: ZonedDateTime = ZonedDateTime.now(),
fetchRange: Pair<Long, Long> = setMangaUpdateInterval.getCurrentFetchRange(zonedDateTime),
fetchRange: Pair<Long, Long> = setMangaFetchInterval.getCurrent(zonedDateTime),
): Boolean {
val updatedManga = setMangaUpdateInterval.updateInterval(manga, chapters, zonedDateTime, fetchRange)
val updatedManga = setMangaFetchInterval.update(manga, chapters, zonedDateTime, fetchRange)
return if (updatedManga != null) {
mangaRepository.updateManga(updatedManga)
} else {

View file

@ -138,7 +138,7 @@ class SyncChaptersWithSource(
// Return if there's nothing to add, delete or change, avoiding unnecessary db transactions.
if (toAdd.isEmpty() && toDelete.isEmpty() && toChange.isEmpty()) {
if (manualFetch || manga.calculateInterval == 0 || manga.nextUpdate < fetchRange.first) {
if (manualFetch || manga.fetchInterval == 0 || manga.nextUpdate < fetchRange.first) {
updateManga.awaitUpdateFetchInterval(
manga,
dbChapters,

View file

@ -138,7 +138,7 @@ class SyncEpisodesWithSource(
// Return if there's nothing to add, delete or change, avoiding unnecessary db transactions.
if (toAdd.isEmpty() && toDelete.isEmpty() && toChange.isEmpty()) {
if (manualFetch || anime.calculateInterval == 0 || anime.nextUpdate < fetchRange.first) {
if (manualFetch || anime.fetchInterval == 0 || anime.nextUpdate < fetchRange.first) {
updateAnime.awaitUpdateFetchInterval(
anime,
dbEpisodes,

View file

@ -74,6 +74,7 @@ import eu.kanade.tachiyomi.source.anime.getNameForAnimeInfo
import eu.kanade.tachiyomi.ui.browse.anime.extension.details.SourcePreferencesScreen
import eu.kanade.tachiyomi.ui.entries.anime.AnimeScreenModel
import eu.kanade.tachiyomi.ui.entries.anime.EpisodeItem
import eu.kanade.tachiyomi.ui.entries.anime.FetchAnimeInterval
import eu.kanade.tachiyomi.util.lang.toRelativeString
import eu.kanade.tachiyomi.util.system.copyToClipboard
import kotlinx.coroutines.delay
@ -97,7 +98,7 @@ import java.util.concurrent.TimeUnit
fun AnimeScreen(
state: AnimeScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
intervalDisplay: () -> Pair<Int, Int>?,
fetchInterval: FetchAnimeInterval?,
dateFormat: DateFormat,
isTabletUi: Boolean,
episodeSwipeStartAction: LibraryPreferences.EpisodeSwipeAction,
@ -127,7 +128,7 @@ fun AnimeScreen(
onShareClicked: (() -> Unit)?,
onDownloadActionClicked: ((DownloadAction) -> Unit)?,
onEditCategoryClicked: (() -> Unit)?,
onEditIntervalClicked: (() -> Unit)?,
onEditFetchIntervalClicked: (() -> Unit)?,
onMigrateClicked: (() -> Unit)?,
changeAnimeSkipIntro: (() -> Unit)?,
@ -162,7 +163,7 @@ fun AnimeScreen(
state = state,
snackbarHostState = snackbarHostState,
dateFormat = dateFormat,
intervalDisplay = intervalDisplay,
fetchInterval = fetchInterval,
episodeSwipeStartAction = episodeSwipeStartAction,
episodeSwipeEndAction = episodeSwipeEndAction,
showNextEpisodeAirTime = showNextEpisodeAirTime,
@ -184,7 +185,7 @@ fun AnimeScreen(
onShareClicked = onShareClicked,
onDownloadActionClicked = onDownloadActionClicked,
onEditCategoryClicked = onEditCategoryClicked,
onEditIntervalClicked = onEditIntervalClicked,
onEditIntervalClicked = onEditFetchIntervalClicked,
onMigrateClicked = onMigrateClicked,
changeAnimeSkipIntro = changeAnimeSkipIntro,
onMultiBookmarkClicked = onMultiBookmarkClicked,
@ -206,7 +207,7 @@ fun AnimeScreen(
showNextEpisodeAirTime = showNextEpisodeAirTime,
alwaysUseExternalPlayer = alwaysUseExternalPlayer,
dateFormat = dateFormat,
intervalDisplay = intervalDisplay,
fetchInterval = fetchInterval,
onBackClicked = onBackClicked,
onEpisodeClicked = onEpisodeClicked,
onDownloadEpisode = onDownloadEpisode,
@ -224,7 +225,7 @@ fun AnimeScreen(
onShareClicked = onShareClicked,
onDownloadActionClicked = onDownloadActionClicked,
onEditCategoryClicked = onEditCategoryClicked,
onEditIntervalClicked = onEditIntervalClicked,
onEditIntervalClicked = onEditFetchIntervalClicked,
changeAnimeSkipIntro = changeAnimeSkipIntro,
onMigrateClicked = onMigrateClicked,
onMultiBookmarkClicked = onMultiBookmarkClicked,
@ -246,7 +247,7 @@ private fun AnimeScreenSmallImpl(
state: AnimeScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
dateFormat: DateFormat,
intervalDisplay: () -> Pair<Int, Int>?,
fetchInterval: FetchAnimeInterval?,
episodeSwipeStartAction: LibraryPreferences.EpisodeSwipeAction,
episodeSwipeEndAction: LibraryPreferences.EpisodeSwipeAction,
showNextEpisodeAirTime: Boolean,
@ -432,8 +433,8 @@ private fun AnimeScreenSmallImpl(
AnimeActionRow(
favorite = state.anime.favorite,
trackingCount = state.trackingCount,
intervalDisplay = intervalDisplay,
isUserIntervalMode = state.anime.calculateInterval < 0,
fetchInterval = fetchInterval,
isUserIntervalMode = state.anime.fetchInterval < 0,
onAddToLibraryClicked = onAddToLibraryClicked,
onWebViewClicked = onWebViewClicked,
onWebViewLongClicked = onWebViewLongClicked,
@ -517,7 +518,7 @@ fun AnimeScreenLargeImpl(
state: AnimeScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
dateFormat: DateFormat,
intervalDisplay: () -> Pair<Int, Int>?,
fetchInterval: FetchAnimeInterval?,
episodeSwipeStartAction: LibraryPreferences.EpisodeSwipeAction,
episodeSwipeEndAction: LibraryPreferences.EpisodeSwipeAction,
showNextEpisodeAirTime: Boolean,
@ -685,8 +686,8 @@ fun AnimeScreenLargeImpl(
AnimeActionRow(
favorite = state.anime.favorite,
trackingCount = state.trackingCount,
intervalDisplay = intervalDisplay,
isUserIntervalMode = state.anime.calculateInterval < 0,
fetchInterval = fetchInterval,
isUserIntervalMode = state.anime.fetchInterval < 0,
onAddToLibraryClicked = onAddToLibraryClicked,
onWebViewClicked = onWebViewClicked,
onWebViewLongClicked = onWebViewLongClicked,

View file

@ -1,6 +1,7 @@
package eu.kanade.presentation.entries.anime
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Spacer
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
@ -8,6 +9,7 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.R
@Composable
@ -18,15 +20,27 @@ fun DuplicateAnimeDialog(
) {
AlertDialog(
onDismissRequest = onDismissRequest,
title = {
Text(text = stringResource(R.string.are_you_sure))
},
text = {
Text(text = stringResource(R.string.confirm_add_duplicate_manga))
},
confirmButton = {
Row {
TextButton(onClick = {
onDismissRequest()
onOpenAnime()
},) {
FlowRow(
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
TextButton(
onClick = {
onDismissRequest()
onOpenAnime()
},
) {
Text(text = stringResource(R.string.action_show_anime))
}
Spacer(modifier = Modifier.weight(1f))
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(R.string.action_cancel))
}
@ -40,11 +54,5 @@ fun DuplicateAnimeDialog(
}
}
},
title = {
Text(text = stringResource(R.string.are_you_sure))
},
text = {
Text(text = stringResource(R.string.confirm_add_duplicate_manga))
},
)
}

View file

@ -80,6 +80,7 @@ import eu.kanade.presentation.entries.DotSeparatorText
import eu.kanade.presentation.entries.ItemCover
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.ui.entries.anime.FetchAnimeInterval
import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.presentation.core.components.material.TextButton
@ -167,7 +168,7 @@ fun AnimeActionRow(
modifier: Modifier = Modifier,
favorite: Boolean,
trackingCount: Int,
intervalDisplay: () -> Pair<Int, Int>?,
fetchInterval: FetchAnimeInterval?,
isUserIntervalMode: Boolean,
onAddToLibraryClicked: () -> Unit,
onWebViewClicked: (() -> Unit)?,
@ -176,7 +177,6 @@ fun AnimeActionRow(
onEditIntervalClicked: (() -> Unit)?,
onEditCategory: (() -> Unit)?,
) {
val interval: Pair<Int, Int>? = intervalDisplay()
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f)
Row(modifier = modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp)) {
@ -191,13 +191,14 @@ fun AnimeActionRow(
onClick = onAddToLibraryClicked,
onLongClick = onEditCategory,
)
if (onEditIntervalClicked != null && interval != null) {
if (onEditIntervalClicked != null && fetchInterval != null) {
val intervalPair = 1.coerceAtLeast(fetchInterval.interval - fetchInterval.leadDays) to (fetchInterval.interval + fetchInterval.followDays)
AnimeActionButton(
title =
if (interval.first == interval.second) {
pluralStringResource(id = R.plurals.day, count = interval.second, interval.second)
if (intervalPair.first == intervalPair.second) {
pluralStringResource(id = R.plurals.day, count = intervalPair.second, intervalPair.second)
} else {
pluralStringResource(id = R.plurals.range_interval_day, count = interval.second, interval.first, interval.second)
pluralStringResource(id = R.plurals.range_interval_day, count = intervalPair.second, intervalPair.first, intervalPair.second)
},
icon = Icons.Default.HourglassEmpty,
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,

View file

@ -1,6 +1,7 @@
package eu.kanade.presentation.entries.manga
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Spacer
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
@ -8,6 +9,7 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.R
@Composable
@ -18,15 +20,27 @@ fun DuplicateMangaDialog(
) {
AlertDialog(
onDismissRequest = onDismissRequest,
title = {
Text(text = stringResource(R.string.are_you_sure))
},
text = {
Text(text = stringResource(R.string.confirm_add_duplicate_manga))
},
confirmButton = {
Row {
TextButton(onClick = {
onDismissRequest()
onOpenManga()
},) {
FlowRow(
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
TextButton(
onClick = {
onDismissRequest()
onOpenManga()
},
) {
Text(text = stringResource(R.string.action_show_manga))
}
Spacer(modifier = Modifier.weight(1f))
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(R.string.action_cancel))
}
@ -40,11 +54,5 @@ fun DuplicateMangaDialog(
}
}
},
title = {
Text(text = stringResource(R.string.are_you_sure))
},
text = {
Text(text = stringResource(R.string.confirm_add_duplicate_manga))
},
)
}

View file

@ -68,6 +68,7 @@ import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.manga.getNameForMangaInfo
import eu.kanade.tachiyomi.ui.browse.manga.extension.details.MangaSourcePreferencesScreen
import eu.kanade.tachiyomi.ui.entries.manga.ChapterItem
import eu.kanade.tachiyomi.ui.entries.manga.FetchMangaInterval
import eu.kanade.tachiyomi.ui.entries.manga.MangaScreenModel
import eu.kanade.tachiyomi.util.lang.toRelativeString
import eu.kanade.tachiyomi.util.system.copyToClipboard
@ -90,7 +91,7 @@ import java.util.Date
fun MangaScreen(
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
intervalDisplay: () -> Pair<Int, Int>?,
fetchInterval: FetchMangaInterval?,
dateFormat: DateFormat,
isTabletUi: Boolean,
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
@ -118,7 +119,7 @@ fun MangaScreen(
onShareClicked: (() -> Unit)?,
onDownloadActionClicked: ((DownloadAction) -> Unit)?,
onEditCategoryClicked: (() -> Unit)?,
onEditIntervalClicked: (() -> Unit)?,
onEditFetchIntervalClicked: (() -> Unit)?,
onMigrateClicked: (() -> Unit)?,
// For bottom action menu
@ -152,7 +153,7 @@ fun MangaScreen(
state = state,
snackbarHostState = snackbarHostState,
dateFormat = dateFormat,
intervalDisplay = intervalDisplay,
fetchInterval = fetchInterval,
chapterSwipeStartAction = chapterSwipeStartAction,
chapterSwipeEndAction = chapterSwipeEndAction,
onBackClicked = onBackClicked,
@ -172,7 +173,7 @@ fun MangaScreen(
onShareClicked = onShareClicked,
onDownloadActionClicked = onDownloadActionClicked,
onEditCategoryClicked = onEditCategoryClicked,
onEditIntervalClicked = onEditIntervalClicked,
onEditIntervalClicked = onEditFetchIntervalClicked,
onMigrateClicked = onMigrateClicked,
onMultiBookmarkClicked = onMultiBookmarkClicked,
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
@ -191,7 +192,7 @@ fun MangaScreen(
chapterSwipeStartAction = chapterSwipeStartAction,
chapterSwipeEndAction = chapterSwipeEndAction,
dateFormat = dateFormat,
intervalDisplay = intervalDisplay,
fetchInterval = fetchInterval,
onBackClicked = onBackClicked,
onChapterClicked = onChapterClicked,
onDownloadChapter = onDownloadChapter,
@ -209,7 +210,7 @@ fun MangaScreen(
onShareClicked = onShareClicked,
onDownloadActionClicked = onDownloadActionClicked,
onEditCategoryClicked = onEditCategoryClicked,
onEditIntervalClicked = onEditIntervalClicked,
onEditIntervalClicked = onEditFetchIntervalClicked,
onMigrateClicked = onMigrateClicked,
onMultiBookmarkClicked = onMultiBookmarkClicked,
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
@ -229,7 +230,7 @@ private fun MangaScreenSmallImpl(
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
dateFormat: DateFormat,
intervalDisplay: () -> Pair<Int, Int>?,
fetchInterval: FetchMangaInterval?,
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
onBackClicked: () -> Unit,
@ -406,8 +407,8 @@ private fun MangaScreenSmallImpl(
MangaActionRow(
favorite = state.manga.favorite,
trackingCount = state.trackingCount,
intervalDisplay = intervalDisplay,
isUserIntervalMode = state.manga.calculateInterval < 0,
fetchInterval = fetchInterval,
isUserIntervalMode = state.manga.fetchInterval < 0,
onAddToLibraryClicked = onAddToLibraryClicked,
onWebViewClicked = onWebViewClicked,
onWebViewLongClicked = onWebViewLongClicked,
@ -465,7 +466,7 @@ fun MangaScreenLargeImpl(
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
dateFormat: DateFormat,
intervalDisplay: () -> Pair<Int, Int>?,
fetchInterval: FetchMangaInterval?,
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
onBackClicked: () -> Unit,
@ -627,8 +628,8 @@ fun MangaScreenLargeImpl(
MangaActionRow(
favorite = state.manga.favorite,
trackingCount = state.trackingCount,
intervalDisplay = intervalDisplay,
isUserIntervalMode = state.manga.calculateInterval < 0,
fetchInterval = fetchInterval,
isUserIntervalMode = state.manga.fetchInterval < 0,
onAddToLibraryClicked = onAddToLibraryClicked,
onWebViewClicked = onWebViewClicked,
onWebViewLongClicked = onWebViewLongClicked,

View file

@ -80,6 +80,7 @@ import eu.kanade.presentation.entries.DotSeparatorText
import eu.kanade.presentation.entries.ItemCover
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.entries.manga.FetchMangaInterval
import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.presentation.core.components.material.TextButton
@ -167,7 +168,7 @@ fun MangaActionRow(
modifier: Modifier = Modifier,
favorite: Boolean,
trackingCount: Int,
intervalDisplay: () -> Pair<Int, Int>?,
fetchInterval: FetchMangaInterval?,
isUserIntervalMode: Boolean,
onAddToLibraryClicked: () -> Unit,
onWebViewClicked: (() -> Unit)?,
@ -176,7 +177,6 @@ fun MangaActionRow(
onEditIntervalClicked: (() -> Unit)?,
onEditCategory: (() -> Unit)?,
) {
val interval: Pair<Int, Int>? = intervalDisplay()
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f)
Row(modifier = modifier.padding(start = 16.dp, top = 8.dp, end = 16.dp)) {
@ -191,13 +191,14 @@ fun MangaActionRow(
onClick = onAddToLibraryClicked,
onLongClick = onEditCategory,
)
if (onEditIntervalClicked != null && interval != null) {
if (onEditIntervalClicked != null && fetchInterval != null) {
val intervalPair = 1.coerceAtLeast(fetchInterval.interval - fetchInterval.leadDays) to (fetchInterval.interval + fetchInterval.followDays)
MangaActionButton(
title =
if (interval.first == interval.second) {
pluralStringResource(id = R.plurals.day, count = interval.second, interval.second)
if (intervalPair.first == intervalPair.second) {
pluralStringResource(id = R.plurals.day, count = intervalPair.second, intervalPair.second)
} else {
pluralStringResource(id = R.plurals.range_interval_day, count = interval.second, interval.first, interval.second)
pluralStringResource(id = R.plurals.range_interval_day, count = intervalPair.second, intervalPair.first, intervalPair.second)
},
icon = Icons.Default.HourglassEmpty,
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,

View file

@ -235,6 +235,7 @@ object SettingsAdvancedScreen : SearchableSettings {
onClick = {
Injekt.get<MangaDownloadCache>().invalidateCache()
Injekt.get<AnimeDownloadCache>().invalidateCache()
context.toast(R.string.download_cache_invalidated)
},
),
Preference.PreferenceItem.TextPreference(

View file

@ -5,11 +5,16 @@ import android.os.Build
import androidx.core.content.ContextCompat
import androidx.sqlite.db.SupportSQLiteDatabase
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
import com.squareup.sqldelight.android.AndroidSqliteDriver
import app.cash.sqldelight.adapter.primitive.FloatColumnAdapter
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
import data.Chapters
import data.History
import data.Manga_sync
import data.Mangas
import dataanime.Anime_sync
import dataanime.Animehistory
import dataanime.Animes
import dataanime.Episodes
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.track.anime.store.DelayedAnimeTrackingStore
@ -131,9 +136,15 @@ class AppModule(val app: Application) : InjektModule {
addSingletonFactory {
Database(
driver = sqlDriverManga,
chaptersAdapter = Chapters.Adapter(
chapter_numberAdapter = FloatColumnAdapter,
),
historyAdapter = History.Adapter(
last_readAdapter = dateAdapter,
),
manga_syncAdapter = Manga_sync.Adapter(
scoreAdapter = FloatColumnAdapter,
),
mangasAdapter = Mangas.Adapter(
genreAdapter = listOfStringsAdapter,
update_strategyAdapter = updateStrategyAdapter,
@ -144,9 +155,15 @@ class AppModule(val app: Application) : InjektModule {
addSingletonFactory {
AnimeDatabase(
driver = sqlDriverAnime,
episodesAdapter = Episodes.Adapter(
episode_numberAdapter = FloatColumnAdapter,
),
animehistoryAdapter = Animehistory.Adapter(
last_seenAdapter = dateAdapter,
),
anime_syncAdapter = Anime_sync.Adapter(
scoreAdapter = FloatColumnAdapter,
),
animesAdapter = Animes.Adapter(
genreAdapter = listOfStringsAdapter,
update_strategyAdapter = updateStrategyAdapter,

View file

@ -511,7 +511,7 @@ class BackupManager(
}
if (!found) {
// Let the db assign the id
val id = mangaHandler.awaitOne {
val id = mangaHandler.awaitOneExecutable {
categoriesQueries.insert(category.name, category.order, category.flags)
categoriesQueries.selectLastInsertedRowId()
}
@ -551,7 +551,7 @@ class BackupManager(
}
if (!found) {
// Let the db assign the id
val id = animeHandler.awaitOne {
val id = animeHandler.awaitOneExecutable {
categoriesQueries.insert(category.name, category.order, category.flags)
categoriesQueries.selectLastInsertedRowId()
}
@ -970,7 +970,7 @@ class BackupManager(
* @return id of [Manga], null if not found
*/
private suspend fun insertManga(manga: Manga): Long {
return mangaHandler.awaitOne(true) {
return mangaHandler.awaitOneExecutable(true) {
mangasQueries.insert(
source = manga.source,
url = manga.url,
@ -1002,7 +1002,7 @@ class BackupManager(
* @return id of [Anime], null if not found
*/
private suspend fun insertAnime(anime: Anime): Long {
return animeHandler.awaitOne(true) {
return animeHandler.awaitOneExecutable(true) {
animesQueries.insert(
source = anime.source,
url = anime.url,
@ -1040,11 +1040,11 @@ class BackupManager(
title = manga.title,
status = manga.status,
thumbnailUrl = manga.thumbnailUrl,
favorite = manga.favorite.toLong(),
favorite = manga.favorite,
lastUpdate = manga.lastUpdate,
nextUpdate = null,
calculateInterval = null,
initialized = manga.initialized.toLong(),
initialized = manga.initialized,
viewer = manga.viewerFlags,
chapterFlags = manga.chapterFlags,
coverLastModified = manga.coverLastModified,
@ -1068,11 +1068,11 @@ class BackupManager(
title = anime.title,
status = anime.status,
thumbnailUrl = anime.thumbnailUrl,
favorite = anime.favorite.toLong(),
favorite = anime.favorite,
lastUpdate = anime.lastUpdate,
nextUpdate = null,
calculateInterval = null,
initialized = anime.initialized.toLong(),
initialized = anime.initialized,
viewer = anime.viewerFlags,
episodeFlags = anime.episodeFlags,
coverLastModified = anime.coverLastModified,
@ -1142,8 +1142,8 @@ class BackupManager(
url = null,
name = null,
scanlator = null,
read = chapter.read.toLong(),
bookmark = chapter.bookmark.toLong(),
read = chapter.read,
bookmark = chapter.bookmark,
lastPageRead = chapter.lastPageRead,
chapterNumber = null,
sourceOrder = null,
@ -1166,8 +1166,8 @@ class BackupManager(
url = null,
name = null,
scanlator = null,
seen = episode.seen.toLong(),
bookmark = episode.bookmark.toLong(),
seen = episode.seen,
bookmark = episode.bookmark,
lastSecondSeen = episode.lastSecondSeen,
totalSeconds = episode.totalSeconds,
episodeNumber = null,

View file

@ -30,9 +30,9 @@ import eu.kanade.tachiyomi.util.system.createFileInCacheDir
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.isActive
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.entries.anime.interactor.SetAnimeUpdateInterval
import tachiyomi.domain.entries.anime.interactor.SetAnimeFetchInterval
import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.entries.manga.interactor.SetMangaUpdateInterval
import tachiyomi.domain.entries.manga.interactor.SetMangaFetchInterval
import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.items.chapter.model.Chapter
import tachiyomi.domain.items.chapter.repository.ChapterRepository
@ -54,15 +54,15 @@ class BackupRestorer(
) {
private val updateManga: UpdateManga = Injekt.get()
private val chapterRepository: ChapterRepository = Injekt.get()
private val setMangaUpdateInterval: SetMangaUpdateInterval = Injekt.get()
private val setMangaFetchInterval: SetMangaFetchInterval = Injekt.get()
private val updateAnime: UpdateAnime = Injekt.get()
private val episodeRepository: EpisodeRepository = Injekt.get()
private val setAnimeUpdateInterval: SetAnimeUpdateInterval = Injekt.get()
private val setAnimeFetchInterval: SetAnimeFetchInterval = Injekt.get()
private var zonedDateTime = ZonedDateTime.now()
private var currentMangaRange = setMangaUpdateInterval.getCurrentFetchRange(zonedDateTime)
private var currentAnimeRange = setAnimeUpdateInterval.getCurrentFetchRange(zonedDateTime)
private var currentMangaFetchInterval = setMangaFetchInterval.getCurrent(zonedDateTime)
private var currentAnimeFetchInterval = setAnimeFetchInterval.getCurrent(zonedDateTime)
private var backupManager = BackupManager(context)
@ -141,8 +141,8 @@ class BackupRestorer(
animeSourceMapping = backupAnimeMaps.associate { it.sourceId to it.name }
zonedDateTime = ZonedDateTime.now()
currentMangaRange = setMangaUpdateInterval.getCurrentFetchRange(zonedDateTime)
currentAnimeRange = setAnimeUpdateInterval.getCurrentFetchRange(zonedDateTime)
currentMangaFetchInterval = setMangaFetchInterval.getCurrent(zonedDateTime)
currentAnimeFetchInterval = setAnimeFetchInterval.getCurrent(zonedDateTime)
return coroutineScope {
// Restore individual manga
@ -217,7 +217,7 @@ class BackupRestorer(
restoreNewManga(updateManga, chapters, categories, history, tracks, backupCategories)
}
val updatedChapters = chapterRepository.getChapterByMangaId(restoredManga.id)
updateManga.awaitUpdateFetchInterval(restoredManga, updatedChapters, zonedDateTime, currentMangaRange)
updateManga.awaitUpdateFetchInterval(restoredManga, updatedChapters, zonedDateTime, currentMangaFetchInterval)
} catch (e: Exception) {
val sourceName = sourceMapping[manga.source] ?: manga.source.toString()
errors.add(Date() to "${manga.title} [$sourceName]: ${e.message}")
@ -292,7 +292,7 @@ class BackupRestorer(
restoreNewAnime(updateAnime, episodes, categories, history, tracks, backupCategories)
}
val updatedEpisodes = episodeRepository.getEpisodeByAnimeId(restoredAnime.id)
updateAnime.awaitUpdateFetchInterval(restoredAnime, updatedEpisodes, zonedDateTime, currentAnimeRange)
updateAnime.awaitUpdateFetchInterval(restoredAnime, updatedEpisodes, zonedDateTime, currentAnimeFetchInterval)
} catch (e: Exception) {
val sourceName = sourceMapping[anime.source] ?: anime.source.toString()
errors.add(Date() to "${anime.title} [$sourceName]: ${e.message}")

View file

@ -56,7 +56,7 @@ import tachiyomi.domain.category.model.Category
import tachiyomi.domain.download.service.DownloadPreferences
import tachiyomi.domain.entries.anime.interactor.GetAnime
import tachiyomi.domain.entries.anime.interactor.GetLibraryAnime
import tachiyomi.domain.entries.anime.interactor.SetAnimeUpdateInterval
import tachiyomi.domain.entries.anime.interactor.SetAnimeFetchInterval
import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.entries.anime.model.toAnimeUpdate
import tachiyomi.domain.items.episode.interactor.GetEpisodeByAnimeId
@ -104,7 +104,7 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
private val getTracks: GetAnimeTracks = Injekt.get()
private val insertTrack: InsertAnimeTrack = Injekt.get()
private val syncEpisodesWithTrackServiceTwoWay: SyncEpisodesWithTrackServiceTwoWay = Injekt.get()
private val setAnimeUpdateInterval: SetAnimeUpdateInterval = Injekt.get()
private val setAnimeFetchInterval: SetAnimeFetchInterval = Injekt.get()
private val notifier = AnimeLibraryUpdateNotifier(context)
@ -232,8 +232,8 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
val restrictions = libraryPreferences.libraryUpdateItemRestriction().get()
val now = ZonedDateTime.now()
val fetchRange = setAnimeUpdateInterval.getCurrentFetchRange(now)
val higherLimit = fetchRange.second
val fetchInterval = setAnimeFetchInterval.getCurrent(now)
val higherLimit = fetchInterval.second
coroutineScope {
animeToUpdate.groupBy { it.anime.source }.values
@ -272,7 +272,7 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
else -> {
try {
val newEpisodes = updateAnime(anime, now, fetchRange)
val newEpisodes = updateAnime(anime, now, fetchInterval)
.sortedByDescending { it.sourceOrder }
if (newEpisodes.isNotEmpty()) {

View file

@ -56,7 +56,7 @@ import tachiyomi.domain.category.model.Category
import tachiyomi.domain.download.service.DownloadPreferences
import tachiyomi.domain.entries.manga.interactor.GetLibraryManga
import tachiyomi.domain.entries.manga.interactor.GetManga
import tachiyomi.domain.entries.manga.interactor.SetMangaUpdateInterval
import tachiyomi.domain.entries.manga.interactor.SetMangaFetchInterval
import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.entries.manga.model.toMangaUpdate
import tachiyomi.domain.items.chapter.interactor.GetChapterByMangaId
@ -104,7 +104,7 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
private val getTracks: GetMangaTracks = Injekt.get()
private val insertTrack: InsertMangaTrack = Injekt.get()
private val syncChaptersWithTrackServiceTwoWay: SyncChaptersWithTrackServiceTwoWay = Injekt.get()
private val setMangaUpdateInterval: SetMangaUpdateInterval = Injekt.get()
private val setMangaFetchInterval: SetMangaFetchInterval = Injekt.get()
private val notifier = MangaLibraryUpdateNotifier(context)
@ -232,8 +232,8 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
val restrictions = libraryPreferences.libraryUpdateItemRestriction().get()
val now = ZonedDateTime.now()
val fetchRange = setMangaUpdateInterval.getCurrentFetchRange(now)
val higherLimit = fetchRange.second
val fetchInterval = setMangaFetchInterval.getCurrent(now)
val higherLimit = fetchInterval.second
coroutineScope {
mangaToUpdate.groupBy { it.manga.source }.values
@ -272,7 +272,7 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
else -> {
try {
val newChapters = updateManga(manga, now, fetchRange)
val newChapters = updateManga(manga, now, fetchInterval)
.sortedByDescending { it.sourceOrder }
if (newChapters.isNotEmpty()) {

View file

@ -121,8 +121,8 @@ internal fun MigrateAnimeDialog(
) {
TextButton(
onClick = {
onClickTitle()
onDismissRequest()
onClickTitle()
},
) {
Text(text = stringResource(R.string.action_show_anime))

View file

@ -329,7 +329,7 @@ class BrowseAnimeSourceScreenModel(
}
suspend fun getDuplicateAnimelibAnime(anime: Anime): Anime? {
return getDuplicateAnimelibAnime.await(anime.title)
return getDuplicateAnimelibAnime.await(anime).getOrNull(0)
}
private fun moveAnimeToCategories(anime: Anime, vararg categories: Category) {

View file

@ -121,8 +121,8 @@ internal fun MigrateMangaDialog(
) {
TextButton(
onClick = {
onClickTitle()
onDismissRequest()
onClickTitle()
},
) {
Text(text = stringResource(R.string.action_show_manga))

View file

@ -323,7 +323,7 @@ class BrowseMangaSourceScreenModel(
}
suspend fun getDuplicateLibraryManga(manga: Manga): Manga? {
return getDuplicateLibraryManga.await(manga.title)
return getDuplicateLibraryManga.await(manga).getOrNull(0)
}
private fun moveMangaToCategories(manga: Manga, vararg categories: Category) {

View file

@ -89,6 +89,13 @@ class AnimeScreen(
val successState = state as AnimeScreenModel.State.Success
val isAnimeHttpSource = remember { successState.source is AnimeHttpSource }
val fetchInterval = remember(successState.anime.fetchInterval) {
FetchAnimeInterval(
interval = successState.anime.fetchInterval,
leadDays = screenModel.leadDay,
followDays = screenModel.followDay,
)
}
LaunchedEffect(successState.anime, screenModel.source) {
if (isAnimeHttpSource) {
@ -106,7 +113,7 @@ class AnimeScreen(
state = successState,
snackbarHostState = screenModel.snackbarHostState,
dateFormat = screenModel.dateFormat,
intervalDisplay = screenModel::intervalDisplay,
fetchInterval = fetchInterval,
isTabletUi = isTabletUi(),
episodeSwipeStartAction = screenModel.episodeSwipeStartAction,
episodeSwipeEndAction = screenModel.episodeSwipeEndAction,
@ -141,7 +148,7 @@ class AnimeScreen(
onShareClicked = { shareAnime(context, screenModel.anime, screenModel.source) }.takeIf { isAnimeHttpSource },
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
onEditCategoryClicked = screenModel::showChangeCategoryDialog.takeIf { successState.anime.favorite },
onEditIntervalClicked = screenModel::showSetAnimeIntervalDialog.takeIf { screenModel.isIntervalEnabled && successState.anime.favorite },
onEditFetchIntervalClicked = screenModel::showSetAnimeFetchIntervalDialog.takeIf { screenModel.isUpdateIntervalEnabled && successState.anime.favorite },
onMigrateClicked = { navigator.push(MigrateAnimeSearchScreen(successState.anime.id)) }.takeIf { successState.anime.favorite },
changeAnimeSkipIntro = screenModel::showAnimeSkipIntroDialog.takeIf { successState.anime.favorite },
onMultiBookmarkClicked = screenModel::bookmarkEpisodes,
@ -235,11 +242,11 @@ class AnimeScreen(
LoadingScreen(Modifier.systemBarsPadding())
}
}
is AnimeScreenModel.Dialog.SetAnimeInterval -> {
is AnimeScreenModel.Dialog.SetAnimeFetchInterval -> {
SetIntervalDialog(
interval = if (dialog.anime.calculateInterval < 0) -dialog.anime.calculateInterval else 0,
interval = if (dialog.anime.fetchInterval < 0) -dialog.anime.fetchInterval else 0,
onDismissRequest = onDismissRequest,
onValueChanged = { screenModel.setFetchRangeInterval(dialog.anime, it) },
onValueChanged = { screenModel.setFetchInterval(dialog.anime, it) },
)
}
AnimeScreenModel.Dialog.ChangeAnimeSkipIntro -> {

View file

@ -79,7 +79,6 @@ import tachiyomi.source.local.entries.anime.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.Calendar
import kotlin.math.absoluteValue
class AnimeScreenModel(
val context: Context,
@ -135,9 +134,9 @@ class AnimeScreenModel(
val dateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get()))
val isIntervalEnabled = LibraryPreferences.ENTRY_OUTSIDE_RELEASE_PERIOD in libraryPreferences.libraryUpdateItemRestriction().get()
private val leadDay = libraryPreferences.leadingAnimeExpectedDays().get()
private val followDay = libraryPreferences.followingAnimeExpectedDays().get()
val isUpdateIntervalEnabled = LibraryPreferences.ENTRY_OUTSIDE_RELEASE_PERIOD in libraryPreferences.libraryUpdateItemRestriction().get()
val leadDay = libraryPreferences.leadingAnimeExpectedDays().get()
val followDay = libraryPreferences.followingAnimeExpectedDays().get()
private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list
private val selectedEpisodeIds: HashSet<Long> = HashSet()
@ -296,7 +295,7 @@ class AnimeScreenModel(
// Add to library
// First, check if duplicate exists if callback is provided
if (checkDuplicate) {
val duplicate = getDuplicateLibraryAnime.await(anime.title)
val duplicate = getDuplicateLibraryAnime.await(anime).getOrNull(0)
if (duplicate != null) {
updateSuccessState { it.copy(dialog = Dialog.DuplicateAnime(anime, duplicate)) }
return@launchIO
@ -371,30 +370,23 @@ class AnimeScreenModel(
}
}
fun showSetAnimeIntervalDialog() {
fun showSetAnimeFetchIntervalDialog() {
val anime = successState?.anime ?: return
updateSuccessState {
it.copy(dialog = Dialog.SetAnimeInterval(anime))
it.copy(dialog = Dialog.SetAnimeFetchInterval(anime))
}
}
// TODO: this should be in the state/composables
fun intervalDisplay(): Pair<Int, Int>? {
val anime = successState?.anime ?: return null
val effInterval = anime.calculateInterval
return 1.coerceAtLeast(effInterval.absoluteValue - leadDay) to (effInterval.absoluteValue + followDay)
}
fun setFetchRangeInterval(anime: Anime, newInterval: Int) {
fun setFetchInterval(anime: Anime, newInterval: Int) {
val interval = when (newInterval) {
// reset interval 0 default to trigger recalculation
// only reset if interval is custom, which is negative
0 -> if (anime.calculateInterval < 0) 0 else anime.calculateInterval
0 -> if (anime.fetchInterval < 0) 0 else anime.fetchInterval
else -> -newInterval
}
coroutineScope.launchIO {
updateAnime.awaitUpdateFetchInterval(
anime.copy(calculateInterval = interval),
anime.copy(fetchInterval = interval),
successState?.episodes?.map { it.episode }.orEmpty(),
)
val newAnime = animeRepository.getAnimeById(animeId)
@ -1011,7 +1003,7 @@ class AnimeScreenModel(
data class ChangeCategory(val anime: Anime, val initialSelection: List<CheckboxState<Category>>) : Dialog
data class DeleteEpisodes(val episodes: List<Episode>) : Dialog
data class DuplicateAnime(val anime: Anime, val duplicate: Anime) : Dialog
data class SetAnimeInterval(val anime: Anime) : Dialog
data class SetAnimeFetchInterval(val anime: Anime) : Dialog
data class ShowQualities(val episode: Episode, val anime: Anime, val source: AnimeSource) : Dialog
data object ChangeAnimeSkipIntro : Dialog
data object SettingsSheet : Dialog
@ -1113,3 +1105,10 @@ data class EpisodeItem(
) {
val isDownloaded = downloadState == AnimeDownload.State.DOWNLOADED
}
@Immutable
data class FetchAnimeInterval(
val interval: Int,
val leadDays: Int,
val followDays: Int,
)

View file

@ -84,6 +84,13 @@ class MangaScreen(
val successState = state as MangaScreenModel.State.Success
val isHttpSource = remember { successState.source is HttpSource }
val fetchInterval = remember(successState.manga.fetchInterval) {
FetchMangaInterval(
interval = successState.manga.fetchInterval,
leadDays = screenModel.leadDay,
followDays = screenModel.followDay,
)
}
LaunchedEffect(successState.manga, screenModel.source) {
if (isHttpSource) {
@ -101,7 +108,7 @@ class MangaScreen(
state = successState,
snackbarHostState = screenModel.snackbarHostState,
dateFormat = screenModel.dateFormat,
intervalDisplay = screenModel::intervalDisplay,
fetchInterval = fetchInterval,
isTabletUi = isTabletUi(),
chapterSwipeStartAction = screenModel.chapterSwipeStartAction,
chapterSwipeEndAction = screenModel.chapterSwipeEndAction,
@ -124,7 +131,7 @@ class MangaScreen(
onShareClicked = { shareManga(context, screenModel.manga, screenModel.source) }.takeIf { isHttpSource },
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
onEditCategoryClicked = screenModel::showChangeCategoryDialog.takeIf { successState.manga.favorite },
onEditIntervalClicked = screenModel::showSetMangaIntervalDialog.takeIf { screenModel.isIntervalEnabled && successState.manga.favorite },
onEditFetchIntervalClicked = screenModel::showSetMangaFetchIntervalDialog.takeIf { screenModel.isUpdateIntervalEnabled && successState.manga.favorite },
onMigrateClicked = { navigator.push(MigrateSearchScreen(successState.manga.id)) }.takeIf { successState.manga.favorite },
onMultiBookmarkClicked = screenModel::bookmarkChapters,
onMultiMarkAsReadClicked = screenModel::markChaptersRead,
@ -217,11 +224,11 @@ class MangaScreen(
LoadingScreen(Modifier.systemBarsPadding())
}
}
is MangaScreenModel.Dialog.SetMangaInterval -> {
is MangaScreenModel.Dialog.SetMangaFetchInterval -> {
SetIntervalDialog(
interval = if (dialog.manga.calculateInterval < 0) -dialog.manga.calculateInterval else 0,
interval = if (dialog.manga.fetchInterval < 0) -dialog.manga.fetchInterval else 0,
onDismissRequest = onDismissRequest,
onValueChanged = { screenModel.setFetchRangeInterval(dialog.manga, it) },
onValueChanged = { screenModel.setFetchInterval(dialog.manga, it) },
)
}
}

View file

@ -76,7 +76,6 @@ import tachiyomi.domain.track.manga.interactor.GetMangaTracks
import tachiyomi.source.local.entries.manga.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.math.absoluteValue
class MangaScreenModel(
val context: Context,
@ -131,9 +130,9 @@ class MangaScreenModel(
val dateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get()))
val skipFiltered by readerPreferences.skipFiltered().asState(coroutineScope)
val isIntervalEnabled = LibraryPreferences.ENTRY_OUTSIDE_RELEASE_PERIOD in libraryPreferences.libraryUpdateItemRestriction().get()
private val leadDay = libraryPreferences.leadingMangaExpectedDays().get()
private val followDay = libraryPreferences.followingMangaExpectedDays().get()
val isUpdateIntervalEnabled = LibraryPreferences.ENTRY_OUTSIDE_RELEASE_PERIOD in libraryPreferences.libraryUpdateItemRestriction().get()
val leadDay = libraryPreferences.leadingMangaExpectedDays().get()
val followDay = libraryPreferences.followingMangaExpectedDays().get()
private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list
private val selectedChapterIds: HashSet<Long> = HashSet()
@ -292,7 +291,7 @@ class MangaScreenModel(
// Add to library
// First, check if duplicate exists if callback is provided
if (checkDuplicate) {
val duplicate = getDuplicateLibraryManga.await(manga.title)
val duplicate = getDuplicateLibraryManga.await(manga).getOrNull(0)
if (duplicate != null) {
updateSuccessState { it.copy(dialog = Dialog.DuplicateManga(manga, duplicate)) }
@ -368,30 +367,23 @@ class MangaScreenModel(
}
}
fun showSetMangaIntervalDialog() {
fun showSetMangaFetchIntervalDialog() {
val manga = successState?.manga ?: return
updateSuccessState {
it.copy(dialog = Dialog.SetMangaInterval(manga))
it.copy(dialog = Dialog.SetMangaFetchInterval(manga))
}
}
// TODO: this should be in the state/composables
fun intervalDisplay(): Pair<Int, Int>? {
val manga = successState?.manga ?: return null
val effInterval = manga.calculateInterval
return 1.coerceAtLeast(effInterval.absoluteValue - leadDay) to (effInterval.absoluteValue + followDay)
}
fun setFetchRangeInterval(manga: Manga, newInterval: Int) {
fun setFetchInterval(manga: Manga, newInterval: Int) {
val interval = when (newInterval) {
// reset interval 0 default to trigger recalculation
// only reset if interval is custom, which is negative
0 -> if (manga.calculateInterval < 0) 0 else manga.calculateInterval
0 -> if (manga.fetchInterval < 0) 0 else manga.fetchInterval
else -> -newInterval
}
coroutineScope.launchIO {
updateManga.awaitUpdateFetchInterval(
manga.copy(calculateInterval = interval),
manga.copy(fetchInterval = interval),
successState?.chapters?.map { it.chapter }.orEmpty(),
)
val newManga = mangaRepository.getMangaById(mangaId)
@ -996,7 +988,7 @@ class MangaScreenModel(
data class ChangeCategory(val manga: Manga, val initialSelection: List<CheckboxState<Category>>) : Dialog
data class DeleteChapters(val chapters: List<Chapter>) : Dialog
data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
data class SetMangaInterval(val manga: Manga) : Dialog
data class SetMangaFetchInterval(val manga: Manga) : Dialog
data object SettingsSheet : Dialog
data object TrackSheet : Dialog
data object FullCover : Dialog
@ -1081,3 +1073,10 @@ data class ChapterItem(
) {
val isDownloaded = downloadState == MangaDownload.State.DOWNLOADED
}
@Immutable
data class FetchMangaInterval(
val interval: Int,
val leadDays: Int,
val followDays: Int,
)

View file

@ -85,7 +85,8 @@ class AnimeUpdatesScreenModel(
combine(
getUpdates.subscribe(calendar).distinctUntilChanged(),
downloadCache.changes,
) { updates, _ -> updates }
downloadManager.queueState,
) { updates, _, _ -> updates }
.catch {
logcat(LogPriority.ERROR, it)
_events.send(Event.InternalError)

View file

@ -81,7 +81,8 @@ class MangaUpdatesScreenModel(
combine(
getUpdates.subscribe(calendar).distinctUntilChanged(),
downloadCache.changes,
) { updates, _ -> updates }
downloadManager.queueState,
) { updates, _, _ -> updates }
.catch {
logcat(LogPriority.ERROR, it)
_events.send(Event.InternalError)

View file

@ -120,7 +120,7 @@ fun Date.toRelativeString(
val difference = now.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY) - this.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY)
val days = difference.floorDiv(MILLISECONDS_IN_DAY).toInt()
return when {
difference < 0 -> context.getString(R.string.recently)
difference < 0 -> dateFormat.format(this)
difference < MILLISECONDS_IN_DAY -> context.getString(R.string.relative_time_today)
difference < MILLISECONDS_IN_DAY.times(7) -> context.resources.getQuantityString(
R.plurals.relative_time,

View file

@ -1,8 +1,10 @@
import org.jetbrains.kotlin.cli.jvm.main
plugins {
id("com.android.library")
kotlin("android")
kotlin("plugin.serialization")
id("com.squareup.sqldelight")
id("app.cash.sqldelight")
}
android {
@ -13,17 +15,19 @@ android {
}
sqldelight {
database("Database") {
packageName = "tachiyomi.data"
dialect = "sqlite:3.24"
schemaOutputDirectory = project.file("./src/main/sqldelight")
sourceFolders = listOf("sqldelight")
}
database("AnimeDatabase") {
packageName = "tachiyomi.mi.data"
dialect = "sqlite:3.24"
schemaOutputDirectory = project.file("./src/main/sqldelightanime")
sourceFolders = listOf("sqldelightanime")
databases {
create("Database") {
packageName.set("tachiyomi.data")
dialect(libs.sqldelight.dialects.sql)
schemaOutputDirectory.set(project.file("./src/main/sqldelight"))
srcDirs.from(project.file("./src/main/sqldelight"))
}
create("AnimeDatabase") {
packageName.set("tachiyomi.mi.data")
dialect(libs.sqldelight.dialects.sql)
schemaOutputDirectory.set(project.file("./src/main/sqldelightanime"))
srcDirs.from(project.file("./src/main/sqldelightanime"))
}
}
}
}
@ -33,9 +37,7 @@ dependencies {
implementation(project(":domain"))
implementation(project(":core"))
api(libs.sqldelight.android.driver)
api(libs.sqldelight.coroutines)
api(libs.sqldelight.android.paging)
api(libs.bundles.sqldelight)
}
tasks {

View file

@ -1,6 +1,6 @@
package tachiyomi.data
import com.squareup.sqldelight.ColumnAdapter
import app.cash.sqldelight.ColumnAdapter
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import java.util.Date

View file

@ -12,7 +12,7 @@ val animeMapper: (Long, Long, String, String?, String?, String?, List<String>?,
favorite = favorite,
lastUpdate = lastUpdate ?: 0,
nextUpdate = nextUpdate ?: 0,
calculateInterval = calculateInterval.toInt(),
fetchInterval = calculateInterval.toInt(),
dateAdded = dateAdded,
viewerFlags = viewerFlags,
episodeFlags = episodeFlags,
@ -32,7 +32,7 @@ val animeMapper: (Long, Long, String, String?, String?, String?, List<String>?,
)
}
val libraryAnime: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?, Long, Long, Long, Long, Long, Long, Long) -> LibraryAnime =
val libraryAnime: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?, Long, Double, Long, Long, Long, Double, Long) -> LibraryAnime =
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, episodeFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt, totalCount, seenCount, latestUpload, episodeFetchedAt, lastSeen, bookmarkCount, category ->
LibraryAnime(
anime = animeMapper(
@ -61,8 +61,8 @@ val libraryAnime: (Long, Long, String, String?, String?, String?, List<String>?,
),
category = category,
totalEpisodes = totalCount,
seenCount = seenCount,
bookmarkCount = bookmarkCount,
seenCount = seenCount.toLong(),
bookmarkCount = bookmarkCount.toLong(),
latestUpload = latestUpload,
episodeFetchedAt = episodeFetchedAt,
lastSeen = lastSeen,

View file

@ -48,9 +48,9 @@ class AnimeRepositoryImpl(
return handler.subscribeToList { animesQueries.getFavoriteBySourceId(sourceId, animeMapper) }
}
override suspend fun getDuplicateLibraryAnime(title: String): Anime? {
return handler.awaitOneOrNull {
animesQueries.getDuplicateLibraryAnime(title, animeMapper)
override suspend fun getDuplicateLibraryAnime(id: Long, title: String): List<Anime> {
return handler.awaitList {
animesQueries.getDuplicateLibraryAnime(title, id, animeMapper)
}
}
@ -74,7 +74,7 @@ class AnimeRepositoryImpl(
}
override suspend fun insertAnime(anime: Anime): Long? {
return handler.awaitOneOrNull(inTransaction = true) {
return handler.awaitOneOrNullExecutable(inTransaction = true) {
animesQueries.insert(
source = anime.source,
url = anime.url,
@ -88,7 +88,7 @@ class AnimeRepositoryImpl(
favorite = anime.favorite,
lastUpdate = anime.lastUpdate,
nextUpdate = anime.nextUpdate,
calculateInterval = anime.calculateInterval.toLong(),
calculateInterval = anime.fetchInterval.toLong(),
initialized = anime.initialized,
viewerFlags = anime.viewerFlags,
episodeFlags = anime.episodeFlags,
@ -133,11 +133,11 @@ class AnimeRepositoryImpl(
title = value.title,
status = value.status,
thumbnailUrl = value.thumbnailUrl,
favorite = value.favorite?.toLong(),
favorite = value.favorite,
lastUpdate = value.lastUpdate,
nextUpdate = value.nextUpdate,
calculateInterval = value.calculateInterval?.toLong(),
initialized = value.initialized?.toLong(),
calculateInterval = value.fetchInterval?.toLong(),
initialized = value.initialized,
viewer = value.viewerFlags,
episodeFlags = value.episodeFlags,
coverLastModified = value.coverLastModified,

View file

@ -12,7 +12,7 @@ val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?,
favorite = favorite,
lastUpdate = lastUpdate ?: 0,
nextUpdate = nextUpdate ?: 0,
calculateInterval = calculateInterval.toInt(),
fetchInterval = calculateInterval.toInt(),
dateAdded = dateAdded,
viewerFlags = viewerFlags,
chapterFlags = chapterFlags,
@ -32,7 +32,7 @@ val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?,
)
}
val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?, Long, Long, Long, Long, Long, Long, Long) -> LibraryManga =
val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?, Long, Double, Long, Long, Long, Double, Long) -> LibraryManga =
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt, totalCount, readCount, latestUpload, chapterFetchedAt, lastRead, bookmarkCount, category ->
LibraryManga(
manga = mangaMapper(
@ -61,8 +61,8 @@ val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?,
),
category = category,
totalChapters = totalCount,
readCount = readCount,
bookmarkCount = bookmarkCount,
readCount = readCount.toLong(),
bookmarkCount = bookmarkCount.toLong(),
latestUpload = latestUpload,
chapterFetchedAt = chapterFetchedAt,
lastRead = lastRead,

View file

@ -48,9 +48,9 @@ class MangaRepositoryImpl(
return handler.subscribeToList { mangasQueries.getFavoriteBySourceId(sourceId, mangaMapper) }
}
override suspend fun getDuplicateLibraryManga(title: String): Manga? {
return handler.awaitOneOrNull {
mangasQueries.getDuplicateLibraryManga(title, mangaMapper)
override suspend fun getDuplicateLibraryManga(id: Long, title: String): List<Manga> {
return handler.awaitList {
mangasQueries.getDuplicateLibraryManga(title, id, mangaMapper)
}
}
@ -74,7 +74,7 @@ class MangaRepositoryImpl(
}
override suspend fun insertManga(manga: Manga): Long? {
return handler.awaitOneOrNull(inTransaction = true) {
return handler.awaitOneOrNullExecutable(inTransaction = true) {
mangasQueries.insert(
source = manga.source,
url = manga.url,
@ -88,7 +88,7 @@ class MangaRepositoryImpl(
favorite = manga.favorite,
lastUpdate = manga.lastUpdate,
nextUpdate = manga.nextUpdate,
calculateInterval = manga.calculateInterval.toLong(),
calculateInterval = manga.fetchInterval.toLong(),
initialized = manga.initialized,
viewerFlags = manga.viewerFlags,
chapterFlags = manga.chapterFlags,
@ -133,11 +133,11 @@ class MangaRepositoryImpl(
title = value.title,
status = value.status,
thumbnailUrl = value.thumbnailUrl,
favorite = value.favorite?.toLong(),
favorite = value.favorite,
lastUpdate = value.lastUpdate,
nextUpdate = value.nextUpdate,
calculateInterval = value.calculateInterval?.toLong(),
initialized = value.initialized?.toLong(),
calculateInterval = value.fetchInterval?.toLong(),
initialized = value.initialized,
viewer = value.viewerFlags,
chapterFlags = value.chapterFlags,
coverLastModified = value.coverLastModified,

View file

@ -1,12 +1,13 @@
package tachiyomi.data.handlers.anime
import androidx.paging.PagingSource
import com.squareup.sqldelight.Query
import com.squareup.sqldelight.db.SqlDriver
import com.squareup.sqldelight.runtime.coroutines.asFlow
import com.squareup.sqldelight.runtime.coroutines.mapToList
import com.squareup.sqldelight.runtime.coroutines.mapToOne
import com.squareup.sqldelight.runtime.coroutines.mapToOneOrNull
import app.cash.sqldelight.ExecutableQuery
import app.cash.sqldelight.Query
import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToList
import app.cash.sqldelight.coroutines.mapToOne
import app.cash.sqldelight.coroutines.mapToOneOrNull
import app.cash.sqldelight.db.SqlDriver
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
@ -40,6 +41,13 @@ class AndroidAnimeDatabaseHandler(
return dispatch(inTransaction) { block(db).executeAsOne() }
}
override suspend fun <T : Any> awaitOneExecutable(
inTransaction: Boolean,
block: suspend AnimeDatabase.() -> ExecutableQuery<T>,
): T {
return dispatch(inTransaction) { block(db).executeAsOne() }
}
override suspend fun <T : Any> awaitOneOrNull(
inTransaction: Boolean,
block: suspend AnimeDatabase.() -> Query<T>,
@ -47,6 +55,13 @@ class AndroidAnimeDatabaseHandler(
return dispatch(inTransaction) { block(db).executeAsOneOrNull() }
}
override suspend fun <T : Any> awaitOneOrNullExecutable(
inTransaction: Boolean,
block: suspend AnimeDatabase.() -> ExecutableQuery<T>,
): T? {
return dispatch(inTransaction) { block(db).executeAsOneOrNull() }
}
override fun <T : Any> subscribeToList(block: AnimeDatabase.() -> Query<T>): Flow<List<T>> {
return block(db).asFlow().mapToList(queryDispatcher)
}

View file

@ -1,7 +1,8 @@
package tachiyomi.data.handlers.anime
import androidx.paging.PagingSource
import com.squareup.sqldelight.Query
import app.cash.sqldelight.ExecutableQuery
import app.cash.sqldelight.Query
import kotlinx.coroutines.flow.Flow
import tachiyomi.mi.data.AnimeDatabase
@ -19,11 +20,21 @@ interface AnimeDatabaseHandler {
block: suspend AnimeDatabase.() -> Query<T>,
): T
suspend fun <T : Any> awaitOneExecutable(
inTransaction: Boolean = false,
block: suspend AnimeDatabase.() -> ExecutableQuery<T>,
): T
suspend fun <T : Any> awaitOneOrNull(
inTransaction: Boolean = false,
block: suspend AnimeDatabase.() -> Query<T>,
): T?
suspend fun <T : Any> awaitOneOrNullExecutable(
inTransaction: Boolean = false,
block: suspend AnimeDatabase.() -> ExecutableQuery<T>,
): T?
fun <T : Any> subscribeToList(block: AnimeDatabase.() -> Query<T>): Flow<List<T>>
fun <T : Any> subscribeToOne(block: AnimeDatabase.() -> Query<T>): Flow<T>

View file

@ -2,7 +2,7 @@ package tachiyomi.data.handlers.anime
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.squareup.sqldelight.Query
import app.cash.sqldelight.Query
import tachiyomi.mi.data.AnimeDatabase
import kotlin.properties.Delegates

View file

@ -1,12 +1,13 @@
package tachiyomi.data.handlers.manga
import androidx.paging.PagingSource
import com.squareup.sqldelight.Query
import com.squareup.sqldelight.db.SqlDriver
import com.squareup.sqldelight.runtime.coroutines.asFlow
import com.squareup.sqldelight.runtime.coroutines.mapToList
import com.squareup.sqldelight.runtime.coroutines.mapToOne
import com.squareup.sqldelight.runtime.coroutines.mapToOneOrNull
import app.cash.sqldelight.ExecutableQuery
import app.cash.sqldelight.Query
import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToList
import app.cash.sqldelight.coroutines.mapToOne
import app.cash.sqldelight.coroutines.mapToOneOrNull
import app.cash.sqldelight.db.SqlDriver
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
@ -40,6 +41,13 @@ class AndroidMangaDatabaseHandler(
return dispatch(inTransaction) { block(db).executeAsOne() }
}
override suspend fun <T : Any> awaitOneExecutable(
inTransaction: Boolean,
block: suspend Database.() -> ExecutableQuery<T>,
): T {
return dispatch(inTransaction) { block(db).executeAsOne() }
}
override suspend fun <T : Any> awaitOneOrNull(
inTransaction: Boolean,
block: suspend Database.() -> Query<T>,
@ -47,6 +55,13 @@ class AndroidMangaDatabaseHandler(
return dispatch(inTransaction) { block(db).executeAsOneOrNull() }
}
override suspend fun <T : Any> awaitOneOrNullExecutable(
inTransaction: Boolean,
block: suspend Database.() -> ExecutableQuery<T>,
): T? {
return dispatch(inTransaction) { block(db).executeAsOneOrNull() }
}
override fun <T : Any> subscribeToList(block: Database.() -> Query<T>): Flow<List<T>> {
return block(db).asFlow().mapToList(queryDispatcher)
}

View file

@ -1,7 +1,8 @@
package tachiyomi.data.handlers.manga
import androidx.paging.PagingSource
import com.squareup.sqldelight.Query
import app.cash.sqldelight.ExecutableQuery
import app.cash.sqldelight.Query
import kotlinx.coroutines.flow.Flow
import tachiyomi.data.Database
@ -19,11 +20,21 @@ interface MangaDatabaseHandler {
block: suspend Database.() -> Query<T>,
): T
suspend fun <T : Any> awaitOneExecutable(
inTransaction: Boolean = false,
block: suspend Database.() -> ExecutableQuery<T>,
): T
suspend fun <T : Any> awaitOneOrNull(
inTransaction: Boolean = false,
block: suspend Database.() -> Query<T>,
): T?
suspend fun <T : Any> awaitOneOrNullExecutable(
inTransaction: Boolean = false,
block: suspend Database.() -> ExecutableQuery<T>,
): T?
fun <T : Any> subscribeToList(block: Database.() -> Query<T>): Flow<List<T>>
fun <T : Any> subscribeToOne(block: Database.() -> Query<T>): Flow<T>

View file

@ -2,7 +2,7 @@ package tachiyomi.data.handlers.manga
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.squareup.sqldelight.Query
import app.cash.sqldelight.Query
import tachiyomi.data.Database
import kotlin.properties.Delegates

View file

@ -2,7 +2,6 @@ package tachiyomi.data.items.chapter
import kotlinx.coroutines.flow.Flow
import logcat.LogPriority
import tachiyomi.core.util.lang.toLong
import tachiyomi.core.util.system.logcat
import tachiyomi.data.handlers.manga.MangaDatabaseHandler
import tachiyomi.domain.items.chapter.model.Chapter
@ -56,8 +55,8 @@ class ChapterRepositoryImpl(
url = chapterUpdate.url,
name = chapterUpdate.name,
scanlator = chapterUpdate.scanlator,
read = chapterUpdate.read?.toLong(),
bookmark = chapterUpdate.bookmark?.toLong(),
read = chapterUpdate.read,
bookmark = chapterUpdate.bookmark,
lastPageRead = chapterUpdate.lastPageRead,
chapterNumber = chapterUpdate.chapterNumber?.toDouble(),
sourceOrder = chapterUpdate.sourceOrder,

View file

@ -2,7 +2,6 @@ package tachiyomi.data.items.episode
import kotlinx.coroutines.flow.Flow
import logcat.LogPriority
import tachiyomi.core.util.lang.toLong
import tachiyomi.core.util.system.logcat
import tachiyomi.data.handlers.anime.AnimeDatabaseHandler
import tachiyomi.domain.items.episode.model.Episode
@ -57,8 +56,8 @@ class EpisodeRepositoryImpl(
url = episodeUpdate.url,
name = episodeUpdate.name,
scanlator = episodeUpdate.scanlator,
seen = episodeUpdate.seen?.toLong(),
bookmark = episodeUpdate.bookmark?.toLong(),
seen = episodeUpdate.seen,
bookmark = episodeUpdate.bookmark,
lastSecondSeen = episodeUpdate.lastSecondSeen,
totalSeconds = episodeUpdate.totalSeconds,
episodeNumber = episodeUpdate.episodeNumber?.toDouble(),

View file

@ -1,3 +1,6 @@
import kotlin.Boolean;
import kotlin.Float;
CREATE TABLE chapters(
_id INTEGER NOT NULL PRIMARY KEY,
manga_id INTEGER NOT NULL,
@ -9,9 +12,9 @@ CREATE TABLE chapters(
last_page_read INTEGER NOT NULL,
chapter_number REAL AS Float NOT NULL,
source_order INTEGER NOT NULL,
date_fetch INTEGER AS Long NOT NULL,
date_upload INTEGER AS Long NOT NULL,
last_modified_at INTEGER AS Long NOT NULL DEFAULT 0,
date_fetch INTEGER NOT NULL,
date_upload INTEGER NOT NULL,
last_modified_at INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
ON DELETE CASCADE
);

View file

@ -1,3 +1,5 @@
import kotlin.Float;
CREATE TABLE manga_sync(
_id INTEGER NOT NULL PRIMARY KEY,
manga_id INTEGER NOT NULL,
@ -10,8 +12,8 @@ CREATE TABLE manga_sync(
status INTEGER NOT NULL,
score REAL AS Float NOT NULL,
remote_url TEXT NOT NULL,
start_date INTEGER AS Long NOT NULL,
finish_date INTEGER AS Long NOT NULL,
start_date INTEGER NOT NULL,
finish_date INTEGER NOT NULL,
UNIQUE (manga_id, sync_id) ON CONFLICT REPLACE,
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
ON DELETE CASCADE

View file

@ -1,6 +1,7 @@
import eu.kanade.tachiyomi.source.model.UpdateStrategy;
import java.lang.String;
import kotlin.collections.List;
import kotlin.Boolean;
import kotlin.String;
CREATE TABLE mangas(
_id INTEGER NOT NULL PRIMARY KEY,
@ -14,17 +15,17 @@ CREATE TABLE mangas(
status INTEGER NOT NULL,
thumbnail_url TEXT,
favorite INTEGER AS Boolean NOT NULL,
last_update INTEGER AS Long,
next_update INTEGER AS Long,
last_update INTEGER,
next_update INTEGER,
initialized INTEGER AS Boolean NOT NULL,
viewer INTEGER NOT NULL,
chapter_flags INTEGER NOT NULL,
cover_last_modified INTEGER AS Long NOT NULL,
date_added INTEGER AS Long NOT NULL,
cover_last_modified INTEGER NOT NULL,
date_added INTEGER NOT NULL,
update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0,
calculate_interval INTEGER DEFAULT 0 NOT NULL,
last_modified_at INTEGER AS Long NOT NULL DEFAULT 0,
favorite_modified_at INTEGER AS Long
last_modified_at INTEGER NOT NULL DEFAULT 0,
favorite_modified_at INTEGER
);
CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1;
@ -92,7 +93,7 @@ SELECT *
FROM mangas
WHERE favorite = 1
AND LOWER(title) = :title
LIMIT 1;
AND _id != :id;
resetViewerFlags:
UPDATE mangas

View file

@ -2,7 +2,7 @@ CREATE TABLE mangas_categories(
_id INTEGER NOT NULL PRIMARY KEY,
manga_id INTEGER NOT NULL,
category_id INTEGER NOT NULL,
last_modified_at INTEGER AS Long NOT NULL DEFAULT 0,
last_modified_at INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY(category_id) REFERENCES categories (_id)
ON DELETE CASCADE,
FOREIGN KEY(manga_id) REFERENCES mangas (_id)

View file

@ -1,3 +1,5 @@
import kotlin.Float;
CREATE TABLE anime_sync(
_id INTEGER NOT NULL PRIMARY KEY,
anime_id INTEGER NOT NULL,
@ -10,8 +12,8 @@ CREATE TABLE anime_sync(
status INTEGER NOT NULL,
score REAL AS Float NOT NULL,
remote_url TEXT NOT NULL,
start_date INTEGER AS Long NOT NULL,
finish_date INTEGER AS Long NOT NULL,
start_date INTEGER NOT NULL,
finish_date INTEGER NOT NULL,
UNIQUE (anime_id, sync_id) ON CONFLICT REPLACE,
FOREIGN KEY(anime_id) REFERENCES animes (_id)
ON DELETE CASCADE

View file

@ -1,6 +1,7 @@
import eu.kanade.tachiyomi.source.model.UpdateStrategy;
import java.lang.String;
import kotlin.collections.List;
import kotlin.Boolean;
import kotlin.String;
CREATE TABLE animes(
_id INTEGER NOT NULL PRIMARY KEY,
@ -14,17 +15,17 @@ CREATE TABLE animes(
status INTEGER NOT NULL,
thumbnail_url TEXT,
favorite INTEGER AS Boolean NOT NULL,
last_update INTEGER AS Long,
next_update INTEGER AS Long,
last_update INTEGER,
next_update INTEGER,
initialized INTEGER AS Boolean NOT NULL,
viewer INTEGER NOT NULL,
episode_flags INTEGER NOT NULL,
cover_last_modified INTEGER AS Long NOT NULL,
date_added INTEGER AS Long NOT NULL,
cover_last_modified INTEGER NOT NULL,
date_added INTEGER NOT NULL,
update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0,
calculate_interval INTEGER DEFAULT 0 NOT NULL,
last_modified_at INTEGER AS Long NOT NULL DEFAULT 0,
favorite_modified_at INTEGER AS Long
last_modified_at INTEGER NOT NULL DEFAULT 0,
favorite_modified_at INTEGER
);
CREATE INDEX animelib_favorite_index ON animes(favorite) WHERE favorite = 1;
@ -92,7 +93,7 @@ SELECT *
FROM animes
WHERE favorite = 1
AND LOWER(title) = :title
LIMIT 1;
AND _id != :id;
resetViewerFlags:
UPDATE animes

View file

@ -2,7 +2,7 @@ CREATE TABLE animes_categories(
_id INTEGER NOT NULL PRIMARY KEY,
anime_id INTEGER NOT NULL,
category_id INTEGER NOT NULL,
last_modified_at INTEGER AS Long NOT NULL DEFAULT 0,
last_modified_at INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY(category_id) REFERENCES categories (_id)
ON DELETE CASCADE,
FOREIGN KEY(anime_id) REFERENCES animes (_id)

View file

@ -1,3 +1,6 @@
import kotlin.Boolean;
import kotlin.Float;
CREATE TABLE episodes(
_id INTEGER NOT NULL PRIMARY KEY,
anime_id INTEGER NOT NULL,
@ -10,9 +13,9 @@ CREATE TABLE episodes(
total_seconds INTEGER NOT NULL,
episode_number REAL AS Float NOT NULL,
source_order INTEGER NOT NULL,
date_fetch INTEGER AS Long NOT NULL,
date_upload INTEGER AS Long NOT NULL,
last_modified_at INTEGER AS Long NOT NULL DEFAULT 0,
date_fetch INTEGER NOT NULL,
date_upload INTEGER NOT NULL,
last_modified_at INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY(anime_id) REFERENCES animes (_id)
ON DELETE CASCADE
);
@ -54,7 +57,7 @@ WHERE url = :episodeUrl;
getEpisodeByUrlAndAnimeId:
SELECT *
FROM episodes
WHERE url = :chapterUrl
WHERE url = :episodeUrl
AND anime_id = :animeId;
removeEpisodesWithIds:

View file

@ -30,7 +30,7 @@ SELECT
episodes._id AS episodeId,
animes.title,
animes.thumbnail_url AS thumbnailUrl,
episodes.chapter_number AS episodeNumber,
episodes.episode_number AS episodeNumber,
animehistory.last_seen AS seenAt,
max_last_seen.last_seen AS maxSeenAt,
max_last_seen.episode_id AS maxSeenAtEpisodeId
@ -47,4 +47,4 @@ JOIN (
) AS max_last_seen
ON episodes.anime_id = max_last_seen.anime_id;
CREATE INDEX animehistory_history_chapter_id_index ON animehistory(episode_id);
CREATE INDEX animehistory_history_episode_id_index ON animehistory(episode_id);

View file

@ -9,8 +9,8 @@ UPDATE animes_categories SET last_modified_at = strftime('%s', 'now');
UPDATE episodes SET last_modified_at = strftime('%s', 'now');
-- Create triggers
DROP TRIGGER IF EXISTS update_last_modified_at_mangas;
CREATE TRIGGER update_last_modified_at_mangas
DROP TRIGGER IF EXISTS update_last_modified_at_animes;
CREATE TRIGGER update_last_modified_at_animes
AFTER UPDATE ON animes
FOR EACH ROW
BEGIN
@ -19,8 +19,8 @@ BEGIN
WHERE _id = new._id;
END;
DROP TRIGGER IF EXISTS update_favorite_modified_at_mangas;
CREATE TRIGGER update_last_favorited_at_mangas
DROP TRIGGER IF EXISTS update_favorite_modified_at_animes;
CREATE TRIGGER update_last_favorited_at_animes
AFTER UPDATE OF favorite ON animes
BEGIN
UPDATE animes
@ -28,8 +28,8 @@ BEGIN
WHERE _id = new._id;
END;
DROP TRIGGER IF EXISTS update_last_modified_at_chapters;
CREATE TRIGGER update_last_modified_at_chapters
DROP TRIGGER IF EXISTS update_last_modified_at_episodes;
CREATE TRIGGER update_last_modified_at_episodes
AFTER UPDATE ON episodes
FOR EACH ROW
BEGIN
@ -38,8 +38,8 @@ BEGIN
WHERE _id = new._id;
END;
DROP TRIGGER IF EXISTS update_last_modified_at_mangas_categories;
CREATE TRIGGER update_last_modified_at_mangas_categories
DROP TRIGGER IF EXISTS update_last_modified_at_animes_categories;
CREATE TRIGGER update_last_modified_at_animes_categories
AFTER UPDATE ON animes_categories
FOR EACH ROW
BEGIN

View file

@ -7,7 +7,7 @@ class GetDuplicateLibraryAnime(
private val animeRepository: AnimeRepository,
) {
suspend fun await(title: String): Anime? {
return animeRepository.getDuplicateLibraryAnime(title.lowercase())
suspend fun await(anime: Anime): List<Anime> {
return animeRepository.getDuplicateLibraryAnime(anime.id, anime.title.lowercase())
}
}

View file

@ -13,32 +13,32 @@ import kotlin.math.absoluteValue
const val MAX_GRACE_PERIOD = 28
class SetAnimeUpdateInterval(
class SetAnimeFetchInterval(
private val libraryPreferences: LibraryPreferences = Injekt.get(),
) {
fun updateInterval(
fun update(
anime: Anime,
episodes: List<Episode>,
zonedDateTime: ZonedDateTime,
fetchRange: Pair<Long, Long>,
): AnimeUpdate? {
val currentFetchRange = if (fetchRange.first == 0L && fetchRange.second == 0L) {
getCurrentFetchRange(ZonedDateTime.now())
val currentInterval = if (fetchRange.first == 0L && fetchRange.second == 0L) {
getCurrent(ZonedDateTime.now())
} else {
fetchRange
}
val interval = anime.calculateInterval.takeIf { it < 0 } ?: calculateInterval(episodes, zonedDateTime)
val nextUpdate = calculateNextUpdate(anime, interval, zonedDateTime, currentFetchRange)
val interval = anime.fetchInterval.takeIf { it < 0 } ?: calculateInterval(episodes, zonedDateTime)
val nextUpdate = calculateNextUpdate(anime, interval, zonedDateTime, currentInterval)
return if (anime.nextUpdate == nextUpdate && anime.calculateInterval == interval) {
return if (anime.nextUpdate == nextUpdate && anime.fetchInterval == interval) {
null
} else {
AnimeUpdate(id = anime.id, nextUpdate = nextUpdate, calculateInterval = interval)
AnimeUpdate(id = anime.id, nextUpdate = nextUpdate, fetchInterval = interval)
}
}
fun getCurrentFetchRange(timeToCal: ZonedDateTime): Pair<Long, Long> {
fun getCurrent(timeToCal: ZonedDateTime): Pair<Long, Long> {
// lead range and the following range depend on if updateOnlyExpectedPeriod set.
var followRange = 0
var leadRange = 0
@ -103,7 +103,7 @@ class SetAnimeUpdateInterval(
): Long {
return if (
anime.nextUpdate !in fetchRange.first.rangeTo(fetchRange.second + 1) ||
anime.calculateInterval == 0
anime.fetchInterval == 0
) {
val latestDate = ZonedDateTime.ofInstant(Instant.ofEpochMilli(anime.lastUpdate), zonedDateTime.zone).toLocalDate().atStartOfDay()
val timeSinceLatest = ChronoUnit.DAYS.between(latestDate, zonedDateTime).toInt()

View file

@ -11,7 +11,7 @@ data class Anime(
val favorite: Boolean,
val lastUpdate: Long,
val nextUpdate: Long,
val calculateInterval: Int,
val fetchInterval: Int,
val dateAdded: Long,
val viewerFlags: Long,
val episodeFlags: Long,
@ -118,7 +118,7 @@ data class Anime(
favorite = false,
lastUpdate = 0L,
nextUpdate = 0L,
calculateInterval = 0,
fetchInterval = 0,
dateAdded = 0L,
viewerFlags = 0L,
episodeFlags = 0L,

View file

@ -8,7 +8,7 @@ data class AnimeUpdate(
val favorite: Boolean? = null,
val lastUpdate: Long? = null,
val nextUpdate: Long? = null,
val calculateInterval: Int? = null,
val fetchInterval: Int? = null,
val dateAdded: Long? = null,
val viewerFlags: Long? = null,
val episodeFlags: Long? = null,
@ -32,7 +32,7 @@ fun Anime.toAnimeUpdate(): AnimeUpdate {
favorite = favorite,
lastUpdate = lastUpdate,
nextUpdate = nextUpdate,
calculateInterval = calculateInterval,
fetchInterval = fetchInterval,
dateAdded = dateAdded,
viewerFlags = viewerFlags,
episodeFlags = episodeFlags,

View file

@ -23,7 +23,7 @@ interface AnimeRepository {
fun getAnimeFavoritesBySourceId(sourceId: Long): Flow<List<Anime>>
suspend fun getDuplicateLibraryAnime(title: String): Anime?
suspend fun getDuplicateLibraryAnime(id: Long, title: String): List<Anime>
suspend fun resetAnimeViewerFlags(): Boolean

View file

@ -7,7 +7,7 @@ class GetDuplicateLibraryManga(
private val mangaRepository: MangaRepository,
) {
suspend fun await(title: String): Manga? {
return mangaRepository.getDuplicateLibraryManga(title.lowercase())
suspend fun await(manga: Manga): List<Manga> {
return mangaRepository.getDuplicateLibraryManga(manga.id, manga.title.lowercase())
}
}

View file

@ -13,32 +13,32 @@ import kotlin.math.absoluteValue
const val MAX_GRACE_PERIOD = 28
class SetMangaUpdateInterval(
class SetMangaFetchInterval(
private val libraryPreferences: LibraryPreferences = Injekt.get(),
) {
fun updateInterval(
fun update(
manga: Manga,
chapters: List<Chapter>,
zonedDateTime: ZonedDateTime,
fetchRange: Pair<Long, Long>,
): MangaUpdate? {
val currentFetchRange = if (fetchRange.first == 0L && fetchRange.second == 0L) {
getCurrentFetchRange(ZonedDateTime.now())
val currentInterval = if (fetchRange.first == 0L && fetchRange.second == 0L) {
getCurrent(ZonedDateTime.now())
} else {
fetchRange
}
val interval = manga.calculateInterval.takeIf { it < 0 } ?: calculateInterval(chapters, zonedDateTime)
val nextUpdate = calculateNextUpdate(manga, interval, zonedDateTime, currentFetchRange)
val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval(chapters, zonedDateTime)
val nextUpdate = calculateNextUpdate(manga, interval, zonedDateTime, currentInterval)
return if (manga.nextUpdate == nextUpdate && manga.calculateInterval == interval) {
return if (manga.nextUpdate == nextUpdate && manga.fetchInterval == interval) {
null
} else {
MangaUpdate(id = manga.id, nextUpdate = nextUpdate, calculateInterval = interval)
MangaUpdate(id = manga.id, nextUpdate = nextUpdate, fetchInterval = interval)
}
}
fun getCurrentFetchRange(timeToCal: ZonedDateTime): Pair<Long, Long> {
fun getCurrent(timeToCal: ZonedDateTime): Pair<Long, Long> {
// lead range and the following range depend on if updateOnlyExpectedPeriod set.
var followRange = 0
var leadRange = 0
@ -103,7 +103,7 @@ class SetMangaUpdateInterval(
): Long {
return if (
manga.nextUpdate !in fetchRange.first.rangeTo(fetchRange.second + 1) ||
manga.calculateInterval == 0
manga.fetchInterval == 0
) {
val latestDate = ZonedDateTime.ofInstant(Instant.ofEpochMilli(manga.lastUpdate), zonedDateTime.zone).toLocalDate().atStartOfDay()
val timeSinceLatest = ChronoUnit.DAYS.between(latestDate, zonedDateTime).toInt()

View file

@ -10,7 +10,7 @@ data class Manga(
val favorite: Boolean,
val lastUpdate: Long,
val nextUpdate: Long,
val calculateInterval: Int,
val fetchInterval: Int,
val dateAdded: Long,
val viewerFlags: Long,
val chapterFlags: Long,
@ -99,7 +99,7 @@ data class Manga(
favorite = false,
lastUpdate = 0L,
nextUpdate = 0L,
calculateInterval = 0,
fetchInterval = 0,
dateAdded = 0L,
viewerFlags = 0L,
chapterFlags = 0L,

View file

@ -8,7 +8,7 @@ data class MangaUpdate(
val favorite: Boolean? = null,
val lastUpdate: Long? = null,
val nextUpdate: Long? = null,
val calculateInterval: Int? = null,
val fetchInterval: Int? = null,
val dateAdded: Long? = null,
val viewerFlags: Long? = null,
val chapterFlags: Long? = null,
@ -32,7 +32,7 @@ fun Manga.toMangaUpdate(): MangaUpdate {
favorite = favorite,
lastUpdate = lastUpdate,
nextUpdate = nextUpdate,
calculateInterval = calculateInterval,
fetchInterval = fetchInterval,
dateAdded = dateAdded,
viewerFlags = viewerFlags,
chapterFlags = chapterFlags,

View file

@ -23,7 +23,7 @@ interface MangaRepository {
fun getMangaFavoritesBySourceId(sourceId: Long): Flow<List<Manga>>
suspend fun getDuplicateLibraryManga(title: String): Manga?
suspend fun getDuplicateLibraryManga(id: Long, title: String): List<Manga>
suspend fun resetMangaViewerFlags(): Boolean

View file

@ -10,14 +10,14 @@ import java.time.Duration
import java.time.ZonedDateTime
@Execution(ExecutionMode.CONCURRENT)
class SetAnimeUpdateIntervalTest {
class SetAnimeFetchIntervalTest {
private val testTime = ZonedDateTime.parse("2020-01-01T00:00:00Z")
private var episode = Episode.create().copy(
dateFetch = testTime.toEpochSecond() * 1000,
dateUpload = testTime.toEpochSecond() * 1000,
)
private val setAnimeUpdateInterval = SetAnimeUpdateInterval(mockk())
private val setAnimeFetchInterval = SetAnimeFetchInterval(mockk())
private fun episodeAddTime(episode: Episode, duration: Duration): Episode {
val newTime = testTime.plus(duration).toEpochSecond() * 1000
@ -33,7 +33,7 @@ class SetAnimeUpdateIntervalTest {
val newEpisode = episodeAddTime(episode, duration)
episodes.add(newEpisode)
}
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 7
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 7
}
@Test
@ -44,7 +44,7 @@ class SetAnimeUpdateIntervalTest {
val newEpisode = episodeAddTime(episode, duration)
episodes.add(newEpisode)
}
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 7
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 7
}
@Test
@ -60,7 +60,7 @@ class SetAnimeUpdateIntervalTest {
val newEpisode = episodeAddTime(episode, duration)
episodes.add(newEpisode)
}
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 7
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 7
}
// Default 1 if interval less than 1
@ -72,7 +72,7 @@ class SetAnimeUpdateIntervalTest {
val newEpisode = episodeAddTime(episode, duration)
episodes.add(newEpisode)
}
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 1
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 1
}
// Normal interval calculation
@ -84,7 +84,7 @@ class SetAnimeUpdateIntervalTest {
val newEpisode = episodeAddTime(episode, duration)
episodes.add(newEpisode)
}
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 1
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 1
}
@Test
@ -95,7 +95,7 @@ class SetAnimeUpdateIntervalTest {
val newEpisode = episodeAddTime(episode, duration)
episodes.add(newEpisode)
}
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 2
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 2
}
// If interval is decimal, floor to closest integer
@ -107,7 +107,7 @@ class SetAnimeUpdateIntervalTest {
val newEpisode = episodeAddTime(episode, duration)
episodes.add(newEpisode)
}
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 1
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 1
}
@Test
@ -118,7 +118,7 @@ class SetAnimeUpdateIntervalTest {
val newEpisode = episodeAddTime(episode, duration)
episodes.add(newEpisode)
}
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 1
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 1
}
// Use fetch time if upload time not available
@ -130,6 +130,6 @@ class SetAnimeUpdateIntervalTest {
val newEpisode = episodeAddTime(episode, duration).copy(dateUpload = 0L)
episodes.add(newEpisode)
}
setAnimeUpdateInterval.calculateInterval(episodes, testTime) shouldBe 1
setAnimeFetchInterval.calculateInterval(episodes, testTime) shouldBe 1
}
}

View file

@ -10,14 +10,14 @@ import java.time.Duration
import java.time.ZonedDateTime
@Execution(ExecutionMode.CONCURRENT)
class SetMangaUpdateIntervalTest {
class SetMangaFetchIntervalTest {
private val testTime = ZonedDateTime.parse("2020-01-01T00:00:00Z")
private var chapter = Chapter.create().copy(
dateFetch = testTime.toEpochSecond() * 1000,
dateUpload = testTime.toEpochSecond() * 1000,
)
private val setMangaUpdateInterval = SetMangaUpdateInterval(mockk())
private val setMangaFetchInterval = SetMangaFetchInterval(mockk())
private fun chapterAddTime(chapter: Chapter, duration: Duration): Chapter {
val newTime = testTime.plus(duration).toEpochSecond() * 1000
@ -33,7 +33,7 @@ class SetMangaUpdateIntervalTest {
val newChapter = chapterAddTime(chapter, duration)
chapters.add(newChapter)
}
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 7
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 7
}
@Test
@ -44,7 +44,7 @@ class SetMangaUpdateIntervalTest {
val newChapter = chapterAddTime(chapter, duration)
chapters.add(newChapter)
}
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 7
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 7
}
@Test
@ -60,7 +60,7 @@ class SetMangaUpdateIntervalTest {
val newChapter = chapterAddTime(chapter, duration)
chapters.add(newChapter)
}
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 7
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 7
}
// Default 1 if interval less than 1
@ -72,7 +72,7 @@ class SetMangaUpdateIntervalTest {
val newChapter = chapterAddTime(chapter, duration)
chapters.add(newChapter)
}
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 1
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 1
}
// Normal interval calculation
@ -84,7 +84,7 @@ class SetMangaUpdateIntervalTest {
val newChapter = chapterAddTime(chapter, duration)
chapters.add(newChapter)
}
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 1
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 1
}
@Test
@ -95,7 +95,7 @@ class SetMangaUpdateIntervalTest {
val newChapter = chapterAddTime(chapter, duration)
chapters.add(newChapter)
}
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 2
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 2
}
// If interval is decimal, floor to closest integer
@ -107,7 +107,7 @@ class SetMangaUpdateIntervalTest {
val newChapter = chapterAddTime(chapter, duration)
chapters.add(newChapter)
}
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 1
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 1
}
@Test
@ -118,7 +118,7 @@ class SetMangaUpdateIntervalTest {
val newChapter = chapterAddTime(chapter, duration)
chapters.add(newChapter)
}
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 1
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 1
}
// Use fetch time if upload time not available
@ -130,6 +130,6 @@ class SetMangaUpdateIntervalTest {
val newChapter = chapterAddTime(chapter, duration).copy(dateUpload = 0L)
chapters.add(newChapter)
}
setMangaUpdateInterval.calculateInterval(chapters, testTime) shouldBe 1
setMangaFetchInterval.calculateInterval(chapters, testTime) shouldBe 1
}
}

View file

@ -3,7 +3,7 @@ aboutlib_version = "10.8.3"
okhttp_version = "5.0.0-alpha.11"
shizuku_version = "12.2.0"
sqlite = "2.3.1"
sqldelight = "1.5.5"
sqldelight = "2.0.0"
leakcanary = "2.12"
voyager = "1.0.0-rc06"
richtext = "0.17.0"
@ -76,10 +76,12 @@ shizuku-provider = { module = "dev.rikka.shizuku:provider", version.ref = "shizu
leakcanary-android = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanary" }
leakcanary-plumber = { module = "com.squareup.leakcanary:plumber-android", version.ref = "leakcanary" }
sqldelight-android-driver = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions-jvm", version.ref = "sqldelight" }
sqldelight-android-paging = { module = "com.squareup.sqldelight:android-paging3-extensions", version.ref = "sqldelight" }
sqldelight-gradle = { module = "com.squareup.sqldelight:gradle-plugin", version.ref = "sqldelight" }
sqldelight-android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions-jvm", version.ref = "sqldelight" }
sqldelight-android-paging = { module = "app.cash.sqldelight:androidx-paging3-extensions", version.ref = "sqldelight" }
sqldelight-primitive-adapters = { module = "app.cash.sqldelight:primitive-adapters", version.ref = "sqldelight" }
sqldelight-dialects-sql = { module = "app.cash.sqldelight:sqlite-3-38-dialect", version.ref = "sqldelight" }
sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref = "sqldelight" }
junit = "org.junit.jupiter:junit-jupiter:5.10.0"
kotest-assertions = "io.kotest:kotest-assertions-core:5.6.2"
@ -106,6 +108,7 @@ js-engine = ["quickjs-android"]
sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"]
coil = ["coil-core", "coil-gif", "coil-compose"]
shizuku = ["shizuku-api", "shizuku-provider"]
sqldelight = ["sqldelight-android-driver", "sqldelight-coroutines", "sqldelight-android-paging", "sqldelight-primitive-adapters"]
voyager = ["voyager-navigator", "voyager-tab-navigator", "voyager-transitions"]
richtext = ["richtext-commonmark", "richtext-m3"]
test = ["junit", "kotest-assertions", "mockk"]

View file

@ -226,7 +226,6 @@
<string name="pref_show_nsfw_source">Show in sources and extensions lists</string>
<string name="parental_controls_info">This does not prevent unofficial or potentially incorrectly flagged extensions from surfacing NSFW (18+) content within the app.</string>
<string name="recently">Recently</string>
<string name="relative_time_today">Today</string>
<plurals name="relative_time">
<item quantity="one">Yesterday</item>
@ -524,6 +523,7 @@
<string name="cache_deleted">Cache cleared. %1$d files have been deleted</string>
<string name="cache_delete_error">Error occurred while clearing</string>
<string name="pref_invalidate_download_cache">Invalidate downloads index</string>
<string name="download_cache_invalidated">Downloads index invalidated</string>
<string name="pref_clear_database">Clear database</string>
<string name="pref_clear_database_summary">Delete history for entries that are not saved in your library</string>
<string name="clear_database_source_item_count">%1$d non-library entries in database</string>

View file

@ -5,7 +5,7 @@ plugins {
}
kotlin {
android()
androidTarget()
sourceSets {
val commonMain by getting {
dependencies {

View file

@ -4,7 +4,7 @@ plugins {
}
kotlin {
android()
androidTarget()
sourceSets {
val commonMain by getting {
dependencies {