Merge manga favorite related fields into favorite_at

Migration 16 replaces favorite, date_added and favorite_modified_at with one nullable favorite_at. A favorite whose date_added is 0 falls back to favorite_modified_at. Manga.favorite and dateAdded become getters derived from it, and restore merges it by taking the earliest non-zero time.
This commit is contained in:
AntsyLich
2026-09-27 22:37:02 +06:00
parent f8fff318ba
commit 986f46c09a
18 changed files with 183 additions and 109 deletions
+1 -1
View File
@@ -36,7 +36,7 @@ android {
defaultConfig {
applicationId = "app.mihon"
versionCode = 31
versionCode = 32
versionName = "0.20.4"
buildConfigField("boolean", "TELEMETRY_INCLUDED", "${Config.includeTelemetry}")
@@ -48,15 +48,10 @@ class UpdateManga(
}
suspend fun awaitUpdateFavorite(mangaId: Long, favorite: Boolean): Boolean {
val dateAdded = when (favorite) {
true -> Clock.System.now().toEpochMilliseconds()
false -> 0
val update = when (favorite) {
true -> MangaUpdate(mangaId) { favoriteAt = Clock.System.now().toEpochMilliseconds() }
false -> MangaUpdate(mangaId) { favoriteAt = null }
}
return mangaRepository.update(
MangaUpdate(mangaId) {
this.favorite = favorite
this.dateAdded = dateAdded
},
)
return mangaRepository.update(update)
}
}
@@ -88,12 +88,11 @@ private fun Manga.toBackupManga() =
thumbnailUrl = this.thumbnailUrl,
favorite = this.favorite,
source = this.source,
dateAdded = this.dateAdded,
dateAdded = this.favoriteAt ?: 0L,
viewer = (this.viewerFlags.toInt() and ReadingMode.MASK),
viewer_flags = this.viewerFlags.toInt(),
chapterFlags = this.chapterFlags.toInt(),
updateStrategy = this.updateStrategy,
favoriteModifiedAt = this.favoriteModifiedAt,
notes = this.notes,
initialized = this.initialized,
memo = this.memo.toByteArray(),
@@ -57,13 +57,19 @@ class BackupManga(
genre = this@BackupManga.genre,
status = this@BackupManga.status.toLong(),
thumbnailUrl = this@BackupManga.thumbnailUrl,
favorite = this@BackupManga.favorite,
source = this@BackupManga.source,
dateAdded = this@BackupManga.dateAdded,
// For libraries older than dateAdded, the favorite timestamp is the only record of when
// an entry was added. It was written by a trigger in seconds, unlike dateAdded.
favoriteAt = if (this@BackupManga.favorite) {
this@BackupManga.dateAdded.takeIf { it != 0L }
?: this@BackupManga.favoriteModifiedAt?.times(1000)
?: 0L
} else {
null
},
viewerFlags = (this@BackupManga.viewer_flags ?: this@BackupManga.viewer).toLong(),
chapterFlags = this@BackupManga.chapterFlags.toLong(),
updateStrategy = this@BackupManga.updateStrategy,
favoriteModifiedAt = this@BackupManga.favoriteModifiedAt,
notes = this@BackupManga.notes,
initialized = this@BackupManga.initialized,
memo = this@BackupManga.memo.toJsonObject(),
@@ -236,17 +236,13 @@ class BrowseSourceViewModel(
val update = if (manga.favorite) {
val coverLastModified = manga.removeCovers(coverCache).coverLastModified
MangaUpdate(manga.id) {
favorite = false
dateAdded = 0
favoriteAt = null
if (coverLastModified != manga.coverLastModified) this.coverLastModified = coverLastModified
}
} else {
setMangaDefaultChapterFlags.await(manga)
addTracks.bindEnhancedTrackers(manga, sourceManager.getOrStub(manga.source))
MangaUpdate(manga.id) {
favorite = true
dateAdded = Clock.System.now().toEpochMilliseconds()
}
MangaUpdate(manga.id) { favoriteAt = Clock.System.now().toEpochMilliseconds() }
}
updateManga.await(update)
@@ -338,7 +338,7 @@ class LibraryViewModel(
manga1.libraryManga.chapterFetchedAt.compareTo(manga2.libraryManga.chapterFetchedAt)
}
LibrarySort.Type.DateAdded -> {
manga1.libraryManga.manga.dateAdded.compareTo(manga2.libraryManga.manga.dateAdded)
compareValues(manga1.libraryManga.manga.favoriteAt, manga2.libraryManga.manga.favoriteAt)
}
LibrarySort.Type.TrackerMean -> {
val item1Score = trackerScores[manga1.id] ?: defaultTrackerScoreSortValue
@@ -572,7 +572,7 @@ class LibraryViewModel(
val toDelete = mangas.map {
it.removeCovers(coverCache)
MangaUpdate(it.id) {
favorite = false
favoriteAt = null
}
}
updateManga.awaitAll(toDelete)
@@ -111,15 +111,13 @@ class MigrateMangaUseCase(
}
val currentMangaUpdate = MangaUpdate(current.id) {
favorite = false
dateAdded = 0
favoriteAt = null
}
.takeIf { replace }
val targetMangaUpdate = MangaUpdate(target.id) {
favorite = true
favoriteAt = current.favoriteAt?.takeIf { replace } ?: Clock.System.now().toEpochMilliseconds()
chapterFlags = current.chapterFlags
viewerFlags = current.viewerFlags
dateAdded = if (replace) current.dateAdded else Clock.System.now().toEpochMilliseconds()
if (MigrationFlag.NOTES in flags) notes = current.notes
}
@@ -117,7 +117,7 @@ private fun ComparisonQueryNode.matches(item: LibraryItem): Boolean {
val match = when (field) {
ComparisonField.ID -> value.toLongOrNull()?.let { queryComparator.apply(manga.id, it) }
ComparisonField.DATE_ADDED -> compareDates(manga.dateAdded, value)
ComparisonField.DATE_ADDED -> manga.favoriteAt?.let { compareDates(it, value) }
ComparisonField.FETCH_INTERVAL -> value.toIntOrNull()
?.let { queryComparator.apply(abs(manga.fetchInterval), it) }
@@ -72,11 +72,11 @@ class RestoreRepositoryImpl(
private fun mergeManga(manga: Manga, dbManga: Manga): Manga {
val details = if (dbManga.initialized || !manga.initialized) dbManga else manga
return dbManga.copy(
favorite = dbManga.favorite || manga.favorite,
dateAdded = listOfNotNull(
dbManga.dateAdded.takeIf { it > 0 },
manga.dateAdded.takeIf { it > 0 },
).minOrNull() ?: 0L,
favoriteAt = if (dbManga.favorite || manga.favorite) {
listOfNotNull(dbManga.favoriteAt, manga.favoriteAt).filter { it > 0 }.minOrNull() ?: 0L
} else {
null
},
title = details.title,
artist = details.artist,
author = details.author,
@@ -93,6 +93,7 @@ class RestoreRepositoryImpl(
private suspend fun updateManga(manga: Manga): Manga {
database.mangasQueries.updateFromBackup(
favoriteAt = manga.favoriteAt,
artist = manga.artist,
author = manga.author,
description = manga.description,
@@ -100,13 +101,11 @@ class RestoreRepositoryImpl(
title = manga.title,
status = manga.status,
thumbnailUrl = manga.thumbnailUrl,
favorite = manga.favorite,
lastUpdate = manga.lastUpdate,
initialized = manga.initialized,
viewer = manga.viewerFlags,
chapterFlags = manga.chapterFlags,
coverLastModified = manga.coverLastModified,
dateAdded = manga.dateAdded,
mangaId = manga.id,
updateStrategy = manga.updateStrategy,
notes = manga.notes,
@@ -117,6 +116,7 @@ class RestoreRepositoryImpl(
private suspend fun insertManga(manga: Manga): Long {
return database.mangasQueries.insertReturningId(
favoriteAt = manga.favoriteAt,
source = manga.source,
url = manga.url,
artist = manga.artist,
@@ -126,7 +126,6 @@ class RestoreRepositoryImpl(
title = manga.title,
status = manga.status,
thumbnailUrl = manga.thumbnailUrl,
favorite = manga.favorite,
lastUpdate = manga.lastUpdate,
nextUpdate = 0L,
calculateInterval = 0L,
@@ -134,7 +133,6 @@ class RestoreRepositoryImpl(
viewerFlags = manga.viewerFlags,
chapterFlags = manga.chapterFlags,
coverLastModified = manga.coverLastModified,
dateAdded = manga.dateAdded,
updateStrategy = manga.updateStrategy,
notes = manga.notes,
memo = manga.memo,
@@ -18,27 +18,24 @@ object MangaMapper {
title: String,
status: Long,
thumbnailUrl: String?,
favorite: Boolean,
lastUpdate: Long?,
nextUpdate: Long?,
initialized: Boolean,
viewerFlags: Long,
chapterFlags: Long,
coverLastModified: Long,
dateAdded: Long,
updateStrategy: UpdateStrategy,
calculateInterval: Long,
favoriteModifiedAt: Long?,
notes: String,
memo: JsonObject,
favoriteAt: Long?,
): Manga = Manga(
id = id,
source = source,
favorite = favorite,
lastUpdate = lastUpdate ?: 0,
nextUpdate = nextUpdate ?: 0,
fetchInterval = calculateInterval.toInt(),
dateAdded = dateAdded,
favoriteAt = favoriteAt,
viewerFlags = viewerFlags,
chapterFlags = chapterFlags,
coverLastModified = coverLastModified,
@@ -52,7 +49,6 @@ object MangaMapper {
thumbnailUrl = thumbnailUrl,
updateStrategy = updateStrategy,
initialized = initialized,
favoriteModifiedAt = favoriteModifiedAt,
notes = notes,
memo = memo,
)
@@ -68,19 +64,17 @@ object MangaMapper {
title: String,
status: Long,
thumbnailUrl: String?,
favorite: Boolean,
lastUpdate: Long?,
nextUpdate: Long?,
initialized: Boolean,
viewerFlags: Long,
chapterFlags: Long,
coverLastModified: Long,
dateAdded: Long,
updateStrategy: UpdateStrategy,
calculateInterval: Long,
favoriteModifiedAt: Long?,
notes: String,
memo: JsonObject,
favoriteAt: Long?,
totalCount: Long,
readCount: Double,
latestUpload: Long,
@@ -100,19 +94,17 @@ object MangaMapper {
title,
status,
thumbnailUrl,
favorite,
lastUpdate,
nextUpdate,
initialized,
viewerFlags,
chapterFlags,
coverLastModified,
dateAdded,
updateStrategy,
calculateInterval,
favoriteModifiedAt,
notes,
memo,
favoriteAt,
),
categories = categories.split(",").map { it.toLong() },
totalChapters = totalCount,
@@ -134,19 +126,17 @@ object MangaMapper {
title: String,
status: Long,
thumbnailUrl: String?,
favorite: Boolean,
lastUpdate: Long?,
nextUpdate: Long?,
initialized: Boolean,
viewerFlags: Long,
chapterFlags: Long,
coverLastModified: Long,
dateAdded: Long,
updateStrategy: UpdateStrategy,
calculateInterval: Long,
favoriteModifiedAt: Long?,
notes: String,
memo: JsonObject,
favoriteAt: Long?,
totalCount: Long,
): MangaWithChapterCount = MangaWithChapterCount(
manga = mapManga(
@@ -160,19 +150,17 @@ object MangaMapper {
title,
status,
thumbnailUrl,
favorite,
lastUpdate,
nextUpdate,
initialized,
viewerFlags,
chapterFlags,
coverLastModified,
dateAdded,
updateStrategy,
calculateInterval,
favoriteModifiedAt,
notes,
memo,
favoriteAt,
),
chapterCount = totalCount,
)
@@ -198,7 +198,7 @@ class MangaRepositoryImpl(
title = it.title,
status = it.status,
thumbnailUrl = it.thumbnailUrl,
favorite = it.favorite,
favoriteAt = it.favoriteAt,
lastUpdate = it.lastUpdate,
nextUpdate = it.nextUpdate,
calculateInterval = it.fetchInterval.toLong(),
@@ -206,7 +206,6 @@ class MangaRepositoryImpl(
viewerFlags = it.viewerFlags,
chapterFlags = it.chapterFlags,
coverLastModified = it.coverLastModified,
dateAdded = it.dateAdded,
updateStrategy = it.updateStrategy,
memo = it.memo,
updateTitle = it.title.isNotBlank(),
@@ -247,14 +246,14 @@ class MangaRepositoryImpl(
mangaUpdates.forEach { value ->
with(value) {
database.mangasQueries.update(
favorite = favorite,
favoriteAtSet = isSet(::favoriteAt),
favoriteAt = favoriteAt,
lastUpdate = lastUpdate,
nextUpdate = nextUpdate,
calculateInterval = fetchInterval?.toLong(),
viewer = viewerFlags,
chapterFlags = chapterFlags,
coverLastModified = coverLastModified,
dateAdded = dateAdded,
mangaId = id,
notes = notes,
)
@@ -15,33 +15,23 @@ CREATE TABLE mangas(
title TEXT NOT NULL,
status INTEGER NOT NULL,
thumbnail_url TEXT,
favorite INTEGER AS Boolean NOT NULL,
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 NOT NULL,
date_added INTEGER NOT NULL,
update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0,
calculate_interval INTEGER DEFAULT 0 NOT NULL,
favorite_modified_at INTEGER,
notes TEXT NOT NULL DEFAULT "",
memo BLOB AS JsonObject NOT NULL DEFAULT '{}'
memo BLOB AS JsonObject NOT NULL DEFAULT '{}',
favorite_at INTEGER
);
CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1;
CREATE INDEX library_favorite_at_index ON mangas(favorite_at) WHERE favorite_at IS NOT NULL;
CREATE INDEX mangas_url_index ON mangas(url);
CREATE INDEX idx_mangas_source ON mangas(source);
CREATE TRIGGER update_last_favorited_at_mangas
AFTER UPDATE OF favorite ON mangas
BEGIN
UPDATE mangas
SET favorite_modified_at = strftime('%s', 'now')
WHERE _id = new._id;
END;
getMangaById:
SELECT *
FROM mangas
@@ -58,12 +48,12 @@ LIMIT 1;
getFavorites:
SELECT *
FROM mangas
WHERE favorite = 1;
WHERE favorite_at IS NOT NULL;
getReadMangaNotInLibrary:
SELECT *
FROM mangas
WHERE favorite = 0 AND _id IN (
WHERE favorite_at IS NULL AND _id IN (
SELECT DISTINCT chapters.manga_id
FROM chapters
WHERE read = 1 OR last_page_read != 0
@@ -77,23 +67,18 @@ getAllMangaSourceAndUrl:
SELECT source, url
FROM mangas;
getMangasWithFavoriteTimestamp:
SELECT *
FROM mangas
WHERE favorite_modified_at IS NOT NULL;
getSourceIdWithFavoriteCount:
SELECT
source,
count(*)
FROM mangas
WHERE favorite = 1
WHERE favorite_at IS NOT NULL
GROUP BY source;
getFavoriteBySourceId:
SELECT *
FROM mangas
WHERE favorite = 1
WHERE favorite_at IS NOT NULL
AND source = :sourceId;
getDuplicateLibraryManga:
@@ -113,7 +98,7 @@ duplicates AS (
FROM mangas M
LEFT JOIN track_dupes D
ON D.manga_id = _id
WHERE favorite = 1
WHERE favorite_at IS NOT NULL
AND _id != :id
AND (lower(title) LIKE '%' || lower(:title) || '%' OR D.manga_id IS NOT NULL)
),
@@ -141,7 +126,7 @@ getUpcomingManga:
SELECT *
FROM mangas
WHERE next_update >= :startOfDay
AND favorite = 1
AND favorite_at IS NOT NULL
AND status IN :statuses
AND (
-- includedEmpty being true expresses "don't care" state and bypasses the membership filter
@@ -179,12 +164,12 @@ SET viewer = 0;
getSourceIdsWithNonLibraryManga:
SELECT source, COUNT(*) AS manga_count
FROM mangas
WHERE favorite = 0
WHERE favorite_at IS NULL
GROUP BY source;
deleteNonLibraryManga:
DELETE FROM mangas
WHERE favorite = 0
WHERE favorite_at IS NULL
AND source IN :sourceIds
AND (
:keepReadManga = 0
@@ -197,19 +182,19 @@ AND (
);
insertReturningId:
INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, calculate_interval, notes, memo)
VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, :notes, :memo)
INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite_at, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, update_strategy, calculate_interval, notes, memo)
VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favoriteAt, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :updateStrategy, :calculateInterval, :notes, :memo)
RETURNING _id;
update:
UPDATE mangas SET
favorite = coalesce(:favorite, favorite),
-- NULL is a real value here (not in the library), which coalesce cannot write
favorite_at = CASE WHEN :favoriteAtSet THEN :favoriteAt ELSE favorite_at END,
last_update = coalesce(:lastUpdate, last_update),
next_update = coalesce(:nextUpdate, next_update),
viewer = coalesce(:viewer, viewer),
chapter_flags = coalesce(:chapterFlags, chapter_flags),
cover_last_modified = coalesce(:coverLastModified, cover_last_modified),
date_added = coalesce(:dateAdded, date_added),
calculate_interval = coalesce(:calculateInterval, calculate_interval),
notes = coalesce(:notes, notes)
WHERE _id = :mangaId;
@@ -223,13 +208,12 @@ UPDATE mangas SET
title = coalesce(:title, title),
status = coalesce(:status, status),
thumbnail_url = coalesce(:thumbnailUrl, thumbnail_url),
favorite = coalesce(:favorite, favorite),
favorite_at = :favoriteAt,
last_update = coalesce(:lastUpdate, last_update),
initialized = coalesce(:initialized, initialized),
viewer = coalesce(:viewer, viewer),
chapter_flags = coalesce(:chapterFlags, chapter_flags),
cover_last_modified = coalesce(:coverLastModified, cover_last_modified),
date_added = coalesce(:dateAdded, date_added),
update_strategy = coalesce(:updateStrategy, update_strategy),
notes = coalesce(:notes, notes),
memo = coalesce(:memo, memo)
@@ -253,13 +237,13 @@ WHERE _id = :mangaId;
insertNetworkManga {
-- Insert the manga if it doesn't exist already
INSERT INTO mangas(
source, url, artist, author, description, genre, title, status, thumbnail_url, favorite,
last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added,
source, url, artist, author, description, genre, title, status, thumbnail_url, favorite_at,
last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified,
update_strategy, calculate_interval, memo
)
SELECT
:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite,
:lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded,
:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favoriteAt,
:lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified,
:updateStrategy, :calculateInterval, :memo
WHERE NOT EXISTS(SELECT 0 FROM mangas WHERE source = :source AND url = :url);
@@ -278,7 +262,7 @@ insertNetworkManga {
memo = :memo
WHERE source = :source
AND url = :url
AND favorite = 0;
AND favorite_at IS NULL;
-- Finally return the manga
SELECT *
@@ -0,0 +1,113 @@
-- Merge favorite, date_added and favorite_modified_at into favorite_at
DROP VIEW historyView;
DROP VIEW libraryView;
DROP VIEW updatesView;
DROP TRIGGER update_last_favorited_at_mangas;
DROP INDEX library_favorite_index;
ALTER TABLE mangas ADD COLUMN favorite_at INTEGER;
UPDATE mangas
SET favorite_at = coalesce(nullif(date_added, 0), favorite_modified_at * 1000, 0)
WHERE favorite = 1;
ALTER TABLE mangas DROP COLUMN favorite;
ALTER TABLE mangas DROP COLUMN date_added;
ALTER TABLE mangas DROP COLUMN favorite_modified_at;
CREATE INDEX library_favorite_at_index ON mangas(favorite_at) WHERE favorite_at IS NOT NULL;
CREATE VIEW libraryView AS
SELECT
M.*,
coalesce(C.total, 0) AS totalCount,
coalesce(C.readCount, 0) AS readCount,
coalesce(C.latestUpload, 0) AS latestUpload,
coalesce(C.fetchedAt, 0) AS chapterFetchedAt,
coalesce(C.lastRead, 0) AS lastRead,
coalesce(C.bookmarkCount, 0) AS bookmarkCount,
coalesce(MC.categories, '0') AS categories
FROM mangas M
LEFT JOIN (
SELECT
chapters.manga_id,
count(*) AS total,
sum(read) AS readCount,
coalesce(max(chapters.date_upload), 0) AS latestUpload,
coalesce(max(history.last_read), 0) AS lastRead,
coalesce(max(chapters.date_fetch), 0) AS fetchedAt,
sum(chapters.bookmark) AS bookmarkCount
FROM chapters
LEFT JOIN excluded_scanlators
ON chapters.manga_id = excluded_scanlators.manga_id
AND chapters.scanlator = excluded_scanlators.scanlator
LEFT JOIN history
ON chapters._id = history.chapter_id
WHERE excluded_scanlators.scanlator IS NULL
GROUP BY chapters.manga_id
) AS C
ON M._id = C.manga_id
LEFT JOIN (
SELECT manga_id, group_concat(category_id) AS categories
FROM mangas_categories
GROUP BY manga_id
) AS MC
ON MC.manga_id = M._id
WHERE M.favorite_at IS NOT NULL;
CREATE VIEW updatesView AS
SELECT
mangas._id AS mangaId,
mangas.title AS mangaTitle,
chapters._id AS chapterId,
chapters.name AS chapterName,
chapters.scanlator,
chapters.url AS chapterUrl,
chapters.read,
chapters.bookmark,
chapters.last_page_read,
mangas.source,
mangas.favorite_at IS NOT NULL AS favorite,
mangas.thumbnail_url AS thumbnailUrl,
mangas.cover_last_modified AS coverLastModified,
chapters.date_upload AS dateUpload,
chapters.date_fetch AS datefetch,
excluded_scanlators.scanlator AS excludedScanlator
FROM mangas JOIN chapters
ON mangas._id = chapters.manga_id
LEFT JOIN excluded_scanlators
ON mangas._id = excluded_scanlators.manga_id
AND chapters.scanlator = excluded_scanlators.scanlator
WHERE favorite_at IS NOT NULL
AND date_fetch > favorite_at
ORDER BY date_fetch DESC;
CREATE VIEW historyView AS
SELECT
history._id AS id,
mangas._id AS mangaId,
chapters._id AS chapterId,
mangas.title,
mangas.thumbnail_url AS thumbnailUrl,
mangas.source,
mangas.favorite_at IS NOT NULL AS favorite,
mangas.cover_last_modified,
chapters.chapter_number AS chapterNumber,
history.last_read AS readAt,
history.time_read AS readDuration,
max_last_read.last_read AS maxReadAt,
max_last_read.chapter_id AS maxReadAtChapterId
FROM mangas
JOIN chapters
ON mangas._id = chapters.manga_id
JOIN history
ON chapters._id = history.chapter_id
JOIN (
SELECT chapters.manga_id,chapters._id AS chapter_id, MAX(history.last_read) AS last_read
FROM chapters JOIN history
ON chapters._id = history.chapter_id
GROUP BY chapters.manga_id
) AS max_last_read
ON chapters.manga_id = max_last_read.manga_id;
@@ -6,7 +6,7 @@ SELECT
mangas.title,
mangas.thumbnail_url AS thumbnailUrl,
mangas.source,
mangas.favorite,
mangas.favorite_at IS NOT NULL AS favorite,
mangas.cover_last_modified,
chapters.chapter_number AS chapterNumber,
history.last_read AS readAt,
@@ -34,7 +34,7 @@ LEFT JOIN (
GROUP BY manga_id
) AS MC
ON MC.manga_id = M._id
WHERE M.favorite = 1;
WHERE M.favorite_at IS NOT NULL;
library:
SELECT *
@@ -10,7 +10,7 @@ SELECT
chapters.bookmark,
chapters.last_page_read,
mangas.source,
mangas.favorite,
mangas.favorite_at IS NOT NULL AS favorite,
mangas.thumbnail_url AS thumbnailUrl,
mangas.cover_last_modified AS coverLastModified,
chapters.date_upload AS dateUpload,
@@ -21,8 +21,8 @@ ON mangas._id = chapters.manga_id
LEFT JOIN excluded_scanlators
ON mangas._id = excluded_scanlators.manga_id
AND chapters.scanlator = excluded_scanlators.scanlator
WHERE favorite = 1
AND date_fetch > date_added
WHERE favorite_at IS NOT NULL
AND date_fetch > favorite_at
ORDER BY date_fetch DESC;
getRecentUpdates:
@@ -19,11 +19,10 @@ import java.io.Serializable as JavaSerializable
data class Manga(
val id: Long,
val source: Long,
val favorite: Boolean,
val lastUpdate: Long,
val nextUpdate: Long,
val fetchInterval: Int,
val dateAdded: Long,
val favoriteAt: Long?,
val viewerFlags: Long,
val chapterFlags: Long,
val coverLastModified: Long,
@@ -37,11 +36,13 @@ data class Manga(
val thumbnailUrl: String?,
val updateStrategy: UpdateStrategy,
val initialized: Boolean,
val favoriteModifiedAt: Long?,
val notes: String,
val memo: JsonObject,
) : JavaSerializable {
val favorite: Boolean
get() = favoriteAt != null
val expectedNextUpdate: Instant?
get() = nextUpdate
.takeIf { status != SManga.COMPLETED.toLong() }
@@ -115,11 +116,10 @@ data class Manga(
url = "",
title = "",
source = -1L,
favorite = false,
lastUpdate = 0L,
nextUpdate = 0L,
fetchInterval = 0,
dateAdded = 0L,
favoriteAt = null,
viewerFlags = 0L,
chapterFlags = 0L,
coverLastModified = 0L,
@@ -131,7 +131,6 @@ data class Manga(
thumbnailUrl = null,
updateStrategy = UpdateStrategy.ALWAYS_UPDATE,
initialized = false,
favoriteModifiedAt = null,
notes = "",
memo = JsonObject.EMPTY,
)
@@ -3,11 +3,10 @@ package tachiyomi.domain.manga.model
import mihon.domain.common.PartialUpdate
class MangaUpdate(val id: Long, block: MangaUpdate.() -> Unit) : PartialUpdate() {
var favorite: Boolean? by field(null)
var favoriteAt: Long? by field(null)
var lastUpdate: Long? by field(null)
var nextUpdate: Long? by field(null)
var fetchInterval: Int? by field(null)
var dateAdded: Long? by field(null)
var viewerFlags: Long? by field(null)
var chapterFlags: Long? by field(null)
var coverLastModified: Long? by field(null)