diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3d03b7935..f86732df0 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -27,7 +27,7 @@ android { defaultConfig { applicationId = "eu.kanade.tachiyomi.sy" - versionCode = 54 + versionCode = 55 versionName = "1.9.3" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt index a70121a8c..ed42a75ad 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt @@ -20,6 +20,7 @@ data class BackupChapter( // chapterNumber is called number is 1.x @ProtoNumber(9) var chapterNumber: Float = 0F, @ProtoNumber(10) var sourceOrder: Long = 0, + @ProtoNumber(11) var lastModifiedAt: Long = 0, ) { fun toChapterImpl(): Chapter { return Chapter.create().copy( @@ -33,11 +34,12 @@ data class BackupChapter( dateFetch = this@BackupChapter.dateFetch, dateUpload = this@BackupChapter.dateUpload, sourceOrder = this@BackupChapter.sourceOrder, + lastModifiedAt = this@BackupChapter.lastModifiedAt, ) } } -val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlator: String?, read: Boolean, bookmark: Boolean, lastPageRead: Long, chapterNumber: Float, source_order: Long, dateFetch: Long, dateUpload: Long -> +val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlator: String?, read: Boolean, bookmark: Boolean, lastPageRead: Long, chapterNumber: Float, source_order: Long, dateFetch: Long, dateUpload: Long, lastModifiedAt: Long -> BackupChapter( url = url, name = name, @@ -49,5 +51,6 @@ val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlat dateFetch = dateFetch, dateUpload = dateUpload, sourceOrder = source_order, + lastModifiedAt = lastModifiedAt, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt index 06ca854c3..d642cdee4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt @@ -41,6 +41,8 @@ data class BackupManga( @ProtoNumber(103) var viewer_flags: Int? = null, @ProtoNumber(104) var history: List = emptyList(), @ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE, + @ProtoNumber(106) var lastModifiedAt: Long = 0, + @ProtoNumber(107) var favoriteModifiedAt: Long? = 0, // SY specific values @ProtoNumber(600) var mergedMangaReferences: List = emptyList(), @@ -76,6 +78,7 @@ data class BackupManga( viewerFlags = (this@BackupManga.viewer_flags ?: this@BackupManga.viewer).toLong(), chapterFlags = this@BackupManga.chapterFlags.toLong(), updateStrategy = this@BackupManga.updateStrategy, + lastModifiedAt = this@BackupManga.lastModifiedAt, filteredScanlators = this@BackupManga.filtered_scanlators?.let(listOfStringsAndAdapter::decode), ) } @@ -135,6 +138,8 @@ data class BackupManga( viewer_flags = manga.viewerFlags.toInt(), chapterFlags = manga.chapterFlags.toInt(), updateStrategy = manga.updateStrategy, + lastModifiedAt = manga.lastModifiedAt, + favoriteModifiedAt = manga.favoriteModifiedAt, // SY --> filtered_scanlators = manga.filteredScanlators?.let(listOfStringsAndAdapter::encode), ).also { backupManga -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt index 8ca265d6b..be1d72b08 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt @@ -19,6 +19,8 @@ interface Chapter : SChapter, Serializable { var date_fetch: Long var source_order: Int + + var last_modified: Long } fun Chapter.toDomainChapter(): DomainChapter? { @@ -36,5 +38,6 @@ fun Chapter.toDomainChapter(): DomainChapter? { dateUpload = date_upload, chapterNumber = chapter_number, scanlator = scanlator, + lastModifiedAt = last_modified, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt index a1a2d3f55..58ba41dec 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt @@ -26,6 +26,8 @@ class ChapterImpl : Chapter { override var source_order: Int = 0 + override var last_modified: Long = 0 + override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || javaClass != other.javaClass) return false diff --git a/app/src/main/java/exh/eh/EHentaiUpdateHelper.kt b/app/src/main/java/exh/eh/EHentaiUpdateHelper.kt index 00c1ff9ea..43437d28e 100644 --- a/app/src/main/java/exh/eh/EHentaiUpdateHelper.kt +++ b/app/src/main/java/exh/eh/EHentaiUpdateHelper.kt @@ -246,6 +246,7 @@ class EHentaiUpdateHelper(context: Context) { chapterNumber = -1F, scanlator = null, sourceOrder = -1, + lastModifiedAt = 0, ) } } diff --git a/data/src/main/java/tachiyomi/data/LibraryQuery.kt b/data/src/main/java/tachiyomi/data/LibraryQuery.kt index eeb8a2396..33fc08c11 100644 --- a/data/src/main/java/tachiyomi/data/LibraryQuery.kt +++ b/data/src/main/java/tachiyomi/data/LibraryQuery.kt @@ -30,13 +30,15 @@ private val mapper = { cursor: SqlCursor -> filtered_scanlators = cursor.getString(18)?.let(listOfStringsAndAdapter::decode), update_strategy = updateStrategyAdapter.decode(cursor.getLong(19)!!), calculate_interval = cursor.getLong(20)!!, - totalCount = cursor.getLong(21)!!, - readCount = cursor.getLong(22)!!, - latestUpload = cursor.getLong(23)!!, - chapterFetchedAt = cursor.getLong(24)!!, - lastRead = cursor.getLong(25)!!, - bookmarkCount = cursor.getLong(26)!!, - category = cursor.getLong(27)!!, + last_modified_at = cursor.getLong(21)!!, + favorite_modified_at = cursor.getLong(22)!!, + totalCount = cursor.getLong(23)!!, + readCount = cursor.getLong(24)!!, + latestUpload = cursor.getLong(25)!!, + chapterFetchedAt = cursor.getLong(26)!!, + lastRead = cursor.getLong(27)!!, + bookmarkCount = cursor.getLong(28)!!, + category = cursor.getLong(29)!!, ) } diff --git a/data/src/main/java/tachiyomi/data/chapter/ChapterMapper.kt b/data/src/main/java/tachiyomi/data/chapter/ChapterMapper.kt index 7b1888205..b91ccd7b4 100644 --- a/data/src/main/java/tachiyomi/data/chapter/ChapterMapper.kt +++ b/data/src/main/java/tachiyomi/data/chapter/ChapterMapper.kt @@ -2,8 +2,8 @@ package tachiyomi.data.chapter import tachiyomi.domain.chapter.model.Chapter -val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Float, Long, Long, Long) -> Chapter = - { id, mangaId, url, name, scanlator, read, bookmark, lastPageRead, chapterNumber, sourceOrder, dateFetch, dateUpload -> +val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Float, Long, Long, Long, Long) -> Chapter = + { id, mangaId, url, name, scanlator, read, bookmark, lastPageRead, chapterNumber, sourceOrder, dateFetch, dateUpload, lastModifiedAt -> Chapter( id = id, mangaId = mangaId, @@ -17,5 +17,6 @@ val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, dateUpload = dateUpload, chapterNumber = chapterNumber, scanlator = scanlator, + lastModifiedAt = lastModifiedAt, ) } diff --git a/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt b/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt index b40c98422..792604468 100644 --- a/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt +++ b/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt @@ -5,8 +5,8 @@ import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.manga.model.Manga import tachiyomi.view.LibraryView -val mangaMapper: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, List?, UpdateStrategy, Long) -> Manga = - { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, filteredScanlators, updateStrategy, calculateInterval -> +val mangaMapper: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, List?, UpdateStrategy, Long, Long, Long?) -> Manga = + { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, filteredScanlators, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt -> Manga( id = id, source = source, @@ -33,11 +33,13 @@ val mangaMapper: (Long, Long, String, String?, String?, String?, List?, // SY --> filteredScanlators = filteredScanlators, // SY <-- + lastModifiedAt = lastModifiedAt, + favoriteModifiedAt = favoriteModifiedAt, ) } -val libraryManga: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, List?, UpdateStrategy, Long, Long, Long, Long, Long, Long, Long, Long) -> LibraryManga = - { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, filteredScanlators, updateStrategy, calculateInterval, totalCount, readCount, latestUpload, chapterFetchedAt, lastRead, bookmarkCount, category -> +val libraryManga: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, List?, UpdateStrategy, Long, Long, Long?, Long, Long, Long, Long, Long, Long, Long) -> LibraryManga = + { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, filteredScanlators, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt, totalCount, readCount, latestUpload, chapterFetchedAt, lastRead, bookmarkCount, category -> LibraryManga( manga = mangaMapper( id, @@ -63,6 +65,8 @@ val libraryManga: (Long, Long, String, String?, String?, String?, List?, // SY <-- updateStrategy, calculateInterval, + lastModifiedAt, + favoriteModifiedAt, ), category = category, totalChapters = totalCount, @@ -98,6 +102,8 @@ val libraryViewMapper: (LibraryView) -> LibraryManga = { initialized = it.initialized, filteredScanlators = it.filtered_scanlators, calculateInterval = it.calculate_interval.toInt(), + lastModifiedAt = it.last_modified_at, + favoriteModifiedAt = it.favorite_modified_at, ), it.category, it.totalCount, diff --git a/data/src/main/sqldelight/tachiyomi/data/chapters.sq b/data/src/main/sqldelight/tachiyomi/data/chapters.sq index 310939645..ee986c9b5 100644 --- a/data/src/main/sqldelight/tachiyomi/data/chapters.sq +++ b/data/src/main/sqldelight/tachiyomi/data/chapters.sq @@ -11,6 +11,7 @@ CREATE TABLE chapters( 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, FOREIGN KEY(manga_id) REFERENCES mangas (_id) ON DELETE CASCADE ); @@ -18,6 +19,15 @@ CREATE TABLE chapters( CREATE INDEX chapters_manga_id_index ON chapters(manga_id); CREATE INDEX chapters_unread_by_manga_index ON chapters(manga_id, read) WHERE read = 0; +CREATE TRIGGER update_last_modified_at_chapters +AFTER UPDATE ON chapters +FOR EACH ROW +BEGIN + UPDATE chapters + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + getChapterById: SELECT * FROM chapters @@ -58,8 +68,8 @@ DELETE FROM chapters WHERE _id IN :chapterIds; insert: -INSERT INTO chapters(manga_id,url,name,scanlator,read,bookmark,last_page_read,chapter_number,source_order,date_fetch,date_upload) -VALUES (:mangaId,:url,:name,:scanlator,:read,:bookmark,:lastPageRead,:chapterNumber,:sourceOrder,:dateFetch,:dateUpload); +INSERT INTO chapters(manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order, date_fetch, date_upload, last_modified_at) +VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, strftime('%s', 'now')); update: UPDATE chapters diff --git a/data/src/main/sqldelight/tachiyomi/data/mangas.sq b/data/src/main/sqldelight/tachiyomi/data/mangas.sq index 33fdde0a8..204d69161 100644 --- a/data/src/main/sqldelight/tachiyomi/data/mangas.sq +++ b/data/src/main/sqldelight/tachiyomi/data/mangas.sq @@ -23,12 +23,31 @@ CREATE TABLE mangas( date_added INTEGER AS Long NOT NULL, filtered_scanlators TEXT AS List, update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0, - calculate_interval INTEGER DEFAULT 0 NOT NULL + calculate_interval INTEGER DEFAULT 0 NOT NULL, + last_modified_at INTEGER AS Long NOT NULL, + favorite_modified_at INTEGER AS Long ); CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1; CREATE INDEX mangas_url_index ON mangas(url); +CREATE TRIGGER update_favorite_modified_at_mangas +AFTER UPDATE OF favorite ON mangas +BEGIN + UPDATE mangas + SET favorite_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +CREATE TRIGGER update_last_modified_at_mangas +AFTER UPDATE ON mangas +FOR EACH ROW +BEGIN + UPDATE mangas + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + getMangaById: SELECT * FROM mangas @@ -53,6 +72,15 @@ WHERE favorite = 0 AND _id IN( SELECT chapters.manga_id FROM chapters WHERE read = 1 OR last_page_read != 0 ); +getAllManga: +SELECT * +FROM mangas; + +getMangasWithFavoriteTimestamp: +SELECT * +FROM mangas +WHERE favorite_modified_at IS NOT NULL; + getSourceIdWithFavoriteCount: SELECT source, @@ -99,8 +127,8 @@ WHERE favorite = 0 AND source IN :sourceIdsAND AND _id NOT IN ( ); insert: -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,filtered_scanlators,update_strategy,calculate_interval) -VALUES (:source,:url,:artist,:author,:description,:genre,:title,:status,:thumbnailUrl,:favorite,:lastUpdate,:nextUpdate,:initialized,:viewerFlags,:chapterFlags,:coverLastModified,:dateAdded,:filteredScanlators,:updateStrategy,:calculateInterval); +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,filtered_scanlators,update_strategy,calculate_interval, last_modified_at) +VALUES (:source,:url,:artist,:author,:description,:genre,:title,:status,:thumbnailUrl,:favorite,:lastUpdate,:nextUpdate,:initialized,:viewerFlags,:chapterFlags,:coverLastModified,:dateAdded,:filteredScanlators,:updateStrategy,:calculateInterval, strftime('%s', 'now')); update: UPDATE mangas SET diff --git a/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq b/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq index a97c9d3ca..c10387a6a 100644 --- a/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq +++ b/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq @@ -2,15 +2,25 @@ 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, FOREIGN KEY(category_id) REFERENCES categories (_id) ON DELETE CASCADE, FOREIGN KEY(manga_id) REFERENCES mangas (_id) ON DELETE CASCADE ); +CREATE TRIGGER update_last_modified_at_mangas_categories +AFTER UPDATE ON mangas_categories +FOR EACH ROW +BEGIN + UPDATE mangas_categories + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + insert: -INSERT INTO mangas_categories(manga_id, category_id) -VALUES (:mangaId, :categoryId); +INSERT INTO mangas_categories(manga_id, category_id, last_modified_at) +VALUES (:mangaId, :categoryId, strftime('%s', 'now')); deleteMangaCategoryByMangaId: DELETE FROM mangas_categories diff --git a/data/src/main/sqldelight/tachiyomi/migrations/28.sqm b/data/src/main/sqldelight/tachiyomi/migrations/28.sqm new file mode 100644 index 000000000..b4d98546a --- /dev/null +++ b/data/src/main/sqldelight/tachiyomi/migrations/28.sqm @@ -0,0 +1,49 @@ +ALTER TABLE mangas ADD COLUMN last_modified_at INTEGER AS Long NOT NULL; +ALTER TABLE mangas ADD COLUMN favorite_modified_at INTEGER AS Long; +ALTER TABLE mangas_categories ADD COLUMN last_modified_at INTEGER AS Long NOT NULL; +ALTER TABLE chapters ADD COLUMN last_modified_at INTEGER AS Long NOT NULL; + +UPDATE mangas SET last_modified_at = strftime('%s', 'now'); +UPDATE mangas SET favorite_modified_at = strftime('%s', 'now') WHERE favorite = 1; +UPDATE mangas_categories SET last_modified_at = strftime('%s', 'now'); +UPDATE chapters 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 +AFTER UPDATE ON mangas +FOR EACH ROW +BEGIN + UPDATE mangas + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +DROP TRIGGER IF EXISTS update_favorite_modified_at_mangas; +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; + +DROP TRIGGER IF EXISTS update_last_modified_at_chapters; +CREATE TRIGGER update_last_modified_at_chapters +AFTER UPDATE ON chapters +FOR EACH ROW +BEGIN + UPDATE chapters + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +DROP TRIGGER IF EXISTS update_last_modified_at_mangas_categories; +CREATE TRIGGER update_last_modified_at_mangas_categories +AFTER UPDATE ON mangas_categories +FOR EACH ROW +BEGIN + UPDATE mangas_categories + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; \ No newline at end of file diff --git a/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt b/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt index e11ca6564..9adee3f1b 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt @@ -13,6 +13,7 @@ data class Chapter( val dateUpload: Long, val chapterNumber: Float, val scanlator: String?, + val lastModifiedAt: Long, ) { val isRecognizedNumber: Boolean get() = chapterNumber >= 0f @@ -31,6 +32,7 @@ data class Chapter( dateUpload = -1, chapterNumber = -1f, scanlator = null, + lastModifiedAt = 0, ) } } diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt index a95dbff8b..a4d3699f5 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt @@ -28,6 +28,8 @@ data class Manga( val thumbnailUrl: String?, val updateStrategy: UpdateStrategy, val initialized: Boolean, + val lastModifiedAt: Long, + val favoriteModifiedAt: Long?, // SY --> val filteredScanlators: List?, // SY <-- @@ -146,6 +148,8 @@ data class Manga( thumbnailUrl = null, updateStrategy = UpdateStrategy.ALWAYS_UPDATE, initialized = false, + lastModifiedAt = 0L, + favoriteModifiedAt = 0L, // SY --> filteredScanlators = null, // SY <--