more fixes
This commit is contained in:
parent
9a50b85d85
commit
525a37475c
3 changed files with 105 additions and 16 deletions
|
|
@ -14,6 +14,9 @@ try {
|
||||||
}catch(IOException ignored){}
|
}catch(IOException ignored){}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
namespace 'com.deniscerri.ytdlnis'
|
||||||
|
compileSdk 33
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
debug {
|
debug {
|
||||||
storeFile file(properties["signingConfig.storeFile"])
|
storeFile file(properties["signingConfig.storeFile"])
|
||||||
|
|
@ -22,11 +25,10 @@ android {
|
||||||
keyPassword properties["signingConfig.keyPassword"]
|
keyPassword properties["signingConfig.keyPassword"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileSdkVersion 33
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.deniscerri.ytdl"
|
applicationId "com.deniscerri.ytdl"
|
||||||
minSdkVersion 23
|
minSdk 23
|
||||||
targetSdkVersion 33
|
targetSdk 33
|
||||||
versionCode project.versionCode
|
versionCode project.versionCode
|
||||||
versionName project.versionName
|
versionName project.versionName
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
@ -106,7 +108,6 @@ android {
|
||||||
composeOptions {
|
composeOptions {
|
||||||
kotlinCompilerExtensionVersion composeVer
|
kotlinCompilerExtensionVersion composeVer
|
||||||
}
|
}
|
||||||
namespace 'com.deniscerri.ytdlnis'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,12 @@ import android.graphics.Color
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.util.Log
|
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
import androidx.core.view.forEach
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.ChapterItem
|
import com.deniscerri.ytdlnis.database.models.ChapterItem
|
||||||
|
|
@ -23,13 +21,28 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.deniscerri.ytdlnis.util.InfoUtil
|
import com.deniscerri.ytdlnis.util.InfoUtil
|
||||||
import com.deniscerri.ytdlnis.util.UiUtil
|
import com.deniscerri.ytdlnis.util.UiUtil
|
||||||
|
import com.google.android.exoplayer2.DefaultLoadControl
|
||||||
|
import com.google.android.exoplayer2.DefaultRenderersFactory
|
||||||
|
import com.google.android.exoplayer2.DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER
|
||||||
import com.google.android.exoplayer2.ExoPlayer
|
import com.google.android.exoplayer2.ExoPlayer
|
||||||
import com.google.android.exoplayer2.MediaItem.fromUri
|
import com.google.android.exoplayer2.MediaItem.fromUri
|
||||||
import com.google.android.exoplayer2.Player
|
import com.google.android.exoplayer2.Player
|
||||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
import com.google.android.exoplayer2.database.DatabaseProvider
|
||||||
|
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
|
||||||
|
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo
|
||||||
|
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector
|
||||||
import com.google.android.exoplayer2.source.MediaSource
|
import com.google.android.exoplayer2.source.MediaSource
|
||||||
import com.google.android.exoplayer2.source.MergingMediaSource
|
import com.google.android.exoplayer2.source.MergingMediaSource
|
||||||
|
import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
||||||
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
||||||
import com.google.android.exoplayer2.ui.PlayerView
|
import com.google.android.exoplayer2.ui.PlayerView
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultDataSource
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
|
||||||
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSource
|
||||||
|
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor
|
||||||
|
import com.google.android.exoplayer2.upstream.cache.SimpleCache
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes
|
||||||
|
import com.google.android.exoplayer2.util.Util
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
|
|
@ -47,6 +60,7 @@ import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.File
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
@ -60,6 +74,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
private lateinit var player: Player
|
private lateinit var player: Player
|
||||||
|
|
||||||
private lateinit var cutSection : ConstraintLayout
|
private lateinit var cutSection : ConstraintLayout
|
||||||
|
private lateinit var durationText: TextView
|
||||||
private lateinit var progress : ProgressBar
|
private lateinit var progress : ProgressBar
|
||||||
private lateinit var rangeSlider : RangeSlider
|
private lateinit var rangeSlider : RangeSlider
|
||||||
private lateinit var fromTextInput : TextInputLayout
|
private lateinit var fromTextInput : TextInputLayout
|
||||||
|
|
@ -78,6 +93,8 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
private lateinit var chapters: List<ChapterItem>
|
private lateinit var chapters: List<ChapterItem>
|
||||||
private lateinit var selectedCuts: MutableList<String>
|
private lateinit var selectedCuts: MutableList<String>
|
||||||
|
|
||||||
|
private lateinit var cache: SimpleCache
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
@ -100,7 +117,52 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
behavior.peekHeight = displayMetrics.heightPixels / 2
|
behavior.peekHeight = displayMetrics.heightPixels / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
player = ExoPlayer.Builder(requireContext()).build()
|
val renderersFactory = DefaultRenderersFactory(requireContext().applicationContext)
|
||||||
|
.setEnableDecoderFallback(true)
|
||||||
|
.setExtensionRendererMode(EXTENSION_RENDERER_MODE_PREFER)
|
||||||
|
.setMediaCodecSelector { mimeType, requiresSecureDecoder, requiresTunnelingDecoder ->
|
||||||
|
var decoderInfos: MutableList<MediaCodecInfo> =
|
||||||
|
MediaCodecSelector.DEFAULT
|
||||||
|
.getDecoderInfos(
|
||||||
|
mimeType,
|
||||||
|
requiresSecureDecoder,
|
||||||
|
requiresTunnelingDecoder
|
||||||
|
)
|
||||||
|
if (MimeTypes.VIDEO_H264 == mimeType) {
|
||||||
|
// copy the list because MediaCodecSelector.DEFAULT returns an unmodifiable list
|
||||||
|
decoderInfos = ArrayList(decoderInfos)
|
||||||
|
decoderInfos.reverse()
|
||||||
|
}
|
||||||
|
decoderInfos
|
||||||
|
}
|
||||||
|
|
||||||
|
val trackSelector = DefaultTrackSelector()
|
||||||
|
val loadControl = DefaultLoadControl()
|
||||||
|
|
||||||
|
// Specify cache folder, my cache folder named media which is inside getCacheDir.
|
||||||
|
val cacheFolder = File(requireContext().cacheDir, "media")
|
||||||
|
|
||||||
|
// Specify cache size and removing policies
|
||||||
|
val cacheEvictor = LeastRecentlyUsedCacheEvictor(1 * 1024 * 1024) // My cache size will be 1MB and it will automatically remove least recently used files if the size is reached out.
|
||||||
|
|
||||||
|
// Build cache
|
||||||
|
val databaseProvider: DatabaseProvider = StandaloneDatabaseProvider(requireContext())
|
||||||
|
cache = SimpleCache(cacheFolder, cacheEvictor, databaseProvider)
|
||||||
|
|
||||||
|
// Build data source factory with cache enabled, if data is available in cache it will return immediately, otherwise it will open a new connection to get the data.
|
||||||
|
val defaultDataSourceFactory = DefaultDataSourceFactory(
|
||||||
|
requireContext(),
|
||||||
|
Util.getUserAgent(requireContext(), requireContext().getString(R.string.app_name))
|
||||||
|
)
|
||||||
|
|
||||||
|
val cacheDataSourceFactory = CacheDataSource.Factory()
|
||||||
|
.setCache(cache)
|
||||||
|
.setUpstreamDataSourceFactory(defaultDataSourceFactory)
|
||||||
|
|
||||||
|
player = ExoPlayer.Builder(requireContext(), renderersFactory)
|
||||||
|
.setTrackSelector(trackSelector)
|
||||||
|
.setLoadControl(loadControl)
|
||||||
|
.build()
|
||||||
val frame = view.findViewById<MaterialCardView>(R.id.frame_layout)
|
val frame = view.findViewById<MaterialCardView>(R.id.frame_layout)
|
||||||
val videoView = view.findViewById<PlayerView>(R.id.video_view)
|
val videoView = view.findViewById<PlayerView>(R.id.video_view)
|
||||||
videoView.player = player
|
videoView.player = player
|
||||||
|
|
@ -109,6 +171,8 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
|
|
||||||
//cut section
|
//cut section
|
||||||
cutSection = view.findViewById(R.id.cut_section)
|
cutSection = view.findViewById(R.id.cut_section)
|
||||||
|
durationText = view.findViewById(R.id.durationText)
|
||||||
|
durationText.text = ""
|
||||||
progress = view.findViewById(R.id.progress)
|
progress = view.findViewById(R.id.progress)
|
||||||
rangeSlider = view.findViewById(R.id.rangeSlider)
|
rangeSlider = view.findViewById(R.id.rangeSlider)
|
||||||
fromTextInput = view.findViewById(R.id.from_textinput)
|
fromTextInput = view.findViewById(R.id.from_textinput)
|
||||||
|
|
@ -154,16 +218,17 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
|
|
||||||
if (data.isEmpty()) throw Exception("No Streaming URL found!")
|
if (data.isEmpty()) throw Exception("No Streaming URL found!")
|
||||||
if (data.size == 2){
|
if (data.size == 2){
|
||||||
val audioSource: MediaSource =
|
val audioSource : MediaSource =
|
||||||
DefaultMediaSourceFactory(requireContext())
|
ProgressiveMediaSource.Factory(cacheDataSourceFactory)
|
||||||
.createMediaSource(fromUri(Uri.parse(data[0])))
|
.createMediaSource(fromUri(Uri.parse(data[0])))
|
||||||
val videoSource: MediaSource =
|
val videoSource: MediaSource =
|
||||||
DefaultMediaSourceFactory(requireContext())
|
ProgressiveMediaSource.Factory(cacheDataSourceFactory)
|
||||||
.createMediaSource(fromUri(Uri.parse(data[1])))
|
.createMediaSource(fromUri(Uri.parse(data[1])))
|
||||||
(player as ExoPlayer).setMediaSource(MergingMediaSource(videoSource, audioSource))
|
(player as ExoPlayer).setMediaSource(MergingMediaSource(videoSource, audioSource))
|
||||||
}else{
|
}else{
|
||||||
player.addMediaItem(fromUri(Uri.parse(data[0])))
|
player.addMediaItem(fromUri(Uri.parse(data[0])))
|
||||||
}
|
}
|
||||||
|
player.addMediaItem(fromUri(Uri.parse(data[0])))
|
||||||
|
|
||||||
progress.visibility = View.GONE
|
progress.visibility = View.GONE
|
||||||
populateSuggestedChapters()
|
populateSuggestedChapters()
|
||||||
|
|
@ -180,18 +245,20 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
//poll video progress
|
//poll video progress
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
videoProgress(player).collect {
|
videoProgress(player).collect {
|
||||||
val startTimestamp = (rangeSlider.valueFrom.toInt() * timeSeconds) / 100
|
val currentTime = infoUtil.formatIntegerDuration(it, Locale.US)
|
||||||
val endTimestamp = (rangeSlider.valueTo.toInt() * timeSeconds) / 100
|
durationText.text = "$currentTime / ${item.duration}"
|
||||||
|
val startTimestamp = convertStringToTimestamp(fromTextInput.editText!!.text.toString())
|
||||||
|
val endTimestamp = convertStringToTimestamp(toTextInput.editText!!.text.toString())
|
||||||
if (it >= endTimestamp){
|
if (it >= endTimestamp){
|
||||||
|
player.prepare()
|
||||||
player.seekTo((startTimestamp * 1000).toLong())
|
player.seekTo((startTimestamp * 1000).toLong())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
videoView.setOnClickListener {
|
videoView.setOnClickListener {
|
||||||
if (player.isPlaying) player.stop()
|
if (player.isPlaying) player.pause()
|
||||||
else {
|
else {
|
||||||
player.prepare()
|
|
||||||
player.play()
|
player.play()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -479,7 +546,6 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
private fun convertStringToTimestamp(duration: String): Int {
|
private fun convertStringToTimestamp(duration: String): Int {
|
||||||
return try {
|
return try {
|
||||||
val timeArray = duration.split(":")
|
val timeArray = duration.split(":")
|
||||||
Log.e("aa", timeArray.toString())
|
|
||||||
var timeSeconds = timeArray[timeArray.lastIndex].toInt()
|
var timeSeconds = timeArray[timeArray.lastIndex].toInt()
|
||||||
var times = 60
|
var times = 60
|
||||||
for (i in timeArray.lastIndex - 1 downTo 0) {
|
for (i in timeArray.lastIndex - 1 downTo 0) {
|
||||||
|
|
@ -506,6 +572,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
|
|
||||||
private fun cleanUp(){
|
private fun cleanUp(){
|
||||||
player.stop()
|
player.stop()
|
||||||
|
cache.release()
|
||||||
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("cutVideoSheet")!!).commit()
|
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("cutVideoSheet")!!).commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,27 @@
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/durationText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:background="#80000000"
|
||||||
|
android:clickable="false"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center"
|
||||||
|
android:maxLength="17"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:minWidth="30dp"
|
||||||
|
android:paddingHorizontal="5dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:cornerRadius="10dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/frame_layout"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/cut_section"
|
android:id="@+id/cut_section"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue