diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 10d81746..17cac9e9 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -69,6 +69,7 @@ dependencies { // uncomment to test extensions directly // implementation(fileTree("lib/")) + implementation(kotlin("script-runtime")) } application { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt index 820ae833..f6a4b284 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/MangaAPI.kt @@ -19,6 +19,7 @@ import suwayomi.tachidesk.manga.controller.DownloadController import suwayomi.tachidesk.manga.controller.ExtensionController import suwayomi.tachidesk.manga.controller.MangaController import suwayomi.tachidesk.manga.controller.SourceController +import suwayomi.tachidesk.manga.controller.UpdateController object MangaAPI { fun defineEndpoints() { @@ -106,5 +107,9 @@ object MangaAPI { get("{mangaId}/chapter/{chapterIndex}", DownloadController::queueChapter) delete("{mangaId}/chapter/{chapterIndex}", DownloadController::unqueueChapter) } + + path("update") { + get("recentChapters", UpdateController::recentChapters) + } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt new file mode 100644 index 00000000..e464bab7 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/UpdateController.kt @@ -0,0 +1,23 @@ +package suwayomi.tachidesk.manga.controller + +import io.javalin.http.Context +import suwayomi.tachidesk.manga.impl.Chapter +import suwayomi.tachidesk.server.JavalinSetup.future + +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +object UpdateController { + /** get recently updated manga chapters */ + fun recentChapters(ctx: Context) { + ctx.future( + future { + Chapter.getRecentChapters() + } + ) + } +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt index 47d93099..a3af825b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt @@ -25,6 +25,7 @@ import suwayomi.tachidesk.manga.impl.util.getChapterDir import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass +import suwayomi.tachidesk.manga.model.dataclass.MangaChapterDataClass import suwayomi.tachidesk.manga.model.table.ChapterMetaTable import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.MangaTable @@ -68,6 +69,7 @@ object Chapter { } val chapterCount = chapterList.count() + var now = Instant.now().epochSecond transaction { chapterList.reversed().forEachIndexed { index, fetchedChapter -> @@ -81,6 +83,7 @@ object Chapter { it[scanlator] = fetchedChapter.scanlator it[sourceOrder] = index + 1 + it[fetchedAt] = now++ it[ChapterTable.manga] = mangaId } } else { @@ -137,6 +140,7 @@ object Chapter { dbChapter[ChapterTable.lastReadAt], chapterCount - index, + dbChapter[ChapterTable.fetchedAt], dbChapter[ChapterTable.isDownloaded], dbChapter[ChapterTable.pageCount], @@ -220,6 +224,7 @@ object Chapter { chapterEntry[ChapterTable.lastReadAt], chapterEntry[ChapterTable.sourceOrder], + chapterEntry[ChapterTable.fetchedAt], chapterEntry[ChapterTable.isDownloaded], pageCount, chapterCount.toInt(), @@ -314,4 +319,21 @@ object Chapter { } } } + + fun getRecentChapters(): List { + return transaction { + val x = (ChapterTable innerJoin MangaTable) +// .selectAll() + .select { (MangaTable.inLibrary eq true) and (ChapterTable.fetchedAt greater MangaTable.inLibraryAt) } + + println(x.count()) + + x.map { + MangaChapterDataClass( + MangaTable.toDataClass(it), + ChapterTable.toDataClass(it) + ) + } + } + } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt index f06ad742..6956225a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Library.kt @@ -16,6 +16,7 @@ import suwayomi.tachidesk.manga.impl.Manga.getManga import suwayomi.tachidesk.manga.model.table.CategoryMangaTable import suwayomi.tachidesk.manga.model.table.CategoryTable import suwayomi.tachidesk.manga.model.table.MangaTable +import java.time.Instant object Library { suspend fun addMangaToLibrary(mangaId: Int) { @@ -25,8 +26,9 @@ object Library { val defaultCategories = CategoryTable.select { CategoryTable.isDefault eq true }.toList() MangaTable.update({ MangaTable.id eq manga.id }) { - it[MangaTable.inLibrary] = true - it[MangaTable.defaultCategory] = defaultCategories.isEmpty() + it[inLibrary] = true + it[inLibraryAt] = Instant.now().epochSecond + it[defaultCategory] = defaultCategories.isEmpty() } defaultCategories.forEach { category -> diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt index 3b53f612..ac9098e5 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt @@ -61,6 +61,7 @@ object Manga { mangaEntry[MangaTable.genre].toGenreList(), MangaStatus.valueOf(mangaEntry[MangaTable.status]).name, mangaEntry[MangaTable.inLibrary], + mangaEntry[MangaTable.inLibraryAt], getSource(mangaEntry[MangaTable.sourceReference]), getMangaMetaMap(mangaId), mangaEntry[MangaTable.realUrl], @@ -121,6 +122,7 @@ object Manga { fetchedManga.genre.toGenreList(), MangaStatus.valueOf(fetchedManga.status).name, mangaEntry[MangaTable.inLibrary], + mangaEntry[MangaTable.inLibraryAt], getSource(mangaEntry[MangaTable.sourceReference]), getMangaMetaMap(mangaId), mangaEntry[MangaTable.realUrl], diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/MangaList.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/MangaList.kt index 9e89242f..e9f2b93d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/MangaList.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/MangaList.kt @@ -81,6 +81,7 @@ object MangaList { manga.genre.toGenreList(), MangaStatus.valueOf(manga.status).name, false, // It's a new manga entry + 0, meta = getMangaMetaMap(mangaId), realUrl = mangaEntry[MangaTable.realUrl], freshData = true @@ -103,6 +104,7 @@ object MangaList { mangaEntry[MangaTable.genre].toGenreList(), MangaStatus.valueOf(mangaEntry[MangaTable.status]).name, mangaEntry[MangaTable.inLibrary], + mangaEntry[MangaTable.inLibraryAt], meta = getMangaMetaMap(mangaId), realUrl = mangaEntry[MangaTable.realUrl], freshData = false diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/ChapterDataClass.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/ChapterDataClass.kt index 540cd989..36593f8f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/ChapterDataClass.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/ChapterDataClass.kt @@ -31,6 +31,9 @@ data class ChapterDataClass( /** this chapter's index, starts with 1 */ val index: Int, + /** the date we fist saw this chapter*/ + val fetchedAt: Long, + /** is chapter downloaded */ val downloaded: Boolean, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaChapterDataClass.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaChapterDataClass.kt new file mode 100644 index 00000000..cdd2b850 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaChapterDataClass.kt @@ -0,0 +1,13 @@ +package suwayomi.tachidesk.manga.model.dataclass + +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +data class MangaChapterDataClass( + val manga: MangaDataClass, + val chapter: ChapterDataClass, +) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaDataClass.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaDataClass.kt index e95d1bf0..0b3353ff 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaDataClass.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaDataClass.kt @@ -26,6 +26,7 @@ data class MangaDataClass( val genre: List = emptyList(), val status: String = MangaStatus.UNKNOWN.name, val inLibrary: Boolean = false, + val inLibraryAt: Long = 0, val source: SourceDataClass? = null, /** meta data for clients */ diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterTable.kt index cc098ddd..343a389e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterTable.kt @@ -25,6 +25,7 @@ object ChapterTable : IntIdTable() { val isBookmarked = bool("bookmark").default(false) val lastPageRead = integer("last_page_read").default(0) val lastReadAt = long("last_read_at").default(0) + val fetchedAt = long("fetched_at").default(0) val sourceOrder = integer("source_order") @@ -48,6 +49,7 @@ fun ChapterTable.toDataClass(chapterEntry: ResultRow) = chapterEntry[lastPageRead], chapterEntry[lastReadAt], chapterEntry[sourceOrder], + chapterEntry[fetchedAt], chapterEntry[isDownloaded], chapterEntry[pageCount], transaction { ChapterTable.select { manga eq chapterEntry[manga].value }.count().toInt() }, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaTable.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaTable.kt index 84f03fa5..b60f6433 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaTable.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaTable.kt @@ -31,6 +31,7 @@ object MangaTable : IntIdTable() { val inLibrary = bool("in_library").default(false) val defaultCategory = bool("default_category").default(true) + val inLibraryAt = long("in_library_at").default(0) // the [source] field name is used by some ancestor of IntIdTable val sourceReference = long("source") @@ -56,6 +57,7 @@ fun MangaTable.toDataClass(mangaEntry: ResultRow) = mangaEntry[genre].toGenreList(), Companion.valueOf(mangaEntry[status]).name, mangaEntry[inLibrary], + mangaEntry[inLibraryAt], meta = getMangaMetaMap(mangaEntry[id].value), realUrl = mangaEntry[realUrl], ) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0017_ChapterFetchedAt.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0017_ChapterFetchedAt.kt new file mode 100644 index 00000000..bfea9176 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0017_ChapterFetchedAt.kt @@ -0,0 +1,18 @@ +package suwayomi.tachidesk.server.database.migration + +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import de.neonew.exposed.migrations.helpers.AddColumnMigration + +@Suppress("ClassName", "unused") +class M0017_ChapterFetchedAt : AddColumnMigration( + "Chapter", + "fetched_at", + "BIGINT", + "0" +) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0018_MangaInLibraryAt.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0018_MangaInLibraryAt.kt new file mode 100644 index 00000000..076c3166 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0018_MangaInLibraryAt.kt @@ -0,0 +1,18 @@ +package suwayomi.tachidesk.server.database.migration + +/* + * Copyright (C) Contributors to the Suwayomi project + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import de.neonew.exposed.migrations.helpers.AddColumnMigration + +@Suppress("ClassName", "unused") +class M0018_MangaInLibraryAt : AddColumnMigration( + "Manga", + "in_library_at", + "BIGINT", + "0" +)