mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
cleanup
This commit is contained in:
parent
0a327ccbda
commit
77294dc68e
10 changed files with 215 additions and 147 deletions
|
@ -1122,23 +1122,25 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
if (isTvSettings()) {
|
||||
val newLocalBinding = ActivityMainTvBinding.inflate(layoutInflater, null, false)
|
||||
setContentView(newLocalBinding.root)
|
||||
TvFocus.focusOutline = WeakReference(newLocalBinding.focusOutline)
|
||||
newLocalBinding.root.viewTreeObserver.addOnGlobalFocusChangeListener { _, newFocus ->
|
||||
// println("refocus $oldFocus -> $newFocus")
|
||||
try {
|
||||
val r = Rect(0, 0, 0, 0)
|
||||
newFocus.getDrawingRect(r)
|
||||
val x = r.centerX()
|
||||
val y = r.centerY()
|
||||
val dx = 0 //screenWidth / 2
|
||||
val dy = screenHeight / 2
|
||||
val r2 = Rect(x - dx, y - dy, x + dx, y + dy)
|
||||
newFocus.requestRectangleOnScreen(r2, false)
|
||||
// TvFocus.current =TvFocus.current.copy(y=y.toFloat())
|
||||
} catch (_: Throwable) {
|
||||
}
|
||||
TvFocus.updateFocusView(newFocus)
|
||||
/*var focus = newFocus
|
||||
|
||||
if(isTrueTvSettings()) {
|
||||
TvFocus.focusOutline = WeakReference(newLocalBinding.focusOutline)
|
||||
newLocalBinding.root.viewTreeObserver.addOnGlobalFocusChangeListener { _, newFocus ->
|
||||
// println("refocus $oldFocus -> $newFocus")
|
||||
try {
|
||||
val r = Rect(0, 0, 0, 0)
|
||||
newFocus.getDrawingRect(r)
|
||||
val x = r.centerX()
|
||||
val y = r.centerY()
|
||||
val dx = 0 //screenWidth / 2
|
||||
val dy = screenHeight / 2
|
||||
val r2 = Rect(x - dx, y - dy, x + dx, y + dy)
|
||||
newFocus.requestRectangleOnScreen(r2, false)
|
||||
// TvFocus.current =TvFocus.current.copy(y=y.toFloat())
|
||||
} catch (_: Throwable) {
|
||||
}
|
||||
TvFocus.updateFocusView(newFocus)
|
||||
/*var focus = newFocus
|
||||
|
||||
while(focus != null) {
|
||||
if(focus is ScrollingView && focus.canScrollVertically()) {
|
||||
|
@ -1149,7 +1151,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
else -> break
|
||||
}
|
||||
}*/
|
||||
}
|
||||
} else {
|
||||
newLocalBinding.focusOutline.isVisible = false
|
||||
}
|
||||
|
||||
newLocalBinding.root.viewTreeObserver.addOnScrollChangedListener {
|
||||
TvFocus.updateFocusView(TvFocus.lastFocus.get(), same = true)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.content.DialogInterface
|
|||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
|
@ -23,18 +22,12 @@ import androidx.preference.PreferenceManager
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.chip.ChipGroup
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.apis
|
||||
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.bookmarksUpdatedEvent
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.mainPluginsLoadedEvent
|
||||
import com.lagradost.cloudstream3.databinding.FragmentHomeBinding
|
||||
import com.lagradost.cloudstream3.databinding.HomeEpisodesExpandedBinding
|
||||
import com.lagradost.cloudstream3.databinding.HomeSelectMainpageBinding
|
||||
|
@ -45,37 +38,26 @@ import com.lagradost.cloudstream3.mvvm.observe
|
|||
import com.lagradost.cloudstream3.mvvm.observeNullable
|
||||
import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
|
||||
import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
|
||||
import com.lagradost.cloudstream3.ui.WatchType
|
||||
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
|
||||
import com.lagradost.cloudstream3.ui.result.txt
|
||||
import com.lagradost.cloudstream3.ui.search.*
|
||||
import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
|
||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.addProgramsToContinueWatching
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.isRecyclerScrollable
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.loadResult
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.ownHide
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.ownShow
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||
import com.lagradost.cloudstream3.utils.Event
|
||||
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
||||
|
||||
import java.util.*
|
||||
|
||||
|
||||
const val HOME_BOOKMARK_VALUE_LIST = "home_bookmarked_last_list"
|
||||
const val HOME_PREF_HOMEPAGE = "home_pref_homepage"
|
||||
|
||||
class HomeFragment : Fragment() {
|
||||
companion object {
|
||||
val configEvent = Event<Int>()
|
||||
|
@ -377,10 +359,7 @@ class HomeFragment : Fragment() {
|
|||
var currentApiName = selectedApiName
|
||||
|
||||
var currentValidApis: MutableList<MainAPI> = mutableListOf()
|
||||
val preSelectedTypes = this.getKey<List<String>>("${DataStoreHelper.currentAccount}/$HOME_PREF_HOMEPAGE")
|
||||
?.mapNotNull { listName -> TvType.values().firstOrNull { it.name == listName } }
|
||||
?.toMutableList()
|
||||
?: mutableListOf(TvType.Movie, TvType.TvSeries)
|
||||
val preSelectedTypes = DataStoreHelper.homePreference.toMutableList()
|
||||
|
||||
binding.cancelBtt.setOnClickListener {
|
||||
dialog.dismissSafe()
|
||||
|
@ -408,7 +387,7 @@ class HomeFragment : Fragment() {
|
|||
}
|
||||
|
||||
fun updateList() {
|
||||
this.setKey("${DataStoreHelper.currentAccount}/$HOME_PREF_HOMEPAGE", preSelectedTypes)
|
||||
DataStoreHelper.homePreference = preSelectedTypes
|
||||
|
||||
arrayAdapter.clear()
|
||||
currentValidApis = validAPIs.filter { api ->
|
||||
|
|
|
@ -12,7 +12,6 @@ import com.lagradost.cloudstream3.APIHolder.filterSearchResultByFilmQuality
|
|||
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.context
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||
import com.lagradost.cloudstream3.CommonActivity.activity
|
||||
import com.lagradost.cloudstream3.HomePageList
|
||||
import com.lagradost.cloudstream3.LoadResponse
|
||||
|
@ -170,10 +169,7 @@ class HomeViewModel : ViewModel() {
|
|||
currentWatchTypes.remove(WatchType.NONE)
|
||||
|
||||
if (currentWatchTypes.size <= 0) {
|
||||
setKey(
|
||||
"${DataStoreHelper.currentAccount}/$HOME_BOOKMARK_VALUE_LIST",
|
||||
intArrayOf()
|
||||
)
|
||||
DataStoreHelper.homeBookmarkedList = intArrayOf()
|
||||
_availableWatchStatusTypes.postValue(setOf<WatchType>() to setOf())
|
||||
_bookmarks.postValue(Pair(false, ArrayList()))
|
||||
return@launchSafe
|
||||
|
@ -181,16 +177,14 @@ class HomeViewModel : ViewModel() {
|
|||
|
||||
val watchPrefNotNull = preferredWatchStatus ?: EnumSet.of(currentWatchTypes.first())
|
||||
//if (currentWatchTypes.any { watchPrefNotNull.contains(it) }) watchPrefNotNull else listOf(currentWatchTypes.first())
|
||||
setKey(
|
||||
"${DataStoreHelper.currentAccount}/$HOME_BOOKMARK_VALUE_LIST",
|
||||
watchPrefNotNull.map { it.internalId }.toIntArray()
|
||||
)
|
||||
|
||||
DataStoreHelper.homeBookmarkedList = watchPrefNotNull.map { it.internalId }.toIntArray()
|
||||
_availableWatchStatusTypes.postValue(
|
||||
Pair(
|
||||
watchPrefNotNull,
|
||||
currentWatchTypes,
|
||||
|
||||
watchPrefNotNull to
|
||||
currentWatchTypes,
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
val list = withContext(Dispatchers.IO) {
|
||||
watchStatusIds.filter { watchPrefNotNull.contains(it.second) }
|
||||
|
@ -463,7 +457,7 @@ class HomeViewModel : ViewModel() {
|
|||
|
||||
fun loadStoredData() {
|
||||
val list = EnumSet.noneOf(WatchType::class.java)
|
||||
getKey<IntArray>("${DataStoreHelper.currentAccount}/$HOME_BOOKMARK_VALUE_LIST")?.map { WatchType.fromInternalId(it) }?.let {
|
||||
DataStoreHelper.homeBookmarkedList.map { WatchType.fromInternalId(it) }.let {
|
||||
list.addAll(it)
|
||||
}
|
||||
loadStoredData(list)
|
||||
|
|
|
@ -33,8 +33,6 @@ import androidx.preference.PreferenceManager
|
|||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||
import com.github.rubensousa.previewseekbar.PreviewBar
|
||||
import com.github.rubensousa.previewseekbar.media3.PreviewTimeBar
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||
import com.lagradost.cloudstream3.CommonActivity.canEnterPipMode
|
||||
import com.lagradost.cloudstream3.CommonActivity.isInPIPMode
|
||||
import com.lagradost.cloudstream3.CommonActivity.keyEventListener
|
||||
|
@ -48,7 +46,7 @@ import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle
|
|||
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment
|
||||
import com.lagradost.cloudstream3.utils.AppUtils
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.requestLocalAudioFocus
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.currentAccount
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||
import com.lagradost.cloudstream3.utils.EpisodeSkip
|
||||
import com.lagradost.cloudstream3.utils.UIHelper
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI
|
||||
|
@ -443,7 +441,7 @@ abstract class AbstractPlayerFragment(
|
|||
|
||||
@SuppressLint("SetTextI18n", "UnsafeOptInUsageError")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
resizeMode = getKey("$currentAccount/$RESIZE_MODE_KEY") ?: 0
|
||||
resizeMode = DataStoreHelper.resizeMode
|
||||
resize(resizeMode, false)
|
||||
|
||||
player.releaseCallbacks()
|
||||
|
@ -466,7 +464,8 @@ abstract class AbstractPlayerFragment(
|
|||
var resume = false
|
||||
progressBar.addOnScrubListener(object : PreviewBar.OnScrubListener {
|
||||
override fun onScrubStart(previewBar: PreviewBar?) {
|
||||
progressBar.isPreviewEnabled = player.hasPreview()
|
||||
val hasPreview = player.hasPreview()
|
||||
progressBar.isPreviewEnabled = hasPreview
|
||||
resume = player.getIsPlaying()
|
||||
if (resume) player.handleEvent(
|
||||
CSPlayerEvent.Pause,
|
||||
|
@ -574,7 +573,7 @@ abstract class AbstractPlayerFragment(
|
|||
|
||||
@SuppressLint("UnsafeOptInUsageError")
|
||||
fun resize(resize: PlayerResize, showToast: Boolean) {
|
||||
setKey("$currentAccount/$RESIZE_MODE_KEY", resize.ordinal)
|
||||
DataStoreHelper.resizeMode = resize.ordinal
|
||||
val type = when (resize) {
|
||||
PlayerResize.Fill -> AspectRatioFrameLayout.RESIZE_MODE_FILL
|
||||
PlayerResize.Fit -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||
|
|
|
@ -49,7 +49,7 @@ import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper
|
|||
import com.lagradost.cloudstream3.ui.result.setText
|
||||
import com.lagradost.cloudstream3.ui.result.txt
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.isUsingMobileData
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.currentAccount
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||
|
@ -357,7 +357,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
|
||||
private fun setPlayBackSpeed(speed: Float) {
|
||||
try {
|
||||
setKey("$currentAccount/$PLAYBACK_SPEED_KEY", speed)
|
||||
DataStoreHelper.playBackSpeed = speed
|
||||
playerBinding?.playerSpeedBtt?.text =
|
||||
getString(R.string.player_speed_text_format).format(speed)
|
||||
.replace(".0x", "x")
|
||||
|
@ -1195,7 +1195,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
// init variables
|
||||
setPlayBackSpeed(getKey("$currentAccount/$PLAYBACK_SPEED_KEY") ?: 1.0f)
|
||||
setPlayBackSpeed(DataStoreHelper.playBackSpeed)
|
||||
savedInstanceState?.getLong(SUBTITLE_DELAY_BUNDLE_KEY)?.let {
|
||||
subtitleDelay = it
|
||||
}
|
||||
|
|
|
@ -199,17 +199,8 @@ data class CurrentTracks(
|
|||
class InvalidFileException(msg: String) : Exception(msg)
|
||||
|
||||
//http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4
|
||||
const val STATE_RESUME_WINDOW = "resumeWindow"
|
||||
const val STATE_RESUME_POSITION = "resumePosition"
|
||||
const val STATE_PLAYER_FULLSCREEN = "playerFullscreen"
|
||||
const val STATE_PLAYER_PLAYING = "playerOnPlay"
|
||||
const val ACTION_MEDIA_CONTROL = "media_control"
|
||||
const val EXTRA_CONTROL_TYPE = "control_type"
|
||||
const val PLAYBACK_SPEED = "playback_speed"
|
||||
const val RESIZE_MODE_KEY = "resize_mode" // Last used resize mode
|
||||
const val PLAYBACK_SPEED_KEY = "playback_speed" // Last used playback speed
|
||||
const val PREFERRED_SUBS_KEY = "preferred_subtitles" // Last used resize mode
|
||||
//const val PLAYBACK_FASTFORWARD = "playback_fastforward" // Last used resize mode
|
||||
|
||||
/** Abstract Exoplayer logic, can be expanded to other players */
|
||||
interface IPlayer {
|
||||
|
|
|
@ -28,86 +28,122 @@ const val MIN_LOD = 3
|
|||
interface IPreviewGenerator {
|
||||
fun hasPreview(): Boolean
|
||||
fun getPreviewImage(fraction: Float): Bitmap?
|
||||
fun clear(keepCache: Boolean = false)
|
||||
fun release()
|
||||
|
||||
var durationMs: Long
|
||||
var loadedImages: Int
|
||||
}
|
||||
|
||||
/** PreviewGenerator that hides the implementation details of the sub generators that is used, used for source switch cache */
|
||||
class PreviewGenerator : IPreviewGenerator {
|
||||
/** the most up to date generator, will always mirror the actual source in the player */
|
||||
private var currentGenerator: IPreviewGenerator = NoPreviewGenerator()
|
||||
/** the longest generated preview of the same episode */
|
||||
private var lastGenerator: IPreviewGenerator = NoPreviewGenerator()
|
||||
/** always NoPreviewGenerator, used as a cache for nothing */
|
||||
private val dummy: IPreviewGenerator = NoPreviewGenerator()
|
||||
|
||||
/** if the current generator is the same as the last by checking time */
|
||||
private fun isSameLength(): Boolean =
|
||||
currentGenerator.durationMs.minus(lastGenerator.durationMs).absoluteValue < 10_000L
|
||||
|
||||
/** use the backup if the current generator is init or if they have the same length */
|
||||
private val backupGenerator: IPreviewGenerator
|
||||
get() {
|
||||
if (currentGenerator.durationMs == 0L || isSameLength()) {
|
||||
return lastGenerator
|
||||
}
|
||||
return dummy
|
||||
}
|
||||
|
||||
override fun hasPreview(): Boolean {
|
||||
return currentGenerator.hasPreview()
|
||||
return currentGenerator.hasPreview() || backupGenerator.hasPreview()
|
||||
}
|
||||
|
||||
override fun getPreviewImage(fraction: Float): Bitmap? {
|
||||
return try {
|
||||
currentGenerator.getPreviewImage(fraction)
|
||||
currentGenerator.getPreviewImage(fraction) ?: backupGenerator.getPreviewImage(fraction)
|
||||
} catch (t: Throwable) {
|
||||
logError(t)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun clear(keepCache: Boolean) {
|
||||
currentGenerator.clear(keepCache)
|
||||
override fun release() {
|
||||
lastGenerator.release()
|
||||
currentGenerator.release()
|
||||
lastGenerator = NoPreviewGenerator()
|
||||
currentGenerator = NoPreviewGenerator()
|
||||
}
|
||||
|
||||
override fun release() {
|
||||
currentGenerator.release()
|
||||
override var durationMs: Long
|
||||
get() = currentGenerator.durationMs
|
||||
set(_) {}
|
||||
override var loadedImages: Int
|
||||
get() = currentGenerator.loadedImages
|
||||
set(_) {}
|
||||
|
||||
fun clear(keepCache: Boolean) {
|
||||
if (keepCache) {
|
||||
if (!isSameLength() || currentGenerator.loadedImages >= lastGenerator.loadedImages || lastGenerator.durationMs == 0L) {
|
||||
// the current generator is better than the last generator, therefore keep the current
|
||||
// or the lengths are not the same, therefore favoring the more recent selection
|
||||
|
||||
// if they are the same we favor the current generator
|
||||
lastGenerator.release()
|
||||
lastGenerator = currentGenerator
|
||||
} else {
|
||||
// otherwise just keep the last generator and throw away the current generator
|
||||
currentGenerator.release()
|
||||
}
|
||||
} else {
|
||||
// we switched the episode, therefore keep nothing
|
||||
lastGenerator.release()
|
||||
lastGenerator = NoPreviewGenerator()
|
||||
currentGenerator.release()
|
||||
// we assume that we set currentGenerator right after this, so currentGenerator != NoPreviewGenerator
|
||||
}
|
||||
}
|
||||
|
||||
fun load(link: ExtractorLink, keepCache: Boolean) {
|
||||
val gen = currentGenerator
|
||||
clear(keepCache)
|
||||
|
||||
when (link.type) {
|
||||
ExtractorLinkType.M3U8 -> {
|
||||
if (gen is M3u8PreviewGenerator) {
|
||||
gen.load(keepCache = keepCache, url = link.url, headers = link.getAllHeaders())
|
||||
} else {
|
||||
currentGenerator.release()
|
||||
currentGenerator = M3u8PreviewGenerator().apply {
|
||||
load(keepCache = keepCache, url = link.url, headers = link.getAllHeaders())
|
||||
}
|
||||
currentGenerator = M3u8PreviewGenerator().apply {
|
||||
load(url = link.url, headers = link.getAllHeaders())
|
||||
}
|
||||
}
|
||||
|
||||
ExtractorLinkType.VIDEO -> {
|
||||
if (gen is Mp4PreviewGenerator) {
|
||||
gen.load(keepCache = keepCache, url = link.url, headers = link.getAllHeaders())
|
||||
} else {
|
||||
currentGenerator.release()
|
||||
currentGenerator = Mp4PreviewGenerator().apply {
|
||||
load(keepCache = keepCache, url = link.url, headers = link.getAllHeaders())
|
||||
}
|
||||
currentGenerator = Mp4PreviewGenerator().apply {
|
||||
load(url = link.url, headers = link.getAllHeaders())
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
Log.i("PreviewImg", "unsupported format for $link")
|
||||
currentGenerator.clear(keepCache)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun load(context: Context, link: ExtractorUri, keepCache: Boolean) {
|
||||
val gen = currentGenerator
|
||||
if (gen is Mp4PreviewGenerator) {
|
||||
gen.load(keepCache = keepCache, context = context, uri = link.uri)
|
||||
} else {
|
||||
currentGenerator.release()
|
||||
currentGenerator = Mp4PreviewGenerator().apply {
|
||||
load(keepCache = keepCache, context = context, uri = link.uri)
|
||||
}
|
||||
clear(keepCache)
|
||||
currentGenerator = Mp4PreviewGenerator().apply {
|
||||
load(keepCache = keepCache, context = context, uri = link.uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NoPreviewGenerator : IPreviewGenerator {
|
||||
private class NoPreviewGenerator : IPreviewGenerator {
|
||||
override fun hasPreview(): Boolean = false
|
||||
override fun getPreviewImage(fraction: Float): Bitmap? = null
|
||||
override fun clear(keepCache: Boolean) = Unit
|
||||
override fun release() = Unit
|
||||
override var durationMs: Long = 0L
|
||||
override var loadedImages: Int = 0
|
||||
}
|
||||
|
||||
class M3u8PreviewGenerator : IPreviewGenerator {
|
||||
private class M3u8PreviewGenerator : IPreviewGenerator {
|
||||
// generated images 1:1 to idx of hsl
|
||||
private var images: Array<Bitmap?> = arrayOf()
|
||||
|
||||
|
@ -118,7 +154,7 @@ class M3u8PreviewGenerator : IPreviewGenerator {
|
|||
private var prefixSum: Array<Double> = arrayOf()
|
||||
|
||||
// how many images has been generated
|
||||
private var loadedImages: Int = 0
|
||||
override var loadedImages: Int = 0
|
||||
|
||||
// how many images we can generate in total, == hsl.size ?: 0
|
||||
private var totalImages: Int = 0
|
||||
|
@ -155,7 +191,7 @@ class M3u8PreviewGenerator : IPreviewGenerator {
|
|||
}*/
|
||||
}
|
||||
|
||||
override fun clear(keepCache: Boolean) {
|
||||
private fun clear() {
|
||||
synchronized(images) {
|
||||
currentJob?.cancel()
|
||||
images = arrayOf()
|
||||
|
@ -170,9 +206,11 @@ class M3u8PreviewGenerator : IPreviewGenerator {
|
|||
images = arrayOf()
|
||||
}
|
||||
|
||||
override var durationMs: Long = 0L
|
||||
|
||||
private var currentJob: Job? = null
|
||||
fun load(keepCache: Boolean, url: String, headers: Map<String, String>) {
|
||||
clear(keepCache)
|
||||
fun load(url: String, headers: Map<String, String>) {
|
||||
clear()
|
||||
currentJob?.cancel()
|
||||
currentJob = ioSafe {
|
||||
withContext(Dispatchers.IO) {
|
||||
|
@ -201,6 +239,7 @@ class M3u8PreviewGenerator : IPreviewGenerator {
|
|||
|
||||
// total duration of the entire m3u8 in seconds
|
||||
val duration = hsl.allTsLinks.sumOf { it.time ?: 0.0 }
|
||||
durationMs = (duration * 1000.0).toLong()
|
||||
val durationInv = 1.0 / duration
|
||||
|
||||
// if the total duration is less then 10s then something is very wrong or
|
||||
|
@ -245,7 +284,7 @@ class M3u8PreviewGenerator : IPreviewGenerator {
|
|||
if (!isActive) {
|
||||
return@withContext
|
||||
}
|
||||
if(img == null || img.width <= 1 || img.height <= 1) continue
|
||||
if (img == null || img.width <= 1 || img.height <= 1) continue
|
||||
synchronized(images) {
|
||||
images[index] = img
|
||||
loadedImages += 1
|
||||
|
@ -269,11 +308,11 @@ class M3u8PreviewGenerator : IPreviewGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
class Mp4PreviewGenerator : IPreviewGenerator {
|
||||
private class Mp4PreviewGenerator : IPreviewGenerator {
|
||||
// lod = level of detail where the number indicates how many ones there is
|
||||
// 2^(lod-1) = images
|
||||
private var loadedLod = 0
|
||||
private var loadedImages = 0
|
||||
override var loadedImages = 0
|
||||
private var images = Array<Bitmap?>((1 shl MAX_LOD) - 1) {
|
||||
null
|
||||
}
|
||||
|
@ -305,7 +344,7 @@ class Mp4PreviewGenerator : IPreviewGenerator {
|
|||
if (idx > loadedImages) {
|
||||
break
|
||||
}
|
||||
if(images[idx] == null) {
|
||||
if (images[idx] == null) {
|
||||
continue
|
||||
}
|
||||
val currentFraction =
|
||||
|
@ -325,7 +364,7 @@ class Mp4PreviewGenerator : IPreviewGenerator {
|
|||
// also check out https://github.com/wseemann/FFmpegMediaMetadataRetriever
|
||||
private val retriever: MediaMetadataRetriever = MediaMetadataRetriever()
|
||||
|
||||
override fun clear(keepCache: Boolean) {
|
||||
private fun clear(keepCache: Boolean) {
|
||||
if (keepCache) return
|
||||
synchronized(images) {
|
||||
loadedLod = 0
|
||||
|
@ -335,11 +374,11 @@ class Mp4PreviewGenerator : IPreviewGenerator {
|
|||
}
|
||||
|
||||
private var currentJob: Job? = null
|
||||
fun load(keepCache: Boolean, url: String, headers: Map<String, String>) {
|
||||
fun load(url: String, headers: Map<String, String>) {
|
||||
currentJob?.cancel()
|
||||
currentJob = ioSafe {
|
||||
Log.i(TAG, "Loading with url = $url headers = $headers")
|
||||
clear(keepCache)
|
||||
clear(true)
|
||||
retriever.setDataSource(url, headers)
|
||||
start(this)
|
||||
}
|
||||
|
@ -360,6 +399,8 @@ class Mp4PreviewGenerator : IPreviewGenerator {
|
|||
clear(false)
|
||||
}
|
||||
|
||||
override var durationMs: Long = 0L
|
||||
|
||||
@Throws
|
||||
@WorkerThread
|
||||
private fun start(scope: CoroutineScope) {
|
||||
|
@ -368,6 +409,7 @@ class Mp4PreviewGenerator : IPreviewGenerator {
|
|||
val durationMs =
|
||||
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong()
|
||||
?: throw IllegalArgumentException("Bad video duration")
|
||||
this.durationMs = durationMs
|
||||
val durationUs = (durationMs * 1000L).toFloat()
|
||||
//val width = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)?.toInt() ?: throw IllegalArgumentException("Bad video width")
|
||||
//val height = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toInt() ?: throw IllegalArgumentException("Bad video height")
|
||||
|
@ -388,7 +430,7 @@ class Mp4PreviewGenerator : IPreviewGenerator {
|
|||
MediaMetadataRetriever.OPTION_CLOSEST_SYNC
|
||||
)
|
||||
if (!scope.isActive) return
|
||||
if(img == null || img.width <= 1 || img.height <= 1) continue
|
||||
if (img == null || img.width <= 1 || img.height <= 1) continue
|
||||
synchronized(images) {
|
||||
images[idx] = img
|
||||
loadedImages = maxOf(loadedImages, idx)
|
||||
|
|
|
@ -22,17 +22,22 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
||||
import com.lagradost.cloudstream3.APIHolder.filterSearchResultByFilmQuality
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiSettings
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||
import com.lagradost.cloudstream3.AllLanguagesName
|
||||
import com.lagradost.cloudstream3.AnimeSearchResponse
|
||||
import com.lagradost.cloudstream3.HomePageList
|
||||
import com.lagradost.cloudstream3.MainAPI
|
||||
import com.lagradost.cloudstream3.MainActivity
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.SearchResponse
|
||||
import com.lagradost.cloudstream3.TvType
|
||||
import com.lagradost.cloudstream3.databinding.FragmentSearchBinding
|
||||
import com.lagradost.cloudstream3.databinding.HomeSelectMainpageBinding
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
|
@ -53,8 +58,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.ownHide
|
|||
import com.lagradost.cloudstream3.utils.AppUtils.ownShow
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.currentAccount
|
||||
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||
|
@ -63,9 +67,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
|
|||
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
const val SEARCH_PREF_TAGS = "search_pref_tags"
|
||||
const val SEARCH_PREF_PROVIDERS = "search_pref_providers"
|
||||
|
||||
class SearchFragment : Fragment() {
|
||||
companion object {
|
||||
fun List<SearchResponse>.filterSearchResponse(): List<SearchResponse> {
|
||||
|
@ -194,7 +195,7 @@ class SearchFragment : Fragment() {
|
|||
validAPIs.flatMap { api -> api.supportedTypes }.distinct()
|
||||
) { list ->
|
||||
if (selectedSearchTypes.toSet() != list.toSet()) {
|
||||
setKey("$currentAccount/$SEARCH_PREF_TAGS", selectedSearchTypes)
|
||||
DataStoreHelper.searchPreferenceTags = list
|
||||
selectedSearchTypes.clear()
|
||||
selectedSearchTypes.addAll(list)
|
||||
search(binding?.mainSearch?.query?.toString())
|
||||
|
@ -233,13 +234,7 @@ class SearchFragment : Fragment() {
|
|||
//searchMagIcon.scaleX = 0.65f
|
||||
//searchMagIcon.scaleY = 0.65f
|
||||
|
||||
context?.let { ctx ->
|
||||
val validAPIs = ctx.filterProviderByPreferredMedia()
|
||||
selectedApis = ctx.getKey(
|
||||
"$currentAccount/$SEARCH_PREF_PROVIDERS",
|
||||
defVal = validAPIs.map { it.name }
|
||||
)!!.toMutableSet()
|
||||
}
|
||||
selectedApis = DataStoreHelper.searchPreferenceProviders.toMutableSet()
|
||||
|
||||
binding?.searchFilter?.setOnClickListener { searchView ->
|
||||
searchView?.context?.let { ctx ->
|
||||
|
@ -287,7 +282,7 @@ class SearchFragment : Fragment() {
|
|||
}
|
||||
|
||||
fun updateList(types: List<TvType>) {
|
||||
setKey("$currentAccount/$SEARCH_PREF_TAGS", types.map { it.name })
|
||||
DataStoreHelper.searchPreferenceTags = types
|
||||
|
||||
arrayAdapter.clear()
|
||||
currentValidApis = validAPIs.filter { api ->
|
||||
|
@ -312,12 +307,7 @@ class SearchFragment : Fragment() {
|
|||
arrayAdapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
val selectedSearchTypes = getKey<List<String>>("$currentAccount/$SEARCH_PREF_TAGS")
|
||||
?.mapNotNull { listName ->
|
||||
TvType.values().firstOrNull { it.name == listName }
|
||||
}
|
||||
?.toMutableList()
|
||||
?: mutableListOf(TvType.Movie, TvType.TvSeries)
|
||||
val selectedSearchTypes = DataStoreHelper.searchPreferenceTags
|
||||
|
||||
bindChips(
|
||||
binding.tvtypesChipsScroll.tvtypesChips,
|
||||
|
@ -343,7 +333,7 @@ class SearchFragment : Fragment() {
|
|||
}
|
||||
|
||||
dialog.setOnDismissListener {
|
||||
context?.setKey("$currentAccount/$SEARCH_PREF_PROVIDERS", currentSelectedApis.toList())
|
||||
DataStoreHelper.searchPreferenceProviders = currentSelectedApis.toList()
|
||||
selectedApis = currentSelectedApis
|
||||
}
|
||||
updateList(selectedSearchTypes.toList())
|
||||
|
@ -354,10 +344,7 @@ class SearchFragment : Fragment() {
|
|||
val settingsManager = context?.let { PreferenceManager.getDefaultSharedPreferences(it) }
|
||||
val isAdvancedSearch = settingsManager?.getBoolean("advanced_search", true) ?: true
|
||||
|
||||
selectedSearchTypes = context?.getKey<List<String>>("$currentAccount/$SEARCH_PREF_TAGS")
|
||||
?.mapNotNull { listName -> TvType.values().firstOrNull { it.name == listName } }
|
||||
?.toMutableList()
|
||||
?: mutableListOf(TvType.Movie, TvType.TvSeries)
|
||||
selectedSearchTypes = DataStoreHelper.searchPreferenceTags.toMutableList()
|
||||
|
||||
if (isTrueTvSettings()) {
|
||||
binding?.searchFilter?.isFocusable = true
|
||||
|
|
|
@ -10,7 +10,9 @@ import androidx.core.widget.doOnTextChanged
|
|||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
||||
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.context
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
||||
|
@ -31,6 +33,8 @@ import com.lagradost.cloudstream3.ui.result.setImage
|
|||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
const val VIDEO_POS_DUR = "video_pos_dur"
|
||||
const val VIDEO_WATCH_STATE = "video_watch_state"
|
||||
|
@ -44,6 +48,28 @@ const val RESULT_EPISODE = "result_episode"
|
|||
const val RESULT_SEASON = "result_season"
|
||||
const val RESULT_DUB = "result_dub"
|
||||
|
||||
|
||||
class UserPreferenceDelegate<T : Any>(
|
||||
private val key: String, private val default: T //, private val klass: KClass<T>
|
||||
) {
|
||||
private val klass: KClass<out T> = default::class
|
||||
private val realKey get() = "${DataStoreHelper.currentAccount}/$key"
|
||||
operator fun getValue(self: Any?, property: KProperty<*>) =
|
||||
AcraApplication.getKeyClass(realKey, klass.java) ?: default
|
||||
|
||||
operator fun setValue(
|
||||
self: Any?,
|
||||
property: KProperty<*>,
|
||||
t: T?
|
||||
) {
|
||||
if (t == null) {
|
||||
removeKey(realKey)
|
||||
} else {
|
||||
AcraApplication.setKeyClass(realKey, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object DataStoreHelper {
|
||||
// be aware, don't change the index of these as Account uses the index for the art
|
||||
private val profileImages = arrayOf(
|
||||
|
@ -56,6 +82,48 @@ object DataStoreHelper {
|
|||
R.drawable.profile_bg_teal
|
||||
)
|
||||
|
||||
private var searchPreferenceProvidersStrings : List<String> by UserPreferenceDelegate(
|
||||
/** java moment right here, as listOf()::class.java != List(0) { "" }::class.java */
|
||||
"search_pref_providers", List(0) { "" }
|
||||
)
|
||||
|
||||
private fun serializeTv(data : List<TvType>) : List<String> = data.map { it.name }
|
||||
|
||||
private fun deserializeTv(data : List<String>) : List<TvType> {
|
||||
return data.mapNotNull { listName ->
|
||||
TvType.values().firstOrNull { it.name == listName }
|
||||
}
|
||||
}
|
||||
|
||||
var searchPreferenceProviders : List<String>
|
||||
get() {
|
||||
val ret = searchPreferenceProvidersStrings
|
||||
return ret.ifEmpty {
|
||||
context?.filterProviderByPreferredMedia()?.map { it.name } ?: emptyList()
|
||||
}
|
||||
} set(value) {
|
||||
searchPreferenceProvidersStrings = value
|
||||
}
|
||||
|
||||
private var searchPreferenceTagsStrings : List<String> by UserPreferenceDelegate("search_pref_tags", listOf(TvType.Movie, TvType.TvSeries).map { it.name })
|
||||
var searchPreferenceTags : List<TvType>
|
||||
get() = deserializeTv(searchPreferenceTagsStrings)
|
||||
set(value) {
|
||||
searchPreferenceTagsStrings = serializeTv(value)
|
||||
}
|
||||
|
||||
|
||||
private var homePreferenceStrings : List<String> by UserPreferenceDelegate("home_pref_homepage", listOf(TvType.Movie, TvType.TvSeries).map { it.name })
|
||||
var homePreference : List<TvType>
|
||||
get() = deserializeTv(homePreferenceStrings)
|
||||
set(value) {
|
||||
homePreferenceStrings = serializeTv(value)
|
||||
}
|
||||
|
||||
var homeBookmarkedList : IntArray by UserPreferenceDelegate("home_bookmarked_last_list", IntArray(0))
|
||||
var playBackSpeed : Float by UserPreferenceDelegate("playback_speed", 1.0f)
|
||||
var resizeMode : Int by UserPreferenceDelegate("resize_mode", 0)
|
||||
|
||||
data class Account(
|
||||
@JsonProperty("keyIndex")
|
||||
val keyIndex: Int,
|
||||
|
|
|
@ -349,14 +349,15 @@
|
|||
android:layout_width="150dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="100dp"
|
||||
android:layout_marginTop="60dp"
|
||||
android:backgroundTint="@color/skipOpTransparent"
|
||||
android:maxLines="1"
|
||||
android:padding="10dp"
|
||||
android:textColor="@color/white"
|
||||
android:visibility="gone"
|
||||
app:cornerRadius="@dimen/rounded_button_radius"
|
||||
app:layout_constraintBottom_toTopOf="@+id/bottom_player_bar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/player_top_holder"
|
||||
app:strokeColor="@color/white"
|
||||
app:strokeWidth="1dp"
|
||||
tools:text="Skip Opening"
|
||||
|
@ -435,6 +436,7 @@
|
|||
android:id="@+id/player_video_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:visibility="visible"
|
||||
android:layoutDirection="ltr"
|
||||
android:orientation="horizontal">
|
||||
|
||||
|
|
Loading…
Reference in a new issue