mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-15 15:02:49 +01:00
Merge branch 'master' into sync-part-final
This commit is contained in:
commit
7bd9c2f7c0
@ -246,7 +246,7 @@ dependencies {
|
|||||||
implementation(libs.logcat)
|
implementation(libs.logcat)
|
||||||
|
|
||||||
// Crash reports/analytics
|
// Crash reports/analytics
|
||||||
implementation(libs.acra.http)
|
implementation(libs.bundles.acra)
|
||||||
"standardImplementation"(libs.firebase.analytics)
|
"standardImplementation"(libs.firebase.analytics)
|
||||||
|
|
||||||
// Shizuku
|
// Shizuku
|
||||||
|
@ -22,7 +22,6 @@ import tachiyomi.domain.chapter.service.ChapterRecognition
|
|||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.source.local.isLocal
|
import tachiyomi.source.local.isLocal
|
||||||
import java.lang.Long.max
|
import java.lang.Long.max
|
||||||
import java.time.Instant
|
|
||||||
import java.time.ZonedDateTime
|
import java.time.ZonedDateTime
|
||||||
import java.util.TreeSet
|
import java.util.TreeSet
|
||||||
|
|
||||||
@ -57,6 +56,7 @@ class SyncChaptersWithSource(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val now = ZonedDateTime.now()
|
val now = ZonedDateTime.now()
|
||||||
|
val nowMillis = now.toInstant().toEpochMilli()
|
||||||
|
|
||||||
val sourceChapters = rawSourceChapters
|
val sourceChapters = rawSourceChapters
|
||||||
.distinctBy { it.url }
|
.distinctBy { it.url }
|
||||||
@ -67,36 +67,27 @@ class SyncChaptersWithSource(
|
|||||||
.copy(mangaId = manga.id, sourceOrder = i.toLong())
|
.copy(mangaId = manga.id, sourceOrder = i.toLong())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chapters from db.
|
|
||||||
val dbChapters = getChaptersByMangaId.await(manga.id)
|
val dbChapters = getChaptersByMangaId.await(manga.id)
|
||||||
|
|
||||||
// Chapters from the source not in db.
|
val newChapters = mutableListOf<Chapter>()
|
||||||
val toAdd = mutableListOf<Chapter>()
|
val updatedChapters = mutableListOf<Chapter>()
|
||||||
|
val removedChapters = dbChapters.filterNot { dbChapter ->
|
||||||
// Chapters whose metadata have changed.
|
|
||||||
val toChange = mutableListOf<Chapter>()
|
|
||||||
|
|
||||||
// Chapters from the db not in source.
|
|
||||||
val toDelete = dbChapters.filterNot { dbChapter ->
|
|
||||||
sourceChapters.any { sourceChapter ->
|
sourceChapters.any { sourceChapter ->
|
||||||
dbChapter.url == sourceChapter.url
|
dbChapter.url == sourceChapter.url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val rightNow = Instant.now().toEpochMilli()
|
|
||||||
|
|
||||||
// Used to not set upload date of older chapters
|
// Used to not set upload date of older chapters
|
||||||
// to a higher value than newer chapters
|
// to a higher value than newer chapters
|
||||||
var maxSeenUploadDate = 0L
|
var maxSeenUploadDate = 0L
|
||||||
|
|
||||||
val sManga = manga.toSManga()
|
|
||||||
for (sourceChapter in sourceChapters) {
|
for (sourceChapter in sourceChapters) {
|
||||||
var chapter = sourceChapter
|
var chapter = sourceChapter
|
||||||
|
|
||||||
// Update metadata from source if necessary.
|
// Update metadata from source if necessary.
|
||||||
if (source is HttpSource) {
|
if (source is HttpSource) {
|
||||||
val sChapter = chapter.toSChapter()
|
val sChapter = chapter.toSChapter()
|
||||||
source.prepareNewChapter(sChapter, sManga)
|
source.prepareNewChapter(sChapter, manga.toSManga())
|
||||||
chapter = chapter.copyFromSChapter(sChapter)
|
chapter = chapter.copyFromSChapter(sChapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,13 +99,13 @@ class SyncChaptersWithSource(
|
|||||||
|
|
||||||
if (dbChapter == null) {
|
if (dbChapter == null) {
|
||||||
val toAddChapter = if (chapter.dateUpload == 0L) {
|
val toAddChapter = if (chapter.dateUpload == 0L) {
|
||||||
val altDateUpload = if (maxSeenUploadDate == 0L) rightNow else maxSeenUploadDate
|
val altDateUpload = if (maxSeenUploadDate == 0L) nowMillis else maxSeenUploadDate
|
||||||
chapter.copy(dateUpload = altDateUpload)
|
chapter.copy(dateUpload = altDateUpload)
|
||||||
} else {
|
} else {
|
||||||
maxSeenUploadDate = max(maxSeenUploadDate, sourceChapter.dateUpload)
|
maxSeenUploadDate = max(maxSeenUploadDate, sourceChapter.dateUpload)
|
||||||
chapter
|
chapter
|
||||||
}
|
}
|
||||||
toAdd.add(toAddChapter)
|
newChapters.add(toAddChapter)
|
||||||
} else {
|
} else {
|
||||||
if (shouldUpdateDbChapter.await(dbChapter, chapter)) {
|
if (shouldUpdateDbChapter.await(dbChapter, chapter)) {
|
||||||
val shouldRenameChapter = downloadProvider.isChapterDirNameChanged(dbChapter, chapter) &&
|
val shouldRenameChapter = downloadProvider.isChapterDirNameChanged(dbChapter, chapter) &&
|
||||||
@ -134,13 +125,13 @@ class SyncChaptersWithSource(
|
|||||||
if (chapter.dateUpload != 0L) {
|
if (chapter.dateUpload != 0L) {
|
||||||
toChangeChapter = toChangeChapter.copy(dateUpload = chapter.dateUpload)
|
toChangeChapter = toChangeChapter.copy(dateUpload = chapter.dateUpload)
|
||||||
}
|
}
|
||||||
toChange.add(toChangeChapter)
|
updatedChapters.add(toChangeChapter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return if there's nothing to add, delete or change, avoiding unnecessary db transactions.
|
// Return if there's nothing to add, delete, or update to avoid unnecessary db transactions.
|
||||||
if (toAdd.isEmpty() && toDelete.isEmpty() && toChange.isEmpty()) {
|
if (newChapters.isEmpty() && removedChapters.isEmpty() && updatedChapters.isEmpty()) {
|
||||||
if (manualFetch || manga.fetchInterval == 0 || manga.nextUpdate < fetchWindow.first) {
|
if (manualFetch || manga.fetchInterval == 0 || manga.nextUpdate < fetchWindow.first) {
|
||||||
updateManga.awaitUpdateFetchInterval(
|
updateManga.awaitUpdateFetchInterval(
|
||||||
manga,
|
manga,
|
||||||
@ -157,20 +148,20 @@ class SyncChaptersWithSource(
|
|||||||
val deletedReadChapterNumbers = TreeSet<Double>()
|
val deletedReadChapterNumbers = TreeSet<Double>()
|
||||||
val deletedBookmarkedChapterNumbers = TreeSet<Double>()
|
val deletedBookmarkedChapterNumbers = TreeSet<Double>()
|
||||||
|
|
||||||
toDelete.forEach { chapter ->
|
removedChapters.forEach { chapter ->
|
||||||
if (chapter.read) deletedReadChapterNumbers.add(chapter.chapterNumber)
|
if (chapter.read) deletedReadChapterNumbers.add(chapter.chapterNumber)
|
||||||
if (chapter.bookmark) deletedBookmarkedChapterNumbers.add(chapter.chapterNumber)
|
if (chapter.bookmark) deletedBookmarkedChapterNumbers.add(chapter.chapterNumber)
|
||||||
deletedChapterNumbers.add(chapter.chapterNumber)
|
deletedChapterNumbers.add(chapter.chapterNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
val deletedChapterNumberDateFetchMap = toDelete.sortedByDescending { it.dateFetch }
|
val deletedChapterNumberDateFetchMap = removedChapters.sortedByDescending { it.dateFetch }
|
||||||
.associate { it.chapterNumber to it.dateFetch }
|
.associate { it.chapterNumber to it.dateFetch }
|
||||||
|
|
||||||
// Date fetch is set in such a way that the upper ones will have bigger value than the lower ones
|
// Date fetch is set in such a way that the upper ones will have bigger value than the lower ones
|
||||||
// Sources MUST return the chapters from most to less recent, which is common.
|
// Sources MUST return the chapters from most to less recent, which is common.
|
||||||
var itemCount = toAdd.size
|
var itemCount = newChapters.size
|
||||||
var updatedToAdd = toAdd.map { toAddItem ->
|
var updatedToAdd = newChapters.map { toAddItem ->
|
||||||
var chapter = toAddItem.copy(dateFetch = rightNow + itemCount--)
|
var chapter = toAddItem.copy(dateFetch = nowMillis + itemCount--)
|
||||||
|
|
||||||
if (chapter.isRecognizedNumber.not() || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter
|
if (chapter.isRecognizedNumber.not() || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter
|
||||||
|
|
||||||
@ -189,8 +180,8 @@ class SyncChaptersWithSource(
|
|||||||
chapter
|
chapter
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toDelete.isNotEmpty()) {
|
if (removedChapters.isNotEmpty()) {
|
||||||
val toDeleteIds = toDelete.map { it.id }
|
val toDeleteIds = removedChapters.map { it.id }
|
||||||
chapterRepository.removeChaptersWithIds(toDeleteIds)
|
chapterRepository.removeChaptersWithIds(toDeleteIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,8 +189,8 @@ class SyncChaptersWithSource(
|
|||||||
updatedToAdd = chapterRepository.addAll(updatedToAdd)
|
updatedToAdd = chapterRepository.addAll(updatedToAdd)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toChange.isNotEmpty()) {
|
if (updatedChapters.isNotEmpty()) {
|
||||||
val chapterUpdates = toChange.map { it.toChapterUpdate() }
|
val chapterUpdates = updatedChapters.map { it.toChapterUpdate() }
|
||||||
updateChapter.awaitAll(chapterUpdates)
|
updateChapter.awaitAll(chapterUpdates)
|
||||||
}
|
}
|
||||||
updateManga.awaitUpdateFetchInterval(manga, now, fetchWindow)
|
updateManga.awaitUpdateFetchInterval(manga, now, fetchWindow)
|
||||||
|
@ -77,7 +77,6 @@ fun SetIntervalDialog(
|
|||||||
title = { Text(stringResource(MR.strings.manga_modify_calculated_interval_title)) },
|
title = { Text(stringResource(MR.strings.manga_modify_calculated_interval_title)) },
|
||||||
text = {
|
text = {
|
||||||
Column {
|
Column {
|
||||||
// TODO: figure out why nextUpdate is a weird number sometimes
|
|
||||||
if (nextUpdateDays >= 0) {
|
if (nextUpdateDays >= 0) {
|
||||||
Text(
|
Text(
|
||||||
stringResource(
|
stringResource(
|
||||||
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
|
import android.app.job.JobInfo
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -51,10 +52,12 @@ import logcat.AndroidLogcatLogger
|
|||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import logcat.LogcatLogger
|
import logcat.LogcatLogger
|
||||||
import org.acra.config.httpSender
|
import org.acra.config.httpSender
|
||||||
|
import org.acra.config.scheduler
|
||||||
import org.acra.ktx.initAcra
|
import org.acra.ktx.initAcra
|
||||||
import org.acra.sender.HttpSender
|
import org.acra.sender.HttpSender
|
||||||
import org.conscrypt.Conscrypt
|
import org.conscrypt.Conscrypt
|
||||||
import tachiyomi.core.i18n.stringResource
|
import tachiyomi.core.i18n.stringResource
|
||||||
|
import tachiyomi.core.preference.Preference
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.widget.WidgetManager
|
import tachiyomi.presentation.widget.WidgetManager
|
||||||
@ -199,12 +202,20 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
|||||||
if (isPreviewBuildType || isReleaseBuildType) {
|
if (isPreviewBuildType || isReleaseBuildType) {
|
||||||
initAcra {
|
initAcra {
|
||||||
buildConfigClass = BuildConfig::class.java
|
buildConfigClass = BuildConfig::class.java
|
||||||
excludeMatchingSharedPreferencesKeys = listOf(".*username.*", ".*password.*", ".*token.*")
|
excludeMatchingSharedPreferencesKeys = listOf(
|
||||||
|
Preference.privateKey(".*"), ".*username.*", ".*password.*", ".*token.*",
|
||||||
|
)
|
||||||
|
|
||||||
httpSender {
|
httpSender {
|
||||||
uri = BuildConfig.ACRA_URI
|
uri = BuildConfig.ACRA_URI
|
||||||
httpMethod = HttpSender.Method.PUT
|
httpMethod = HttpSender.Method.PUT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scheduler {
|
||||||
|
requiresBatteryNotLow = true
|
||||||
|
requiresDeviceIdle = true
|
||||||
|
requiresNetworkType = JobInfo.NETWORK_TYPE_UNMETERED
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,16 +19,15 @@ class FetchInterval(
|
|||||||
dateTime: ZonedDateTime,
|
dateTime: ZonedDateTime,
|
||||||
window: Pair<Long, Long>,
|
window: Pair<Long, Long>,
|
||||||
): MangaUpdate? {
|
): MangaUpdate? {
|
||||||
|
val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval(
|
||||||
|
chapters = getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true),
|
||||||
|
zone = dateTime.zone,
|
||||||
|
)
|
||||||
val currentWindow = if (window.first == 0L && window.second == 0L) {
|
val currentWindow = if (window.first == 0L && window.second == 0L) {
|
||||||
getWindow(ZonedDateTime.now())
|
getWindow(ZonedDateTime.now())
|
||||||
} else {
|
} else {
|
||||||
window
|
window
|
||||||
}
|
}
|
||||||
val chapters = getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true)
|
|
||||||
val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval(
|
|
||||||
chapters,
|
|
||||||
dateTime.zone,
|
|
||||||
)
|
|
||||||
val nextUpdate = calculateNextUpdate(manga, interval, dateTime, currentWindow)
|
val nextUpdate = calculateNextUpdate(manga, interval, dateTime, currentWindow)
|
||||||
|
|
||||||
return if (manga.nextUpdate == nextUpdate && manga.fetchInterval == interval) {
|
return if (manga.nextUpdate == nextUpdate && manga.fetchInterval == interval) {
|
||||||
@ -102,7 +101,7 @@ class FetchInterval(
|
|||||||
manga.fetchInterval == 0
|
manga.fetchInterval == 0
|
||||||
) {
|
) {
|
||||||
val latestDate = ZonedDateTime.ofInstant(
|
val latestDate = ZonedDateTime.ofInstant(
|
||||||
Instant.ofEpochMilli(manga.lastUpdate),
|
if (manga.lastUpdate > 0) Instant.ofEpochMilli(manga.lastUpdate) else Instant.now(),
|
||||||
dateTime.zone,
|
dateTime.zone,
|
||||||
)
|
)
|
||||||
.toLocalDate()
|
.toLocalDate()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
[versions]
|
[versions]
|
||||||
aboutlib_version = "10.10.0"
|
aboutlib_version = "10.10.0"
|
||||||
|
acra = "5.11.3"
|
||||||
leakcanary = "2.12"
|
leakcanary = "2.12"
|
||||||
moko = "0.23.0"
|
moko = "0.23.0"
|
||||||
okhttp_version = "5.0.0-alpha.12"
|
okhttp_version = "5.0.0-alpha.12"
|
||||||
@ -67,7 +68,8 @@ moko-gradle = { module = "dev.icerock.moko:resources-generator", version.ref = "
|
|||||||
|
|
||||||
logcat = "com.squareup.logcat:logcat:0.1"
|
logcat = "com.squareup.logcat:logcat:0.1"
|
||||||
|
|
||||||
acra-http = "ch.acra:acra-http:5.11.3"
|
acra-http = { module = "ch.acra:acra-http", version.ref = "acra" }
|
||||||
|
acra-scheduler = { module = "ch.acra:acra-advanced-scheduler", version.ref = "acra" }
|
||||||
firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.5.0"
|
firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.5.0"
|
||||||
|
|
||||||
aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" }
|
aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" }
|
||||||
@ -99,6 +101,7 @@ google-api-services-drive = "com.google.apis:google-api-services-drive:v3-rev197
|
|||||||
google-api-client-oauth = "com.google.oauth-client:google-oauth-client:1.34.1"
|
google-api-client-oauth = "com.google.oauth-client:google-oauth-client:1.34.1"
|
||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
|
acra = ["acra-http", "acra-scheduler"]
|
||||||
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"]
|
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"]
|
||||||
js-engine = ["quickjs-android"]
|
js-engine = ["quickjs-android"]
|
||||||
sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"]
|
sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"]
|
||||||
|
Loading…
Reference in New Issue
Block a user