CHANGELOG
- I think i fixed pausing and resuming multiple downloads since it was doing some funky stuff - added rewind button - fixed play button appearing in cut section when the video is playing - fixed add extra command not working in the command template edit screen - removed "Downloading" word in the preferences screen. Its Downloads now - Show terminal preference was reversed so now its in correct behaviour - Fixed share card not showing up - Added embed metadata preference. Turning it off will remove any embedding metadata and parsing metadata commands
This commit is contained in:
parent
fecc38df82
commit
26ea19ab8c
34 changed files with 362 additions and 194 deletions
|
|
@ -10,7 +10,7 @@ def properties = new Properties()
|
|||
def versionMajor = 1
|
||||
def versionMinor = 6
|
||||
def versionPatch = 3
|
||||
def versionBuild = 13 // bump for dogfood builds, public betas, etc.
|
||||
def versionBuild = 15 // bump for dogfood builds, public betas, etc.
|
||||
def versionExt = ""
|
||||
|
||||
if (versionBuild > 0){
|
||||
|
|
@ -148,8 +148,8 @@ dependencies {
|
|||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||
implementation 'com.google.android.material:material:1.9.0-rc01'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
||||
implementation 'androidx.preference:preference:1.2.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.1'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$navVer"
|
||||
implementation "androidx.navigation:navigation-ui-ktx:$navVer"
|
||||
implementation 'androidx.core:core-ktx:1.10.1'
|
||||
|
|
|
|||
|
|
@ -147,10 +147,10 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
|||
|
||||
pauseButton.setOnClickListener {
|
||||
if (pauseButton.tag == ActiveDownloadAction.Pause){
|
||||
onItemClickListener.onPauseClick(item.id, ActiveDownloadAction.Pause)
|
||||
onItemClickListener.onPauseClick(item.id, ActiveDownloadAction.Pause, position)
|
||||
|
||||
}else{
|
||||
onItemClickListener.onPauseClick(item.id, ActiveDownloadAction.Resume)
|
||||
onItemClickListener.onPauseClick(item.id, ActiveDownloadAction.Resume, position)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -158,7 +158,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
|||
}
|
||||
interface OnItemClickListener {
|
||||
fun onCancelClick(itemID: Long)
|
||||
fun onPauseClick(itemID: Long, action: ActiveDownloadAction)
|
||||
fun onPauseClick(itemID: Long, action: ActiveDownloadAction, position: Int)
|
||||
fun onOutputClick(item: DownloadItem)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -132,13 +132,13 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
|
|||
|
||||
pauseButton.setOnClickListener {
|
||||
if (pauseButton.tag == ActiveDownloadAdapter.ActiveDownloadAction.Pause){
|
||||
onItemClickListener.onPauseClick(item.id, ActiveDownloadAdapter.ActiveDownloadAction.Pause)
|
||||
onItemClickListener.onPauseClick(item.id, ActiveDownloadAdapter.ActiveDownloadAction.Pause, position)
|
||||
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_play_arrow_white)
|
||||
if (progressBar.progress == 0) progressBar.isIndeterminate = false
|
||||
cancelButton.visibility = View.VISIBLE
|
||||
pauseButton.tag = ActiveDownloadAdapter.ActiveDownloadAction.Resume
|
||||
}else{
|
||||
onItemClickListener.onPauseClick(item.id, ActiveDownloadAdapter.ActiveDownloadAction.Resume)
|
||||
onItemClickListener.onPauseClick(item.id, ActiveDownloadAdapter.ActiveDownloadAction.Resume, position)
|
||||
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_pause_white)
|
||||
progressBar.isIndeterminate = true
|
||||
cancelButton.visibility = View.GONE
|
||||
|
|
@ -152,7 +152,7 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
|
|||
}
|
||||
interface OnItemClickListener {
|
||||
fun onCancelClick(itemID: Long)
|
||||
fun onPauseClick(itemID: Long, action: ActiveDownloadAdapter.ActiveDownloadAction)
|
||||
fun onPauseClick(itemID: Long, action: ActiveDownloadAdapter.ActiveDownloadAction, position: Int)
|
||||
fun onCardClick()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class TemplatesAdapter(onItemClickListener: OnItemClickListener, activity: Activ
|
|||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: CommandTemplate, newItem: CommandTemplate): Boolean {
|
||||
return oldItem.title == newItem.title && oldItem.content == newItem.content
|
||||
return oldItem.title == newItem.title && oldItem.content == newItem.content && oldItem.useAsExtraCommand == newItem.useAsExtraCommand
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ interface DownloadDao {
|
|||
@Query("SELECT * FROM downloads WHERE status='Active' or status='Paused'")
|
||||
fun getActiveDownloadsList() : List<DownloadItem>
|
||||
|
||||
@Query("SELECT * FROM downloads WHERE status='Active' or status='Queued' or status='Paused'")
|
||||
@Query("SELECT * FROM downloads WHERE status='Active' or status='Queued' or status='QueuedPaused' or status='Paused'")
|
||||
fun getActiveAndQueuedDownloadsList() : List<DownloadItem>
|
||||
|
||||
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id")
|
||||
@Query("SELECT * FROM downloads WHERE status='Queued' or status='QueuedPaused' ORDER BY downloadStartTime, id")
|
||||
fun getQueuedDownloads() : Flow<List<DownloadItem>>
|
||||
|
||||
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id")
|
||||
@Query("SELECT * FROM downloads WHERE status='Queued' or status='QueuedPaused' ORDER BY downloadStartTime, id")
|
||||
fun getQueuedDownloadsList() : List<DownloadItem>
|
||||
|
||||
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
|||
val savedDownloads : Flow<List<DownloadItem>> = downloadDao.getSavedDownloads()
|
||||
|
||||
enum class Status {
|
||||
Active, Paused, Queued, Error, Cancelled, Saved
|
||||
Active, Paused, Queued, QueuedPaused, Error, Cancelled, Saved
|
||||
}
|
||||
|
||||
suspend fun insert(item: DownloadItem) : Long {
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
repository.delete(item)
|
||||
}
|
||||
|
||||
fun updateDownload(item: DownloadItem) = viewModelScope.launch(Dispatchers.IO){
|
||||
suspend fun updateDownload(item: DownloadItem){
|
||||
repository.update(item)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,10 +51,7 @@ class ShareActivity : BaseActivity() {
|
|||
private lateinit var sharedPreferences: SharedPreferences
|
||||
private var quickDownload by Delegates.notNull<Boolean>()
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
finishAndRemoveTask()
|
||||
}
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -108,6 +105,8 @@ class ShareActivity : BaseActivity() {
|
|||
return
|
||||
}
|
||||
|
||||
runCatching { supportFragmentManager.popBackStack() }
|
||||
|
||||
quickDownload = intent.getBooleanExtra("quick_download", sharedPreferences.getBoolean("quick_download", false))
|
||||
|
||||
val loadingBottomSheet = BottomSheetDialog(this)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.media3.common.AudioAttributes
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.MediaItem.fromUri
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.datasource.DefaultDataSource
|
||||
import androidx.media3.datasource.cronet.CronetDataSource
|
||||
import androidx.media3.exoplayer.DefaultLoadControl
|
||||
|
|
@ -33,6 +34,7 @@ import com.deniscerri.ytdlnis.R
|
|||
import com.deniscerri.ytdlnis.database.models.ChapterItem
|
||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||
import com.deniscerri.ytdlnis.util.InfoUtil
|
||||
import com.deniscerri.ytdlnis.util.UiUtil.setTextAndRecalculateWidth
|
||||
import com.deniscerri.ytdlnis.util.VideoPlayerUtil
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
|
|
@ -68,6 +70,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private var urls
|
|||
private lateinit var durationText: TextView
|
||||
private lateinit var progress : ProgressBar
|
||||
private lateinit var pauseBtn : MaterialButton
|
||||
private lateinit var rewindBtn : MaterialButton
|
||||
private lateinit var muteBtn : MaterialButton
|
||||
private lateinit var rangeSlider : RangeSlider
|
||||
private lateinit var fromTextInput : TextInputLayout
|
||||
|
|
@ -119,6 +122,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private var urls
|
|||
durationText.text = ""
|
||||
progress = view.findViewById(R.id.progress)
|
||||
pauseBtn = view.findViewById(R.id.pause)
|
||||
rewindBtn = view.findViewById(R.id.rewind)
|
||||
muteBtn = view.findViewById(R.id.mute)
|
||||
rangeSlider = view.findViewById(R.id.rangeSlider)
|
||||
fromTextInput = view.findViewById(R.id.from_textinput)
|
||||
|
|
@ -211,14 +215,23 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private var urls
|
|||
}
|
||||
}
|
||||
|
||||
player.addListener(object : Player.Listener {
|
||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||
if (isPlaying){
|
||||
pauseBtn.visibility = View.GONE
|
||||
}else{
|
||||
pauseBtn.visibility = View.VISIBLE
|
||||
}
|
||||
super.onIsPlayingChanged(isPlaying)
|
||||
}
|
||||
})
|
||||
|
||||
videoView.setOnClickListener {
|
||||
if (player.isPlaying){
|
||||
player.pause()
|
||||
pauseBtn.visibility = View.VISIBLE
|
||||
}
|
||||
else {
|
||||
player.play()
|
||||
pauseBtn.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -232,12 +245,22 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private var urls
|
|||
}
|
||||
}
|
||||
|
||||
rewindBtn.setOnClickListener {
|
||||
try {
|
||||
val seconds = convertStringToTimestamp(fromTextInput.editText!!.text.toString())
|
||||
val endTimestamp = (rangeSlider.valueTo.toInt() * timeSeconds) / 100
|
||||
val startValue = (seconds.toFloat() / endTimestamp) * 100
|
||||
player.seekTo((((startValue * timeSeconds) / 100) * 1000).toLong())
|
||||
player.play()
|
||||
}catch (ignored: Exception) {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun initCutSection(){
|
||||
fromTextInput.editText!!.setText("0:00")
|
||||
toTextInput.editText!!.setText(item.duration)
|
||||
fromTextInput.editText!!.setTextAndRecalculateWidth("0:00")
|
||||
toTextInput.editText!!.setTextAndRecalculateWidth(item.duration)
|
||||
|
||||
rangeSlider.addOnSliderTouchListener(object : RangeSlider.OnSliderTouchListener {
|
||||
override fun onStartTrackingTouch(slider: RangeSlider) {
|
||||
|
|
@ -252,8 +275,8 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private var urls
|
|||
val startTimestampString = infoUtil.formatIntegerDuration(startTimestamp, Locale.US)
|
||||
val endTimestampString = infoUtil.formatIntegerDuration(endTimestamp, Locale.US)
|
||||
|
||||
fromTextInput.editText!!.setText(startTimestampString)
|
||||
toTextInput.editText!!.setText(endTimestampString)
|
||||
fromTextInput.editText!!.setTextAndRecalculateWidth(startTimestampString)
|
||||
toTextInput.editText!!.setTextAndRecalculateWidth(endTimestampString)
|
||||
|
||||
|
||||
okBtn.isEnabled = !(values[0] == 0F && values[1] == 100F)
|
||||
|
|
@ -282,9 +305,9 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private var urls
|
|||
val endValue = (endSeconds.toFloat() / endTimestamp) * 100
|
||||
|
||||
if (seconds == 0) {
|
||||
fromTextInput.editText!!.setText(infoUtil.formatIntegerDuration(startTimestamp, Locale.US))
|
||||
fromTextInput.editText!!.setTextAndRecalculateWidth(infoUtil.formatIntegerDuration(startTimestamp, Locale.US))
|
||||
}else if (startValue > 100){
|
||||
fromTextInput.editText!!.setText(infoUtil.formatIntegerDuration(startTimestamp, Locale.US))
|
||||
fromTextInput.editText!!.setTextAndRecalculateWidth(infoUtil.formatIntegerDuration(startTimestamp, Locale.US))
|
||||
}
|
||||
|
||||
rangeSlider.setValues(startValue, endValue)
|
||||
|
|
@ -318,9 +341,9 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private var urls
|
|||
val endValue = (seconds.toFloat() / endTimestamp) * 100
|
||||
|
||||
if (seconds == 0) {
|
||||
toTextInput.editText!!.setText(infoUtil.formatIntegerDuration(endTimestamp, Locale.US))
|
||||
toTextInput.editText!!.setTextAndRecalculateWidth(infoUtil.formatIntegerDuration(endTimestamp, Locale.US))
|
||||
}else if (endValue > 100 || endValue <= rangeSlider.valueFrom.toInt()){
|
||||
toTextInput.editText!!.setText(infoUtil.formatIntegerDuration(endTimestamp, Locale.US))
|
||||
toTextInput.editText!!.setTextAndRecalculateWidth(infoUtil.formatIntegerDuration(endTimestamp, Locale.US))
|
||||
}
|
||||
|
||||
rangeSlider.setValues(startValue, endValue)
|
||||
|
|
@ -520,8 +543,10 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private var urls
|
|||
|
||||
private fun videoProgress(player: ExoPlayer?) = flow {
|
||||
while (true) {
|
||||
emit((player!!.currentPosition / 1000).toInt())
|
||||
delay(1000)
|
||||
runCatching {
|
||||
emit((player!!.currentPosition / 1000).toInt())
|
||||
delay(1000)
|
||||
}
|
||||
}
|
||||
}.flowOn(Dispatchers.Main)
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,8 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
|
|||
val view = LayoutInflater.from(context).inflate(R.layout.download_multiple_bottom_sheet, null)
|
||||
dialog.setContentView(view)
|
||||
|
||||
if (items.isEmpty()) dismiss()
|
||||
|
||||
dialog.setOnShowListener {
|
||||
behavior = BottomSheetBehavior.from(view.parent as View)
|
||||
val displayMetrics = DisplayMetrics()
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.text.FieldPosition
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
|
|
@ -324,7 +325,9 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
|
|||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
item.status = DownloadRepository.Status.Cancelled.toString()
|
||||
downloadViewModel.updateDownload(item)
|
||||
lifecycleScope.launch(Dispatchers.IO){
|
||||
downloadViewModel.updateDownload(item)
|
||||
}
|
||||
|
||||
Snackbar.make(requireView().rootView, getString(R.string.cancelled) + ": " + item.title, Snackbar.LENGTH_LONG)
|
||||
.setAction(getString(R.string.undo)) {
|
||||
|
|
@ -533,19 +536,30 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
|
|||
override fun onCancelClick(itemID: Long) {
|
||||
cancelActiveDownload(itemID)
|
||||
}
|
||||
override fun onPauseClick(itemID: Long, action: ActiveDownloadAdapter.ActiveDownloadAction) {
|
||||
override fun onPauseClick(itemID: Long, action: ActiveDownloadAdapter.ActiveDownloadAction, position: Int) {
|
||||
val item = activeItems.find { it.id == itemID } ?: return
|
||||
when(action){
|
||||
ActiveDownloadAdapter.ActiveDownloadAction.Pause -> {
|
||||
cancelItem(itemID.toInt())
|
||||
item.status = DownloadRepository.Status.Paused.toString()
|
||||
downloadViewModel.updateDownload(item)
|
||||
lifecycleScope.launch {
|
||||
cancelItem(itemID.toInt())
|
||||
item.status = DownloadRepository.Status.Paused.toString()
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.updateDownload(item)
|
||||
}
|
||||
activeDownloads.notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
ActiveDownloadAdapter.ActiveDownloadAction.Resume -> {
|
||||
item.status = DownloadRepository.Status.Active.toString()
|
||||
downloadViewModel.updateDownload(item)
|
||||
runBlocking {
|
||||
downloadViewModel.queueDownloads(listOf(item))
|
||||
lifecycleScope.launch {
|
||||
item.status = DownloadRepository.Status.Active.toString()
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.updateDownload(item)
|
||||
}
|
||||
activeDownloads.notifyItemChanged(position)
|
||||
|
||||
runBlocking {
|
||||
downloadViewModel.queueDownloads(listOf(item))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -572,7 +586,9 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
|
|||
|
||||
activeItems.find { it.id == itemID }?.let {
|
||||
it.status = DownloadRepository.Status.Cancelled.toString()
|
||||
downloadViewModel.updateDownload(it)
|
||||
lifecycleScope.launch(Dispatchers.IO){
|
||||
downloadViewModel.updateDownload(it)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import com.deniscerri.ytdlnis.database.models.ResultItem
|
|||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
||||
import com.deniscerri.ytdlnis.receiver.ShareActivity
|
||||
import com.deniscerri.ytdlnis.util.UiUtil.setTextAndRecalculateWidth
|
||||
import com.google.android.material.bottomappbar.BottomAppBar
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
|
|
@ -120,8 +121,8 @@ class SelectPlaylistItemsBottomSheetDialog(private val items: List<ResultItem?>,
|
|||
val checkAll = view.findViewById<ExtendedFloatingActionButton>(R.id.check_all)
|
||||
checkAll!!.setOnClickListener {
|
||||
if (listAdapter.getCheckedItems().size != items.size){
|
||||
fromTextInput.editText!!.setText("1")
|
||||
toTextInput.editText!!.setText(items.size.toString())
|
||||
fromTextInput.editText!!.setTextAndRecalculateWidth("1")
|
||||
toTextInput.editText!!.setTextAndRecalculateWidth(items.size.toString())
|
||||
listAdapter.checkAll()
|
||||
fromTextInput.isEnabled = true
|
||||
toTextInput.isEnabled = true
|
||||
|
|
@ -130,8 +131,8 @@ class SelectPlaylistItemsBottomSheetDialog(private val items: List<ResultItem?>,
|
|||
reset()
|
||||
fromTextInput.isEnabled = true
|
||||
toTextInput.isEnabled = true
|
||||
fromTextInput.editText!!.setText("")
|
||||
toTextInput.editText!!.setText("")
|
||||
fromTextInput.editText!!.setTextAndRecalculateWidth("")
|
||||
toTextInput.editText!!.setTextAndRecalculateWidth("")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,12 @@
|
|||
package com.deniscerri.ytdlnis.ui.downloads
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.View.OnClickListener
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.allViews
|
||||
import androidx.core.view.children
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
|
@ -27,10 +22,10 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
|||
import com.deniscerri.ytdlnis.util.NotificationUtil
|
||||
import com.deniscerri.ytdlnis.util.UiUtil.forceFastScrollMode
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -80,15 +75,21 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
|||
if (pauseResume.text == requireContext().getString(R.string.pause)){
|
||||
|
||||
lifecycleScope.launch {
|
||||
runBlocking {
|
||||
val queued = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getQueued()
|
||||
}
|
||||
queued.forEach {
|
||||
WorkManager.getInstance(requireContext()).cancelUniqueWork(it.id.toString())
|
||||
val queued = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getQueued()
|
||||
}
|
||||
|
||||
queued.forEach {
|
||||
it.status = DownloadRepository.Status.QueuedPaused.toString()
|
||||
runBlocking {
|
||||
downloadViewModel.updateDownload(it)
|
||||
}
|
||||
}
|
||||
|
||||
queued.forEach {
|
||||
WorkManager.getInstance(requireContext()).cancelUniqueWork(it.id.toString())
|
||||
}
|
||||
|
||||
list.forEach {
|
||||
cancelItem(it.id.toInt())
|
||||
it.status = DownloadRepository.Status.Paused.toString()
|
||||
|
|
@ -108,6 +109,7 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
|||
val queuedItems = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getQueued()
|
||||
}
|
||||
queuedItems.map { it.status = DownloadRepository.Status.Queued.toString() }
|
||||
toQueue.addAll(queuedItems)
|
||||
runBlocking {
|
||||
downloadViewModel.queueDownloads(toQueue)
|
||||
|
|
@ -163,19 +165,38 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
|||
cancelDownload(itemID)
|
||||
}
|
||||
|
||||
override fun onPauseClick(itemID: Long, action: ActiveDownloadAdapter.ActiveDownloadAction) {
|
||||
override fun onPauseClick(itemID: Long, action: ActiveDownloadAdapter.ActiveDownloadAction, position: Int) {
|
||||
val item = list.find { it.id == itemID } ?: return
|
||||
when(action){
|
||||
ActiveDownloadAdapter.ActiveDownloadAction.Pause -> {
|
||||
cancelItem(itemID.toInt())
|
||||
item.status = DownloadRepository.Status.Paused.toString()
|
||||
downloadViewModel.updateDownload(item)
|
||||
lifecycleScope.launch {
|
||||
cancelItem(itemID.toInt())
|
||||
item.status = DownloadRepository.Status.Paused.toString()
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.updateDownload(item)
|
||||
}
|
||||
activeDownloads.notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
ActiveDownloadAdapter.ActiveDownloadAction.Resume -> {
|
||||
item.status = DownloadRepository.Status.Active.toString()
|
||||
downloadViewModel.updateDownload(item)
|
||||
runBlocking {
|
||||
downloadViewModel.queueDownloads(listOf(item))
|
||||
lifecycleScope.launch {
|
||||
item.status = DownloadRepository.Status.Active.toString()
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.updateDownload(item)
|
||||
}
|
||||
activeDownloads.notifyItemChanged(position)
|
||||
|
||||
val queue = if (list.size > 1) listOf(item)
|
||||
else withContext(Dispatchers.IO){
|
||||
val list = downloadViewModel.getQueued().toMutableList()
|
||||
list.map { it.status = DownloadRepository.Status.Queued.toString() }
|
||||
list.add(0, item)
|
||||
list
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
downloadViewModel.queueDownloads(queue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -203,7 +224,9 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
|||
cancelItem(itemID.toInt())
|
||||
list.find { it.id == itemID }?.let {
|
||||
it.status = DownloadRepository.Status.Cancelled.toString()
|
||||
downloadViewModel.updateDownload(it)
|
||||
lifecycleScope.launch(Dispatchers.IO){
|
||||
downloadViewModel.updateDownload(it)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,7 +279,9 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
|||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
item.status = DownloadRepository.Status.Cancelled.toString()
|
||||
downloadViewModel.updateDownload(item)
|
||||
lifecycleScope.launch(Dispatchers.IO){
|
||||
downloadViewModel.updateDownload(item)
|
||||
}
|
||||
|
||||
Snackbar.make(queuedRecyclerView, getString(R.string.cancelled) + ": " + item.title, Snackbar.LENGTH_LONG)
|
||||
.setAction(getString(R.string.undo)) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||
|
||||
|
||||
class DownloadSettingsFragment : BaseSettingsFragment() {
|
||||
override val title: Int = R.string.downloading
|
||||
override val title: Int = R.string.downloads
|
||||
|
||||
private var concurrentDownloads: SeekBarPreference? = null
|
||||
private var ignoreBatteryOptimization: Preference? = null
|
||||
|
|
|
|||
|
|
@ -115,14 +115,13 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
|||
Preference.OnPreferenceChangeListener { pref: Preference?, _: Any ->
|
||||
val packageManager = requireContext().packageManager
|
||||
val aliasComponentName = ComponentName(requireContext(), "com.deniscerri.ytdlnis.terminalShareAlias")
|
||||
|
||||
if ((pref as SwitchPreferenceCompat).isChecked){
|
||||
packageManager.setComponentEnabledSetting(aliasComponentName,
|
||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
||||
PackageManager.DONT_KILL_APP)
|
||||
}else{
|
||||
packageManager.setComponentEnabledSetting(aliasComponentName,
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
|
||||
PackageManager.DONT_KILL_APP)
|
||||
}
|
||||
true
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class InfoUtil(private val context: Context) {
|
|||
val element = dataArray.getJSONObject(i)
|
||||
if (element.getInt("duration") == -1) continue
|
||||
element.put("uploader", element.getString("uploaderName"))
|
||||
val v = createVideoFromPipedJSON(element, element.getString("url").removePrefix("/watch?v="))
|
||||
val v = createVideoFromPipedJSON(element, "https://youtube.com" + element.getString("url"))
|
||||
if (v == null || v.thumb.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
|
|
@ -93,7 +93,7 @@ class InfoUtil(private val context: Context) {
|
|||
val element = dataArray.getJSONObject(i)
|
||||
if (element.getInt("duration") == -1) continue
|
||||
element.put("uploader", element.getString("uploaderName"))
|
||||
val v = createVideoFromPipedJSON(element, element.getString("url").removePrefix("/watch?v="))
|
||||
val v = createVideoFromPipedJSON(element, "https://youtube.com" + element.getString("url"))
|
||||
if (v == null || v.thumb.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
|
|
@ -118,7 +118,7 @@ class InfoUtil(private val context: Context) {
|
|||
var nextpage = res.getString("nextpage")
|
||||
for (i in 0 until dataArray.length()){
|
||||
val obj = dataArray.getJSONObject(i)
|
||||
val itm = createVideoFromPipedJSON(obj, obj.getString("url").removePrefix("/watch?v="))
|
||||
val itm = createVideoFromPipedJSON(obj, "https://youtube.com" + obj.getString("url"))
|
||||
itm?.playlistTitle = "YTDLnis"
|
||||
items.add(itm)
|
||||
}
|
||||
|
|
@ -137,9 +137,7 @@ class InfoUtil(private val context: Context) {
|
|||
return try {
|
||||
val id = getIDFromYoutubeURL(url)
|
||||
val res = genericRequest("$pipedURL/streams/$id")
|
||||
if (res.length() == 0) getFromYTDL(url) else listOf(createVideoFromPipedJSON(
|
||||
res, url
|
||||
))
|
||||
if (res.length() == 0) getFromYTDL(url) else listOf(createVideoFromPipedJSON(res, url))
|
||||
}catch (e: Exception){
|
||||
val v = getFromYTDL(url)
|
||||
v.forEach { it!!.url = url }
|
||||
|
|
@ -176,7 +174,6 @@ class InfoUtil(private val context: Context) {
|
|||
private fun createVideoFromPipedJSON(obj: JSONObject, url: String): ResultItem? {
|
||||
var video: ResultItem? = null
|
||||
try {
|
||||
|
||||
val id = getIDFromYoutubeURL(url)
|
||||
val title = Html.fromHtml(obj.getString("title").toString()).toString()
|
||||
val author = try {
|
||||
|
|
@ -399,7 +396,7 @@ class InfoUtil(private val context: Context) {
|
|||
val element = res.getJSONObject(i)
|
||||
if (element.getInt("duration") < 0) continue
|
||||
element.put("uploader", element.getString("uploaderName"))
|
||||
val v = createVideoFromPipedJSON(element, element.getString("url").removePrefix("/watch?v="))
|
||||
val v = createVideoFromPipedJSON(element, "https://youtube.com" + element.getString("url"))
|
||||
if (v == null || v.thumb.isEmpty()) continue
|
||||
v.playlistTitle = context.getString(R.string.trendingPlaylist)
|
||||
items.add(v)
|
||||
|
|
@ -410,7 +407,7 @@ class InfoUtil(private val context: Context) {
|
|||
return items
|
||||
}
|
||||
|
||||
fun getIDFromYoutubeURL(inputQuery: String) : String {
|
||||
private fun getIDFromYoutubeURL(inputQuery: String) : String {
|
||||
var el: Array<String?> =
|
||||
inputQuery.split("/".toRegex()).dropLastWhile { it.isEmpty() }
|
||||
.toTypedArray()
|
||||
|
|
@ -437,7 +434,7 @@ class InfoUtil(private val context: Context) {
|
|||
val id = getIDFromYoutubeURL(url)
|
||||
val res = genericRequest("$pipedURL/streams/$id")
|
||||
if (res.length() == 0) getFromYTDL(url)[0]!!
|
||||
val item = createVideoFromPipedJSON(res, id)
|
||||
val item = createVideoFromPipedJSON(res, "https://youtube.com/watch?v=$id")
|
||||
item!!.formats
|
||||
}else{
|
||||
getFormatsFromYTDL(url)
|
||||
|
|
@ -507,11 +504,16 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("--skip-download")
|
||||
request.addOption("-R", "1")
|
||||
request.addOption("--socket-timeout", "5")
|
||||
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
||||
if (cookiesFile.exists()){
|
||||
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||
|
||||
|
||||
if (sharedPreferences.getBoolean("use_cookies", false)){
|
||||
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
||||
if (cookiesFile.exists()){
|
||||
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val proxy = sharedPreferences.getString("proxy", "")
|
||||
if (proxy!!.isNotBlank()){
|
||||
request.addOption("--proxy", proxy)
|
||||
|
|
@ -535,7 +537,7 @@ class InfoUtil(private val context: Context) {
|
|||
urls.forEach {
|
||||
val id = getIDFromYoutubeURL(it)
|
||||
val res = genericRequest("$pipedURL/streams/$id")
|
||||
val vid = createVideoFromPipedJSON(res, id)
|
||||
val vid = createVideoFromPipedJSON(res, it)
|
||||
progress(vid!!.formats)
|
||||
}
|
||||
}
|
||||
|
|
@ -566,11 +568,15 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("-R", "1")
|
||||
request.addOption("--socket-timeout", "5")
|
||||
|
||||
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
||||
if (cookiesFile.exists()){
|
||||
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||
if (sharedPreferences.getBoolean("use_cookies", false)){
|
||||
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
||||
if (cookiesFile.exists()){
|
||||
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
val proxy = sharedPreferences.getString("proxy", "")
|
||||
if (proxy!!.isNotBlank()){
|
||||
request.addOption("--proxy", proxy)
|
||||
|
|
@ -715,11 +721,15 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("-R", "1")
|
||||
request.addOption("--socket-timeout", "5")
|
||||
|
||||
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
||||
if (cookiesFile.exists()){
|
||||
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||
if (sharedPreferences.getBoolean("use_cookies", false)){
|
||||
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
||||
if (cookiesFile.exists()){
|
||||
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
val proxy = sharedPreferences.getString("proxy", "")
|
||||
if (proxy!!.isNotBlank()){
|
||||
request.addOption("--proxy", proxy)
|
||||
|
|
@ -861,7 +871,7 @@ class InfoUtil(private val context: Context) {
|
|||
val m = p.matcher(url)
|
||||
|
||||
if (m.find()){
|
||||
return getStreamingUrlAndChaptersFromPIPED(getIDFromYoutubeURL(url))
|
||||
return getStreamingUrlAndChaptersFromPIPED(url)
|
||||
}else{
|
||||
throw Exception()
|
||||
}
|
||||
|
|
@ -874,11 +884,15 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("-R", "1")
|
||||
request.addOption("--socket-timeout", "5")
|
||||
|
||||
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
||||
if (cookiesFile.exists()){
|
||||
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||
if (sharedPreferences.getBoolean("use_cookies", false)){
|
||||
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
||||
if (cookiesFile.exists()){
|
||||
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
val proxy = sharedPreferences.getString("proxy", "")
|
||||
if (proxy!!.isNotBlank()){
|
||||
request.addOption("--proxy", proxy)
|
||||
|
|
@ -898,12 +912,13 @@ class InfoUtil(private val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getStreamingUrlAndChaptersFromPIPED(id: String) : MutableList<String?> {
|
||||
private fun getStreamingUrlAndChaptersFromPIPED(url: String) : MutableList<String?> {
|
||||
val id = getIDFromYoutubeURL(url)
|
||||
val res = genericRequest("$pipedURL/streams/$id")
|
||||
if (res.length() == 0) {
|
||||
throw Exception()
|
||||
}else{
|
||||
val item = createVideoFromPipedJSON(res, id)
|
||||
val item = createVideoFromPipedJSON(res, url)
|
||||
if (item!!.urls.isBlank()) throw Exception()
|
||||
val list = mutableListOf<String?>(Gson().toJson(item.chapters))
|
||||
list.addAll(item.urls.split(","))
|
||||
|
|
@ -974,6 +989,7 @@ class InfoUtil(private val context: Context) {
|
|||
request.addCommands(listOf("--replace-in-metadata","uploader",".*.",downloadItem.author.take(25)))
|
||||
}
|
||||
request.addCommands(listOf("--replace-in-metadata","uploader"," - Topic$",""))
|
||||
|
||||
downloadItem.customFileNameTemplate = downloadItem.customFileNameTemplate.replace("%(uploader)s", "%(uploader|${downloadItem.author})s")
|
||||
|
||||
if (downloadItem.downloadSections.isNotBlank()){
|
||||
|
|
@ -1015,9 +1031,11 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("--restrict-filenames")
|
||||
}
|
||||
|
||||
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
||||
if (cookiesFile.exists()){
|
||||
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||
if (sharedPreferences.getBoolean("use_cookies", false)){
|
||||
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
||||
if (cookiesFile.exists()){
|
||||
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||
}
|
||||
}
|
||||
|
||||
val proxy = sharedPreferences.getString("proxy", "")
|
||||
|
|
@ -1055,7 +1073,20 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("--split-chapters")
|
||||
request.addOption("-o", "chapter:%(section_title)s.%(ext)s")
|
||||
}else{
|
||||
request.addOption("--embed-metadata")
|
||||
|
||||
if (sharedPreferences.getBoolean("embed_metadata", true)){
|
||||
request.addOption("--embed-metadata")
|
||||
|
||||
request.addOption("--parse-metadata", "%(release_year,upload_date)s:%(meta_date)s")
|
||||
|
||||
if (downloadItem.playlistTitle.isNotEmpty()) {
|
||||
request.addOption("--parse-metadata", "%(album,playlist,title)s:%(meta_album)s")
|
||||
request.addOption("--parse-metadata", "%(track_number,playlist_index)d:%(meta_track)s")
|
||||
} else {
|
||||
request.addOption("--parse-metadata", "%(album,title)s:%(meta_album)s")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (downloadItem.audioPreferences.embedThumb) {
|
||||
request.addOption("--embed-thumbnail")
|
||||
|
|
@ -1072,14 +1103,6 @@ class InfoUtil(private val context: Context) {
|
|||
}
|
||||
}
|
||||
}
|
||||
request.addOption("--parse-metadata", "%(release_year,upload_date)s:%(meta_date)s")
|
||||
|
||||
if (downloadItem.playlistTitle.isNotEmpty()) {
|
||||
request.addOption("--parse-metadata", "%(album,playlist,title)s:%(meta_album)s")
|
||||
request.addOption("--parse-metadata", "%(track_number,playlist_index)d:%(meta_track)s")
|
||||
} else {
|
||||
request.addOption("--parse-metadata", "%(album,title)s:%(meta_album)s")
|
||||
}
|
||||
|
||||
if (downloadItem.customFileNameTemplate.isNotBlank()){
|
||||
request.addOption("-o", "${downloadItem.customFileNameTemplate}.%(ext)s")
|
||||
|
|
@ -1090,6 +1113,10 @@ class InfoUtil(private val context: Context) {
|
|||
DownloadViewModel.Type.video -> {
|
||||
val supportedContainers = context.resources.getStringArray(R.array.video_containers)
|
||||
|
||||
if (!sharedPreferences.getBoolean("embed_metadata", true)){
|
||||
request.addOption("--no-embed-metadata")
|
||||
}
|
||||
|
||||
if (downloadItem.videoPreferences.addChapters) {
|
||||
request.addOption("--sponsorblock-mark", "all")
|
||||
request.addOption("--embed-chapters")
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ import androidx.core.content.FileProvider
|
|||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.models.CommandTemplate
|
||||
|
|
@ -53,7 +52,6 @@ import com.google.android.material.textfield.TextInputLayout
|
|||
import com.google.android.material.timepicker.MaterialTimePicker
|
||||
import com.google.android.material.timepicker.TimeFormat
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.util.Calendar
|
||||
|
|
@ -105,7 +103,7 @@ object UiUtil {
|
|||
val ok : Button = bottomSheet.findViewById(R.id.template_create)!!
|
||||
val title : TextInputLayout = bottomSheet.findViewById(R.id.title)!!
|
||||
val content : TextInputLayout = bottomSheet.findViewById(R.id.content)!!
|
||||
val extraCommandsSwitch : MaterialSwitch = bottomSheet.findViewById(R.id.extra_command_switch)!!
|
||||
val extraCommandsSwitch : MaterialSwitch = bottomSheet.findViewById(R.id.extraCommandsSwitch)!!
|
||||
val shortcutsChipGroup : ChipGroup = bottomSheet.findViewById(R.id.shortcutsChipGroup)!!
|
||||
val editShortcuts : Button = bottomSheet.findViewById(R.id.edit_shortcuts)!!
|
||||
|
||||
|
|
@ -154,7 +152,9 @@ object UiUtil {
|
|||
}
|
||||
}
|
||||
|
||||
extraCommandsSwitch.isChecked = item!!.useAsExtraCommand
|
||||
if (item != null){
|
||||
extraCommandsSwitch.isChecked = item.useAsExtraCommand
|
||||
}
|
||||
|
||||
commandTemplateViewModel.shortcuts.observe(lifeCycle){
|
||||
shortcutsChipGroup.removeAllViews()
|
||||
|
|
@ -180,6 +180,7 @@ object UiUtil {
|
|||
}else{
|
||||
item.title = title.editText!!.text.toString()
|
||||
item.content = content.editText!!.text.toString()
|
||||
item.useAsExtraCommand = extraCommandsSwitch.isChecked
|
||||
Log.e("aa", item.toString())
|
||||
commandTemplateViewModel.update(item)
|
||||
newTemplate(item)
|
||||
|
|
@ -559,4 +560,19 @@ object UiUtil {
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun EditText.setTextAndRecalculateWidth(t : String){
|
||||
this.setText(t)
|
||||
val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
|
||||
val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
|
||||
this.measure(widthMeasureSpec, heightMeasureSpec)
|
||||
val requiredWidth: Int = this.measuredWidth
|
||||
if (t.isEmpty()){
|
||||
this.layoutParams.width = 70
|
||||
}else{
|
||||
this.layoutParams.width = requiredWidth
|
||||
}
|
||||
this.requestLayout()
|
||||
}
|
||||
}
|
||||
7
app/src/main/res/drawable/baseline_video_metadata.xml
Normal file
7
app/src/main/res/drawable/baseline_video_metadata.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<vector android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="?android:colorAccent" android:pathData="M3,6h18v5h2V6c0,-1.1 -0.9,-2 -2,-2H3C1.9,4 1,4.9 1,6v12c0,1.1 0.9,2 2,2h9v-2H3V6z"/>
|
||||
<path android:fillColor="?android:colorAccent" android:pathData="M15,12l-6,-4l0,8z"/>
|
||||
<path android:fillColor="?android:colorAccent" android:pathData="M22.71,18.43c0.03,-0.29 0.04,-0.58 0.01,-0.86l1.07,-0.85c0.1,-0.08 0.12,-0.21 0.06,-0.32l-1.03,-1.79c-0.06,-0.11 -0.19,-0.15 -0.31,-0.11L21.23,15c-0.23,-0.17 -0.48,-0.31 -0.75,-0.42l-0.2,-1.36C20.26,13.09 20.16,13 20.03,13h-2.07c-0.12,0 -0.23,0.09 -0.25,0.21l-0.2,1.36c-0.26,0.11 -0.51,0.26 -0.74,0.42l-1.28,-0.5c-0.12,-0.05 -0.25,0 -0.31,0.11l-1.03,1.79c-0.06,0.11 -0.04,0.24 0.06,0.32l1.07,0.86c-0.03,0.29 -0.04,0.58 -0.01,0.86l-1.07,0.85c-0.1,0.08 -0.12,0.21 -0.06,0.32l1.03,1.79c0.06,0.11 0.19,0.15 0.31,0.11l1.27,-0.5c0.23,0.17 0.48,0.31 0.75,0.42l0.2,1.36c0.02,0.12 0.12,0.21 0.25,0.21h2.07c0.12,0 0.23,-0.09 0.25,-0.21l0.2,-1.36c0.26,-0.11 0.51,-0.26 0.74,-0.42l1.28,0.5c0.12,0.05 0.25,0 0.31,-0.11l1.03,-1.79c0.06,-0.11 0.04,-0.24 -0.06,-0.32L22.71,18.43zM19,19.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5S19.83,19.5 19,19.5z"/>
|
||||
</vector>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<size android:width="10dp" android:height="100dp" />
|
||||
|
||||
<corners
|
||||
android:radius="50dp" />
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:height="60dp"
|
||||
android:state_pressed="true"
|
||||
android:drawable="@drawable/thumb"/>
|
||||
|
||||
<item
|
||||
android:height="60dp"
|
||||
android:drawable="@drawable/thumb"/>
|
||||
</selector>
|
||||
|
|
@ -36,6 +36,8 @@
|
|||
android:id="@+id/providers"
|
||||
android:layout_margin="10dp"
|
||||
app:chipSpacingVertical="-10dp"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="20dp"
|
||||
app:singleSelection="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -166,7 +168,6 @@
|
|||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
/>
|
||||
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -278,6 +279,8 @@
|
|||
|
||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
|
||||
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/home_fabs"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@
|
|||
android:id="@+id/providers"
|
||||
android:layout_margin="10dp"
|
||||
app:chipSpacingVertical="-10dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingStart="0dp"
|
||||
app:singleSelection="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -162,7 +164,6 @@
|
|||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -181,8 +182,6 @@
|
|||
app:spanCount="2"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
/>
|
||||
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -270,6 +269,7 @@
|
|||
|
||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/home_fabs"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@
|
|||
android:layout_margin="10dp"
|
||||
app:singleSelection="true"
|
||||
app:chipSpacingVertical="-10dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingStart="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:singleLine="true" />
|
||||
|
|
@ -147,7 +149,6 @@
|
|||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -166,7 +167,6 @@
|
|||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
/>
|
||||
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -298,6 +298,8 @@
|
|||
|
||||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
|
||||
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/home_fabs"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -102,8 +102,9 @@
|
|||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/extra_command_switch"
|
||||
android:id="@+id/extraCommandsSwitch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:checked="false"
|
||||
|
|
|
|||
|
|
@ -56,6 +56,24 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/rewind"
|
||||
android:clickable="false"
|
||||
app:iconSize="50dp"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
app:iconTint="?android:colorAccent"
|
||||
app:icon="@drawable/exomedia_ic_rewind_white"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:indeterminate="true"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
android:translationZ="10dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/frame_layout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/mute"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
|
|
@ -129,7 +147,7 @@
|
|||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/from_textinput"
|
||||
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
||||
android:layout_width="90dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/start">
|
||||
|
||||
|
|
@ -154,7 +172,7 @@
|
|||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/to_textinput"
|
||||
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
||||
android:layout_width="90dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/end"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
|
|
|||
|
|
@ -1,42 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:scrollbars="none"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:fillViewport="true">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bottom_sheet_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/use_extra_commands"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
android:orientation="vertical">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/scr"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="10dp"
|
||||
android:scrollbars="none">
|
||||
android:scrollbars="none"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linear"
|
||||
|
|
@ -44,6 +29,25 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bottom_sheet_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/use_extra_commands"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/current"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -92,7 +96,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:layout_marginTop="5dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/current">
|
||||
app:layout_constraintTop_toBottomOf="@+id/scr">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/command"
|
||||
|
|
@ -104,44 +108,49 @@
|
|||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/shortcuts"
|
||||
style="@style/Widget.Material3.Button.IconButton.Filled"
|
||||
android:layout_width="wrap_content"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_shortcut"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/okButton"
|
||||
app:layout_constraintStart_toEndOf="@+id/commands"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/commands"
|
||||
style="@style/Widget.Material3.Button.IconButton.Filled"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_terminal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/okButton"
|
||||
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:autoLink="all"
|
||||
android:layout_margin="20dp"
|
||||
android:text="@string/ok"
|
||||
app:icon="@drawable/ic_check"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/shortcuts"
|
||||
style="@style/Widget.Material3.Button.IconButton.Filled"
|
||||
android:layout_width="wrap_content"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_shortcut"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/okButton"
|
||||
app:layout_constraintStart_toEndOf="@+id/commands"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout2" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/commands"
|
||||
style="@style/Widget.Material3.Button.IconButton.Filled"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_terminal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout2" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/okButton"
|
||||
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:autoLink="all"
|
||||
android:layout_margin="20dp"
|
||||
android:text="@string/ok"
|
||||
app:icon="@drawable/ic_check"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout2" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
|
@ -27,11 +27,6 @@
|
|||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:scrollbars="none"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:fastScrollEnabled="true"
|
||||
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
|
||||
app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
|
||||
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
|
||||
app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/pause_resume">
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@
|
|||
android:id="@+id/providers"
|
||||
android:layout_margin="10dp"
|
||||
app:chipSpacingVertical="-10dp"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:layout_width="wrap_content"
|
||||
app:singleSelection="true"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:tools="http://schemas.android.com/tools"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="100dp"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
|
|
@ -13,6 +11,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="30dp"
|
||||
android:paddingVertical="10dp"
|
||||
android:visibility="gone"
|
||||
android:text="@string/file_size"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
@ -26,6 +25,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
app:fastScrollEnabled="true"
|
||||
android:scrollbars="none"
|
||||
android:paddingBottom="100dp"
|
||||
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
|
||||
app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
|
||||
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
|
||||
|
|
@ -38,4 +38,5 @@
|
|||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@
|
|||
<item>lv</item>
|
||||
<item>ms</item>
|
||||
<item>nb</item>
|
||||
<item>pa</item>
|
||||
<item>pl</item>
|
||||
<item>pt</item>
|
||||
<item>pt-BR</item>
|
||||
|
|
|
|||
|
|
@ -298,4 +298,6 @@
|
|||
<string name="write_description_summary">Write description file</string>
|
||||
<string name="extra_command">Extra Command</string>
|
||||
<string name="use_cookies">Use Cookies</string>
|
||||
<string name="embed_metadata">Embed Metadata</string>
|
||||
<string name="embed_metadata_summary">Embed metadata to the video file. Also embeds chapters/infojson if present</string>
|
||||
</resources>
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
<locale android:name="lv"/>
|
||||
<locale android:name="ms"/>
|
||||
<locale android:name="nb-NO"/>
|
||||
<locale android:name="pa"/>
|
||||
<locale android:name="pl"/>
|
||||
<locale android:name="pt"/>
|
||||
<locale android:name="pt-BR" />
|
||||
|
|
|
|||
|
|
@ -20,6 +20,14 @@
|
|||
app:summary="@string/enable_mtime_summary"
|
||||
app:title="@string/enable_mtime" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
app:defaultValue="true"
|
||||
app:icon="@drawable/baseline_video_metadata"
|
||||
app:key="embed_metadata"
|
||||
app:summary="@string/embed_metadata_summary"
|
||||
app:title="@string/embed_metadata" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
app:defaultValue="true"
|
||||
|
|
@ -170,4 +178,9 @@
|
|||
android:summary="@string/use_extra_commands_summary"
|
||||
app:title="@string/use_extra_commands" />
|
||||
|
||||
<Preference
|
||||
app:isPreferenceVisible="false"
|
||||
android:key="use_cookies"
|
||||
android:defaultValue="false" />
|
||||
|
||||
</PreferenceScreen>
|
||||
Loading…
Reference in a new issue