This commit is contained in:
LagradOst 2021-09-20 00:36:32 +02:00
parent bc1653e8c1
commit 989d7666be
8 changed files with 178 additions and 33 deletions

View File

@ -351,6 +351,7 @@ data class AnimeEpisode(
val date: String? = null,
val rating: Int? = null,
val descript: String? = null,
val episode : Int? = null,
)
data class TorrentLoadResponse(

View File

@ -141,7 +141,7 @@ class EpisodeAdapter(
localCard = card
val name = if (card.name == null) "${episodeText.context.getString(R.string.episode)} ${card.episode}" else "${card.episode}. ${card.name}"
episodeText.text = name
episodeText.text = if(card.isFiller == true) episodeText.context.getString(R.string.filler_format).format(name) else name
episodeText.isSelected = true // is needed for text repeating
val displayPos = card.getDisplayPosition()

View File

@ -22,9 +22,11 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.core.text.color
import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.gms.cast.framework.CastButtonFactory
@ -35,6 +37,7 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiFromName
import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.MainActivity.Companion.showToast
import com.lagradost.cloudstream3.MainActivity.Companion.updateLocale
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.WatchType
@ -92,6 +95,7 @@ data class ResultEpisode(
val duration: Long, // duration in MS
val rating: Int?,
val descript: String?,
val isFiller: Boolean?,
)
fun ResultEpisode.getRealPosition(): Long {
@ -121,6 +125,7 @@ fun Context.buildResultEpisode(
index: Int,
rating: Int?,
descript: String?,
isFiller: Boolean?,
): ResultEpisode {
val posDur = getViewPos(id)
return ResultEpisode(
@ -136,6 +141,7 @@ fun Context.buildResultEpisode(
posDur?.duration ?: 0,
rating,
descript,
isFiller
)
}
@ -442,9 +448,11 @@ class ResultFragment : Fragment() {
if (isMovie) null else episodeClick.data.episode
)
val folder = when (currentType) {
TvType.Anime -> "Anime/$titleName"
TvType.Movie -> "Movies"
TvType.AnimeMovie -> "Movies"
TvType.TvSeries -> "TVSeries/$titleName"
TvType.ONA -> "ONA"
TvType.Cartoon -> "Cartoons/$titleName"
@ -817,11 +825,22 @@ class ResultFragment : Fragment() {
}
observe(viewModel.publicEpisodes) { episodes ->
if (result_episodes == null || result_episodes.adapter == null) return@observe
currentEpisodes = episodes
(result_episodes?.adapter as EpisodeAdapter?)?.cardList = episodes
(result_episodes?.adapter as EpisodeAdapter?)?.updateLayout()
(result_episodes?.adapter as EpisodeAdapter?)?.notifyDataSetChanged()
when (episodes) {
is Resource.Failure -> {
result_episode_loading.isVisible = false
}
is Resource.Loading -> {
result_episode_loading.isVisible = true
}
is Resource.Success -> {
result_episode_loading.isVisible = false
if (result_episodes == null || result_episodes.adapter == null) return@observe
currentEpisodes = episodes.value
(result_episodes?.adapter as EpisodeAdapter?)?.cardList = episodes.value
(result_episodes?.adapter as EpisodeAdapter?)?.updateLayout()
(result_episodes?.adapter as EpisodeAdapter?)?.notifyDataSetChanged()
}
}
}
observe(viewModel.selectedRange) { range ->
@ -856,6 +875,10 @@ class ResultFragment : Fragment() {
is Resource.Success -> {
val d = data.value
if (d is LoadResponse) {
if (d !is AnimeLoadResponse && result_episode_loading.isVisible) { // no episode loading when not anime
result_episode_loading.isVisible = false
}
updateVisStatus(2)
result_vpn?.text = when (api.vpnStatus) {
@ -1034,7 +1057,8 @@ class ResultFragment : Fragment() {
0L,
0L,
null,
null
null,
null,
)
)
)
@ -1073,25 +1097,28 @@ class ResultFragment : Fragment() {
}
}
val tempUrl = url
if (tempUrl != null) {
result_reload_connectionerror.setOnClickListener {
viewModel.load(it.context, tempUrl, apiName)
}
context?.let { ctx ->
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
val showFillers = settingsManager.getBoolean(ctx.getString(R.string.show_fillers_key), true)
result_reload_connection_open_in_browser.setOnClickListener {
val i = Intent(ACTION_VIEW)
i.data = Uri.parse(tempUrl)
try {
startActivity(i)
} catch (e: Exception) {
e.printStackTrace()
val tempUrl = url
if (tempUrl != null) {
result_reload_connectionerror.setOnClickListener {
viewModel.load(it.context, tempUrl, apiName, showFillers)
}
}
if (viewModel.resultResponse.value == null) {
context?.let { ctx ->
viewModel.load(ctx, tempUrl, apiName)
result_reload_connection_open_in_browser.setOnClickListener {
val i = Intent(ACTION_VIEW)
i.data = Uri.parse(tempUrl)
try {
startActivity(i)
} catch (e: Exception) {
e.printStackTrace()
}
}
if (viewModel.resultResponse.value == null) {
viewModel.load(ctx, tempUrl, apiName, showFillers)
}
}
}

View File

@ -23,6 +23,7 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultSeason
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.FillerEpisodeCheck.getFillerEpisodes
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -39,7 +40,7 @@ class ResultViewModel : ViewModel() {
private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
private val episodeById: MutableLiveData<HashMap<Int, Int>> = MutableLiveData() // lookup by ID to get Index
private val _publicEpisodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
private val _publicEpisodes: MutableLiveData<Resource<List<ResultEpisode>>> = MutableLiveData()
private val _publicEpisodesCount: MutableLiveData<Int> = MutableLiveData() // before the sorting
private val _rangeOptions: MutableLiveData<List<String>> = MutableLiveData()
val selectedRange: MutableLiveData<String> = MutableLiveData()
@ -48,7 +49,7 @@ class ResultViewModel : ViewModel() {
val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse
val episodes: LiveData<List<ResultEpisode>> get() = _episodes
val publicEpisodes: LiveData<List<ResultEpisode>> get() = _publicEpisodes
val publicEpisodes: LiveData<Resource<List<ResultEpisode>>> get() = _publicEpisodes
val publicEpisodesCount: LiveData<Int> get() = _publicEpisodesCount
private val dubStatus: MutableLiveData<DubStatus> = MutableLiveData()
@ -106,7 +107,7 @@ class ResultViewModel : ViewModel() {
val seasons = seasonTypes.toList().map { it.first }.sortedBy { it }
seasonSelections.postValue(seasons)
if (seasons.isEmpty()) { // WHAT THE FUCK DID YOU DO????? HOW DID YOU DO THIS
_publicEpisodes.postValue(ArrayList())
_publicEpisodes.postValue(Resource.Success( ArrayList()))
return
}
@ -156,7 +157,7 @@ class ResultViewModel : ViewModel() {
selectedRange.postValue(allRange)
}
_publicEpisodes.postValue(currentList)
_publicEpisodes.postValue(Resource.Success( currentList))
}
fun changeSeason(context: Context, selection: Int?) {
@ -224,17 +225,18 @@ class ResultViewModel : ViewModel() {
}
}
private fun filterName(name : String?) : String? {
if(name == null) return null
private fun filterName(name: String?): String? {
if (name == null) return null
Regex("[eE]pisode [0-9]*(.*)").find(name)?.groupValues?.get(1)?.let {
if(it.isEmpty())
if (it.isEmpty())
return null
}
return name
}
fun load(context: Context, url: String, apiName: String) = viewModelScope.launch {
fun load(context: Context, url: String, apiName: String, showFillers : Boolean) = viewModelScope.launch {
_resultResponse.postValue(Resource.Loading(url))
_publicEpisodes.postValue(Resource.Loading())
_apiName.postValue(apiName)
val api = getApiFromName(apiName)
@ -273,14 +275,17 @@ class ResultViewModel : ViewModel() {
val dataList = (if (isDub) d.dubEpisodes else d.subEpisodes)
val fillerEpisodes = if(showFillers) safeApiCall { getFillerEpisodes(d.name) } else null
if (dataList != null) { // TODO dub and sub at the same time
val episodes = ArrayList<ResultEpisode>()
for ((index, i) in dataList.withIndex()) {
val episode = i.episode ?: (index + 1);
episodes.add(
context.buildResultEpisode(
filterName(i.name),
i.posterUrl,
index + 1, //TODO MAKE ABLE TO NOT HAVE SOME EPISODE
episode,
null, // TODO FIX SEASON
i.url,
apiName,
@ -288,6 +293,10 @@ class ResultViewModel : ViewModel() {
index,
i.rating,
i.descript,
if (fillerEpisodes is Resource.Success) fillerEpisodes.value?.let {
it.contains(episode) && it[episode] == true
}
?: false else false,
)
)
}
@ -309,7 +318,8 @@ class ResultViewModel : ViewModel() {
(mainId + index + 1).hashCode(),
index,
i.rating,
i.descript
i.descript,
null,
)
)
}
@ -329,6 +339,7 @@ class ResultViewModel : ViewModel() {
0,
null,
null,
null,
)
), -1
)
@ -347,6 +358,7 @@ class ResultViewModel : ViewModel() {
0,
null,
null,
null,
)
), -1
)

View File

@ -0,0 +1,90 @@
package com.lagradost.cloudstream3.utils
import org.jsoup.Jsoup
import java.util.*
import kotlin.collections.HashMap
object FillerEpisodeCheck {
private const val MAIN_URL = "https://www.animefillerlist.com"
var list: HashMap<String, String>? = null
private fun fixName(name: String): String {
return name.toLowerCase(Locale.ROOT)/*.replace(" ", "")*/.replace("-", " ").replace("[^a-zA-Z0-9 ]".toRegex(), "")
}
private fun getFillerList(): Boolean {
if (list != null) return true
try {
val result = khttp.get("$MAIN_URL/shows")
val documented = Jsoup.parse(result.text)
val localHTMLList = documented.select("div#ShowList > div.Group > ul > li > a")
val localList = HashMap<String, String>()
for (i in localHTMLList) {
val name = i.text()
if (name.toLowerCase(Locale.ROOT).contains("manga only")) continue
val href = i.attr("href")
if (name.isNullOrEmpty() || href.isNullOrEmpty()) {
continue
}
val values = "(.*) \\((.*)\\)".toRegex().matchEntire(name)?.groups
if (values != null) {
for (index in 1 until values.size) {
val localName = values[index]?.value ?: continue
localList[fixName(localName)] = href
}
} else {
localList[fixName(name)] = href
}
}
if (localList.size > 0) {
list = localList
return true
}
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
fun getFillerEpisodes(query: String): HashMap<Int, Boolean>? {
try {
if (!getFillerList()) return null
val localList = list ?: return null
// Strips these from the name
val blackList = listOf(
"TV Dubbed",
"(Dub)",
"Subbed",
"(TV)",
"(Uncensored)",
"(Censored)",
"(\\d+)" // year
)
val blackListRegex =
Regex(""" (${blackList.joinToString(separator = "|").replace("(", "\\(").replace(")", "\\)")})""")
val realQuery = fixName(query.replace(blackListRegex, "")).replace("shippuuden", "shippuden")
if (!localList.containsKey(realQuery)) return null
val href = localList[realQuery]?.replace(MAIN_URL, "") ?: return null // JUST IN CASE
val result = khttp.get("$MAIN_URL$href")
val documented = Jsoup.parse(result.text) ?: return null
val hashMap = HashMap<Int, Boolean>()
documented.select("table.EpisodeList > tbody > tr").forEach {
val type = it.selectFirst("td.Type > span").text() == "Filler"
val episodeNumber = it.selectFirst("td.Number").text().toIntOrNull()
if (episodeNumber != null) {
hashMap[episodeNumber] = type
}
}
return hashMap
} catch (e: Exception) {
e.printStackTrace()
return null
}
}
}

View File

@ -435,6 +435,13 @@
/>
</LinearLayout>
<androidx.core.widget.ContentLoadingProgressBar
style="@style/Widget.AppCompat.ProgressBar"
android:id="@+id/result_episode_loading"
android:layout_gravity="center"
android:layout_width="50dp"
android:layout_height="50dp">
</androidx.core.widget.ContentLoadingProgressBar>
<androidx.recyclerview.widget.RecyclerView
android:layout_marginTop="0dp"
android:paddingBottom="100dp"

View File

@ -21,6 +21,7 @@
<string name="double_tap_enabled_key" translatable="false">double_tap_enabled_key</string>
<string name="swipe_vertical_enabled_key" translatable="false">swipe_vertical_enabled_key</string>
<string name="display_sub_key" translatable="false">display_sub_key</string>
<string name="show_fillers_key" translatable="false">show_fillers_key</string>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string>
@ -48,6 +49,7 @@
<string name="player_speed_text_format" formatted="true">Speed (%.2fx)</string>
<string name="rated_format" formatted="true">Rated: %.1f</string>
<string name="new_update_format" formatted="true">New update found!\n%s -> %s</string>
<string name="filler_format" formatted="true">(Filler) %s</string>
<string name="app_name">CloudStream</string>
<string name="title_home">Home</string>
@ -169,6 +171,7 @@
<string name="advanced_search_des">Gives you the search results separated by provider</string>
<string name="bug_report_settings_off">Only sends data on crashes</string>
<string name="bug_report_settings_on">Sends no data</string>
<string name="show_fillers_settings">Show filler episode for anime</string>
<string name="updates_settings">Show app updates</string>
<string name="updates_settings_des">Automatically search for new updates on start</string>
<string name="uprereleases_settings">Update to prereleases</string>

View File

@ -103,6 +103,11 @@
android:title="@string/app_language"
android:icon="@drawable/ic_baseline_language_24">
</Preference>
<SwitchPreference
android:key="@string/show_fillers_key"
android:icon="@drawable/ic_baseline_skip_next_24"
android:title="@string/show_fillers_settings"
android:defaultValue="true"/>
<SwitchPreference
android:key="acra.disable"
android:icon="@drawable/ic_baseline_bug_report_24"