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 date: String? = null,
val rating: Int? = null, val rating: Int? = null,
val descript: String? = null, val descript: String? = null,
val episode : Int? = null,
) )
data class TorrentLoadResponse( data class TorrentLoadResponse(

View File

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

View File

@ -22,9 +22,11 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.core.text.color import androidx.core.text.color
import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView import androidx.core.widget.NestedScrollView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.gms.cast.framework.CastButtonFactory 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.getApiFromName
import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.MainActivity.Companion.showToast 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.Resource
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
@ -92,6 +95,7 @@ data class ResultEpisode(
val duration: Long, // duration in MS val duration: Long, // duration in MS
val rating: Int?, val rating: Int?,
val descript: String?, val descript: String?,
val isFiller: Boolean?,
) )
fun ResultEpisode.getRealPosition(): Long { fun ResultEpisode.getRealPosition(): Long {
@ -121,6 +125,7 @@ fun Context.buildResultEpisode(
index: Int, index: Int,
rating: Int?, rating: Int?,
descript: String?, descript: String?,
isFiller: Boolean?,
): ResultEpisode { ): ResultEpisode {
val posDur = getViewPos(id) val posDur = getViewPos(id)
return ResultEpisode( return ResultEpisode(
@ -136,6 +141,7 @@ fun Context.buildResultEpisode(
posDur?.duration ?: 0, posDur?.duration ?: 0,
rating, rating,
descript, descript,
isFiller
) )
} }
@ -442,9 +448,11 @@ class ResultFragment : Fragment() {
if (isMovie) null else episodeClick.data.episode if (isMovie) null else episodeClick.data.episode
) )
val folder = when (currentType) { val folder = when (currentType) {
TvType.Anime -> "Anime/$titleName" TvType.Anime -> "Anime/$titleName"
TvType.Movie -> "Movies" TvType.Movie -> "Movies"
TvType.AnimeMovie -> "Movies"
TvType.TvSeries -> "TVSeries/$titleName" TvType.TvSeries -> "TVSeries/$titleName"
TvType.ONA -> "ONA" TvType.ONA -> "ONA"
TvType.Cartoon -> "Cartoons/$titleName" TvType.Cartoon -> "Cartoons/$titleName"
@ -817,11 +825,22 @@ class ResultFragment : Fragment() {
} }
observe(viewModel.publicEpisodes) { episodes -> observe(viewModel.publicEpisodes) { episodes ->
if (result_episodes == null || result_episodes.adapter == null) return@observe when (episodes) {
currentEpisodes = episodes is Resource.Failure -> {
(result_episodes?.adapter as EpisodeAdapter?)?.cardList = episodes result_episode_loading.isVisible = false
(result_episodes?.adapter as EpisodeAdapter?)?.updateLayout() }
(result_episodes?.adapter as EpisodeAdapter?)?.notifyDataSetChanged() 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 -> observe(viewModel.selectedRange) { range ->
@ -856,6 +875,10 @@ class ResultFragment : Fragment() {
is Resource.Success -> { is Resource.Success -> {
val d = data.value val d = data.value
if (d is LoadResponse) { 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) updateVisStatus(2)
result_vpn?.text = when (api.vpnStatus) { result_vpn?.text = when (api.vpnStatus) {
@ -1034,7 +1057,8 @@ class ResultFragment : Fragment() {
0L, 0L,
0L, 0L,
null, null,
null null,
null,
) )
) )
) )
@ -1073,25 +1097,28 @@ class ResultFragment : Fragment() {
} }
} }
val tempUrl = url context?.let { ctx ->
if (tempUrl != null) { val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
result_reload_connectionerror.setOnClickListener { val showFillers = settingsManager.getBoolean(ctx.getString(R.string.show_fillers_key), true)
viewModel.load(it.context, tempUrl, apiName)
}
result_reload_connection_open_in_browser.setOnClickListener { val tempUrl = url
val i = Intent(ACTION_VIEW) if (tempUrl != null) {
i.data = Uri.parse(tempUrl) result_reload_connectionerror.setOnClickListener {
try { viewModel.load(it.context, tempUrl, apiName, showFillers)
startActivity(i)
} catch (e: Exception) {
e.printStackTrace()
} }
}
if (viewModel.resultResponse.value == null) { result_reload_connection_open_in_browser.setOnClickListener {
context?.let { ctx -> val i = Intent(ACTION_VIEW)
viewModel.load(ctx, tempUrl, apiName) 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.setResultWatchState
import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.FillerEpisodeCheck.getFillerEpisodes
import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -39,7 +40,7 @@ class ResultViewModel : ViewModel() {
private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData() private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData()
private val episodeById: MutableLiveData<HashMap<Int, Int>> = MutableLiveData() // lookup by ID to get Index 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 _publicEpisodesCount: MutableLiveData<Int> = MutableLiveData() // before the sorting
private val _rangeOptions: MutableLiveData<List<String>> = MutableLiveData() private val _rangeOptions: MutableLiveData<List<String>> = MutableLiveData()
val selectedRange: MutableLiveData<String> = MutableLiveData() val selectedRange: MutableLiveData<String> = MutableLiveData()
@ -48,7 +49,7 @@ class ResultViewModel : ViewModel() {
val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse val resultResponse: LiveData<Resource<Any?>> get() = _resultResponse
val episodes: LiveData<List<ResultEpisode>> get() = _episodes 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 val publicEpisodesCount: LiveData<Int> get() = _publicEpisodesCount
private val dubStatus: MutableLiveData<DubStatus> = MutableLiveData() private val dubStatus: MutableLiveData<DubStatus> = MutableLiveData()
@ -106,7 +107,7 @@ class ResultViewModel : ViewModel() {
val seasons = seasonTypes.toList().map { it.first }.sortedBy { it } val seasons = seasonTypes.toList().map { it.first }.sortedBy { it }
seasonSelections.postValue(seasons) seasonSelections.postValue(seasons)
if (seasons.isEmpty()) { // WHAT THE FUCK DID YOU DO????? HOW DID YOU DO THIS if (seasons.isEmpty()) { // WHAT THE FUCK DID YOU DO????? HOW DID YOU DO THIS
_publicEpisodes.postValue(ArrayList()) _publicEpisodes.postValue(Resource.Success( ArrayList()))
return return
} }
@ -156,7 +157,7 @@ class ResultViewModel : ViewModel() {
selectedRange.postValue(allRange) selectedRange.postValue(allRange)
} }
_publicEpisodes.postValue(currentList) _publicEpisodes.postValue(Resource.Success( currentList))
} }
fun changeSeason(context: Context, selection: Int?) { fun changeSeason(context: Context, selection: Int?) {
@ -224,17 +225,18 @@ class ResultViewModel : ViewModel() {
} }
} }
private fun filterName(name : String?) : String? { private fun filterName(name: String?): String? {
if(name == null) return null if (name == null) return null
Regex("[eE]pisode [0-9]*(.*)").find(name)?.groupValues?.get(1)?.let { Regex("[eE]pisode [0-9]*(.*)").find(name)?.groupValues?.get(1)?.let {
if(it.isEmpty()) if (it.isEmpty())
return null return null
} }
return name 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)) _resultResponse.postValue(Resource.Loading(url))
_publicEpisodes.postValue(Resource.Loading())
_apiName.postValue(apiName) _apiName.postValue(apiName)
val api = getApiFromName(apiName) val api = getApiFromName(apiName)
@ -273,14 +275,17 @@ class ResultViewModel : ViewModel() {
val dataList = (if (isDub) d.dubEpisodes else d.subEpisodes) 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 if (dataList != null) { // TODO dub and sub at the same time
val episodes = ArrayList<ResultEpisode>() val episodes = ArrayList<ResultEpisode>()
for ((index, i) in dataList.withIndex()) { for ((index, i) in dataList.withIndex()) {
val episode = i.episode ?: (index + 1);
episodes.add( episodes.add(
context.buildResultEpisode( context.buildResultEpisode(
filterName(i.name), filterName(i.name),
i.posterUrl, i.posterUrl,
index + 1, //TODO MAKE ABLE TO NOT HAVE SOME EPISODE episode,
null, // TODO FIX SEASON null, // TODO FIX SEASON
i.url, i.url,
apiName, apiName,
@ -288,6 +293,10 @@ class ResultViewModel : ViewModel() {
index, index,
i.rating, i.rating,
i.descript, 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(), (mainId + index + 1).hashCode(),
index, index,
i.rating, i.rating,
i.descript i.descript,
null,
) )
) )
} }
@ -329,6 +339,7 @@ class ResultViewModel : ViewModel() {
0, 0,
null, null,
null, null,
null,
) )
), -1 ), -1
) )
@ -347,6 +358,7 @@ class ResultViewModel : ViewModel() {
0, 0,
null, null,
null, null,
null,
) )
), -1 ), -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> </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 <androidx.recyclerview.widget.RecyclerView
android:layout_marginTop="0dp" android:layout_marginTop="0dp"
android:paddingBottom="100dp" android:paddingBottom="100dp"

View File

@ -21,6 +21,7 @@
<string name="double_tap_enabled_key" translatable="false">double_tap_enabled_key</string> <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="swipe_vertical_enabled_key" translatable="false">swipe_vertical_enabled_key</string>
<string name="display_sub_key" translatable="false">display_sub_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 --> <!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string> <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="player_speed_text_format" formatted="true">Speed (%.2fx)</string>
<string name="rated_format" formatted="true">Rated: %.1f</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="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="app_name">CloudStream</string>
<string name="title_home">Home</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="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_off">Only sends data on crashes</string>
<string name="bug_report_settings_on">Sends no data</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">Show app updates</string>
<string name="updates_settings_des">Automatically search for new updates on start</string> <string name="updates_settings_des">Automatically search for new updates on start</string>
<string name="uprereleases_settings">Update to prereleases</string> <string name="uprereleases_settings">Update to prereleases</string>

View File

@ -103,6 +103,11 @@
android:title="@string/app_language" android:title="@string/app_language"
android:icon="@drawable/ic_baseline_language_24"> android:icon="@drawable/ic_baseline_language_24">
</Preference> </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 <SwitchPreference
android:key="acra.disable" android:key="acra.disable"
android:icon="@drawable/ic_baseline_bug_report_24" android:icon="@drawable/ic_baseline_bug_report_24"