forked from recloudstream/cloudstream
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
e78e068214
19 changed files with 591 additions and 534 deletions
|
@ -36,7 +36,7 @@ android {
|
|||
targetSdkVersion 30
|
||||
|
||||
versionCode 48
|
||||
versionName "2.10.30"
|
||||
versionName "2.10.31"
|
||||
|
||||
resValue "string", "app_version",
|
||||
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"
|
||||
|
|
|
@ -8,7 +8,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
|
|||
import org.jsoup.Jsoup
|
||||
|
||||
class HDMovie5 : MainAPI() {
|
||||
override var mainUrl = "https://hdmovie2.biz"
|
||||
override var mainUrl = "https://Hdmovie2.biz"
|
||||
override var name = "HDMovie"
|
||||
override var lang = "hi"
|
||||
|
||||
|
|
|
@ -2,8 +2,11 @@ package com.lagradost.cloudstream3.movieproviders
|
|||
|
||||
import android.util.Log
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
|
||||
import com.lagradost.cloudstream3.SubtitleFile
|
||||
import com.lagradost.cloudstream3.TvType
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.metaproviders.TmdbLink
|
||||
import com.lagradost.cloudstream3.metaproviders.TmdbProvider
|
||||
import com.lagradost.cloudstream3.movieproviders.SflixProvider.Companion.extractRabbitStream
|
||||
|
@ -15,7 +18,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
|
|||
class TwoEmbedProvider : TmdbProvider() {
|
||||
override val apiName = "2Embed"
|
||||
override var name = "2Embed"
|
||||
override var mainUrl = "https://www.2embed.to"
|
||||
override var mainUrl = "https://2embed.org"
|
||||
override val useMetaLoadResponse = true
|
||||
override val instantLinkLoading = false
|
||||
override val supportedTypes = setOf(
|
||||
|
@ -43,10 +46,10 @@ class TwoEmbedProvider : TmdbProvider() {
|
|||
) else listOf(mappedData.tmdbID.toString(), "tmdb")
|
||||
val isMovie = mappedData.episode == null && mappedData.season == null
|
||||
val embedUrl = if (isMovie) {
|
||||
"$mainUrl/embed/$site/movie?id=$id"
|
||||
"$mainUrl/embed/movie?$site=$id"
|
||||
} else {
|
||||
val suffix = "$id&s=${mappedData.season ?: 1}&e=${mappedData.episode ?: 1}"
|
||||
"$mainUrl/embed/$site/tv?id=$suffix"
|
||||
val suffix = "$id&sea=${mappedData.season ?: 1}&epi=${mappedData.episode ?: 1}"
|
||||
"$mainUrl/embed/series?$site=$suffix"
|
||||
}
|
||||
|
||||
val document = app.get(embedUrl).document
|
||||
|
|
|
@ -12,21 +12,40 @@ import com.lagradost.cloudstream3.R
|
|||
import com.lagradost.cloudstream3.utils.UIHelper.adjustAlpha
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
|
||||
class MyMiniControllerFragment : MiniControllerFragment() {
|
||||
var currentColor: Int = 0
|
||||
|
||||
// I KNOW, KINDA SPAGHETTI SOLUTION, BUT IT WORKS
|
||||
override fun onInflate(context: Context, attributeSet: AttributeSet, bundle: Bundle?) {
|
||||
val obtainStyledAttributes = context.obtainStyledAttributes(attributeSet, R.styleable.CustomCast, 0, 0)
|
||||
if (obtainStyledAttributes.hasValue(R.styleable.CustomCast_customCastBackgroundColor)) {
|
||||
currentColor = obtainStyledAttributes.getColor(R.styleable.CustomCast_customCastBackgroundColor, 0)
|
||||
}
|
||||
obtainStyledAttributes.recycle()
|
||||
super.onInflate(context, attributeSet, bundle)
|
||||
override fun onDestroy() {
|
||||
currentColor = 0
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
// I KNOW, KINDA SPAGHETTI SOLUTION, BUT IT WORKS
|
||||
override fun onInflate(context: Context, attributeSet: AttributeSet, bundle: Bundle?) {
|
||||
super.onInflate(context, attributeSet, bundle)
|
||||
|
||||
// somehow this leaks and I really dont know why, it seams like if you go back to a fragment with this, it leaks????
|
||||
if (currentColor == 0) {
|
||||
WeakReference(
|
||||
context.obtainStyledAttributes(
|
||||
attributeSet,
|
||||
R.styleable.CustomCast
|
||||
)
|
||||
).apply {
|
||||
if (get()
|
||||
?.hasValue(R.styleable.CustomCast_customCastBackgroundColor) == true
|
||||
) {
|
||||
currentColor =
|
||||
get()
|
||||
?.getColor(R.styleable.CustomCast_customCastBackgroundColor, 0) ?: 0
|
||||
}
|
||||
get()?.recycle()
|
||||
}.clear()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
@ -38,18 +57,24 @@ class MyMiniControllerFragment : MiniControllerFragment() {
|
|||
val containerCurrent: RelativeLayout? = view.findViewById(R.id.container_current)
|
||||
|
||||
context?.let { ctx ->
|
||||
progressBar?.setBackgroundColor(adjustAlpha(ctx.colorFromAttribute(R.attr.colorPrimary), 0.35f))
|
||||
val params = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 2.toPx)
|
||||
progressBar?.setBackgroundColor(
|
||||
adjustAlpha(
|
||||
ctx.colorFromAttribute(R.attr.colorPrimary),
|
||||
0.35f
|
||||
)
|
||||
)
|
||||
val params = RelativeLayout.LayoutParams(
|
||||
RelativeLayout.LayoutParams.MATCH_PARENT,
|
||||
2.toPx
|
||||
)
|
||||
|
||||
progressBar?.layoutParams = params
|
||||
|
||||
if (currentColor != 0) {
|
||||
containerCurrent?.setBackgroundColor(currentColor)
|
||||
}
|
||||
val color = currentColor
|
||||
if (color != 0)
|
||||
containerCurrent?.setBackgroundColor(color)
|
||||
}
|
||||
val child = containerAll?.getChildAt(0)
|
||||
child?.alpha = 0f // REMOVE GRADIENT
|
||||
|
||||
} catch (e: Exception) {
|
||||
// JUST IN CASE
|
||||
}
|
||||
|
|
|
@ -31,12 +31,8 @@ class DownloadChildFragment : Fragment() {
|
|||
|
||||
override fun onDestroyView() {
|
||||
(download_child_list?.adapter as DownloadChildAdapter?)?.killAdapter()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent -= it }
|
||||
super.onDestroy()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
|
|
|
@ -65,16 +65,12 @@ class DownloadFragment : Fragment() {
|
|||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
(download_list?.adapter as DownloadHeaderAdapter?)?.killAdapter()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (downloadDeleteEventListener != null) {
|
||||
VideoDownloadManager.downloadDeleteEvent -= downloadDeleteEventListener!!
|
||||
downloadDeleteEventListener = null
|
||||
}
|
||||
super.onDestroy()
|
||||
(download_list?.adapter as DownloadHeaderAdapter?)?.killAdapter()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
|
@ -83,7 +79,17 @@ class DownloadFragment : Fragment() {
|
|||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
downloadsViewModel =
|
||||
ViewModelProvider(this).get(DownloadViewModel::class.java)
|
||||
ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||
|
||||
return inflater.inflate(R.layout.fragment_downloads, container, false)
|
||||
}
|
||||
|
||||
private var downloadDeleteEventListener: ((Int) -> Unit)? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
hideKeyboard()
|
||||
|
||||
observe(downloadsViewModel.noDownloadsText) {
|
||||
text_no_downloads.text = it
|
||||
}
|
||||
|
@ -114,16 +120,8 @@ class DownloadFragment : Fragment() {
|
|||
getBytesAsText(it)
|
||||
)
|
||||
download_app?.setLayoutWidth(it)
|
||||
download_storage_appbar?.visibility = View.VISIBLE
|
||||
download_storage_appbar?.isVisible = it > 0
|
||||
}
|
||||
return inflater.inflate(R.layout.fragment_downloads, container, false)
|
||||
}
|
||||
|
||||
private var downloadDeleteEventListener: ((Int) -> Unit)? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
hideKeyboard()
|
||||
|
||||
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
|
||||
DownloadHeaderAdapter(
|
||||
|
|
|
@ -8,6 +8,7 @@ import androidx.lifecycle.MutableLiveData
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.lagradost.cloudstream3.isMovieType
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE
|
||||
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getFolderName
|
||||
|
@ -100,16 +101,20 @@ class DownloadViewModel : ViewModel() {
|
|||
(it.child?.episode ?: 0) + (it.child?.season?.times(10000) ?: 0)
|
||||
} // episode sorting by episode, lowest to highest
|
||||
}
|
||||
try {
|
||||
val stat = StatFs(Environment.getExternalStorageDirectory().path)
|
||||
|
||||
val stat = StatFs(Environment.getExternalStorageDirectory().path)
|
||||
val localBytesAvailable = stat.availableBytes//stat.blockSizeLong * stat.blockCountLong
|
||||
val localTotalBytes = stat.blockSizeLong * stat.blockCountLong
|
||||
val localDownloadedBytes = visual.sumOf { it.totalBytes }
|
||||
|
||||
val localBytesAvailable = stat.availableBytes//stat.blockSizeLong * stat.blockCountLong
|
||||
val localTotalBytes = stat.blockSizeLong * stat.blockCountLong
|
||||
val localDownloadedBytes = visual.sumOf { it.totalBytes }
|
||||
|
||||
_usedBytes.postValue(localTotalBytes - localBytesAvailable - localDownloadedBytes)
|
||||
_availableBytes.postValue(localBytesAvailable)
|
||||
_downloadBytes.postValue(localDownloadedBytes)
|
||||
_usedBytes.postValue(localTotalBytes - localBytesAvailable - localDownloadedBytes)
|
||||
_availableBytes.postValue(localBytesAvailable)
|
||||
_downloadBytes.postValue(localDownloadedBytes)
|
||||
} catch (e : Exception) {
|
||||
_downloadBytes.postValue(0)
|
||||
logError(e)
|
||||
}
|
||||
|
||||
_headerCards.postValue(visual)
|
||||
}
|
||||
|
|
|
@ -20,8 +20,13 @@ class EasyDownloadButton : IDisposable {
|
|||
val id: Int
|
||||
}
|
||||
|
||||
private var _clickCallback: ((DownloadClickEvent) -> Unit)? = null
|
||||
private var _imageChangeCallback: ((Pair<Int, String>) -> Unit)? = null
|
||||
|
||||
override fun dispose() {
|
||||
try {
|
||||
_clickCallback = null
|
||||
_imageChangeCallback = null
|
||||
downloadProgressEventListener?.let { VideoDownloadManager.downloadProgressEvent -= it }
|
||||
downloadStatusEventListener?.let { VideoDownloadManager.downloadStatusEvent -= it }
|
||||
} catch (e: Exception) {
|
||||
|
@ -119,6 +124,8 @@ class EasyDownloadButton : IDisposable {
|
|||
clickCallback: (DownloadClickEvent) -> Unit,
|
||||
isTextPercentage: Boolean = false
|
||||
) {
|
||||
_clickCallback = clickCallback
|
||||
_imageChangeCallback = downloadImageChangeCallback
|
||||
var lastState: VideoDownloadManager.DownloadType? = null
|
||||
var currentBytes = setupCurrentBytes ?: 0
|
||||
var totalBytes = setupTotalBytes ?: 0
|
||||
|
@ -142,7 +149,7 @@ class EasyDownloadButton : IDisposable {
|
|||
} else {
|
||||
Pair(R.drawable.netflix_download, R.string.download)
|
||||
}
|
||||
downloadImageChangeCallback.invoke(
|
||||
_imageChangeCallback?.invoke(
|
||||
Pair(
|
||||
img.first,
|
||||
downloadView.context.getString(img.second)
|
||||
|
@ -223,7 +230,7 @@ class EasyDownloadButton : IDisposable {
|
|||
|
||||
downloadView.setOnClickListener {
|
||||
if (currentBytes <= 0) {
|
||||
clickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_DOWNLOAD, data))
|
||||
_clickCallback?.invoke(DownloadClickEvent(DOWNLOAD_ACTION_DOWNLOAD, data))
|
||||
} else {
|
||||
val list = arrayListOf(
|
||||
Pair(DOWNLOAD_ACTION_PLAY_FILE, R.string.popup_play_file),
|
||||
|
@ -243,7 +250,7 @@ class EasyDownloadButton : IDisposable {
|
|||
it.popupMenuNoIcons(
|
||||
list
|
||||
) {
|
||||
clickCallback.invoke(DownloadClickEvent(itemId, data))
|
||||
_clickCallback?.invoke(DownloadClickEvent(itemId, data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -350,6 +350,7 @@ abstract class AbstractPlayerFragment(
|
|||
resizeMode = getKey(RESIZE_MODE_KEY) ?: 0
|
||||
resize(resizeMode, false)
|
||||
|
||||
player.releaseCallbacks()
|
||||
player.initCallbacks(
|
||||
playerUpdated = ::playerUpdated,
|
||||
updateIsPlaying = ::updateIsPlaying,
|
||||
|
|
|
@ -108,6 +108,21 @@ class CS3IPlayer : IPlayer {
|
|||
private var playerUpdated: ((Any?) -> Unit)? = null
|
||||
private var embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)? = null
|
||||
|
||||
override fun releaseCallbacks() {
|
||||
playerUpdated = null
|
||||
updateIsPlaying = null
|
||||
requestAutoFocus = null
|
||||
playerError = null
|
||||
playerDimensionsLoaded = null
|
||||
requestedListeningPercentages = null
|
||||
playerPositionChanged = null
|
||||
nextEpisode = null
|
||||
prevEpisode = null
|
||||
subtitlesUpdates = null
|
||||
embeddedSubtitlesFetched = null
|
||||
requestSubtitleUpdate = null
|
||||
}
|
||||
|
||||
override fun initCallbacks(
|
||||
playerUpdated: (Any?) -> Unit,
|
||||
updateIsPlaying: ((Pair<CSPlayerLoading, CSPlayerLoading>) -> Unit)?,
|
||||
|
|
|
@ -315,6 +315,8 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
|
||||
override fun onDestroy() {
|
||||
exitFullscreen()
|
||||
player.release()
|
||||
player.releaseCallbacks()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
|
@ -1104,7 +1106,6 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// init variables
|
||||
setPlayBackSpeed(getKey(PLAYBACK_SPEED_KEY) ?: 1.0f)
|
||||
|
||||
|
|
|
@ -359,12 +359,12 @@ class GeneratorPlayer : FullScreenPlayer() {
|
|||
}
|
||||
})
|
||||
|
||||
dialog.search_filter.setOnClickListener {
|
||||
dialog.search_filter.setOnClickListener { view ->
|
||||
val lang639_1 = languages.map { it.ISO_639_1 }
|
||||
activity?.showDialog(
|
||||
languages.map { it.languageName },
|
||||
lang639_1.indexOf(currentLanguageTwoLetters),
|
||||
context.getString(R.string.subs_subtitle_languages),
|
||||
view?.context?.getString(R.string.subs_subtitle_languages) ?: return@setOnClickListener,
|
||||
true,
|
||||
{ }
|
||||
) { index ->
|
||||
|
@ -609,7 +609,7 @@ class GeneratorPlayer : FullScreenPlayer() {
|
|||
|
||||
val currentPrefMedia =
|
||||
settingsManager.getString(
|
||||
getString(R.string.subtitles_encoding_key),
|
||||
ctx.getString(R.string.subtitles_encoding_key),
|
||||
null
|
||||
)
|
||||
|
||||
|
|
|
@ -88,6 +88,7 @@ interface IPlayer {
|
|||
subtitlesUpdates: (() -> Unit)? = null, // callback from player to inform that subtitles have updated in some way
|
||||
embeddedSubtitlesFetched: ((List<SubtitleData>) -> Unit)? = null, // callback from player to give all embedded subtitles
|
||||
)
|
||||
fun releaseCallbacks()
|
||||
|
||||
fun updateSubtitleStyle(style: SaveCaptionStyle)
|
||||
fun saveData()
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -129,10 +129,14 @@ object UIHelper {
|
|||
}
|
||||
|
||||
fun Activity?.navigate(@IdRes navigation: Int, arguments: Bundle? = null) {
|
||||
if (this is FragmentActivity) {
|
||||
(supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment?)?.navController?.navigate(
|
||||
navigation, arguments
|
||||
)
|
||||
try {
|
||||
if (this is FragmentActivity) {
|
||||
(supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment?)?.navController?.navigate(
|
||||
navigation, arguments
|
||||
)
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
logError(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
-->
|
||||
<LinearLayout
|
||||
android:id="@+id/download_storage_appbar"
|
||||
tools:visibility="visible"
|
||||
android:visibility="gone"
|
||||
android:layout_margin="10dp"
|
||||
android:orientation="vertical"
|
||||
|
|
|
@ -360,14 +360,15 @@
|
|||
tools:text="121min" />
|
||||
</com.lagradost.cloudstream3.widget.FlowLayout>
|
||||
|
||||
<!--
|
||||
This has half margin and half padding to make TV focus on description look better.
|
||||
The focus outline now settles between the poster and text.
|
||||
-->
|
||||
<!--
|
||||
This has half margin and half padding to make TV focus on description look better.
|
||||
The focus outline now settles between the poster and text.
|
||||
-->
|
||||
<FrameLayout
|
||||
android:layout_marginHorizontal="5dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:padding="5dp"
|
||||
android:maxLength="1000"
|
||||
|
@ -848,9 +849,10 @@ The focus outline now settles between the poster and text.
|
|||
android:textColor="?attr/grayTextColor"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="normal"
|
||||
android:text="Episode 1022 will be released in" />
|
||||
tools:text="Episode 1022 will be released in" />
|
||||
|
||||
<TextView
|
||||
android:paddingEnd="5dp"
|
||||
android:paddingStart="5dp"
|
||||
android:gravity="center"
|
||||
android:id="@+id/result_next_airing_time"
|
||||
|
@ -903,6 +905,7 @@ The focus outline now settles between the poster and text.
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="0dp"
|
||||
android:clipToPadding="false"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:paddingBottom="100dp"
|
||||
tools:listitem="@layout/result_episode" />
|
||||
|
|
|
@ -497,7 +497,7 @@
|
|||
"language": "en",
|
||||
"name": "2Embed",
|
||||
"status": 1,
|
||||
"url": "https://www.2embed.to"
|
||||
"url": "https://2embed.org"
|
||||
},
|
||||
"UakinoProvider": {
|
||||
"language": "uk",
|
||||
|
|
|
@ -241,8 +241,8 @@
|
|||
},
|
||||
"TwoEmbedProvider": {
|
||||
"name": "2Embed",
|
||||
"url": "https://www.2embed.ru",
|
||||
"status": 0
|
||||
"url": "https://2embed.org",
|
||||
"status": 1
|
||||
},
|
||||
"VMoveeProvider": {
|
||||
"name": "VMovee",
|
||||
|
|
Loading…
Reference in a new issue