Adapt model implementation for TachiyomiX 1.6 (#3412)

This commit is contained in:
AntsyLich
2026-06-15 23:32:39 +06:00
committed by GitHub
parent f00cac265f
commit 94cab02c1e
26 changed files with 177 additions and 28 deletions
@@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.data.backup.models.backupChapterMapper
import eu.kanade.tachiyomi.data.backup.models.backupTrackMapper import eu.kanade.tachiyomi.data.backup.models.backupTrackMapper
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
import tachiyomi.data.Database import tachiyomi.data.Database
import tachiyomi.data.MemoColumnAdapter
import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.history.interactor.GetHistory import tachiyomi.domain.history.interactor.GetHistory
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
@@ -107,4 +108,5 @@ private fun Manga.toBackupManga() =
version = this.version, version = this.version,
notes = this.notes, notes = this.notes,
initialized = this.initialized, initialized = this.initialized,
memo = MemoColumnAdapter.encode(this.memo),
) )
@@ -1,11 +1,14 @@
package eu.kanade.tachiyomi.data.backup.models package eu.kanade.tachiyomi.data.backup.models
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.protobuf.ProtoNumber import kotlinx.serialization.protobuf.ProtoNumber
import mihon.core.common.extensions.EMPTY
import tachiyomi.data.MemoColumnAdapter
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
@Serializable @Serializable
data class BackupChapter( class BackupChapter(
// in 1.x some of these values have different names // in 1.x some of these values have different names
// url is called key in 1.x // url is called key in 1.x
@ProtoNumber(1) var url: String, @ProtoNumber(1) var url: String,
@@ -22,6 +25,7 @@ data class BackupChapter(
@ProtoNumber(10) var sourceOrder: Long = 0, @ProtoNumber(10) var sourceOrder: Long = 0,
@ProtoNumber(11) var lastModifiedAt: Long = 0, @ProtoNumber(11) var lastModifiedAt: Long = 0,
@ProtoNumber(12) var version: Long = 0, @ProtoNumber(12) var version: Long = 0,
@ProtoNumber(13) var memo: ByteArray = byteArrayOf(),
) { ) {
fun toChapterImpl(): Chapter { fun toChapterImpl(): Chapter {
return Chapter.create().copy( return Chapter.create().copy(
@@ -37,6 +41,7 @@ data class BackupChapter(
sourceOrder = this@BackupChapter.sourceOrder, sourceOrder = this@BackupChapter.sourceOrder,
lastModifiedAt = this@BackupChapter.lastModifiedAt, lastModifiedAt = this@BackupChapter.lastModifiedAt,
version = this@BackupChapter.version, version = this@BackupChapter.version,
memo = MemoColumnAdapter.decode(this@BackupChapter.memo),
) )
} }
} }
@@ -57,6 +62,7 @@ val backupChapterMapper = {
lastModifiedAt: Long, lastModifiedAt: Long,
version: Long, version: Long,
_: Long, _: Long,
memo: JsonObject,
-> ->
BackupChapter( BackupChapter(
url = url, url = url,
@@ -71,5 +77,6 @@ val backupChapterMapper = {
sourceOrder = sourceOrder, sourceOrder = sourceOrder,
lastModifiedAt = lastModifiedAt, lastModifiedAt = lastModifiedAt,
version = version, version = version,
memo = MemoColumnAdapter.encode(memo),
) )
} }
@@ -2,12 +2,15 @@ package eu.kanade.tachiyomi.data.backup.models
import eu.kanade.tachiyomi.source.model.UpdateStrategy import eu.kanade.tachiyomi.source.model.UpdateStrategy
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.protobuf.ProtoNumber import kotlinx.serialization.protobuf.ProtoNumber
import mihon.core.common.extensions.EMPTY
import tachiyomi.data.MemoColumnAdapter
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
@Serializable @Serializable
data class BackupManga( class BackupManga(
// in 1.x some of these values have different names // in 1.x some of these values have different names
@ProtoNumber(1) var source: Long, @ProtoNumber(1) var source: Long,
// url is called key in 1.x // url is called key in 1.x
@@ -43,6 +46,7 @@ data class BackupManga(
@ProtoNumber(109) var version: Long = 0, @ProtoNumber(109) var version: Long = 0,
@ProtoNumber(110) var notes: String = "", @ProtoNumber(110) var notes: String = "",
@ProtoNumber(111) var initialized: Boolean = false, @ProtoNumber(111) var initialized: Boolean = false,
@ProtoNumber(112) var memo: ByteArray = byteArrayOf(),
) { ) {
fun getMangaImpl(): Manga { fun getMangaImpl(): Manga {
return Manga.create().copy( return Manga.create().copy(
@@ -65,6 +69,7 @@ data class BackupManga(
version = this@BackupManga.version, version = this@BackupManga.version,
notes = this@BackupManga.notes, notes = this@BackupManga.notes,
initialized = this@BackupManga.initialized, initialized = this@BackupManga.initialized,
memo = MemoColumnAdapter.decode(this@BackupManga.memo),
) )
} }
} }
@@ -10,6 +10,8 @@ import eu.kanade.tachiyomi.data.backup.models.BackupHistory
import eu.kanade.tachiyomi.data.backup.models.BackupManga import eu.kanade.tachiyomi.data.backup.models.BackupManga
import eu.kanade.tachiyomi.data.backup.models.BackupTracking import eu.kanade.tachiyomi.data.backup.models.BackupTracking
import tachiyomi.data.Database import tachiyomi.data.Database
import tachiyomi.data.MemoColumnAdapter
import tachiyomi.data.MemoColumnAdapter.encode
import tachiyomi.data.UpdateStrategyColumnAdapter import tachiyomi.data.UpdateStrategyColumnAdapter
import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
@@ -134,6 +136,7 @@ class MangaRestorer(
version = manga.version, version = manga.version,
isSyncing = 1, isSyncing = 1,
notes = manga.notes, notes = manga.notes,
memo = manga.memo.let(MemoColumnAdapter::encode),
) )
return manga return manga
} }
@@ -207,6 +210,7 @@ class MangaRestorer(
chapter.dateFetch, chapter.dateFetch,
chapter.dateUpload, chapter.dateUpload,
chapter.version, chapter.version,
chapter.memo,
) )
} }
} }
@@ -230,6 +234,7 @@ class MangaRestorer(
chapterId = chapter.id, chapterId = chapter.id,
version = chapter.version, version = chapter.version,
isSyncing = 0, isSyncing = 0,
memo = chapter.memo.let(MemoColumnAdapter::encode),
) )
} }
} }
@@ -263,6 +268,7 @@ class MangaRestorer(
updateStrategy = manga.updateStrategy, updateStrategy = manga.updateStrategy,
version = manga.version, version = manga.version,
notes = manga.notes, notes = manga.notes,
memo = manga.memo,
) )
.awaitAsOne() .awaitAsOne()
} }
@@ -47,5 +47,6 @@ fun Chapter.toDomainChapter(): DomainChapter? {
scanlator = scanlator, scanlator = scanlator,
lastModifiedAt = last_modified, lastModifiedAt = last_modified,
version = version, version = version,
memo = memo,
) )
} }
@@ -2,6 +2,8 @@
package eu.kanade.tachiyomi.data.database.models package eu.kanade.tachiyomi.data.database.models
import kotlinx.serialization.json.JsonObject
class ChapterImpl : Chapter { class ChapterImpl : Chapter {
override var id: Long? = null override var id: Long? = null
@@ -32,6 +34,8 @@ class ChapterImpl : Chapter {
override var version: Long = 0 override var version: Long = 0
override var memo: JsonObject = JsonObject(emptyMap())
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other == null || javaClass != other.javaClass) return false if (other == null || javaClass != other.javaClass) return false
@@ -31,10 +31,12 @@ import nl.adaptivity.xmlutil.XmlDeclMode
import nl.adaptivity.xmlutil.core.XmlVersion import nl.adaptivity.xmlutil.core.XmlVersion
import nl.adaptivity.xmlutil.serialization.XML import nl.adaptivity.xmlutil.serialization.XML
import tachiyomi.core.common.storage.AndroidStorageFolderProvider import tachiyomi.core.common.storage.AndroidStorageFolderProvider
import tachiyomi.data.Chapters
import tachiyomi.data.Database import tachiyomi.data.Database
import tachiyomi.data.DateColumnAdapter import tachiyomi.data.DateColumnAdapter
import tachiyomi.data.History import tachiyomi.data.History
import tachiyomi.data.Mangas import tachiyomi.data.Mangas
import tachiyomi.data.MemoColumnAdapter
import tachiyomi.data.StringListColumnAdapter import tachiyomi.data.StringListColumnAdapter
import tachiyomi.data.UpdateStrategyColumnAdapter import tachiyomi.data.UpdateStrategyColumnAdapter
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
@@ -81,6 +83,10 @@ class AppModule(val app: Application) : InjektModule {
mangasAdapter = Mangas.Adapter( mangasAdapter = Mangas.Adapter(
genreAdapter = StringListColumnAdapter, genreAdapter = StringListColumnAdapter,
update_strategyAdapter = UpdateStrategyColumnAdapter, update_strategyAdapter = UpdateStrategyColumnAdapter,
memoAdapter = MemoColumnAdapter,
),
chaptersAdapter = Chapters.Adapter(
memoAdapter = MemoColumnAdapter,
), ),
) )
} }
@@ -132,6 +132,7 @@ class UpdateMangaFromRemote(
status = remoteManga.status.toLong(), status = remoteManga.status.toLong(),
updateStrategy = remoteManga.update_strategy, updateStrategy = remoteManga.update_strategy,
initialized = true, initialized = true,
memo = remoteManga.memo,
), ),
) )
if (success && title != null) { if (success && title != null) {
@@ -0,0 +1,8 @@
package mihon.core.common.extensions
import kotlinx.serialization.json.JsonObject
val JsonObjectEmpty = JsonObject(emptyMap())
val JsonObject.Companion.EMPTY: JsonObject
inline get() = JsonObjectEmpty
@@ -2,6 +2,10 @@ package tachiyomi.data
import app.cash.sqldelight.ColumnAdapter import app.cash.sqldelight.ColumnAdapter
import eu.kanade.tachiyomi.source.model.UpdateStrategy import eu.kanade.tachiyomi.source.model.UpdateStrategy
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject
import java.util.Date import java.util.Date
object DateColumnAdapter : ColumnAdapter<Date, Long> { object DateColumnAdapter : ColumnAdapter<Date, Long> {
@@ -27,3 +31,13 @@ object UpdateStrategyColumnAdapter : ColumnAdapter<UpdateStrategy, Long> {
override fun encode(value: UpdateStrategy): Long = value.ordinal.toLong() override fun encode(value: UpdateStrategy): Long = value.ordinal.toLong()
} }
object MemoColumnAdapter : ColumnAdapter<JsonObject, ByteArray> {
override fun decode(databaseValue: ByteArray): JsonObject {
return Json.decodeFromString<JsonObject>(databaseValue.decodeToString())
}
override fun encode(value: JsonObject): ByteArray {
return value.toString().encodeToByteArray()
}
}
@@ -4,10 +4,13 @@ import app.cash.sqldelight.async.coroutines.awaitAsList
import app.cash.sqldelight.async.coroutines.awaitAsOne import app.cash.sqldelight.async.coroutines.awaitAsOne
import app.cash.sqldelight.async.coroutines.awaitAsOneOrNull import app.cash.sqldelight.async.coroutines.awaitAsOneOrNull
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.json.JsonObject
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.common.util.lang.toLong import tachiyomi.core.common.util.lang.toLong
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.data.Database import tachiyomi.data.Database
import tachiyomi.data.MemoColumnAdapter
import tachiyomi.data.MemoColumnAdapter.encode
import tachiyomi.data.subscribeToList import tachiyomi.data.subscribeToList
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.chapter.model.ChapterUpdate import tachiyomi.domain.chapter.model.ChapterUpdate
@@ -34,6 +37,7 @@ class ChapterRepositoryImpl(
chapter.dateFetch, chapter.dateFetch,
chapter.dateUpload, chapter.dateUpload,
chapter.version, chapter.version,
chapter.memo,
) )
.awaitAsOne() .awaitAsOne()
chapter.copy(id = chapterId) chapter.copy(id = chapterId)
@@ -71,6 +75,7 @@ class ChapterRepositoryImpl(
chapterId = chapterUpdate.id, chapterId = chapterUpdate.id,
version = chapterUpdate.version, version = chapterUpdate.version,
isSyncing = 0, isSyncing = 0,
memo = chapterUpdate.memo?.let(MemoColumnAdapter::encode),
) )
} }
} }
@@ -143,6 +148,7 @@ class ChapterRepositoryImpl(
lastModifiedAt: Long, lastModifiedAt: Long,
version: Long, version: Long,
isSyncing: Long, isSyncing: Long,
memo: JsonObject,
): Chapter = Chapter( ): Chapter = Chapter(
id = id, id = id,
mangaId = mangaId, mangaId = mangaId,
@@ -158,5 +164,6 @@ class ChapterRepositoryImpl(
scanlator = scanlator, scanlator = scanlator,
lastModifiedAt = lastModifiedAt, lastModifiedAt = lastModifiedAt,
version = version, version = version,
memo = memo,
) )
} }
@@ -1,6 +1,7 @@
package tachiyomi.data.manga package tachiyomi.data.manga
import eu.kanade.tachiyomi.source.model.UpdateStrategy import eu.kanade.tachiyomi.source.model.UpdateStrategy
import kotlinx.serialization.json.JsonObject
import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.library.model.LibraryManga
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaWithChapterCount import tachiyomi.domain.manga.model.MangaWithChapterCount
@@ -33,6 +34,7 @@ object MangaMapper {
version: Long, version: Long,
isSyncing: Long, isSyncing: Long,
notes: String, notes: String,
memo: JsonObject,
): Manga = Manga( ): Manga = Manga(
id = id, id = id,
source = source, source = source,
@@ -58,6 +60,7 @@ object MangaMapper {
favoriteModifiedAt = favoriteModifiedAt, favoriteModifiedAt = favoriteModifiedAt,
version = version, version = version,
notes = notes, notes = notes,
memo = memo,
) )
fun mapLibraryManga( fun mapLibraryManga(
@@ -86,6 +89,7 @@ object MangaMapper {
version: Long, version: Long,
isSyncing: Long, isSyncing: Long,
notes: String, notes: String,
memo: JsonObject,
totalCount: Long, totalCount: Long,
readCount: Double, readCount: Double,
latestUpload: Long, latestUpload: Long,
@@ -120,6 +124,7 @@ object MangaMapper {
version, version,
isSyncing, isSyncing,
notes, notes,
memo,
), ),
categories = categories.split(",").map { it.toLong() }, categories = categories.split(",").map { it.toLong() },
totalChapters = totalCount, totalChapters = totalCount,
@@ -156,6 +161,7 @@ object MangaMapper {
version: Long, version: Long,
isSyncing: Long, isSyncing: Long,
notes: String, notes: String,
memo: JsonObject,
totalCount: Long, totalCount: Long,
): MangaWithChapterCount = MangaWithChapterCount( ): MangaWithChapterCount = MangaWithChapterCount(
manga = mapManga( manga = mapManga(
@@ -184,6 +190,7 @@ object MangaMapper {
version, version,
isSyncing, isSyncing,
notes, notes,
memo,
), ),
chapterCount = totalCount, chapterCount = totalCount,
) )
@@ -7,6 +7,7 @@ import kotlinx.coroutines.flow.Flow
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.common.util.system.logcat import tachiyomi.core.common.util.system.logcat
import tachiyomi.data.Database import tachiyomi.data.Database
import tachiyomi.data.MemoColumnAdapter
import tachiyomi.data.StringListColumnAdapter import tachiyomi.data.StringListColumnAdapter
import tachiyomi.data.UpdateStrategyColumnAdapter import tachiyomi.data.UpdateStrategyColumnAdapter
import tachiyomi.data.subscribeToList import tachiyomi.data.subscribeToList
@@ -154,6 +155,7 @@ class MangaRepositoryImpl(
dateAdded = it.dateAdded, dateAdded = it.dateAdded,
updateStrategy = it.updateStrategy, updateStrategy = it.updateStrategy,
version = it.version, version = it.version,
memo = it.memo,
updateTitle = it.title.isNotBlank(), updateTitle = it.title.isNotBlank(),
updateCover = !it.thumbnailUrl.isNullOrBlank(), updateCover = !it.thumbnailUrl.isNullOrBlank(),
updateDetails = it.initialized, updateDetails = it.initialized,
@@ -191,6 +193,7 @@ class MangaRepositoryImpl(
version = value.version, version = value.version,
isSyncing = 0, isSyncing = 0,
notes = value.notes, notes = value.notes,
memo = value.memo?.let(MemoColumnAdapter::encode),
) )
} }
} }
@@ -1,4 +1,5 @@
import kotlin.Boolean; import kotlin.Boolean;
import kotlinx.serialization.json.JsonObject;
CREATE TABLE chapters( CREATE TABLE chapters(
_id INTEGER NOT NULL PRIMARY KEY, _id INTEGER NOT NULL PRIMARY KEY,
@@ -16,6 +17,7 @@ CREATE TABLE chapters(
last_modified_at INTEGER NOT NULL DEFAULT 0, last_modified_at INTEGER NOT NULL DEFAULT 0,
version INTEGER NOT NULL DEFAULT 0, version INTEGER NOT NULL DEFAULT 0,
is_syncing INTEGER NOT NULL DEFAULT 0, is_syncing INTEGER NOT NULL DEFAULT 0,
memo BLOB AS JsonObject NOT NULL DEFAULT '{}',
FOREIGN KEY(manga_id) REFERENCES mangas (_id) FOREIGN KEY(manga_id) REFERENCES mangas (_id)
ON DELETE CASCADE ON DELETE CASCADE
); );
@@ -98,12 +100,12 @@ SET is_syncing = 0
WHERE is_syncing = 1; WHERE is_syncing = 1;
insert: insert:
INSERT INTO chapters(manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order, date_fetch, date_upload, last_modified_at, version, is_syncing) INSERT INTO chapters(manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order, date_fetch, date_upload, last_modified_at, version, is_syncing, memo)
VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, 0, :version, 0); VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, 0, :version, 0, :memo);
insertReturningId: insertReturningId:
INSERT INTO chapters(manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order, date_fetch, date_upload, last_modified_at, version, is_syncing) INSERT INTO chapters(manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order, date_fetch, date_upload, last_modified_at, version, is_syncing, memo)
VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, 0, :version, 0) VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, 0, :version, 0, :memo)
RETURNING _id; RETURNING _id;
update: update:
@@ -120,5 +122,6 @@ SET manga_id = coalesce(:mangaId, manga_id),
date_fetch = coalesce(:dateFetch, date_fetch), date_fetch = coalesce(:dateFetch, date_fetch),
date_upload = coalesce(:dateUpload, date_upload), date_upload = coalesce(:dateUpload, date_upload),
version = coalesce(:version, version), version = coalesce(:version, version),
is_syncing = coalesce(:isSyncing, is_syncing) is_syncing = coalesce(:isSyncing, is_syncing),
memo = coalesce(:memo, memo)
WHERE _id = :chapterId; WHERE _id = :chapterId;
@@ -1,7 +1,8 @@
import eu.kanade.tachiyomi.source.model.UpdateStrategy; import eu.kanade.tachiyomi.source.model.UpdateStrategy;
import kotlin.collections.List;
import kotlin.Boolean; import kotlin.Boolean;
import kotlin.String; import kotlin.String;
import kotlin.collections.List;
import kotlinx.serialization.json.JsonObject;
CREATE TABLE mangas( CREATE TABLE mangas(
_id INTEGER NOT NULL PRIMARY KEY, _id INTEGER NOT NULL PRIMARY KEY,
@@ -28,7 +29,8 @@ CREATE TABLE mangas(
favorite_modified_at INTEGER, favorite_modified_at INTEGER,
version INTEGER NOT NULL DEFAULT 0, version INTEGER NOT NULL DEFAULT 0,
is_syncing INTEGER NOT NULL DEFAULT 0, is_syncing INTEGER NOT NULL DEFAULT 0,
notes TEXT NOT NULL DEFAULT "" notes TEXT NOT NULL DEFAULT "",
memo BLOB AS JsonObject NOT NULL DEFAULT '{}'
); );
CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1; CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1;
@@ -194,8 +196,8 @@ AND (
); );
insertReturningId: insertReturningId:
INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, calculate_interval, last_modified_at, version, notes) INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, calculate_interval, last_modified_at, version, notes, memo)
VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, 0, :version, :notes) VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, 0, :version, :notes, :memo)
RETURNING _id; RETURNING _id;
update: update:
@@ -221,7 +223,8 @@ UPDATE mangas SET
calculate_interval = coalesce(:calculateInterval, calculate_interval), calculate_interval = coalesce(:calculateInterval, calculate_interval),
version = coalesce(:version, version), version = coalesce(:version, version),
is_syncing = coalesce(:isSyncing, is_syncing), is_syncing = coalesce(:isSyncing, is_syncing),
notes = coalesce(:notes, notes) notes = coalesce(:notes, notes),
memo = coalesce(:memo, memo)
WHERE _id = :mangaId; WHERE _id = :mangaId;
insertNetworkManga { insertNetworkManga {
@@ -229,12 +232,12 @@ insertNetworkManga {
INSERT INTO mangas( INSERT INTO mangas(
source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, source, url, artist, author, description, genre, title, status, thumbnail_url, favorite,
last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added,
update_strategy, calculate_interval, last_modified_at, version update_strategy, calculate_interval, last_modified_at, version, memo
) )
SELECT SELECT
:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite,
:lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded,
:updateStrategy, :calculateInterval, 0, :version :updateStrategy, :calculateInterval, 0, :version, :memo
WHERE NOT EXISTS(SELECT 0 FROM mangas WHERE source = :source AND url = :url); WHERE NOT EXISTS(SELECT 0 FROM mangas WHERE source = :source AND url = :url);
-- Update the relevant details if applicable and not favorite -- Update the relevant details if applicable and not favorite
@@ -248,7 +251,8 @@ insertNetworkManga {
genre = CASE WHEN :updateDetails THEN :genre ELSE genre END, genre = CASE WHEN :updateDetails THEN :genre ELSE genre END,
status = CASE WHEN :updateDetails THEN :status ELSE status END, status = CASE WHEN :updateDetails THEN :status ELSE status END,
update_strategy = CASE WHEN :updateDetails THEN :updateStrategy ELSE update_strategy END, update_strategy = CASE WHEN :updateDetails THEN :updateStrategy ELSE update_strategy END,
initialized = :updateDetails initialized = :updateDetails,
memo = :memo
WHERE source = :source WHERE source = :source
AND url = :url AND url = :url
AND favorite = 0; AND favorite = 0;
@@ -0,0 +1,2 @@
ALTER TABLE mangas ADD COLUMN memo BLOB NOT NULL DEFAULT '{}';
ALTER TABLE chapters ADD COLUMN memo BLOB NOT NULL DEFAULT '{}';
@@ -1,5 +1,6 @@
package mihon.domain.extension.interactor package mihon.domain.extension.interactor
import kotlinx.serialization.json.Json
import mihon.domain.extension.repository.ExtensionStoreRepository import mihon.domain.extension.repository.ExtensionStoreRepository
class GetExtensionStoreCountAsFlow( class GetExtensionStoreCountAsFlow(
@@ -1,5 +1,8 @@
package tachiyomi.domain.chapter.model package tachiyomi.domain.chapter.model
import kotlinx.serialization.json.JsonObject
import mihon.core.common.extensions.EMPTY
data class Chapter( data class Chapter(
val id: Long, val id: Long,
val mangaId: Long, val mangaId: Long,
@@ -15,6 +18,7 @@ data class Chapter(
val scanlator: String?, val scanlator: String?,
val lastModifiedAt: Long, val lastModifiedAt: Long,
val version: Long, val version: Long,
val memo: JsonObject,
) { ) {
val isRecognizedNumber: Boolean val isRecognizedNumber: Boolean
get() = chapterNumber >= 0f get() = chapterNumber >= 0f
@@ -45,6 +49,7 @@ data class Chapter(
scanlator = null, scanlator = null,
lastModifiedAt = 0, lastModifiedAt = 0,
version = 1, version = 1,
memo = JsonObject.EMPTY,
) )
} }
} }
@@ -1,5 +1,7 @@
package tachiyomi.domain.chapter.model package tachiyomi.domain.chapter.model
import kotlinx.serialization.json.JsonObject
data class ChapterUpdate( data class ChapterUpdate(
val id: Long, val id: Long,
val mangaId: Long? = null, val mangaId: Long? = null,
@@ -14,6 +16,7 @@ data class ChapterUpdate(
val chapterNumber: Double? = null, val chapterNumber: Double? = null,
val scanlator: String? = null, val scanlator: String? = null,
val version: Long? = null, val version: Long? = null,
val memo: JsonObject? = null,
) )
fun Chapter.toChapterUpdate(): ChapterUpdate { fun Chapter.toChapterUpdate(): ChapterUpdate {
@@ -31,5 +34,6 @@ fun Chapter.toChapterUpdate(): ChapterUpdate {
chapterNumber, chapterNumber,
scanlator, scanlator,
version, version,
memo,
) )
} }
@@ -3,6 +3,8 @@ package tachiyomi.domain.manga.model
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.UpdateStrategy import eu.kanade.tachiyomi.source.model.UpdateStrategy
import kotlinx.serialization.json.JsonObject
import mihon.core.common.extensions.EMPTY
import tachiyomi.core.common.preference.TriState import tachiyomi.core.common.preference.TriState
import java.io.Serializable import java.io.Serializable
import java.time.Instant import java.time.Instant
@@ -33,6 +35,7 @@ data class Manga(
val favoriteModifiedAt: Long?, val favoriteModifiedAt: Long?,
val version: Long, val version: Long,
val notes: String, val notes: String,
val memo: JsonObject,
) : Serializable { ) : Serializable {
val expectedNextUpdate: Instant? val expectedNextUpdate: Instant?
@@ -128,6 +131,7 @@ data class Manga(
favoriteModifiedAt = null, favoriteModifiedAt = null,
version = 0L, version = 0L,
notes = "", notes = "",
memo = JsonObject.EMPTY,
) )
} }
} }
@@ -1,6 +1,7 @@
package tachiyomi.domain.manga.model package tachiyomi.domain.manga.model
import eu.kanade.tachiyomi.source.model.UpdateStrategy import eu.kanade.tachiyomi.source.model.UpdateStrategy
import kotlinx.serialization.json.JsonObject
data class MangaUpdate( data class MangaUpdate(
val id: Long, val id: Long,
@@ -25,6 +26,7 @@ data class MangaUpdate(
val initialized: Boolean? = null, val initialized: Boolean? = null,
val version: Long? = null, val version: Long? = null,
val notes: String? = null, val notes: String? = null,
val memo: JsonObject? = null,
) )
fun Manga.toMangaUpdate(): MangaUpdate { fun Manga.toMangaUpdate(): MangaUpdate {
@@ -51,5 +53,6 @@ fun Manga.toMangaUpdate(): MangaUpdate {
initialized = initialized, initialized = initialized,
version = version, version = version,
notes = notes, notes = notes,
memo = memo,
) )
} }
@@ -1,3 +1,19 @@
package eu.kanade.tachiyomi.source.model package eu.kanade.tachiyomi.source.model
data class MangasPage(val mangas: List<SManga>, val hasNextPage: Boolean) class MangasPage(val mangas: List<SManga>, val hasNextPage: Boolean) {
@Deprecated("MangasPage is now a regular class")
operator fun component1(): List<SManga> = mangas
@Deprecated("MangasPage is now a regular class")
operator fun component2(): Boolean = hasNextPage
@Deprecated("MangasPage is now a regular class")
fun copy(
mangas: List<SManga> = this.mangas,
hasNextPage: Boolean = this.hasNextPage,
): MangasPage = MangasPage(
mangas = mangas,
hasNextPage = hasNextPage,
)
}
@@ -2,6 +2,7 @@
package eu.kanade.tachiyomi.source.model package eu.kanade.tachiyomi.source.model
import kotlinx.serialization.json.JsonObject
import java.io.Serializable import java.io.Serializable
interface SChapter : Serializable { interface SChapter : Serializable {
@@ -10,12 +11,25 @@ interface SChapter : Serializable {
var name: String var name: String
var date_upload: Long
var chapter_number: Float var chapter_number: Float
var scanlator: String? var scanlator: String?
var date_upload: Long
/**
* Extra metadata associated with the chapter.
*
* The JSON object is not visible to users and intended for internal or source-specific
* purposes. Apps may define their own namespaced keys (e.g., `"mihon.*"`) for sources to populate.
*
* This allows apps to attach and ask for custom information without affecting the visible
* chapter data.
*
* @since tachiyomix 1.6
*/
var memo: JsonObject
fun copyFrom(other: SChapter) { fun copyFrom(other: SChapter) {
name = other.name name = other.name
url = other.url url = other.url
@@ -2,15 +2,19 @@
package eu.kanade.tachiyomi.source.model package eu.kanade.tachiyomi.source.model
import kotlinx.serialization.json.JsonObject
class SChapterImpl : SChapter { class SChapterImpl : SChapter {
override lateinit var url: String override lateinit var url: String
override lateinit var name: String override lateinit var name: String
override var date_upload: Long = 0
override var chapter_number: Float = -1f override var chapter_number: Float = -1f
override var scanlator: String? = null override var scanlator: String? = null
override var date_upload: Long = 0
override var memo: JsonObject = JsonObject(emptyMap())
} }
@@ -2,6 +2,7 @@
package eu.kanade.tachiyomi.source.model package eu.kanade.tachiyomi.source.model
import kotlinx.serialization.json.JsonObject
import java.io.Serializable import java.io.Serializable
interface SManga : Serializable { interface SManga : Serializable {
@@ -10,22 +11,35 @@ interface SManga : Serializable {
var title: String var title: String
var thumbnail_url: String?
var artist: String? var artist: String?
var author: String? var author: String?
var status: Int
var description: String? var description: String?
var genre: String? var genre: String?
var status: Int
var thumbnail_url: String?
var update_strategy: UpdateStrategy var update_strategy: UpdateStrategy
var initialized: Boolean var initialized: Boolean
/**
* Extra metadata associated with the manga.
*
* The JSON object is not visible to users and intended for internal or source-specific
* purposes. Apps may define their own namespaced keys (e.g., `"mihon.*"`) for sources to populate.
*
* This allows apps to attach and ask for custom information without affecting the visible
* manga data.
*
* @since tachiyomix 1.6
*/
var memo: JsonObject
fun getGenres(): List<String>? { fun getGenres(): List<String>? {
if (genre.isNullOrBlank()) return null if (genre.isNullOrBlank()) return null
return genre?.split(", ")?.map { it.trim() }?.filterNot { it.isBlank() }?.distinct() return genre?.split(", ")?.map { it.trim() }?.filterNot { it.isBlank() }?.distinct()
@@ -2,25 +2,29 @@
package eu.kanade.tachiyomi.source.model package eu.kanade.tachiyomi.source.model
import kotlinx.serialization.json.JsonObject
class SMangaImpl : SManga { class SMangaImpl : SManga {
override lateinit var url: String override lateinit var url: String
override lateinit var title: String override lateinit var title: String
override var thumbnail_url: String? = null
override var artist: String? = null override var artist: String? = null
override var author: String? = null override var author: String? = null
override var status: Int = 0
override var description: String? = null override var description: String? = null
override var genre: String? = null override var genre: String? = null
override var status: Int = 0
override var thumbnail_url: String? = null
override var update_strategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE override var update_strategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE
override var initialized: Boolean = false override var initialized: Boolean = false
override var memo: JsonObject = JsonObject(emptyMap())
} }