|
|
|
@ -6,24 +6,31 @@ import androidx.lifecycle.MutableLiveData |
|
|
|
|
import io.legado.app.App |
|
|
|
|
import io.legado.app.base.BaseViewModel |
|
|
|
|
import io.legado.app.constant.AppPattern |
|
|
|
|
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.Coroutine |
|
|
|
|
import io.legado.app.help.coroutine.CompositeCoroutine |
|
|
|
|
import io.legado.app.model.webBook.WebBook |
|
|
|
|
import kotlinx.coroutines.Dispatchers |
|
|
|
|
import kotlinx.coroutines.ExecutorCoroutineDispatcher |
|
|
|
|
import kotlinx.coroutines.asCoroutineDispatcher |
|
|
|
|
import java.util.concurrent.Executors |
|
|
|
|
import kotlin.math.min |
|
|
|
|
|
|
|
|
|
class ChangeCoverViewModel(application: Application) : BaseViewModel(application) { |
|
|
|
|
private var searchPool = |
|
|
|
|
Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() |
|
|
|
|
private val threadCount = AppConfig.threadCount |
|
|
|
|
private var searchPool: ExecutorCoroutineDispatcher? = null |
|
|
|
|
var name: String = "" |
|
|
|
|
var author: String = "" |
|
|
|
|
private var task: Coroutine<*>? = null |
|
|
|
|
private var tasks = CompositeCoroutine() |
|
|
|
|
private var bookSourceList = arrayListOf<BookSource>() |
|
|
|
|
val searchStateData = MutableLiveData<Boolean>() |
|
|
|
|
val searchBooksLiveData = MutableLiveData<List<SearchBook>>() |
|
|
|
|
private val searchBooks = ArrayList<SearchBook>() |
|
|
|
|
|
|
|
|
|
@Volatile |
|
|
|
|
private var searchIndex = -1 |
|
|
|
|
|
|
|
|
|
fun initData(arguments: Bundle?) { |
|
|
|
|
arguments?.let { bundle -> |
|
|
|
|
bundle.getString("name")?.let { |
|
|
|
@ -35,13 +42,18 @@ class ChangeCoverViewModel(application: Application) : BaseViewModel(application |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private fun initSearchPool() { |
|
|
|
|
searchPool = Executors.newFixedThreadPool(threadCount).asCoroutineDispatcher() |
|
|
|
|
searchIndex = -1 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun loadDbSearchBook() { |
|
|
|
|
execute { |
|
|
|
|
App.db.searchBookDao().getEnableHasCover(name, author).let { |
|
|
|
|
searchBooks.addAll(it) |
|
|
|
|
if (it.size <= 1) { |
|
|
|
|
searchBooksLiveData.postValue(searchBooks) |
|
|
|
|
search() |
|
|
|
|
startSearch() |
|
|
|
|
} else { |
|
|
|
|
searchBooksLiveData.postValue(searchBooks) |
|
|
|
|
} |
|
|
|
@ -49,50 +61,70 @@ class ChangeCoverViewModel(application: Application) : BaseViewModel(application |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun search() { |
|
|
|
|
task = execute { |
|
|
|
|
val bookSourceList = App.db.bookSourceDao().allEnabled |
|
|
|
|
for (item in bookSourceList) { |
|
|
|
|
//task取消时自动取消 by (scope = this@execute) |
|
|
|
|
WebBook(item).searchBook(name, scope = this@execute, context = searchPool) |
|
|
|
|
.timeout(30000L) |
|
|
|
|
.onSuccess(Dispatchers.IO) { |
|
|
|
|
if (it.isNotEmpty()) { |
|
|
|
|
val searchBook = it[0] |
|
|
|
|
if (searchBook.name == name && searchBook.author == author |
|
|
|
|
&& !searchBook.coverUrl.isNullOrEmpty() |
|
|
|
|
) { |
|
|
|
|
App.db.searchBookDao().insert(searchBook) |
|
|
|
|
if (!searchBooks.contains(searchBook)) { |
|
|
|
|
searchBooks.add(searchBook) |
|
|
|
|
searchBooksLiveData.postValue(searchBooks) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}.onStart { |
|
|
|
|
private fun startSearch() { |
|
|
|
|
execute { |
|
|
|
|
bookSourceList.clear() |
|
|
|
|
bookSourceList.addAll(App.db.bookSourceDao().allEnabled) |
|
|
|
|
searchStateData.postValue(true) |
|
|
|
|
}.onCancel { |
|
|
|
|
searchStateData.postValue(false) |
|
|
|
|
initSearchPool() |
|
|
|
|
for (i in 0 until threadCount) { |
|
|
|
|
search() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
task?.invokeOnCompletion { |
|
|
|
|
searchStateData.postValue(false) |
|
|
|
|
private fun search() { |
|
|
|
|
synchronized(this) { |
|
|
|
|
if (searchIndex >= bookSourceList.lastIndex) { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
searchIndex++ |
|
|
|
|
val source = bookSourceList[searchIndex] |
|
|
|
|
val task = WebBook(source).searchBook(name, scope = this, context = searchPool!!) |
|
|
|
|
.timeout(60000L) |
|
|
|
|
.onSuccess(Dispatchers.IO) { |
|
|
|
|
if (it.isNotEmpty()) { |
|
|
|
|
val searchBook = it[0] |
|
|
|
|
if (searchBook.name == name && searchBook.author == author |
|
|
|
|
&& !searchBook.coverUrl.isNullOrEmpty() |
|
|
|
|
) { |
|
|
|
|
App.db.searchBookDao().insert(searchBook) |
|
|
|
|
if (!searchBooks.contains(searchBook)) { |
|
|
|
|
searchBooks.add(searchBook) |
|
|
|
|
searchBooksLiveData.postValue(searchBooks) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
.onFinally { |
|
|
|
|
synchronized(this) { |
|
|
|
|
if (searchIndex < bookSourceList.lastIndex) { |
|
|
|
|
search() |
|
|
|
|
} else { |
|
|
|
|
searchIndex++ |
|
|
|
|
} |
|
|
|
|
if (searchIndex >= bookSourceList.lastIndex + min(bookSourceList.size, |
|
|
|
|
threadCount) |
|
|
|
|
) { |
|
|
|
|
searchStateData.postValue(false) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
tasks.add(task) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun stopSearch() { |
|
|
|
|
if (task?.isActive == true) { |
|
|
|
|
task?.cancel() |
|
|
|
|
if (tasks.isEmpty) { |
|
|
|
|
startSearch() |
|
|
|
|
} else { |
|
|
|
|
search() |
|
|
|
|
tasks.clear() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
override fun onCleared() { |
|
|
|
|
super.onCleared() |
|
|
|
|
searchPool.close() |
|
|
|
|
searchPool?.close() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |