Compare commits

..

1 commit

Author SHA1 Message Date
firelight
9964f6fbf0
fix subtitle clipping 2026-07-31 01:53:01 +00:00
34 changed files with 140 additions and 658 deletions

View file

@ -701,23 +701,6 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(
homeViewModel.queryTextSubmit("") homeViewModel.queryTextSubmit("")
} }
// Load value for toggling Tv layout real time clock. Hide by default at startup
// set visibility first, to apply a scroll effect later
context?.let {
if (isLayout(TV)) {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(it)
val toggleClock =
settingsManager.getBoolean(
getString(R.string.tv_layout_clock_key),
false
)
binding.homeClock.isVisible = toggleClock
} else {
binding.homeClock.isVisible = false
}
}
homeMasterRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { homeMasterRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (isLayout(PHONE)) { if (isLayout(PHONE)) {
@ -757,17 +740,6 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>(
view.getLocationInWindow(rect) view.getLocationInWindow(rect)
scrollParent.isVisible = true scrollParent.isVisible = true
scrollParent.translationY = rect[1].toFloat() - 60.toPx scrollParent.translationY = rect[1].toFloat() - 60.toPx
// Move the TV layout real time clock out of the way too
// We check if we have the correct layout and if the clock is enabled
if(isLayout(TV) && binding.homeClock.isVisible) {
val scrollParent = binding.homeClock
val rect = IntArray(2)
view.getLocationInWindow(rect)
scrollParent.isVisible = true
scrollParent.translationY = rect[1].toFloat() - 60.toPx
}
} }
} }
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)

View file

@ -307,7 +307,6 @@ open class FullScreenPlayer : AbstractPlayerFragment<FragmentPlayerBinding>(
playerVideoTitleRez, playerVideoTitleRez,
playerVideoInfo, playerVideoInfo,
playerGoBackHolder, playerGoBackHolder,
playerVideoClock,
).forEach { ).forEach {
it.animateY(titleMove) it.animateY(titleMove)
} }
@ -773,7 +772,7 @@ open class FullScreenPlayer : AbstractPlayerFragment<FragmentPlayerBinding>(
val showPlayerEpisodes = !isGone && isThereEpisodes() val showPlayerEpisodes = !isGone && isThereEpisodes()
playerEpisodesButtonRoot.isVisible = showPlayerEpisodes playerEpisodesButtonRoot.isVisible = showPlayerEpisodes
playerEpisodesButton.isVisible = showPlayerEpisodes playerEpisodesButton.isVisible = showPlayerEpisodes
playerVideoTitleHolder.isGone = togglePlayerTitleGone || playerVideoTitle.text.isBlank() playerVideoTitleHolder.isGone = togglePlayerTitleGone
playerVideoTitleRez.isGone = isGone || playerVideoTitleRez.text.isBlank() playerVideoTitleRez.isGone = isGone || playerVideoTitleRez.text.isBlank()
playerEpisodeFiller.isGone = isGone playerEpisodeFiller.isGone = isGone
playerCenterMenu.isGone = isGone playerCenterMenu.isGone = isGone

View file

@ -8,7 +8,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Typeface
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.Spanned import android.text.Spanned
@ -80,7 +79,6 @@ import com.lagradost.cloudstream3.ui.player.CS3IPlayer.Companion.preferredAudioT
import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.updateForcedEncoding import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.updateForcedEncoding
import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType
import com.lagradost.cloudstream3.ui.player.source_priority.LinkSource import com.lagradost.cloudstream3.ui.player.source_priority.LinkSource
import com.lagradost.cloudstream3.ui.player.source_priority.ProfileSettings
import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper
import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper.getLinkPriority import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper.getLinkPriority
import com.lagradost.cloudstream3.ui.player.source_priority.QualityProfileDialog import com.lagradost.cloudstream3.ui.player.source_priority.QualityProfileDialog
@ -134,7 +132,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.Serializable import java.io.Serializable
import java.lang.ref.WeakReference
import java.util.Calendar import java.util.Calendar
import java.util.UUID import java.util.UUID
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
@ -1115,29 +1112,21 @@ class GeneratorPlayer : FullScreenPlayer() {
var sourceIndex = 0 var sourceIndex = 0
var startSource = 0 var startSource = 0
// Filtered and sorted links var sortedUrls = emptyList<Pair<ExtractorLink?, ExtractorUri?>>()
var currentHiddenFooter: View? = null
var filteredLinks: List<DisplayLink> = emptyList()
fun refreshLinks(qualityProfile: Int) { fun refreshLinks(qualityProfile: Int) {
val currentLinkUsed = currentSelectedLink sortedUrls = viewModel.state.sortLinks(qualityProfile)
// Always display current linkFooter if (sortedUrls.isEmpty()) {
val sortedLinks = viewModel.state.sortLinks(qualityProfile)
filteredLinks = sortedLinks.filter { it.shouldUseLink || it.link == currentLinkUsed }
if (sortedLinks.isEmpty()) {
sourceDialog.findViewById<LinearLayout>(R.id.sort_sources_holder)?.isGone = sourceDialog.findViewById<LinearLayout>(R.id.sort_sources_holder)?.isGone =
true true
} else { } else {
startSource = filteredLinks.indexOfFirst { it.link == currentLinkUsed } startSource = sortedUrls.indexOf(currentSelectedLink)
sourceIndex = startSource sourceIndex = startSource
val sourcesArrayAdapter = val sourcesArrayAdapter =
ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice) ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
sourcesArrayAdapter.addAll(filteredLinks.map { displayLink -> sourcesArrayAdapter.addAll(sortedUrls.map { (link, uri) ->
val (link, uri) = displayLink.link
val name = link?.name ?: uri?.name ?: "NULL" val name = link?.name ?: uri?.name ?: "NULL"
"$name ${Qualities.getStringByInt(link?.quality)}" "$name ${Qualities.getStringByInt(link?.quality)}"
}) })
@ -1153,7 +1142,7 @@ class GeneratorPlayer : FullScreenPlayer() {
} }
providerList.setOnItemLongClickListener { _, _, position, _ -> providerList.setOnItemLongClickListener { _, _, position, _ ->
sortedLinks.getOrNull(position)?.link?.first?.url?.let { sortedUrls.getOrNull(position)?.first?.url?.let {
clipboardHelper( clipboardHelper(
txt(R.string.video_source), txt(R.string.video_source),
it it
@ -1161,25 +1150,6 @@ class GeneratorPlayer : FullScreenPlayer() {
} }
true true
} }
val hiddenLinks = sortedLinks.size - filteredLinks.size
providerList.removeFooterView(currentHiddenFooter)
if (hiddenLinks > 0) {
val hiddenLinksFooter: TextView = layoutInflater.inflate(
R.layout.sort_bottom_footer_add_choice, null
) as TextView
providerList.addFooterView(hiddenLinksFooter, null, false)
currentHiddenFooter = hiddenLinksFooter
val hiddenLinksText =
ctx.resources.getQuantityString(R.plurals.links_hidden, hiddenLinks)
.format(hiddenLinks)
hiddenLinksFooter.text = hiddenLinksText
hiddenLinksFooter.setCompoundDrawables(null, null, null, null)
hiddenLinksFooter.setTypeface(null, Typeface.ITALIC)
}
} }
} }
@ -1393,8 +1363,8 @@ class GeneratorPlayer : FullScreenPlayer() {
} }
} }
if (init) { if (init) {
filteredLinks.getOrNull(sourceIndex)?.let { sortedUrls.getOrNull(sourceIndex)?.let {
loadLink(it.link, true) loadLink(it, true)
} }
} }
sourceDialog.dismissSafe(activity) sourceDialog.dismissSafe(activity)
@ -1562,10 +1532,6 @@ class GeneratorPlayer : FullScreenPlayer() {
} }
override fun playerError(exception: Throwable) { override fun playerError(exception: Throwable) {
currentSelectedLink?.let { link ->
viewModel.modifyState { this.addError(link) }
}
val currentUrl = val currentUrl =
currentSelectedLink?.let { it.first?.url ?: it.second?.uri?.toString() } ?: "unknown" currentSelectedLink?.let { it.first?.url ?: it.second?.uri?.toString() } ?: "unknown"
val headers = currentSelectedLink?.first?.headers?.toString() ?: "none" val headers = currentSelectedLink?.first?.headers?.toString() ?: "none"
@ -1588,22 +1554,8 @@ class GeneratorPlayer : FullScreenPlayer() {
private fun noLinksFound() { private fun noLinksFound() {
viewModel.forceClearCache = true viewModel.forceClearCache = true
val hiddenLinks = viewModel.state.sortLinks(currentQualityProfile).count { !it.shouldUseLink }
context?.let { ctx ->
// Display that there are hidden links to the user.
if (hiddenLinks > 0) {
val noLinksString = ctx.getString(R.string.no_links_found_toast)
val hiddenString =
ctx.resources.getQuantityString(R.plurals.links_hidden, hiddenLinks)
.format(hiddenLinks)
val toastText = "$noLinksString\n($hiddenString)"
showToast(toastText, Toast.LENGTH_SHORT)
} else {
showToast(R.string.no_links_found_toast, Toast.LENGTH_SHORT) showToast(R.string.no_links_found_toast, Toast.LENGTH_SHORT)
}
}
activity?.popCurrentPage() activity?.popCurrentPage()
} }
@ -1614,9 +1566,7 @@ class GeneratorPlayer : FullScreenPlayer() {
} }
val links = viewModel.state.sortLinks(currentQualityProfile) val links = viewModel.state.sortLinks(currentQualityProfile)
if (links.isEmpty()) {
val firstAvailableLink = links.firstOrNull { it.shouldUseLink }?.link
if (firstAvailableLink == null) {
noLinksFound() noLinksFound()
return return
} }
@ -1624,7 +1574,7 @@ class GeneratorPlayer : FullScreenPlayer() {
if (!isPlayerActive.compareAndSet(false, true)) { if (!isPlayerActive.compareAndSet(false, true)) {
return return
} }
loadLink(firstAvailableLink, false) loadLink(links.first(), false)
showPlayerMetadata() showPlayerMetadata()
} }
@ -1691,26 +1641,25 @@ class GeneratorPlayer : FullScreenPlayer() {
} }
} }
private fun getNextLink(): DisplayLink? {
val links = viewModel.state.sortLinks(currentQualityProfile)
val currentIndex = links.indexOfFirst { it.link == currentSelectedLink }
val nextPotentialLink =
links.withIndex().firstOrNull { it.index > currentIndex && it.value.shouldUseLink }
return nextPotentialLink?.value
}
override fun hasNextMirror(): Boolean { override fun hasNextMirror(): Boolean {
return getNextLink() != null val links = viewModel.state.sortLinks(currentQualityProfile)
return links.isNotEmpty() && links.indexOf(currentSelectedLink) + 1 < links.size
} }
override fun nextMirror() { override fun nextMirror() {
val nextLink = getNextLink() val links = viewModel.state.sortLinks(currentQualityProfile)
if (nextLink == null) { if (links.isEmpty()) {
noLinksFound() noLinksFound()
return return
} }
loadLink(nextLink.link, true) val newIndex = links.indexOf(currentSelectedLink) + 1
if (newIndex >= links.size) {
noLinksFound()
return
}
loadLink(links[newIndex], true)
} }
override fun onDestroy() { override fun onDestroy() {
@ -2217,7 +2166,6 @@ class GeneratorPlayer : FullScreenPlayer() {
isPlayerActive.set(false) isPlayerActive.set(false)
binding?.overlayLoadingSkipButton?.isVisible = false binding?.overlayLoadingSkipButton?.isVisible = false
binding?.playerLoadingOverlay?.isVisible = true binding?.playerLoadingOverlay?.isVisible = true
viewModel.modifyState { setError(emptyList()) }
uiReset() uiReset()
} }
@ -2271,14 +2219,6 @@ class GeneratorPlayer : FullScreenPlayer() {
fromTagToEnglishLanguageName(it)?.lowercase() ?: return@mapNotNull null fromTagToEnglishLanguageName(it)?.lowercase() ?: return@mapNotNull null
} ?: listOf() } ?: listOf()
} }
// Set up TV clock visibility
if (isLayout(TV)) {
val showTvClock = settingsManager.getBoolean(ctx.getString(R.string.tv_layout_clock_key), false)
playerBinding?.playerVideoClock?.isVisible = showTvClock
} else {
playerBinding?.playerVideoClock?.isVisible = false
}
} }
unwrapBundle(savedInstanceState) unwrapBundle(savedInstanceState)
@ -2357,23 +2297,19 @@ class GeneratorPlayer : FullScreenPlayer() {
} }
} }
observe(viewModel.currentLinks) { (_, instance) -> observe(viewModel.currentLinks) { (links, instance) ->
if (instance != viewModel.state.instance) return@observe // Outdated observe if (instance != viewModel.state.instance) return@observe // Outdated observe
val sortedLinks = viewModel.state.sortLinks(currentQualityProfile) val turnVisible = links.isNotEmpty() && viewModel.generator?.canSkipLoading == true
val usableLinks = sortedLinks.count { link -> link.shouldUseLink }
val turnVisible = usableLinks > 0 && viewModel.generator?.canSkipLoading == true
val wasGone = binding.overlayLoadingSkipButton.isGone val wasGone = binding.overlayLoadingSkipButton.isGone
binding.overlayLoadingSkipButton.apply { binding.overlayLoadingSkipButton.apply {
isVisible = turnVisible isVisible = turnVisible
if (links.isEmpty()) {
if (usableLinks == 0) {
setText(R.string.skip_loading) setText(R.string.skip_loading)
} else { } else {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
text = "${context.getString(R.string.skip_loading)} (${usableLinks})" text = "${context.getString(R.string.skip_loading)} (${links.size})"
} }
} }

View file

@ -10,8 +10,6 @@ import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.launchSafe import com.lagradost.cloudstream3.mvvm.launchSafe
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.ui.player.source_priority.ProfileSettings
import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper
import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper.getLinkPriority import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper.getLinkPriority
import com.lagradost.cloudstream3.ui.result.ResultEpisode import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
@ -42,20 +40,12 @@ data class GeneratorState(
val id: Int?, val id: Int?,
) )
data class DisplayLink(
val link: VideoLink,
// If the link should be displayed and used by the player
val shouldUseLink: Boolean,
val priority: Int
)
/** Immutable state of all current links relevant to displaying the video */ /** Immutable state of all current links relevant to displaying the video */
// @MustUseReturnValues // @MustUseReturnValues
// @Immutable // @Immutable
data class VideoState( data class VideoState(
val subtitles: PersistentSet<SubtitleData> = persistentSetOf(), val subtitles: PersistentSet<SubtitleData> = persistentSetOf(),
val links: PersistentSet<VideoLink> = persistentSetOf(), val links: PersistentSet<VideoLink> = persistentSetOf(),
val erroredLinks: PersistentSet<VideoLink> = persistentSetOf(),
val stamps: PersistentList<VideoSkipStamp> = persistentListOf(), val stamps: PersistentList<VideoSkipStamp> = persistentListOf(),
val loading: Resource<Unit> = Resource.Loading(), val loading: Resource<Unit> = Resource.Loading(),
val generatorState: GeneratorState? = null, val generatorState: GeneratorState? = null,
@ -66,52 +56,19 @@ data class VideoState(
* *
* sortedBy is not exactly expensive, but each hasNextMirror does it again, so this alleviates unnecessary recomputation * sortedBy is not exactly expensive, but each hasNextMirror does it again, so this alleviates unnecessary recomputation
* */ * */
private val sortedLinks: ConcurrentHashMap<Int, List<DisplayLink>> = ConcurrentHashMap() private val sortedLinks: ConcurrentHashMap<Int, List<VideoLink>> = ConcurrentHashMap()
/**
* The cache is guaranteed to be up to date link-wise due to the immutable links.
* However, hideNegativeSources and hideErrorSources could be updated, which requires clearing the cache.
*/
fun clearSortedLinksCache() = sortedLinks.clear() fun clearSortedLinksCache() = sortedLinks.clear()
private fun hasLinkErrored(link: VideoLink): Boolean {
return erroredLinks.any { it == link }
}
private fun VideoLink.toDisplayLink(
qualityProfile: Int,
hideNegativeSources: Boolean,
hideErrorSources: Boolean
): DisplayLink {
val priority = getLinkPriority(qualityProfile, this.first)
val shouldHideLink =
(hideNegativeSources && priority < 0) || (hideErrorSources && hasLinkErrored(this))
val displayLink = DisplayLink(this, !shouldHideLink, priority)
return displayLink
}
// Modifying sortedLinks is not considered a "visible" side effect, and rerunning it does not change the result // Modifying sortedLinks is not considered a "visible" side effect, and rerunning it does not change the result
// It is by all standards, idempotent and by extension also pure as it has no "visible" side effect // It is by all standards, idempotent and by extension also pure as it has no "visible" side effect
/** Returns .links in the sorted order according to the qualityProfile. /** Returns .links in the sorted order according to the qualityProfile.
* Use .links if order is not needed */ * Use .links if order is not needed */
@Contract(pure = true) @Contract(pure = true)
fun sortLinks(qualityProfile: Int): List<DisplayLink> { fun sortLinks(qualityProfile: Int): List<VideoLink> {
sortedLinks[qualityProfile]?.let { return sortedLinks[qualityProfile] ?: links.sortedBy { link ->
return it
}
val hideNegativeSources =
QualityDataHelper.getProfileSetting(qualityProfile, ProfileSettings.HideNegativeSources)
val hideErrorSources =
QualityDataHelper.getProfileSetting(qualityProfile, ProfileSettings.HideErrorSources)
return links.map { link ->
// negative because we want to sort highest quality first // negative because we want to sort highest quality first
link.toDisplayLink(qualityProfile, hideNegativeSources, hideErrorSources) -getLinkPriority(qualityProfile, link.first)
}.sortedBy {
// negative because we want to sort highest quality first
-it.priority
}.also { value -> sortedLinks[qualityProfile] = value } }.also { value -> sortedLinks[qualityProfile] = value }
} }
@ -156,12 +113,6 @@ data class VideoState(
@JvmName("setVideoSkipStamp") @JvmName("setVideoSkipStamp")
@Contract(pure = true) @Contract(pure = true)
fun set(items: Collection<VideoSkipStamp>): VideoState = copy(stamps = items.toPersistentList()) fun set(items: Collection<VideoSkipStamp>): VideoState = copy(stamps = items.toPersistentList())
@Contract(pure = true)
fun addError(item: VideoLink): VideoState = copy(erroredLinks = erroredLinks.add(item))
@Contract(pure = true)
fun setError(items: Collection<VideoLink>): VideoState = copy(erroredLinks = items.toPersistentSet())
} }
data class VideoLive<T>( data class VideoLive<T>(
@ -190,8 +141,9 @@ class PlayerGeneratorViewModel : ViewModel() {
var state = VideoState(instance = 0) var state = VideoState(instance = 0)
private set private set
private val _currentLinks = MutableLiveData<VideoLive<Set<VideoLink>>>(null) private val _currentLinks =
val currentLinks: LiveData<VideoLive<Set<VideoLink>>> = _currentLinks MutableLiveData<VideoLive<Set<Pair<ExtractorLink?, ExtractorUri?>>>>(null)
val currentLinks: LiveData<VideoLive<Set<Pair<ExtractorLink?, ExtractorUri?>>>> = _currentLinks
private val _currentSubtitles = MutableLiveData<VideoLive<Set<SubtitleData>>>(null) private val _currentSubtitles = MutableLiveData<VideoLive<Set<SubtitleData>>>(null)
val currentSubtitles: LiveData<VideoLive<Set<SubtitleData>>> = _currentSubtitles val currentSubtitles: LiveData<VideoLive<Set<SubtitleData>>> = _currentSubtitles

View file

@ -12,16 +12,12 @@ import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.DataStoreHelper.currentAccount import com.lagradost.cloudstream3.utils.DataStoreHelper.currentAccount
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.Qualities
import java.util.EnumMap
import java.util.concurrent.ConcurrentHashMap
import kotlin.also
import kotlin.math.abs import kotlin.math.abs
object QualityDataHelper { object QualityDataHelper {
private const val VIDEO_SOURCE_PRIORITY = "video_source_priority" private const val VIDEO_SOURCE_PRIORITY = "video_source_priority"
private const val VIDEO_PROFILE_NAME = "video_profile_name" private const val VIDEO_PROFILE_NAME = "video_profile_name"
private const val VIDEO_QUALITY_PRIORITY = "video_quality_priority" private const val VIDEO_QUALITY_PRIORITY = "video_quality_priority"
const val VIDEO_PROFILE_SETTINGS = "video_profile_settings"
// Old key only supporting one type per profile // Old key only supporting one type per profile
@Deprecated("Changed to support multiple types per profile") @Deprecated("Changed to support multiple types per profile")
@ -57,21 +53,13 @@ object QualityDataHelper {
val types: Set<QualityProfileType> val types: Set<QualityProfileType>
) )
// Map profile and name to priority
val sourcePriorityCache: ConcurrentHashMap<Int, HashMap<String, Int>> = ConcurrentHashMap()
fun getSourcePriority(profile: Int, name: String?): Int { fun getSourcePriority(profile: Int, name: String?): Int {
if (name == null) return DEFAULT_SOURCE_PRIORITY if (name == null) return DEFAULT_SOURCE_PRIORITY
return getKey<Int>(
return sourcePriorityCache[profile]?.get(name) ?: (getKey<Int>(
"$currentAccount/$VIDEO_SOURCE_PRIORITY/$profile", "$currentAccount/$VIDEO_SOURCE_PRIORITY/$profile",
name, name,
DEFAULT_SOURCE_PRIORITY DEFAULT_SOURCE_PRIORITY
) ?: DEFAULT_SOURCE_PRIORITY).also { ) ?: DEFAULT_SOURCE_PRIORITY
sourcePriorityCache.getOrPut(profile) { hashMapOf() }
sourcePriorityCache[profile]?.set(name, it)
}
} }
fun getAllSourcePriorityNames(profile: Int): List<String> { fun getAllSourcePriorityNames(profile: Int): List<String> {
@ -89,8 +77,6 @@ object QualityDataHelper {
} else { } else {
setKey(folder, name, priority) setKey(folder, name, priority)
} }
sourcePriorityCache[profile]?.set(name, priority)
} }
fun setProfileName(profile: Int, name: String?) { fun setProfileName(profile: Int, name: String?) {
@ -107,17 +93,12 @@ object QualityDataHelper {
?: txt(R.string.profile_number, profile) ?: txt(R.string.profile_number, profile)
} }
// Map profile and quality to priority
val qualityPriorityCache: ConcurrentHashMap<Int, EnumMap<Qualities, Int>> = ConcurrentHashMap()
fun getQualityPriority(profile: Int, quality: Qualities): Int { fun getQualityPriority(profile: Int, quality: Qualities): Int {
return qualityPriorityCache[profile]?.get(quality) ?: (getKey<Int>( return getKey<Int>(
"$currentAccount/$VIDEO_QUALITY_PRIORITY/$profile", "$currentAccount/$VIDEO_QUALITY_PRIORITY/$profile",
quality.value.toString(), quality.value.toString(),
quality.defaultPriority quality.defaultPriority
)?.also { ) ?: quality.defaultPriority
qualityPriorityCache.getOrPut(profile) { EnumMap(Qualities::class.java) }
qualityPriorityCache[profile]?.set(quality, it)
}) ?: quality.defaultPriority
} }
fun setQualityPriority(profile: Int, quality: Qualities, priority: Int) { fun setQualityPriority(profile: Int, quality: Qualities, priority: Int) {
@ -126,24 +107,8 @@ object QualityDataHelper {
quality.value.toString(), quality.value.toString(),
priority priority
) )
qualityPriorityCache[profile]?.set(quality, priority)
} }
fun <T> setProfileSetting(profile: Int, setting: ProfileSettings<T>, value: T) {
val folder = "$currentAccount/$VIDEO_PROFILE_SETTINGS/$profile"
// Prevent unnecessary keys
if (value == setting.defaultValue) {
removeKey(folder, setting.key)
} else {
setKey(folder, setting.key, value)
}
}
inline fun <reified T : Any> getProfileSetting(profile: Int, setting: ProfileSettings<T>): T {
val folder = "$currentAccount/$VIDEO_PROFILE_SETTINGS/$profile"
val value = getKey<T>(folder, setting.key)
return value ?: setting.defaultValue
}
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
fun getQualityProfileTypes(profile: Int): Set<QualityProfileType> { fun getQualityProfileTypes(profile: Int): Set<QualityProfileType> {
@ -259,8 +224,3 @@ object QualityDataHelper {
return Qualities.entries.minBy { abs(it.value - target) } return Qualities.entries.minBy { abs(it.value - target) }
} }
} }
sealed class ProfileSettings<T>(val key: String, val defaultValue: T) {
object HideErrorSources : ProfileSettings<Boolean>("hide_error_sources", false)
object HideNegativeSources : ProfileSettings<Boolean>("hide_negative_sources", false)
}

View file

@ -14,7 +14,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.fixSystemBarsPadding
class SourcePriorityDialog( class SourcePriorityDialog(
val ctx: Context, val ctx: Context,
@StyleRes val themeRes: Int, @StyleRes themeRes: Int,
val links: List<LinkSource>, val links: List<LinkSource>,
private val profile: QualityDataHelper.QualityProfile, private val profile: QualityDataHelper.QualityProfile,
/** /**
@ -28,27 +28,28 @@ class SourcePriorityDialog(
PlayerSelectSourcePriorityBinding.inflate(LayoutInflater.from(ctx), null, false) PlayerSelectSourcePriorityBinding.inflate(LayoutInflater.from(ctx), null, false)
setContentView(binding.root) setContentView(binding.root)
fixSystemBarsPadding(binding.root) fixSystemBarsPadding(binding.root)
val sourcesRecyclerView = binding.sortSources
val qualitiesRecyclerView = binding.sortQualities
val profileText = binding.profileTextEditable
val saveBtt = binding.saveBtt
val exitBtt = binding.closeBtt
val helpBtt = binding.helpBtt
binding.apply { profileText.setText(QualityDataHelper.getProfileName(profile.id).asString(context))
profileTextEditable.setText( profileText.hint = txt(R.string.profile_number, profile.id).asString(context)
QualityDataHelper.getProfileName(profile.id).asString(context)
)
profileTextEditable.hint = txt(R.string.profile_number, profile.id).asString(context)
sortSources.adapter = PriorityAdapter<Nothing?>( sourcesRecyclerView.adapter = PriorityAdapter<Nothing?>(
).apply { ).apply {
val sortedLinks = links.map { link -> submitList(links.map { link ->
SourcePriority( SourcePriority(
null, null,
link.source, link.source,
QualityDataHelper.getSourcePriority(profile.id, link.source) QualityDataHelper.getSourcePriority(profile.id, link.source)
) )
}.distinctBy { it.name }.sortedBy { -it.priority } }.distinctBy { it.name }.sortedBy { -it.priority })
submitList(sortedLinks)
} }
sortQualities.adapter = PriorityAdapter<Qualities>( qualitiesRecyclerView.adapter = PriorityAdapter<Qualities>(
).apply { ).apply {
submitList(Qualities.entries.mapNotNull { submitList(Qualities.entries.mapNotNull {
SourcePriority( SourcePriority(
@ -61,8 +62,8 @@ class SourcePriorityDialog(
@Suppress("UNCHECKED_CAST") // We know the types @Suppress("UNCHECKED_CAST") // We know the types
saveBtt.setOnClickListener { saveBtt.setOnClickListener {
val qualityAdapter = sortQualities.adapter as? PriorityAdapter<Qualities> val qualityAdapter = qualitiesRecyclerView.adapter as? PriorityAdapter<Qualities>
val sourcesAdapter = sortSources.adapter as? PriorityAdapter<Nothing?> val sourcesAdapter = sourcesRecyclerView.adapter as? PriorityAdapter<Nothing?>
val qualities = qualityAdapter?.immutableCurrentList ?: emptyList() val qualities = qualityAdapter?.immutableCurrentList ?: emptyList()
val sources = sourcesAdapter?.immutableCurrentList ?: emptyList() val sources = sourcesAdapter?.immutableCurrentList ?: emptyList()
@ -78,7 +79,7 @@ class SourcePriorityDialog(
qualityAdapter?.submitList(qualities.sortedBy { -it.priority }) qualityAdapter?.submitList(qualities.sortedBy { -it.priority })
sourcesAdapter?.submitList(sources.sortedBy { -it.priority }) sourcesAdapter?.submitList(sources.sortedBy { -it.priority })
val savedProfileName = profileTextEditable.text.toString() val savedProfileName = profileText.text.toString()
if (savedProfileName.isBlank()) { if (savedProfileName.isBlank()) {
QualityDataHelper.setProfileName(profile.id, null) QualityDataHelper.setProfileName(profile.id, null)
} else { } else {
@ -87,8 +88,8 @@ class SourcePriorityDialog(
updatedCallback.invoke() updatedCallback.invoke()
} }
closeBtt.setOnClickListener { exitBtt.setOnClickListener {
dismissSafe() this.dismissSafe()
} }
helpBtt.setOnClickListener { helpBtt.setOnClickListener {
@ -97,10 +98,6 @@ class SourcePriorityDialog(
}.show() }.show()
} }
settingsBtt.setOnClickListener {
SourceProfileSettingsDialog(ctx, themeRes, profile.id).show()
}
}
super.show() super.show()
} }
} }

View file

@ -1,48 +0,0 @@
package com.lagradost.cloudstream3.ui.player.source_priority
import android.app.Dialog
import android.content.Context
import android.view.LayoutInflater
import androidx.annotation.StyleRes
import com.lagradost.cloudstream3.databinding.SourceProfileSettingsDialogBinding
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.fixSystemBarsPadding
class SourceProfileSettingsDialog(
val ctx: Context,
@StyleRes themeRes: Int,
val profile: Int
) : Dialog(ctx, themeRes) {
override fun show() {
val binding =
SourceProfileSettingsDialogBinding.inflate(LayoutInflater.from(ctx), null, false)
setContentView(binding.root)
fixSystemBarsPadding(binding.root)
binding.apply {
var hideErrorSources = QualityDataHelper.getProfileSetting(profile, ProfileSettings.HideErrorSources)
var hideNegativeSources = QualityDataHelper.getProfileSetting(profile, ProfileSettings.HideNegativeSources)
profileHideErrorSources.isChecked = hideErrorSources
profileHideErrorSources.setOnCheckedChangeListener { _, bool ->
hideErrorSources = bool
}
profileHideNegativeSources.isChecked = hideNegativeSources
profileHideNegativeSources.setOnCheckedChangeListener { _, bool ->
hideNegativeSources = bool
}
applyBtt.setOnClickListener {
QualityDataHelper.setProfileSetting(profile, ProfileSettings.HideErrorSources, hideErrorSources)
QualityDataHelper.setProfileSetting(profile, ProfileSettings.HideNegativeSources, hideNegativeSources)
dismissSafe()
}
cancelBtt.setOnClickListener {
dismissSafe()
}
}
super.show()
}
}

View file

@ -77,7 +77,6 @@ val appLanguages = arrayListOf(
Pair("Azərbaycan dili", "az"), Pair("Azərbaycan dili", "az"),
Pair("Bahasa Indonesia", "in"), Pair("Bahasa Indonesia", "in"),
Pair("Bahasa Melayu", "ms"), Pair("Bahasa Melayu", "ms"),
Pair("català", "ca"),
Pair("Deutsch", "de"), Pair("Deutsch", "de"),
Pair("English", "en"), Pair("English", "en"),
Pair("Español", "es"), Pair("Español", "es"),
@ -98,7 +97,6 @@ val appLanguages = arrayListOf(
Pair("Português", "pt"), Pair("Português", "pt"),
Pair("Português (Brasil)", "pt-BR"), Pair("Português (Brasil)", "pt-BR"),
Pair("Română", "ro"), Pair("Română", "ro"),
Pair("Shqip мова", "sq"),
Pair("Slovenčina", "sk"), Pair("Slovenčina", "sk"),
Pair("Soomaaliga", "so"), Pair("Soomaaliga", "so"),
Pair("Svenska", "sv"), Pair("Svenska", "sv"),
@ -108,7 +106,6 @@ val appLanguages = arrayListOf(
Pair("Wikang Filipino", "fil"), Pair("Wikang Filipino", "fil"),
Pair("Čeština", "cs"), Pair("Čeština", "cs"),
Pair("Ελληνικά", "el"), Pair("Ελληνικά", "el"),
Pair("беларуская мова", "be"),
Pair("български", "bg"), Pair("български", "bg"),
Pair("македонски", "mk"), Pair("македонски", "mk"),
Pair("русский", "ru"), Pair("русский", "ru"),

View file

@ -228,8 +228,6 @@ class SettingsUI : BasePreferenceFragmentCompat() {
return@setOnPreferenceClickListener true return@setOnPreferenceClickListener true
} }
getPref(R.string.tv_layout_clock_key)?.hideOn(PHONE or EMULATOR)
getPref(R.string.confirm_exit_key)?.setOnPreferenceClickListener { getPref(R.string.confirm_exit_key)?.setOnPreferenceClickListener {
val prefNames = resources.getStringArray(R.array.confirm_exit) val prefNames = resources.getStringArray(R.array.confirm_exit)
val prefValues = resources.getIntArray(R.array.confirm_exit_values) val prefValues = resources.getIntArray(R.array.confirm_exit_values)

View file

@ -181,11 +181,11 @@ object DataStore {
} }
fun <T : Any> Context.getKey(path: String, valueType: Class<T>): T? { fun <T : Any> Context.getKey(path: String, valueType: Class<T>): T? {
return try { try {
val json: String = getSharedPrefs().getString(path, null) ?: return null val json: String = getSharedPrefs().getString(path, null) ?: return null
parseJson(json, valueType.kotlin) return parseJson(json, valueType.kotlin)
} catch (_: Exception) { } catch (e: Exception) {
null return null
} }
} }
@ -193,37 +193,21 @@ object DataStore {
setKey(getFolderName(folder, path), value) setKey(getFolderName(folder, path), value)
} }
@Deprecated(
message = "Use parseJson<T>(this) directly instead.",
level = DeprecationLevel.WARNING,
replaceWith = ReplaceWith(
expression = "parseJson<T>(this)",
imports = ["com.lagradost.cloudstream3.utils.AppUtils.parseJson"],
),
)
inline fun <reified T : Any> String.toKotlinObject(): T { inline fun <reified T : Any> String.toKotlinObject(): T {
return parseJson(this) return parseJson(this)
} }
@Deprecated(
message = "Use parseJson<T>(this) directly instead.",
level = DeprecationLevel.WARNING,
replaceWith = ReplaceWith(
expression = "parseJson<T>(this)",
imports = ["com.lagradost.cloudstream3.utils.AppUtils.parseJson"],
),
)
fun <T : Any> String.toKotlinObject(valueType: Class<T>): T { fun <T : Any> String.toKotlinObject(valueType: Class<T>): T {
return parseJson(this, valueType.kotlin) return parseJson(this, valueType.kotlin)
} }
// GET KEY GIVEN PATH AND DEFAULT VALUE, NULL IF ERROR // GET KEY GIVEN PATH AND DEFAULT VALUE, NULL IF ERROR
inline fun <reified T : Any> Context.getKey(path: String, defVal: T?): T? { inline fun <reified T : Any> Context.getKey(path: String, defVal: T?): T? {
return try { try {
val json: String = getSharedPrefs().getString(path, null) ?: return defVal val json: String = getSharedPrefs().getString(path, null) ?: return defVal
parseJson<T>(json) return json.toKotlinObject()
} catch (_: Exception) { } catch (e: Exception) {
null return null
} }
} }

View file

@ -149,12 +149,10 @@ object TvChannelUtils {
.setInputId(inputId) .setInputId(inputId)
.build() .build()
val channelUri = runCatching { val channelUri = context.contentResolver.insert(
context.contentResolver.insert(
TvContractCompat.Channels.CONTENT_URI, TvContractCompat.Channels.CONTENT_URI,
channel.toContentValues() channel.toContentValues()
) )
}.getOrNull()
channelUri?.let { channelUri?.let {
val channelId = ContentUris.parseId(it) val channelId = ContentUris.parseId(it)

View file

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="m612,668 l56,-56 -148,-148v-184h-80v216l172,172ZM480,880q-83,0 -156,-31.5T197,763q-54,-54 -85.5,-127T80,480q0,-83 31.5,-156T197,197q54,-54 127,-85.5T480,80q83,0 156,31.5T763,197q54,54 85.5,127T880,480q0,83 -31.5,156T763,763q-54,54 -127,85.5T480,880ZM480,480ZM480,800q133,0 226.5,-93.5T800,480q0,-133 -93.5,-226.5T480,160q-133,0 -226.5,93.5T160,480q0,133 93.5,226.5T480,800Z"
android:fillColor="#e3e3e3"/>
</vector>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -18,8 +19,8 @@
android:id="@+id/subtitles_click_settings" android:id="@+id/subtitles_click_settings"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
@ -32,21 +33,14 @@
android:textSize="20sp" android:textSize="20sp"
android:textStyle="bold" /> android:textStyle="bold" />
<com.google.android.material.button.MaterialButton
android:id="@+id/settings_btt"
style="@style/WhiteButton"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical|end"
android:text="@string/title_settings" />
<ImageView <ImageView
android:id="@+id/help_btt" android:id="@+id/help_btt"
android:layout_width="44dp" android:layout_width="44dp"
android:layout_height="44dp" android:layout_height="44dp"
android:background="?attr/selectableItemBackgroundBorderless" android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/help"
android:padding="10dp" android:padding="10dp"
android:src="@drawable/baseline_help_outline_24" /> android:src="@drawable/baseline_help_outline_24"
android:contentDescription="@string/help" />
</LinearLayout> </LinearLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
@ -99,8 +93,8 @@
android:id="@+id/apply_btt_holder" android:id="@+id/apply_btt_holder"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp" android:layout_height="60dp"
android:gravity="center_vertical"
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingHorizontal="?android:attr/listPreferredItemPaddingStart"> android:paddingHorizontal="?android:attr/listPreferredItemPaddingStart">
<EditText <EditText
@ -114,19 +108,19 @@
android:textColor="?attr/textColor" android:textColor="?attr/textColor"
android:textSize="20sp" android:textSize="20sp"
android:textStyle="bold" android:textStyle="bold"
tools:ignore="LabelFor" tools:text="@string/profile_number"
tools:text="@string/profile_number" /> tools:ignore="LabelFor" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/save_btt" android:id="@+id/save_btt"
style="@style/WhiteButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:text="@string/sort_save" /> android:text="@string/sort_save"
style="@style/WhiteButton" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/close_btt" android:id="@+id/close_btt"
style="@style/BlackButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:text="@string/sort_close" /> android:text="@string/sort_close"
style="@style/BlackButton" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View file

@ -227,12 +227,6 @@
tools:listitem="@layout/homepage_parent" tools:listitem="@layout/homepage_parent"
tools:visibility="gone" /> tools:visibility="gone" />
<TextClock
android:id="@+id/home_clock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/home_api_fab" android:id="@+id/home_api_fab"
style="@style/ExtendedFloatingActionButton" style="@style/ExtendedFloatingActionButton"

View file

@ -93,7 +93,6 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout> </com.facebook.shimmer.ShimmerFrameLayout>
</FrameLayout> </FrameLayout>
<LinearLayout <LinearLayout
@ -164,22 +163,6 @@
tools:listitem="@layout/homepage_parent_tv" tools:listitem="@layout/homepage_parent_tv"
tools:visibility="gone" /> tools:visibility="gone" />
<TextClock
android:id="@+id/home_clock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxWidth="600dp"
android:textColor="@color/white"
android:textSize="20sp"
android:visibility="visible"
android:format12Hour="hh:mm a"
android:format24Hour="HH:mm"
android:fontFamily="@font/google_sans"
android:textStyle="bold"
android:gravity="start"
android:layout_gravity="start"
android:layout_margin="10dp" />
<LinearLayout <LinearLayout
android:id="@+id/home_api_holder" android:id="@+id/home_api_holder"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -264,15 +264,6 @@
tools:visibility="visible" tools:visibility="visible"
android:layout_gravity="center"/> android:layout_gravity="center"/>
</LinearLayout> </LinearLayout>
<TextClock
android:id="@+id/player_video_clock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:gravity="end"
android:visibility="gone"
android:textStyle="bold" />
</LinearLayout> </LinearLayout>
<!-- Removed as it has no use anymore--> <!-- Removed as it has no use anymore-->

View file

@ -352,22 +352,6 @@
android:layout_marginEnd="32dp" android:layout_marginEnd="32dp"
android:orientation="vertical"> android:orientation="vertical">
<TextClock
android:id="@+id/player_video_clock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:gravity="end"
android:maxWidth="600dp"
android:textAlignment="viewEnd"
android:textColor="@color/white"
android:textSize="16sp"
android:visibility="visible"
android:format12Hour="hh:mm a"
android:format24Hour="HH:mm"
android:fontFamily="@font/google_sans"
android:textStyle="bold" />
<LinearLayout <LinearLayout
android:id="@+id/player_video_title_holder" android:id="@+id/player_video_title_holder"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -160,13 +160,6 @@
<!-- android:layout_gravity="center"--> <!-- android:layout_gravity="center"-->
<!-- android:src="@drawable/outline_edit_24" />--> <!-- android:src="@drawable/outline_edit_24" />-->
<com.google.android.material.button.MaterialButton
android:id="@+id/settings_btt"
style="@style/WhiteButton"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical|end"
android:text="@string/title_settings" />
<Space <Space
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View file

@ -1,99 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/profile_settings_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/primaryBlackBackground"
android:orientation="vertical">
<!-- <ScrollView-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent">-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_rowWeight="1"
android:layout_marginTop="20dp"
android:layout_marginBottom="10dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/profile_settings"
android:textColor="?attr/textColor"
android:textSize="20sp"
android:textStyle="bold" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/profile_hide_negative_sources"
style="@style/SettingsItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/google_sans"
android:nextFocusLeft="@id/apply_btt"
android:nextFocusRight="@id/cancel_btt"
android:nextFocusDown="@id/profile_hide_error_sources"
android:text="@string/profile_hide_negative_sources"
app:drawableEndCompat="@null"
app:trackTint="@color/toggle_selector" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/profile_hide_error_sources"
style="@style/SettingsItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/google_sans"
android:nextFocusLeft="@id/apply_btt"
android:nextFocusRight="@id/cancel_btt"
android:nextFocusUp="@id/profile_hide_negative_sources"
android:nextFocusDown="@id/apply_btt"
android:text="@string/profile_hide_error_sources"
app:drawableEndCompat="@null"
app:trackTint="@color/toggle_selector" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_gravity="bottom"
android:gravity="bottom|end"
android:orientation="horizontal"
tools:ignore="UselessParent">
<com.google.android.material.button.MaterialButton
android:id="@+id/apply_btt"
style="@style/WhiteButton"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical|end"
android:nextFocusRight="@id/cancel_btt"
android:nextFocusUp="@id/subtitles_italic"
android:text="@string/sort_apply"
android:visibility="visible">
<requestFocus />
</com.google.android.material.button.MaterialButton>
<com.google.android.material.button.MaterialButton
android:id="@+id/cancel_btt"
style="@style/BlackButton"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical|end"
android:nextFocusLeft="@id/apply_btt"
android:nextFocusUp="@id/subtitles_remove_captions"
android:text="@string/sort_cancel" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<!-- </ScrollView>-->
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -171,12 +171,6 @@
android:layout_marginEnd="32dp" android:layout_marginEnd="32dp"
android:orientation="vertical"> android:orientation="vertical">
<TextClock
android:id="@+id/player_video_clock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"/>
<LinearLayout <LinearLayout
android:id="@+id/player_video_title_holder" android:id="@+id/player_video_title_holder"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -3,7 +3,7 @@
<string name="result_poster_img_des">পোস্টার</string> <string name="result_poster_img_des">পোস্টার</string>
<string name="play_with_app_name">ক্লাউডস্ট্রিম দিয়ে চালান</string> <string name="play_with_app_name">ক্লাউডস্ট্রিম দিয়ে চালান</string>
<string name="title_home">হোম</string> <string name="title_home">হোম</string>
<string name="cast_format" formatted="true">অভিনয়ে: %s</string> <string name="cast_format" formatted="true">অভিনয়ে %s</string>
<string name="next_episode_time_day_format" formatted="true">%1$dদিন %2$dঘন্টা %3$dমিনিট</string> <string name="next_episode_time_day_format" formatted="true">%1$dদিন %2$dঘন্টা %3$dমিনিট</string>
<string name="next_episode_time_hour_format" formatted="true">%1$dঘন্টা %2$dমিনিট</string> <string name="next_episode_time_hour_format" formatted="true">%1$dঘন্টা %2$dমিনিট</string>
<string name="next_episode_time_min_format" formatted="true">%d মিনিট</string> <string name="next_episode_time_min_format" formatted="true">%d মিনিট</string>
@ -22,7 +22,7 @@
<string name="title_search">খুঁজুন</string> <string name="title_search">খুঁজুন</string>
<string name="title_downloads">ডাউনলোডসমূহ</string> <string name="title_downloads">ডাউনলোডসমূহ</string>
<string name="title_settings">সেটিংস</string> <string name="title_settings">সেটিংস</string>
<string name="app_dub_sub_episode_text_format" formatted="true">%1$s পর্ব %2$d</string> <string name="app_dub_sub_episode_text_format" formatted="true">%1$s এপি %2$d</string>
<string name="next_episode_format" formatted="true">পর্ব %d মুক্তির তারিখ</string> <string name="next_episode_format" formatted="true">পর্ব %d মুক্তির তারিখ</string>
<string name="result_share">শেয়ার</string> <string name="result_share">শেয়ার</string>
<string name="result_open_in_browser">ব্রাউজারে খুলুন</string> <string name="result_open_in_browser">ব্রাউজারে খুলুন</string>
@ -131,8 +131,8 @@
<string name="backup_failed">স্টোরেজ এর অনুমতি অনুপস্থিত। দয়া করে আবার চেষ্টা করুন।</string> <string name="backup_failed">স্টোরেজ এর অনুমতি অনুপস্থিত। দয়া করে আবার চেষ্টা করুন।</string>
<string name="settings_info">তথ্য</string> <string name="settings_info">তথ্য</string>
<string name="show_trailers_settings">ট্রেইলার প্রদর্শন করুন</string> <string name="show_trailers_settings">ট্রেইলার প্রদর্শন করুন</string>
<string name="kitsu_settings">Kitsu থেকে পোস্টারসমূহ প্রদর্শন করুন</string> <string name="kitsu_settings">কিটসু হতে পোস্টারসমূহ প্রদর্শন করুন</string>
<string name="automatic_plugin_updates">স্বয়ংক্রিয় প্লাগইন আপডেট</string> <string name="automatic_plugin_updates">স্বয়ংক্রিয়ভাবে প্লাগিন এর হালনাগাদ</string>
<string name="automatic_plugin_download">স্বয়ংক্রিয়ভাবে প্লাগিনসমুহের ডাউনলোড</string> <string name="automatic_plugin_download">স্বয়ংক্রিয়ভাবে প্লাগিনসমুহের ডাউনলোড</string>
<string name="category_updates">হালনাগাদ ও ব্যাকআপ</string> <string name="category_updates">হালনাগাদ ও ব্যাকআপ</string>
<string name="updates_settings">অ্যাপ এর হালনাগাদ দেখান</string> <string name="updates_settings">অ্যাপ এর হালনাগাদ দেখান</string>
@ -148,9 +148,9 @@
<string name="double_tap_to_seek_settings_des">সামনে বা পিছনের দিকে যেতে ডান বা বাম দিকে দুবার আলতো চাপুন</string> <string name="double_tap_to_seek_settings_des">সামনে বা পিছনের দিকে যেতে ডান বা বাম দিকে দুবার আলতো চাপুন</string>
<string name="delete_file">ফাইল ডিলিট</string> <string name="delete_file">ফাইল ডিলিট</string>
<string name="subs_default_reset_toast">মান ডিফল্ট এ রিসেট করুন</string> <string name="subs_default_reset_toast">মান ডিফল্ট এ রিসেট করুন</string>
<string name="updates_settings_des">স্টার্টআপে নতুন আপডেটের জন্য স্বয়ংক্রিয়ভাবে অনুসন্ধান করুন যোগ।</string> <string name="updates_settings_des">স্টার্টআপে নতুন আপডেটের জন্য স্বয়ংক্রিয়ভাবে অনুসন্ধান করুন</string>
<string name="movies_singular">সিনেমা</string> <string name="movies_singular">সিনেমা</string>
<string name="show_fillers_settings">অ্যানিমে ফিলার পর্ব প্রদর্শন</string> <string name="show_fillers_settings">এনিমে এর ফিলার পর্ব দেখায়</string>
<string name="tv_series">টিভি সিরিজ</string> <string name="tv_series">টিভি সিরিজ</string>
<string name="no_links_found_toast">লিংক পাওয়া যায়নি</string> <string name="no_links_found_toast">লিংক পাওয়া যায়নি</string>
<string name="benene_des">চা খাওয়ানো হয়েছে</string> <string name="benene_des">চা খাওয়ানো হয়েছে</string>
@ -174,7 +174,7 @@
<string name="delete">ডিলিট</string> <string name="delete">ডিলিট</string>
<string name="start">শুরু</string> <string name="start">শুরু</string>
<string name="cartoons">কার্টুন</string> <string name="cartoons">কার্টুন</string>
<string name="apk_installer_settings_des">Some devices do not support the new package installer. Try the legacy option if updates do not install</string> <string name="apk_installer_settings_des">কিছু ফোন নতুন প্যাকেজ ইনস্টলার সাপোর্ট করে না। যদি আপডেটগুলি ইনস্টল না হয় তবে পুরোনো পদ্ধতি ব্যবহার করে দেখুন</string>
<string name="no_subtitles">সাবটাইটেল নেই</string> <string name="no_subtitles">সাবটাইটেল নেই</string>
<string name="no_chromecast_support_toast">এই প্রোভাইডার ক্রোমকাস্ট সাপোর্ট করে না</string> <string name="no_chromecast_support_toast">এই প্রোভাইডার ক্রোমকাস্ট সাপোর্ট করে না</string>
<string name="advanced_search">উন্নত অনুসন্ধান</string> <string name="advanced_search">উন্নত অনুসন্ধান</string>
@ -204,7 +204,7 @@
<string name="anim">আমাদের তৈরি Anime দেখার অ্যাপ্লিকেশন</string> <string name="anim">আমাদের তৈরি Anime দেখার অ্যাপ্লিকেশন</string>
<string name="nsfw">18+</string> <string name="nsfw">18+</string>
<string name="anime">এনিমে</string> <string name="anime">এনিমে</string>
<string name="pref_filter_search_quality">অনুসন্ধানের ফলে নির্বাচিত ভিডিওর মান লুকান</string> <string name="pref_filter_search_quality">অনুসন্ধান ফলাফলে নির্বাচিত ভিডিও কুয়ালিটি লুকান</string>
<string name="app_storage">অ্যাপ</string> <string name="app_storage">অ্যাপ</string>
<string name="livestreams">লাইভস্ট্রিম</string> <string name="livestreams">লাইভস্ট্রিম</string>
<string name="apk_installer_settings">APK ইনস্টলার</string> <string name="apk_installer_settings">APK ইনস্টলার</string>
@ -354,37 +354,4 @@
<string name="speech_recognition_unavailable">স্পিচ রিকগনিশন উপলব্ধ নেই</string> <string name="speech_recognition_unavailable">স্পিচ রিকগনিশন উপলব্ধ নেই</string>
<string name="begin_speaking">কথা বলা শুরু করুন…</string> <string name="begin_speaking">কথা বলা শুরু করুন…</string>
<string name="download_queue">ডাউনলোড তালিকা</string> <string name="download_queue">ডাউনলোড তালিকা</string>
<string name="play_full_series_button">সম্পূর্ণ সিরিজ প্লে করুন</string>
<string name="torrent_info">এই ভিডিওটি একটি টরেন্ট, অর্থাৎ আপনার ভিডিও অ্যাক্টিভিটি ট্র্যাক করা সম্ভব।\nএগোনোর আগে টরেন্টিং সম্পর্কে ভালোভাবে বুঝে নিন।</string>
<string name="downloads_delete_select">ডিলিট করার জন্য আইটেম সিলেক্ট করুন</string>
<string name="downloads_empty">বর্তমানে কোনো ডাউনলোড নেই।</string>
<string name="queue_empty_message">বর্তমানে কোনো ডাউনলোড কিউতে নেই।</string>
<string name="offline_file">অফলাইনে দেখার জন্য উপলব্ধ</string>
<string name="select_all">সব সিলেক্ট করুন</string>
<string name="deselect_all">সব ডিসিলেক্ট করুন</string>
<string name="open_local_video">লোকাল ভিডিও খুলুন</string>
<string name="extra_brightness_settings">অতিরিক্ত ব্রাইটনেস</string>
<string name="extra_brightness_settings_des">ডিসপ্লে ব্রাইটনেস ১০০% ছাড়িয়ে গেলে ব্রাইটনেস ফিল্টার চালু করুন</string>
<string name="search_suggestions">সার্চ সাজেশন</string>
<string name="search_suggestions_des">টাইপ করার সময় সার্চ সাজেশন দেখান</string>
<string name="clear_suggestions">সাজেশন মুছে ফেলুন</string>
<string name="show_player_metadata_overlay">প্লেয়ার মেটাডেটা ওভারলে প্রদর্শন</string>
<string name="show_cast_in_details">কাস্ট প্যানেল প্রদর্শন</string>
<string name="install_prerelease">প্রাক-মুক্তি সংস্করণ ইনস্টলেশন</string>
<string name="prerelease_already_installed">প্রাক-মুক্তি সংস্করণ ইতোমধ্যে ইনস্টল রয়েছে।</string>
<string name="prerelease_install_failed">প্রাক-মুক্তি সংস্করণ ইনস্টল করতে সমস্যা হয়েছে।</string>
<string name="delete_format" formatted="true">(%1$d | %2$s) মুছুন</string>
<string name="test_warning">সাবধান</string>
<string name="delete_message_multiple" formatted="true">আপনি কি নিম্নলিখিত আইটেমগুলো স্থায়ীভাবে মুছতে চান?\n\n%s</string>
<string name="delete_files">ফাইল মুছুন</string>
<string name="delete_message_series_episodes" formatted="true">আপনি কি নিশ্চিত যে আপনি %1$s-এ নিচের এপিসোডগুলো স্থায়ীভাবে মুছে ফেলতে চান?\n\n%2$s</string>
<string name="delete_message_series_section" formatted="true">আপনি নিচের সিরিজগুলোর সব এপিসোডও স্থায়ীভাবে মুছে ফেলবেন:\n\n%s</string>
<string name="delete_message_series_only" formatted="true">আপনি কি নিশ্চিত যে আপনি নিচের সিরিজটির সব এপিসোড স্থায়ীভাবে মুছে ফেলতে চান?\n\n%s</string>
<string name="music_singular">সঙ্গীত</string>
<string name="audio_book_singular">অডিও বই</string>
<string name="custom_media_singular">মিডিয়া</string>
<string name="audio_singular">অডিও</string>
<string name="podcast_singular">পডকাস্ট</string>
<string name="video_singular">ভিডিও</string>
<string name="encoding_error">এনকোডিং ত্রুটি</string>
</resources> </resources>

View file

@ -682,17 +682,4 @@
<string name="torrent_preferred_media">Zet torrent aan in Instellingen/Providers/Media voorkeur</string> <string name="torrent_preferred_media">Zet torrent aan in Instellingen/Providers/Media voorkeur</string>
<string name="torrent_not_accepted">Herstart de app en accepteer de Stream Torrent pop-up om verder te gaan.</string> <string name="torrent_not_accepted">Herstart de app en accepteer de Stream Torrent pop-up om verder te gaan.</string>
<string name="update_plugins_manually">Handmatige Update Knop</string> <string name="update_plugins_manually">Handmatige Update Knop</string>
<string name="biometric_setting_summary">Open de app met Vingerafdruk, Face ID, PIN, Pattern en Wachtwoord.</string>
<string name="preview_seekbar">Seekbar preview</string>
<string name="preview_seekbar_desc">Zet preview thumbnail aan op seekbar</string>
<string name="software_decoding">Software decodering</string>
<string name="software_decoding_desc">Software decodering staat de mogelijkheid toe om de speler videobestanden af te laten spelen op jouw apparaat, maar het kan een haperige of onstabiele kijkervaring zorgen op hoge resolutie.</string>
<string name="volume_exceeded_100">Volume heeft de 100% overschreden</string>
<string name="update_plugins">Update Plugins</string>
<string name="no_plugins_updated_manually">Geen plugins zijn geupdate.</string>
<string name="player_notification_channel_name">Speler meldingen</string>
<string name="player_notification_channel_description">De speler melding om de kijkervaring van de achtergrond de besturen</string>
<string name="subtitles_from_online">Online</string>
<string name="download_parallel_settings_des">Hoeveel verschillende items kunnen in parallel gedownload worden</string>
<string name="parallel_downloads">Parallel downloads</string>
</resources> </resources>

View file

@ -742,13 +742,4 @@
<string name="video_singular">Wideo</string> <string name="video_singular">Wideo</string>
<string name="skip_type_preview">Zapowiedź</string> <string name="skip_type_preview">Zapowiedź</string>
<string name="player_is_live">Na żywo</string> <string name="player_is_live">Na żywo</string>
<string name="profile_settings">Ustawienia profilu</string>
<string name="profile_hide_negative_sources">Ukryj źródła z negatywnym priorytetem</string>
<string name="profile_hide_error_sources">Ukryj źródła z błędami</string>
<plurals name="links_hidden">
<item quantity="one">%d ukryty odnośnik</item>
<item quantity="few">%d ukryte odnośniki</item>
<item quantity="many">%d ukrytych odnośników</item>
<item quantity="other">%d ukrytych odnośników</item>
</plurals>
</resources> </resources>

View file

@ -36,7 +36,7 @@
<string name="next_episode">Tập tiếp theo</string> <string name="next_episode">Tập tiếp theo</string>
<string name="result_tags">Thể loại</string> <string name="result_tags">Thể loại</string>
<string name="result_share">Chia sẻ</string> <string name="result_share">Chia sẻ</string>
<string name="result_open_in_browser">Phát bằng Trình duyệt</string> <string name="result_open_in_browser">Mở bằng trình duyệt</string>
<string name="skip_loading">Bỏ tải</string> <string name="skip_loading">Bỏ tải</string>
<string name="loading">Đang tải…</string> <string name="loading">Đang tải…</string>
<string name="type_watching">Đang xem</string> <string name="type_watching">Đang xem</string>
@ -255,13 +255,13 @@
<string name="watch_quality_pref">Chất lượng xem ưu tiên (WiFi)</string> <string name="watch_quality_pref">Chất lượng xem ưu tiên (WiFi)</string>
<string name="limit_title">Số ký tự tối đa tiêu đề trình phát</string> <string name="limit_title">Số ký tự tối đa tiêu đề trình phát</string>
<string name="limit_title_rez">Hiển thị thông tin trình phát</string> <string name="limit_title_rez">Hiển thị thông tin trình phát</string>
<string name="video_buffer_size_settings">Kích thước bộ đệm video</string> <string name="video_buffer_size_settings">Kích thước bộ nhớ đệm video</string>
<string name="video_buffer_length_settings">Thời lượng bộ đệm video</string> <string name="video_buffer_length_settings">Thời lượng bộ nhớ đệm</string>
<string name="video_buffer_disk_settings">Bộ nhớ đệm video trên thiết bị</string> <string name="video_buffer_disk_settings">Bộ nhớ đệm video trên thiết bị</string>
<string name="video_buffer_clear_settings">Xoá bộ nhớ đệm hình ảnh và video</string> <string name="video_buffer_clear_settings">Xoá bộ nhớ đệm hình ảnh và video</string>
<string name="video_ram_description">Sẽ gây lỗi nếu đặt quá cao trên thiết bị có bộ nhớ thấp như Android TV.</string> <string name="video_ram_description">Sẽ gây lỗi nếu đặt quá cao trên thiết bị có bộ nhớ thấp như Android TV.</string>
<string name="video_disk_description">Sẽ gây lỗi nếu đặt quá cao trên thiết bị có dung lượng lưu trữ thấp như Android TV.</string> <string name="video_disk_description">Sẽ gây lỗi nếu đặt quá cao trên máy có dung lượng lưu trữ thấp như Android TV.</string>
<string name="dns_pref">DNS qua HTTPS</string> <string name="dns_pref">DNS over HTTPS</string>
<string name="dns_pref_summary">Rất hữu ích để bỏ chặn ISP</string> <string name="dns_pref_summary">Rất hữu ích để bỏ chặn ISP</string>
<string name="add_site_pref">Bản sao trang web</string> <string name="add_site_pref">Bản sao trang web</string>
<string name="remove_site_pref">Xoá trang web</string> <string name="remove_site_pref">Xoá trang web</string>

View file

@ -125,7 +125,7 @@
<string name="continue_watching">Працягнуць прагляд</string> <string name="continue_watching">Працягнуць прагляд</string>
<string name="action_remove_watching">Выдаліць</string> <string name="action_remove_watching">Выдаліць</string>
<string name="action_open_watching">Больш інфармацыі</string> <string name="action_open_watching">Больш інфармацыі</string>
<string name="action_open_play">@string/home_play</string> <string name="action_open_play">\@string/home_play</string>
<string name="vpn_might_be_needed">Для карэктнай працы гэтага пастаўшчыка можа спатрэбіцца VPN</string> <string name="vpn_might_be_needed">Для карэктнай працы гэтага пастаўшчыка можа спатрэбіцца VPN</string>
<string name="vpn_torrent">Гэты пастаўшчык — Torrent, рэкамендуецца VPN</string> <string name="vpn_torrent">Гэты пастаўшчык — Torrent, рэкамендуецца VPN</string>
<string name="provider_info_meta">Вэб-сайт не пастаўляе метададзеных, загрузіць відэа не ўдасца, калі на сайце яго няма.</string> <string name="provider_info_meta">Вэб-сайт не пастаўляе метададзеных, загрузіць відэа не ўдасца, калі на сайце яго няма.</string>

View file

@ -123,7 +123,7 @@
<string name="continue_watching">Continua mirant</string> <string name="continue_watching">Continua mirant</string>
<string name="action_remove_watching">Eliminar</string> <string name="action_remove_watching">Eliminar</string>
<string name="action_open_watching">Més info</string> <string name="action_open_watching">Més info</string>
<string name="action_open_play">@string/home_play</string> <string name="action_open_play">\@string/home_play</string>
<string name="vpn_might_be_needed">Potser necessiteu una VPN per que aquest proveïdor funcioni correctament</string> <string name="vpn_might_be_needed">Potser necessiteu una VPN per que aquest proveïdor funcioni correctament</string>
<string name="vpn_torrent">Aquest proveïdor és un Torrent, es recomana fer servir una VPN</string> <string name="vpn_torrent">Aquest proveïdor és un Torrent, es recomana fer servir una VPN</string>
<string name="provider_info_meta">El lloc no proveeix metadades, la càrrega del video fallarà si no existeix al lloc.</string> <string name="provider_info_meta">El lloc no proveeix metadades, la càrrega del video fallarà si no existeix al lloc.</string>

View file

@ -127,7 +127,7 @@
<string name="continue_watching">Vazhdo shikimin</string> <string name="continue_watching">Vazhdo shikimin</string>
<string name="action_remove_watching">Hiq</string> <string name="action_remove_watching">Hiq</string>
<string name="action_open_watching">Më shumë informacion</string> <string name="action_open_watching">Më shumë informacion</string>
<string name="action_open_play">@string/home_play</string> <string name="action_open_play">\@string/home_play</string>
<string name="vpn_might_be_needed">Një VPN mund të nevojitet që ky ofrues të funksionojë në rregull</string> <string name="vpn_might_be_needed">Një VPN mund të nevojitet që ky ofrues të funksionojë në rregull</string>
<string name="vpn_torrent">Ky ofrues është Torrent, rekomandohet një VPN</string> <string name="vpn_torrent">Ky ofrues është Torrent, rekomandohet një VPN</string>
<string name="provider_info_meta">Metadata nuk ofrohet nga kjo faqe, ngarkimi i videos do të dështojë nëse nuk ekziston në këtë faqe.</string> <string name="provider_info_meta">Metadata nuk ofrohet nga kjo faqe, ngarkimi i videos do të dështojë nëse nuk ekziston në këtë faqe.</string>
@ -735,11 +735,4 @@
<item quantity="other">%d shkarkime në radhë</item> <item quantity="other">%d shkarkime në radhë</item>
</plurals> </plurals>
<string name="player_is_live">Live</string> <string name="player_is_live">Live</string>
<string name="profile_settings">Cilësimet e profil-it</string>
<string name="profile_hide_negative_sources">Fshih burimet me prioritet negativ</string>
<string name="profile_hide_error_sources">Fshih burimet me error-e</string>
<plurals name="links_hidden">
<item quantity="one">%d link i fshehur</item>
<item quantity="other">%d linqe të fshehura</item>
</plurals>
</resources> </resources>

View file

@ -101,7 +101,6 @@
<string name="opensubtitles_key">opensubtitles_key</string> <string name="opensubtitles_key">opensubtitles_key</string>
<string name="subdl_key">subdl_key</string> <string name="subdl_key">subdl_key</string>
<string name="animeskip_key">animeskip_key</string> <string name="animeskip_key">animeskip_key</string>
<string name="tv_layout_clock_key">tv_layout_clock_key</string>
<string name="pref_category_security_key">pref_category_security_key</string> <string name="pref_category_security_key">pref_category_security_key</string>
<string name="pref_category_gestures_key">pref_category_gestures_key</string> <string name="pref_category_gestures_key">pref_category_gestures_key</string>

View file

@ -374,8 +374,6 @@
<string name="pref_category_looks">Looks</string> <string name="pref_category_looks">Looks</string>
<string name="pref_category_ui_features">Features</string> <string name="pref_category_ui_features">Features</string>
<string name="category_general">General</string> <string name="category_general">General</string>
<string name="tv_layout_clock_settings">Show real time clock</string>
<string name="tv_layout_clock_settings_des">Show a real time clock at the top of the screen. Applies to the homepage and player</string>
<string name="random_button_settings">Random Button</string> <string name="random_button_settings">Random Button</string>
<string name="random_button_settings_desc">Show random button on Homepage and Library</string> <string name="random_button_settings_desc">Show random button on Homepage and Library</string>
<string name="provider_lang_settings">Extension languages</string> <string name="provider_lang_settings">Extension languages</string>
@ -624,14 +622,6 @@
<string name="action_subscribe">Subscribe</string> <string name="action_subscribe">Subscribe</string>
<string name="action_unsubscribe">Unsubscribe</string> <string name="action_unsubscribe">Unsubscribe</string>
<string name="profile_number">Profile %d</string> <string name="profile_number">Profile %d</string>
<string name="profile_settings">Profile settings</string>
<string name="profile_hide_negative_sources">Hide sources with a negative priority</string>
<string name="profile_hide_error_sources">Hide sources with errors</string>
<plurals name="links_hidden">
<item quantity="one">%d hidden link</item>
<item quantity="other">%d hidden links</item>
</plurals>
<string name="wifi">Wi-Fi</string> <string name="wifi">Wi-Fi</string>
<string name="mobile_data">Mobile data</string> <string name="mobile_data">Mobile data</string>
<string name="set_default">Set default</string> <string name="set_default">Set default</string>
@ -793,4 +783,5 @@
<item quantity="other">%d downloads queued</item> <item quantity="other">%d downloads queued</item>
</plurals> </plurals>
<string name="player_is_live">Live</string> <string name="player_is_live">Live</string>
</resources> </resources>

View file

@ -89,12 +89,6 @@
android:icon="@drawable/metadata_overlay_icon" android:icon="@drawable/metadata_overlay_icon"
android:key="@string/show_player_metadata_key" android:key="@string/show_player_metadata_key"
android:title="@string/show_player_metadata_overlay" /> android:title="@string/show_player_metadata_overlay" />
<SwitchPreference
android:icon="@drawable/ic_baseline_clock_24"
android:summary="@string/tv_layout_clock_settings_des"
android:title="@string/tv_layout_clock_settings"
android:defaultValue="false"
android:key="@string/tv_layout_clock_key" />
<SwitchPreference <SwitchPreference
android:icon="@drawable/ic_baseline_play_arrow_24" android:icon="@drawable/ic_baseline_play_arrow_24"
android:summary="@string/random_button_settings_desc" android:summary="@string/random_button_settings_desc"

View file

@ -1 +0,0 @@
- চেঞ্জলগ যোগ করা হয়েছে!

View file

@ -1,7 +0,0 @@
CloudStream-3 দিয়ে সিনেমা, টিভি-সিরিজ ও অ্যানিমে স্ট্রিম ও ডাউনলোড করা যায়।
অ্যাপে কোনো বিজ্ঞাপন ও অ্যানালিটিক্স নেই এবং
এটি একাধিক ট্রেলার ও মুভি সাইট সাপোর্ট করে, আরও আছে যেমন—
বুকমার্ক
সাবটাইটেল ডাউনলোড
ক্রোমকাস্ট সাপোর্ট

View file

@ -1 +0,0 @@
সিনেমা, টিভি সিরিজ ও অ্যানিমে স্ট্রিম ও ডাউনলোড করুন।

View file

@ -1 +0,0 @@
CloudStream