pull/1303/head
parent
c4ddc2b642
commit
da907755c4
@ -1,118 +0,0 @@ |
|||||||
package io.legado.app.model.webBook |
|
||||||
|
|
||||||
import io.legado.app.data.appDb |
|
||||||
import io.legado.app.data.entities.BookSource |
|
||||||
import io.legado.app.data.entities.SearchBook |
|
||||||
import io.legado.app.help.AppConfig |
|
||||||
import io.legado.app.help.coroutine.CompositeCoroutine |
|
||||||
import io.legado.app.utils.getPrefString |
|
||||||
import kotlinx.coroutines.CoroutineScope |
|
||||||
import kotlinx.coroutines.ExecutorCoroutineDispatcher |
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher |
|
||||||
import splitties.init.appCtx |
|
||||||
import java.util.concurrent.Executors |
|
||||||
import kotlin.math.min |
|
||||||
|
|
||||||
class SearchBookModel(private val scope: CoroutineScope, private val callBack: CallBack) { |
|
||||||
val threadCount = AppConfig.threadCount |
|
||||||
private var searchPool: ExecutorCoroutineDispatcher? = null |
|
||||||
private var mSearchId = 0L |
|
||||||
private var searchPage = 1 |
|
||||||
private var searchKey: String = "" |
|
||||||
private var tasks = CompositeCoroutine() |
|
||||||
private var bookSourceList = arrayListOf<BookSource>() |
|
||||||
|
|
||||||
@Volatile |
|
||||||
private var searchIndex = -1 |
|
||||||
|
|
||||||
private fun initSearchPool() { |
|
||||||
searchPool = Executors.newFixedThreadPool(min(threadCount,8)).asCoroutineDispatcher() |
|
||||||
} |
|
||||||
|
|
||||||
fun search(searchId: Long, key: String) { |
|
||||||
callBack.onSearchStart() |
|
||||||
if (searchId != mSearchId) { |
|
||||||
if (key.isEmpty()) { |
|
||||||
callBack.onSearchCancel() |
|
||||||
return |
|
||||||
} else { |
|
||||||
this.searchKey = key |
|
||||||
} |
|
||||||
if (mSearchId != 0L) { |
|
||||||
close() |
|
||||||
} |
|
||||||
initSearchPool() |
|
||||||
mSearchId = searchId |
|
||||||
searchPage = 1 |
|
||||||
val searchGroup = appCtx.getPrefString("searchGroup") ?: "" |
|
||||||
bookSourceList.clear() |
|
||||||
if (searchGroup.isBlank()) { |
|
||||||
bookSourceList.addAll(appDb.bookSourceDao.allEnabled) |
|
||||||
} else { |
|
||||||
bookSourceList.addAll(appDb.bookSourceDao.getEnabledByGroup(searchGroup)) |
|
||||||
} |
|
||||||
} else { |
|
||||||
searchPage++ |
|
||||||
} |
|
||||||
searchIndex = -1 |
|
||||||
for (i in 0 until threadCount) { |
|
||||||
search(searchId) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private fun search(searchId: Long) { |
|
||||||
synchronized(this) { |
|
||||||
if (searchIndex >= bookSourceList.lastIndex) { |
|
||||||
return |
|
||||||
} |
|
||||||
searchIndex++ |
|
||||||
val source = bookSourceList[searchIndex] |
|
||||||
val task = WebBook.searchBook( |
|
||||||
scope, |
|
||||||
source, |
|
||||||
searchKey, |
|
||||||
searchPage, |
|
||||||
context = searchPool!! |
|
||||||
).timeout(30000L) |
|
||||||
.onSuccess(searchPool) { |
|
||||||
if (searchId == mSearchId) { |
|
||||||
callBack.onSearchSuccess(it) |
|
||||||
} |
|
||||||
} |
|
||||||
.onFinally(searchPool) { |
|
||||||
synchronized(this) { |
|
||||||
if (searchIndex < bookSourceList.lastIndex) { |
|
||||||
search(searchId) |
|
||||||
} else { |
|
||||||
searchIndex++ |
|
||||||
} |
|
||||||
if (searchIndex >= bookSourceList.lastIndex |
|
||||||
+ min(bookSourceList.size, threadCount) |
|
||||||
) { |
|
||||||
callBack.onSearchFinish() |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
tasks.add(task) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
fun cancelSearch() { |
|
||||||
close() |
|
||||||
callBack.onSearchCancel() |
|
||||||
} |
|
||||||
|
|
||||||
fun close() { |
|
||||||
tasks.clear() |
|
||||||
searchPool?.close() |
|
||||||
mSearchId = 0L |
|
||||||
} |
|
||||||
|
|
||||||
interface CallBack { |
|
||||||
fun onSearchStart() |
|
||||||
fun onSearchSuccess(searchBooks: ArrayList<SearchBook>) |
|
||||||
fun onSearchFinish() |
|
||||||
fun onSearchCancel() |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,200 @@ |
|||||||
|
package io.legado.app.model.webBook |
||||||
|
|
||||||
|
import io.legado.app.constant.PreferKey |
||||||
|
import io.legado.app.data.appDb |
||||||
|
import io.legado.app.data.entities.BookSource |
||||||
|
import io.legado.app.data.entities.SearchBook |
||||||
|
import io.legado.app.help.AppConfig |
||||||
|
import io.legado.app.help.coroutine.CompositeCoroutine |
||||||
|
import io.legado.app.utils.getPrefBoolean |
||||||
|
import io.legado.app.utils.getPrefString |
||||||
|
import kotlinx.coroutines.CoroutineScope |
||||||
|
import kotlinx.coroutines.ExecutorCoroutineDispatcher |
||||||
|
import kotlinx.coroutines.asCoroutineDispatcher |
||||||
|
import kotlinx.coroutines.isActive |
||||||
|
import splitties.init.appCtx |
||||||
|
import java.util.concurrent.Executors |
||||||
|
import kotlin.math.min |
||||||
|
|
||||||
|
class SearchModel(private val scope: CoroutineScope, private val callBack: CallBack) { |
||||||
|
val threadCount = AppConfig.threadCount |
||||||
|
private var searchPool: ExecutorCoroutineDispatcher? = null |
||||||
|
private var mSearchId = 0L |
||||||
|
private var searchPage = 1 |
||||||
|
private var searchKey: String = "" |
||||||
|
private var tasks = CompositeCoroutine() |
||||||
|
private var bookSourceList = arrayListOf<BookSource>() |
||||||
|
private var searchBooks = arrayListOf<SearchBook>() |
||||||
|
|
||||||
|
@Volatile |
||||||
|
private var searchIndex = -1 |
||||||
|
|
||||||
|
private fun initSearchPool() { |
||||||
|
searchPool?.close() |
||||||
|
searchPool = Executors.newFixedThreadPool(min(threadCount, 8)).asCoroutineDispatcher() |
||||||
|
} |
||||||
|
|
||||||
|
fun search(searchId: Long, key: String) { |
||||||
|
callBack.onSearchStart() |
||||||
|
if (searchId != mSearchId) { |
||||||
|
if (key.isEmpty()) { |
||||||
|
callBack.onSearchCancel() |
||||||
|
return |
||||||
|
} else { |
||||||
|
this.searchKey = key |
||||||
|
} |
||||||
|
if (mSearchId != 0L) { |
||||||
|
close() |
||||||
|
} |
||||||
|
initSearchPool() |
||||||
|
mSearchId = searchId |
||||||
|
searchPage = 1 |
||||||
|
val searchGroup = appCtx.getPrefString("searchGroup") ?: "" |
||||||
|
bookSourceList.clear() |
||||||
|
if (searchGroup.isBlank()) { |
||||||
|
bookSourceList.addAll(appDb.bookSourceDao.allEnabled) |
||||||
|
} else { |
||||||
|
bookSourceList.addAll(appDb.bookSourceDao.getEnabledByGroup(searchGroup)) |
||||||
|
} |
||||||
|
} else { |
||||||
|
searchPage++ |
||||||
|
} |
||||||
|
searchIndex = -1 |
||||||
|
for (i in 0 until threadCount) { |
||||||
|
search(searchId) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Synchronized |
||||||
|
private fun search(searchId: Long) { |
||||||
|
if (searchIndex >= bookSourceList.lastIndex) { |
||||||
|
return |
||||||
|
} |
||||||
|
searchIndex++ |
||||||
|
val source = bookSourceList[searchIndex] |
||||||
|
val task = WebBook.searchBook( |
||||||
|
scope, |
||||||
|
source, |
||||||
|
searchKey, |
||||||
|
searchPage, |
||||||
|
context = searchPool!! |
||||||
|
).timeout(30000L) |
||||||
|
.onSuccess(searchPool) { |
||||||
|
onSuccess(searchId, it) |
||||||
|
} |
||||||
|
.onFinally(searchPool) { |
||||||
|
onFinally(searchId) |
||||||
|
} |
||||||
|
tasks.add(task) |
||||||
|
} |
||||||
|
|
||||||
|
@Synchronized |
||||||
|
private fun onSuccess(searchId: Long, searchBooks: ArrayList<SearchBook>) { |
||||||
|
if (searchId == mSearchId) { |
||||||
|
val precision = appCtx.getPrefBoolean(PreferKey.precisionSearch) |
||||||
|
appDb.searchBookDao.insert(*searchBooks.toTypedArray()) |
||||||
|
mergeItems(scope, searchBooks, precision) |
||||||
|
callBack.onSearchSuccess(searchBooks) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Synchronized |
||||||
|
private fun onFinally(searchId: Long) { |
||||||
|
if (searchIndex < bookSourceList.lastIndex) { |
||||||
|
search(searchId) |
||||||
|
} else { |
||||||
|
searchIndex++ |
||||||
|
} |
||||||
|
if (searchIndex >= bookSourceList.lastIndex |
||||||
|
+ min(bookSourceList.size, threadCount) |
||||||
|
) { |
||||||
|
callBack.onSearchFinish() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun mergeItems(scope: CoroutineScope, newDataS: List<SearchBook>, precision: Boolean) { |
||||||
|
if (newDataS.isNotEmpty()) { |
||||||
|
val copyData = ArrayList(searchBooks) |
||||||
|
val equalData = arrayListOf<SearchBook>() |
||||||
|
val containsData = arrayListOf<SearchBook>() |
||||||
|
val otherData = arrayListOf<SearchBook>() |
||||||
|
copyData.forEach { |
||||||
|
if (!scope.isActive) return |
||||||
|
if (it.name == searchKey || it.author == searchKey) { |
||||||
|
equalData.add(it) |
||||||
|
} else if (it.name.contains(searchKey) || it.author.contains(searchKey)) { |
||||||
|
containsData.add(it) |
||||||
|
} else { |
||||||
|
otherData.add(it) |
||||||
|
} |
||||||
|
} |
||||||
|
newDataS.forEach { nBook -> |
||||||
|
if (!scope.isActive) return |
||||||
|
if (nBook.name == searchKey || nBook.author == searchKey) { |
||||||
|
var hasSame = false |
||||||
|
equalData.forEach { pBook -> |
||||||
|
if (!scope.isActive) return |
||||||
|
if (pBook.name == nBook.name && pBook.author == nBook.author) { |
||||||
|
pBook.addOrigin(nBook.origin) |
||||||
|
hasSame = true |
||||||
|
} |
||||||
|
} |
||||||
|
if (!hasSame) { |
||||||
|
equalData.add(nBook) |
||||||
|
} |
||||||
|
} else if (nBook.name.contains(searchKey) || nBook.author.contains(searchKey)) { |
||||||
|
var hasSame = false |
||||||
|
containsData.forEach { pBook -> |
||||||
|
if (!scope.isActive) return |
||||||
|
if (pBook.name == nBook.name && pBook.author == nBook.author) { |
||||||
|
pBook.addOrigin(nBook.origin) |
||||||
|
hasSame = true |
||||||
|
} |
||||||
|
} |
||||||
|
if (!hasSame) { |
||||||
|
containsData.add(nBook) |
||||||
|
} |
||||||
|
} else if (!precision) { |
||||||
|
var hasSame = false |
||||||
|
otherData.forEach { pBook -> |
||||||
|
if (!scope.isActive) return |
||||||
|
if (pBook.name == nBook.name && pBook.author == nBook.author) { |
||||||
|
pBook.addOrigin(nBook.origin) |
||||||
|
hasSame = true |
||||||
|
} |
||||||
|
} |
||||||
|
if (!hasSame) { |
||||||
|
otherData.add(nBook) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (!scope.isActive) return |
||||||
|
equalData.sortByDescending { it.origins.size } |
||||||
|
equalData.addAll(containsData.sortedByDescending { it.origins.size }) |
||||||
|
if (!precision) { |
||||||
|
equalData.addAll(otherData) |
||||||
|
} |
||||||
|
searchBooks = equalData |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun cancelSearch() { |
||||||
|
close() |
||||||
|
callBack.onSearchCancel() |
||||||
|
} |
||||||
|
|
||||||
|
fun close() { |
||||||
|
tasks.clear() |
||||||
|
searchPool?.close() |
||||||
|
searchPool = null |
||||||
|
mSearchId = 0L |
||||||
|
} |
||||||
|
|
||||||
|
interface CallBack { |
||||||
|
fun onSearchStart() |
||||||
|
fun onSearchSuccess(searchBooks: ArrayList<SearchBook>) |
||||||
|
fun onSearchFinish() |
||||||
|
fun onSearchCancel() |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue