书籍类型采用位运算,查询更高效

This commit is contained in:
kunfei 2022-10-02 22:57:00 +08:00
parent 5a26f87a71
commit 21729149cc
50 changed files with 2074 additions and 216 deletions

View File

@ -2,11 +2,11 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 54, "version": 54,
"identityHash": "9045ef48ed80bddcaa0c7b4b05d15f24", "identityHash": "2b32177325d903e84445cc80ad7cbce8",
"entities": [ "entities": [
{ {
"tableName": "books", "tableName": "books",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bookUrl` TEXT NOT NULL DEFAULT '', `tocUrl` TEXT NOT NULL DEFAULT '', `origin` TEXT NOT NULL DEFAULT '', `originName` TEXT NOT NULL DEFAULT '', `name` TEXT NOT NULL DEFAULT '', `author` TEXT NOT NULL DEFAULT '', `kind` TEXT, `customTag` TEXT, `coverUrl` TEXT, `customCoverUrl` TEXT, `intro` TEXT, `customIntro` TEXT, `charset` TEXT, `type` INTEGER NOT NULL DEFAULT 0, `group` INTEGER NOT NULL DEFAULT 0, `latestChapterTitle` TEXT, `latestChapterTime` INTEGER NOT NULL DEFAULT 0, `lastCheckTime` INTEGER NOT NULL DEFAULT 0, `lastCheckCount` INTEGER NOT NULL DEFAULT 0, `totalChapterNum` INTEGER NOT NULL DEFAULT 0, `durChapterTitle` TEXT, `durChapterIndex` INTEGER NOT NULL DEFAULT 0, `durChapterPos` INTEGER NOT NULL DEFAULT 0, `durChapterTime` INTEGER NOT NULL DEFAULT 0, `wordCount` TEXT, `canUpdate` INTEGER NOT NULL DEFAULT 1, `order` INTEGER NOT NULL DEFAULT 0, `originOrder` INTEGER NOT NULL DEFAULT 0, `variable` TEXT, `readConfig` TEXT, PRIMARY KEY(`bookUrl`))", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bookUrl` TEXT NOT NULL DEFAULT '', `tocUrl` TEXT NOT NULL DEFAULT '', `origin` TEXT NOT NULL DEFAULT '', `originName` TEXT NOT NULL DEFAULT '', `name` TEXT NOT NULL DEFAULT '', `author` TEXT NOT NULL DEFAULT '', `kind` TEXT, `customTag` TEXT, `coverUrl` TEXT, `customCoverUrl` TEXT, `intro` TEXT, `customIntro` TEXT, `charset` TEXT, `type` INTEGER NOT NULL DEFAULT 1, `group` INTEGER NOT NULL DEFAULT 0, `latestChapterTitle` TEXT, `latestChapterTime` INTEGER NOT NULL DEFAULT 0, `lastCheckTime` INTEGER NOT NULL DEFAULT 0, `lastCheckCount` INTEGER NOT NULL DEFAULT 0, `totalChapterNum` INTEGER NOT NULL DEFAULT 0, `durChapterTitle` TEXT, `durChapterIndex` INTEGER NOT NULL DEFAULT 0, `durChapterPos` INTEGER NOT NULL DEFAULT 0, `durChapterTime` INTEGER NOT NULL DEFAULT 0, `wordCount` TEXT, `canUpdate` INTEGER NOT NULL DEFAULT 1, `order` INTEGER NOT NULL DEFAULT 0, `originOrder` INTEGER NOT NULL DEFAULT 0, `variable` TEXT, `readConfig` TEXT, PRIMARY KEY(`bookUrl`))",
"fields": [ "fields": [
{ {
"fieldPath": "bookUrl", "fieldPath": "bookUrl",
@ -97,7 +97,7 @@
"columnName": "type", "columnName": "type",
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": true, "notNull": true,
"defaultValue": "0" "defaultValue": "1"
}, },
{ {
"fieldPath": "group", "fieldPath": "group",
@ -1705,7 +1705,7 @@
"views": [], "views": [],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "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, '9045ef48ed80bddcaa0c7b4b05d15f24')" "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2b32177325d903e84445cc80ad7cbce8')"
] ]
} }
} }

View File

@ -0,0 +1,1711 @@
{
"formatVersion": 1,
"database": {
"version": 55,
"identityHash": "09020e77cffa237f9a98c62eed0a01f2",
"entities": [
{
"tableName": "books",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bookUrl` TEXT NOT NULL DEFAULT '', `tocUrl` TEXT NOT NULL DEFAULT '', `origin` TEXT NOT NULL DEFAULT 'loc_book', `originName` TEXT NOT NULL DEFAULT '', `name` TEXT NOT NULL DEFAULT '', `author` TEXT NOT NULL DEFAULT '', `kind` TEXT, `customTag` TEXT, `coverUrl` TEXT, `customCoverUrl` TEXT, `intro` TEXT, `customIntro` TEXT, `charset` TEXT, `type` INTEGER NOT NULL DEFAULT 1, `group` INTEGER NOT NULL DEFAULT 0, `latestChapterTitle` TEXT, `latestChapterTime` INTEGER NOT NULL DEFAULT 0, `lastCheckTime` INTEGER NOT NULL DEFAULT 0, `lastCheckCount` INTEGER NOT NULL DEFAULT 0, `totalChapterNum` INTEGER NOT NULL DEFAULT 0, `durChapterTitle` TEXT, `durChapterIndex` INTEGER NOT NULL DEFAULT 0, `durChapterPos` INTEGER NOT NULL DEFAULT 0, `durChapterTime` INTEGER NOT NULL DEFAULT 0, `wordCount` TEXT, `canUpdate` INTEGER NOT NULL DEFAULT 1, `order` INTEGER NOT NULL DEFAULT 0, `originOrder` INTEGER NOT NULL DEFAULT 0, `variable` TEXT, `readConfig` TEXT, PRIMARY KEY(`bookUrl`))",
"fields": [
{
"fieldPath": "bookUrl",
"columnName": "bookUrl",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "tocUrl",
"columnName": "tocUrl",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "origin",
"columnName": "origin",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'loc_book'"
},
{
"fieldPath": "originName",
"columnName": "originName",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "author",
"columnName": "author",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "kind",
"columnName": "kind",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "customTag",
"columnName": "customTag",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "coverUrl",
"columnName": "coverUrl",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "customCoverUrl",
"columnName": "customCoverUrl",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "intro",
"columnName": "intro",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "customIntro",
"columnName": "customIntro",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "charset",
"columnName": "charset",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "1"
},
{
"fieldPath": "group",
"columnName": "group",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "latestChapterTitle",
"columnName": "latestChapterTitle",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "latestChapterTime",
"columnName": "latestChapterTime",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "lastCheckTime",
"columnName": "lastCheckTime",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "lastCheckCount",
"columnName": "lastCheckCount",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "totalChapterNum",
"columnName": "totalChapterNum",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "durChapterTitle",
"columnName": "durChapterTitle",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "durChapterIndex",
"columnName": "durChapterIndex",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "durChapterPos",
"columnName": "durChapterPos",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "durChapterTime",
"columnName": "durChapterTime",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "wordCount",
"columnName": "wordCount",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "canUpdate",
"columnName": "canUpdate",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "1"
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "originOrder",
"columnName": "originOrder",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "variable",
"columnName": "variable",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "readConfig",
"columnName": "readConfig",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"bookUrl"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_books_name_author",
"unique": true,
"columnNames": [
"name",
"author"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_books_name_author` ON `${TABLE_NAME}` (`name`, `author`)"
}
],
"foreignKeys": []
},
{
"tableName": "book_groups",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`groupId` INTEGER NOT NULL, `groupName` TEXT NOT NULL, `cover` TEXT, `order` INTEGER NOT NULL, `show` INTEGER NOT NULL, PRIMARY KEY(`groupId`))",
"fields": [
{
"fieldPath": "groupId",
"columnName": "groupId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "groupName",
"columnName": "groupName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "cover",
"columnName": "cover",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "show",
"columnName": "show",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"groupId"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "book_sources",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bookSourceUrl` TEXT NOT NULL, `bookSourceName` TEXT NOT NULL, `bookSourceGroup` TEXT, `bookSourceType` INTEGER NOT NULL, `bookUrlPattern` TEXT, `customOrder` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `enabledExplore` INTEGER NOT NULL, `enabledReview` INTEGER, `enabledCookieJar` INTEGER DEFAULT 0, `concurrentRate` TEXT, `header` TEXT, `loginUrl` TEXT, `loginUi` TEXT, `loginCheckJs` TEXT, `coverDecodeJs` TEXT, `bookSourceComment` TEXT, `variableComment` TEXT, `lastUpdateTime` INTEGER NOT NULL, `respondTime` INTEGER NOT NULL, `weight` INTEGER NOT NULL, `exploreUrl` TEXT, `ruleExplore` TEXT, `searchUrl` TEXT, `ruleSearch` TEXT, `ruleBookInfo` TEXT, `ruleToc` TEXT, `ruleContent` TEXT, `ruleReview` TEXT, PRIMARY KEY(`bookSourceUrl`))",
"fields": [
{
"fieldPath": "bookSourceUrl",
"columnName": "bookSourceUrl",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "bookSourceName",
"columnName": "bookSourceName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "bookSourceGroup",
"columnName": "bookSourceGroup",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "bookSourceType",
"columnName": "bookSourceType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "bookUrlPattern",
"columnName": "bookUrlPattern",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "customOrder",
"columnName": "customOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabledExplore",
"columnName": "enabledExplore",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabledReview",
"columnName": "enabledReview",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "enabledCookieJar",
"columnName": "enabledCookieJar",
"affinity": "INTEGER",
"notNull": false,
"defaultValue": "0"
},
{
"fieldPath": "concurrentRate",
"columnName": "concurrentRate",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "header",
"columnName": "header",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "loginUrl",
"columnName": "loginUrl",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "loginUi",
"columnName": "loginUi",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "loginCheckJs",
"columnName": "loginCheckJs",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "coverDecodeJs",
"columnName": "coverDecodeJs",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "bookSourceComment",
"columnName": "bookSourceComment",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "variableComment",
"columnName": "variableComment",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastUpdateTime",
"columnName": "lastUpdateTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "respondTime",
"columnName": "respondTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "weight",
"columnName": "weight",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "exploreUrl",
"columnName": "exploreUrl",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ruleExplore",
"columnName": "ruleExplore",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "searchUrl",
"columnName": "searchUrl",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ruleSearch",
"columnName": "ruleSearch",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ruleBookInfo",
"columnName": "ruleBookInfo",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ruleToc",
"columnName": "ruleToc",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ruleContent",
"columnName": "ruleContent",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ruleReview",
"columnName": "ruleReview",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"bookSourceUrl"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_book_sources_bookSourceUrl",
"unique": false,
"columnNames": [
"bookSourceUrl"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_book_sources_bookSourceUrl` ON `${TABLE_NAME}` (`bookSourceUrl`)"
}
],
"foreignKeys": []
},
{
"tableName": "chapters",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `isVolume` INTEGER NOT NULL, `baseUrl` TEXT NOT NULL, `bookUrl` TEXT NOT NULL, `index` INTEGER NOT NULL, `isVip` INTEGER NOT NULL, `isPay` INTEGER NOT NULL, `resourceUrl` TEXT, `tag` TEXT, `start` INTEGER, `end` INTEGER, `startFragmentId` TEXT, `endFragmentId` TEXT, `variable` TEXT, PRIMARY KEY(`url`, `bookUrl`), FOREIGN KEY(`bookUrl`) REFERENCES `books`(`bookUrl`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "isVolume",
"columnName": "isVolume",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "baseUrl",
"columnName": "baseUrl",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "bookUrl",
"columnName": "bookUrl",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "index",
"columnName": "index",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isVip",
"columnName": "isVip",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isPay",
"columnName": "isPay",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "resourceUrl",
"columnName": "resourceUrl",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "tag",
"columnName": "tag",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "start",
"columnName": "start",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "end",
"columnName": "end",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "startFragmentId",
"columnName": "startFragmentId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "endFragmentId",
"columnName": "endFragmentId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "variable",
"columnName": "variable",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"url",
"bookUrl"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_chapters_bookUrl",
"unique": false,
"columnNames": [
"bookUrl"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_chapters_bookUrl` ON `${TABLE_NAME}` (`bookUrl`)"
},
{
"name": "index_chapters_bookUrl_index",
"unique": true,
"columnNames": [
"bookUrl",
"index"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_chapters_bookUrl_index` ON `${TABLE_NAME}` (`bookUrl`, `index`)"
}
],
"foreignKeys": [
{
"table": "books",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"bookUrl"
],
"referencedColumns": [
"bookUrl"
]
}
]
},
{
"tableName": "replace_rules",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL DEFAULT '', `group` TEXT, `pattern` TEXT NOT NULL DEFAULT '', `replacement` TEXT NOT NULL DEFAULT '', `scope` TEXT, `scopeTitle` INTEGER NOT NULL DEFAULT 0, `scopeContent` INTEGER NOT NULL DEFAULT 1, `isEnabled` INTEGER NOT NULL DEFAULT 1, `isRegex` INTEGER NOT NULL DEFAULT 1, `timeoutMillisecond` INTEGER NOT NULL DEFAULT 3000, `sortOrder` INTEGER NOT NULL DEFAULT 0)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "group",
"columnName": "group",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "pattern",
"columnName": "pattern",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "replacement",
"columnName": "replacement",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "scope",
"columnName": "scope",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "scopeTitle",
"columnName": "scopeTitle",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "scopeContent",
"columnName": "scopeContent",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "1"
},
{
"fieldPath": "isEnabled",
"columnName": "isEnabled",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "1"
},
{
"fieldPath": "isRegex",
"columnName": "isRegex",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "1"
},
{
"fieldPath": "timeoutMillisecond",
"columnName": "timeoutMillisecond",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "3000"
},
{
"fieldPath": "order",
"columnName": "sortOrder",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_replace_rules_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_replace_rules_id` ON `${TABLE_NAME}` (`id`)"
}
],
"foreignKeys": []
},
{
"tableName": "searchBooks",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bookUrl` TEXT NOT NULL, `origin` TEXT NOT NULL, `originName` TEXT NOT NULL, `type` INTEGER NOT NULL, `name` TEXT NOT NULL, `author` TEXT NOT NULL, `kind` TEXT, `coverUrl` TEXT, `intro` TEXT, `wordCount` TEXT, `latestChapterTitle` TEXT, `tocUrl` TEXT NOT NULL, `time` INTEGER NOT NULL, `variable` TEXT, `originOrder` INTEGER NOT NULL, PRIMARY KEY(`bookUrl`), FOREIGN KEY(`origin`) REFERENCES `book_sources`(`bookSourceUrl`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "bookUrl",
"columnName": "bookUrl",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "origin",
"columnName": "origin",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "originName",
"columnName": "originName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "author",
"columnName": "author",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "kind",
"columnName": "kind",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "coverUrl",
"columnName": "coverUrl",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "intro",
"columnName": "intro",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "wordCount",
"columnName": "wordCount",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "latestChapterTitle",
"columnName": "latestChapterTitle",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "tocUrl",
"columnName": "tocUrl",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "time",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "variable",
"columnName": "variable",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "originOrder",
"columnName": "originOrder",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"bookUrl"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_searchBooks_bookUrl",
"unique": true,
"columnNames": [
"bookUrl"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_searchBooks_bookUrl` ON `${TABLE_NAME}` (`bookUrl`)"
},
{
"name": "index_searchBooks_origin",
"unique": false,
"columnNames": [
"origin"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_searchBooks_origin` ON `${TABLE_NAME}` (`origin`)"
}
],
"foreignKeys": [
{
"table": "book_sources",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"origin"
],
"referencedColumns": [
"bookSourceUrl"
]
}
]
},
{
"tableName": "search_keywords",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`word` TEXT NOT NULL, `usage` INTEGER NOT NULL, `lastUseTime` INTEGER NOT NULL, PRIMARY KEY(`word`))",
"fields": [
{
"fieldPath": "word",
"columnName": "word",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "usage",
"columnName": "usage",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "lastUseTime",
"columnName": "lastUseTime",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"word"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_search_keywords_word",
"unique": true,
"columnNames": [
"word"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_search_keywords_word` ON `${TABLE_NAME}` (`word`)"
}
],
"foreignKeys": []
},
{
"tableName": "cookies",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `cookie` TEXT NOT NULL, PRIMARY KEY(`url`))",
"fields": [
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "cookie",
"columnName": "cookie",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"url"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_cookies_url",
"unique": true,
"columnNames": [
"url"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_cookies_url` ON `${TABLE_NAME}` (`url`)"
}
],
"foreignKeys": []
},
{
"tableName": "rssSources",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceUrl` TEXT NOT NULL, `sourceName` TEXT NOT NULL, `sourceIcon` TEXT NOT NULL, `sourceGroup` TEXT, `sourceComment` TEXT, `enabled` INTEGER NOT NULL, `variableComment` TEXT, `enabledCookieJar` INTEGER DEFAULT 0, `concurrentRate` TEXT, `header` TEXT, `loginUrl` TEXT, `loginUi` TEXT, `loginCheckJs` TEXT, `coverDecodeJs` TEXT, `sortUrl` TEXT, `singleUrl` INTEGER NOT NULL, `articleStyle` INTEGER NOT NULL, `ruleArticles` TEXT, `ruleNextPage` TEXT, `ruleTitle` TEXT, `rulePubDate` TEXT, `ruleDescription` TEXT, `ruleImage` TEXT, `ruleLink` TEXT, `ruleContent` TEXT, `style` TEXT, `enableJs` INTEGER NOT NULL, `loadWithBaseUrl` INTEGER NOT NULL, `lastUpdateTime` INTEGER NOT NULL DEFAULT 0, `customOrder` INTEGER NOT NULL, PRIMARY KEY(`sourceUrl`))",
"fields": [
{
"fieldPath": "sourceUrl",
"columnName": "sourceUrl",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceName",
"columnName": "sourceName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceIcon",
"columnName": "sourceIcon",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceGroup",
"columnName": "sourceGroup",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "sourceComment",
"columnName": "sourceComment",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "variableComment",
"columnName": "variableComment",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "enabledCookieJar",
"columnName": "enabledCookieJar",
"affinity": "INTEGER",
"notNull": false,
"defaultValue": "0"
},
{
"fieldPath": "concurrentRate",
"columnName": "concurrentRate",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "header",
"columnName": "header",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "loginUrl",
"columnName": "loginUrl",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "loginUi",
"columnName": "loginUi",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "loginCheckJs",
"columnName": "loginCheckJs",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "coverDecodeJs",
"columnName": "coverDecodeJs",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "sortUrl",
"columnName": "sortUrl",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "singleUrl",
"columnName": "singleUrl",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "articleStyle",
"columnName": "articleStyle",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ruleArticles",
"columnName": "ruleArticles",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ruleNextPage",
"columnName": "ruleNextPage",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ruleTitle",
"columnName": "ruleTitle",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "rulePubDate",
"columnName": "rulePubDate",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ruleDescription",
"columnName": "ruleDescription",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ruleImage",
"columnName": "ruleImage",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ruleLink",
"columnName": "ruleLink",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ruleContent",
"columnName": "ruleContent",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "style",
"columnName": "style",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "enableJs",
"columnName": "enableJs",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "loadWithBaseUrl",
"columnName": "loadWithBaseUrl",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "lastUpdateTime",
"columnName": "lastUpdateTime",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "customOrder",
"columnName": "customOrder",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"sourceUrl"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_rssSources_sourceUrl",
"unique": false,
"columnNames": [
"sourceUrl"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_rssSources_sourceUrl` ON `${TABLE_NAME}` (`sourceUrl`)"
}
],
"foreignKeys": []
},
{
"tableName": "bookmarks",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`time` INTEGER NOT NULL, `bookName` TEXT NOT NULL, `bookAuthor` TEXT NOT NULL, `chapterIndex` INTEGER NOT NULL, `chapterPos` INTEGER NOT NULL, `chapterName` TEXT NOT NULL, `bookText` TEXT NOT NULL, `content` TEXT NOT NULL, PRIMARY KEY(`time`))",
"fields": [
{
"fieldPath": "time",
"columnName": "time",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "bookName",
"columnName": "bookName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "bookAuthor",
"columnName": "bookAuthor",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "chapterIndex",
"columnName": "chapterIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "chapterPos",
"columnName": "chapterPos",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "chapterName",
"columnName": "chapterName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "bookText",
"columnName": "bookText",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"time"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_bookmarks_bookName_bookAuthor",
"unique": false,
"columnNames": [
"bookName",
"bookAuthor"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_bookmarks_bookName_bookAuthor` ON `${TABLE_NAME}` (`bookName`, `bookAuthor`)"
}
],
"foreignKeys": []
},
{
"tableName": "rssArticles",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`origin` TEXT NOT NULL, `sort` TEXT NOT NULL, `title` TEXT NOT NULL, `order` INTEGER NOT NULL, `link` TEXT NOT NULL, `pubDate` TEXT, `description` TEXT, `content` TEXT, `image` TEXT, `read` INTEGER NOT NULL, `variable` TEXT, PRIMARY KEY(`origin`, `link`))",
"fields": [
{
"fieldPath": "origin",
"columnName": "origin",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "link",
"columnName": "link",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "pubDate",
"columnName": "pubDate",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "description",
"columnName": "description",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "image",
"columnName": "image",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "read",
"columnName": "read",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "variable",
"columnName": "variable",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"origin",
"link"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "rssReadRecords",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`record` TEXT NOT NULL, `read` INTEGER NOT NULL, PRIMARY KEY(`record`))",
"fields": [
{
"fieldPath": "record",
"columnName": "record",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "read",
"columnName": "read",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"record"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "rssStars",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`origin` TEXT NOT NULL, `sort` TEXT NOT NULL, `title` TEXT NOT NULL, `starTime` INTEGER NOT NULL, `link` TEXT NOT NULL, `pubDate` TEXT, `description` TEXT, `content` TEXT, `image` TEXT, `variable` TEXT, PRIMARY KEY(`origin`, `link`))",
"fields": [
{
"fieldPath": "origin",
"columnName": "origin",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "starTime",
"columnName": "starTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "link",
"columnName": "link",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "pubDate",
"columnName": "pubDate",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "description",
"columnName": "description",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "image",
"columnName": "image",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "variable",
"columnName": "variable",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"origin",
"link"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "txtTocRules",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `rule` TEXT NOT NULL, `example` TEXT, `serialNumber` INTEGER NOT NULL, `enable` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "rule",
"columnName": "rule",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "example",
"columnName": "example",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "serialNumber",
"columnName": "serialNumber",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enable",
"columnName": "enable",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "readRecord",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `bookName` TEXT NOT NULL, `readTime` INTEGER NOT NULL DEFAULT 0, `lastRead` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`deviceId`, `bookName`))",
"fields": [
{
"fieldPath": "deviceId",
"columnName": "deviceId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "bookName",
"columnName": "bookName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "readTime",
"columnName": "readTime",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "lastRead",
"columnName": "lastRead",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
}
],
"primaryKey": {
"columnNames": [
"deviceId",
"bookName"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "httpTTS",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `contentType` TEXT, `concurrentRate` TEXT DEFAULT '0', `loginUrl` TEXT, `loginUi` TEXT, `header` TEXT, `enabledCookieJar` INTEGER DEFAULT 0, `loginCheckJs` TEXT, `lastUpdateTime` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "contentType",
"columnName": "contentType",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "concurrentRate",
"columnName": "concurrentRate",
"affinity": "TEXT",
"notNull": false,
"defaultValue": "'0'"
},
{
"fieldPath": "loginUrl",
"columnName": "loginUrl",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "loginUi",
"columnName": "loginUi",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "header",
"columnName": "header",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "enabledCookieJar",
"columnName": "enabledCookieJar",
"affinity": "INTEGER",
"notNull": false,
"defaultValue": "0"
},
{
"fieldPath": "loginCheckJs",
"columnName": "loginCheckJs",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastUpdateTime",
"columnName": "lastUpdateTime",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "caches",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` TEXT, `deadline` INTEGER NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "deadline",
"columnName": "deadline",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"key"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_caches_key",
"unique": true,
"columnNames": [
"key"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_caches_key` ON `${TABLE_NAME}` (`key`)"
}
],
"foreignKeys": []
},
{
"tableName": "ruleSubs",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `type` INTEGER NOT NULL, `customOrder` INTEGER NOT NULL, `autoUpdate` INTEGER NOT NULL, `update` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "customOrder",
"columnName": "customOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "autoUpdate",
"columnName": "autoUpdate",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "update",
"columnName": "update",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "keyboardAssists",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` INTEGER NOT NULL DEFAULT 0, `key` TEXT NOT NULL DEFAULT '', `value` TEXT NOT NULL DEFAULT '', `serialNo` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`type`, `key`))",
"fields": [
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "serialNo",
"columnName": "serialNo",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
}
],
"primaryKey": {
"columnNames": [
"type",
"key"
],
"autoGenerate": false
},
"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, '09020e77cffa237f9a98c62eed0a01f2')"
]
}
}

View File

@ -7,10 +7,7 @@ import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookProgress import io.legado.app.data.entities.BookProgress
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.help.AppWebDav import io.legado.app.help.*
import io.legado.app.help.BookHelp
import io.legado.app.help.CacheManager
import io.legado.app.help.ContentProcessor
import io.legado.app.help.glide.ImageLoader import io.legado.app.help.glide.ImageLoader
import io.legado.app.model.BookCover import io.legado.app.model.BookCover
import io.legado.app.model.localBook.LocalBook import io.legado.app.model.localBook.LocalBook
@ -98,7 +95,7 @@ object BookController {
} }
val book = appDb.bookDao.getBook(bookUrl) val book = appDb.bookDao.getBook(bookUrl)
?: return returnData.setErrorMsg("bookUrl不对") ?: return returnData.setErrorMsg("bookUrl不对")
if (book.isLocalBook()) { if (book.isLocal) {
val toc = LocalBook.getChapterList(book) val toc = LocalBook.getChapterList(book)
appDb.bookChapterDao.delByBook(book.bookUrl) appDb.bookChapterDao.delByBook(book.bookUrl)
appDb.bookChapterDao.insert(*toc.toTypedArray()) appDb.bookChapterDao.insert(*toc.toTypedArray())

View File

@ -0,0 +1,26 @@
package io.legado.app.constant
import androidx.annotation.IntDef
object BookSourceType {
const val default = 0 // 0 文本
const val audio = 1 // 1 音频
const val image = 2 // 2 图片
const val file = 3 // 3 只提供下载服务的网站
@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.SOURCE)
@IntDef(default, audio, image, file)
annotation class Type
fun toBookType(sourceType: Int) {
when (sourceType) {
file -> BookType.text or BookType.webFile
image -> BookType.image
audio -> BookType.audio
else -> BookType.text
}
}
}

View File

@ -2,20 +2,49 @@ package io.legado.app.constant
import androidx.annotation.IntDef import androidx.annotation.IntDef
/**
* 以二进制位来区分,可能一本书籍包含多个类型,每一位代表一个类型,数值为2的n次方
* 以二进制位来区分,数据库查询更高效
*/
object BookType { object BookType {
const val default = 0 // 0 文本 /**
const val audio = 1 // 1 音频 * 8 文本
const val image = 2 // 2 图片 */
const val file = 3 // 3 只提供下载服务的网站 const val text = 0b1000
const val local = "loc_book"
/**
* 16 音频
*/
const val audio = 0b100000
/**
* 32 图片
*/
const val image = 0b1000000
/**
* 64 只提供下载服务的网站
*/
const val webFile = 0b10000000
/**
* 128 本地
*/
const val local = 0b100000000
/**
* 本地书籍书源标志
*/
const val localTag = "loc_book"
/** /**
* 书源已webDav::开头的书籍,可以从webDav更新或重新下载 * 书源已webDav::开头的书籍,可以从webDav更新或重新下载
*/ */
const val webDav = "webDav::" const val webDavTag = "webDav::"
@Target(AnnotationTarget.VALUE_PARAMETER) @Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.SOURCE) @Retention(AnnotationRetention.SOURCE)
@IntDef(default, audio, image, file) @IntDef(text, audio, image, webFile)
annotation class Type annotation class Type
} }

View File

@ -20,7 +20,7 @@ val appDb by lazy {
} }
@Database( @Database(
version = 54, version = 55,
exportSchema = true, exportSchema = true,
entities = [Book::class, BookGroup::class, BookSource::class, BookChapter::class, entities = [Book::class, BookGroup::class, BookSource::class, BookChapter::class,
ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class, ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class,
@ -39,6 +39,7 @@ val appDb by lazy {
AutoMigration(from = 51, to = 52), AutoMigration(from = 51, to = 52),
AutoMigration(from = 52, to = 53), AutoMigration(from = 52, to = 53),
AutoMigration(from = 53, to = 54), AutoMigration(from = 53, to = 54),
AutoMigration(from = 54, to = 55, spec = DatabaseMigrations.Migration_44_45::class)
] ]
) )
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {

View File

@ -1,45 +1,24 @@
package io.legado.app.data package io.legado.app.data
import androidx.room.migration.AutoMigrationSpec
import androidx.room.migration.Migration import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteDatabase
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.BookSourceType
import io.legado.app.constant.BookType
object DatabaseMigrations { object DatabaseMigrations {
val migrations: Array<Migration> by lazy { val migrations: Array<Migration> by lazy {
arrayOf( arrayOf(
migration_10_11, migration_10_11, migration_11_12, migration_12_13, migration_13_14,
migration_11_12, migration_14_15, migration_15_17, migration_17_18, migration_18_19,
migration_12_13, migration_19_20, migration_20_21, migration_21_22, migration_22_23,
migration_13_14, migration_23_24, migration_24_25, migration_25_26, migration_26_27,
migration_14_15, migration_27_28, migration_28_29, migration_29_30, migration_30_31,
migration_15_17, migration_31_32, migration_32_33, migration_33_34, migration_34_35,
migration_17_18, migration_35_36, migration_36_37, migration_37_38, migration_38_39,
migration_18_19, migration_39_40, migration_40_41, migration_41_42, migration_42_43,
migration_19_20,
migration_20_21,
migration_21_22,
migration_22_23,
migration_23_24,
migration_24_25,
migration_25_26,
migration_26_27,
migration_27_28,
migration_28_29,
migration_29_30,
migration_30_31,
migration_31_32,
migration_32_33,
migration_33_34,
migration_34_35,
migration_35_36,
migration_36_37,
migration_37_38,
migration_38_39,
migration_39_40,
migration_40_41,
migration_41_42,
migration_42_43
) )
} }
@ -343,4 +322,44 @@ object DatabaseMigrations {
database.execSQL("ALTER TABLE `chapters` ADD `isVolume` INTEGER NOT NULL DEFAULT 0") database.execSQL("ALTER TABLE `chapters` ADD `isVolume` INTEGER NOT NULL DEFAULT 0")
} }
} }
@Suppress("ClassName")
class Migration_44_45 : AutoMigrationSpec {
override fun onPostMigrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
update books set type = ${BookType.audio}
where type = ${BookSourceType.audio}
""".trimIndent()
)
db.execSQL(
"""
update books set type = ${BookType.image}
where type = ${BookSourceType.image}
""".trimIndent()
)
db.execSQL(
"""
update books set type = ${BookType.webFile}
where type = ${BookSourceType.file}
""".trimIndent()
)
db.execSQL(
"""
update books set type = ${BookType.text}
where type = ${BookSourceType.default}
""".trimIndent()
)
db.execSQL(
"""
update books set type = type | ${BookType.local}
where origin = '${BookType.localTag}' or origin like '${BookType.webDavTag}%'
""".trimIndent()
)
}
}
} }

View File

@ -11,8 +11,8 @@ interface BookDao {
@Query( @Query(
""" """
select * from books where type != ${BookType.audio} select * from books where type & ${BookType.text} > 0
and origin != '${BookType.local}' and origin not like '${BookType.webDav}%' and type & ${BookType.local} = 0
and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0 and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0
and (select show from book_groups where groupId = ${AppConst.bookGroupNetNoneId}) != 1 and (select show from book_groups where groupId = ${AppConst.bookGroupNetNoneId}) != 1
""" """
@ -22,15 +22,15 @@ interface BookDao {
@Query("SELECT * FROM books order by durChapterTime desc") @Query("SELECT * FROM books order by durChapterTime desc")
fun flowAll(): Flow<List<Book>> fun flowAll(): Flow<List<Book>>
@Query("SELECT * FROM books WHERE type = ${BookType.audio}") @Query("SELECT * FROM books WHERE type & ${BookType.audio} > 0")
fun flowAudio(): Flow<List<Book>> fun flowAudio(): Flow<List<Book>>
@Query("SELECT * FROM books WHERE origin = '${BookType.local}' or origin like '${BookType.webDav}%'") @Query("SELECT * FROM books WHERE type & ${BookType.local} > 0")
fun flowLocal(): Flow<List<Book>> fun flowLocal(): Flow<List<Book>>
@Query( @Query(
""" """
select * from books where type != ${BookType.audio} and origin != '${BookType.local}' and origin not like '${BookType.webDav}%' select * from books where type & ${BookType.audio} = 0 and type & ${BookType.local} = 0
and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0 and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0
""" """
) )
@ -38,13 +38,13 @@ interface BookDao {
@Query( @Query(
""" """
select * from books where origin = '${BookType.local}' or origin like '${BookType.webDav}%' select * from books where type & ${BookType.local} > 0
and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0 and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0
""" """
) )
fun flowLocalNoGroup(): Flow<List<Book>> fun flowLocalNoGroup(): Flow<List<Book>>
@Query("SELECT bookUrl FROM books WHERE origin = '${BookType.local}' or origin like '${BookType.webDav}%'") @Query("SELECT bookUrl FROM books WHERE type & ${BookType.local} > 0")
fun flowLocalUri(): Flow<List<String>> fun flowLocalUri(): Flow<List<String>>
@Query("SELECT * FROM books WHERE (`group` & :group) > 0") @Query("SELECT * FROM books WHERE (`group` & :group) > 0")
@ -65,13 +65,13 @@ interface BookDao {
@Query("SELECT * FROM books WHERE name = :name and author = :author") @Query("SELECT * FROM books WHERE name = :name and author = :author")
fun getBook(name: String, author: String): Book? fun getBook(name: String, author: String): Book?
@get:Query("select count(bookUrl) from books where (SELECT sum(groupId) FROM book_groups) & `group` = 0") @get:Query("select count(bookUrl) from books where (SELECT sum(groupId) FROM book_groups)")
val noGroupSize: Int val noGroupSize: Int
@get:Query("SELECT * FROM books where origin <> '${BookType.local}' and type = 0") @get:Query("SELECT * FROM books where type & ${BookType.local} = 0")
val webBooks: List<Book> val webBooks: List<Book>
@get:Query("SELECT * FROM books where origin <> '${BookType.local}' and canUpdate = 1") @get:Query("SELECT * FROM books where type & ${BookType.local} = 0 and canUpdate = 1")
val hasUpdateBooks: List<Book> val hasUpdateBooks: List<Book>
@get:Query("SELECT * FROM books") @get:Query("SELECT * FROM books")

View File

@ -23,21 +23,21 @@ interface BookGroupDao {
with const as (SELECT sum(groupId) sumGroupId FROM book_groups where groupId > 0) with const as (SELECT sum(groupId) sumGroupId FROM book_groups where groupId > 0)
SELECT book_groups.* FROM book_groups, const where (groupId >= 0 and show > 0) SELECT book_groups.* FROM book_groups, const where (groupId >= 0 and show > 0)
or (groupId = -1 and show > 0) or (groupId = -1 and show > 0)
or (groupId = -2 and show > 0 and (select count(bookUrl) from books where origin = '${BookType.local}') > 0) or (groupId = -2 and show > 0 and (select count(bookUrl) from books where type & ${BookType.local} > 0) > 0)
or (groupId = -3 and show > 0 and (select count(bookUrl) from books where type = ${BookType.audio}) > 0) or (groupId = -3 and show > 0 and (select count(bookUrl) from books where type & ${BookType.audio} > 0) > 0)
or (groupId = -4 and show > 0 or (groupId = -4 and show > 0
and ( and (
select count(bookUrl) from books select count(bookUrl) from books
where type != '${BookType.audio}' where type & ${BookType.audio} = 0
and origin != '${BookType.local}' and type & ${BookType.local} = 0
and const.sumGroupId & `group` = 0 and const.sumGroupId & `group` = 0
) > 0 ) > 0
) )
or (groupId = -5 and show > 0 or (groupId = -5 and show > 0
and ( and (
select count(bookUrl) from books select count(bookUrl) from books
where type != '${BookType.audio}' where type & ${BookType.audio} = 0
and origin = '${BookType.local}' and type & ${BookType.local} > 0
and const.sumGroupId & `group` = 0 and const.sumGroupId & `group` = 0
) > 0 ) > 0
) )

View File

@ -10,11 +10,12 @@ import io.legado.app.help.BookHelp
import io.legado.app.help.ContentProcessor import io.legado.app.help.ContentProcessor
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.isEpub
import io.legado.app.help.isImage
import io.legado.app.model.ReadBook import io.legado.app.model.ReadBook
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.MD5Utils import io.legado.app.utils.MD5Utils
import io.legado.app.utils.fromJsonObject import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.isUri
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@ -37,8 +38,8 @@ data class Book(
@ColumnInfo(defaultValue = "") @ColumnInfo(defaultValue = "")
var tocUrl: String = "", var tocUrl: String = "",
// 书源URL(默认BookType.local) // 书源URL(默认BookType.local)
@ColumnInfo(defaultValue = "") @ColumnInfo(defaultValue = BookType.localTag)
var origin: String = BookType.local, var origin: String = BookType.localTag,
//书源名称 or 本地书籍文件名 //书源名称 or 本地书籍文件名
@ColumnInfo(defaultValue = "") @ColumnInfo(defaultValue = "")
var originName: String = "", var originName: String = "",
@ -62,8 +63,8 @@ data class Book(
var customIntro: String? = null, var customIntro: String? = null,
// 自定义字符集名称(仅适用于本地书籍) // 自定义字符集名称(仅适用于本地书籍)
var charset: String? = null, var charset: String? = null,
// 0:text 1:audio 3:image // 类型,详见BookType
@ColumnInfo(defaultValue = "0") @ColumnInfo(defaultValue = "1")
var type: Int = 0, var type: Int = 0,
// 自定义分组索引号 // 自定义分组索引号
@ColumnInfo(defaultValue = "0") @ColumnInfo(defaultValue = "0")
@ -108,29 +109,6 @@ data class Book(
var readConfig: ReadConfig? = null var readConfig: ReadConfig? = null
) : Parcelable, BaseBook { ) : Parcelable, BaseBook {
fun isLocalBook(): Boolean {
//通过判断书籍链接来判断http* file:// content://
//origin判断不可靠 http* BookType.local webDav::
return origin == BookType.local || bookUrl.isUri()
}
fun isLocalTxt(): Boolean {
return isLocalBook() && originName.endsWith(".txt", true)
}
fun isEpub(): Boolean {
return originName.endsWith(".epub", true)
}
fun isUmd(): Boolean {
return originName.endsWith(".umd", true)
}
@Suppress("unused")
fun isOnLineTxt(): Boolean {
return !isLocalBook() && type == 0
}
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other is Book) { if (other is Book) {
return other.bookUrl == bookUrl return other.bookUrl == bookUrl
@ -206,7 +184,7 @@ data class Book(
return useReplaceRule return useReplaceRule
} }
//图片类书源 epub本地 默认关闭净化 //图片类书源 epub本地 默认关闭净化
if (type == BookType.image || isEpub()) { if (isImage || isEpub) {
return false return false
} }
return AppConfig.replaceEnableDefault return AppConfig.replaceEnableDefault
@ -226,7 +204,7 @@ data class Book(
fun getPageAnim(): Int { fun getPageAnim(): Int {
var pageAnim = config.pageAnim var pageAnim = config.pageAnim
?: if (type == BookType.image) PageAnim.scrollPageAnim else ReadBookConfig.pageAnim ?: if (type and BookType.image > 0) PageAnim.scrollPageAnim else ReadBookConfig.pageAnim
if (pageAnim < 0) { if (pageAnim < 0) {
pageAnim = ReadBookConfig.pageAnim pageAnim = ReadBookConfig.pageAnim
} }
@ -239,7 +217,7 @@ data class Book(
fun getImageStyle(): String? { fun getImageStyle(): String? {
return config.imageStyle return config.imageStyle
?: if (type == BookType.image) imgStyleFull else null ?: if (isImage) imgStyleFull else null
} }
fun setTtsEngine(ttsEngine: String?) { fun setTtsEngine(ttsEngine: String?) {
@ -358,4 +336,4 @@ data class Book(
@TypeConverter @TypeConverter
fun stringToReadConfig(json: String?) = GSON.fromJsonObject<ReadConfig>(json).getOrNull() fun stringToReadConfig(json: String?) = GSON.fromJsonObject<ReadConfig>(json).getOrNull()
} }
} }

View File

@ -4,7 +4,7 @@ import android.os.Parcelable
import android.text.TextUtils import android.text.TextUtils
import androidx.room.* import androidx.room.*
import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.constant.BookType import io.legado.app.constant.BookSourceType
import io.legado.app.data.entities.rule.* import io.legado.app.data.entities.rule.*
import io.legado.app.help.source.SourceAnalyzer import io.legado.app.help.source.SourceAnalyzer
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
@ -29,7 +29,7 @@ data class BookSource(
// 分组 // 分组
var bookSourceGroup: String? = null, var bookSourceGroup: String? = null,
// 类型0 文本1 音频, 2 图片, 3 文件(指的是类似知轩藏书只提供下载的网站) // 类型0 文本1 音频, 2 图片, 3 文件(指的是类似知轩藏书只提供下载的网站)
@BookType.Type @BookSourceType.Type
var bookSourceType: Int = 0, var bookSourceType: Int = 0,
// 详情页url正则 // 详情页url正则
var bookUrlPattern: String? = null, var bookUrlPattern: String? = null,

View File

@ -4,6 +4,7 @@ import android.content.Context
import android.os.Parcelable import android.os.Parcelable
import androidx.room.* import androidx.room.*
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.BookType
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonObject import io.legado.app.utils.fromJsonObject
import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.IgnoredOnParcel
@ -26,7 +27,7 @@ data class SearchBook(
override var bookUrl: String = "", override var bookUrl: String = "",
var origin: String = "", // 书源规则 var origin: String = "", // 书源规则
var originName: String = "", var originName: String = "",
var type: Int = 0, // @BookType var type: Int = BookType.text, // @BookType
override var name: String = "", override var name: String = "",
override var author: String = "", override var author: String = "",
override var kind: String? = null, override var kind: String? = null,

View File

@ -0,0 +1,65 @@
package io.legado.app.help
import io.legado.app.constant.BookSourceType
import io.legado.app.constant.BookType
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookSource
val Book.isAudio: Boolean
get() {
return type and BookType.audio > 0
}
val Book.isImage: Boolean
get() {
return type and BookType.image > 0
}
val Book.isLocal: Boolean
get() {
return type and BookType.local > 0
}
val Book.isLocalTxt: Boolean
get() {
return isLocal && originName.endsWith(".txt", true)
}
val Book.isEpub: Boolean
get() {
return isLocal && originName.endsWith(".epub", true)
}
val Book.isUmd: Boolean
get() {
return isLocal && originName.endsWith(".umd", true)
}
val Book.isOnLineTxt: Boolean
get() {
return !isLocal && type and BookType.text > 0
}
fun Book.upType() {
if (type < 8) {
type = when (type) {
BookSourceType.image -> BookType.image
BookSourceType.audio -> BookType.audio
BookSourceType.file -> BookType.webFile
else -> BookType.text
}
if (origin == "loc_book" || origin.startsWith(BookType.webDavTag)) {
type = type or BookType.local
}
}
}
fun BookSource.getBookType(): Int {
return when (bookSourceType) {
BookSourceType.file -> BookType.text or BookType.webFile
BookSourceType.image -> BookType.image
BookSourceType.audio -> BookType.audio
else -> BookType.text
}
}

View File

@ -60,7 +60,7 @@ object BookHelp {
val originNames = ArrayList<String>() val originNames = ArrayList<String>()
appDb.bookDao.all.forEach { appDb.bookDao.all.forEach {
bookFolderNames.add(it.getFolderName()) bookFolderNames.add(it.getFolderName())
if (it.isEpub()) originNames.add(it.originName) if (it.isEpub) originNames.add(it.originName)
} }
downloadDir.getFile(cacheFolderName) downloadDir.getFile(cacheFolderName)
.listFiles()?.forEach { bookFile -> .listFiles()?.forEach { bookFile ->
@ -198,7 +198,7 @@ object BookHelp {
fun getChapterFiles(book: Book): List<String> { fun getChapterFiles(book: Book): List<String> {
val fileNameList = arrayListOf<String>() val fileNameList = arrayListOf<String>()
if (book.isLocalTxt()) { if (book.isLocalTxt) {
return fileNameList return fileNameList
} }
FileUtils.createFolderIfNotExist( FileUtils.createFolderIfNotExist(
@ -214,7 +214,7 @@ object BookHelp {
* 检测该章节是否下载 * 检测该章节是否下载
*/ */
fun hasContent(book: Book, bookChapter: BookChapter): Boolean { fun hasContent(book: Book, bookChapter: BookChapter): Boolean {
return if (book.isLocalTxt()) { return if (book.isLocalTxt) {
true true
} else { } else {
downloadDir.exists( downloadDir.exists(
@ -258,9 +258,9 @@ object BookHelp {
if (file.exists()) { if (file.exists()) {
return file.readText() return file.readText()
} }
if (book.isLocalBook()) { if (book.isLocal) {
val string = LocalBook.getContent(book, bookChapter) val string = LocalBook.getContent(book, bookChapter)
if (string != null && book.isEpub()) { if (string != null && book.isEpub) {
saveText(book, bookChapter, string) saveText(book, bookChapter, string)
} }
return string return string

View File

@ -4,13 +4,12 @@ import androidx.annotation.Keep
import com.jayway.jsonpath.JsonPath import com.jayway.jsonpath.JsonPath
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.constant.BookType import io.legado.app.constant.BookSourceType
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.rule.* import io.legado.app.data.entities.rule.*
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
import io.legado.app.utils.* import io.legado.app.utils.*
import java.io.InputStream import java.io.InputStream
import java.util.regex.Pattern import java.util.regex.Pattern
@Suppress("RegExpRedundantEscape") @Suppress("RegExpRedundantEscape")
@ -91,7 +90,7 @@ object SourceAnalyzer {
searchUrl = toNewUrl(jsonItem.readString("ruleSearchUrl")) searchUrl = toNewUrl(jsonItem.readString("ruleSearchUrl"))
exploreUrl = toNewUrls(jsonItem.readString("ruleFindUrl")) exploreUrl = toNewUrls(jsonItem.readString("ruleFindUrl"))
bookSourceType = bookSourceType =
if (jsonItem.readString("bookSourceType") == "AUDIO") BookType.audio else BookType.default if (jsonItem.readString("bookSourceType") == "AUDIO") BookSourceType.audio else BookSourceType.default
enabled = jsonItem.readBool("enable") ?: true enabled = jsonItem.readBool("enable") ?: true
if (exploreUrl.isNullOrBlank()) { if (exploreUrl.isNullOrBlank()) {
enabledExplore = false enabledExplore = false
@ -226,7 +225,7 @@ object SourceAnalyzer {
var bookSourceName: String = "", // 名称 var bookSourceName: String = "", // 名称
var bookSourceGroup: String? = null, // 分组 var bookSourceGroup: String? = null, // 分组
var bookSourceUrl: String = "", // 地址,包括 http/https var bookSourceUrl: String = "", // 地址,包括 http/https
var bookSourceType: Int = BookType.default, // 类型0 文本1 音频 var bookSourceType: Int = BookSourceType.default, // 类型0 文本1 音频
var bookUrlPattern: String? = null, // 详情页url正则 var bookUrlPattern: String? = null, // 详情页url正则
var customOrder: Int = 0, // 手动排序编号 var customOrder: Int = 0, // 手动排序编号
var enabled: Boolean = true, // 是否启用 var enabled: Boolean = true, // 是否启用

View File

@ -3,6 +3,7 @@ package io.legado.app.help.storage
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import io.legado.app.constant.BookType
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
@ -123,8 +124,9 @@ object ImportOldData {
book.origin = jsonItem.readString("$.tag") ?: "" book.origin = jsonItem.readString("$.tag") ?: ""
book.originName = jsonItem.readString("$.bookInfoBean.origin") ?: "" book.originName = jsonItem.readString("$.bookInfoBean.origin") ?: ""
book.author = jsonItem.readString("$.bookInfoBean.author") ?: "" book.author = jsonItem.readString("$.bookInfoBean.author") ?: ""
book.type = val local = if (book.origin == "loc_book") BookType.local else 0
if (jsonItem.readString("$.bookInfoBean.bookSourceType") == "AUDIO") 1 else 0 book.type = local or
if (jsonItem.readString("$.bookInfoBean.bookSourceType") == "AUDIO") BookType.audio else BookType.text
book.tocUrl = jsonItem.readString("$.bookInfoBean.chapterUrl") ?: book.bookUrl book.tocUrl = jsonItem.readString("$.bookInfoBean.chapterUrl") ?: book.bookUrl
book.coverUrl = jsonItem.readString("$.bookInfoBean.coverUrl") book.coverUrl = jsonItem.readString("$.bookInfoBean.coverUrl")
book.customCoverUrl = jsonItem.readString("$.customCoverPath") book.customCoverUrl = jsonItem.readString("$.customCoverPath")

View File

@ -16,6 +16,7 @@ import io.legado.app.help.LauncherIconHelp
import io.legado.app.help.config.LocalConfig import io.legado.app.help.config.LocalConfig
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.upType
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
@ -65,6 +66,9 @@ object Restore {
suspend fun restoreDatabase(path: String = Backup.backupPath) { suspend fun restoreDatabase(path: String = Backup.backupPath) {
withContext(IO) { withContext(IO) {
fileToListT<Book>(path, "bookshelf.json")?.let { fileToListT<Book>(path, "bookshelf.json")?.let {
it.forEach { book ->
book.upType()
}
appDb.bookDao.insert(*it.toTypedArray()) appDb.bookDao.insert(*it.toTypedArray())
} }
fileToListT<Bookmark>(path, "bookmark.json")?.let { fileToListT<Bookmark>(path, "bookmark.json")?.let {

View File

@ -32,9 +32,13 @@ object BookCover {
private set private set
lateinit var defaultDrawable: Drawable lateinit var defaultDrawable: Drawable
private set private set
var coverRuleConfig: CoverRuleConfig = val coverRuleConfig: CoverRuleConfig
GSON.fromJsonObject<CoverRuleConfig>(CacheManager.get(coverRuleConfigKey)).getOrNull() get() {
?: DefaultData.coverRuleConfig return GSON.fromJsonObject<CoverRuleConfig>(CacheManager.get(coverRuleConfigKey))
.getOrNull()
?: DefaultData.coverRuleConfig
}
init { init {
upDefaultCover() upDefaultCover()
@ -132,14 +136,12 @@ object BookCover {
} }
fun saveCoverRuleConfig(config: CoverRuleConfig) { fun saveCoverRuleConfig(config: CoverRuleConfig) {
coverRuleConfig = config
val json = GSON.toJson(config) val json = GSON.toJson(config)
CacheManager.put(coverRuleConfigKey, json) CacheManager.put(coverRuleConfigKey, json)
} }
fun delCoverRuleConfig() { fun delCoverRuleConfig() {
CacheManager.delete(coverRuleConfigKey) CacheManager.delete(coverRuleConfigKey)
coverRuleConfig = DefaultData.coverRuleConfig
} }
data class CoverRuleConfig( data class CoverRuleConfig(

View File

@ -10,6 +10,7 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.exception.ConcurrentException import io.legado.app.exception.ConcurrentException
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.isLocal
import io.legado.app.model.webBook.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.service.CacheBookService import io.legado.app.service.CacheBookService
import io.legado.app.utils.postEvent import io.legado.app.utils.postEvent
@ -59,7 +60,7 @@ object CacheBook {
} }
fun start(context: Context, book: Book, start: Int, end: Int) { fun start(context: Context, book: Book, start: Int, end: Int) {
if (!book.isLocalBook()) { if (!book.isLocal) {
context.startService<CacheBookService> { context.startService<CacheBookService> {
action = IntentAction.start action = IntentAction.start
putExtra("bookUrl", book.bookUrl) putExtra("bookUrl", book.bookUrl)

View File

@ -240,7 +240,7 @@ object Debug {
.onSuccess { .onSuccess {
log(debugSource, "︽详情页解析完成") log(debugSource, "︽详情页解析完成")
log(debugSource, showTime = false) log(debugSource, showTime = false)
if (book.type != BookType.file) { if (book.type and BookType.webFile == 0) {
tocDebug(scope, bookSource, book) tocDebug(scope, bookSource, book)
} else { } else {
log(debugSource, "≡文件类书源跳过解析目录", state = 1000) log(debugSource, "≡文件类书源跳过解析目录", state = 1000)

View File

@ -1,7 +1,6 @@
package io.legado.app.model package io.legado.app.model
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.constant.BookType
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.* import io.legado.app.data.entities.*
import io.legado.app.help.AppWebDav import io.legado.app.help.AppWebDav
@ -10,6 +9,7 @@ import io.legado.app.help.ContentProcessor
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.isLocal
import io.legado.app.model.webBook.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.service.BaseReadAloudService import io.legado.app.service.BaseReadAloudService
import io.legado.app.ui.book.read.page.entities.TextChapter import io.legado.app.ui.book.read.page.entities.TextChapter
@ -50,7 +50,7 @@ object ReadBook : CoroutineScope by MainScope() {
chapterSize = appDb.bookChapterDao.getChapterCount(book.bookUrl) chapterSize = appDb.bookChapterDao.getChapterCount(book.bookUrl)
durChapterIndex = book.durChapterIndex durChapterIndex = book.durChapterIndex
durChapterPos = book.durChapterPos durChapterPos = book.durChapterPos
isLocalBook = book.origin == BookType.local isLocalBook = book.isLocal
clearTextChapter() clearTextChapter()
callBack?.upMenuView() callBack?.upMenuView()
callBack?.upPageAnim() callBack?.upPageAnim()
@ -73,7 +73,7 @@ object ReadBook : CoroutineScope by MainScope() {
} }
fun upWebBook(book: Book) { fun upWebBook(book: Book) {
if (book.origin == BookType.local) { if (book.isLocal) {
bookSource = null bookSource = null
} else { } else {
appDb.bookSourceDao.getBookSource(book.origin)?.let { appDb.bookSourceDao.getBookSource(book.origin)?.let {
@ -290,7 +290,7 @@ object ReadBook : CoroutineScope by MainScope() {
return return
} }
book?.let { book -> book?.let { book ->
if (book.isLocalBook()) return if (book.isLocal) return
if (addLoading(index)) { if (addLoading(index)) {
Coroutine.async { Coroutine.async {
appDb.bookChapterDao.getChapter(book.bookUrl, index)?.let { chapter -> appDb.bookChapterDao.getChapter(book.bookUrl, index)?.let { chapter ->
@ -318,7 +318,7 @@ object ReadBook : CoroutineScope by MainScope() {
if (book != null && bookSource != null) { if (book != null && bookSource != null) {
CacheBook.getOrCreate(bookSource, book).download(scope, chapter) CacheBook.getOrCreate(bookSource, book).download(scope, chapter)
} else if (book != null) { } else if (book != null) {
val msg = if (book.isLocalBook()) "无内容" else "没有书源" val msg = if (book.isLocal) "无内容" else "没有书源"
contentLoadFinish( contentLoadFinish(
book, chapter, "加载正文失败\n$msg", resetPageOffset = resetPageOffset book, chapter, "加载正文失败\n$msg", resetPageOffset = resetPageOffset
) { ) {

View File

@ -14,17 +14,19 @@ import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
import io.legado.app.exception.TocEmptyException import io.legado.app.exception.TocEmptyException
import io.legado.app.help.AppWebDav
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.AppWebDav import io.legado.app.help.isEpub
import io.legado.app.help.isUmd
import io.legado.app.lib.webdav.WebDav import io.legado.app.lib.webdav.WebDav
import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.coroutines.runBlocking
import org.jsoup.nodes.Entities import org.jsoup.nodes.Entities
import splitties.init.appCtx import splitties.init.appCtx
import java.io.* import java.io.*
import java.util.regex.Pattern import java.util.regex.Pattern
import kotlinx.coroutines.runBlocking
/** /**
* 书籍文件导入 目录正文解析 * 书籍文件导入 目录正文解析
@ -65,10 +67,10 @@ object LocalBook {
@Throws(Exception::class) @Throws(Exception::class)
fun getChapterList(book: Book): ArrayList<BookChapter> { fun getChapterList(book: Book): ArrayList<BookChapter> {
val chapters = when { val chapters = when {
book.isEpub() -> { book.isEpub -> {
EpubFile.getChapterList(book) EpubFile.getChapterList(book)
} }
book.isUmd() -> { book.isUmd -> {
UmdFile.getChapterList(book) UmdFile.getChapterList(book)
} }
else -> { else -> {
@ -89,10 +91,10 @@ object LocalBook {
fun getContent(book: Book, chapter: BookChapter): String? { fun getContent(book: Book, chapter: BookChapter): String? {
val content = try { val content = try {
when { when {
book.isEpub() -> { book.isEpub -> {
EpubFile.getContent(book, chapter) EpubFile.getContent(book, chapter)
} }
book.isUmd() -> { book.isUmd -> {
UmdFile.getContent(book, chapter) UmdFile.getContent(book, chapter)
} }
else -> { else -> {
@ -146,6 +148,7 @@ object LocalBook {
if (book == null) { if (book == null) {
val nameAuthor = analyzeNameAuthor(fileName) val nameAuthor = analyzeNameAuthor(fileName)
book = Book( book = Book(
type = BookType.text and BookType.local,
bookUrl = bookUrl, bookUrl = bookUrl,
name = nameAuthor.first, name = nameAuthor.first,
author = nameAuthor.second, author = nameAuthor.second,
@ -158,8 +161,8 @@ object LocalBook {
latestChapterTime = updateTime, latestChapterTime = updateTime,
order = appDb.bookDao.minOrder - 1 order = appDb.bookDao.minOrder - 1
) )
if (book.isEpub()) EpubFile.upBookInfo(book) if (book.isEpub) EpubFile.upBookInfo(book)
if (book.isUmd()) UmdFile.upBookInfo(book) if (book.isUmd) UmdFile.upBookInfo(book)
appDb.bookDao.insert(book) appDb.bookDao.insert(book)
} else { } else {
//已有书籍说明是更新,删除原有目录 //已有书籍说明是更新,删除原有目录
@ -304,22 +307,20 @@ object LocalBook {
//下载book.remoteUrl对应的远程文件并更新bookUrl 返回inputStream //下载book.remoteUrl对应的远程文件并更新bookUrl 返回inputStream
private fun downloadRemoteBook(localBook: Book): InputStream? { private fun downloadRemoteBook(localBook: Book): InputStream? {
if (localBook.origin == BookType.local) return null
//webDav::${http} //webDav::${http}
val webDavUrl = localBook.origin.split("::").getOrNull(1) if (!localBook.origin.startsWith(BookType.webDavTag)) return null
webDavUrl ?: return null val webDavUrl = localBook.origin.substring(8)
if (webDavUrl.isBlank()) return null
try { try {
val uri = AppWebDav.authorization?.let { val uri = AppWebDav.authorization?.let {
val webdav = WebDav(webDavUrl, it) val webdav = WebDav(webDavUrl, it)
runBlocking { runBlocking {
webdav.downloadInputStream().let { inputStream -> saveBookFile(webdav.downloadInputStream(), localBook.originName)
saveBookFile(inputStream, localBook.originName)
}
} }
} }
return uri?.let { return uri?.let {
localBook.bookUrl = if (it.isContentScheme()) it.toString() localBook.bookUrl = if (it.isContentScheme()) it.toString()
else it.path!! else it.path!!
localBook.save() localBook.save()
it.inputStream(appCtx) it.inputStream(appCtx)
} }

View File

@ -137,7 +137,7 @@ object BookInfo {
Debug.log(bookSource.bookSourceUrl, "${e.localizedMessage}") Debug.log(bookSource.bookSourceUrl, "${e.localizedMessage}")
DebugLog.e("获取封面出错", e) DebugLog.e("获取封面出错", e)
} }
if (book.type != BookType.file) { if (book.type and BookType.webFile == 0) {
coroutineContext.ensureActive() coroutineContext.ensureActive()
Debug.log(bookSource.bookSourceUrl, "┌获取目录链接") Debug.log(bookSource.bookSourceUrl, "┌获取目录链接")
book.tocUrl = analyzeRule.getString(infoRule.tocUrl, isUrl = true) book.tocUrl = analyzeRule.getString(infoRule.tocUrl, isUrl = true)

View File

@ -7,6 +7,7 @@ import io.legado.app.data.entities.SearchBook
import io.legado.app.data.entities.rule.BookListRule import io.legado.app.data.entities.rule.BookListRule
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.getBookType
import io.legado.app.model.Debug import io.legado.app.model.Debug
import io.legado.app.model.analyzeRule.AnalyzeRule import io.legado.app.model.analyzeRule.AnalyzeRule
import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.model.analyzeRule.AnalyzeUrl
@ -138,7 +139,7 @@ object BookList {
book.origin = bookSource.bookSourceUrl book.origin = bookSource.bookSourceUrl
book.originName = bookSource.bookSourceName book.originName = bookSource.bookSourceName
book.originOrder = bookSource.customOrder book.originOrder = bookSource.customOrder
book.type = bookSource.bookSourceType book.type = bookSource.getBookType()
analyzeRule.ruleData = book analyzeRule.ruleData = book
BookInfo.analyzeBookInfo( BookInfo.analyzeBookInfo(
book, book,
@ -173,9 +174,9 @@ object BookList {
ruleLastChapter: List<AnalyzeRule.SourceRule> ruleLastChapter: List<AnalyzeRule.SourceRule>
): SearchBook? { ): SearchBook? {
val searchBook = SearchBook(variable = variable) val searchBook = SearchBook(variable = variable)
searchBook.type = bookSource.getBookType()
searchBook.origin = bookSource.bookSourceUrl searchBook.origin = bookSource.bookSourceUrl
searchBook.originName = bookSource.bookSourceName searchBook.originName = bookSource.bookSourceName
searchBook.type = bookSource.bookSourceType
searchBook.originOrder = bookSource.customOrder searchBook.originOrder = bookSource.customOrder
analyzeRule.ruleData = searchBook analyzeRule.ruleData = searchBook
analyzeRule.setContent(item) analyzeRule.setContent(item)

View File

@ -6,6 +6,7 @@ import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.getBookType
import io.legado.app.help.http.StrResponse import io.legado.app.help.http.StrResponse
import io.legado.app.model.Debug import io.legado.app.model.Debug
import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.model.analyzeRule.AnalyzeUrl
@ -134,7 +135,7 @@ object WebBook {
book: Book, book: Book,
canReName: Boolean = true, canReName: Boolean = true,
): Book { ): Book {
book.type = bookSource.bookSourceType book.type = bookSource.getBookType()
if (!book.infoHtml.isNullOrEmpty()) { if (!book.infoHtml.isNullOrEmpty()) {
BookInfo.analyzeBookInfo( BookInfo.analyzeBookInfo(
bookSource = bookSource, bookSource = bookSource,
@ -189,7 +190,7 @@ object WebBook {
bookSource: BookSource, bookSource: BookSource,
book: Book, book: Book,
): Result<List<BookChapter>> { ): Result<List<BookChapter>> {
book.type = bookSource.bookSourceType book.type = bookSource.getBookType()
return kotlin.runCatching { return kotlin.runCatching {
if (book.bookUrl == book.tocUrl && !book.tocHtml.isNullOrEmpty()) { if (book.bookUrl == book.tocUrl && !book.tocHtml.isNullOrEmpty()) {
BookChapterList.analyzeChapterList( BookChapterList.analyzeChapterList(

View File

@ -6,7 +6,7 @@ import com.script.ScriptException
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseService import io.legado.app.base.BaseService
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.BookType import io.legado.app.constant.BookSourceType
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction import io.legado.app.constant.IntentAction
import io.legado.app.data.appDb import io.legado.app.data.appDb
@ -214,7 +214,7 @@ class CheckSourceService : BaseService() {
} }
//校验目录 //校验目录
if (CheckSource.checkCategory && if (CheckSource.checkCategory &&
source.bookSourceType != BookType.file source.bookSourceType != BookSourceType.file
) { ) {
val toc = WebBook.getChapterListAwait(source, mBook).getOrThrow() val toc = WebBook.getChapterListAwait(source, mBook).getOrThrow()
val nextChapterUrl = toc.getOrNull(1)?.url ?: toc.first().url val nextChapterUrl = toc.getOrNull(1)?.url ?: toc.first().url

View File

@ -12,7 +12,6 @@ import androidx.activity.viewModels
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.BookType
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.Status import io.legado.app.constant.Status
import io.legado.app.constant.Theme import io.legado.app.constant.Theme
@ -22,6 +21,7 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.databinding.ActivityAudioPlayBinding import io.legado.app.databinding.ActivityAudioPlayBinding
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.isAudio
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.model.AudioPlay import io.legado.app.model.AudioPlay
import io.legado.app.model.BookCover import io.legado.app.model.BookCover
@ -207,7 +207,7 @@ class AudioPlayActivity :
get() = AudioPlay.book get() = AudioPlay.book
override fun changeTo(source: BookSource, book: Book, toc: List<BookChapter>) { override fun changeTo(source: BookSource, book: Book, toc: List<BookChapter>) {
if (book.type == BookType.audio) { if (book.isAudio) {
viewModel.changeTo(source, book, toc) viewModel.changeTo(source, book, toc)
} else { } else {
AudioPlay.stop(this) AudioPlay.stop(this)

View File

@ -10,7 +10,6 @@ import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.AppConst.charsets import io.legado.app.constant.AppConst.charsets
import io.legado.app.constant.BookType
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb import io.legado.app.data.appDb
@ -21,6 +20,7 @@ import io.legado.app.databinding.ActivityCacheBookBinding
import io.legado.app.databinding.DialogEditTextBinding import io.legado.app.databinding.DialogEditTextBinding
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.isAudio
import io.legado.app.lib.dialogs.SelectItem import io.legado.app.lib.dialogs.SelectItem
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.selector import io.legado.app.lib.dialogs.selector
@ -166,7 +166,7 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
else -> appDb.bookDao.flowByGroup(groupId) else -> appDb.bookDao.flowByGroup(groupId)
}.conflate().map { books -> }.conflate().map { books ->
val booksDownload = books.filter { val booksDownload = books.filter {
it.type == BookType.default || it.type == BookType.image !it.isAudio
} }
when (getPrefInt(PreferKey.bookshelfSort)) { when (getPrefInt(PreferKey.bookshelfSort)) {
1 -> booksDownload.sortedByDescending { it.latestChapterTime } 1 -> booksDownload.sortedByDescending { it.latestChapterTime }

View File

@ -10,6 +10,7 @@ import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.databinding.ItemDownloadBinding import io.legado.app.databinding.ItemDownloadBinding
import io.legado.app.help.isLocal
import io.legado.app.model.CacheBook import io.legado.app.model.CacheBook
import io.legado.app.utils.gone import io.legado.app.utils.gone
import io.legado.app.utils.visible import io.legado.app.utils.visible
@ -33,7 +34,7 @@ class CacheAdapter(context: Context, private val callBack: CallBack) :
if (payloads.isEmpty()) { if (payloads.isEmpty()) {
tvName.text = item.name tvName.text = item.name
tvAuthor.text = context.getString(R.string.author_show, item.getRealAuthor()) tvAuthor.text = context.getString(R.string.author_show, item.getRealAuthor())
if (item.isLocalBook()) { if (item.isLocal) {
tvDownload.setText(R.string.local_book) tvDownload.setText(R.string.local_book)
} else { } else {
val cs = cacheChapters[item.bookUrl] val cs = cacheChapters[item.bookUrl]
@ -49,7 +50,7 @@ class CacheAdapter(context: Context, private val callBack: CallBack) :
} }
} }
} else { } else {
if (item.isLocalBook()) { if (item.isLocal) {
tvDownload.setText(R.string.local_book) tvDownload.setText(R.string.local_book)
} else { } else {
val cacheSize = cacheChapters[item.bookUrl]?.size ?: 0 val cacheSize = cacheChapters[item.bookUrl]?.size ?: 0
@ -84,7 +85,7 @@ class CacheAdapter(context: Context, private val callBack: CallBack) :
} }
private fun upDownloadIv(iv: ImageView, book: Book) { private fun upDownloadIv(iv: ImageView, book: Book) {
if (book.isLocalBook()) { if (book.isLocal) {
iv.gone() iv.gone()
} else { } else {
iv.visible() iv.visible()

View File

@ -2,7 +2,6 @@ package io.legado.app.ui.book.info
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@ -12,7 +11,6 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels import androidx.activity.viewModels
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.BookType
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.Theme import io.legado.app.constant.Theme
import io.legado.app.data.appDb import io.legado.app.data.appDb
@ -22,6 +20,9 @@ import io.legado.app.data.entities.BookSource
import io.legado.app.databinding.ActivityBookInfoBinding import io.legado.app.databinding.ActivityBookInfoBinding
import io.legado.app.databinding.DialogEditTextBinding import io.legado.app.databinding.DialogEditTextBinding
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.isAudio
import io.legado.app.help.isLocal
import io.legado.app.help.isLocalTxt
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.backgroundColor import io.legado.app.lib.theme.backgroundColor
import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.bottomBackground
@ -132,9 +133,9 @@ class BookInfoActivity :
menu.findItem(R.id.menu_can_update)?.isVisible = menu.findItem(R.id.menu_can_update)?.isVisible =
viewModel.bookSource != null viewModel.bookSource != null
menu.findItem(R.id.menu_split_long_chapter)?.isVisible = menu.findItem(R.id.menu_split_long_chapter)?.isVisible =
viewModel.bookData.value?.isLocalTxt() ?: false viewModel.bookData.value?.isLocalTxt ?: false
menu.findItem(R.id.menu_upload)?.isVisible = menu.findItem(R.id.menu_upload)?.isVisible =
viewModel.bookData.value?.isLocalBook() ?: false viewModel.bookData.value?.isLocal ?: false
return super.onMenuOpened(featureId, menu) return super.onMenuOpened(featureId, menu)
} }
@ -161,7 +162,7 @@ class BookInfoActivity :
R.id.menu_refresh -> { R.id.menu_refresh -> {
upLoading(true) upLoading(true)
viewModel.bookData.value?.let { viewModel.bookData.value?.let {
if (it.isLocalBook()) { if (it.isLocal) {
it.tocUrl = "" it.tocUrl = ""
} }
viewModel.loadBookInfo(it, false) viewModel.loadBookInfo(it, false)
@ -437,7 +438,7 @@ class BookInfoActivity :
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
private fun deleteBook() { private fun deleteBook() {
viewModel.bookData.value?.let { viewModel.bookData.value?.let {
if (it.isLocalBook()) { if (it.isLocal) {
alert( alert(
titleResource = R.string.sure, titleResource = R.string.sure,
messageResource = R.string.sure_del messageResource = R.string.sure_del
@ -490,8 +491,8 @@ class BookInfoActivity :
} }
private fun startReadActivity(book: Book) { private fun startReadActivity(book: Book) {
when (book.type) { when {
BookType.audio -> readBookResult.launch( book.isAudio -> readBookResult.launch(
Intent(this, AudioPlayActivity::class.java) Intent(this, AudioPlayActivity::class.java)
.putExtra("bookUrl", book.bookUrl) .putExtra("bookUrl", book.bookUrl)
.putExtra("inBookshelf", viewModel.inBookshelf) .putExtra("inBookshelf", viewModel.inBookshelf)

View File

@ -16,6 +16,7 @@ import io.legado.app.data.entities.BookSource
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.isLocal
import io.legado.app.model.BookCover import io.legado.app.model.BookCover
import io.legado.app.model.ReadBook import io.legado.app.model.ReadBook
import io.legado.app.model.analyzeRule.AnalyzeRule import io.legado.app.model.analyzeRule.AnalyzeRule
@ -33,7 +34,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
var bookSource: BookSource? = null var bookSource: BookSource? = null
private var changeSourceCoroutine: Coroutine<*>? = null private var changeSourceCoroutine: Coroutine<*>? = null
val isImportBookOnLine: Boolean val isImportBookOnLine: Boolean
get() = (bookSource?.bookSourceType ?: BookType.local) == BookType.file get() = (bookSource?.bookSourceType ?: BookType.local) == BookType.webFile
fun initData(intent: Intent) { fun initData(intent: Intent) {
execute { execute {
@ -75,7 +76,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
execute { execute {
bookData.postValue(book) bookData.postValue(book)
upCoverByRule(book) upCoverByRule(book)
bookSource = if (book.isLocalBook()) null else bookSource = if (book.isLocal) null else
appDb.bookSourceDao.getBookSource(book.origin) appDb.bookSourceDao.getBookSource(book.origin)
if (book.tocUrl.isEmpty()) { if (book.tocUrl.isEmpty()) {
loadBookInfo(book) loadBookInfo(book)
@ -112,7 +113,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
scope: CoroutineScope = viewModelScope scope: CoroutineScope = viewModelScope
) { ) {
execute(scope) { execute(scope) {
if (book.isLocalBook()) { if (book.isLocal) {
loadChapter(book, scope) loadChapter(book, scope)
} else { } else {
bookSource?.let { bookSource -> bookSource?.let { bookSource ->
@ -143,7 +144,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
scope: CoroutineScope = viewModelScope scope: CoroutineScope = viewModelScope
) { ) {
execute(scope) { execute(scope) {
if (book.isLocalBook()) { if (book.isLocal) {
LocalBook.getChapterList(book).let { LocalBook.getChapterList(book).let {
appDb.bookDao.update(book) appDb.bookDao.update(book)
appDb.bookChapterDao.delByBook(book.bookUrl) appDb.bookChapterDao.delByBook(book.bookUrl)
@ -277,7 +278,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
bookData.value?.let { bookData.value?.let {
it.delete() it.delete()
inBookshelf = false inBookshelf = false
if (it.isLocalBook()) { if (it.isLocal) {
LocalBook.deleteBook(it, deleteOriginal) LocalBook.deleteBook(it, deleteOriginal)
} }
} }

View File

@ -11,6 +11,9 @@ import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.BookType import io.legado.app.constant.BookType
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.databinding.ActivityBookInfoEditBinding import io.legado.app.databinding.ActivityBookInfoEditBinding
import io.legado.app.help.isAudio
import io.legado.app.help.isImage
import io.legado.app.help.isLocal
import io.legado.app.ui.book.changecover.ChangeCoverDialog import io.legado.app.ui.book.changecover.ChangeCoverDialog
import io.legado.app.utils.* import io.legado.app.utils.*
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
@ -72,9 +75,9 @@ class BookInfoEditActivity :
tieBookName.setText(book.name) tieBookName.setText(book.name)
tieBookAuthor.setText(book.author) tieBookAuthor.setText(book.author)
spType.setSelection( spType.setSelection(
when (book.type) { when {
BookType.image -> 2 book.isImage -> 2
BookType.audio -> 1 book.isAudio -> 1
else -> 0 else -> 0
} }
) )
@ -93,10 +96,11 @@ class BookInfoEditActivity :
viewModel.book?.let { book -> viewModel.book?.let { book ->
book.name = tieBookName.text?.toString() ?: "" book.name = tieBookName.text?.toString() ?: ""
book.author = tieBookAuthor.text?.toString() ?: "" book.author = tieBookAuthor.text?.toString() ?: ""
val local = if (book.isLocal) BookType.local else 0
book.type = when (spType.selectedItemPosition) { book.type = when (spType.selectedItemPosition) {
2 -> BookType.image 2 -> BookType.image or local
1 -> BookType.audio 1 -> BookType.audio or local
else -> BookType.default else -> BookType.text or local
} }
val customCoverUrl = tieCoverUrl.text?.toString() val customCoverUrl = tieCoverUrl.text?.toString()
book.customCoverUrl = if (customCoverUrl == book.coverUrl) null else customCoverUrl book.customCoverUrl = if (customCoverUrl == book.coverUrl) null else customCoverUrl

View File

@ -12,6 +12,7 @@ import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
import io.legado.app.databinding.ItemArrangeBookBinding import io.legado.app.databinding.ItemArrangeBookBinding
import io.legado.app.help.isLocal
import io.legado.app.lib.theme.backgroundColor import io.legado.app.lib.theme.backgroundColor
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
import io.legado.app.ui.widget.recycler.ItemTouchCallback import io.legado.app.ui.widget.recycler.ItemTouchCallback
@ -53,7 +54,7 @@ class BookAdapter(context: Context, val callBack: CallBack) :
tvAuthor.visibility = if (item.author.isEmpty()) View.GONE else View.VISIBLE tvAuthor.visibility = if (item.author.isEmpty()) View.GONE else View.VISIBLE
tvGroupS.text = getGroupName(item.group) tvGroupS.text = getGroupName(item.group)
checkbox.isChecked = selectedBooks.contains(item) checkbox.isChecked = selectedBooks.contains(item)
if (item.isLocalBook()) { if (item.isLocal) {
tvOrigin.setText(R.string.local_book) tvOrigin.setText(R.string.local_book)
} else { } else {
tvOrigin.text = item.originName tvOrigin.text = item.originName

View File

@ -7,6 +7,7 @@ import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.isLocal
import io.legado.app.model.webBook.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.utils.toastOnUi import io.legado.app.utils.toastOnUi
@ -45,7 +46,7 @@ class BookshelfManageViewModel(application: Application) : BaseViewModel(applica
batchChangeSourceSize.value = books.size batchChangeSourceSize.value = books.size
books.forEachIndexed { index, book -> books.forEachIndexed { index, book ->
batchChangeSourcePosition.value = index + 1 batchChangeSourcePosition.value = index + 1
if (book.isLocalBook()) return@forEachIndexed if (book.isLocal) return@forEachIndexed
if (book.origin == source.bookSourceUrl) return@forEachIndexed if (book.origin == source.bookSourceUrl) return@forEachIndexed
WebBook.preciseSearchAwait(this, source, book.name, book.author) WebBook.preciseSearchAwait(this, source, book.name, book.author)
.onFailure { .onFailure {

View File

@ -14,6 +14,7 @@ import io.legado.app.databinding.DialogContentEditBinding
import io.legado.app.databinding.DialogEditTextBinding import io.legado.app.databinding.DialogEditTextBinding
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.ContentProcessor import io.legado.app.help.ContentProcessor
import io.legado.app.help.isLocal
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.model.ReadBook import io.legado.app.model.ReadBook
@ -125,7 +126,7 @@ class ContentEditDialog : BaseDialogFragment(R.layout.dialog_content_edit) {
if (reset) { if (reset) {
content = null content = null
BookHelp.delContent(book, chapter) BookHelp.delContent(book, chapter)
if (!book.isLocalBook()) ReadBook.bookSource?.let { bookSource -> if (!book.isLocal) ReadBook.bookSource?.let { bookSource ->
WebBook.getContentAwait(bookSource, book, chapter) WebBook.getContentAwait(bookSource, book, chapter)
} }
} }

View File

@ -22,10 +22,7 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookProgress import io.legado.app.data.entities.BookProgress
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.AppWebDav import io.legado.app.help.*
import io.legado.app.help.BookHelp
import io.legado.app.help.IntentData
import io.legado.app.help.TTS
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ReadTipConfig import io.legado.app.help.config.ReadTipConfig
@ -248,13 +245,13 @@ class ReadBookActivity : BaseReadBookActivity(),
val menu = menu val menu = menu
val book = ReadBook.book val book = ReadBook.book
if (menu != null && book != null) { if (menu != null && book != null) {
val onLine = !book.isLocalBook() val onLine = !book.isLocal
for (i in 0 until menu.size) { for (i in 0 until menu.size) {
val item = menu[i] val item = menu[i]
when (item.groupId) { when (item.groupId) {
R.id.menu_group_on_line -> item.isVisible = onLine R.id.menu_group_on_line -> item.isVisible = onLine
R.id.menu_group_local -> item.isVisible = !onLine R.id.menu_group_local -> item.isVisible = !onLine
R.id.menu_group_text -> item.isVisible = book.isLocalTxt() R.id.menu_group_text -> item.isVisible = book.isLocalTxt
else -> when (item.itemId) { else -> when (item.itemId) {
R.id.menu_enable_replace -> item.isChecked = book.getUseReplaceRule() R.id.menu_enable_replace -> item.isChecked = book.getUseReplaceRule()
R.id.menu_re_segment -> item.isChecked = book.getReSegment() R.id.menu_re_segment -> item.isChecked = book.getReSegment()
@ -346,7 +343,7 @@ class ReadBookActivity : BaseReadBookActivity(),
} }
R.id.menu_edit_content -> showDialogFragment(ContentEditDialog()) R.id.menu_edit_content -> showDialogFragment(ContentEditDialog())
R.id.menu_update_toc -> ReadBook.book?.let { R.id.menu_update_toc -> ReadBook.book?.let {
if (it.isEpub()) { if (it.isEpub) {
BookHelp.clearCache(it) BookHelp.clearCache(it)
} }
loadChapterList(it) loadChapterList(it)
@ -790,7 +787,7 @@ class ReadBookActivity : BaseReadBookActivity(),
get() = ReadBook.book get() = ReadBook.book
override fun changeTo(source: BookSource, book: Book, toc: List<BookChapter>) { override fun changeTo(source: BookSource, book: Book, toc: List<BookChapter>) {
if (book.type != BookType.audio) { if (!book.isAudio) {
viewModel.changeTo(book, toc) viewModel.changeTo(book, toc)
} else { } else {
ReadAloud.stop(this) ReadAloud.stop(this)
@ -986,7 +983,7 @@ class ReadBookActivity : BaseReadBookActivity(),
override fun payAction() { override fun payAction() {
ReadBook.book?.let { book -> ReadBook.book?.let { book ->
if (book.isLocalBook()) return if (book.isLocal) return
val chapter = appDb.bookChapterDao.getChapter(book.bookUrl, ReadBook.durChapterIndex) val chapter = appDb.bookChapterDao.getChapter(book.bookUrl, ReadBook.durChapterIndex)
if (chapter == null) { if (chapter == null) {
toastOnUi("no chapter") toastOnUi("no chapter")

View File

@ -20,6 +20,7 @@ import io.legado.app.help.BookHelp
import io.legado.app.help.ContentProcessor import io.legado.app.help.ContentProcessor
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.isLocal
import io.legado.app.model.ReadAloud import io.legado.app.model.ReadAloud
import io.legado.app.model.ReadBook import io.legado.app.model.ReadBook
import io.legado.app.model.analyzeRule.AnalyzeRule import io.legado.app.model.analyzeRule.AnalyzeRule
@ -78,7 +79,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
} else { } else {
loadChapterList(book) loadChapterList(book)
} }
} else if (book.isLocalBook() } else if (book.isLocal
&& LocalBook.getLastModified(book).getOrDefault(0L) > book.latestChapterTime && LocalBook.getLastModified(book).getOrDefault(0L) > book.latestChapterTime
) { ) {
loadChapterList(book) loadChapterList(book)
@ -97,7 +98,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
if (!isSameBook || !BaseReadAloudService.isRun) { if (!isSameBook || !BaseReadAloudService.isRun) {
syncBookProgress(book) syncBookProgress(book)
} }
if (!book.isLocalBook() && ReadBook.bookSource == null) { if (!book.isLocal && ReadBook.bookSource == null) {
autoChangeSource(book.name, book.author) autoChangeSource(book.name, book.author)
return return
} }
@ -107,7 +108,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
* 加载详情页 * 加载详情页
*/ */
private fun loadBookInfo(book: Book) { private fun loadBookInfo(book: Book) {
if (book.isLocalBook()) { if (book.isLocal) {
loadChapterList(book) loadChapterList(book)
} else { } else {
ReadBook.bookSource?.let { source -> ReadBook.bookSource?.let { source ->
@ -125,7 +126,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
* 加载目录 * 加载目录
*/ */
fun loadChapterList(book: Book) { fun loadChapterList(book: Book) {
if (book.isLocalBook()) { if (book.isLocal) {
execute { execute {
LocalBook.getChapterList(book).let { LocalBook.getChapterList(book).let {
book.latestChapterTime = System.currentTimeMillis() book.latestChapterTime = System.currentTimeMillis()

View File

@ -13,6 +13,7 @@ import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.isEpub
import io.legado.app.model.ReadBook import io.legado.app.model.ReadBook
import io.legado.app.model.localBook.EpubFile import io.legado.app.model.localBook.EpubFile
import io.legado.app.utils.BitmapUtils import io.legado.app.utils.BitmapUtils
@ -71,7 +72,7 @@ object ImageProvider {
return withContext(IO) { return withContext(IO) {
val vFile = BookHelp.getImage(book, src) val vFile = BookHelp.getImage(book, src)
if (!vFile.exists()) { if (!vFile.exists()) {
if (book.isEpub()) { if (book.isEpub) {
EpubFile.getImage(book, src)?.use { input -> EpubFile.getImage(book, src)?.use { input ->
val newFile = FileUtils.createFileIfNotExist(vFile.absolutePath) val newFile = FileUtils.createFileIfNotExist(vFile.absolutePath)
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")

View File

@ -97,7 +97,7 @@ class RemoteBookViewModel(application: Application) : BaseViewModel(application)
val downloadBookPath = RemoteBookWebDav.getRemoteBook(remoteBook) val downloadBookPath = RemoteBookWebDav.getRemoteBook(remoteBook)
downloadBookPath?.let { downloadBookPath?.let {
val localBook = LocalBook.importFile(it) val localBook = LocalBook.importFile(it)
localBook.origin = BookType.webDav + remoteBook.path localBook.origin = BookType.webDavTag + remoteBook.path
localBook.save() localBook.save()
remoteBook.isOnBookShelf = true remoteBook.isOnBookShelf = true
} }

View File

@ -100,7 +100,7 @@ object RemoteBookWebDav : RemoteBookManager() {
WebDav(putUrl, it).upload(localBookUri.path!!) WebDav(putUrl, it).upload(localBookUri.path!!)
} }
} }
book.origin = BookType.webDav + putUrl book.origin = BookType.webDavTag + putUrl
book.save() book.save()
return true return true
} }

View File

@ -15,6 +15,7 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.databinding.ActivitySearchContentBinding import io.legado.app.databinding.ActivitySearchContentBinding
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.IntentData import io.legado.app.help.IntentData
import io.legado.app.help.isLocal
import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.getPrimaryTextColor import io.legado.app.lib.theme.getPrimaryTextColor
import io.legado.app.lib.theme.primaryTextColor import io.legado.app.lib.theme.primaryTextColor
@ -193,7 +194,7 @@ class SearchContentActivity :
} }
val isLocalBook: Boolean val isLocalBook: Boolean
get() = viewModel.book?.isLocalBook() == true get() = viewModel.book?.isLocal == true
override fun openSearchResult(searchResult: SearchResult, index: Int) { override fun openSearchResult(searchResult: SearchResult, index: Int) {
postEvent(EventBus.SEARCH_RESULT, viewModel.searchResultList as List<SearchResult>) postEvent(EventBus.SEARCH_RESULT, viewModel.searchResultList as List<SearchResult>)

View File

@ -11,7 +11,7 @@ import com.google.android.material.tabs.TabLayout
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.BookType import io.legado.app.constant.BookSourceType
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.rule.* import io.legado.app.data.entities.rule.*
@ -204,9 +204,9 @@ class BookSourceEditActivity :
binding.cbIsEnableReview.isChecked = it.enabledReview ?: false binding.cbIsEnableReview.isChecked = it.enabledReview ?: false
binding.spType.setSelection( binding.spType.setSelection(
when (it.bookSourceType) { when (it.bookSourceType) {
BookType.file -> 3 BookSourceType.file -> 3
BookType.image -> 2 BookSourceType.image -> 2
BookType.audio -> 1 BookSourceType.audio -> 1
else -> 0 else -> 0
} }
) )
@ -327,10 +327,10 @@ class BookSourceEditActivity :
source.enabledCookieJar = binding.cbIsEnableCookie.isChecked source.enabledCookieJar = binding.cbIsEnableCookie.isChecked
source.enabledReview = binding.cbIsEnableReview.isChecked source.enabledReview = binding.cbIsEnableReview.isChecked
source.bookSourceType = when (binding.spType.selectedItemPosition) { source.bookSourceType = when (binding.spType.selectedItemPosition) {
3 -> BookType.file 3 -> BookSourceType.file
2 -> BookType.image 2 -> BookSourceType.image
1 -> BookType.audio 1 -> BookSourceType.audio
else -> BookType.default else -> BookSourceType.default
} }
val searchRule = SearchRule() val searchRule = SearchRule()
val exploreRule = ExploreRule() val exploreRule = ExploreRule()

View File

@ -14,6 +14,7 @@ import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.databinding.FragmentChapterListBinding import io.legado.app.databinding.FragmentChapterListBinding
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.isLocal
import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.getPrimaryTextColor import io.legado.app.lib.theme.getPrimaryTextColor
import io.legado.app.ui.widget.recycler.UpLinearLayoutManager import io.legado.app.ui.widget.recycler.UpLinearLayoutManager
@ -152,7 +153,7 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
get() = viewModel.bookData.value get() = viewModel.bookData.value
override val isLocalBook: Boolean override val isLocalBook: Boolean
get() = viewModel.bookData.value?.isLocalBook() == true get() = viewModel.bookData.value?.isLocal == true
override fun durChapterIndex(): Int { override fun durChapterIndex(): Int {
return durChapterIndex return durChapterIndex

View File

@ -5,7 +5,6 @@ import androidx.lifecycle.viewModelScope
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.constant.BookType
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
@ -15,6 +14,7 @@ import io.legado.app.help.BookHelp
import io.legado.app.help.DefaultData import io.legado.app.help.DefaultData
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.LocalConfig import io.legado.app.help.config.LocalConfig
import io.legado.app.help.isLocal
import io.legado.app.model.CacheBook import io.legado.app.model.CacheBook
import io.legado.app.model.analyzeRule.AnalyzeRule import io.legado.app.model.analyzeRule.AnalyzeRule
import io.legado.app.model.webBook.WebBook import io.legado.app.model.webBook.WebBook
@ -59,7 +59,7 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
fun upToc(books: List<Book>) { fun upToc(books: List<Book>) {
execute(context = upTocPool) { execute(context = upTocPool) {
books.filter { books.filter {
it.origin != BookType.local && it.canUpdate !it.isLocal && it.canUpdate
}.let { }.let {
addToWaitUp(it) addToWaitUp(it)
} }
@ -233,6 +233,7 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
if (LocalConfig.needUpRssSources) { if (LocalConfig.needUpRssSources) {
DefaultData.importDefaultRssSources() DefaultData.importDefaultRssSources()
} }
} }
} }
} }

View File

@ -4,10 +4,10 @@ import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.view.ViewGroup import android.view.ViewGroup
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.constant.BookType
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.databinding.ItemBookshelfGridBinding import io.legado.app.databinding.ItemBookshelfGridBinding
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.isLocal
import io.legado.app.utils.invisible import io.legado.app.utils.invisible
import splitties.views.onLongClick import splitties.views.onLongClick
@ -41,7 +41,7 @@ class BooksAdapterGrid(context: Context, private val callBack: CallBack) :
} }
private fun upRefresh(binding: ItemBookshelfGridBinding, item: Book) { private fun upRefresh(binding: ItemBookshelfGridBinding, item: Book) {
if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) { if (!item.isLocal && callBack.isUpdate(item.bookUrl)) {
binding.bvUnread.invisible() binding.bvUnread.invisible()
binding.rlLoading.show() binding.rlLoading.show()
} else { } else {

View File

@ -4,10 +4,10 @@ import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.view.ViewGroup import android.view.ViewGroup
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.constant.BookType
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.databinding.ItemBookshelfListBinding import io.legado.app.databinding.ItemBookshelfListBinding
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.isLocal
import io.legado.app.utils.invisible import io.legado.app.utils.invisible
import splitties.views.onLongClick import splitties.views.onLongClick
@ -47,7 +47,7 @@ class BooksAdapterList(context: Context, private val callBack: CallBack) :
} }
private fun upRefresh(binding: ItemBookshelfListBinding, item: Book) { private fun upRefresh(binding: ItemBookshelfListBinding, item: Book) {
if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) { if (!item.isLocal && callBack.isUpdate(item.bookUrl)) {
binding.bvUnread.invisible() binding.bvUnread.invisible()
binding.rlLoading.show() binding.rlLoading.show()
} else { } else {

View File

@ -10,11 +10,15 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseFragment import io.legado.app.base.BaseFragment
import io.legado.app.constant.* import io.legado.app.constant.AppConst
import io.legado.app.constant.AppLog
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.databinding.FragmentBooksBinding import io.legado.app.databinding.FragmentBooksBinding
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.isAudio
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.book.audio.AudioPlayActivity import io.legado.app.ui.book.audio.AudioPlayActivity
@ -187,8 +191,8 @@ class BooksFragment() : BaseFragment(R.layout.fragment_books),
} }
override fun open(book: Book) { override fun open(book: Book) {
when (book.type) { when {
BookType.audio -> book.isAudio ->
startActivity<AudioPlayActivity> { startActivity<AudioPlayActivity> {
putExtra("bookUrl", book.bookUrl) putExtra("bookUrl", book.bookUrl)
} }

View File

@ -5,12 +5,12 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.legado.app.constant.BookType
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
import io.legado.app.databinding.ItemBookshelfGridBinding import io.legado.app.databinding.ItemBookshelfGridBinding
import io.legado.app.databinding.ItemBookshelfGridGroupBinding import io.legado.app.databinding.ItemBookshelfGridGroupBinding
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.isLocal
import io.legado.app.utils.invisible import io.legado.app.utils.invisible
import splitties.views.onLongClick import splitties.views.onLongClick
@ -111,7 +111,7 @@ class BooksAdapterGrid(context: Context, callBack: CallBack) :
} }
private fun upRefresh(binding: ItemBookshelfGridBinding, item: Book) { private fun upRefresh(binding: ItemBookshelfGridBinding, item: Book) {
if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) { if (!item.isLocal && callBack.isUpdate(item.bookUrl)) {
binding.bvUnread.invisible() binding.bvUnread.invisible()
binding.rlLoading.show() binding.rlLoading.show()
} else { } else {

View File

@ -5,12 +5,12 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.legado.app.constant.BookType
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
import io.legado.app.databinding.ItemBookshelfListBinding import io.legado.app.databinding.ItemBookshelfListBinding
import io.legado.app.databinding.ItemBookshelfListGroupBinding import io.legado.app.databinding.ItemBookshelfListGroupBinding
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.isLocal
import io.legado.app.utils.gone import io.legado.app.utils.gone
import io.legado.app.utils.invisible import io.legado.app.utils.invisible
import io.legado.app.utils.visible import io.legado.app.utils.visible
@ -126,7 +126,7 @@ class BooksAdapterList(context: Context, callBack: CallBack) :
} }
private fun upRefresh(binding: ItemBookshelfListBinding, item: Book) { private fun upRefresh(binding: ItemBookshelfListBinding, item: Book) {
if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) { if (!item.isLocal && callBack.isUpdate(item.bookUrl)) {
binding.bvUnread.invisible() binding.bvUnread.invisible()
binding.rlLoading.show() binding.rlLoading.show()
} else { } else {

View File

@ -9,12 +9,16 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.* import io.legado.app.constant.AppConst
import io.legado.app.constant.AppLog
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
import io.legado.app.databinding.FragmentBookshelf1Binding import io.legado.app.databinding.FragmentBookshelf1Binding
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.isAudio
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.book.audio.AudioPlayActivity import io.legado.app.ui.book.audio.AudioPlayActivity
@ -181,8 +185,8 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1),
override fun onItemClick(position: Int) { override fun onItemClick(position: Int) {
when (val item = getItem(position)) { when (val item = getItem(position)) {
is Book -> when (item.type) { is Book -> when {
BookType.audio -> item.isAudio ->
startActivity<AudioPlayActivity> { startActivity<AudioPlayActivity> {
putExtra("bookUrl", item.bookUrl) putExtra("bookUrl", item.bookUrl)
} }