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:
deniscerri 2023-07-29 14:00:14 +02:00
parent fecc38df82
commit 26ea19ab8c
No known key found for this signature in database
GPG key ID: 95C43D517D830350
34 changed files with 362 additions and 194 deletions

View file

@ -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'

View file

@ -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)
}

View file

@ -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()
}

View file

@ -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
}
}
}

View file

@ -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")

View file

@ -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 {

View file

@ -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)
}

View file

@ -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)

View file

@ -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)

View file

@ -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()

View file

@ -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)
}
}
}

View file

@ -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("")
}
}

View file

@ -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)
}
}
}

View file

@ -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)) {

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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()
}
}

View 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>

View file

@ -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" />

View file

@ -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>

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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>

View file

@ -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">

View file

@ -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"

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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" />

View file

@ -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>