Option to open random manga from source

pull/440/head
Koitharu 3 years ago
parent f105f4b496
commit 2378d104c3
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -7,6 +7,7 @@ import org.koitharu.kotatsu.core.util.ext.asArrayList
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
import org.koitharu.kotatsu.history.data.HistoryRepository import org.koitharu.kotatsu.history.data.HistoryRepository
import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
@ -21,19 +22,39 @@ class ExploreRepository @Inject constructor(
) { ) {
suspend fun findRandomManga(tagsLimit: Int): Manga { suspend fun findRandomManga(tagsLimit: Int): Manga {
val blacklistTagRegex = TagsBlacklist(settings.suggestionsTagsBlacklist, 0.4f) val tagsBlacklist = TagsBlacklist(settings.suggestionsTagsBlacklist, 0.4f)
val tags = historyRepository.getPopularTags(tagsLimit).mapNotNull { val tags = historyRepository.getPopularTags(tagsLimit).mapNotNull {
if (it in blacklistTagRegex) null else it.title if (it in tagsBlacklist) null else it.title
} }
val sources = sourcesRepository.getEnabledSources() val sources = sourcesRepository.getEnabledSources()
check(sources.isNotEmpty()) { "No sources available" } check(sources.isNotEmpty()) { "No sources available" }
for (i in 0..4) { for (i in 0..4) {
val list = getList(sources.random(), tags, blacklistTagRegex) val list = getList(sources.random(), tags, tagsBlacklist)
val manga = list.randomOrNull() ?: continue val manga = list.randomOrNull() ?: continue
val details = runCatchingCancellable { val details = runCatchingCancellable {
mangaRepositoryFactory.create(manga.source).getDetails(manga) mangaRepositoryFactory.create(manga.source).getDetails(manga)
}.getOrNull() ?: continue }.getOrNull() ?: continue
if ((settings.isSuggestionsExcludeNsfw && details.isNsfw) || details in blacklistTagRegex) { if ((settings.isSuggestionsExcludeNsfw && details.isNsfw) || details in tagsBlacklist) {
continue
}
return details
}
throw NoSuchElementException()
}
suspend fun findRandomManga(source: MangaSource, tagsLimit: Int): Manga {
val tagsBlacklist = TagsBlacklist(settings.suggestionsTagsBlacklist, 0.4f)
val skipNsfw = settings.isSuggestionsExcludeNsfw && source.contentType != ContentType.HENTAI
val tags = historyRepository.getPopularTags(tagsLimit).mapNotNull {
if (it in tagsBlacklist) null else it.title
}
for (i in 0..4) {
val list = getList(source, tags, tagsBlacklist)
val manga = list.randomOrNull() ?: continue
val details = runCatchingCancellable {
mangaRepositoryFactory.create(manga.source).getDetails(manga)
}.getOrNull() ?: continue
if ((skipNsfw && details.isNsfw) || details in tagsBlacklist) {
continue continue
} }
return details return details

@ -11,6 +11,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.explore.domain.ExploreRepository
import org.koitharu.kotatsu.filter.ui.FilterCoordinator import org.koitharu.kotatsu.filter.ui.FilterCoordinator
import org.koitharu.kotatsu.list.domain.ListExtraProvider import org.koitharu.kotatsu.list.domain.ListExtraProvider
import org.koitharu.kotatsu.list.ui.model.EmptyState import org.koitharu.kotatsu.list.ui.model.EmptyState
@ -29,6 +30,7 @@ class LocalListViewModel @Inject constructor(
downloadScheduler: DownloadWorker.Scheduler, downloadScheduler: DownloadWorker.Scheduler,
listExtraProvider: ListExtraProvider, listExtraProvider: ListExtraProvider,
private val deleteLocalMangaUseCase: DeleteLocalMangaUseCase, private val deleteLocalMangaUseCase: DeleteLocalMangaUseCase,
exploreRepository: ExploreRepository,
@LocalStorageChanges private val localStorageChanges: SharedFlow<LocalManga?>, @LocalStorageChanges private val localStorageChanges: SharedFlow<LocalManga?>,
) : RemoteListViewModel( ) : RemoteListViewModel(
savedStateHandle, savedStateHandle,
@ -37,6 +39,7 @@ class LocalListViewModel @Inject constructor(
settings, settings,
listExtraProvider, listExtraProvider,
downloadScheduler, downloadScheduler,
exploreRepository,
), SharedPreferences.OnSharedPreferenceChangeListener { ), SharedPreferences.OnSharedPreferenceChangeListener {
val onMangaRemoved = MutableEventFlow<Unit>() val onMangaRemoved = MutableEventFlow<Unit>()

@ -12,9 +12,13 @@ import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.list.ListSelectionController
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
import org.koitharu.kotatsu.core.util.ext.addMenuProvider import org.koitharu.kotatsu.core.util.ext.addMenuProvider
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.core.util.ext.withArgs
import org.koitharu.kotatsu.databinding.FragmentListBinding import org.koitharu.kotatsu.databinding.FragmentListBinding
import org.koitharu.kotatsu.details.ui.DetailsActivity
import org.koitharu.kotatsu.filter.ui.FilterOwner import org.koitharu.kotatsu.filter.ui.FilterOwner
import org.koitharu.kotatsu.filter.ui.FilterSheetFragment import org.koitharu.kotatsu.filter.ui.FilterSheetFragment
import org.koitharu.kotatsu.filter.ui.MangaFilter import org.koitharu.kotatsu.filter.ui.MangaFilter
@ -35,6 +39,10 @@ class RemoteListFragment : MangaListFragment(), FilterOwner {
override fun onViewBindingCreated(binding: FragmentListBinding, savedInstanceState: Bundle?) { override fun onViewBindingCreated(binding: FragmentListBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState) super.onViewBindingCreated(binding, savedInstanceState)
addMenuProvider(RemoteListMenuProvider()) addMenuProvider(RemoteListMenuProvider())
viewModel.isRandomLoading.observe(viewLifecycleOwner, MenuInvalidator(requireActivity()))
viewModel.onOpenManga.observeEvent(viewLifecycleOwner) {
startActivity(DetailsActivity.newIntent(binding.root.context, it))
}
} }
override fun onScrolledToEnd() { override fun onScrolledToEnd() {
@ -75,6 +83,11 @@ class RemoteListFragment : MangaListFragment(), FilterOwner {
true true
} }
R.id.action_random -> {
viewModel.openRandom()
true
}
R.id.action_filter -> { R.id.action_filter -> {
onFilterClick(null) onFilterClick(null)
true true
@ -83,6 +96,11 @@ class RemoteListFragment : MangaListFragment(), FilterOwner {
else -> false else -> false
} }
override fun onPrepareMenu(menu: Menu) {
super.onPrepareMenu(menu)
menu.findItem(R.id.action_random)?.isEnabled = !viewModel.isRandomLoading.value
}
override fun onQueryTextSubmit(query: String?): Boolean { override fun onQueryTextSubmit(query: String?): Boolean {
if (query.isNullOrEmpty()) { if (query.isNullOrEmpty()) {
return false return false

@ -19,10 +19,12 @@ import kotlinx.coroutines.plus
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.core.util.ext.require import org.koitharu.kotatsu.core.util.ext.require
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
import org.koitharu.kotatsu.explore.domain.ExploreRepository
import org.koitharu.kotatsu.filter.ui.FilterCoordinator import org.koitharu.kotatsu.filter.ui.FilterCoordinator
import org.koitharu.kotatsu.filter.ui.MangaFilter import org.koitharu.kotatsu.filter.ui.MangaFilter
import org.koitharu.kotatsu.filter.ui.model.FilterState import org.koitharu.kotatsu.filter.ui.model.FilterState
@ -49,14 +51,19 @@ open class RemoteListViewModel @Inject constructor(
settings: AppSettings, settings: AppSettings,
listExtraProvider: ListExtraProvider, listExtraProvider: ListExtraProvider,
downloadScheduler: DownloadWorker.Scheduler, downloadScheduler: DownloadWorker.Scheduler,
private val exploreRepository: ExploreRepository,
) : MangaListViewModel(settings, downloadScheduler), MangaFilter by filter { ) : MangaListViewModel(settings, downloadScheduler), MangaFilter by filter {
val source = savedStateHandle.require<MangaSource>(RemoteListFragment.ARG_SOURCE) val source = savedStateHandle.require<MangaSource>(RemoteListFragment.ARG_SOURCE)
val isRandomLoading = MutableStateFlow(false)
val onOpenManga = MutableEventFlow<Manga>()
private val repository = mangaRepositoryFactory.create(source) private val repository = mangaRepositoryFactory.create(source)
private val mangaList = MutableStateFlow<List<Manga>?>(null) private val mangaList = MutableStateFlow<List<Manga>?>(null)
private val hasNextPage = MutableStateFlow(false) private val hasNextPage = MutableStateFlow(false)
private val listError = MutableStateFlow<Throwable?>(null) private val listError = MutableStateFlow<Throwable?>(null)
private var loadingJob: Job? = null private var loadingJob: Job? = null
private var randomJob: Job? = null
override val content = combine( override val content = combine(
mangaList, mangaList,
@ -149,4 +156,16 @@ open class RemoteListViewModel @Inject constructor(
textSecondary = 0, textSecondary = 0,
actionStringRes = if (canResetFilter) R.string.reset_filter else 0, actionStringRes = if (canResetFilter) R.string.reset_filter else 0,
) )
fun openRandom() {
if (randomJob?.isActive == true) {
return
}
randomJob = launchLoadingJob(Dispatchers.Default) {
isRandomLoading.value = true
val manga = exploreRepository.findRandomManga(source, 16)
onOpenManga.call(manga)
isRandomLoading.value = false
}
}
} }

@ -10,6 +10,12 @@
app:actionViewClass="androidx.appcompat.widget.SearchView" app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="ifRoom|collapseActionView" /> app:showAsAction="ifRoom|collapseActionView" />
<item
android:id="@+id/action_random"
android:icon="@drawable/ic_dice"
android:title="@string/random"
app:showAsAction="ifRoom" />
<item <item
android:id="@+id/action_filter" android:id="@+id/action_filter"
android:orderInCategory="30" android:orderInCategory="30"
@ -21,4 +27,4 @@
android:orderInCategory="50" android:orderInCategory="50"
android:title="@string/settings" android:title="@string/settings"
app:showAsAction="never" /> app:showAsAction="never" />
</menu> </menu>

Loading…
Cancel
Save