mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
fixed potential crash issues
This commit is contained in:
parent
891d9b717f
commit
a467060486
8 changed files with 166 additions and 90 deletions
|
@ -1,5 +1,6 @@
|
|||
package com.lagradost.cloudstream3.ui.home
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
@ -8,9 +9,16 @@ import com.lagradost.cloudstream3.APIHolder.apis
|
|||
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||
import com.lagradost.cloudstream3.HomePageResponse
|
||||
import com.lagradost.cloudstream3.MainAPI
|
||||
import com.lagradost.cloudstream3.SearchResponse
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.ui.APIRepository
|
||||
import com.lagradost.cloudstream3.ui.WatchType
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class HomeViewModel : ViewModel() {
|
||||
var repo: APIRepository? = null
|
||||
|
@ -25,7 +33,43 @@ class HomeViewModel : ViewModel() {
|
|||
return APIRepository(apis.first { it.hasMainPage })
|
||||
}
|
||||
|
||||
fun load(api : MainAPI?) = viewModelScope.launch {
|
||||
private val availableWatchStatusTypes = MutableLiveData<Pair<WatchType, List<WatchType>>>()
|
||||
private val bookmarks = MutableLiveData<List<SearchResponse>>()
|
||||
|
||||
fun loadStoredData(context: Context, preferredWatchStatus: WatchType?) = viewModelScope.launch {
|
||||
val watchStatusIds = withContext(Dispatchers.IO) {
|
||||
context.getAllWatchStateIds().map { id ->
|
||||
Pair(id, context.getResultWatchState(id))
|
||||
}
|
||||
}
|
||||
val length = WatchType.values().size
|
||||
val currentWatchTypes = HashSet<WatchType>()
|
||||
|
||||
for (watch in watchStatusIds) {
|
||||
currentWatchTypes.add(watch.second)
|
||||
if (currentWatchTypes.size >= length) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (currentWatchTypes.size <= 0) {
|
||||
bookmarks.postValue(ArrayList())
|
||||
return@launch
|
||||
}
|
||||
|
||||
val watchStatus = preferredWatchStatus ?: currentWatchTypes.first()
|
||||
availableWatchStatusTypes.postValue(
|
||||
Pair(
|
||||
watchStatus,
|
||||
currentWatchTypes.sortedBy { it.internalId }.toList()
|
||||
)
|
||||
)
|
||||
val list = withContext(Dispatchers.IO) {
|
||||
watchStatusIds.map { context.getBookmarkedData(it.first) }
|
||||
}
|
||||
}
|
||||
|
||||
fun load(api: MainAPI?) = viewModelScope.launch {
|
||||
repo = if (api?.hasMainPage == true) {
|
||||
APIRepository(api)
|
||||
} else {
|
||||
|
|
|
@ -598,7 +598,7 @@ class PlayerFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private lateinit var volumeObserver: SettingsContentObserver
|
||||
private var volumeObserver: SettingsContentObserver? = null
|
||||
|
||||
companion object {
|
||||
fun newInstance(data: PlayerData, startPos: Long? = null) =
|
||||
|
@ -913,19 +913,18 @@ class PlayerFragment : Fragment() {
|
|||
resizeMode = requireContext().getKey(RESIZE_MODE_KEY, 0)!!
|
||||
playbackSpeed = requireContext().getKey(PLAYBACK_SPEED_KEY, 1f)!!
|
||||
|
||||
volumeObserver = SettingsContentObserver(
|
||||
Handler(
|
||||
Looper.getMainLooper()
|
||||
), requireActivity()
|
||||
)
|
||||
|
||||
activity?.contentResolver
|
||||
?.registerContentObserver(
|
||||
android.provider.Settings.System.CONTENT_URI, true, volumeObserver
|
||||
activity?.let {
|
||||
it.contentResolver?.registerContentObserver(
|
||||
android.provider.Settings.System.CONTENT_URI, true, SettingsContentObserver(
|
||||
Handler(
|
||||
Looper.getMainLooper()
|
||||
), it
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (!isDownloadedFile) {
|
||||
viewModel = ViewModelProvider(requireActivity()).get(ResultViewModel::class.java)
|
||||
viewModel = ViewModelProvider(activity ?: this).get(ResultViewModel::class.java)
|
||||
|
||||
observeDirectly(viewModel.episodes) { _episodes ->
|
||||
episodes = _episodes
|
||||
|
@ -1347,7 +1346,7 @@ class PlayerFragment : Fragment() {
|
|||
outState.putBoolean(STATE_PLAYER_PLAYING, isPlayerPlaying)
|
||||
outState.putInt(RESIZE_MODE_KEY, resizeMode)
|
||||
outState.putFloat(PLAYBACK_SPEED, playbackSpeed)
|
||||
if(!isDownloadedFile) {
|
||||
if (!isDownloadedFile) {
|
||||
outState.putString("data", mapper.writeValueAsString(playerData))
|
||||
}
|
||||
super.onSaveInstanceState(outState)
|
||||
|
|
|
@ -197,7 +197,7 @@ class EpisodeAdapter(
|
|||
downloadButton.setUpButton(
|
||||
downloadInfo?.fileLength, downloadInfo?.totalBytes, episodeDownloadBar, episodeDownloadImage, null,
|
||||
VideoDownloadHelper.DownloadEpisodeCached(
|
||||
card.name, card.poster, card.episode, card.season, card.id, 0, card.rating, card.descript
|
||||
card.name, card.poster, card.episode, card.season, card.id, 0, card.rating, card.descript, System.currentTimeMillis(),
|
||||
)
|
||||
) {
|
||||
if (it.action == DOWNLOAD_ACTION_DOWNLOAD) {
|
||||
|
|
|
@ -65,7 +65,9 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
|||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename
|
||||
import jp.wasabeef.glide.transformations.BlurTransformation
|
||||
import kotlinx.android.synthetic.main.fragment_result.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
@ -168,7 +170,7 @@ class ResultFragment : Fragment() {
|
|||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
viewModel =
|
||||
ViewModelProvider(requireActivity()).get(ResultViewModel::class.java)
|
||||
ViewModelProvider(activity ?: this).get(ResultViewModel::class.java)
|
||||
return inflater.inflate(R.layout.fragment_result, container, false)
|
||||
}
|
||||
|
||||
|
@ -270,15 +272,17 @@ class ResultFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
if (activity?.isCastApiAvailable() == true) {
|
||||
CastButtonFactory.setUpMediaRouteButton(activity, media_route_button)
|
||||
val castContext = CastContext.getSharedInstance(requireActivity().applicationContext)
|
||||
activity?.let {
|
||||
if (it.isCastApiAvailable()) {
|
||||
CastButtonFactory.setUpMediaRouteButton(it, media_route_button)
|
||||
val castContext = CastContext.getSharedInstance(it.applicationContext)
|
||||
|
||||
if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = VISIBLE
|
||||
castContext.addCastStateListener { state ->
|
||||
if (media_route_button != null) {
|
||||
if (state == CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = GONE else {
|
||||
if (media_route_button.visibility == GONE) media_route_button.visibility = VISIBLE
|
||||
if (castContext.castState != CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = VISIBLE
|
||||
castContext.addCastStateListener { state ->
|
||||
if (media_route_button != null) {
|
||||
if (state == CastState.NO_DEVICES_AVAILABLE) media_route_button.visibility = GONE else {
|
||||
if (media_route_button.visibility == GONE) media_route_button.visibility = VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -302,7 +306,7 @@ class ResultFragment : Fragment() {
|
|||
}
|
||||
|
||||
result_back.setOnClickListener {
|
||||
requireActivity().popCurrentPage()
|
||||
activity?.popCurrentPage()
|
||||
}
|
||||
|
||||
fun handleAction(episodeClick: EpisodeClickEvent): Job = main {
|
||||
|
@ -432,7 +436,8 @@ class ResultFragment : Fragment() {
|
|||
currentType ?: return@let,
|
||||
currentHeaderName ?: return@let,
|
||||
currentPoster ?: return@let,
|
||||
currentId ?: return@let
|
||||
currentId ?: return@let,
|
||||
System.currentTimeMillis(),
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -451,7 +456,8 @@ class ResultFragment : Fragment() {
|
|||
epData.id,
|
||||
parentId,
|
||||
epData.rating,
|
||||
epData.descript
|
||||
epData.descript,
|
||||
System.currentTimeMillis(),
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -559,76 +565,78 @@ class ResultFragment : Fragment() {
|
|||
}
|
||||
|
||||
ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> {
|
||||
if (activity?.checkWrite() != true) {
|
||||
activity?.requestRW()
|
||||
if (activity?.checkWrite() == true) return@main
|
||||
}
|
||||
val data = currentLinks ?: return@main
|
||||
val subs = currentSubs
|
||||
|
||||
val outputDir = requireContext().cacheDir
|
||||
val outputFile = File.createTempFile("mirrorlist", ".m3u8", outputDir)
|
||||
var text = "#EXTM3U"
|
||||
if (subs != null) {
|
||||
for (sub in subs) {
|
||||
text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${sub.lang}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.lang}\",URI=\"${sub.url}\""
|
||||
activity?.let { act ->
|
||||
if (!act.checkWrite()) {
|
||||
act.requestRW()
|
||||
if (act.checkWrite()) return@main
|
||||
}
|
||||
val data = currentLinks ?: return@main
|
||||
val subs = currentSubs
|
||||
|
||||
val outputDir = requireContext().cacheDir
|
||||
val outputFile = withContext(Dispatchers.IO) {
|
||||
File.createTempFile("mirrorlist", ".m3u8", outputDir)
|
||||
}
|
||||
var text = "#EXTM3U"
|
||||
if (subs != null) {
|
||||
for (sub in subs) {
|
||||
text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${sub.lang}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.lang}\",URI=\"${sub.url}\""
|
||||
}
|
||||
}
|
||||
for (link in data.sortedBy { -it.quality }) {
|
||||
text += "\n#EXTINF:, ${link.name}\n${link.url}"
|
||||
}
|
||||
outputFile.writeText(text)
|
||||
|
||||
|
||||
val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT)
|
||||
|
||||
vlcIntent.setPackage(VLC_PACKAGE)
|
||||
vlcIntent.addFlags(FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
vlcIntent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION)
|
||||
vlcIntent.addFlags(FLAG_GRANT_READ_URI_PERMISSION)
|
||||
vlcIntent.addFlags(FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
|
||||
vlcIntent.setDataAndType(
|
||||
FileProvider.getUriForFile(
|
||||
act,
|
||||
act.applicationContext.packageName + ".provider",
|
||||
outputFile
|
||||
), "video/*"
|
||||
)
|
||||
|
||||
val startId = VLC_FROM_PROGRESS
|
||||
|
||||
var position = startId
|
||||
if (startId == VLC_FROM_START) {
|
||||
position = 1
|
||||
} else if (startId == VLC_FROM_PROGRESS) {
|
||||
position = 0
|
||||
}
|
||||
|
||||
vlcIntent.putExtra("position", position)
|
||||
|
||||
vlcIntent.component = VLC_COMPONENT
|
||||
requireContext().setKey(VLC_LAST_ID_KEY, episodeClick.data.id)
|
||||
act.startActivityForResult(vlcIntent, VLC_REQUEST_CODE)
|
||||
}
|
||||
for (link in data.sortedBy { -it.quality }) {
|
||||
text += "\n#EXTINF:, ${link.name}\n${link.url}"
|
||||
}
|
||||
outputFile.writeText(text)
|
||||
|
||||
|
||||
val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT)
|
||||
|
||||
vlcIntent.setPackage(VLC_PACKAGE)
|
||||
vlcIntent.addFlags(FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
vlcIntent.addFlags(FLAG_GRANT_PREFIX_URI_PERMISSION)
|
||||
vlcIntent.addFlags(FLAG_GRANT_READ_URI_PERMISSION)
|
||||
vlcIntent.addFlags(FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
|
||||
vlcIntent.setDataAndType(
|
||||
FileProvider.getUriForFile(
|
||||
requireActivity(),
|
||||
requireActivity().applicationContext.packageName + ".provider",
|
||||
outputFile
|
||||
), "video/*"
|
||||
)
|
||||
|
||||
val startId = VLC_FROM_PROGRESS
|
||||
|
||||
var position = startId
|
||||
if (startId == VLC_FROM_START) {
|
||||
position = 1
|
||||
} else if (startId == VLC_FROM_PROGRESS) {
|
||||
position = 0
|
||||
}
|
||||
|
||||
vlcIntent.putExtra("position", position)
|
||||
|
||||
vlcIntent.component = VLC_COMPONENT
|
||||
requireContext().setKey(VLC_LAST_ID_KEY, episodeClick.data.id)
|
||||
activity?.startActivityForResult(vlcIntent, VLC_REQUEST_CODE)
|
||||
}
|
||||
|
||||
ACTION_PLAY_EPISODE_IN_PLAYER -> {
|
||||
if (buildInPlayer) {
|
||||
(requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction()
|
||||
.setCustomAnimations(
|
||||
(activity as AppCompatActivity?)?.supportFragmentManager?.beginTransaction()
|
||||
?.setCustomAnimations(
|
||||
R.anim.enter_anim,
|
||||
R.anim.exit_anim,
|
||||
R.anim.pop_enter,
|
||||
R.anim.pop_exit
|
||||
)
|
||||
.add(
|
||||
)?.add(
|
||||
R.id.homeRoot,
|
||||
PlayerFragment.newInstance(
|
||||
PlayerData(index, null, 0),
|
||||
episodeClick.data.getRealPosition()
|
||||
)
|
||||
)
|
||||
.commit()
|
||||
)?.commit()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -924,7 +932,8 @@ class ResultFragment : Fragment() {
|
|||
localId,
|
||||
localId,
|
||||
d.rating,
|
||||
d.plot
|
||||
d.plot,
|
||||
System.currentTimeMillis(),
|
||||
)
|
||||
) { downloadClickEvent ->
|
||||
if (downloadClickEvent.action == DOWNLOAD_ACTION_DOWNLOAD) {
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.lagradost.cloudstream3.mvvm.safeApiCall
|
|||
import com.lagradost.cloudstream3.ui.APIRepository
|
||||
import com.lagradost.cloudstream3.ui.WatchType
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultSeason
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
||||
|
@ -55,10 +56,14 @@ class ResultViewModel : ViewModel() {
|
|||
context.setResultWatchState(currentId, status.internalId)
|
||||
val resultPage = page.value
|
||||
if (resultPage != null) {
|
||||
val current = context.getBookmarkedData(currentId)
|
||||
val currentTime = System.currentTimeMillis()
|
||||
context.setBookmarkedData(
|
||||
currentId,
|
||||
DataStoreHelper.BookmarkedData(
|
||||
currentId,
|
||||
current?.bookmarkedTime ?: currentTime,
|
||||
currentTime,
|
||||
resultPage.name,
|
||||
resultPage.url,
|
||||
resultPage.apiName,
|
||||
|
|
|
@ -62,6 +62,11 @@ class SearchFragment : Fragment() {
|
|||
fixGrid()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
main_search?.clearFocus()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
|
@ -104,7 +109,8 @@ class SearchFragment : Fragment() {
|
|||
}
|
||||
|
||||
val edit = settingsManagerLocal.edit()
|
||||
edit.putStringSet(getString(R.string.search_providers_list_key),
|
||||
edit.putStringSet(
|
||||
getString(R.string.search_providers_list_key),
|
||||
apiNames.filter { a -> apiNamesSettingLocal.contains(a) }.toSet()
|
||||
)
|
||||
edit.apply()
|
||||
|
@ -142,7 +148,7 @@ class SearchFragment : Fragment() {
|
|||
search_loading_bar.alpha = 0f
|
||||
}
|
||||
is Resource.Failure -> {
|
||||
// Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show()
|
||||
// Toast.makeText(activity, "Server error", Toast.LENGTH_LONG).show()
|
||||
searchExitIcon.alpha = 1f
|
||||
search_loading_bar.alpha = 0f
|
||||
}
|
||||
|
@ -152,15 +158,18 @@ class SearchFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
allApi.providersActive = requireActivity().getApiSettings()
|
||||
activity?.let {
|
||||
allApi.providersActive = it.getApiSettings()
|
||||
}
|
||||
|
||||
main_search.setOnQueryTextFocusChangeListener { searchView, b ->
|
||||
if (b) {
|
||||
// https://stackoverflow.com/questions/12022715/unable-to-show-keyboard-automatically-in-the-searchview
|
||||
searchView?.postDelayed({
|
||||
val imm: InputMethodManager? =
|
||||
requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager?
|
||||
imm?.showSoftInput(view.findFocus(), 0)
|
||||
(activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager?)?.showSoftInput(
|
||||
view.findFocus(),
|
||||
0
|
||||
)
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +178,7 @@ class SearchFragment : Fragment() {
|
|||
//searchViewModel.search("iron man")
|
||||
//(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro")
|
||||
/*
|
||||
(requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction()
|
||||
(activity as AppCompatActivity?)?.supportFragmentManager.beginTransaction()
|
||||
.setCustomAnimations(R.anim.enter_anim,
|
||||
R.anim.exit_anim,
|
||||
R.anim.pop_enter,
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.lagradost.cloudstream3.SearchResponse
|
|||
import com.lagradost.cloudstream3.TvType
|
||||
import com.lagradost.cloudstream3.ui.WatchType
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getKeys
|
||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||
|
||||
const val VIDEO_POS_DUR = "video_pos_dur"
|
||||
|
@ -27,6 +28,8 @@ object DataStoreHelper {
|
|||
|
||||
data class BookmarkedData(
|
||||
val parentId: Int,
|
||||
val bookmarkedTime : Long,
|
||||
val latestUpdatedTime : Long,
|
||||
override val name: String,
|
||||
override val url: String,
|
||||
override val apiName: String,
|
||||
|
@ -37,6 +40,11 @@ object DataStoreHelper {
|
|||
|
||||
var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION
|
||||
|
||||
fun Context.getAllWatchStateIds(): List<Int> {
|
||||
val folder = "$currentAccount/$RESULT_WATCH_STATE"
|
||||
return getKeys(folder).mapNotNull { it.removePrefix(folder).toIntOrNull() }
|
||||
}
|
||||
|
||||
fun Context.setBookmarkedData(id: Int?, data: BookmarkedData) {
|
||||
if (id == null) return
|
||||
setKey("$currentAccount/$RESULT_WATCH_STATE", id.toString(), data)
|
||||
|
|
|
@ -12,14 +12,16 @@ object VideoDownloadHelper {
|
|||
val parentId: Int,
|
||||
val rating: Int?,
|
||||
val descript: String?,
|
||||
val cacheTime: Long,
|
||||
)
|
||||
|
||||
data class DownloadHeaderCached(
|
||||
val apiName: String,
|
||||
val url: String,
|
||||
val type : TvType,
|
||||
val type: TvType,
|
||||
val name: String,
|
||||
val poster: String?,
|
||||
val id: Int,
|
||||
val cacheTime: Long,
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue