mirror of
https://github.com/mihonapp/mihon.git
synced 2026-10-16 13:48:09 -07:00
Rework database queries for the new schema (#4022)
* query - category Key every single-row statement on :id and list columns on one line. A lookup by primary key no longer needs LIMIT 1, and resetting every category's flags takes a value rather than a nullable nobody passed. Assisted-by: Claude:claude-opus-5 * query - chapter List columns in the table's order and name bind and mapper parameters after the columns they fill. Inserts pass their arguments by name, since reordering the columns put the upload and fetch dates side by side with the same type. With (manga_id, remote_url) unique, looking a chapter up by them no longer needs LIMIT 1. Available scanlators come back distinct instead of one row per chapter, which is all the only caller wanted. insert was insertReturningId without the RETURNING, so restore uses the latter and the former goes, along with the unused getChapterByUrl. Inserting skips a chapter that a sync running alongside stored first, rather than failing the whole batch and losing every new chapter of that update. Assisted-by: Claude:claude-opus-5 * query - excluded_scanlator An entry excludes a scanlator at most once now. Inserting one that is already excluded does nothing, so restoring a backup that lists one twice drops the repeat instead of failing the entry, and restore no longer reads the stored exclusions first to skip them itself. Saving an unchanged selection no longer runs an empty delete, which still made every chapter query run again. Assisted-by: Claude:claude-opus-5 * query - extension_store Take the upsert's new values from excluded and drop a WHERE its conflict target already implies. That also retires a `signing_key =: signingKey` that only parsed by accident. Assisted-by: Claude:claude-opus-5 * query - history Restoring an entry's history looked up the history row and, failing that, the chapter for every item in the backup, one query each. It now reads the entry's chapters and history once and matches them in memory, which leaves getHistoryByChapterUrlAndMangaId without callers. Creating a backup looked up the chapter of every history row one at a time and failed on a row whose chapter was gone. It reads the entry's chapters once and leaves such a row out. The upsert takes its new values from excluded, drops a trailing WHERE its conflict target already pins, and names its parameters after the columns they fill. Assisted-by: Claude:claude-opus-5 * query - manga List columns in the table's order and name bind and mapper parameters after the columns they fill, forwarding mapper arguments by name. With (source_id, remote_url) unique, looking an entry up by them no longer needs LIMIT 1, and fetching a network entry becomes a single upsert that refreshes an existing row only while it is outside the library, instead of an insert guarded by NOT EXISTS followed by an update of the same row. "Is anything read" checks use EXISTS rather than IN over a DISTINCT subquery, category membership drops a coalesce on a column that is never null, and the unused getAllManga goes. Applying the default chapter flags to the library is one transaction instead of one per entry, and restore checks backup entries against the stored urls as a set rather than a list. Assisted-by: Claude:claude-opus-5 * query - manga_category An entry belongs to a category at most once now. Inserting a pairing that already exists does nothing, so setting an entry's categories from a list that names one twice no longer fails, which restore could produce when two backup categories resolve to the same local one. Assisted-by: Claude:claude-opus-5 * query - manga_track List columns in the table's order and name bind and mapper parameters after the columns they fill. That retires the sync-era :syncId, :mediaId, :totalChapter and :trackingUrl. Assisted-by: Claude:claude-opus-5 * query - source List columns in the table's order and name the upsert's parameters after its columns. The upsert takes the conflicting row's values from excluded instead of rebinding them, and drops a WHERE its conflict target already implies. The call passes its two string arguments by name, since swapping them would still compile. Assisted-by: Claude:claude-opus-5 * query - historyView The latest read entry is read from the tables through the index on history's read time instead of aggregating the whole view for one row: ~50ms to under 1ms on a 114k chapter library. Assisted-by: Claude:claude-opus-5 * query - libraryView List the columns like the other queries. Assisted-by: Claude:claude-opus-5 * query - updatesView Drop a coalesce on category_id, which is never null, from the queries over updatesView, and the unused getRecentUpdates. The view definitions themselves are unchanged, so no migration is needed. The read and unread lookups become separate queries with the status written into each. SQLite only uses a partial index when the query's own condition matches it, which a bound parameter never does, so the unread lookup the widget follows walked nearly the whole fetch date index, ~60ms each time on a 114k chapter library and after every chapter or history write. With the status written in, it's served by the partial index over unread chapters, ~8ms. Assisted-by: Claude:claude-opus-5 * Query browse entries only while they are shown Each loaded entry kept a live query for as long as the screen existed, so every write to manga re-ran all of them. An entry now starts from the row the listing stored and queries only while shown. Assisted-by: Claude:claude-opus-5 * Read library selection categories from memory The change category dialog and category updates read each selected entry's categories from the loaded library instead of querying them one at a time. Assisted-by: Claude:claude-opus-5
This commit is contained in:
@@ -34,11 +34,12 @@ class RestoreRepositoryImpl(
|
||||
private val trackRepository: TrackRepository,
|
||||
) : RestoreRepository {
|
||||
|
||||
override suspend fun getMangaUrlsBySourceId(): Map<Long, List<String>> {
|
||||
override suspend fun getMangaUrlsBySourceId(): Map<Long, Set<String>> {
|
||||
return database.mangaQueries
|
||||
.getAllMangaSourceAndUrl()
|
||||
.awaitAsList()
|
||||
.groupBy({ it.source_id }, { it.remote_url })
|
||||
.mapValues { it.value.toSet() }
|
||||
}
|
||||
|
||||
override suspend fun restoreManga(entries: List<RestoredManga>, update: suspend (Manga) -> MangaUpdate) {
|
||||
@@ -93,49 +94,49 @@ class RestoreRepositoryImpl(
|
||||
|
||||
private suspend fun updateManga(manga: Manga): Manga {
|
||||
database.mangaQueries.updateFromBackup(
|
||||
favoriteAt = manga.favoriteAt,
|
||||
artist = manga.artist,
|
||||
author = manga.author,
|
||||
description = manga.description,
|
||||
genre = manga.genre,
|
||||
title = manga.title,
|
||||
status = manga.status,
|
||||
thumbnailUrl = manga.thumbnailUrl,
|
||||
lastUpdate = manga.lastUpdate,
|
||||
initialized = manga.initialized,
|
||||
viewer = manga.viewerFlags,
|
||||
chapterFlags = manga.chapterFlags,
|
||||
coverLastModified = manga.coverLastModified,
|
||||
mangaId = manga.id,
|
||||
updateStrategy = manga.updateStrategy,
|
||||
notes = manga.notes,
|
||||
memo = manga.memo,
|
||||
userFavoriteAt = manga.favoriteAt,
|
||||
remoteArtist = manga.artist,
|
||||
remoteAuthor = manga.author,
|
||||
remoteDescription = manga.description,
|
||||
remoteGenre = manga.genre,
|
||||
remoteTitle = manga.title,
|
||||
remoteStatus = manga.status,
|
||||
remoteCover = manga.thumbnailUrl,
|
||||
stateChapterLastUpdate = manga.lastUpdate,
|
||||
stateInitialized = manga.initialized,
|
||||
userReaderFlags = manga.viewerFlags,
|
||||
userChapterFlags = manga.chapterFlags,
|
||||
stateCoverLastModified = manga.coverLastModified,
|
||||
id = manga.id,
|
||||
remoteUpdateStrategy = manga.updateStrategy,
|
||||
userNotes = manga.notes,
|
||||
remoteMemo = manga.memo,
|
||||
)
|
||||
return manga
|
||||
}
|
||||
|
||||
private suspend fun insertManga(manga: Manga): Long {
|
||||
return database.mangaQueries.insertReturningId(
|
||||
favoriteAt = manga.favoriteAt,
|
||||
source = manga.source,
|
||||
url = manga.url,
|
||||
artist = manga.artist,
|
||||
author = manga.author,
|
||||
description = manga.description,
|
||||
genre = manga.genre,
|
||||
title = manga.title,
|
||||
status = manga.status,
|
||||
thumbnailUrl = manga.thumbnailUrl,
|
||||
lastUpdate = manga.lastUpdate,
|
||||
nextUpdate = 0L,
|
||||
calculateInterval = 0L,
|
||||
initialized = manga.initialized,
|
||||
viewerFlags = manga.viewerFlags,
|
||||
chapterFlags = manga.chapterFlags,
|
||||
coverLastModified = manga.coverLastModified,
|
||||
updateStrategy = manga.updateStrategy,
|
||||
notes = manga.notes,
|
||||
memo = manga.memo,
|
||||
userFavoriteAt = manga.favoriteAt,
|
||||
sourceId = manga.source,
|
||||
remoteUrl = manga.url,
|
||||
remoteArtist = manga.artist,
|
||||
remoteAuthor = manga.author,
|
||||
remoteDescription = manga.description,
|
||||
remoteGenre = manga.genre,
|
||||
remoteTitle = manga.title,
|
||||
remoteStatus = manga.status,
|
||||
remoteCover = manga.thumbnailUrl,
|
||||
stateChapterLastUpdate = manga.lastUpdate,
|
||||
stateChapterNextUpdate = 0L,
|
||||
stateChapterFetchInterval = 0L,
|
||||
stateInitialized = manga.initialized,
|
||||
userReaderFlags = manga.viewerFlags,
|
||||
userChapterFlags = manga.chapterFlags,
|
||||
stateCoverLastModified = manga.coverLastModified,
|
||||
remoteUpdateStrategy = manga.updateStrategy,
|
||||
userNotes = manga.notes,
|
||||
remoteMemo = manga.memo,
|
||||
)
|
||||
.awaitAsOne()
|
||||
}
|
||||
@@ -183,28 +184,29 @@ class RestoreRepositoryImpl(
|
||||
.partition { it.id > 0 }
|
||||
|
||||
newChapters.forEach { chapter ->
|
||||
database.chapterQueries.insert(
|
||||
database.chapterQueries.insertReturningId(
|
||||
mangaId = chapter.mangaId,
|
||||
url = chapter.url,
|
||||
name = chapter.name,
|
||||
scanlator = chapter.scanlator,
|
||||
read = chapter.read,
|
||||
bookmark = chapter.bookmark,
|
||||
lastPageRead = chapter.lastPageRead,
|
||||
chapterNumber = chapter.chapterNumber,
|
||||
sourceOrder = chapter.sourceOrder,
|
||||
dateFetch = chapter.dateFetch,
|
||||
dateUpload = chapter.dateUpload,
|
||||
memo = chapter.memo,
|
||||
remoteUrl = chapter.url,
|
||||
remoteName = chapter.name,
|
||||
remoteScanlator = chapter.scanlator,
|
||||
userRead = chapter.read,
|
||||
userBookmark = chapter.bookmark,
|
||||
userLastPageRead = chapter.lastPageRead,
|
||||
remoteChapterNumber = chapter.chapterNumber,
|
||||
remoteOrder = chapter.sourceOrder,
|
||||
stateDateFetch = chapter.dateFetch,
|
||||
remoteDateUpload = chapter.dateUpload,
|
||||
remoteMemo = chapter.memo,
|
||||
)
|
||||
.awaitAsOneOrNull()
|
||||
}
|
||||
existingChapters.forEach { chapter ->
|
||||
database.chapterQueries.updateFromBackup(
|
||||
read = chapter.read,
|
||||
bookmark = chapter.bookmark,
|
||||
lastPageRead = chapter.lastPageRead,
|
||||
chapterId = chapter.id,
|
||||
memo = chapter.memo,
|
||||
userRead = chapter.read,
|
||||
userBookmark = chapter.bookmark,
|
||||
userLastPageRead = chapter.lastPageRead,
|
||||
id = chapter.id,
|
||||
remoteMemo = chapter.memo,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -213,28 +215,30 @@ class RestoreRepositoryImpl(
|
||||
this.copy(id = 0L, mangaId = 0L, dateFetch = 0L, dateUpload = 0L)
|
||||
|
||||
private suspend fun restoreHistory(manga: Manga, restoredHistory: List<RestoredHistory>) {
|
||||
if (restoredHistory.isEmpty()) return
|
||||
val chapterIdsByUrl = chapterRepository.getChapterByMangaId(manga.id).associate { it.url to it.id }
|
||||
val dbHistoryByChapterId = database.historyQueries
|
||||
.getHistoryByMangaId(manga.id)
|
||||
.awaitAsList()
|
||||
.associateBy { it.chapter_id }
|
||||
|
||||
val toUpdate = restoredHistory
|
||||
.groupBy { it.chapterUrl }
|
||||
.mapNotNull { (chapterUrl, copies) ->
|
||||
// Chapter doesn't exist; skip
|
||||
.filter { it.chapterUrl in chapterIdsByUrl }
|
||||
// A backup of a library that still had duplicate chapters carries a history entry for
|
||||
// each copy; they are one chapter now, read as late as any copy and for as long as all
|
||||
.groupBy { chapterIdsByUrl.getValue(it.chapterUrl) }
|
||||
.map { (chapterId, copies) ->
|
||||
val readAt = copies.maxOf { it.readAt?.time ?: 0L }
|
||||
val readDuration = copies.sumOf { it.readDuration }
|
||||
val dbHistory = database.historyQueries
|
||||
.getHistoryByChapterUrlAndMangaId(chapterUrl, manga.id)
|
||||
.awaitAsOneOrNull()
|
||||
|
||||
if (dbHistory == null) {
|
||||
val chapter = database.chapterQueries
|
||||
.getChapterByUrlAndMangaId(chapterUrl, manga.id)
|
||||
.awaitAsOneOrNull()
|
||||
// Chapter doesn't exist; skip
|
||||
?: return@mapNotNull null
|
||||
val dbHistory = dbHistoryByChapterId[chapterId]
|
||||
// New history entry
|
||||
return@mapNotNull Triple(chapter.id, Date(readAt), readDuration)
|
||||
}
|
||||
?: return@map Triple(chapterId, Date(readAt), readDuration)
|
||||
|
||||
// Update history entry
|
||||
// Update history entry. 0 is kept rather than written as NULL, since it marks history
|
||||
// the user removed.
|
||||
Triple(
|
||||
dbHistory.chapter_id,
|
||||
chapterId,
|
||||
Date(max(readAt, dbHistory.read_at?.time ?: 0L)),
|
||||
max(readDuration, dbHistory.read_duration) - dbHistory.read_duration,
|
||||
)
|
||||
@@ -271,15 +275,15 @@ class RestoreRepositoryImpl(
|
||||
existingTracks.forEach { track ->
|
||||
database.manga_trackQueries.update(
|
||||
mangaId = track.mangaId,
|
||||
syncId = track.trackerId,
|
||||
mediaId = track.remoteId,
|
||||
trackerId = track.trackerId,
|
||||
remoteId = track.remoteId,
|
||||
libraryId = track.libraryId,
|
||||
title = track.title,
|
||||
lastChapterRead = track.lastChapterRead,
|
||||
totalChapter = track.totalChapters,
|
||||
totalChapters = track.totalChapters,
|
||||
status = track.status,
|
||||
score = track.score,
|
||||
trackingUrl = track.remoteUrl,
|
||||
remoteUrl = track.remoteUrl,
|
||||
startDate = track.startDate,
|
||||
finishDate = track.finishDate,
|
||||
`private` = track.private,
|
||||
@@ -289,11 +293,6 @@ class RestoreRepositoryImpl(
|
||||
}
|
||||
|
||||
private suspend fun restoreExcludedScanlators(manga: Manga, excludedScanlators: List<String>) {
|
||||
if (excludedScanlators.isEmpty()) return
|
||||
val existingExcludedScanlators = database.excluded_scanlatorQueries
|
||||
.getExcludedScanlatorsByMangaId(manga.id)
|
||||
.awaitAsList()
|
||||
val toInsert = excludedScanlators.filter { it !in existingExcludedScanlators }
|
||||
toInsert.forEach { database.excluded_scanlatorQueries.insert(manga.id, it) }
|
||||
excludedScanlators.forEach { database.excluded_scanlatorQueries.insert(manga.id, it) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,14 +61,14 @@ class CategoryRepositoryImpl(
|
||||
}
|
||||
|
||||
override suspend fun updateName(categoryId: Long, name: String) {
|
||||
database.categoryQueries.updateName(name = name, categoryId = categoryId)
|
||||
database.categoryQueries.updateName(name = name, id = categoryId)
|
||||
}
|
||||
|
||||
override suspend fun updateFlags(categoryId: Long, flags: Long) {
|
||||
database.categoryQueries.updateFlags(flags = flags, categoryId = categoryId)
|
||||
database.categoryQueries.updateFlags(flags = flags, id = categoryId)
|
||||
}
|
||||
|
||||
override suspend fun updateAllFlags(flags: Long?) {
|
||||
override suspend fun updateAllFlags(flags: Long) {
|
||||
database.categoryQueries.updateAllFlags(flags = flags)
|
||||
}
|
||||
|
||||
@@ -77,16 +77,16 @@ class CategoryRepositoryImpl(
|
||||
val current = database.categoryQueries.getUserCategoryIds().awaitAsList()
|
||||
val ids = orderedIds.filter { it in current } + current.filterNot { it in orderedIds }
|
||||
ids.forEachIndexed { index, categoryId ->
|
||||
database.categoryQueries.updateOrder(order = -index - 2L, categoryId = categoryId)
|
||||
database.categoryQueries.updateOrder(order = -index - 2L, id = categoryId)
|
||||
}
|
||||
ids.forEachIndexed { index, categoryId ->
|
||||
database.categoryQueries.updateOrder(order = index.toLong(), categoryId = categoryId)
|
||||
database.categoryQueries.updateOrder(order = index.toLong(), id = categoryId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun delete(categoryId: Long) {
|
||||
database.categoryQueries.delete(categoryId = categoryId)
|
||||
database.categoryQueries.delete(id = categoryId)
|
||||
}
|
||||
|
||||
private fun mapCategory(
|
||||
|
||||
@@ -57,30 +57,30 @@ class ChapterRepositoryImpl(
|
||||
val stored = added.filter { existing.add(it.mangaId to it.url) }.map { chapter ->
|
||||
val chapterId = database.chapterQueries.insertReturningId(
|
||||
mangaId = chapter.mangaId,
|
||||
url = chapter.url,
|
||||
name = chapter.name,
|
||||
scanlator = chapter.scanlator,
|
||||
read = chapter.read,
|
||||
bookmark = chapter.bookmark,
|
||||
lastPageRead = chapter.lastPageRead,
|
||||
chapterNumber = chapter.chapterNumber,
|
||||
sourceOrder = chapter.sourceOrder,
|
||||
dateFetch = chapter.dateFetch,
|
||||
dateUpload = chapter.dateUpload,
|
||||
memo = chapter.memo,
|
||||
remoteUrl = chapter.url,
|
||||
remoteName = chapter.name,
|
||||
remoteScanlator = chapter.scanlator,
|
||||
userRead = chapter.read,
|
||||
userBookmark = chapter.bookmark,
|
||||
userLastPageRead = chapter.lastPageRead,
|
||||
remoteChapterNumber = chapter.chapterNumber,
|
||||
remoteOrder = chapter.sourceOrder,
|
||||
stateDateFetch = chapter.dateFetch,
|
||||
remoteDateUpload = chapter.dateUpload,
|
||||
remoteMemo = chapter.memo,
|
||||
)
|
||||
.awaitAsOne()
|
||||
chapter.copy(id = chapterId)
|
||||
}
|
||||
updated.forEach { chapterUpdate ->
|
||||
database.chapterQueries.updateRemote(
|
||||
name = chapterUpdate.name,
|
||||
scanlator = chapterUpdate.scanlator,
|
||||
chapterNumber = chapterUpdate.chapterNumber,
|
||||
sourceOrder = chapterUpdate.sourceOrder,
|
||||
dateUpload = chapterUpdate.dateUpload,
|
||||
chapterId = chapterUpdate.id,
|
||||
memo = chapterUpdate.memo,
|
||||
remoteName = chapterUpdate.name,
|
||||
remoteScanlator = chapterUpdate.scanlator,
|
||||
remoteChapterNumber = chapterUpdate.chapterNumber,
|
||||
remoteOrder = chapterUpdate.sourceOrder,
|
||||
remoteDateUpload = chapterUpdate.dateUpload,
|
||||
id = chapterUpdate.id,
|
||||
remoteMemo = chapterUpdate.memo,
|
||||
)
|
||||
}
|
||||
stored
|
||||
@@ -91,11 +91,11 @@ class ChapterRepositoryImpl(
|
||||
database.transaction {
|
||||
chapterUpdates.forEach { chapterUpdate ->
|
||||
database.chapterQueries.update(
|
||||
read = chapterUpdate.read,
|
||||
bookmark = chapterUpdate.bookmark,
|
||||
lastPageRead = chapterUpdate.lastPageRead,
|
||||
dateFetch = chapterUpdate.dateFetch,
|
||||
chapterId = chapterUpdate.id,
|
||||
userRead = chapterUpdate.read,
|
||||
userBookmark = chapterUpdate.bookmark,
|
||||
userLastPageRead = chapterUpdate.lastPageRead,
|
||||
stateDateFetch = chapterUpdate.dateFetch,
|
||||
id = chapterUpdate.id,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -147,38 +147,37 @@ class ChapterRepositoryImpl(
|
||||
|
||||
override suspend fun getChapterByUrlAndMangaId(url: String, mangaId: Long): Chapter? {
|
||||
return database.chapterQueries
|
||||
.getChapterByUrlAndMangaId(url, mangaId, ::mapChapter)
|
||||
.getChapterByUrlAndMangaId(mangaId = mangaId, remoteUrl = url, mapper = ::mapChapter)
|
||||
.awaitAsOneOrNull()
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun mapChapter(
|
||||
id: Long,
|
||||
mangaId: Long,
|
||||
url: String,
|
||||
name: String,
|
||||
scanlator: String?,
|
||||
read: Boolean,
|
||||
bookmark: Boolean,
|
||||
lastPageRead: Long,
|
||||
chapterNumber: Double,
|
||||
sourceOrder: Long,
|
||||
dateFetch: Long,
|
||||
dateUpload: Long,
|
||||
memo: JsonObject,
|
||||
remoteUrl: String,
|
||||
remoteName: String,
|
||||
remoteScanlator: String?,
|
||||
remoteChapterNumber: Double,
|
||||
remoteDateUpload: Long,
|
||||
remoteOrder: Long,
|
||||
remoteMemo: JsonObject,
|
||||
userRead: Boolean,
|
||||
userBookmark: Boolean,
|
||||
userLastPageRead: Long,
|
||||
stateDateFetch: Long,
|
||||
): Chapter = Chapter(
|
||||
id = id,
|
||||
mangaId = mangaId,
|
||||
read = read,
|
||||
bookmark = bookmark,
|
||||
lastPageRead = lastPageRead,
|
||||
dateFetch = dateFetch,
|
||||
sourceOrder = sourceOrder,
|
||||
url = url,
|
||||
name = name,
|
||||
dateUpload = dateUpload,
|
||||
chapterNumber = chapterNumber,
|
||||
scanlator = scanlator,
|
||||
memo = memo,
|
||||
read = userRead,
|
||||
bookmark = userBookmark,
|
||||
lastPageRead = userLastPageRead,
|
||||
dateFetch = stateDateFetch,
|
||||
sourceOrder = remoteOrder,
|
||||
url = remoteUrl,
|
||||
name = remoteName,
|
||||
dateUpload = remoteDateUpload,
|
||||
chapterNumber = remoteChapterNumber,
|
||||
scanlator = remoteScanlator,
|
||||
memo = remoteMemo,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,72 +9,72 @@ import tachiyomi.domain.manga.model.MangaWithChapterCount
|
||||
object MangaMapper {
|
||||
fun mapManga(
|
||||
id: Long,
|
||||
source: Long,
|
||||
url: String,
|
||||
artist: String?,
|
||||
author: String?,
|
||||
description: String?,
|
||||
genre: List<String>?,
|
||||
title: String,
|
||||
status: Long,
|
||||
thumbnailUrl: String?,
|
||||
lastUpdate: Long?,
|
||||
nextUpdate: Long?,
|
||||
initialized: Boolean,
|
||||
viewerFlags: Long,
|
||||
chapterFlags: Long,
|
||||
coverLastModified: Long,
|
||||
updateStrategy: UpdateStrategy,
|
||||
calculateInterval: Long,
|
||||
notes: String,
|
||||
memo: JsonObject,
|
||||
favoriteAt: Long?,
|
||||
sourceId: Long,
|
||||
remoteUrl: String,
|
||||
remoteTitle: String,
|
||||
remoteAuthor: String?,
|
||||
remoteArtist: String?,
|
||||
remoteDescription: String?,
|
||||
remoteGenre: List<String>?,
|
||||
remoteStatus: Long,
|
||||
remoteCover: String?,
|
||||
remoteUpdateStrategy: UpdateStrategy,
|
||||
remoteMemo: JsonObject,
|
||||
userFavoriteAt: Long?,
|
||||
userNotes: String,
|
||||
userReaderFlags: Long,
|
||||
userChapterFlags: Long,
|
||||
stateChapterLastUpdate: Long?,
|
||||
stateChapterNextUpdate: Long?,
|
||||
stateChapterFetchInterval: Long,
|
||||
stateCoverLastModified: Long,
|
||||
stateInitialized: Boolean,
|
||||
): Manga = Manga(
|
||||
id = id,
|
||||
source = source,
|
||||
lastUpdate = lastUpdate ?: 0,
|
||||
nextUpdate = nextUpdate ?: 0,
|
||||
fetchInterval = calculateInterval.toInt(),
|
||||
favoriteAt = favoriteAt,
|
||||
viewerFlags = viewerFlags,
|
||||
chapterFlags = chapterFlags,
|
||||
coverLastModified = coverLastModified,
|
||||
url = url,
|
||||
title = title,
|
||||
artist = artist,
|
||||
author = author,
|
||||
description = description,
|
||||
genre = genre,
|
||||
status = status,
|
||||
thumbnailUrl = thumbnailUrl,
|
||||
updateStrategy = updateStrategy,
|
||||
initialized = initialized,
|
||||
notes = notes,
|
||||
memo = memo,
|
||||
source = sourceId,
|
||||
favoriteAt = userFavoriteAt,
|
||||
lastUpdate = stateChapterLastUpdate ?: 0,
|
||||
nextUpdate = stateChapterNextUpdate ?: 0,
|
||||
fetchInterval = stateChapterFetchInterval.toInt(),
|
||||
viewerFlags = userReaderFlags,
|
||||
chapterFlags = userChapterFlags,
|
||||
coverLastModified = stateCoverLastModified,
|
||||
url = remoteUrl,
|
||||
title = remoteTitle,
|
||||
artist = remoteArtist,
|
||||
author = remoteAuthor,
|
||||
description = remoteDescription,
|
||||
genre = remoteGenre,
|
||||
status = remoteStatus,
|
||||
thumbnailUrl = remoteCover,
|
||||
updateStrategy = remoteUpdateStrategy,
|
||||
initialized = stateInitialized,
|
||||
notes = userNotes,
|
||||
memo = remoteMemo,
|
||||
)
|
||||
|
||||
fun mapLibraryManga(
|
||||
id: Long,
|
||||
source: Long,
|
||||
url: String,
|
||||
artist: String?,
|
||||
author: String?,
|
||||
description: String?,
|
||||
genre: List<String>?,
|
||||
title: String,
|
||||
status: Long,
|
||||
thumbnailUrl: String?,
|
||||
lastUpdate: Long?,
|
||||
nextUpdate: Long?,
|
||||
initialized: Boolean,
|
||||
viewerFlags: Long,
|
||||
chapterFlags: Long,
|
||||
coverLastModified: Long,
|
||||
updateStrategy: UpdateStrategy,
|
||||
calculateInterval: Long,
|
||||
notes: String,
|
||||
memo: JsonObject,
|
||||
favoriteAt: Long?,
|
||||
sourceId: Long,
|
||||
remoteUrl: String,
|
||||
remoteTitle: String,
|
||||
remoteAuthor: String?,
|
||||
remoteArtist: String?,
|
||||
remoteDescription: String?,
|
||||
remoteGenre: List<String>?,
|
||||
remoteStatus: Long,
|
||||
remoteCover: String?,
|
||||
remoteUpdateStrategy: UpdateStrategy,
|
||||
remoteMemo: JsonObject,
|
||||
userFavoriteAt: Long,
|
||||
userNotes: String,
|
||||
userReaderFlags: Long,
|
||||
userChapterFlags: Long,
|
||||
stateChapterLastUpdate: Long?,
|
||||
stateChapterNextUpdate: Long?,
|
||||
stateChapterFetchInterval: Long,
|
||||
stateCoverLastModified: Long,
|
||||
stateInitialized: Boolean,
|
||||
totalCount: Long,
|
||||
readCount: Double,
|
||||
latestUpload: Long,
|
||||
@@ -85,26 +85,26 @@ object MangaMapper {
|
||||
): LibraryManga = LibraryManga(
|
||||
manga = mapManga(
|
||||
id = id,
|
||||
source = source,
|
||||
url = url,
|
||||
artist = artist,
|
||||
author = author,
|
||||
description = description,
|
||||
genre = genre,
|
||||
title = title,
|
||||
status = status,
|
||||
thumbnailUrl = thumbnailUrl,
|
||||
lastUpdate = lastUpdate,
|
||||
nextUpdate = nextUpdate,
|
||||
initialized = initialized,
|
||||
viewerFlags = viewerFlags,
|
||||
chapterFlags = chapterFlags,
|
||||
coverLastModified = coverLastModified,
|
||||
updateStrategy = updateStrategy,
|
||||
calculateInterval = calculateInterval,
|
||||
notes = notes,
|
||||
memo = memo,
|
||||
favoriteAt = favoriteAt,
|
||||
sourceId = sourceId,
|
||||
remoteUrl = remoteUrl,
|
||||
remoteTitle = remoteTitle,
|
||||
remoteAuthor = remoteAuthor,
|
||||
remoteArtist = remoteArtist,
|
||||
remoteDescription = remoteDescription,
|
||||
remoteGenre = remoteGenre,
|
||||
remoteStatus = remoteStatus,
|
||||
remoteCover = remoteCover,
|
||||
remoteUpdateStrategy = remoteUpdateStrategy,
|
||||
remoteMemo = remoteMemo,
|
||||
userFavoriteAt = userFavoriteAt,
|
||||
userNotes = userNotes,
|
||||
userReaderFlags = userReaderFlags,
|
||||
userChapterFlags = userChapterFlags,
|
||||
stateChapterLastUpdate = stateChapterLastUpdate,
|
||||
stateChapterNextUpdate = stateChapterNextUpdate,
|
||||
stateChapterFetchInterval = stateChapterFetchInterval,
|
||||
stateCoverLastModified = stateCoverLastModified,
|
||||
stateInitialized = stateInitialized,
|
||||
),
|
||||
categories = categories.split(",").map { it.toLong() },
|
||||
totalChapters = totalCount,
|
||||
@@ -117,51 +117,51 @@ object MangaMapper {
|
||||
|
||||
fun mapMangaWithChapterCount(
|
||||
id: Long,
|
||||
source: Long,
|
||||
url: String,
|
||||
artist: String?,
|
||||
author: String?,
|
||||
description: String?,
|
||||
genre: List<String>?,
|
||||
title: String,
|
||||
status: Long,
|
||||
thumbnailUrl: String?,
|
||||
lastUpdate: Long?,
|
||||
nextUpdate: Long?,
|
||||
initialized: Boolean,
|
||||
viewerFlags: Long,
|
||||
chapterFlags: Long,
|
||||
coverLastModified: Long,
|
||||
updateStrategy: UpdateStrategy,
|
||||
calculateInterval: Long,
|
||||
notes: String,
|
||||
memo: JsonObject,
|
||||
favoriteAt: Long?,
|
||||
totalCount: Long,
|
||||
sourceId: Long,
|
||||
remoteUrl: String,
|
||||
remoteTitle: String,
|
||||
remoteAuthor: String?,
|
||||
remoteArtist: String?,
|
||||
remoteDescription: String?,
|
||||
remoteGenre: List<String>?,
|
||||
remoteStatus: Long,
|
||||
remoteCover: String?,
|
||||
remoteUpdateStrategy: UpdateStrategy,
|
||||
remoteMemo: JsonObject,
|
||||
userFavoriteAt: Long?,
|
||||
userNotes: String,
|
||||
userReaderFlags: Long,
|
||||
userChapterFlags: Long,
|
||||
stateChapterLastUpdate: Long?,
|
||||
stateChapterNextUpdate: Long?,
|
||||
stateChapterFetchInterval: Long,
|
||||
stateCoverLastModified: Long,
|
||||
stateInitialized: Boolean,
|
||||
chapterCount: Long,
|
||||
): MangaWithChapterCount = MangaWithChapterCount(
|
||||
manga = mapManga(
|
||||
id = id,
|
||||
source = source,
|
||||
url = url,
|
||||
artist = artist,
|
||||
author = author,
|
||||
description = description,
|
||||
genre = genre,
|
||||
title = title,
|
||||
status = status,
|
||||
thumbnailUrl = thumbnailUrl,
|
||||
lastUpdate = lastUpdate,
|
||||
nextUpdate = nextUpdate,
|
||||
initialized = initialized,
|
||||
viewerFlags = viewerFlags,
|
||||
chapterFlags = chapterFlags,
|
||||
coverLastModified = coverLastModified,
|
||||
updateStrategy = updateStrategy,
|
||||
calculateInterval = calculateInterval,
|
||||
notes = notes,
|
||||
memo = memo,
|
||||
favoriteAt = favoriteAt,
|
||||
sourceId = sourceId,
|
||||
remoteUrl = remoteUrl,
|
||||
remoteTitle = remoteTitle,
|
||||
remoteAuthor = remoteAuthor,
|
||||
remoteArtist = remoteArtist,
|
||||
remoteDescription = remoteDescription,
|
||||
remoteGenre = remoteGenre,
|
||||
remoteStatus = remoteStatus,
|
||||
remoteCover = remoteCover,
|
||||
remoteUpdateStrategy = remoteUpdateStrategy,
|
||||
remoteMemo = remoteMemo,
|
||||
userFavoriteAt = userFavoriteAt,
|
||||
userNotes = userNotes,
|
||||
userReaderFlags = userReaderFlags,
|
||||
userChapterFlags = userChapterFlags,
|
||||
stateChapterLastUpdate = stateChapterLastUpdate,
|
||||
stateChapterNextUpdate = stateChapterNextUpdate,
|
||||
stateChapterFetchInterval = stateChapterFetchInterval,
|
||||
stateCoverLastModified = stateCoverLastModified,
|
||||
stateInitialized = stateInitialized,
|
||||
),
|
||||
chapterCount = totalCount,
|
||||
chapterCount = chapterCount,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -48,13 +48,13 @@ class MangaRepositoryImpl(
|
||||
|
||||
override suspend fun getMangaByUrlAndSourceId(url: String, sourceId: Long): Manga? {
|
||||
return database.mangaQueries
|
||||
.getMangaByUrlAndSource(url, sourceId, MangaMapper::mapManga)
|
||||
.getMangaByUrlAndSource(sourceId = sourceId, remoteUrl = url, mapper = MangaMapper::mapManga)
|
||||
.awaitAsOneOrNull()
|
||||
}
|
||||
|
||||
override fun getMangaByUrlAndSourceIdAsFlow(url: String, sourceId: Long): Flow<Manga?> {
|
||||
return database.mangaQueries
|
||||
.getMangaByUrlAndSource(url, sourceId, MangaMapper::mapManga)
|
||||
.getMangaByUrlAndSource(sourceId = sourceId, remoteUrl = url, mapper = MangaMapper::mapManga)
|
||||
.subscribeToOneOrNull()
|
||||
}
|
||||
|
||||
@@ -161,7 +161,11 @@ class MangaRepositoryImpl(
|
||||
excludedScanlators.minus(current).forEach { scanlator ->
|
||||
database.excluded_scanlatorQueries.insert(mangaId, scanlator)
|
||||
}
|
||||
database.excluded_scanlatorQueries.remove(mangaId, current.minus(excludedScanlators))
|
||||
val toRemove = current.minus(excludedScanlators)
|
||||
// An empty delete still tells every chapter query to run again
|
||||
if (toRemove.isNotEmpty()) {
|
||||
database.excluded_scanlatorQueries.remove(mangaId, toRemove)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,25 +193,25 @@ class MangaRepositoryImpl(
|
||||
return database.transactionWithResult {
|
||||
manga.map {
|
||||
database.mangaQueries.insertNetworkManga(
|
||||
source = it.source,
|
||||
url = it.url,
|
||||
artist = it.artist,
|
||||
author = it.author,
|
||||
description = it.description,
|
||||
genre = it.genre,
|
||||
title = it.title,
|
||||
status = it.status,
|
||||
thumbnailUrl = it.thumbnailUrl,
|
||||
favoriteAt = it.favoriteAt,
|
||||
lastUpdate = it.lastUpdate,
|
||||
nextUpdate = it.nextUpdate,
|
||||
calculateInterval = it.fetchInterval.toLong(),
|
||||
initialized = it.initialized,
|
||||
viewerFlags = it.viewerFlags,
|
||||
chapterFlags = it.chapterFlags,
|
||||
coverLastModified = it.coverLastModified,
|
||||
updateStrategy = it.updateStrategy,
|
||||
memo = it.memo,
|
||||
sourceId = it.source,
|
||||
remoteUrl = it.url,
|
||||
remoteArtist = it.artist,
|
||||
remoteAuthor = it.author,
|
||||
remoteDescription = it.description,
|
||||
remoteGenre = it.genre,
|
||||
remoteTitle = it.title,
|
||||
remoteStatus = it.status,
|
||||
remoteCover = it.thumbnailUrl,
|
||||
userFavoriteAt = it.favoriteAt,
|
||||
stateChapterLastUpdate = it.lastUpdate,
|
||||
stateChapterNextUpdate = it.nextUpdate,
|
||||
stateChapterFetchInterval = it.fetchInterval.toLong(),
|
||||
stateInitialized = it.initialized,
|
||||
userReaderFlags = it.viewerFlags,
|
||||
userChapterFlags = it.chapterFlags,
|
||||
stateCoverLastModified = it.coverLastModified,
|
||||
remoteUpdateStrategy = it.updateStrategy,
|
||||
remoteMemo = it.memo,
|
||||
updateTitle = it.title.isNotBlank(),
|
||||
updateCover = !it.thumbnailUrl.isNullOrBlank(),
|
||||
updateDetails = it.initialized,
|
||||
@@ -221,18 +225,18 @@ class MangaRepositoryImpl(
|
||||
override suspend fun updateRemote(update: MangaRemoteUpdate): Boolean {
|
||||
return try {
|
||||
database.mangaQueries.updateRemote(
|
||||
artist = update.artist,
|
||||
author = update.author,
|
||||
description = update.description,
|
||||
genre = update.genre,
|
||||
title = update.title,
|
||||
status = update.status,
|
||||
thumbnailUrl = update.thumbnailUrl,
|
||||
initialized = update.initialized,
|
||||
coverLastModified = update.coverLastModified,
|
||||
updateStrategy = update.updateStrategy,
|
||||
memo = update.memo,
|
||||
mangaId = update.id,
|
||||
remoteArtist = update.artist,
|
||||
remoteAuthor = update.author,
|
||||
remoteDescription = update.description,
|
||||
remoteGenre = update.genre,
|
||||
remoteTitle = update.title,
|
||||
remoteStatus = update.status,
|
||||
remoteCover = update.thumbnailUrl,
|
||||
stateInitialized = update.initialized,
|
||||
stateCoverLastModified = update.coverLastModified,
|
||||
remoteUpdateStrategy = update.updateStrategy,
|
||||
remoteMemo = update.memo,
|
||||
id = update.id,
|
||||
)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
@@ -246,16 +250,16 @@ class MangaRepositoryImpl(
|
||||
mangaUpdates.forEach { value ->
|
||||
with(value) {
|
||||
database.mangaQueries.update(
|
||||
favoriteAtSet = isSet(::favoriteAt),
|
||||
favoriteAt = favoriteAt,
|
||||
lastUpdate = lastUpdate,
|
||||
nextUpdate = nextUpdate,
|
||||
calculateInterval = fetchInterval?.toLong(),
|
||||
viewer = viewerFlags,
|
||||
chapterFlags = chapterFlags,
|
||||
coverLastModified = coverLastModified,
|
||||
mangaId = id,
|
||||
notes = notes,
|
||||
userFavoriteAtSet = isSet(::favoriteAt),
|
||||
userFavoriteAt = favoriteAt,
|
||||
stateChapterLastUpdate = lastUpdate,
|
||||
stateChapterNextUpdate = nextUpdate,
|
||||
stateChapterFetchInterval = fetchInterval?.toLong(),
|
||||
userReaderFlags = viewerFlags,
|
||||
userChapterFlags = chapterFlags,
|
||||
stateCoverLastModified = coverLastModified,
|
||||
id = id,
|
||||
userNotes = notes,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,12 +31,12 @@ class StubSourceRepositoryImpl(
|
||||
}
|
||||
|
||||
override suspend fun upsertStubSource(id: Long, lang: String, name: String) {
|
||||
database.sourceQueries.upsert(id = id, lang = lang, name = name)
|
||||
database.sourceQueries.upsert(id = id, name = name, language = lang)
|
||||
}
|
||||
|
||||
private fun mapStubSource(
|
||||
id: Long,
|
||||
lang: String,
|
||||
name: String,
|
||||
lang: String,
|
||||
): StubSource = StubSource(id = id, lang = lang, name = name)
|
||||
}
|
||||
|
||||
@@ -6,22 +6,22 @@ object TrackMapper {
|
||||
fun mapTrack(
|
||||
id: Long,
|
||||
mangaId: Long,
|
||||
syncId: Long,
|
||||
trackerId: Long,
|
||||
remoteId: Long,
|
||||
remoteUrl: String,
|
||||
libraryId: Long?,
|
||||
title: String,
|
||||
lastChapterRead: Double,
|
||||
totalChapters: Long,
|
||||
status: Long,
|
||||
score: Double,
|
||||
remoteUrl: String,
|
||||
startDate: Long,
|
||||
finishDate: Long,
|
||||
private: Boolean,
|
||||
): Track = Track(
|
||||
id = id,
|
||||
mangaId = mangaId,
|
||||
trackerId = syncId,
|
||||
trackerId = trackerId,
|
||||
remoteId = remoteId,
|
||||
libraryId = libraryId,
|
||||
title = title,
|
||||
|
||||
@@ -46,7 +46,7 @@ class TrackRepositoryImpl(
|
||||
override suspend fun delete(mangaId: Long, trackerId: Long) {
|
||||
database.manga_trackQueries.delete(
|
||||
mangaId = mangaId,
|
||||
syncId = trackerId,
|
||||
trackerId = trackerId,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ class TrackRepositoryImpl(
|
||||
tracks.forEach { mangaTrack ->
|
||||
database.manga_trackQueries.upsert(
|
||||
mangaId = mangaTrack.mangaId,
|
||||
syncId = mangaTrack.trackerId,
|
||||
trackerId = mangaTrack.trackerId,
|
||||
remoteId = mangaTrack.remoteId,
|
||||
libraryId = mangaTrack.libraryId,
|
||||
title = mangaTrack.title,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package tachiyomi.data.updates
|
||||
|
||||
import app.cash.sqldelight.Query
|
||||
import app.cash.sqldelight.async.coroutines.awaitAsList
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
@@ -25,14 +26,7 @@ class UpdatesRepositoryImpl(
|
||||
after: Long,
|
||||
limit: Long,
|
||||
): List<UpdatesWithRelations> {
|
||||
return database.updatesViewQueries
|
||||
.getUpdatesByReadStatus(
|
||||
read = read,
|
||||
after = after,
|
||||
limit = limit,
|
||||
mapper = ::mapUpdatesWithRelations,
|
||||
)
|
||||
.awaitAsList()
|
||||
return getUpdatesByReadStatus(read = read, after = after, limit = limit).awaitAsList()
|
||||
}
|
||||
|
||||
override fun subscribeAll(
|
||||
@@ -67,14 +61,23 @@ class UpdatesRepositoryImpl(
|
||||
after: Long,
|
||||
limit: Long,
|
||||
): Flow<List<UpdatesWithRelations>> {
|
||||
return database.updatesViewQueries
|
||||
.getUpdatesByReadStatus(
|
||||
read = read,
|
||||
return getUpdatesByReadStatus(read = read, after = after, limit = limit).subscribeToList()
|
||||
}
|
||||
|
||||
private fun getUpdatesByReadStatus(read: Boolean, after: Long, limit: Long): Query<UpdatesWithRelations> {
|
||||
return if (read) {
|
||||
database.updatesViewQueries.getReadUpdates(
|
||||
after = after,
|
||||
limit = limit,
|
||||
mapper = ::mapUpdatesWithRelations,
|
||||
)
|
||||
.subscribeToList()
|
||||
} else {
|
||||
database.updatesViewQueries.getUnreadUpdates(
|
||||
after = after,
|
||||
limit = limit,
|
||||
mapper = ::mapUpdatesWithRelations,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
|
||||
@@ -19,24 +19,15 @@ END;
|
||||
getCategory:
|
||||
SELECT id, name, `order`, flags
|
||||
FROM category
|
||||
WHERE id = :id
|
||||
LIMIT 1;
|
||||
WHERE id = :id;
|
||||
|
||||
getCategories:
|
||||
SELECT
|
||||
id,
|
||||
name,
|
||||
`order`,
|
||||
flags
|
||||
SELECT id, name, `order`, flags
|
||||
FROM category
|
||||
ORDER BY `order`;
|
||||
|
||||
getCategoriesByMangaId:
|
||||
SELECT
|
||||
C.id,
|
||||
C.name,
|
||||
C.`order`,
|
||||
C.flags
|
||||
SELECT C.id, C.name, C.`order`, C.flags
|
||||
FROM category C
|
||||
JOIN manga_category MC
|
||||
ON C.id = MC.category_id
|
||||
@@ -44,18 +35,19 @@ WHERE MC.manga_id = :mangaId
|
||||
ORDER BY C.`order`;
|
||||
|
||||
insert:
|
||||
-- The position is taken inside the insert, so two categories created at once cannot claim the same one
|
||||
INSERT INTO category(name, `order`, flags)
|
||||
SELECT :name, coalesce(max(`order`), -1) + 1, :flags
|
||||
FROM category;
|
||||
|
||||
delete:
|
||||
DELETE FROM category
|
||||
WHERE id = :categoryId;
|
||||
WHERE id = :id;
|
||||
|
||||
updateName:
|
||||
UPDATE category
|
||||
SET name = :name
|
||||
WHERE id = :categoryId;
|
||||
WHERE id = :id;
|
||||
|
||||
getUserCategoryIds:
|
||||
SELECT id
|
||||
@@ -66,13 +58,13 @@ ORDER BY `order`;
|
||||
updateOrder:
|
||||
UPDATE category
|
||||
SET `order` = :order
|
||||
WHERE id = :categoryId;
|
||||
WHERE id = :id;
|
||||
|
||||
updateFlags:
|
||||
UPDATE category
|
||||
SET flags = :flags
|
||||
WHERE id = :categoryId;
|
||||
WHERE id = :id;
|
||||
|
||||
updateAllFlags:
|
||||
UPDATE category SET
|
||||
flags = coalesce(:flags, flags);
|
||||
UPDATE category
|
||||
SET flags = :flags;
|
||||
|
||||
@@ -25,19 +25,17 @@ CREATE INDEX chapter_unread_state_date_fetch_index ON chapter(state_date_fetch)
|
||||
|
||||
getChapterById:
|
||||
SELECT
|
||||
id, manga_id, remote_url, remote_name, remote_scanlator,
|
||||
user_read, user_bookmark, user_last_page_read, remote_chapter_number, remote_order,
|
||||
state_date_fetch, remote_date_upload,
|
||||
remote_memo
|
||||
id, manga_id, remote_url, remote_name, remote_scanlator, remote_chapter_number,
|
||||
remote_date_upload, remote_order, remote_memo, user_read, user_bookmark,
|
||||
user_last_page_read, state_date_fetch
|
||||
FROM chapter
|
||||
WHERE id = :id;
|
||||
|
||||
getChaptersByMangaId:
|
||||
SELECT
|
||||
C.id, C.manga_id, C.remote_url, C.remote_name, C.remote_scanlator,
|
||||
C.user_read, C.user_bookmark, C.user_last_page_read, C.remote_chapter_number, C.remote_order,
|
||||
C.state_date_fetch, C.remote_date_upload,
|
||||
C.remote_memo
|
||||
C.id, C.manga_id, C.remote_url, C.remote_name, C.remote_scanlator, C.remote_chapter_number,
|
||||
C.remote_date_upload, C.remote_order, C.remote_memo, C.user_read, C.user_bookmark,
|
||||
C.user_last_page_read, C.state_date_fetch
|
||||
FROM chapter C
|
||||
LEFT JOIN excluded_scanlator ES
|
||||
ON C.manga_id = ES.manga_id
|
||||
@@ -49,75 +47,70 @@ AND (
|
||||
);
|
||||
|
||||
getScanlatorsByMangaId:
|
||||
SELECT remote_scanlator
|
||||
SELECT DISTINCT remote_scanlator
|
||||
FROM chapter
|
||||
WHERE manga_id = :mangaId;
|
||||
|
||||
getBookmarkedChaptersByMangaId:
|
||||
SELECT
|
||||
id, manga_id, remote_url, remote_name, remote_scanlator,
|
||||
user_read, user_bookmark, user_last_page_read, remote_chapter_number, remote_order,
|
||||
state_date_fetch, remote_date_upload,
|
||||
remote_memo
|
||||
id, manga_id, remote_url, remote_name, remote_scanlator, remote_chapter_number,
|
||||
remote_date_upload, remote_order, remote_memo, user_read, user_bookmark,
|
||||
user_last_page_read, state_date_fetch
|
||||
FROM chapter
|
||||
WHERE user_bookmark
|
||||
AND manga_id = :mangaId;
|
||||
|
||||
getChapterByUrl:
|
||||
SELECT
|
||||
id, manga_id, remote_url, remote_name, remote_scanlator,
|
||||
user_read, user_bookmark, user_last_page_read, remote_chapter_number, remote_order,
|
||||
state_date_fetch, remote_date_upload,
|
||||
remote_memo
|
||||
FROM chapter
|
||||
WHERE remote_url = :chapterUrl;
|
||||
WHERE manga_id = :mangaId
|
||||
AND user_bookmark;
|
||||
|
||||
getChapterByUrlAndMangaId:
|
||||
SELECT
|
||||
id, manga_id, remote_url, remote_name, remote_scanlator,
|
||||
user_read, user_bookmark, user_last_page_read, remote_chapter_number, remote_order,
|
||||
state_date_fetch, remote_date_upload,
|
||||
remote_memo
|
||||
id, manga_id, remote_url, remote_name, remote_scanlator, remote_chapter_number,
|
||||
remote_date_upload, remote_order, remote_memo, user_read, user_bookmark,
|
||||
user_last_page_read, state_date_fetch
|
||||
FROM chapter
|
||||
WHERE remote_url = :chapterUrl
|
||||
AND manga_id = :mangaId
|
||||
LIMIT 1;
|
||||
WHERE manga_id = :mangaId
|
||||
AND remote_url = :remoteUrl;
|
||||
|
||||
removeChaptersWithIds:
|
||||
DELETE FROM chapter
|
||||
WHERE id IN :chapterIds;
|
||||
|
||||
insert:
|
||||
INSERT INTO chapter(manga_id, remote_url, remote_name, remote_scanlator, user_read, user_bookmark, user_last_page_read, remote_chapter_number, remote_order, state_date_fetch, remote_date_upload, remote_memo)
|
||||
VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, :memo);
|
||||
|
||||
insertReturningId:
|
||||
INSERT INTO chapter(manga_id, remote_url, remote_name, remote_scanlator, user_read, user_bookmark, user_last_page_read, remote_chapter_number, remote_order, state_date_fetch, remote_date_upload, remote_memo)
|
||||
VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, :memo)
|
||||
INSERT INTO chapter(
|
||||
manga_id, remote_url, remote_name, remote_scanlator, remote_chapter_number,
|
||||
remote_date_upload, remote_order, remote_memo, user_read, user_bookmark,
|
||||
user_last_page_read, state_date_fetch
|
||||
)
|
||||
VALUES (
|
||||
:mangaId, :remoteUrl, :remoteName, :remoteScanlator, :remoteChapterNumber,
|
||||
:remoteDateUpload, :remoteOrder, :remoteMemo, :userRead, :userBookmark,
|
||||
:userLastPageRead, :stateDateFetch
|
||||
)
|
||||
-- Two syncs of the same entry can race; the chapter the other one already stored is skipped rather
|
||||
-- than failing the whole batch
|
||||
ON CONFLICT(manga_id, remote_url) DO NOTHING
|
||||
RETURNING id;
|
||||
|
||||
update:
|
||||
UPDATE chapter
|
||||
SET user_read = coalesce(:read, user_read),
|
||||
user_bookmark = coalesce(:bookmark, user_bookmark),
|
||||
user_last_page_read = coalesce(:lastPageRead, user_last_page_read),
|
||||
state_date_fetch = coalesce(:dateFetch, state_date_fetch)
|
||||
WHERE id = :chapterId;
|
||||
SET user_read = coalesce(:userRead, user_read),
|
||||
user_bookmark = coalesce(:userBookmark, user_bookmark),
|
||||
user_last_page_read = coalesce(:userLastPageRead, user_last_page_read),
|
||||
state_date_fetch = coalesce(:stateDateFetch, state_date_fetch)
|
||||
WHERE id = :id;
|
||||
|
||||
updateFromBackup:
|
||||
UPDATE chapter
|
||||
SET user_read = coalesce(:read, user_read),
|
||||
user_bookmark = coalesce(:bookmark, user_bookmark),
|
||||
user_last_page_read = coalesce(:lastPageRead, user_last_page_read),
|
||||
remote_memo = coalesce(:memo, remote_memo)
|
||||
WHERE id = :chapterId;
|
||||
SET user_read = coalesce(:userRead, user_read),
|
||||
user_bookmark = coalesce(:userBookmark, user_bookmark),
|
||||
user_last_page_read = coalesce(:userLastPageRead, user_last_page_read),
|
||||
remote_memo = coalesce(:remoteMemo, remote_memo)
|
||||
WHERE id = :id;
|
||||
|
||||
updateRemote:
|
||||
UPDATE chapter
|
||||
SET remote_name = :name,
|
||||
remote_scanlator = coalesce(:scanlator, remote_scanlator),
|
||||
remote_chapter_number = :chapterNumber,
|
||||
remote_order = :sourceOrder,
|
||||
remote_date_upload = coalesce(:dateUpload, remote_date_upload),
|
||||
remote_memo = :memo
|
||||
WHERE id = :chapterId;
|
||||
SET remote_name = :remoteName,
|
||||
remote_scanlator = coalesce(:remoteScanlator, remote_scanlator),
|
||||
remote_chapter_number = :remoteChapterNumber,
|
||||
remote_order = :remoteOrder,
|
||||
remote_date_upload = coalesce(:remoteDateUpload, remote_date_upload),
|
||||
remote_memo = :remoteMemo
|
||||
WHERE id = :id;
|
||||
|
||||
@@ -9,7 +9,8 @@ CREATE TABLE excluded_scanlator(
|
||||
|
||||
insert:
|
||||
INSERT INTO excluded_scanlator(manga_id, scanlator)
|
||||
VALUES (:mangaId, :scanlator);
|
||||
VALUES (:mangaId, :scanlator)
|
||||
ON CONFLICT(manga_id, scanlator) DO NOTHING;
|
||||
|
||||
remove:
|
||||
DELETE FROM excluded_scanlator
|
||||
|
||||
@@ -21,23 +21,26 @@ SELECT
|
||||
FROM extension_store;
|
||||
|
||||
getCount:
|
||||
SELECT COUNT(*)
|
||||
SELECT count(*)
|
||||
FROM extension_store;
|
||||
|
||||
upsert:
|
||||
INSERT INTO extension_store(index_url, name, badge_label, signing_key, contact_website, contact_discord, is_legacy, extension_list_url)
|
||||
VALUES (:indexUrl, :name, :badgeLabel, :signingKey, :contactWebsite, :contactDiscord, :isLegacy, :extensionListUrl)
|
||||
ON CONFLICT(index_url)
|
||||
DO UPDATE
|
||||
SET
|
||||
name = :name,
|
||||
badge_label = :badgeLabel,
|
||||
signing_key =: signingKey,
|
||||
contact_website = :contactWebsite,
|
||||
contact_discord = :contactDiscord,
|
||||
is_legacy = :isLegacy,
|
||||
extension_list_url = :extensionListUrl
|
||||
WHERE index_url = :indexUrl;
|
||||
INSERT INTO extension_store(
|
||||
index_url, name, badge_label, signing_key, contact_website, contact_discord, is_legacy,
|
||||
extension_list_url
|
||||
)
|
||||
VALUES (
|
||||
:indexUrl, :name, :badgeLabel, :signingKey, :contactWebsite, :contactDiscord, :isLegacy,
|
||||
:extensionListUrl
|
||||
)
|
||||
ON CONFLICT(index_url) DO UPDATE SET
|
||||
name = excluded.name,
|
||||
badge_label = excluded.badge_label,
|
||||
signing_key = excluded.signing_key,
|
||||
contact_website = excluded.contact_website,
|
||||
contact_discord = excluded.contact_discord,
|
||||
is_legacy = excluded.is_legacy,
|
||||
extension_list_url = excluded.extension_list_url;
|
||||
|
||||
delete:
|
||||
DELETE FROM extension_store
|
||||
|
||||
@@ -16,30 +16,14 @@ CREATE INDEX history_manga_id_index ON history(manga_id);
|
||||
CREATE INDEX history_read_at_index ON history(read_at);
|
||||
|
||||
getHistoryByMangaId:
|
||||
SELECT
|
||||
H.id,
|
||||
H.chapter_id,
|
||||
H.read_at,
|
||||
H.read_duration
|
||||
FROM history H
|
||||
WHERE H.manga_id = :mangaId;
|
||||
|
||||
getHistoryByChapterUrlAndMangaId:
|
||||
SELECT
|
||||
H.id,
|
||||
H.chapter_id,
|
||||
H.read_at,
|
||||
H.read_duration
|
||||
FROM history H
|
||||
JOIN chapter C
|
||||
ON H.chapter_id = C.id
|
||||
WHERE C.remote_url = :chapterUrl AND C.manga_id = :mangaId
|
||||
LIMIT 1;
|
||||
SELECT id, chapter_id, read_at, read_duration
|
||||
FROM history
|
||||
WHERE manga_id = :mangaId;
|
||||
|
||||
resetHistoryById:
|
||||
UPDATE history
|
||||
SET read_at = 0
|
||||
WHERE id = :historyId;
|
||||
WHERE id = :id;
|
||||
|
||||
resetHistoryByMangaId:
|
||||
UPDATE history
|
||||
@@ -55,15 +39,12 @@ WHERE read_at = 0;
|
||||
|
||||
upsert:
|
||||
INSERT INTO history(manga_id, chapter_id, read_at, read_duration)
|
||||
SELECT C.manga_id, :chapterId, :readAt, :readDuration
|
||||
FROM chapter C
|
||||
WHERE C.id = :chapterId
|
||||
ON CONFLICT(chapter_id)
|
||||
DO UPDATE
|
||||
SET
|
||||
read_at = :readAt,
|
||||
read_duration = read_duration + :readDuration
|
||||
WHERE chapter_id = :chapterId;
|
||||
SELECT manga_id, id, :readAt, :readDuration
|
||||
FROM chapter
|
||||
WHERE id = :chapterId
|
||||
ON CONFLICT(chapter_id) DO UPDATE SET
|
||||
read_at = excluded.read_at,
|
||||
read_duration = read_duration + excluded.read_duration;
|
||||
|
||||
getReadDuration:
|
||||
SELECT coalesce(sum(read_duration), 0)
|
||||
|
||||
@@ -33,73 +33,68 @@ CREATE INDEX manga_user_favorite_at_index ON manga(user_favorite_at) WHERE user_
|
||||
|
||||
getMangaById:
|
||||
SELECT
|
||||
id, source_id, remote_url, remote_artist, remote_author, remote_description, remote_genre, remote_title,
|
||||
remote_status, remote_cover, state_chapter_last_update, state_chapter_next_update, state_initialized,
|
||||
user_reader_flags, user_chapter_flags, state_cover_last_modified, remote_update_strategy,
|
||||
state_chapter_fetch_interval, user_notes, remote_memo, user_favorite_at
|
||||
id, source_id, remote_url, remote_title, remote_author, remote_artist, remote_description,
|
||||
remote_genre, remote_status, remote_cover, remote_update_strategy, remote_memo,
|
||||
user_favorite_at, user_notes, user_reader_flags, user_chapter_flags,
|
||||
state_chapter_last_update, state_chapter_next_update, state_chapter_fetch_interval,
|
||||
state_cover_last_modified, state_initialized
|
||||
FROM manga
|
||||
WHERE id = :id;
|
||||
|
||||
-- TODO: this should ideally never really have more than 1 result
|
||||
getMangaByUrlAndSource:
|
||||
SELECT
|
||||
id, source_id, remote_url, remote_artist, remote_author, remote_description, remote_genre, remote_title,
|
||||
remote_status, remote_cover, state_chapter_last_update, state_chapter_next_update, state_initialized,
|
||||
user_reader_flags, user_chapter_flags, state_cover_last_modified, remote_update_strategy,
|
||||
state_chapter_fetch_interval, user_notes, remote_memo, user_favorite_at
|
||||
id, source_id, remote_url, remote_title, remote_author, remote_artist, remote_description,
|
||||
remote_genre, remote_status, remote_cover, remote_update_strategy, remote_memo,
|
||||
user_favorite_at, user_notes, user_reader_flags, user_chapter_flags,
|
||||
state_chapter_last_update, state_chapter_next_update, state_chapter_fetch_interval,
|
||||
state_cover_last_modified, state_initialized
|
||||
FROM manga
|
||||
WHERE remote_url = :url
|
||||
AND source_id = :source
|
||||
LIMIT 1;
|
||||
WHERE source_id = :sourceId
|
||||
AND remote_url = :remoteUrl;
|
||||
|
||||
getFavorites:
|
||||
SELECT
|
||||
id, source_id, remote_url, remote_artist, remote_author, remote_description, remote_genre, remote_title,
|
||||
remote_status, remote_cover, state_chapter_last_update, state_chapter_next_update, state_initialized,
|
||||
user_reader_flags, user_chapter_flags, state_cover_last_modified, remote_update_strategy,
|
||||
state_chapter_fetch_interval, user_notes, remote_memo, user_favorite_at
|
||||
id, source_id, remote_url, remote_title, remote_author, remote_artist, remote_description,
|
||||
remote_genre, remote_status, remote_cover, remote_update_strategy, remote_memo,
|
||||
user_favorite_at, user_notes, user_reader_flags, user_chapter_flags,
|
||||
state_chapter_last_update, state_chapter_next_update, state_chapter_fetch_interval,
|
||||
state_cover_last_modified, state_initialized
|
||||
FROM manga
|
||||
WHERE user_favorite_at IS NOT NULL;
|
||||
|
||||
getReadMangaNotInLibrary:
|
||||
SELECT
|
||||
id, source_id, remote_url, remote_artist, remote_author, remote_description, remote_genre, remote_title,
|
||||
remote_status, remote_cover, state_chapter_last_update, state_chapter_next_update, state_initialized,
|
||||
user_reader_flags, user_chapter_flags, state_cover_last_modified, remote_update_strategy,
|
||||
state_chapter_fetch_interval, user_notes, remote_memo, user_favorite_at
|
||||
id, source_id, remote_url, remote_title, remote_author, remote_artist, remote_description,
|
||||
remote_genre, remote_status, remote_cover, remote_update_strategy, remote_memo,
|
||||
user_favorite_at, user_notes, user_reader_flags, user_chapter_flags,
|
||||
state_chapter_last_update, state_chapter_next_update, state_chapter_fetch_interval,
|
||||
state_cover_last_modified, state_initialized
|
||||
FROM manga
|
||||
WHERE user_favorite_at IS NULL AND id IN (
|
||||
SELECT DISTINCT chapter.manga_id
|
||||
WHERE user_favorite_at IS NULL
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM chapter
|
||||
WHERE user_read = 1 OR user_last_page_read != 0
|
||||
WHERE chapter.manga_id = manga.id
|
||||
AND (user_read = 1 OR user_last_page_read != 0)
|
||||
);
|
||||
|
||||
getAllManga:
|
||||
SELECT
|
||||
id, source_id, remote_url, remote_artist, remote_author, remote_description, remote_genre, remote_title,
|
||||
remote_status, remote_cover, state_chapter_last_update, state_chapter_next_update, state_initialized,
|
||||
user_reader_flags, user_chapter_flags, state_cover_last_modified, remote_update_strategy,
|
||||
state_chapter_fetch_interval, user_notes, remote_memo, user_favorite_at
|
||||
FROM manga;
|
||||
|
||||
getAllMangaSourceAndUrl:
|
||||
SELECT source_id, remote_url
|
||||
FROM manga;
|
||||
|
||||
getSourceIdWithFavoriteCount:
|
||||
SELECT
|
||||
source_id,
|
||||
count(*)
|
||||
SELECT source_id, count(*)
|
||||
FROM manga
|
||||
WHERE user_favorite_at IS NOT NULL
|
||||
GROUP BY source_id;
|
||||
|
||||
getFavoriteBySourceId:
|
||||
SELECT
|
||||
id, source_id, remote_url, remote_artist, remote_author, remote_description, remote_genre, remote_title,
|
||||
remote_status, remote_cover, state_chapter_last_update, state_chapter_next_update, state_initialized,
|
||||
user_reader_flags, user_chapter_flags, state_cover_last_modified, remote_update_strategy,
|
||||
state_chapter_fetch_interval, user_notes, remote_memo, user_favorite_at
|
||||
id, source_id, remote_url, remote_title, remote_author, remote_artist, remote_description,
|
||||
remote_genre, remote_status, remote_cover, remote_update_strategy, remote_memo,
|
||||
user_favorite_at, user_notes, user_reader_flags, user_chapter_flags,
|
||||
state_chapter_last_update, state_chapter_next_update, state_chapter_fetch_interval,
|
||||
state_cover_last_modified, state_initialized
|
||||
FROM manga
|
||||
WHERE user_favorite_at IS NOT NULL
|
||||
AND source_id = :sourceId;
|
||||
@@ -107,57 +102,43 @@ AND source_id = :sourceId;
|
||||
getDuplicateLibraryManga:
|
||||
WITH
|
||||
track_dupes AS (
|
||||
SELECT DISTINCT S2.manga_id
|
||||
FROM manga_track S1
|
||||
INNER JOIN manga_track S2
|
||||
ON S1.tracker_id = S2.tracker_id
|
||||
AND S1.remote_id = S2.remote_id
|
||||
AND S1.remote_id != 0
|
||||
AND S1.manga_id != S2.manga_id
|
||||
WHERE S1.manga_id = :id
|
||||
),
|
||||
duplicates AS (
|
||||
SELECT
|
||||
M.id, M.source_id, M.remote_url, M.remote_artist, M.remote_author, M.remote_description, M.remote_genre, M.remote_title,
|
||||
M.remote_status, M.remote_cover, M.state_chapter_last_update, M.state_chapter_next_update, M.state_initialized,
|
||||
M.user_reader_flags, M.user_chapter_flags, M.state_cover_last_modified, M.remote_update_strategy,
|
||||
M.state_chapter_fetch_interval, M.user_notes, M.remote_memo, M.user_favorite_at
|
||||
FROM manga M
|
||||
LEFT JOIN track_dupes D
|
||||
ON D.manga_id = id
|
||||
WHERE user_favorite_at IS NOT NULL
|
||||
AND id != :id
|
||||
AND (lower(remote_title) LIKE '%' || lower(:title) || '%' OR D.manga_id IS NOT NULL)
|
||||
),
|
||||
chapter_counts AS (
|
||||
SELECT
|
||||
M.id AS manga_id,
|
||||
count(*) AS chapter_count
|
||||
FROM duplicates M
|
||||
JOIN chapter C
|
||||
ON M.id = C.manga_id
|
||||
LEFT JOIN excluded_scanlator ES
|
||||
ON C.manga_id = ES.manga_id
|
||||
AND C.remote_scanlator = ES.scanlator
|
||||
WHERE ES.scanlator IS NULL
|
||||
GROUP BY M.id
|
||||
SELECT DISTINCT T2.manga_id
|
||||
FROM manga_track T1
|
||||
JOIN manga_track T2
|
||||
ON T1.tracker_id = T2.tracker_id
|
||||
AND T1.remote_id = T2.remote_id
|
||||
AND T1.remote_id != 0
|
||||
AND T1.manga_id != T2.manga_id
|
||||
WHERE T1.manga_id = :id
|
||||
)
|
||||
SELECT
|
||||
M.id, M.source_id, M.remote_url, M.remote_artist, M.remote_author, M.remote_description, M.remote_genre, M.remote_title,
|
||||
M.remote_status, M.remote_cover, M.state_chapter_last_update, M.state_chapter_next_update, M.state_initialized,
|
||||
M.user_reader_flags, M.user_chapter_flags, M.state_cover_last_modified, M.remote_update_strategy,
|
||||
M.state_chapter_fetch_interval, M.user_notes, M.remote_memo, M.user_favorite_at,
|
||||
coalesce(CC.chapter_count, 0) AS chapter_count
|
||||
FROM duplicates M
|
||||
LEFT JOIN chapter_counts CC
|
||||
ON M.id = CC.manga_id;
|
||||
M.id, M.source_id, M.remote_url, M.remote_title, M.remote_author, M.remote_artist,
|
||||
M.remote_description, M.remote_genre, M.remote_status, M.remote_cover,
|
||||
M.remote_update_strategy, M.remote_memo, M.user_favorite_at, M.user_notes,
|
||||
M.user_reader_flags, M.user_chapter_flags, M.state_chapter_last_update,
|
||||
M.state_chapter_next_update, M.state_chapter_fetch_interval,
|
||||
M.state_cover_last_modified, M.state_initialized,
|
||||
(
|
||||
SELECT count(*)
|
||||
FROM chapter C
|
||||
LEFT JOIN excluded_scanlator ES
|
||||
ON C.manga_id = ES.manga_id
|
||||
AND C.remote_scanlator = ES.scanlator
|
||||
WHERE C.manga_id = M.id
|
||||
AND ES.scanlator IS NULL
|
||||
) AS chapter_count
|
||||
FROM manga M
|
||||
WHERE M.user_favorite_at IS NOT NULL
|
||||
AND M.id != :id
|
||||
AND (lower(M.remote_title) LIKE '%' || lower(:remoteTitle) || '%' OR M.id IN (SELECT manga_id FROM track_dupes));
|
||||
|
||||
getUpcomingManga:
|
||||
SELECT
|
||||
id, source_id, remote_url, remote_artist, remote_author, remote_description, remote_genre, remote_title,
|
||||
remote_status, remote_cover, state_chapter_last_update, state_chapter_next_update, state_initialized,
|
||||
user_reader_flags, user_chapter_flags, state_cover_last_modified, remote_update_strategy,
|
||||
state_chapter_fetch_interval, user_notes, remote_memo, user_favorite_at
|
||||
id, source_id, remote_url, remote_title, remote_author, remote_artist, remote_description,
|
||||
remote_genre, remote_status, remote_cover, remote_update_strategy, remote_memo,
|
||||
user_favorite_at, user_notes, user_reader_flags, user_chapter_flags,
|
||||
state_chapter_last_update, state_chapter_next_update, state_chapter_fetch_interval,
|
||||
state_cover_last_modified, state_initialized
|
||||
FROM manga
|
||||
WHERE state_chapter_next_update >= :startOfDay
|
||||
AND user_favorite_at IS NOT NULL
|
||||
@@ -168,7 +149,7 @@ AND (
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM manga_category
|
||||
WHERE manga_category.manga_id = manga.id
|
||||
AND COALESCE(manga_category.category_id, 0) IN :includedCategories
|
||||
AND manga_category.category_id IN :includedCategories
|
||||
)
|
||||
OR (0 IN :includedCategories AND NOT EXISTS (
|
||||
SELECT 1 FROM manga_category WHERE manga_category.manga_id = manga.id)
|
||||
@@ -181,7 +162,7 @@ AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM manga_category
|
||||
WHERE manga_category.manga_id = manga.id
|
||||
AND COALESCE(manga_category.category_id, 0) IN :excludedCategories
|
||||
AND manga_category.category_id IN :excludedCategories
|
||||
)
|
||||
AND (
|
||||
0 NOT IN :excludedCategories
|
||||
@@ -193,10 +174,11 @@ ORDER BY state_chapter_next_update ASC;
|
||||
|
||||
resetViewerFlags:
|
||||
UPDATE manga
|
||||
SET user_reader_flags = 0;
|
||||
SET user_reader_flags = 0
|
||||
WHERE user_reader_flags != 0;
|
||||
|
||||
getSourceIdsWithNonLibraryManga:
|
||||
SELECT source_id, COUNT(*) AS manga_count
|
||||
SELECT source_id, count(*) AS manga_count
|
||||
FROM manga
|
||||
WHERE user_favorite_at IS NULL
|
||||
GROUP BY source_id;
|
||||
@@ -207,105 +189,119 @@ WHERE user_favorite_at IS NULL
|
||||
AND source_id IN :sourceIds
|
||||
AND (
|
||||
:keepReadManga = 0
|
||||
OR id NOT IN (
|
||||
SELECT DISTINCT manga_id
|
||||
OR NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM chapter
|
||||
WHERE user_read = 1
|
||||
OR user_last_page_read != 0
|
||||
WHERE chapter.manga_id = manga.id
|
||||
AND (user_read = 1 OR user_last_page_read != 0)
|
||||
)
|
||||
);
|
||||
|
||||
insertReturningId:
|
||||
INSERT INTO manga(source_id, remote_url, remote_artist, remote_author, remote_description, remote_genre, remote_title, remote_status, remote_cover, user_favorite_at, state_chapter_last_update, state_chapter_next_update, state_initialized, user_reader_flags, user_chapter_flags, state_cover_last_modified, remote_update_strategy, state_chapter_fetch_interval, user_notes, remote_memo)
|
||||
VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favoriteAt, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :updateStrategy, :calculateInterval, :notes, :memo)
|
||||
INSERT INTO manga(
|
||||
source_id, remote_url, remote_title, remote_author, remote_artist, remote_description,
|
||||
remote_genre, remote_status, remote_cover, remote_update_strategy, remote_memo,
|
||||
user_favorite_at, user_notes, user_reader_flags, user_chapter_flags,
|
||||
state_chapter_last_update, state_chapter_next_update, state_chapter_fetch_interval,
|
||||
state_cover_last_modified, state_initialized
|
||||
)
|
||||
VALUES (
|
||||
:sourceId, :remoteUrl, :remoteTitle, :remoteAuthor, :remoteArtist, :remoteDescription,
|
||||
:remoteGenre, :remoteStatus, :remoteCover, :remoteUpdateStrategy, :remoteMemo,
|
||||
:userFavoriteAt, :userNotes, :userReaderFlags, :userChapterFlags,
|
||||
:stateChapterLastUpdate, :stateChapterNextUpdate, :stateChapterFetchInterval,
|
||||
:stateCoverLastModified, :stateInitialized
|
||||
)
|
||||
RETURNING id;
|
||||
|
||||
update:
|
||||
UPDATE manga SET
|
||||
-- NULL is a real value here (not in the library), which coalesce cannot write
|
||||
user_favorite_at = CASE WHEN :favoriteAtSet THEN :favoriteAt ELSE user_favorite_at END,
|
||||
state_chapter_last_update = coalesce(:lastUpdate, state_chapter_last_update),
|
||||
state_chapter_next_update = coalesce(:nextUpdate, state_chapter_next_update),
|
||||
user_reader_flags = coalesce(:viewer, user_reader_flags),
|
||||
user_chapter_flags = coalesce(:chapterFlags, user_chapter_flags),
|
||||
state_cover_last_modified = coalesce(:coverLastModified, state_cover_last_modified),
|
||||
state_chapter_fetch_interval = coalesce(:calculateInterval, state_chapter_fetch_interval),
|
||||
user_notes = coalesce(:notes, user_notes)
|
||||
WHERE id = :mangaId;
|
||||
user_favorite_at = CASE WHEN :userFavoriteAtSet THEN :userFavoriteAt ELSE user_favorite_at END,
|
||||
user_notes = coalesce(:userNotes, user_notes),
|
||||
user_reader_flags = coalesce(:userReaderFlags, user_reader_flags),
|
||||
user_chapter_flags = coalesce(:userChapterFlags, user_chapter_flags),
|
||||
state_chapter_last_update = coalesce(:stateChapterLastUpdate, state_chapter_last_update),
|
||||
state_chapter_next_update = coalesce(:stateChapterNextUpdate, state_chapter_next_update),
|
||||
state_chapter_fetch_interval = coalesce(:stateChapterFetchInterval, state_chapter_fetch_interval),
|
||||
state_cover_last_modified = coalesce(:stateCoverLastModified, state_cover_last_modified)
|
||||
WHERE id = :id;
|
||||
|
||||
updateFromBackup:
|
||||
UPDATE manga SET
|
||||
remote_artist = coalesce(:artist, remote_artist),
|
||||
remote_author = coalesce(:author, remote_author),
|
||||
remote_description = coalesce(:description, remote_description),
|
||||
remote_genre = coalesce(:genre, remote_genre),
|
||||
remote_title = coalesce(:title, remote_title),
|
||||
remote_status = coalesce(:status, remote_status),
|
||||
remote_cover = coalesce(:thumbnailUrl, remote_cover),
|
||||
user_favorite_at = :favoriteAt,
|
||||
state_chapter_last_update = coalesce(:lastUpdate, state_chapter_last_update),
|
||||
state_initialized = coalesce(:initialized, state_initialized),
|
||||
user_reader_flags = coalesce(:viewer, user_reader_flags),
|
||||
user_chapter_flags = coalesce(:chapterFlags, user_chapter_flags),
|
||||
state_cover_last_modified = coalesce(:coverLastModified, state_cover_last_modified),
|
||||
remote_update_strategy = coalesce(:updateStrategy, remote_update_strategy),
|
||||
user_notes = coalesce(:notes, user_notes),
|
||||
remote_memo = coalesce(:memo, remote_memo)
|
||||
WHERE id = :mangaId;
|
||||
remote_title = coalesce(:remoteTitle, remote_title),
|
||||
remote_author = coalesce(:remoteAuthor, remote_author),
|
||||
remote_artist = coalesce(:remoteArtist, remote_artist),
|
||||
remote_description = coalesce(:remoteDescription, remote_description),
|
||||
remote_genre = coalesce(:remoteGenre, remote_genre),
|
||||
remote_status = coalesce(:remoteStatus, remote_status),
|
||||
remote_cover = coalesce(:remoteCover, remote_cover),
|
||||
remote_update_strategy = coalesce(:remoteUpdateStrategy, remote_update_strategy),
|
||||
remote_memo = coalesce(:remoteMemo, remote_memo),
|
||||
user_favorite_at = :userFavoriteAt,
|
||||
user_notes = coalesce(:userNotes, user_notes),
|
||||
user_reader_flags = coalesce(:userReaderFlags, user_reader_flags),
|
||||
user_chapter_flags = coalesce(:userChapterFlags, user_chapter_flags),
|
||||
state_chapter_last_update = coalesce(:stateChapterLastUpdate, state_chapter_last_update),
|
||||
state_cover_last_modified = coalesce(:stateCoverLastModified, state_cover_last_modified),
|
||||
state_initialized = coalesce(:stateInitialized, state_initialized)
|
||||
WHERE id = :id;
|
||||
|
||||
updateRemote:
|
||||
UPDATE manga SET
|
||||
remote_artist = coalesce(:artist, remote_artist),
|
||||
remote_author = coalesce(:author, remote_author),
|
||||
remote_description = coalesce(:description, remote_description),
|
||||
remote_genre = coalesce(:genre, remote_genre),
|
||||
remote_title = coalesce(:title, remote_title),
|
||||
remote_status = :status,
|
||||
remote_cover = coalesce(:thumbnailUrl, remote_cover),
|
||||
state_initialized = :initialized,
|
||||
state_cover_last_modified = coalesce(:coverLastModified, state_cover_last_modified),
|
||||
remote_update_strategy = :updateStrategy,
|
||||
remote_memo = :memo
|
||||
WHERE id = :mangaId;
|
||||
remote_title = coalesce(:remoteTitle, remote_title),
|
||||
remote_author = coalesce(:remoteAuthor, remote_author),
|
||||
remote_artist = coalesce(:remoteArtist, remote_artist),
|
||||
remote_description = coalesce(:remoteDescription, remote_description),
|
||||
remote_genre = coalesce(:remoteGenre, remote_genre),
|
||||
remote_status = :remoteStatus,
|
||||
remote_cover = coalesce(:remoteCover, remote_cover),
|
||||
remote_update_strategy = :remoteUpdateStrategy,
|
||||
remote_memo = :remoteMemo,
|
||||
state_cover_last_modified = coalesce(:stateCoverLastModified, state_cover_last_modified),
|
||||
state_initialized = :stateInitialized
|
||||
WHERE id = :id;
|
||||
|
||||
insertNetworkManga {
|
||||
-- Insert the manga if it doesn't exist already
|
||||
-- A new entry is inserted as the source reported it. An existing one takes the fresh details
|
||||
-- only while it is outside the library, so a library entry keeps what the user already has.
|
||||
INSERT INTO manga(
|
||||
source_id, remote_url, remote_artist, remote_author, remote_description, remote_genre, remote_title, remote_status, remote_cover, user_favorite_at,
|
||||
state_chapter_last_update, state_chapter_next_update, state_initialized, user_reader_flags, user_chapter_flags, state_cover_last_modified,
|
||||
remote_update_strategy, state_chapter_fetch_interval, user_notes, remote_memo
|
||||
source_id, remote_url, remote_title, remote_author, remote_artist, remote_description,
|
||||
remote_genre, remote_status, remote_cover, remote_update_strategy, remote_memo,
|
||||
user_favorite_at, user_notes, user_reader_flags, user_chapter_flags,
|
||||
state_chapter_last_update, state_chapter_next_update, state_chapter_fetch_interval,
|
||||
state_cover_last_modified, state_initialized
|
||||
)
|
||||
SELECT
|
||||
: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 manga WHERE source_id = :source AND remote_url = :url);
|
||||
VALUES (
|
||||
:sourceId, :remoteUrl, :remoteTitle, :remoteAuthor, :remoteArtist, :remoteDescription,
|
||||
:remoteGenre, :remoteStatus, :remoteCover, :remoteUpdateStrategy, :remoteMemo,
|
||||
:userFavoriteAt, '', :userReaderFlags, :userChapterFlags,
|
||||
:stateChapterLastUpdate, :stateChapterNextUpdate, :stateChapterFetchInterval,
|
||||
:stateCoverLastModified, :stateInitialized
|
||||
)
|
||||
ON CONFLICT(source_id, remote_url) DO UPDATE SET
|
||||
remote_title = CASE WHEN :updateTitle THEN excluded.remote_title ELSE remote_title END,
|
||||
remote_cover = CASE WHEN :updateCover THEN excluded.remote_cover ELSE remote_cover END,
|
||||
remote_author = CASE WHEN :updateDetails THEN excluded.remote_author ELSE remote_author END,
|
||||
remote_artist = CASE WHEN :updateDetails THEN excluded.remote_artist ELSE remote_artist END,
|
||||
remote_description = CASE WHEN :updateDetails THEN excluded.remote_description ELSE remote_description END,
|
||||
remote_genre = CASE WHEN :updateDetails THEN excluded.remote_genre ELSE remote_genre END,
|
||||
remote_status = CASE WHEN :updateDetails THEN excluded.remote_status ELSE remote_status END,
|
||||
remote_update_strategy = CASE
|
||||
WHEN :updateDetails THEN excluded.remote_update_strategy
|
||||
ELSE remote_update_strategy
|
||||
END,
|
||||
remote_memo = excluded.remote_memo,
|
||||
state_initialized = :updateDetails
|
||||
WHERE user_favorite_at IS NULL;
|
||||
|
||||
-- Update the relevant details if applicable and not favorite
|
||||
UPDATE manga
|
||||
SET
|
||||
remote_title = CASE WHEN :updateTitle THEN :title ELSE remote_title END,
|
||||
remote_cover = CASE WHEN :updateCover THEN :thumbnailUrl ELSE remote_cover END,
|
||||
remote_author = CASE WHEN :updateDetails THEN :author ELSE remote_author END,
|
||||
remote_artist = CASE WHEN :updateDetails THEN :artist ELSE remote_artist END,
|
||||
remote_description = CASE WHEN :updateDetails THEN :description ELSE remote_description END,
|
||||
remote_genre = CASE WHEN :updateDetails THEN :genre ELSE remote_genre END,
|
||||
remote_status = CASE WHEN :updateDetails THEN :status ELSE remote_status END,
|
||||
remote_update_strategy = CASE WHEN :updateDetails THEN :updateStrategy ELSE remote_update_strategy END,
|
||||
state_initialized = :updateDetails,
|
||||
remote_memo = :memo
|
||||
WHERE source_id = :source
|
||||
AND remote_url = :url
|
||||
AND user_favorite_at IS NULL;
|
||||
|
||||
-- Finally return the manga
|
||||
SELECT
|
||||
id, source_id, remote_url, remote_artist, remote_author, remote_description, remote_genre, remote_title,
|
||||
remote_status, remote_cover, state_chapter_last_update, state_chapter_next_update, state_initialized,
|
||||
user_reader_flags, user_chapter_flags, state_cover_last_modified, remote_update_strategy,
|
||||
state_chapter_fetch_interval, user_notes, remote_memo, user_favorite_at
|
||||
id, source_id, remote_url, remote_title, remote_author, remote_artist, remote_description,
|
||||
remote_genre, remote_status, remote_cover, remote_update_strategy, remote_memo,
|
||||
user_favorite_at, user_notes, user_reader_flags, user_chapter_flags,
|
||||
state_chapter_last_update, state_chapter_next_update, state_chapter_fetch_interval,
|
||||
state_cover_last_modified, state_initialized
|
||||
FROM manga
|
||||
WHERE source_id = :source
|
||||
AND remote_url = :url
|
||||
LIMIT 1;
|
||||
WHERE source_id = :sourceId
|
||||
AND remote_url = :remoteUrl;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ CREATE INDEX manga_category_category_id_index ON manga_category(category_id);
|
||||
|
||||
insert:
|
||||
INSERT INTO manga_category(manga_id, category_id)
|
||||
VALUES (:mangaId, :categoryId);
|
||||
VALUES (:mangaId, :categoryId)
|
||||
ON CONFLICT(manga_id, category_id) DO NOTHING;
|
||||
|
||||
deleteMangaCategoryByMangaId:
|
||||
DELETE FROM manga_category
|
||||
|
||||
@@ -24,63 +24,64 @@ CREATE INDEX manga_track_tracker_id_remote_id_index ON manga_track(tracker_id, r
|
||||
|
||||
delete:
|
||||
DELETE FROM manga_track
|
||||
WHERE manga_id = :mangaId AND tracker_id = :syncId;
|
||||
WHERE manga_id = :mangaId
|
||||
AND tracker_id = :trackerId;
|
||||
|
||||
getTracks:
|
||||
SELECT
|
||||
id, manga_id, tracker_id, remote_id, library_id,
|
||||
title, last_chapter_read, total_chapters, status, score,
|
||||
remote_url, start_date, finish_date, private
|
||||
id, manga_id, tracker_id, remote_id, remote_url, library_id, title, last_chapter_read,
|
||||
total_chapters, status, score, start_date, finish_date, private
|
||||
FROM manga_track;
|
||||
|
||||
getTrackById:
|
||||
SELECT
|
||||
id, manga_id, tracker_id, remote_id, library_id,
|
||||
title, last_chapter_read, total_chapters, status, score,
|
||||
remote_url, start_date, finish_date, private
|
||||
id, manga_id, tracker_id, remote_id, remote_url, library_id, title, last_chapter_read,
|
||||
total_chapters, status, score, start_date, finish_date, private
|
||||
FROM manga_track
|
||||
WHERE id = :id;
|
||||
|
||||
getTracksByMangaId:
|
||||
SELECT
|
||||
id, manga_id, tracker_id, remote_id, library_id,
|
||||
title, last_chapter_read, total_chapters, status, score,
|
||||
remote_url, start_date, finish_date, private
|
||||
id, manga_id, tracker_id, remote_id, remote_url, library_id, title, last_chapter_read,
|
||||
total_chapters, status, score, start_date, finish_date, private
|
||||
FROM manga_track
|
||||
WHERE manga_id = :mangaId;
|
||||
|
||||
upsert:
|
||||
INSERT INTO manga_track(manga_id,tracker_id,remote_id,library_id,title,last_chapter_read,total_chapters,status,score,remote_url,start_date,finish_date,private)
|
||||
VALUES (:mangaId,:syncId,:remoteId,:libraryId,:title,:lastChapterRead,:totalChapters,:status,:score,:remoteUrl,:startDate,:finishDate,:private)
|
||||
ON CONFLICT(manga_id, tracker_id)
|
||||
DO UPDATE
|
||||
SET
|
||||
remote_id = :remoteId,
|
||||
library_id = :libraryId,
|
||||
title = :title,
|
||||
last_chapter_read = :lastChapterRead,
|
||||
total_chapters = :totalChapters,
|
||||
status = :status,
|
||||
score = :score,
|
||||
remote_url = :remoteUrl,
|
||||
start_date = :startDate,
|
||||
finish_date = :finishDate,
|
||||
private = :private
|
||||
WHERE manga_id = :mangaId AND tracker_id = :syncId;
|
||||
INSERT INTO manga_track(
|
||||
manga_id, tracker_id, remote_id, remote_url, library_id, title, last_chapter_read,
|
||||
total_chapters, status, score, start_date, finish_date, private
|
||||
)
|
||||
VALUES (
|
||||
:mangaId, :trackerId, :remoteId, :remoteUrl, :libraryId, :title, :lastChapterRead,
|
||||
:totalChapters, :status, :score, :startDate, :finishDate, :private
|
||||
)
|
||||
ON CONFLICT(manga_id, tracker_id) DO UPDATE SET
|
||||
remote_id = excluded.remote_id,
|
||||
remote_url = excluded.remote_url,
|
||||
library_id = excluded.library_id,
|
||||
title = excluded.title,
|
||||
last_chapter_read = excluded.last_chapter_read,
|
||||
total_chapters = excluded.total_chapters,
|
||||
status = excluded.status,
|
||||
score = excluded.score,
|
||||
start_date = excluded.start_date,
|
||||
finish_date = excluded.finish_date,
|
||||
private = excluded.private;
|
||||
|
||||
update:
|
||||
UPDATE manga_track
|
||||
SET
|
||||
manga_id = coalesce(:mangaId, manga_id),
|
||||
tracker_id = coalesce(:syncId, tracker_id),
|
||||
remote_id = coalesce(:mediaId, remote_id),
|
||||
tracker_id = coalesce(:trackerId, tracker_id),
|
||||
remote_id = coalesce(:remoteId, remote_id),
|
||||
remote_url = coalesce(:remoteUrl, remote_url),
|
||||
library_id = coalesce(:libraryId, library_id),
|
||||
title = coalesce(:title, title),
|
||||
last_chapter_read = coalesce(:lastChapterRead, last_chapter_read),
|
||||
total_chapters = coalesce(:totalChapter, total_chapters),
|
||||
total_chapters = coalesce(:totalChapters, total_chapters),
|
||||
status = coalesce(:status, status),
|
||||
score = coalesce(:score, score),
|
||||
remote_url = coalesce(:trackingUrl, remote_url),
|
||||
start_date = coalesce(:startDate, start_date),
|
||||
finish_date = coalesce(:finishDate, finish_date),
|
||||
private = coalesce(:private, private)
|
||||
|
||||
@@ -5,20 +5,17 @@ CREATE TABLE source(
|
||||
);
|
||||
|
||||
findAll:
|
||||
SELECT id, language, name
|
||||
SELECT id, name, language
|
||||
FROM source;
|
||||
|
||||
findOne:
|
||||
SELECT id, language, name
|
||||
SELECT id, name, language
|
||||
FROM source
|
||||
WHERE id = :id;
|
||||
|
||||
upsert:
|
||||
INSERT INTO source(id, language, name)
|
||||
VALUES (:id, :lang, :name)
|
||||
ON CONFLICT(id)
|
||||
DO UPDATE
|
||||
SET
|
||||
language = :lang,
|
||||
name = :name
|
||||
WHERE id = :id;
|
||||
INSERT INTO source(id, name, language)
|
||||
VALUES (:id, :name, :language)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
name = excluded.name,
|
||||
language = excluded.language;
|
||||
|
||||
@@ -46,18 +46,22 @@ ORDER BY readAt DESC;
|
||||
|
||||
getLatestHistory:
|
||||
SELECT
|
||||
id,
|
||||
mangaId,
|
||||
chapterId,
|
||||
title,
|
||||
thumbnailUrl,
|
||||
sourceId,
|
||||
favorite,
|
||||
coverLastModified,
|
||||
chapterNumber,
|
||||
readAt,
|
||||
readDuration
|
||||
FROM historyView
|
||||
WHERE historyView.readAt > 0
|
||||
ORDER BY readAt DESC
|
||||
history.id AS id,
|
||||
manga.id AS mangaId,
|
||||
chapter.id AS chapterId,
|
||||
manga.remote_title AS title,
|
||||
manga.remote_cover AS thumbnailUrl,
|
||||
manga.source_id AS sourceId,
|
||||
manga.user_favorite_at IS NOT NULL AS favorite,
|
||||
manga.state_cover_last_modified AS coverLastModified,
|
||||
chapter.remote_chapter_number AS chapterNumber,
|
||||
history.read_at AS readAt,
|
||||
history.read_duration AS readDuration
|
||||
FROM history
|
||||
JOIN manga
|
||||
ON manga.id = history.manga_id
|
||||
JOIN chapter
|
||||
ON chapter.id = history.chapter_id
|
||||
WHERE history.read_at > 0
|
||||
ORDER BY history.read_at DESC
|
||||
LIMIT 1;
|
||||
|
||||
@@ -40,11 +40,10 @@ WHERE M.user_favorite_at IS NOT NULL;
|
||||
|
||||
library:
|
||||
SELECT
|
||||
id, source_id, remote_url, remote_artist, remote_author,
|
||||
remote_description, remote_genre, remote_title, remote_status, remote_cover,
|
||||
state_chapter_last_update, state_chapter_next_update, state_initialized, user_reader_flags,
|
||||
user_chapter_flags, state_cover_last_modified, remote_update_strategy, state_chapter_fetch_interval,
|
||||
user_notes, remote_memo,
|
||||
user_favorite_at, totalCount, readCount, latestUpload, chapterFetchedAt,
|
||||
lastRead, bookmarkCount, categories
|
||||
id, source_id, remote_url, remote_title, remote_author, remote_artist, remote_description,
|
||||
remote_genre, remote_status, remote_cover, remote_update_strategy, remote_memo,
|
||||
user_favorite_at, user_notes, user_reader_flags, user_chapter_flags,
|
||||
state_chapter_last_update, state_chapter_next_update, state_chapter_fetch_interval,
|
||||
state_cover_last_modified, state_initialized, totalCount, readCount, latestUpload,
|
||||
chapterFetchedAt, lastRead, bookmarkCount, categories
|
||||
FROM libraryView;
|
||||
|
||||
@@ -25,23 +25,11 @@ WHERE user_favorite_at IS NOT NULL
|
||||
AND state_date_fetch > user_favorite_at
|
||||
ORDER BY state_date_fetch DESC;
|
||||
|
||||
getRecentUpdates:
|
||||
SELECT
|
||||
mangaId, mangaTitle, chapterId, chapterName,
|
||||
scanlator, chapterUrl, read, bookmark,
|
||||
lastPageRead, sourceId, favorite, thumbnailUrl,
|
||||
coverLastModified, dateUpload, dateFetch, excludedScanlator
|
||||
FROM updatesView
|
||||
WHERE dateUpload > :after
|
||||
ORDER BY dateFetch DESC
|
||||
LIMIT :limit;
|
||||
|
||||
getRecentUpdatesWithFilters:
|
||||
SELECT
|
||||
mangaId, mangaTitle, chapterId, chapterName,
|
||||
scanlator, chapterUrl, read, bookmark,
|
||||
lastPageRead, sourceId, favorite, thumbnailUrl,
|
||||
coverLastModified, dateUpload, dateFetch, excludedScanlator
|
||||
mangaId, mangaTitle, chapterId, chapterName, scanlator, chapterUrl, read, bookmark,
|
||||
lastPageRead, sourceId, favorite, thumbnailUrl, coverLastModified, dateUpload, dateFetch,
|
||||
excludedScanlator
|
||||
FROM updatesView
|
||||
WHERE dateUpload > :after
|
||||
AND (:read IS NULL OR read = :read)
|
||||
@@ -52,19 +40,17 @@ AND (
|
||||
OR (:started = 0 AND lastPageRead = 0 AND read = 0)
|
||||
)
|
||||
AND (:bookmarked IS NULL OR bookmark = :bookmarked)
|
||||
AND (
|
||||
(excludedScanlator IS NULL OR :hideExcludedScanlators = 0)
|
||||
)
|
||||
AND (excludedScanlator IS NULL OR :hideExcludedScanlators = 0)
|
||||
AND (
|
||||
-- includedEmpty being true expresses "don't care" state and bypasses the membership filter
|
||||
:includedEmpty
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM manga_category
|
||||
WHERE manga_category.manga_id = mangaId
|
||||
AND COALESCE(manga_category.category_id, 0) IN :includedCategories
|
||||
AND manga_category.category_id IN :includedCategories
|
||||
)
|
||||
OR (0 IN :includedCategories AND NOT EXISTS (
|
||||
SELECT 1 FROM manga_category mc WHERE mc.manga_id = mangaId)
|
||||
SELECT 1 FROM manga_category WHERE manga_category.manga_id = mangaId)
|
||||
)
|
||||
)
|
||||
AND (
|
||||
@@ -74,7 +60,7 @@ AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM manga_category
|
||||
WHERE manga_category.manga_id = mangaId
|
||||
AND COALESCE(manga_category.category_id, 0) IN :excludedCategories
|
||||
AND manga_category.category_id IN :excludedCategories
|
||||
)
|
||||
AND (
|
||||
0 NOT IN :excludedCategories
|
||||
@@ -85,14 +71,24 @@ AND (
|
||||
ORDER BY dateFetch DESC
|
||||
LIMIT :limit;
|
||||
|
||||
getUpdatesByReadStatus:
|
||||
getReadUpdates:
|
||||
SELECT
|
||||
mangaId, mangaTitle, chapterId, chapterName,
|
||||
scanlator, chapterUrl, read, bookmark,
|
||||
lastPageRead, sourceId, favorite, thumbnailUrl,
|
||||
coverLastModified, dateUpload, dateFetch, excludedScanlator
|
||||
mangaId, mangaTitle, chapterId, chapterName, scanlator, chapterUrl, read, bookmark,
|
||||
lastPageRead, sourceId, favorite, thumbnailUrl, coverLastModified, dateUpload, dateFetch,
|
||||
excludedScanlator
|
||||
FROM updatesView
|
||||
WHERE read = :read
|
||||
WHERE read = 1
|
||||
AND dateUpload > :after
|
||||
ORDER BY dateFetch DESC
|
||||
LIMIT :limit;
|
||||
|
||||
getUnreadUpdates:
|
||||
SELECT
|
||||
mangaId, mangaTitle, chapterId, chapterName, scanlator, chapterUrl, read, bookmark,
|
||||
lastPageRead, sourceId, favorite, thumbnailUrl, coverLastModified, dateUpload, dateFetch,
|
||||
excludedScanlator
|
||||
FROM updatesView
|
||||
WHERE read = 0
|
||||
AND dateUpload > :after
|
||||
ORDER BY dateFetch DESC
|
||||
LIMIT :limit;
|
||||
|
||||
Reference in New Issue
Block a user