downloads playable

This commit is contained in:
LagradOst 2021-07-19 15:19:47 +02:00
parent 3bf76a9563
commit 5ffb53bf1c
11 changed files with 405 additions and 175 deletions

View file

@ -1,25 +1,29 @@
package com.lagradost.cloudstream3 package com.lagradost.cloudstream3
import android.R.attr
import android.app.PictureInPictureParams import android.app.PictureInPictureParams
import android.content.ComponentName import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.NavOptions
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import com.google.android.gms.cast.framework.CastButtonFactory import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import com.lagradost.cloudstream3.UIHelper.checkWrite import com.lagradost.cloudstream3.UIHelper.checkWrite
import com.lagradost.cloudstream3.UIHelper.getResourceColor
import com.lagradost.cloudstream3.UIHelper.hasPIPPermission import com.lagradost.cloudstream3.UIHelper.hasPIPPermission
import com.lagradost.cloudstream3.UIHelper.isUsingMobileData import com.lagradost.cloudstream3.UIHelper.isUsingMobileData
import com.lagradost.cloudstream3.UIHelper.requestRW import com.lagradost.cloudstream3.UIHelper.requestRW
import com.lagradost.cloudstream3.UIHelper.shouldShowPIPMode import com.lagradost.cloudstream3.UIHelper.shouldShowPIPMode
import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver
import com.lagradost.cloudstream3.ui.download.DownloadChildFragment
import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.getKeys import com.lagradost.cloudstream3.utils.DataStore.getKeys
import com.lagradost.cloudstream3.utils.DataStore.removeKey import com.lagradost.cloudstream3.utils.DataStore.removeKey
@ -54,6 +58,7 @@ class MainActivity : AppCompatActivity() {
var canShowPipMode: Boolean = false var canShowPipMode: Boolean = false
var isInPIPMode: Boolean = false var isInPIPMode: Boolean = false
lateinit var mainContext: MainActivity lateinit var mainContext: MainActivity
lateinit var navOptions: NavOptions
//https://github.com/anggrayudi/SimpleStorage/blob/4eb6306efb6cdfae4e34f170c8b9d4e135b04d51/sample/src/main/java/com/anggrayudi/storage/sample/activity/MainActivity.kt#L624 //https://github.com/anggrayudi/SimpleStorage/blob/4eb6306efb6cdfae4e34f170c8b9d4e135b04d51/sample/src/main/java/com/anggrayudi/storage/sample/activity/MainActivity.kt#L624
const val REQUEST_CODE_STORAGE_ACCESS = 1 const val REQUEST_CODE_STORAGE_ACCESS = 1
@ -89,6 +94,17 @@ class MainActivity : AppCompatActivity() {
it.isVisible it.isVisible
} }
if (currentFragment is NavHostFragment) {
val child = currentFragment.childFragmentManager.fragments.last {
it.isVisible
}
if (child is DownloadChildFragment) {
val navController = findNavController(R.id.nav_host_fragment)
navController.navigate(R.id.navigation_downloads, Bundle(), navOptions)
return true
}
}
if (currentFragment != null && supportFragmentManager.fragments.size > 2) { if (currentFragment != null && supportFragmentManager.fragments.size > 2) {
//MainActivity.showNavbar() //MainActivity.showNavbar()
supportFragmentManager.beginTransaction() supportFragmentManager.beginTransaction()
@ -146,15 +162,35 @@ class MainActivity : AppCompatActivity() {
hasPIPPermission() // CHECK IF FEATURE IS ENABLED IN SETTINGS hasPIPPermission() // CHECK IF FEATURE IS ENABLED IN SETTINGS
val navController = findNavController(R.id.nav_host_fragment) val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations. navOptions = NavOptions.Builder()
val appBarConfiguration = AppBarConfiguration( .setLaunchSingleTop(true)
setOf( .setEnterAnim(R.anim.nav_enter_anim)
R.id.navigation_home, R.id.navigation_search, R.id.navigation_notifications .setExitAnim(R.anim.nav_exit_anim)
) .setPopEnterAnim(R.anim.nav_pop_enter)
) .setPopExitAnim(R.anim.nav_pop_exit)
//setupActionBarWithNavController(navController, appBarConfiguration) .setPopUpTo(navController.graph.startDestination, false)
navView.setupWithNavController(navController) .build()
navView.setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
// R.id.navigation_home -> {
// navController.navigate(R.id.navigation_home, null, navOptions)
//}
R.id.navigation_search -> {
navController.navigate(R.id.navigation_search, null, navOptions)
}
R.id.navigation_downloads -> {
navController.navigate(R.id.navigation_downloads, null, navOptions)
}
R.id.navigation_settings -> {
navController.navigate(R.id.navigation_settings, null, navOptions)
}
}
true
}
navView.itemRippleColor = ColorStateList.valueOf(getResourceColor(R.attr.colorPrimary, 0.1f))
if (!checkWrite()) { if (!checkWrite()) {
requestRW() requestRW()

View file

@ -19,6 +19,7 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.view.ContextThemeWrapper
@ -26,6 +27,10 @@ import androidx.appcompat.view.menu.MenuBuilder
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.alpha
import androidx.core.graphics.blue
import androidx.core.graphics.green
import androidx.core.graphics.red
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.gms.cast.framework.CastContext import com.google.android.gms.cast.framework.CastContext
@ -59,6 +64,20 @@ object UIHelper {
1337) 1337)
} }
@ColorInt
fun Context.getResourceColor(@AttrRes resource: Int, alphaFactor: Float = 1f): Int {
val typedArray = obtainStyledAttributes(intArrayOf(resource))
val color = typedArray.getColor(0, 0)
typedArray.recycle()
if (alphaFactor < 1f) {
val alpha = (color.alpha * alphaFactor).roundToInt()
return Color.argb(alpha, color.red, color.green, color.blue)
}
return color
}
fun AppCompatActivity.loadResult(url: String, slug: String, apiName: String) { fun AppCompatActivity.loadResult(url: String, slug: String, apiName: String) {
this.runOnUiThread { this.runOnUiThread {
viewModelStore.clear() viewModelStore.clear()

View file

@ -4,14 +4,20 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.ui.player.PlayerData
import com.lagradost.cloudstream3.ui.player.PlayerFragment
import com.lagradost.cloudstream3.ui.player.UriData
import com.lagradost.cloudstream3.ui.result.getRealPosition
import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.getKeys import com.lagradost.cloudstream3.utils.DataStore.getKeys
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.cloudstream3.utils.VideoDownloadManager import com.lagradost.cloudstream3.utils.VideoDownloadManager
import kotlinx.android.synthetic.main.fragment_child_downloads.* import kotlinx.android.synthetic.main.fragment_child_downloads.*
@ -68,6 +74,11 @@ class DownloadChildFragment : Fragment() {
} }
context?.fixPaddingStatusbar(download_child_root) context?.fixPaddingStatusbar(download_child_root)
download_child_toolbar.title = name
download_child_toolbar.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24)
download_child_toolbar.setNavigationOnClickListener {
activity?.onBackPressed()
}
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> = val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
DownloadChildAdapter( DownloadChildAdapter(
@ -77,6 +88,28 @@ class DownloadChildFragment : Fragment() {
val info = val info =
VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(requireContext(), click.data.id) VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(requireContext(), click.data.id)
?: return@DownloadChildAdapter ?: return@DownloadChildAdapter
(requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.enter_anim,
R.anim.exit_anim,
R.anim.pop_enter,
R.anim.pop_exit
)
.add(
R.id.homeRoot,
PlayerFragment.newInstance(
UriData(
info.path.toString(),
click.data.id,
name ?: "null",
click.data.episode,
click.data.season
),
context?.getViewPos(click.data.id)?.position ?: 0
)
)
.commit()
} }
} }
download_child_list.adapter = adapter download_child_list.adapter = adapter

View file

@ -8,8 +8,10 @@ import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.MainActivity
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.isMovieType import com.lagradost.cloudstream3.isMovieType
@ -64,6 +66,7 @@ class DownloadFragment : Fragment() {
observe(downloadsViewModel.downloadBytes) { observe(downloadsViewModel.downloadBytes) {
download_app_txt?.text = "App • ${getBytesAsText(it)}GB" download_app_txt?.text = "App • ${getBytesAsText(it)}GB"
download_app?.setLayoutWidth(it) download_app?.setLayoutWidth(it)
download_storage_appbar?.visibility = View.VISIBLE
} }
return inflater.inflate(R.layout.fragment_downloads, container, false) return inflater.inflate(R.layout.fragment_downloads, container, false)
} }
@ -79,10 +82,11 @@ class DownloadFragment : Fragment() {
} }
else { else {
val folder = getFolderName(DOWNLOAD_EPISODE_CACHE, click.data.id.toString()) val folder = getFolderName(DOWNLOAD_EPISODE_CACHE, click.data.id.toString())
activity?.supportFragmentManager?.beginTransaction() val navController = activity?.findNavController(R.id.nav_host_fragment)
?.setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim, R.anim.pop_enter, R.anim.pop_exit) navController?.navigate(R.id.navigation_download_child, Bundle().apply {
?.add(R.id.homeRoot, DownloadChildFragment.newInstance(click.data.name, folder)) putString("folder", folder)
?.commit() putString("name", click.data.name)
})
} }
} }
download_list.adapter = adapter download_list.adapter = adapter

View file

@ -39,6 +39,7 @@ import androidx.transition.TransitionManager
import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.readValue
import com.google.android.exoplayer2.* import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.C.TIME_UNSET import com.google.android.exoplayer2.C.TIME_UNSET
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
@ -71,6 +72,7 @@ import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeDirectly import com.lagradost.cloudstream3.mvvm.observeDirectly
import com.lagradost.cloudstream3.ui.result.ResultEpisode import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.ui.result.ResultViewModel import com.lagradost.cloudstream3.ui.result.ResultViewModel
import com.lagradost.cloudstream3.utils.AppUtils.getVideoContentUri
import com.lagradost.cloudstream3.utils.CastHelper.startCast import com.lagradost.cloudstream3.utils.CastHelper.startCast
import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStore.setKey
@ -81,7 +83,6 @@ import com.lagradost.cloudstream3.utils.getId
import kotlinx.android.synthetic.main.fragment_player.* import kotlinx.android.synthetic.main.fragment_player.*
import kotlinx.android.synthetic.main.player_custom_layout.* import kotlinx.android.synthetic.main.player_custom_layout.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.io.File
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSession import javax.net.ssl.SSLSession
@ -131,6 +132,14 @@ data class PlayerData(
val mirrorId: Int, val mirrorId: Int,
) )
data class UriData(
val uri: String,
val id: Int?,
val name: String,
val episode: Int?,
val season: Int?,
)
// YE, I KNOW, THIS COULD BE HANDLED A LOT BETTER // YE, I KNOW, THIS COULD BE HANDLED A LOT BETTER
class PlayerFragment : Fragment() { class PlayerFragment : Fragment() {
private var isCurrentlyPlaying: Boolean = false private var isCurrentlyPlaying: Boolean = false
@ -143,6 +152,9 @@ class PlayerFragment : Fragment() {
private var isPlayerPlaying = true private var isPlayerPlaying = true
private lateinit var viewModel: ResultViewModel private lateinit var viewModel: ResultViewModel
private lateinit var playerData: PlayerData private lateinit var playerData: PlayerData
private lateinit var uriData: UriData
private var isDownloadedFile = false
private var downloadId = 0
private var isLoading = true private var isLoading = true
private var isShowing = true private var isShowing = true
private lateinit var exoPlayer: SimpleExoPlayer private lateinit var exoPlayer: SimpleExoPlayer
@ -598,14 +610,31 @@ class PlayerFragment : Fragment() {
} }
} }
} }
fun newInstance(uriData: UriData, startPos: Long? = null) =
PlayerFragment().apply {
arguments = Bundle().apply {
//println(data)
putString("uriData", mapper.writeValueAsString(uriData))
if (startPos != null) {
putLong(STATE_RESUME_POSITION, startPos)
}
}
}
} }
private fun savePos() { private fun savePos() {
if (this::exoPlayer.isInitialized) { if (this::exoPlayer.isInitialized) {
if (exoPlayer.duration > 0 && exoPlayer.currentPosition > 0) { if (exoPlayer.duration > 0 && exoPlayer.currentPosition > 0) {
context?.let { ctx -> context?.let { ctx ->
ctx.setViewPos(getEpisode()?.id, exoPlayer.currentPosition, exoPlayer.duration) ctx.setViewPos(
viewModel.reloadEpisodes(ctx) if (isDownloadedFile) uriData.id else getEpisode()?.id,
exoPlayer.currentPosition,
exoPlayer.duration
)
if (!isDownloadedFile)
viewModel.reloadEpisodes(ctx)
} }
} }
} }
@ -774,7 +803,7 @@ class PlayerFragment : Fragment() {
navigationBarHeight = requireContext().getNavigationBarHeight() navigationBarHeight = requireContext().getNavigationBarHeight()
statusBarHeight = requireContext().getStatusBarHeight() statusBarHeight = requireContext().getStatusBarHeight()
if (activity?.isCastApiAvailable() == true) { if (activity?.isCastApiAvailable() == true && !isDownloadedFile) {
CastButtonFactory.setUpMediaRouteButton(activity, player_media_route_button) CastButtonFactory.setUpMediaRouteButton(activity, player_media_route_button)
val castContext = CastContext.getSharedInstance(requireContext()) val castContext = CastContext.getSharedInstance(requireContext())
@ -852,14 +881,24 @@ class PlayerFragment : Fragment() {
} }
} }
isDownloadedFile = false
arguments?.getString("uriData")?.let {
uriData = mapper.readValue(it)
isDownloadedFile = true
}
arguments?.getString("data")?.let { arguments?.getString("data")?.let {
playerData = mapper.readValue(it, PlayerData::class.java) playerData = mapper.readValue(it)
} }
arguments?.getLong(STATE_RESUME_POSITION)?.let { arguments?.getLong(STATE_RESUME_POSITION)?.let {
playbackPosition = it playbackPosition = it
} }
sources_btt.visibility =
if (isDownloadedFile) View.GONE else View.VISIBLE
player_media_route_button.visibility =
if (isDownloadedFile) View.GONE else View.VISIBLE
if (savedInstanceState != null) { if (savedInstanceState != null) {
currentWindow = savedInstanceState.getInt(STATE_RESUME_WINDOW) currentWindow = savedInstanceState.getInt(STATE_RESUME_WINDOW)
playbackPosition = savedInstanceState.getLong(STATE_RESUME_POSITION) playbackPosition = savedInstanceState.getLong(STATE_RESUME_POSITION)
@ -883,60 +922,61 @@ class PlayerFragment : Fragment() {
android.provider.Settings.System.CONTENT_URI, true, volumeObserver android.provider.Settings.System.CONTENT_URI, true, volumeObserver
) )
viewModel = ViewModelProvider(requireActivity()).get(ResultViewModel::class.java) if (!isDownloadedFile) {
viewModel = ViewModelProvider(requireActivity()).get(ResultViewModel::class.java)
observeDirectly(viewModel.episodes) { _episodes -> observeDirectly(viewModel.episodes) { _episodes ->
episodes = _episodes episodes = _episodes
if (isLoading) { if (isLoading) {
if (playerData.episodeIndex > 0 && playerData.episodeIndex < episodes.size) { if (playerData.episodeIndex > 0 && playerData.episodeIndex < episodes.size) {
} else { } else {
// WHAT THE FUCK DID YOU DO // WHAT THE FUCK DID YOU DO
}
} }
} }
}
observe(viewModel.apiName) { observe(viewModel.apiName) {
apiName = it apiName = it
} }
overlay_loading_skip_button?.alpha = 0.5f overlay_loading_skip_button?.alpha = 0.5f
observeDirectly(viewModel.allEpisodes) { _allEpisodes -> observeDirectly(viewModel.allEpisodes) { _allEpisodes ->
allEpisodes = _allEpisodes allEpisodes = _allEpisodes
val current = getUrls() val current = getUrls()
if (current != null) { if (current != null) {
if (current.isNotEmpty()) { if (current.isNotEmpty()) {
overlay_loading_skip_button?.alpha = 1f overlay_loading_skip_button?.alpha = 1f
} else {
overlay_loading_skip_button?.alpha = 0.5f
}
} else { } else {
overlay_loading_skip_button?.alpha = 0.5f overlay_loading_skip_button?.alpha = 0.5f
} }
} else {
overlay_loading_skip_button?.alpha = 0.5f
} }
}
observeDirectly(viewModel.allEpisodesSubs) { _allEpisodesSubs -> observeDirectly(viewModel.allEpisodesSubs) { _allEpisodesSubs ->
allEpisodesSubs = _allEpisodesSubs allEpisodesSubs = _allEpisodesSubs
} }
observeDirectly(viewModel.resultResponse) { data -> observeDirectly(viewModel.resultResponse) { data ->
when (data) { when (data) {
is Resource.Success -> { is Resource.Success -> {
val d = data.value val d = data.value
if (d is LoadResponse) { if (d is LoadResponse) {
localData = d localData = d
currentPoster = d.posterUrl currentPoster = d.posterUrl
currentHeaderName = d.name currentHeaderName = d.name
currentIsMovie = !d.isEpisodeBased() currentIsMovie = !d.isEpisodeBased()
}
}
is Resource.Failure -> {
//WTF, HOW DID YOU EVEN GET HERE
} }
} }
is Resource.Failure -> {
//WTF, HOW DID YOU EVEN GET HERE
}
} }
} }
val fastForwardTime = settingsManager.getInt("fast_forward_button_time", 10) val fastForwardTime = settingsManager.getInt("fast_forward_button_time", 10)
exo_rew_text.text = fastForwardTime.toString() exo_rew_text.text = fastForwardTime.toString()
exo_ffwd_text.text = fastForwardTime.toString() exo_ffwd_text.text = fastForwardTime.toString()
@ -1193,7 +1233,7 @@ class PlayerFragment : Fragment() {
} }
private fun hasNextEpisode(): Boolean { private fun hasNextEpisode(): Boolean {
return episodes.size > playerData.episodeIndex + 1 return !isDownloadedFile && episodes.size > playerData.episodeIndex + 1 // TODO FIX DOWNLOADS NEXT EPISODE
} }
private var isCurrentlySkippingEp = false private var isCurrentlySkippingEp = false
@ -1335,8 +1375,8 @@ class PlayerFragment : Fragment() {
private val updateProgressAction = Runnable { updateProgressBar() }*/ private val updateProgressAction = Runnable { updateProgressBar() }*/
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun initPlayer(currentUrl: ExtractorLink?) { fun initPlayer(currentUrl: ExtractorLink?, uri: String? = null) {
if (currentUrl == null) return if (currentUrl == null && uri == null) return
isCurrentlyPlaying = true isCurrentlyPlaying = true
hasUsedFirstRender = false hasUsedFirstRender = false
@ -1347,9 +1387,9 @@ class PlayerFragment : Fragment() {
exoPlayer.release() exoPlayer.release()
} }
val isOnline = val isOnline =
currentUrl.url.startsWith("https://") || currentUrl.url.startsWith("http://") currentUrl != null && (currentUrl.url.startsWith("https://") || currentUrl.url.startsWith("http://"))
if (settingsManager.getBoolean("ignore_ssl", true)) { if (settingsManager.getBoolean("ignore_ssl", true) && !isDownloadedFile) {
// Disables ssl check // Disables ssl check
val sslContext: SSLContext = SSLContext.getInstance("TLS") val sslContext: SSLContext = SSLContext.getInstance("TLS")
sslContext.init(null, arrayOf(SSLTrustManager()), java.security.SecureRandom()) sslContext.init(null, arrayOf(SSLTrustManager()), java.security.SecureRandom())
@ -1367,7 +1407,8 @@ class PlayerFragment : Fragment() {
/*FastAniApi.currentHeaders?.forEach { /*FastAniApi.currentHeaders?.forEach {
dataSource.setRequestProperty(it.key, it.value) dataSource.setRequestProperty(it.key, it.value)
}*/ }*/
dataSource.setRequestProperty("Referer", currentUrl.referer) if (currentUrl != null)
dataSource.setRequestProperty("Referer", currentUrl.referer)
dataSource dataSource
} else { } else {
DefaultDataSourceFactory(requireContext(), USER_AGENT).createDataSource() DefaultDataSourceFactory(requireContext(), USER_AGENT).createDataSource()
@ -1375,15 +1416,31 @@ class PlayerFragment : Fragment() {
} }
} }
val mimeType = if (currentUrl.isM3u8) MimeTypes.APPLICATION_M3U8 else MimeTypes.APPLICATION_MP4 val mimeType =
if (currentUrl == null && uri != null)
MimeTypes.APPLICATION_MP4 else
if (currentUrl?.isM3u8 == true)
MimeTypes.APPLICATION_M3U8
else
MimeTypes.APPLICATION_MP4
val mediaItemBuilder = MediaItem.Builder() val mediaItemBuilder = MediaItem.Builder()
//Replace needed for android 6.0.0 https://github.com/google/ExoPlayer/issues/5983 //Replace needed for android 6.0.0 https://github.com/google/ExoPlayer/issues/5983
.setMimeType(mimeType) .setMimeType(mimeType)
if (isOnline) { if (currentUrl != null) {
mediaItemBuilder.setUri(currentUrl.url) mediaItemBuilder.setUri(currentUrl.url)
} else { } else if (uri != null) {
mediaItemBuilder.setUri(Uri.fromFile(File(currentUrl.url))) val uriPrimary = Uri.parse(uri)
if (uriPrimary.scheme == "content") {
mediaItemBuilder.setUri(uriPrimary)
// video_title?.text = uriPrimary.toString()
} else {
//mediaItemBuilder.setUri(Uri.parse(currentUrl.url))
val realUri = getVideoContentUri(requireContext(), uri)
// video_title?.text = uri.toString()
mediaItemBuilder.setUri(realUri)
}
} }
val mediaItem = mediaItemBuilder.build() val mediaItem = mediaItemBuilder.build()
@ -1401,7 +1458,7 @@ class PlayerFragment : Fragment() {
.setTrackSelector(trackSelector) .setTrackSelector(trackSelector)
_exoPlayer.setMediaSourceFactory(DefaultMediaSourceFactory(CustomFactory())) _exoPlayer.setMediaSourceFactory(DefaultMediaSourceFactory(CustomFactory()))
println("START POS: " + playbackPosition)
exoPlayer = _exoPlayer.build().apply { exoPlayer = _exoPlayer.build().apply {
playWhenReady = isPlayerPlaying playWhenReady = isPlayerPlaying
seekTo(currentWindow, playbackPosition) seekTo(currentWindow, playbackPosition)
@ -1431,24 +1488,38 @@ class PlayerFragment : Fragment() {
exoPlayer.playbackParameters = PlaybackParameters(playbackSpeed) exoPlayer.playbackParameters = PlaybackParameters(playbackSpeed)
player_speed_text?.text = "Speed (${playbackSpeed}x)".replace(".0x", "x") player_speed_text?.text = "Speed (${playbackSpeed}x)".replace(".0x", "x")
if (localData != null) { var hName: String? = null
var epEpisode: Int? = null
var epSeason: Int? = null
var isEpisodeBased = true
if (isDownloadedFile) {
hName = uriData.name
epEpisode = uriData.episode
epSeason = uriData.season
isEpisodeBased = epEpisode != null
video_title_rez?.text = ""
} else if (localData != null && currentUrl != null) {
val data = localData!! val data = localData!!
val localEpisode = getEpisode() val localEpisode = getEpisode()
if (localEpisode != null) { if (localEpisode != null) {
val episode = localEpisode.episode epEpisode = localEpisode.episode
val season: Int? = localEpisode.season epSeason = localEpisode.season
val isEpisodeBased = data.isEpisodeBased() hName = data.name
video_title?.text = data.name + isEpisodeBased = data.isEpisodeBased()
if (isEpisodeBased)
if (season == null)
" - Episode $episode"
else
" \"S${season}:E${episode}\""
else ""
video_title_rez?.text = currentUrl.name video_title_rez?.text = currentUrl.name
} }
} }
//TODO FIX
video_title?.text = hName +
if (isEpisodeBased)
if (epSeason == null)
" - Episode $epEpisode"
else
" \"S${epSeason}:E${epEpisode}\""
else ""
/* /*
exo_remaining.text = Util.getStringForTime(formatBuilder, exo_remaining.text = Util.getStringForTime(formatBuilder,
formatter, formatter,
@ -1465,8 +1536,10 @@ class PlayerFragment : Fragment() {
val height = exoPlayer.videoFormat?.height val height = exoPlayer.videoFormat?.height
val width = exoPlayer.videoFormat?.width val width = exoPlayer.videoFormat?.width
video_title_rez?.text = video_title_rez?.text =
if (height == null || width == null) currentUrl.name else "${currentUrl.name} - ${width}x${height}" if (height == null || width == null) currentUrl?.name
?: "" else if (isDownloadedFile) "${width}x${height}" else "${currentUrl?.name} - ${width}x${height}"
if (!hasUsedFirstRender) { // DON'T WANT TO SET MULTIPLE MESSAGES if (!hasUsedFirstRender) { // DON'T WANT TO SET MULTIPLE MESSAGES
changeSkip() changeSkip()
@ -1521,11 +1594,11 @@ class PlayerFragment : Fragment() {
} }
override fun onPlayerError(error: ExoPlaybackException) { override fun onPlayerError(error: ExoPlaybackException) {
println("CURRENT URL: " + currentUrl.url) println("CURRENT URL: " + currentUrl?.url ?: uri)
// Lets pray this doesn't spam Toasts :) // Lets pray this doesn't spam Toasts :)
when (error.type) { when (error.type) {
ExoPlaybackException.TYPE_SOURCE -> { ExoPlaybackException.TYPE_SOURCE -> {
if (currentUrl.url != "") { if (currentUrl?.url != "") {
Toast.makeText( Toast.makeText(
activity, activity,
"Source error\n" + error.sourceException.message, "Source error\n" + error.sourceException.message,
@ -1565,6 +1638,9 @@ class PlayerFragment : Fragment() {
//http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4 //http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun initPlayer() { private fun initPlayer() {
if (isDownloadedFile) {
initPlayer(null, uriData.uri)
}
println("INIT PLAYER") println("INIT PLAYER")
view?.setOnTouchListener { _, _ -> return@setOnTouchListener true } // VERY IMPORTANT https://stackoverflow.com/questions/28818926/prevent-clicking-on-a-button-in-an-activity-while-showing-a-fragment view?.setOnTouchListener { _, _ -> return@setOnTouchListener true } // VERY IMPORTANT https://stackoverflow.com/questions/28818926/prevent-clicking-on-a-button-in-an-activity-while-showing-a-fragment
val tempCurrentUrls = getUrls() val tempCurrentUrls = getUrls()

View file

@ -0,0 +1,26 @@
package com.lagradost.cloudstream3.utils
import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.provider.MediaStore
object AppUtils {
fun getVideoContentUri(context: Context, videoFilePath: String): Uri? {
val cursor = context.contentResolver.query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI, arrayOf(MediaStore.Video.Media._ID),
MediaStore.Video.Media.DATA + "=? ", arrayOf(videoFilePath), null
)
return if (cursor != null && cursor.moveToFirst()) {
val id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID))
cursor.close()
Uri.withAppendedPath(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "" + id)
} else {
val values = ContentValues()
values.put(MediaStore.Video.Media.DATA, videoFilePath)
context.contentResolver.insert(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values
)
}
}
}

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:background="@color/bitDarkerGrayBackground" android:background="?attr/darkBackground"
android:id="@+id/download_child_root" android:id="@+id/download_child_root"
android:orientation="vertical" android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
@ -9,11 +9,29 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".ui.download.DownloadFragment"> tools:context=".ui.download.DownloadFragment">
<com.google.android.material.appbar.AppBarLayout
android:background="@android:color/transparent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/download_child_toolbar"
android:paddingTop="@dimen/navbarHeight"
tools:title="Overlord"
android:background="?attr/darkBackground"
app:navigationIconTint="?attr/iconColor"
app:titleTextColor="?attr/textColor"
app:layout_scrollFlags="scroll|enterAlways"
android:layout_width="match_parent" android:layout_height="wrap_content">
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:layout_margin="10dp" android:background="?attr/bitDarkerGrayBackground"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:padding="10dp"
tools:listitem="@layout/download_child_episode" tools:listitem="@layout/download_child_episode"
android:id="@+id/download_child_list" android:id="@+id/download_child_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView> </androidx.recyclerview.widget.RecyclerView>
</LinearLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/download_root" android:id="@+id/download_root"
android:orientation="vertical" android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
@ -7,100 +7,114 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/darkBackground"
tools:context=".ui.download.DownloadFragment"> tools:context=".ui.download.DownloadFragment">
<LinearLayout <com.google.android.material.appbar.AppBarLayout
android:layout_margin="10dp" android:background="?attr/darkBackground"
android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<TextView <!--
android:layout_marginBottom="5dp" For Scroll add to LinearLayout
android:text="@string/download_storage_text" app:layout_scrollFlags="scroll|enterAlways"
android:layout_width="wrap_content" -->
android:layout_height="wrap_content">
</TextView>
<LinearLayout <LinearLayout
android:layout_marginBottom="5dp" android:id="@+id/download_storage_appbar"
android:layout_width="fill_parent" android:visibility="gone"
android:layout_height="12dp" android:layout_margin="10dp"
android:orientation="horizontal"> android:orientation="vertical"
<View
android:layout_weight="0.5"
android:id="@+id/download_used"
android:background="@color/usedStorageColor"
android:layout_width="0dp"
android:layout_height="match_parent"/>
<View
android:id="@+id/download_app"
android:layout_weight="0.10"
android:background="?attr/colorPrimary"
android:layout_width="0dp"
android:layout_height="match_parent"/>
<View
android:id="@+id/download_free"
android:layout_weight="0.10"
android:background="@color/freeStorageColor"
android:layout_width="0dp"
android:layout_height="match_parent"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<View <TextView
android:layout_marginEnd="5dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:layout_gravity="center_vertical" android:text="@string/download_storage_text"
android:background="@color/usedStorageColor"
android:layout_width="10dp"
android:layout_height="10dp"/>
<TextView
android:id="@+id/download_used_txt"
android:layout_gravity="center_vertical"
tools:text="Used • 30.58GB"
android:textSize="12sp"
android:textColor="?attr/textColor"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"/> android:layout_height="wrap_content">
</TextView>
<LinearLayout
android:layout_marginBottom="5dp"
android:layout_width="fill_parent"
android:layout_height="12dp"
android:orientation="horizontal">
<View
android:layout_weight="0.5"
android:id="@+id/download_used"
android:background="@color/usedStorageColor"
android:layout_width="0dp"
android:layout_height="match_parent"/>
<View
android:id="@+id/download_app"
android:layout_weight="0.10"
android:background="?attr/colorPrimary"
android:layout_width="0dp"
android:layout_height="match_parent"/>
<View
android:id="@+id/download_free"
android:layout_weight="0.10"
android:background="@color/freeStorageColor"
android:layout_width="0dp"
android:layout_height="match_parent"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:layout_marginEnd="5dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_gravity="center_vertical"
android:background="@color/usedStorageColor"
android:layout_width="10dp"
android:layout_height="10dp"/>
<TextView
android:id="@+id/download_used_txt"
android:layout_gravity="center_vertical"
tools:text="Used • 30.58GB"
android:textSize="12sp"
android:textColor="?attr/textColor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<View <View
android:layout_margin="5dp" android:layout_margin="5dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
android:layout_width="10dp" android:layout_width="10dp"
android:layout_height="10dp"/> android:layout_height="10dp"/>
<TextView <TextView
android:id="@+id/download_app_txt" android:id="@+id/download_app_txt"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
tools:text="App • 30.58GB" tools:text="App • 30.58GB"
android:textSize="12sp" android:textSize="12sp"
android:textColor="?attr/textColor" android:textColor="?attr/textColor"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
<View <View
android:layout_margin="5dp" android:layout_margin="5dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:background="@color/freeStorageColor" android:background="@color/freeStorageColor"
android:layout_width="10dp" android:layout_width="10dp"
android:layout_height="10dp"/> android:layout_height="10dp"/>
<TextView <TextView
android:id="@+id/download_free_txt" android:id="@+id/download_free_txt"
android:textSize="12sp" android:textSize="12sp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
tools:text="Free • 30.58GB" tools:text="Free • 30.58GB"
android:textColor="?attr/textColor" android:textColor="?attr/textColor"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> </com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:layout_margin="10dp" android:background="?attr/bitDarkerGrayBackground"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:padding="10dp"
android:id="@+id/download_list" android:id="@+id/download_list"
android:layout_width="match_parent" android:layout_width="match_parent"
tools:listitem="@layout/download_header_episode" tools:listitem="@layout/download_header_episode"
android:layout_height="wrap_content"> android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView> </androidx.recyclerview.widget.RecyclerView>
<TextView <TextView
android:id="@+id/text_no_downloads" android:id="@+id/text_no_downloads"
@ -115,4 +129,4 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/> app:layout_constraintBottom_toBottomOf="parent"/>
</LinearLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -8,9 +8,7 @@
android:id="@+id/searchRoot" android:id="@+id/searchRoot"
tools:context=".ui.search.SearchFragment" tools:context=".ui.search.SearchFragment"
android:orientation="vertical" android:orientation="vertical"
android:layout_marginTop="@dimen/navbarHeight" android:layout_marginTop="@dimen/navbarHeight">
android:background="@color/grayBackground">
<FrameLayout android:visibility="visible" <FrameLayout android:visibility="visible"
android:layout_margin="10dp" android:layout_margin="10dp"
android:background="@drawable/search_background" android:background="@drawable/search_background"

View file

@ -1,23 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<!--
<item <item
android:id="@+id/navigation_home" android:id="@+id/navigation_home"
android:icon="@drawable/ic_home_black_24dp" android:icon="@drawable/ic_home_black_24dp"
android:title="@string/title_home"/> android:title="@string/title_home"/>
-->
<item <item
android:id="@+id/navigation_search" android:id="@+id/navigation_search"
android:icon="@drawable/search_icon" android:icon="@drawable/search_icon"
android:title="@string/title_search"/> android:title="@string/title_search"/>
<item <item
android:id="@+id/navigation_notifications" android:id="@+id/navigation_downloads"
android:icon="@drawable/netflix_download" android:icon="@drawable/netflix_download"
android:title="@string/title_downloads"/> android:title="@string/title_downloads"/>
<item <item
android:id="@+id/navigation_settings" android:id="@+id/navigation_settings"
android:icon="@drawable/ic_outline_settings_24" android:icon="@drawable/ic_outline_settings_24"
android:title="@string/title_settings"/> android:title="@string/title_settings"/>
</menu> </menu>

View file

@ -18,7 +18,7 @@
tools:layout="@layout/fragment_search"/> tools:layout="@layout/fragment_search"/>
<fragment <fragment
android:id="@+id/navigation_notifications" android:id="@+id/navigation_downloads"
android:name="com.lagradost.cloudstream3.ui.download.DownloadFragment" android:name="com.lagradost.cloudstream3.ui.download.DownloadFragment"
android:label="@string/title_downloads" android:label="@string/title_downloads"
tools:layout="@layout/fragment_downloads"/> tools:layout="@layout/fragment_downloads"/>
@ -28,4 +28,11 @@
android:name="com.lagradost.cloudstream3.ui.settings.SettingsFragment" android:name="com.lagradost.cloudstream3.ui.settings.SettingsFragment"
android:label="@string/title_settings" android:label="@string/title_settings"
/> />
<fragment
android:id="@+id/navigation_download_child"
android:layout_height="match_parent"
android:name="com.lagradost.cloudstream3.ui.download.DownloadChildFragment"
android:label="@string/title_settings"
/>
</navigation> </navigation>