Initial support for local tag search
BREAKING CHANGE: Upgrade database
This commit is contained in:
1 parent
e1f34a7b1b
commit
e0e5a155aa
20 files changed
+761
-245
No files matched your search
@@ -0,0 +1,525 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 22,
|
||||
"identityHash": "f61ae3beef3739c0de7db945d2c9a4a4",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "GALLERIES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TOKEN` TEXT NOT NULL, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_TAGS` TEXT, `PAGES` INTEGER NOT NULL DEFAULT 0, `SIMPLE_LANGUAGE` TEXT, `FAVORITE_SLOT` INTEGER NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "TOKEN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbKey",
|
||||
"columnName": "THUMB",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "posted",
|
||||
"columnName": "POSTED",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "uploader",
|
||||
"columnName": "UPLOADER",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "rating",
|
||||
"columnName": "RATING",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleTags",
|
||||
"columnName": "SIMPLE_TAGS",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "pages",
|
||||
"columnName": "PAGES",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "0"
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleLanguage",
|
||||
"columnName": "SIMPLE_LANGUAGE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "favoriteSlot",
|
||||
"columnName": "FAVORITE_SLOT",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_LABELS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `POSITION` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_DOWNLOAD_LABELS_LABEL",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"LABEL"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DOWNLOAD_LABELS_LABEL` ON `${TABLE_NAME}` (`LABEL`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOADS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`LABEL`) REFERENCES `DOWNLOAD_LABELS`(`LABEL`) ON UPDATE CASCADE ON DELETE SET NULL )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "state",
|
||||
"columnName": "STATE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "legacy",
|
||||
"columnName": "LEGACY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "label",
|
||||
"columnName": "LABEL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_DOWNLOADS_LABEL",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"LABEL"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_DOWNLOADS_LABEL` ON `${TABLE_NAME}` (`LABEL`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "GALLERIES",
|
||||
"onDelete": "NO ACTION",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"GID"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "DOWNLOAD_LABELS",
|
||||
"onDelete": "SET NULL",
|
||||
"onUpdate": "CASCADE",
|
||||
"columns": [
|
||||
"LABEL"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"LABEL"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "DOWNLOAD_DIRNAME",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "dirname",
|
||||
"columnName": "DIRNAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "FILTER",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "text",
|
||||
"columnName": "TEXT",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "enable",
|
||||
"columnName": "ENABLE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "HISTORY",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "GALLERIES",
|
||||
"onDelete": "NO ACTION",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"GID"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"GID"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "LOCAL_FAVORITES",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "TIME",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "GALLERIES",
|
||||
"onDelete": "NO ACTION",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"GID"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"GID"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "PROGRESS",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `PAGE` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "gid",
|
||||
"columnName": "GID",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "page",
|
||||
"columnName": "PAGE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"GID"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "GALLERIES",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"GID"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"GID"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "QUICK_SEARCH",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL, PRIMARY KEY(`_id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "NAME",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mode",
|
||||
"columnName": "MODE",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "category",
|
||||
"columnName": "CATEGORY",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "keyword",
|
||||
"columnName": "KEYWORD",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "advanceSearch",
|
||||
"columnName": "ADVANCE_SEARCH",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "minRating",
|
||||
"columnName": "MIN_RATING",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageFrom",
|
||||
"columnName": "PAGE_FROM",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "pageTo",
|
||||
"columnName": "PAGE_TO",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "position",
|
||||
"columnName": "POSITION",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"_id"
|
||||
]
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"ftsVersion": "FTS4",
|
||||
"ftsOptions": {
|
||||
"tokenizer": "simple",
|
||||
"tokenizerArgs": [],
|
||||
"contentTable": "GALLERIES",
|
||||
"languageIdColumnName": "",
|
||||
"matchInfo": "FTS4",
|
||||
"notIndexedColumns": [],
|
||||
"prefixSizes": [],
|
||||
"preferredOrder": "ASC"
|
||||
},
|
||||
"contentSyncTriggers": [
|
||||
"CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_GALLERIES_FTS_BEFORE_UPDATE BEFORE UPDATE ON `GALLERIES` BEGIN DELETE FROM `GALLERIES_FTS` WHERE `docid`=OLD.`rowid`; END",
|
||||
"CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_GALLERIES_FTS_BEFORE_DELETE BEFORE DELETE ON `GALLERIES` BEGIN DELETE FROM `GALLERIES_FTS` WHERE `docid`=OLD.`rowid`; END",
|
||||
"CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_GALLERIES_FTS_AFTER_UPDATE AFTER UPDATE ON `GALLERIES` BEGIN INSERT INTO `GALLERIES_FTS`(`docid`, `TITLE`, `TITLE_JPN`, `SIMPLE_TAGS`) VALUES (NEW.`rowid`, NEW.`TITLE`, NEW.`TITLE_JPN`, NEW.`SIMPLE_TAGS`); END",
|
||||
"CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_GALLERIES_FTS_AFTER_INSERT AFTER INSERT ON `GALLERIES` BEGIN INSERT INTO `GALLERIES_FTS`(`docid`, `TITLE`, `TITLE_JPN`, `SIMPLE_TAGS`) VALUES (NEW.`rowid`, NEW.`TITLE`, NEW.`TITLE_JPN`, NEW.`SIMPLE_TAGS`); END"
|
||||
],
|
||||
"tableName": "GALLERIES_FTS",
|
||||
"createSql": "CREATE VIRTUAL TABLE IF NOT EXISTS `${TABLE_NAME}` USING FTS4(`TITLE` TEXT NOT NULL, `TITLE_JPN` TEXT NOT NULL, `SIMPLE_TAGS` TEXT, content=`GALLERIES`)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "TITLE",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "titleJpn",
|
||||
"columnName": "TITLE_JPN",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "simpleTags",
|
||||
"columnName": "SIMPLE_TAGS",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": []
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f61ae3beef3739c0de7db945d2c9a4a4')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ class EhApplication : Application(), SingletonImageLoader.Factory {
|
||||
EhDB
|
||||
}
|
||||
launch {
|
||||
DownloadManager.readPagesFromLocal()
|
||||
DownloadManager.readMetadataFromLocal()
|
||||
}
|
||||
launch {
|
||||
FileUtils.cleanupDirectory(AppConfig.externalCrashDir)
|
||||
|
||||
@@ -64,7 +64,7 @@ object EhTagDatabase : CoroutineScope {
|
||||
var initialized by mutableStateOf(false)
|
||||
|
||||
fun getTranslation(prefix: String? = NAMESPACE_PREFIX, tag: String?): String? {
|
||||
return tagGroups[prefix]?.get(tag)?.trim()?.takeIf { it.isNotEmpty() }
|
||||
return tagGroups[prefix]?.get(tag)?.trim()?.ifEmpty { null }
|
||||
}
|
||||
|
||||
private fun internalSuggestFlow(
|
||||
|
||||
@@ -20,6 +20,8 @@ import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverter
|
||||
import androidx.room.TypeConverters
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.NOT_FAVORITED
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import kotlinx.serialization.Serializable
|
||||
@@ -27,6 +29,7 @@ import kotlinx.serialization.Serializable
|
||||
@Parcelize
|
||||
@Serializable
|
||||
@Entity(tableName = "GALLERIES")
|
||||
@TypeConverters(SimpleTagsConverter::class)
|
||||
data class BaseGalleryInfo(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "GID")
|
||||
@@ -62,8 +65,8 @@ data class BaseGalleryInfo(
|
||||
@Ignore
|
||||
override var rated: Boolean = false,
|
||||
|
||||
@Ignore
|
||||
override var simpleTags: ArrayList<String>? = null,
|
||||
@ColumnInfo(name = "SIMPLE_TAGS")
|
||||
override var simpleTags: List<String>? = null,
|
||||
|
||||
@ColumnInfo(name = "PAGES", defaultValue = "0")
|
||||
override var pages: Int = 0,
|
||||
@@ -88,3 +91,11 @@ data class BaseGalleryInfo(
|
||||
) : GalleryInfo, Parcelable {
|
||||
constructor() : this(0, "")
|
||||
}
|
||||
|
||||
class SimpleTagsConverter {
|
||||
@TypeConverter
|
||||
fun fromString(value: String) = value.split(", ")
|
||||
|
||||
@TypeConverter
|
||||
fun toString(value: List<String>) = value.joinToString()
|
||||
}
|
||||
@@ -45,7 +45,7 @@ data class GalleryDetail(
|
||||
var torrentUrl: String? = null,
|
||||
var archiveUrl: String? = null,
|
||||
var parent: String? = null,
|
||||
var newerVersions: ArrayList<BaseGalleryInfo> = ArrayList(),
|
||||
var newerVersions: List<BaseGalleryInfo> = emptyList(),
|
||||
var visible: String? = null,
|
||||
var language: String? = null,
|
||||
var size: String? = null,
|
||||
@@ -56,8 +56,9 @@ data class GalleryDetail(
|
||||
val previewPages: Int,
|
||||
val previewList: List<GalleryPreview>,
|
||||
) : GalleryInfo by galleryInfo, Parcelable {
|
||||
override fun generateSLang() {
|
||||
fun fillInfo() {
|
||||
val index = LANGUAGES.indexOf(language)
|
||||
if (index != -1) simpleLanguage = S_LANGS[index]
|
||||
simpleTags = tags.flatten()
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ interface GalleryInfo {
|
||||
var disowned: Boolean
|
||||
var rating: Float
|
||||
var rated: Boolean
|
||||
var simpleTags: ArrayList<String>?
|
||||
var simpleTags: List<String>?
|
||||
var pages: Int
|
||||
var thumbWidth: Int
|
||||
var thumbHeight: Int
|
||||
|
||||
@@ -80,7 +80,7 @@ data class ListUrlBuilder(
|
||||
minRating = q.minRating
|
||||
pageFrom = q.pageFrom
|
||||
pageTo = q.pageTo
|
||||
mNext = q.name.substringAfterLast('@', "").takeIf { it.isNotEmpty() }
|
||||
mNext = q.name.substringAfterLast('@', "").ifEmpty { null }
|
||||
}
|
||||
|
||||
fun toQuickSearch(name: String): QuickSearch {
|
||||
|
||||
@@ -60,6 +60,6 @@ object GalleryApiParser {
|
||||
@SerialName("filecount")
|
||||
val pages: Int,
|
||||
val rating: Float,
|
||||
val tags: ArrayList<String>,
|
||||
val tags: List<String>,
|
||||
)
|
||||
}
|
||||
@@ -114,8 +114,8 @@ object GalleryDetailParser {
|
||||
)
|
||||
parseDetail(galleryDetail, document, body)
|
||||
|
||||
// Generate simpleLanguage for local favorites
|
||||
galleryDetail.generateSLang()
|
||||
// Fill info for database
|
||||
galleryDetail.fillInfo()
|
||||
return galleryDetail
|
||||
}
|
||||
|
||||
@@ -231,16 +231,10 @@ object GalleryDetailParser {
|
||||
// newer version
|
||||
d.getElementById("gnd")?.run {
|
||||
val dates = PATTERN_NEWER_DATE.findAll(body).map { it.groupValues[1] }.toList()
|
||||
select("a").forEachIndexed { index, element ->
|
||||
gd.newerVersions = select("a").mapIndexedNotNull { index, element ->
|
||||
val result = GalleryDetailUrlParser.parse(element.attr("href"))
|
||||
if (result != null) {
|
||||
val gi = BaseGalleryInfo(
|
||||
result.gid,
|
||||
result.token,
|
||||
element.text().trim(),
|
||||
posted = dates[index],
|
||||
)
|
||||
gd.newerVersions.add(gi)
|
||||
result?.run {
|
||||
BaseGalleryInfo(gid, token, element.text().trim(), posted = dates[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,206 +2,37 @@ package com.hippo.ehviewer.dao
|
||||
|
||||
import androidx.room.AutoMigration
|
||||
import androidx.room.Database
|
||||
import androidx.room.DeleteColumn
|
||||
import androidx.room.DeleteTable
|
||||
import androidx.room.RenameColumn
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.NOT_FAVORITED
|
||||
|
||||
@DeleteTable(tableName = "BOOKMARKS")
|
||||
class DropBookMarkDao : AutoMigrationSpec
|
||||
|
||||
@RenameColumn(tableName = "HISTORY", fromColumnName = "MODE", toColumnName = "FAVORITE_SLOT")
|
||||
class HistoryMigration : AutoMigrationSpec {
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("UPDATE HISTORY SET FAVORITE_SLOT = FAVORITE_SLOT - 2")
|
||||
}
|
||||
}
|
||||
|
||||
class AddPositionColumn : AutoMigrationSpec {
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
val needMigrationTables = arrayOf(
|
||||
"QUICK_SEARCH",
|
||||
"DOWNLOAD_LABELS",
|
||||
"DOWNLOADS",
|
||||
)
|
||||
needMigrationTables.forEach { table ->
|
||||
// TODO: Rewrite this with row_number() when min sdk is 30 (SQLite 3.28.0)
|
||||
db.execSQL("UPDATE $table SET POSITION = (SELECT COUNT(*) FROM $table T WHERE T.TIME < $table.TIME)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteColumn(tableName = "QUICK_SEARCH", columnName = "TIME")
|
||||
@DeleteColumn(tableName = "DOWNLOAD_LABELS", columnName = "TIME")
|
||||
class DropTimeColumn : AutoMigrationSpec
|
||||
|
||||
class AddGalleryTable : AutoMigrationSpec {
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
val needMigrationTables = arrayOf(
|
||||
"HISTORY",
|
||||
"DOWNLOADS",
|
||||
"LOCAL_FAVORITES",
|
||||
)
|
||||
needMigrationTables.forEachIndexed { index, table ->
|
||||
db.execSQL(
|
||||
"INSERT OR IGNORE INTO GALLERIES " +
|
||||
"SELECT GID, TOKEN, TITLE, TITLE_JPN, THUMB, CATEGORY, POSTED, UPLOADER, RATING, SIMPLE_LANGUAGE, " +
|
||||
"${if (index == 0) "FAVORITE_SLOT" else NOT_FAVORITED} FROM $table",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "TOKEN")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "TITLE")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "TITLE_JPN")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "THUMB")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "CATEGORY")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "POSTED")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "UPLOADER")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "RATING")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "SIMPLE_LANGUAGE")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "FAVORITE_SLOT")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "TOKEN")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "TITLE")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "TITLE_JPN")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "THUMB")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "CATEGORY")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "POSTED")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "UPLOADER")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "RATING")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "SIMPLE_LANGUAGE")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "TOKEN")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "TITLE")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "TITLE_JPN")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "THUMB")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "CATEGORY")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "POSTED")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "UPLOADER")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "RATING")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "SIMPLE_LANGUAGE")
|
||||
class GalleryTableMigration : AutoMigrationSpec
|
||||
|
||||
class ThumbKeyMigration : AutoMigrationSpec {
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
val needMigrationTables = arrayOf(
|
||||
"DOWNLOADS",
|
||||
"HISTORY",
|
||||
"LOCAL_FAVORITES",
|
||||
)
|
||||
val prefixToRemove = arrayOf(
|
||||
"https://ehgt.org/",
|
||||
"https://s.exhentai.org/t/",
|
||||
"https://exhentai.org/t/",
|
||||
)
|
||||
needMigrationTables.forEach { table ->
|
||||
prefixToRemove.forEach { prefix ->
|
||||
db.execSQL("UPDATE $table SET thumb = SUBSTR(thumb ,LENGTH('$prefix') + 1) WHERE thumb LIKE '$prefix%'")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RenameColumn(tableName = "DOWNLOADS", fromColumnName = "POSITION", toColumnName = "TIME")
|
||||
class DownloadsMigration : AutoMigrationSpec
|
||||
|
||||
class GalleryFtsMigration : AutoMigrationSpec {
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("INSERT INTO GalleryFts(GalleryFts) VALUES('rebuild')")
|
||||
}
|
||||
}
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
BaseGalleryInfo::class, DownloadLabel::class, DownloadEntity::class, DownloadDirname::class,
|
||||
Filter::class, HistoryInfo::class, LocalFavoriteInfo::class, ProgressInfo::class, QuickSearch::class,
|
||||
GalleryFts::class,
|
||||
GalleryEntityFts::class,
|
||||
],
|
||||
version = 21,
|
||||
version = 22,
|
||||
exportSchema = true,
|
||||
autoMigrations = [
|
||||
AutoMigration(
|
||||
from = 4,
|
||||
to = 5,
|
||||
spec = ThumbKeyMigration::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 5,
|
||||
to = 6,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 6,
|
||||
to = 7,
|
||||
spec = DropBookMarkDao::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 7,
|
||||
to = 8,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 8,
|
||||
to = 9,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 9,
|
||||
to = 10,
|
||||
spec = HistoryMigration::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 10,
|
||||
to = 11,
|
||||
spec = AddPositionColumn::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 11,
|
||||
to = 12,
|
||||
spec = DropTimeColumn::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 12,
|
||||
to = 13,
|
||||
spec = AddGalleryTable::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 13,
|
||||
to = 14,
|
||||
spec = GalleryTableMigration::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 14,
|
||||
to = 15,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 15,
|
||||
to = 16,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 16,
|
||||
to = 17,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 17,
|
||||
to = 18,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 18,
|
||||
to = 19,
|
||||
spec = DownloadsMigration::class,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 19,
|
||||
to = 20,
|
||||
),
|
||||
AutoMigration(
|
||||
from = 20,
|
||||
to = 21,
|
||||
spec = GalleryFtsMigration::class,
|
||||
),
|
||||
AutoMigration(from = 4, to = 5, spec = Schema4to5::class),
|
||||
AutoMigration(from = 5, to = 6),
|
||||
AutoMigration(from = 6, to = 7, spec = Schema6to7::class),
|
||||
AutoMigration(from = 7, to = 8),
|
||||
AutoMigration(from = 8, to = 9),
|
||||
AutoMigration(from = 9, to = 10, spec = Schema9to10::class),
|
||||
AutoMigration(from = 10, to = 11, spec = Schema10to11::class),
|
||||
AutoMigration(from = 11, to = 12, spec = Schema11to12::class),
|
||||
AutoMigration(from = 12, to = 13, spec = Schema12to13::class),
|
||||
AutoMigration(from = 13, to = 14, spec = Schema13to14::class),
|
||||
AutoMigration(from = 14, to = 15),
|
||||
AutoMigration(from = 15, to = 16),
|
||||
AutoMigration(from = 16, to = 17),
|
||||
AutoMigration(from = 17, to = 18),
|
||||
AutoMigration(from = 18, to = 19, spec = Schema18to19::class),
|
||||
AutoMigration(from = 19, to = 20),
|
||||
AutoMigration(from = 20, to = 21, spec = Schema20to21::class),
|
||||
AutoMigration(from = 21, to = 22, spec = Schema21to22::class),
|
||||
],
|
||||
)
|
||||
@TypeConverters(FilterModeConverter::class)
|
||||
@@ -221,10 +52,7 @@ abstract class EhDatabase : RoomDatabase() {
|
||||
entities = [Search::class],
|
||||
version = 2,
|
||||
autoMigrations = [
|
||||
AutoMigration(
|
||||
from = 1,
|
||||
to = 2,
|
||||
),
|
||||
AutoMigration(from = 1, to = 2),
|
||||
],
|
||||
)
|
||||
abstract class SearchDatabase : RoomDatabase() {
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.hippo.ehviewer.dao
|
||||
|
||||
import androidx.room.DeleteColumn
|
||||
import androidx.room.DeleteTable
|
||||
import androidx.room.RenameColumn
|
||||
import androidx.room.RenameTable
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo.Companion.NOT_FAVORITED
|
||||
|
||||
class Schema4to5 : AutoMigrationSpec {
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
val needMigrationTables = arrayOf(
|
||||
"DOWNLOADS",
|
||||
"HISTORY",
|
||||
"LOCAL_FAVORITES",
|
||||
)
|
||||
val prefixToRemove = arrayOf(
|
||||
"https://ehgt.org/",
|
||||
"https://s.exhentai.org/t/",
|
||||
"https://exhentai.org/t/",
|
||||
)
|
||||
needMigrationTables.forEach { table ->
|
||||
prefixToRemove.forEach { prefix ->
|
||||
db.execSQL("UPDATE $table SET thumb = SUBSTR(thumb ,LENGTH('$prefix') + 1) WHERE thumb LIKE '$prefix%'")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteTable(tableName = "BOOKMARKS")
|
||||
class Schema6to7 : AutoMigrationSpec
|
||||
|
||||
@RenameColumn(tableName = "HISTORY", fromColumnName = "MODE", toColumnName = "FAVORITE_SLOT")
|
||||
class Schema9to10 : AutoMigrationSpec {
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("UPDATE HISTORY SET FAVORITE_SLOT = FAVORITE_SLOT - 2")
|
||||
}
|
||||
}
|
||||
|
||||
class Schema10to11 : AutoMigrationSpec {
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
val needMigrationTables = arrayOf(
|
||||
"QUICK_SEARCH",
|
||||
"DOWNLOAD_LABELS",
|
||||
"DOWNLOADS",
|
||||
)
|
||||
needMigrationTables.forEach { table ->
|
||||
// TODO: Rewrite this with row_number() when min sdk is 30 (SQLite 3.28.0)
|
||||
db.execSQL("UPDATE $table SET POSITION = (SELECT COUNT(*) FROM $table T WHERE T.TIME < $table.TIME)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteColumn(tableName = "QUICK_SEARCH", columnName = "TIME")
|
||||
@DeleteColumn(tableName = "DOWNLOAD_LABELS", columnName = "TIME")
|
||||
class Schema11to12 : AutoMigrationSpec
|
||||
|
||||
class Schema12to13 : AutoMigrationSpec {
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
val needMigrationTables = arrayOf(
|
||||
"HISTORY",
|
||||
"DOWNLOADS",
|
||||
"LOCAL_FAVORITES",
|
||||
)
|
||||
needMigrationTables.forEachIndexed { index, table ->
|
||||
db.execSQL(
|
||||
"INSERT OR IGNORE INTO GALLERIES " +
|
||||
"SELECT GID, TOKEN, TITLE, TITLE_JPN, THUMB, CATEGORY, POSTED, UPLOADER, RATING, SIMPLE_LANGUAGE, " +
|
||||
"${if (index == 0) "FAVORITE_SLOT" else NOT_FAVORITED} FROM $table",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "TOKEN")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "TITLE")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "TITLE_JPN")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "THUMB")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "CATEGORY")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "POSTED")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "UPLOADER")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "RATING")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "SIMPLE_LANGUAGE")
|
||||
@DeleteColumn(tableName = "HISTORY", columnName = "FAVORITE_SLOT")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "TOKEN")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "TITLE")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "TITLE_JPN")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "THUMB")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "CATEGORY")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "POSTED")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "UPLOADER")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "RATING")
|
||||
@DeleteColumn(tableName = "LOCAL_FAVORITES", columnName = "SIMPLE_LANGUAGE")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "TOKEN")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "TITLE")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "TITLE_JPN")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "THUMB")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "CATEGORY")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "POSTED")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "UPLOADER")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "RATING")
|
||||
@DeleteColumn(tableName = "DOWNLOADS", columnName = "SIMPLE_LANGUAGE")
|
||||
class Schema13to14 : AutoMigrationSpec
|
||||
|
||||
@RenameColumn(tableName = "DOWNLOADS", fromColumnName = "POSITION", toColumnName = "TIME")
|
||||
class Schema18to19 : AutoMigrationSpec
|
||||
|
||||
class Schema20to21 : AutoMigrationSpec {
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("INSERT INTO GalleryFts(GalleryFts) VALUES('rebuild')")
|
||||
}
|
||||
}
|
||||
|
||||
// Virtual tables can't be altered, so we use rename to recreate it
|
||||
@RenameTable(fromTableName = "GalleryFts", toTableName = "GALLERIES_FTS")
|
||||
class Schema21to22 : AutoMigrationSpec
|
||||
+5
-2
@@ -6,11 +6,14 @@ import androidx.room.Fts4
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
|
||||
@Fts4(contentEntity = BaseGalleryInfo::class)
|
||||
@Entity
|
||||
class GalleryFts(
|
||||
@Entity(tableName = "GALLERIES_FTS")
|
||||
class GalleryEntityFts(
|
||||
@ColumnInfo(name = "TITLE")
|
||||
val title: String,
|
||||
|
||||
@ColumnInfo(name = "TITLE_JPN")
|
||||
val titleJpn: String,
|
||||
|
||||
@ColumnInfo(name = "SIMPLE_TAGS")
|
||||
val simpleTags: String?,
|
||||
)
|
||||
@@ -18,7 +18,7 @@ interface HistoryDao {
|
||||
|
||||
@Query(
|
||||
"""SELECT GALLERIES.* FROM HISTORY JOIN GALLERIES USING(GID)
|
||||
JOIN GalleryFts ON GALLERIES.rowid = docid WHERE GalleryFts MATCH :title ORDER BY TIME DESC""",
|
||||
JOIN GALLERIES_FTS ON GALLERIES.rowid = docid WHERE GALLERIES_FTS MATCH :title ORDER BY TIME DESC""",
|
||||
)
|
||||
fun joinListLazy(title: String): PagingSource<Int, BaseGalleryInfo>
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ interface LocalFavoritesDao {
|
||||
|
||||
@Query(
|
||||
"""SELECT GALLERIES.* FROM LOCAL_FAVORITES JOIN GALLERIES USING(GID)
|
||||
JOIN GalleryFts ON GALLERIES.rowid = docid WHERE GalleryFts MATCH :title ORDER BY TIME DESC""",
|
||||
JOIN GALLERIES_FTS ON GALLERIES.rowid = docid WHERE GALLERIES_FTS MATCH :title ORDER BY TIME DESC""",
|
||||
)
|
||||
fun joinListLazy(title: String): PagingSource<Int, BaseGalleryInfo>
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.hippo.ehviewer.spider.SpiderQueen.OnSpiderListener
|
||||
import com.hippo.ehviewer.spider.putToDownloadDir
|
||||
import com.hippo.ehviewer.spider.readComicInfo
|
||||
import com.hippo.ehviewer.spider.readCompatFromUniFile
|
||||
import com.hippo.ehviewer.spider.toSimpleTags
|
||||
import com.hippo.ehviewer.util.AppConfig
|
||||
import com.hippo.ehviewer.util.insertWith
|
||||
import com.hippo.ehviewer.util.mapNotNull
|
||||
@@ -529,17 +530,23 @@ object DownloadManager : OnSpiderListener, CoroutineScope {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun readPagesFromLocal() {
|
||||
val list = allInfoList.filter { it.pages == 0 }.parMapNotNull(concurrency = 5) { info ->
|
||||
suspend fun readMetadataFromLocal() {
|
||||
val list = allInfoList.filter { it.pages == 0 || it.simpleTags == null }.parMapNotNull(concurrency = 5) { info ->
|
||||
info.downloadDir?.run {
|
||||
val pages = findFile(SPIDER_INFO_FILENAME)?.let {
|
||||
readCompatFromUniFile(it)?.pages
|
||||
} ?: findFile(COMIC_INFO_FILE)?.let {
|
||||
readComicInfo(it)?.pageCount
|
||||
}
|
||||
pages?.let {
|
||||
info.pages = it
|
||||
val comicInfo = findFile(COMIC_INFO_FILE)?.let { readComicInfo(it) }
|
||||
if (comicInfo != null) {
|
||||
info.pages = comicInfo.pageCount
|
||||
info.simpleTags = comicInfo.toSimpleTags()
|
||||
info.galleryInfo
|
||||
} else if (info.pages == 0) {
|
||||
findFile(SPIDER_INFO_FILENAME)?.let {
|
||||
readCompatFromUniFile(it)?.run {
|
||||
info.pages = pages
|
||||
info.galleryInfo
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,20 @@ package com.hippo.ehviewer.spider
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.client.data.SimpleTagsConverter
|
||||
import com.hippo.ehviewer.client.data.TagNamespace
|
||||
import com.hippo.unifile.UniFile
|
||||
import com.hippo.unifile.openInputStream
|
||||
import com.hippo.unifile.openOutputStream
|
||||
import java.io.InputStreamReader
|
||||
import java.io.OutputStreamWriter
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import nl.adaptivity.xmlutil.XmlDeclMode
|
||||
import nl.adaptivity.xmlutil.core.XmlVersion
|
||||
import nl.adaptivity.xmlutil.newGenericWriter
|
||||
@@ -39,14 +45,15 @@ fun GalleryInfo.getComicInfo(): ComicInfo {
|
||||
with(TagNamespace) {
|
||||
when (this@getComicInfo) {
|
||||
is GalleryDetail -> tags.forEach { tagList ->
|
||||
val list = tagList.filterNot { it == TAG_ORIGINAL || it.startsWith('_') }
|
||||
when (val ns = TagNamespace(tagList.groupName)) {
|
||||
Artist, Cosplayer -> artists.addAll(tagList)
|
||||
Group -> groups.addAll(tagList)
|
||||
Character -> characters.addAll(tagList)
|
||||
Parody -> tagList.forEach { tag -> if (tag != TAG_ORIGINAL) parodies.add(tag) }
|
||||
Other -> otherTags.addAll(tagList)
|
||||
Artist, Cosplayer -> artists.addAll(list)
|
||||
Group -> groups.addAll(list)
|
||||
Character -> characters.addAll(list)
|
||||
Parody -> parodies.addAll(list)
|
||||
Other -> otherTags.addAll(list)
|
||||
Female, Male, Mixed -> ns.toPrefix()?.let { prefix ->
|
||||
tagList.forEach { tag -> otherTags.add("$prefix:$tag") }
|
||||
list.forEach { tag -> otherTags.add("$prefix:$tag") }
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
@@ -70,18 +77,26 @@ fun GalleryInfo.getComicInfo(): ComicInfo {
|
||||
return ComicInfo(
|
||||
series = title,
|
||||
alternateSeries = titleJpn.takeUnless { it.isNullOrBlank() },
|
||||
writer = groups.takeUnless { it.isEmpty() }?.joinToString(),
|
||||
penciller = artists.takeUnless { it.isEmpty() }?.joinToString(),
|
||||
genre = otherTags.takeUnless { it.isEmpty() }?.joinToString(),
|
||||
writer = groups.ifEmpty { null },
|
||||
penciller = artists.ifEmpty { null },
|
||||
genre = otherTags.ifEmpty { null },
|
||||
web = EhUrl.getGalleryDetailUrl(gid, token),
|
||||
pageCount = pages,
|
||||
languageISO = simpleLanguage?.lowercase(),
|
||||
characters = characters.takeUnless { it.isEmpty() }?.joinToString(),
|
||||
teams = parodies.takeUnless { it.isEmpty() }?.joinToString(),
|
||||
characters = characters.ifEmpty { null },
|
||||
teams = parodies.ifEmpty { null },
|
||||
communityRating = "%.1f".format(rating),
|
||||
)
|
||||
}
|
||||
|
||||
fun ComicInfo.toSimpleTags() = listOfNotNull(
|
||||
writer,
|
||||
penciller,
|
||||
genre,
|
||||
characters,
|
||||
teams,
|
||||
).flatten().ifEmpty { null }
|
||||
|
||||
fun ComicInfo.write(file: UniFile) {
|
||||
file.openOutputStream().use {
|
||||
it.channel.truncate(0)
|
||||
@@ -103,6 +118,16 @@ fun readComicInfo(file: UniFile): ComicInfo? = runCatching {
|
||||
}
|
||||
}.getOrNull()
|
||||
|
||||
@Suppress("ktlint:standard:annotation")
|
||||
typealias SimpleTags = @Serializable(SimpleTagsSerializer::class) List<String>
|
||||
|
||||
object SimpleTagsSerializer : KSerializer<SimpleTags> {
|
||||
private val converter = SimpleTagsConverter()
|
||||
override val descriptor = PrimitiveSerialDescriptor("SimpleTags", PrimitiveKind.STRING)
|
||||
override fun deserialize(decoder: Decoder) = converter.fromString(decoder.decodeString())
|
||||
override fun serialize(encoder: Encoder, value: SimpleTags) = encoder.encodeString(converter.toString(value))
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ComicInfo(
|
||||
@XmlElement
|
||||
@@ -115,15 +140,15 @@ data class ComicInfo(
|
||||
|
||||
@XmlElement
|
||||
@SerialName("Writer")
|
||||
val writer: String?,
|
||||
val writer: SimpleTags?,
|
||||
|
||||
@XmlElement
|
||||
@SerialName("Penciller")
|
||||
val penciller: String?,
|
||||
val penciller: SimpleTags?,
|
||||
|
||||
@XmlElement
|
||||
@SerialName("Genre")
|
||||
val genre: String?,
|
||||
val genre: SimpleTags?,
|
||||
|
||||
@XmlElement
|
||||
@SerialName("Web")
|
||||
@@ -139,11 +164,11 @@ data class ComicInfo(
|
||||
|
||||
@XmlElement
|
||||
@SerialName("Characters")
|
||||
val characters: String?,
|
||||
val characters: SimpleTags?,
|
||||
|
||||
@XmlElement
|
||||
@SerialName("Teams")
|
||||
val teams: String?,
|
||||
val teams: SimpleTags?,
|
||||
|
||||
@XmlElement
|
||||
@SerialName("CommunityRating")
|
||||
|
||||
@@ -188,7 +188,7 @@ class SpiderDen(val info: GalleryInfo) {
|
||||
|
||||
private suspend fun saveFromHttpResponse(index: Int, response: HttpResponse): Boolean {
|
||||
val url = response.request.url.toString()
|
||||
val extension = MimeTypeMap.getFileExtensionFromUrl(url).takeUnless { it.isEmpty() } ?: "jpg"
|
||||
val extension = MimeTypeMap.getFileExtensionFromUrl(url).ifEmpty { "jpg" }
|
||||
return saveResponseMeta(index, extension) { outFile ->
|
||||
outFile.openOutputStream().use {
|
||||
val chan = it.channel.apply { truncate(0) }
|
||||
|
||||
@@ -14,4 +14,7 @@ data class DownloadsFilterState(
|
||||
|
||||
fun DownloadsFilterState.take(info: DownloadInfo) =
|
||||
(label == "" || info.label == label) && (state == -1 || info.state == state) &&
|
||||
with(info) { title.containsIgnoreCase(keyword) || titleJpn.containsIgnoreCase(keyword) }
|
||||
with(info) {
|
||||
title.containsIgnoreCase(keyword) || titleJpn.containsIgnoreCase(keyword) ||
|
||||
simpleTags?.any { it.containsIgnoreCase(keyword) } ?: false
|
||||
}
|
||||
@@ -541,6 +541,7 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) = c
|
||||
GalleryPageUrlSuggestion(gid, pToken, page)
|
||||
}
|
||||
},
|
||||
tagNamespace = true,
|
||||
searchBarOffsetY = { searchBarOffsetY },
|
||||
trailingIcon = {
|
||||
val sheetState = LocalSideSheetState.current
|
||||
|
||||
@@ -104,6 +104,7 @@ fun SearchBarScreen(
|
||||
onSearchHidden: () -> Unit,
|
||||
refreshState: PullToRefreshState? = null,
|
||||
suggestionProvider: SuggestionProvider? = null,
|
||||
tagNamespace: Boolean = false,
|
||||
searchBarOffsetY: () -> Int,
|
||||
trailingIcon: @Composable () -> Unit,
|
||||
filter: @Composable (() -> Unit)? = null,
|
||||
@@ -125,7 +126,7 @@ fun SearchBarScreen(
|
||||
val query = searchFieldState.text.toString()
|
||||
var keywords = query.substringBeforeLast(' ', "")
|
||||
if (keywords.isNotEmpty()) keywords += ' '
|
||||
keywords += wrapTagKeyword(keyword)
|
||||
keywords += if (tagNamespace) wrapTagKeyword(keyword) else keyword.substringAfter(':')
|
||||
if (!keywords.endsWith(':')) keywords += ' '
|
||||
searchFieldState.setTextAndPlaceCursorAtEnd(keywords)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user