Merge branch 'main' into weblate-ytdlnis-strings

This commit is contained in:
deniscerri 2023-07-22 22:24:47 +02:00 committed by GitHub
commit f309f0813b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
67 changed files with 881 additions and 434 deletions

View file

@ -10,7 +10,7 @@ def properties = new Properties()
def versionMajor = 1
def versionMinor = 6
def versionPatch = 3
def versionBuild = 9 // bump for dogfood builds, public betas, etc.
def versionBuild = 10 // bump for dogfood builds, public betas, etc.
def versionExt = ""
if (versionBuild > 0){

View file

@ -185,6 +185,8 @@ class MainActivity : BaseActivity() {
}
true
}
(navigationView as NavigationView).getHeaderView(0).findViewById<TextView>(R.id.title).text = ThemeUtil.getStyledAppName(this)
}
cookieViewModel.updateCookiesFile()
val intent = intent

View file

@ -10,6 +10,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
@ -17,6 +18,7 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.util.FileUtil
import com.google.android.material.button.MaterialButton
@ -56,12 +58,13 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
val card = holder.cardView
card.tag = "${item!!.id}##card"
val uiHandler = Handler(Looper.getMainLooper())
val thumbnail = card.findViewById<ImageView>(R.id.image_view)
// THUMBNAIL ----------------------------------
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("queue")){
val imageURL = item!!.thumb
val imageURL = item.thumb
if (imageURL.isNotEmpty()) {
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
} else {
@ -74,7 +77,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
// PROGRESS BAR ----------------------------------------------------
val progressBar = card.findViewById<LinearProgressIndicator>(R.id.progress)
progressBar.tag = "${item!!.id}##progress"
progressBar.tag = "${item.id}##progress"
progressBar.progress = 0
progressBar.isIndeterminate = true
@ -105,17 +108,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
val formatDetailsChip = card.findViewById<Chip>(R.id.format_note)
val formatDetailsText = StringBuilder(item.format.format_note.uppercase())
val codecText =
if (item.format.encoding != "") {
item.format.encoding.uppercase()
}else if (item.format.vcodec != "none" && item.format.vcodec != ""){
item.format.vcodec.uppercase()
} else {
item.format.acodec.uppercase()
}
if (codecText != "" && codecText != "none"){
formatDetailsText.append(" \t\t $codecText")
}
formatDetailsText.append(" \t\t ${item.container.uppercase().ifEmpty { item.format.container.uppercase() }}")
val fileSize = FileUtil.convertFileSize(item.format.filesize)
if (fileSize != "?") formatDetailsText.append(" \t\t $fileSize")
@ -131,19 +124,54 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
}
// CANCEL BUTTON ----------------------------------
val cancelButton = card.findViewById<MaterialButton>(R.id.active_download_cancel)
if (cancelButton.hasOnClickListeners()) cancelButton.setOnClickListener(null)
// PAUSE BUTTON ----------------------------------
val pauseButton = card.findViewById<MaterialButton>(R.id.active_download_pause)
if (pauseButton.hasOnClickListeners()) pauseButton.setOnClickListener(null)
cancelButton.setOnClickListener {
onItemClickListener.onCancelClick(item.id)
// CANCEL BUTTON ----------------------------------
val cancelButton = card.findViewById<MaterialButton>(R.id.active_download_delete)
if (cancelButton.hasOnClickListeners()) cancelButton.setOnClickListener(null)
cancelButton.setOnClickListener {onItemClickListener.onCancelClick(item.id)}
if (item.status == DownloadRepository.Status.Paused.toString()){
progressBar.isIndeterminate = false
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_play_arrow_white)
pauseButton.tag = ActiveDownloadAction.Resume
cancelButton.visibility = View.VISIBLE
}else{
progressBar.isIndeterminate = true
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_pause_white)
cancelButton.visibility = View.GONE
pauseButton.tag = ActiveDownloadAction.Pause
}
pauseButton.setOnClickListener {
if (pauseButton.tag == ActiveDownloadAction.Pause){
onItemClickListener.onPauseClick(item.id, ActiveDownloadAction.Pause)
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 = ActiveDownloadAction.Resume
}else{
onItemClickListener.onPauseClick(item.id, ActiveDownloadAction.Resume)
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_pause_white)
progressBar.isIndeterminate = true
cancelButton.visibility = View.GONE
pauseButton.tag = ActiveDownloadAction.Pause
}
}
}
interface OnItemClickListener {
fun onCancelClick(itemID: Long)
fun onPauseClick(itemID: Long, action: ActiveDownloadAction)
fun onOutputClick(item: DownloadItem)
}
enum class ActiveDownloadAction {
Resume, Pause
}
companion object {
private val DIFF_CALLBACK: DiffUtil.ItemCallback<DownloadItem> = object : DiffUtil.ItemCallback<DownloadItem>() {
override fun areItemsTheSame(oldItem: DownloadItem, newItem: DownloadItem): Boolean {
@ -152,7 +180,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
}
override fun areContentsTheSame(oldItem: DownloadItem, newItem: DownloadItem): Boolean {
return oldItem.id == newItem.id && oldItem.title == newItem.title && oldItem.author == newItem.author && oldItem.thumb == newItem.thumb
return oldItem.id == newItem.id && oldItem.title == newItem.title && oldItem.author == newItem.author && oldItem.thumb == newItem.thumb && oldItem.status == newItem.status
}
}
}

View file

@ -10,6 +10,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
@ -17,6 +18,7 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.util.FileUtil
import com.google.android.material.button.MaterialButton
@ -86,9 +88,6 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
}
itemTitle.text = title.ifEmpty { item.url }
val duration = card.findViewById<TextView>(R.id.duration)
duration.text = item.duration
val formatDetailsChip = card.findViewById<TextView>(R.id.format_note)
val formatDetailsText = StringBuilder(item.format.format_note.uppercase())
@ -110,12 +109,41 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
formatDetailsChip.text = formatDetailsText
// CANCEL BUTTON ----------------------------------
val cancelButton = card.findViewById<MaterialButton>(R.id.active_download_cancel)
if (cancelButton.hasOnClickListeners()) cancelButton.setOnClickListener(null)
// PAUSE BUTTON ----------------------------------
val pauseButton = card.findViewById<MaterialButton>(R.id.active_download_pause)
if (pauseButton.hasOnClickListeners()) pauseButton.setOnClickListener(null)
cancelButton.setOnClickListener {
onItemClickListener.onCancelClick(item.id)
// CANCEL BUTTON ----------------------------------
val cancelButton = card.findViewById<MaterialButton>(R.id.active_download_delete)
if (cancelButton.hasOnClickListeners()) cancelButton.setOnClickListener(null)
cancelButton.setOnClickListener {onItemClickListener.onCancelClick(item.id)}
if (item.status == DownloadRepository.Status.Paused.toString()){
progressBar.isIndeterminate = false
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_play_arrow_white)
pauseButton.tag = ActiveDownloadAdapter.ActiveDownloadAction.Resume
cancelButton.visibility = View.VISIBLE
}else{
progressBar.isIndeterminate = true
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_pause_white)
cancelButton.visibility = View.GONE
pauseButton.tag = ActiveDownloadAdapter.ActiveDownloadAction.Pause
}
pauseButton.setOnClickListener {
if (pauseButton.tag == ActiveDownloadAdapter.ActiveDownloadAction.Pause){
onItemClickListener.onPauseClick(item.id, ActiveDownloadAdapter.ActiveDownloadAction.Pause)
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)
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_pause_white)
progressBar.isIndeterminate = true
cancelButton.visibility = View.GONE
pauseButton.tag = ActiveDownloadAdapter.ActiveDownloadAction.Pause
}
}
card.setOnClickListener {
@ -124,9 +152,11 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
}
interface OnItemClickListener {
fun onCancelClick(itemID: Long)
fun onPauseClick(itemID: Long, action: ActiveDownloadAdapter.ActiveDownloadAction)
fun onCardClick()
}
companion object {
private val DIFF_CALLBACK: DiffUtil.ItemCallback<DownloadItem> = object : DiffUtil.ItemCallback<DownloadItem>() {
override fun areItemsTheSame(oldItem: DownloadItem, newItem: DownloadItem): Boolean {
@ -135,7 +165,7 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
}
override fun areContentsTheSame(oldItem: DownloadItem, newItem: DownloadItem): Boolean {
return oldItem.id == newItem.id && oldItem.title == newItem.title && oldItem.author == newItem.author && oldItem.thumb == newItem.thumb
return oldItem.id == newItem.id && oldItem.title == newItem.title && oldItem.author == newItem.author && oldItem.thumb == newItem.thumb && oldItem.status == newItem.status
}
}
}

View file

@ -90,13 +90,12 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
// BUTTONS ----------------------------------
val videoURL = video.url
val buttonLayout = card.findViewById<LinearLayout>(R.id.download_button_layout)
val musicBtn = buttonLayout.findViewById<MaterialButton>(R.id.download_music)
val musicBtn = card.findViewById<MaterialButton>(R.id.download_music)
musicBtn.tag = "$videoURL##audio"
musicBtn.setTag(R.id.cancelDownload, "false")
musicBtn.setOnClickListener { onItemClickListener.onButtonClick(videoURL, DownloadViewModel.Type.audio) }
musicBtn.setOnLongClickListener{ onItemClickListener.onLongButtonClick(videoURL, DownloadViewModel.Type.audio); true}
val videoBtn = buttonLayout.findViewById<MaterialButton>(R.id.download_video)
val videoBtn = card.findViewById<MaterialButton>(R.id.download_video)
videoBtn.tag = "$videoURL##video"
videoBtn.setTag(R.id.cancelDownload, "false")
videoBtn.setOnClickListener { onItemClickListener.onButtonClick(videoURL, DownloadViewModel.Type.video) }

View file

@ -10,16 +10,16 @@ interface DownloadDao {
@Query("SELECT * FROM downloads ORDER BY status")
fun getAllDownloads() : Flow<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE status='Active'")
@Query("SELECT * FROM downloads WHERE status='Active' or status='Paused'")
fun getActiveDownloads() : Flow<List<DownloadItem>>
@Query("SELECT COUNT(*) FROM downloads WHERE status='Active'")
@Query("SELECT COUNT(*) FROM downloads WHERE status='Active' or status='Paused'")
fun getActiveDownloadsCount() : Flow<Int>
@Query("SELECT * FROM downloads WHERE status='Active'")
@Query("SELECT * FROM downloads WHERE status='Active' or status='Paused'")
fun getActiveDownloadsList() : List<DownloadItem>
@Query("SELECT * FROM downloads WHERE status='Active' or status='Queued'")
@Query("SELECT * FROM downloads WHERE status='Active' or status='Queued' or status='Paused'")
fun getActiveAndQueuedDownloadsList() : List<DownloadItem>
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id")

View file

@ -16,7 +16,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
val savedDownloads : Flow<List<DownloadItem>> = downloadDao.getSavedDownloads()
enum class Status {
Active, Queued, Error, Processing, Cancelled, Saved
Active, Paused, Queued, Error, Cancelled, Saved
}
suspend fun insert(item: DownloadItem) : Long {

View file

@ -12,6 +12,7 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.anggrayudi.storage.file.dataDirectory
import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.models.CookieItem
import com.deniscerri.ytdlnis.database.repository.CookieRepository
@ -72,11 +73,11 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
flush()
}
var dbPath = "/data/data/com.deniscerri.ytdl/app_webview/Cookies"
if (!File(dbPath).exists()) dbPath = "/data/data/com.deniscerri.ytdl/app_webview/Default/Cookies"
val dbPath = File("/data/data/com.deniscerri.ytdl/").walkTopDown().find { it.name == "Cookies" }
?: throw Exception("Cookies File not found!")
SQLiteDatabase.openDatabase(
dbPath, null, OPEN_READONLY
dbPath.absolutePath, null, OPEN_READONLY
).run {
val projection = arrayOf(
CookieObject.HOST,

View file

@ -166,6 +166,11 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
else -> sharedPreferences.getString("command_path", FileUtil.getDefaultCommandPath())
}
val container = when(type){
Type.audio -> sharedPreferences.getString("audio_format", "")
else -> sharedPreferences.getString("video_format", "")
}
val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
@ -182,10 +187,10 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
resultItem.duration,
type,
getFormat(resultItem.formats, type),
sharedPreferences.getString("video_format", "Default")!!,
container!!,
"",
resultItem.formats,
downloadPath!!, resultItem.website, "", resultItem.playlistTitle, audioPreferences, videoPreferences, "", customFileNameTemplate!!, saveThumb, DownloadRepository.Status.Processing.toString(), 0, null
downloadPath!!, resultItem.website, "", resultItem.playlistTitle, audioPreferences, videoPreferences, "", customFileNameTemplate!!, saveThumb, DownloadRepository.Status.Queued.toString(), 0, null
)
}
@ -307,7 +312,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
container,
"",
ArrayList(),
path, historyItem.website, "", "", audioPreferences, videoPreferences, "", customFileNameTemplate!!, saveThumb, DownloadRepository.Status.Processing.toString(), 0, null
path, historyItem.website, "", "", audioPreferences, videoPreferences, "", customFileNameTemplate!!, saveThumb, DownloadRepository.Status.Queued.toString(), 0, null
)
}
@ -459,7 +464,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
var exists = false
items.forEach {
it.status = DownloadRepository.Status.Queued.toString()
if (it.status != DownloadRepository.Status.Paused.toString()) it.status = DownloadRepository.Status.Queued.toString()
if (activeAndQueuedDownloads.firstOrNull{d ->
d.id = 0
d.status = DownloadRepository.Status.Queued.toString()

View file

@ -3,8 +3,14 @@ package com.deniscerri.ytdlnis.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.util.NotificationUtil
import com.yausername.youtubedl_android.YoutubeDL
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class CancelDownloadNotificationReceiver : BroadcastReceiver() {
override fun onReceive(c: Context, intent: Intent) {
@ -14,6 +20,14 @@ class CancelDownloadNotificationReceiver : BroadcastReceiver() {
val notificationUtil = NotificationUtil(c)
notificationUtil.cancelDownloadNotification(id)
YoutubeDL.getInstance().destroyProcessById(id.toString())
val dbManager = DBManager.getInstance(c)
CoroutineScope(Dispatchers.IO).launch{
val item = dbManager.downloadDao.getDownloadById(id.toLong())
item.status = DownloadRepository.Status.Cancelled.toString()
dbManager.downloadDao.update(item)
}
}
}
}

View file

@ -5,14 +5,12 @@ import android.app.Activity
import android.content.*
import android.content.Context.CLIPBOARD_SERVICE
import android.content.res.ColorStateList
import android.content.res.Resources.Theme
import android.graphics.Color
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.Editable
import android.util.Log
import android.util.TypedValue
import android.view.*
import android.view.View.*
import android.widget.*
@ -36,7 +34,6 @@ import com.deniscerri.ytdlnis.adapter.HomeAdapter
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.databinding.FragmentHomeBinding
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadMultipleBottomSheetDialog
import com.deniscerri.ytdlnis.ui.downloadcard.ResultCardDetailsDialog
@ -137,7 +134,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
searchHistory = view.findViewById(R.id.search_history_scroll_view)
searchHistoryLinearLayout = view.findViewById(R.id.search_history_linear_layout)
materialToolbar!!.title = ThemeUtil.getStyledAppName(requireContext())
runCatching { materialToolbar!!.title = ThemeUtil.getStyledAppName(requireContext()) }
homeAdapter =
HomeAdapter(
@ -257,6 +254,30 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
val queriesInitStartBtn = queriesConstraint.findViewById<MaterialButton>(R.id.init_search_query)
val isRightToLeft = resources.getBoolean(R.bool.is_right_to_left)
val providersChipGroup = searchView!!.findViewById<ChipGroup>(R.id.providers)
val providers = resources.getStringArray(R.array.search_engines)
val providersValues = resources.getStringArray(R.array.search_engines_values).toMutableList()
val currentProvider = sharedPreferences?.getString("search_engine", "ytsearch")
for(i in providersValues.indices){
val provider = providers[i]
val providerValue = providersValues[i]
val tmp = layoutinflater!!.inflate(R.layout.filter_chip, providersChipGroup, false) as Chip
tmp.text = provider
tmp.id = i
if (currentProvider == providerValue) tmp.isChecked = true
tmp.setOnClickListener {
val editor = sharedPreferences?.edit()
editor?.putString("search_engine", providerValue)
editor?.apply()
}
providersChipGroup!!.addView(tmp)
}
infoUtil = InfoUtil(requireContext())
searchView!!.addTransitionListener { _, _, newState ->

View file

@ -23,7 +23,6 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.map
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
import androidx.media3.exoplayer.source.MediaSource
import androidx.media3.exoplayer.source.MergingMediaSource
@ -35,6 +34,7 @@ import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.work.WorkManager
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.adapter.ActiveDownloadAdapter
import com.deniscerri.ytdlnis.adapter.ActiveDownloadMinifiedAdapter
import com.deniscerri.ytdlnis.adapter.GenericDownloadAdapter
import com.deniscerri.ytdlnis.database.models.DownloadItem
@ -68,7 +68,6 @@ import java.util.*
class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogFragment(), GenericDownloadAdapter.OnItemClickListener, ActiveDownloadMinifiedAdapter.OnItemClickListener {
private lateinit var infoUtil: InfoUtil
private lateinit var notificationUtil: NotificationUtil
private lateinit var player: ExoPlayer
private lateinit var videoView: PlayerView
private lateinit var downloadViewModel: DownloadViewModel
private lateinit var resultViewModel: ResultViewModel
@ -232,9 +231,8 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
}
videoView = view.findViewById<PlayerView>(R.id.video_view)
videoView = view.findViewById(R.id.video_view)
val pause = view.findViewById<MaterialButton>(R.id.pause)
val frameLayout = view.findViewById<FrameLayout>(R.id.frame_layout)
val player = VideoPlayerUtil.buildPlayer(requireContext())
videoView.player = player
@ -261,7 +259,7 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
}
}
data.removeFirst()
if (data.size > 1) data.removeFirst()
if (data.isEmpty()) throw Exception("No Streaming URL found!")
if (data.size == 2){
@ -280,7 +278,6 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
player.prepare()
player.play()
}catch (e: Exception){
frameLayout.visibility = View.GONE
e.printStackTrace()
}
}
@ -536,7 +533,24 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
override fun onCancelClick(itemID: Long) {
cancelActiveDownload(itemID)
}
override fun onPauseClick(itemID: Long, action: ActiveDownloadAdapter.ActiveDownloadAction) {
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)
}
ActiveDownloadAdapter.ActiveDownloadAction.Resume -> {
item.status = DownloadRepository.Status.Active.toString()
downloadViewModel.updateDownload(item)
runBlocking {
downloadViewModel.queueDownloads(listOf(item))
}
}
}
}
override fun onCardClick() {
this.dismiss()
findNavController().navigate(
@ -544,6 +558,12 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
)
}
private fun cancelItem(id: Int){
YoutubeDL.getInstance().destroyProcessById(id.toString())
WorkManager.getInstance(requireContext()).cancelUniqueWork(id.toString())
notificationUtil.cancelDownloadNotification(id)
}
private fun cancelActiveDownload(itemID: Long){
val id = itemID.toInt()
YoutubeDL.getInstance().destroyProcessById(id.toString())

View file

@ -7,6 +7,7 @@ import android.view.View
import android.view.View.OnClickListener
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
@ -19,8 +20,11 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.util.NotificationUtil
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.runBlocking
class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickListener, OnClickListener {
@ -32,6 +36,7 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
lateinit var downloadItem: DownloadItem
private lateinit var notificationUtil: NotificationUtil
private lateinit var list: List<DownloadItem>
private lateinit var pauseResume: MaterialButton
override fun onCreateView(
@ -39,7 +44,7 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
fragmentView = inflater.inflate(R.layout.fragment_generic_download_queue, container, false)
fragmentView = inflater.inflate(R.layout.fragment_active, container, false)
activity = getActivity()
notificationUtil = NotificationUtil(requireContext())
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
@ -59,10 +64,62 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
activeRecyclerView = view.findViewById(R.id.download_recyclerview)
activeRecyclerView.adapter = activeDownloads
activeRecyclerView.layoutManager = GridLayoutManager(context, resources.getInteger(R.integer.grid_size))
pauseResume = view.findViewById(R.id.pause_resume)
pauseResume.setOnClickListener {
if (pauseResume.text == requireContext().getString(R.string.pause)){
list.forEach {
cancelItem(it.id.toInt())
it.status = DownloadRepository.Status.Paused.toString()
downloadViewModel.updateDownload(it)
val card = view.findViewWithTag<MaterialCardView>("${it.id}##card")
val progressBar = card.findViewById<LinearProgressIndicator>(R.id.progress)
val pauseButton = card.findViewById<MaterialButton>(R.id.active_download_pause)
val cancelButton = card.findViewById<MaterialButton>(R.id.active_download_delete)
progressBar.isIndeterminate = false
pauseButton.icon = ContextCompat.getDrawable(requireContext(), R.drawable.exomedia_ic_play_arrow_white)
pauseButton.tag = ActiveDownloadAdapter.ActiveDownloadAction.Resume
cancelButton.visibility = View.VISIBLE
}
}else{
val toQueue = list.filter { it.status == DownloadRepository.Status.Paused.toString() }
toQueue.forEach {
it.status = DownloadRepository.Status.Active.toString()
downloadViewModel.updateDownload(it)
val card = view.findViewWithTag<MaterialCardView>("${it.id}##card")
val progressBar = card.findViewById<LinearProgressIndicator>(R.id.progress)
val pauseButton = card.findViewById<MaterialButton>(R.id.active_download_pause)
val cancelButton = card.findViewById<MaterialButton>(R.id.active_download_delete)
pauseButton.icon = ContextCompat.getDrawable(requireContext(), R.drawable.exomedia_ic_pause_white)
progressBar.isIndeterminate = true
cancelButton.visibility = View.GONE
pauseButton.tag = ActiveDownloadAdapter.ActiveDownloadAction.Pause
}
runBlocking {
downloadViewModel.queueDownloads(toQueue)
}
}
}
downloadViewModel.activeDownloads.observe(viewLifecycleOwner) {
list = it
activeDownloads.submitList(it)
if (list.isNotEmpty()){
pauseResume.visibility = View.VISIBLE
if (list.all { l -> l.status == DownloadRepository.Status.Paused.toString() }){
pauseResume.text = requireContext().getString(R.string.resume)
}else{
pauseResume.text = requireContext().getString(R.string.pause)
}
}else{
pauseResume.visibility = View.GONE
}
it.forEach{item ->
WorkManager.getInstance(requireContext())
.getWorkInfosForUniqueWorkLiveData(item.id.toString())
@ -95,6 +152,25 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
cancelDownload(itemID)
}
override fun onPauseClick(itemID: Long, action: ActiveDownloadAdapter.ActiveDownloadAction) {
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)
}
ActiveDownloadAdapter.ActiveDownloadAction.Resume -> {
item.status = DownloadRepository.Status.Active.toString()
downloadViewModel.updateDownload(item)
runBlocking {
downloadViewModel.queueDownloads(listOf(item))
}
}
}
}
override fun onOutputClick(item: DownloadItem) {
if (item.logID != null) {
val bundle = Bundle()
@ -106,12 +182,14 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
}
}
private fun cancelDownload(itemID: Long){
val id = itemID.toInt()
private fun cancelItem(id: Int){
YoutubeDL.getInstance().destroyProcessById(id.toString())
WorkManager.getInstance(requireContext()).cancelUniqueWork(id.toString())
notificationUtil.cancelDownloadNotification(id)
}
private fun cancelDownload(itemID: Long){
cancelItem(itemID.toInt())
list.find { it.id == itemID }?.let {
it.status = DownloadRepository.Status.Cancelled.toString()
downloadViewModel.updateDownload(it)

View file

@ -115,7 +115,6 @@ class DownloadQueueMainFragment : Fragment(){
when(m.itemId){
R.id.clear_queue -> {
showDeleteDialog {
cancelAllDownloads()
downloadViewModel.cancelQueued()
}
}

View file

@ -2,8 +2,6 @@ package com.deniscerri.ytdlnis.ui.more.settings
import android.content.ComponentName
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
@ -18,10 +16,8 @@ import androidx.work.WorkInfo
import androidx.work.WorkManager
import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.ui.more.TerminalActivity
import com.deniscerri.ytdlnis.util.ThemeUtil
import com.deniscerri.ytdlnis.util.UpdateUtil
import com.google.android.gms.common.wrappers.Wrappers.packageManager
import java.util.Locale
@ -33,7 +29,7 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
private var accent: ListPreference? = null
private var highContrast: SwitchPreferenceCompat? = null
private var locale: ListPreference? = null
private var hideTerminal: SwitchPreferenceCompat? = null
private var showTerminalShareIcon: SwitchPreferenceCompat? = null
private var updateUtil: UpdateUtil? = null
private var activeDownloadCount = 0
@ -54,7 +50,7 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
accent = findPreference("theme_accent")
highContrast = findPreference("high_contrast")
locale = findPreference("locale")
hideTerminal = findPreference("hide_terminal")
showTerminalShareIcon = findPreference("show_terminal")
if(VERSION.SDK_INT < VERSION_CODES.TIRAMISU){
val values = resources.getStringArray(R.array.language_values)
@ -115,7 +111,7 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
true
}
hideTerminal!!.onPreferenceChangeListener =
showTerminalShareIcon!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { pref: Preference?, _: Any ->
val packageManager = requireContext().packageManager
val aliasComponentName = ComponentName(requireContext(), "com.deniscerri.ytdlnis.terminalShareAlias")

View file

@ -2,9 +2,15 @@ package com.deniscerri.ytdlnis.util
import android.content.Context
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.storage.StorageManager
import android.os.storage.StorageVolume
import android.provider.DocumentsContract
import android.util.Log
import android.webkit.MimeTypeMap
import androidx.core.net.toFile
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile
import com.anggrayudi.storage.callback.FileCallback
@ -12,11 +18,12 @@ import com.anggrayudi.storage.callback.FolderCallback
import com.anggrayudi.storage.file.copyFileTo
import com.anggrayudi.storage.file.copyFolderTo
import com.anggrayudi.storage.file.getAbsolutePath
import com.anggrayudi.storage.file.moveFileTo
import com.anggrayudi.storage.file.moveFolderTo
import com.anggrayudi.storage.file.getBasePath
import com.deniscerri.ytdlnis.App
import com.deniscerri.ytdlnis.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.internal.closeQuietly
import java.io.File
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
@ -32,15 +39,10 @@ import kotlin.math.pow
object FileUtil {
fun deleteFile(path: String){
val file = File(path)
if (file.exists()) {
file.delete()
}
}
internal object Compare {
fun max(a: File, b: File): File {
return if (a.length() > b.length()) a else b
runCatching {
if (!File(path).delete()){
DocumentFile.fromSingleUri(App.instance, Uri.parse(path))?.delete()
}
}
}
@ -100,7 +102,7 @@ object FileUtil {
withContext(Dispatchers.IO){
curr.copyFolderTo(context, dst!!, skipEmptyFiles = false, callback = object : FolderCallback() {
override fun onStart(folder: DocumentFile, totalFilesToCopy: Int, workerThread: Thread): Long {
return 1000 // update progress every 1 second
return 500 // update progress every half second
}
override fun onParentConflict(destinationFolder: DocumentFile, action: ParentFolderConflictAction, canMerge: Boolean) {
@ -120,11 +122,66 @@ object FileUtil {
it.deleteRecursively()
}
override fun onFailed(errorCode: ErrorCode) {
//if its usb?
runCatching {
it.walkTopDown().forEach { f ->
if (f.isDirectory) return@forEach
val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(f.extension) ?: "*/*"
val destUri = DocumentsContract.createDocument(
context.contentResolver,
dst.uri,
mimeType,
f.name
) ?: return@runCatching
val inputStream = f.inputStream()
val outputStream =
context.contentResolver.openOutputStream(destUri) ?: return@runCatching
inputStream.copyTo(outputStream)
inputStream.closeQuietly()
outputStream.closeQuietly()
fileList.add(DocumentFile.fromSingleUri(context, destUri)!!.getAbsolutePath(context))
}
it.deleteRecursively()
}
super.onFailed(errorCode)
}
})
}
}else{
withContext(Dispatchers.IO){
curr.copyFileTo(context, dst!!, callback = object : FileCallback() {
override fun onFailed(errorCode: ErrorCode) {
//if its usb?
runCatching {
val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(it.extension) ?: "*/*"
val destUri = DocumentsContract.createDocument(
context.contentResolver,
dst.uri,
mimeType,
it.name
) ?: return@runCatching
val inputStream = it.inputStream()
val outputStream =
context.contentResolver.openOutputStream(destUri) ?: return@runCatching
inputStream.copyTo(outputStream)
inputStream.closeQuietly()
outputStream.closeQuietly()
fileList.add(DocumentFile.fromSingleUri(context, destUri)!!.getAbsolutePath(context))
it.delete()
}
super.onFailed(errorCode)
}
override fun onStart(file: Any, workerThread: Thread): Long {
return 1000 // update progress every 1 second
}
@ -136,7 +193,7 @@ object FileUtil {
override fun onCompleted(result: Any) {
destFile = (result as DocumentFile)
fileList.add(destFile.getAbsolutePath(context))
it.deleteRecursively()
it.delete()
super.onCompleted(result)
}
})
@ -170,10 +227,9 @@ object FileUtil {
}
}
private fun scanMedia(files: List<String>, context: Context) : List<String> {
try {
val paths = files.sortedByDescending { File(it).length() }
MediaScannerConnection.scanFile(context, paths.toTypedArray(), null, null)
runCatching {MediaScannerConnection.scanFile(context, paths.toTypedArray(), null, null) }
return paths
}catch (e: Exception){
e.printStackTrace()

View file

@ -8,6 +8,7 @@ import android.util.Log
import android.widget.Toast
import androidx.preference.PreferenceManager
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.Converters
import com.deniscerri.ytdlnis.database.models.ChapterItem
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.Format
@ -452,12 +453,12 @@ 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()){
if (cookiesFile.exists()) {
request.addOption("--cookies", cookiesFile.absolutePath)
}
val proxy = sharedPreferences.getString("proxy", "")
if (proxy!!.isNotBlank()){
if (proxy!!.isNotBlank()) {
request.addOption("--proxy", proxy)
}
@ -471,34 +472,7 @@ class InfoUtil(private val context: Context) {
val json = results[0]
val jsonArray = JSONArray(json)
val formats : ArrayList<Format> = ArrayList()
for (f in 0 until jsonArray.length()){
val format = jsonArray.getJSONObject(f)
if (format.has("filesize")){
if (format.get("filesize") == "None"){
format.remove("filesize")
format.put("filesize", 0)
}
try{
val size = format.get("filesize").toString().toFloat()
format.remove("filesize")
format.put("filesize", size)
}catch (ignored: Exception){}
}
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
if (format.has("format_note")){
if (!formatProper!!.format_note.contains("audio only", true)) {
formatProper.format_note = format.getString("format_note")
}else{
formatProper.format_note = "${format.getString("format_note")} audio"
}
}
if (formatProper!!.format_note == "storyboard") continue
formatProper.container = format.getString("ext")
formats.add(formatProper)
}
return formats
return parseYTDLFormats(jsonArray)
} catch (e: Exception) {
Looper.prepare().run {
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
@ -540,35 +514,8 @@ class InfoUtil(private val context: Context) {
YoutubeDL.getInstance().execute(request){ progress, _, line ->
try{
val formats = mutableListOf<Format>()
val listOfStrings = JSONArray(line)
for (f in 0 until listOfStrings.length()){
val format = listOfStrings.get(f) as JSONObject
try{
if (format.getString("filesize") == "None") continue
}catch (e: Exception) { continue }
if (format.has("filesize")){
if (format.get("filesize") == "None"){
format.remove("filesize")
format.put("filesize", 0)
}
try{
val size = format.get("filesize").toString().toFloat()
format.remove("filesize")
format.put("filesize", size)
}catch (ignored: Exception){}
}
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
if ( !formatProper!!.format_note.contains("audio only", true)) {
formatProper.format_note = format.getString("format_note")
}else{
formatProper.format_note = "${format.getString("format_note")} audio"
}
if (formatProper.format_note == "storyboard") continue
formatProper.container = format.getString("ext")
formats.add(formatProper)
}
progress(formats)
progress(parseYTDLFormats(listOfStrings))
}catch (e: Exception){
progress(emptyList())
}
@ -668,37 +615,7 @@ class InfoUtil(private val context: Context) {
if (jsonObject.has("playlist_title")) playlistTitle = jsonObject.getString("playlist_title")
if(playlistTitle.equals(query)) playlistTitle = ""
val formatsInJSON = if (jsonObject.has("formats") && jsonObject.get("formats") is JSONArray) jsonObject.getJSONArray("formats") else null
val formats : ArrayList<Format> = ArrayList()
if (formatsInJSON != null) {
for (f in 0 until formatsInJSON.length()){
val format = formatsInJSON.getJSONObject(f)
if (format.has("filesize")){
if (format.get("filesize") == "None"){
format.remove("filesize")
format.put("filesize", 0)
}
try{
val size = format.get("filesize").toString().toFloat()
format.remove("filesize")
format.put("filesize", size)
}catch (ignored: Exception){}
}
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
if (formatProper.format_note == null) continue
if (format.has("format_note")){
if (!formatProper!!.format_note.contains("audio only", true)) {
formatProper.format_note = format.getString("format_note")
}else{
if (!formatProper.format_note.endsWith("audio", true)){
formatProper.format_note = "${format.getString("format_note")} audio"
}
}
}
if (formatProper.format_note == "storyboard") continue
formatProper.container = format.getString("ext")
formats.add(formatProper)
}
}
val formats : ArrayList<Format> = parseYTDLFormats(formatsInJSON)
val chaptersInJSON = if (jsonObject.has("chapters") && jsonObject.get("chapters") is JSONArray) jsonObject.getJSONArray("chapters") else null
val listType: Type = object : TypeToken<List<ChapterItem>>() {}.type
@ -734,6 +651,47 @@ class InfoUtil(private val context: Context) {
return items
}
private fun parseYTDLFormats(formatsInJSON: JSONArray?) : ArrayList<Format> {
val formats = arrayListOf<Format>()
if (formatsInJSON != null) {
for (f in 0 until formatsInJSON.length()){
val format = formatsInJSON.getJSONObject(f)
if (format.has("filesize")){
if (format.get("filesize") == "None"){
format.remove("filesize")
if (format.has("filesize_approx") && format.get("filesize_approx") != "None"){
format.put("filesize", format.getInt("filesize_approx"))
}else{
format.put("filesize", 0)
}
}
try{
val size = format.get("filesize").toString().toFloat()
format.remove("filesize")
format.put("filesize", size)
}catch (ignored: Exception){}
}
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
if (formatProper.format_note == null) continue
if (format.has("format_note")){
if (!formatProper!!.format_note.contains("audio only", true)) {
formatProper.format_note = format.getString("format_note")
}else{
if (!formatProper.format_note.endsWith("audio", true)){
formatProper.format_note = "${format.getString("format_note")} audio"
}
}
}
if (formatProper.format_note == "storyboard") continue
formatProper.container = format.getString("ext")
formats.add(formatProper)
}
}
return formats
}
fun getMissingInfo(url: String): ResultItem? {
try {
val request = YoutubeDLRequest(url)
@ -885,33 +843,57 @@ class InfoUtil(private val context: Context) {
fun getStreamingUrlAndChapters(url: String) : MutableList<String?> {
try {
val request = YoutubeDLRequest(url)
request.addOption("--get-url")
request.addOption("--print", "%(chapters)s")
request.addOption("--skip-download")
request.addOption("-R", "1")
request.addOption("--socket-timeout", "5")
val p = Pattern.compile("(^(https?)://(www.)?(music.)?youtu(.be)?)|(^(https?)://(www.)?piped.video)")
val m = p.matcher(url)
val cookiesFile = File(context.cacheDir, "cookies.txt")
if (cookiesFile.exists()){
request.addOption("--cookies", cookiesFile.absolutePath)
if (m.find()){
return getStreamingUrlAndChaptersFromPIPED(getIDFromYoutubeURL(url))
}else{
throw Exception()
}
}catch (e: Exception) {
try {
val request = YoutubeDLRequest(url)
request.addOption("--get-url")
request.addOption("--print", "%(chapters)s")
request.addOption("--skip-download")
request.addOption("-R", "1")
request.addOption("--socket-timeout", "5")
val proxy = sharedPreferences.getString("proxy", "")
if (proxy!!.isNotBlank()){
request.addOption("--proxy", proxy)
}
val cookiesFile = File(context.cacheDir, "cookies.txt")
if (cookiesFile.exists()){
request.addOption("--cookies", cookiesFile.absolutePath)
}
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
val results: Array<String?> = try {
val lineSeparator = System.getProperty("line.separator")
youtubeDLResponse.out.split(lineSeparator!!).toTypedArray()
val proxy = sharedPreferences.getString("proxy", "")
if (proxy!!.isNotBlank()){
request.addOption("--proxy", proxy)
}
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
val results: Array<String?> = try {
val lineSeparator = System.getProperty("line.separator")
youtubeDLResponse.out.split(lineSeparator!!).toTypedArray()
} catch (e: Exception) {
arrayOf(youtubeDLResponse.out)
}
return results.filter { it!!.isNotEmpty() }.toMutableList()
} catch (e: Exception) {
arrayOf(youtubeDLResponse.out)
return mutableListOf()
}
return results.filter { it!!.isNotEmpty() }.toMutableList()
} catch (e: Exception) {
return mutableListOf()
}
}
private fun getStreamingUrlAndChaptersFromPIPED(id: String) : MutableList<String?> {
val res = genericRequest("$pipedURL/streams/$id")
if (res.length() == 0) {
throw Exception()
}else{
val item = createVideoFromPipedJSON(res, id)
if (item!!.urls.isBlank()) throw Exception()
val list = mutableListOf<String?>(Gson().toJson(item.chapters))
list.addAll(item.urls.split(","))
return list
}
}
@ -978,7 +960,6 @@ 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$",""))
if (downloadItem.customFileNameTemplate.isBlank()) downloadItem.customFileNameTemplate = "%(uploader|${downloadItem.author})s - %(title)s"
downloadItem.customFileNameTemplate = downloadItem.customFileNameTemplate.replace("%(uploader)s", "%(uploader|${downloadItem.author})s")
if (downloadItem.downloadSections.isNotBlank()){
@ -1050,38 +1031,41 @@ class InfoUtil(private val context: Context) {
}
}
request.addOption("--embed-metadata")
if (downloadItem.audioPreferences.embedThumb) {
request.addOption("--embed-thumbnail")
request.addOption("--convert-thumbnails", "jpg")
if (sharedPreferences.getBoolean("crop_thumbnail", true)){
try {
val config = File(context.cacheDir.absolutePath + "/downloads/${downloadItem.id}/config" + downloadItem.title + "##" + downloadItem.format.format_id + ".txt")
val configData = "--ppa \"ffmpeg: -c:v mjpeg -vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\""
config.writeText(configData)
request.addOption("--ppa", "ThumbnailsConvertor:-qmin 1 -q:v 1")
request.addOption("--config", config.absolutePath)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
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")
}
request.addOption("-P", tempFileDir.absolutePath)
if (downloadItem.audioPreferences.splitByChapters && downloadItem.downloadSections.isBlank()){
request.addOption("--split-chapters")
request.addOption("-o", "chapter:%(section_title)s.%(ext)s")
}else{
request.addOption("-o", "${downloadItem.customFileNameTemplate}.%(ext)s")
request.addOption("--embed-metadata")
if (downloadItem.audioPreferences.embedThumb) {
request.addOption("--embed-thumbnail")
request.addOption("--convert-thumbnails", "jpg")
if (sharedPreferences.getBoolean("crop_thumbnail", true)){
try {
val config = File(context.cacheDir.absolutePath + "/downloads/${downloadItem.id}/config" + downloadItem.title + "##" + downloadItem.format.format_id + ".txt")
val configData = "--ppa \"ffmpeg: -c:v mjpeg -vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\""
config.writeText(configData)
request.addOption("--ppa", "ThumbnailsConvertor:-qmin 1 -q:v 1")
request.addOption("--config", config.absolutePath)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
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")
}
}
}
@ -1149,7 +1133,9 @@ class InfoUtil(private val context: Context) {
if (downloadItem.videoPreferences.splitByChapters && downloadItem.downloadSections.isBlank()){
request.addOption("--split-chapters")
}else{
request.addOption("-o", "${downloadItem.customFileNameTemplate}.%(ext)s")
if (downloadItem.customFileNameTemplate.isNotBlank()){
request.addOption("-o", "${downloadItem.customFileNameTemplate}.%(ext)s")
}
}
}

View file

@ -117,8 +117,8 @@ object ThemeUtil {
private fun getThemeColor(context: Context, colorCode: Int): Int {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
val accent = sharedPreferences.getString("theme_accent", "blue")
return if (accent == "Default" || accent == "blue"){
"c22727".toInt(16)
return if (accent == "blue"){
"d43c3b".toInt(16)
}else{
val value = TypedValue()
context.theme.resolveAttribute(colorCode, value, true)

View file

@ -17,6 +17,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.inputmethod.InputMethodManager
import android.webkit.MimeTypeMap
import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout
@ -251,7 +252,7 @@ object UiUtil {
fragmentContext.packageName + ".fileprovider",
file
)
val mime = fragmentContext.contentResolver.getType(uri)
val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(file.extension)
val i = Intent(Intent.ACTION_VIEW)
i.setDataAndType(uri, mime)
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

View file

@ -60,7 +60,7 @@ class DownloadWorker(
return Result.failure()
}
if (downloadItem.status != DownloadRepository.Status.Queued.toString()) return Result.failure()
if (downloadItem.status != DownloadRepository.Status.Queued.toString() && downloadItem.status != DownloadRepository.Status.Paused.toString()) return Result.failure()
val pendingIntent = NavDeepLinkBuilder(context)
.setGraph(R.navigation.nav_graph)
@ -134,7 +134,7 @@ class DownloadWorker(
}.onSuccess {
val wasQuickDownloaded = updateDownloadItem(downloadItem, infoUtil, dao, resultDao)
CoroutineScope(Dispatchers.IO).launch {
runBlocking {
var finalPaths : List<String>?
//move file from internal to set download directory
setProgressAsync(workDataOf("progress" to 100, "output" to "Moving file to ${FileUtil.formatPath(downloadLocation)}", "id" to downloadItem.id, "log" to logDownloads))
@ -174,30 +174,26 @@ class DownloadWorker(
downloadItem.title, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else finalPaths,
NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID
)
}
if (wasQuickDownloaded){
runCatching {
setProgressAsync(workDataOf("progress" to 100, "output" to "Creating Result Items", "id" to downloadItem.id, "log" to false))
runBlocking {
infoUtil.getFromYTDL(downloadItem.url).forEach { res ->
if (res != null) {
resultDao.insert(res)
if (wasQuickDownloaded){
runCatching {
setProgressAsync(workDataOf("progress" to 100, "output" to "Creating Result Items", "id" to downloadItem.id, "log" to false))
runBlocking {
infoUtil.getFromYTDL(downloadItem.url).forEach { res ->
if (res != null) {
resultDao.insert(res)
}
}
}
}
}
}
runBlocking {
dao.delete(downloadItem.id)
}
}.onFailure {
if (it is YoutubeDL.CanceledException) {
downloadItem.status = DownloadRepository.Status.Cancelled.toString()
runBlocking {
dao.update(downloadItem)
}
return Result.failure(
Data.Builder().putString("output", "Download has been cancelled!").build()
)

View file

@ -0,0 +1,5 @@
<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:textColorPrimary" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
</vector>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<padding
android:top="10dp"
android:left="10dp"
android:right="10dp"
android:bottom="10dp"/>
</shape>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="@drawable/line"/>
<item
android:drawable="@drawable/line"/>
</selector>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:pivotX="65%"
android:pivotY="20%"
android:toDegrees="250"
android:fromDegrees="270">
<shape android:shape="rectangle">
<solid android:color="?android:colorAccent"/>
<size
android:width="40dp"
android:height="20dp"/>
<corners
android:topLeftRadius="20dp"
android:topRightRadius="20dp"/>
</shape>
</rotate>

View file

@ -0,0 +1,8 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="@drawable/thumb"/>
<item
android:drawable="@drawable/thumb"/>
</selector>

View file

@ -27,6 +27,28 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<HorizontalScrollView
android:layout_width="wrap_content"
android:scrollbars="none"
android:layout_height="wrap_content">
<com.google.android.material.chip.ChipGroup
android:id="@+id/providers"
android:layout_margin="10dp"
app:chipSpacingVertical="-10dp"
app:singleSelection="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:singleLine="true" />
</HorizontalScrollView>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
style="@style/Divider.Vertical"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/queries_constraint"
android:layout_width="match_parent"
@ -132,9 +154,12 @@
android:id="@+id/recyclerViewHome"
android:orientation="vertical"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:scrollbars="none"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
android:clipToPadding="false"
android:paddingBottom="100dp"
app:spanCount="3"

View file

@ -27,6 +27,28 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<HorizontalScrollView
android:layout_width="wrap_content"
android:scrollbars="none"
android:layout_height="wrap_content">
<com.google.android.material.chip.ChipGroup
android:id="@+id/providers"
android:layout_margin="10dp"
app:chipSpacingVertical="-10dp"
app:singleSelection="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:singleLine="true" />
</HorizontalScrollView>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
style="@style/Divider.Vertical"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/queries_constraint"
android:layout_width="match_parent"
@ -147,9 +169,13 @@
android:id="@+id/recyclerViewHome"
android:orientation="vertical"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:scrollbars="none"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
android:clipToPadding="false"
android:paddingBottom="100dp"
app:spanCount="2"

View file

@ -27,6 +27,28 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<HorizontalScrollView
android:layout_width="wrap_content"
android:scrollbars="none"
android:layout_height="wrap_content">
<com.google.android.material.chip.ChipGroup
android:id="@+id/providers"
android:layout_margin="10dp"
app:singleSelection="true"
app:chipSpacingVertical="-10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:singleLine="true" />
</HorizontalScrollView>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
style="@style/Divider.Vertical"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/queries_constraint"
android:layout_width="match_parent"
@ -132,9 +154,12 @@
android:id="@+id/recyclerViewHome"
android:orientation="vertical"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:scrollbars="none"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
android:clipToPadding="false"
android:paddingBottom="100dp"
app:spanCount="4"

View file

@ -108,7 +108,7 @@
android:dividerPadding="5dp"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/output"
app:layout_constraintEnd_toStartOf="@+id/active_download_cancel"
app:layout_constraintEnd_toStartOf="@+id/active_download_pause"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearlayout2_horizontalscrollview">
@ -152,14 +152,30 @@
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/active_download_cancel"
android:id="@+id/active_download_pause"
style="?attr/materialIconButtonFilledStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
app:backgroundTint="?attr/colorSurface"
app:cornerRadius="15dp"
app:icon="@drawable/ic_cancel"
android:layout_marginEnd="5dp"
app:icon="@drawable/exomedia_ic_play_arrow_white"
app:iconSize="30dp"
app:iconTint="?android:textColorPrimary"
app:layout_constraintBottom_toTopOf="@+id/output"
app:layout_constraintEnd_toStartOf="@id/active_download_delete"
app:layout_constraintTop_toBottomOf="@+id/linearlayout2_horizontalscrollview"
app:layout_constraintVertical_bias="0.51" />
<com.google.android.material.button.MaterialButton
android:id="@+id/active_download_delete"
style="?attr/materialIconButtonFilledStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
app:backgroundTint="?attr/colorSurface"
app:cornerRadius="15dp"
app:icon="@drawable/baseline_delete_24"
app:iconSize="30dp"
app:iconTint="?android:textColorPrimary"
app:layout_constraintBottom_toTopOf="@+id/output"

View file

@ -15,63 +15,53 @@
app:layout_constraintTop_toTopOf="parent"
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar"
app:strokeWidth="0dp"
app:cardElevation="10dp"
app:cardMaxElevation="12dp"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:alpha="0.3"
android:scaleY="200"
app:trackColor="#000" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:paddingHorizontal="20dp"
android:paddingHorizontal="10dp"
android:paddingVertical="10dp"
android:layout_height="wrap_content">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image_view"
<FrameLayout
android:id="@+id/image_frame"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintVertical_bias="0.0"
android:adjustViewBounds="true"
android:background="?attr/colorSurfaceVariant"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="H,16:9"
app:layout_constraintEnd_toStartOf="@+id/download_item_data"
app:layout_constraintHorizontal_weight="0.3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar" />
app:layout_constraintDimensionRatio="H,16:9"
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar"
android:adjustViewBounds="true"
app:layout_constraintEnd_toStartOf="@+id/download_item_data"
app:layout_constraintBottom_toBottomOf="parent"
>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress"
android:layout_width="match_parent"
android:elevation="10dp"
app:layout_constraintStart_toStartOf="@id/image_view"
app:layout_constraintEnd_toEndOf="@id/image_view"
app:layout_constraintBottom_toBottomOf="@id/image_view"
app:layout_constraintTop_toTopOf="@id/image_view"
android:layout_height="0dp"
android:layout_gravity="bottom"
android:alpha="0.6"
android:scaleY="200"
app:trackColor="#000" />
<TextView
android:id="@+id/duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:background="#80000000"
android:clickable="false"
android:ellipsize="end"
android:gravity="center"
android:maxLength="17"
android:maxLines="1"
android:minWidth="30dp"
android:paddingHorizontal="5dp"
android:textColor="@color/white"
android:textSize="12sp"
android:textStyle="bold"
app:cornerRadius="10dp"
app:layout_constraintBottom_toBottomOf="@+id/image_view"
app:layout_constraintEnd_toEndOf="@+id/image_view" />
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintVertical_bias="0.0"
android:background="?attr/colorSurfaceVariant"
android:scaleType="centerCrop"/>
</FrameLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/download_item_data"
@ -79,9 +69,9 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/active_download_cancel"
app:layout_constraintEnd_toStartOf="@id/active_download_pause"
app:layout_constraintHorizontal_weight="0.7"
app:layout_constraintStart_toEndOf="@+id/image_view"
app:layout_constraintStart_toEndOf="@+id/image_frame"
app:layout_constraintTop_toTopOf="parent">
<TextView
@ -139,14 +129,29 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/active_download_cancel"
style="?attr/materialIconButtonFilledStyle"
android:id="@+id/active_download_pause"
style="@style/Widget.Material3.Button.IconButton"
app:layout_constraintVertical_bias="0.0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:backgroundTint="?attr/colorSurface"
android:background="?android:attr/selectableItemBackground"
app:cornerRadius="15dp"
app:icon="@drawable/ic_cancel"
app:icon="@drawable/exomedia_ic_pause_white"
app:iconSize="30dp"
app:iconTint="?android:textColorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/active_download_delete"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/active_download_delete"
style="@style/Widget.Material3.Button.IconButton"
app:layout_constraintVertical_bias="0.0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
app:cornerRadius="15dp"
app:icon="@drawable/baseline_delete_24"
app:iconSize="30dp"
app:iconTint="?android:textColorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
@ -159,7 +164,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="download_item_data,active_download_cancel" />
app:constraint_referenced_ids="download_item_data,active_download_delete" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -129,7 +129,7 @@
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/from_textinput"
style="@style/Widget.Material3.TextInputLayout.FilledBox"
android:layout_width="wrap_content"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:hint="@string/start">
@ -154,7 +154,7 @@
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/to_textinput"
style="@style/Widget.Material3.TextInputLayout.FilledBox"
android:layout_width="wrap_content"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:hint="@string/end"
app:layout_constraintBottom_toBottomOf="parent"

View file

@ -92,6 +92,11 @@
android:paddingTop="10dp"
android:layout_height="wrap_content"
android:orientation="vertical"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
android:paddingBottom="70dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

View file

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="100dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<com.google.android.material.button.MaterialButton
android:id="@+id/pause_resume"
android:visibility="gone"
style="@style/Widget.Material3.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="30dp"
android:paddingVertical="10dp"
android:text="@string/resume"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/download_recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
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">
</androidx.recyclerview.widget.RecyclerView>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -4,6 +4,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="200dp"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">

View file

@ -112,8 +112,11 @@
android:orientation="vertical"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
android:clipToPadding="false"
android:paddingBottom="100dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager">

View file

@ -27,6 +27,28 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<HorizontalScrollView
android:layout_width="wrap_content"
android:scrollbars="none"
android:layout_height="wrap_content">
<com.google.android.material.chip.ChipGroup
android:id="@+id/providers"
android:layout_margin="10dp"
app:chipSpacingVertical="-10dp"
android:layout_width="wrap_content"
app:singleSelection="true"
android:layout_height="wrap_content"
app:singleLine="true" />
</HorizontalScrollView>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
style="@style/Divider.Vertical"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/queries_constraint"
android:layout_width="match_parent"
@ -135,11 +157,16 @@
android:id="@+id/recyclerViewHome"
android:orientation="vertical"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:scrollbars="none"
android:clipToPadding="false"
android:paddingBottom="100dp"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
/>

View file

@ -2,6 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="100dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
@ -22,6 +23,11 @@
android:id="@+id/download_recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"

View file

@ -24,6 +24,7 @@
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"

View file

@ -2,6 +2,8 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout

View file

@ -2,6 +2,8 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
xmlns:android="http://schemas.android.com/apk/res/android">
@ -53,7 +55,7 @@
android:layout_height="0dp"
android:dividerPadding="5dp"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/download_button_layout"
app:layout_constraintBottom_toTopOf="@+id/download_music"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
@ -62,15 +64,15 @@
android:id="@+id/result_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="10dp"
android:maxLines="2"
android:ellipsize="end"
android:maxLines="2"
android:paddingStart="10dp"
android:paddingTop="10dp"
android:paddingEnd="10dp"
android:scrollbars="none"
android:shadowColor="@color/black"
android:shadowDx="4"
android:shadowDy="4"
android:scrollbars="none"
android:shadowRadius="2"
android:textColor="#FFF"
android:textSize="17sp"
@ -80,15 +82,15 @@
android:id="@+id/author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom"
android:paddingStart="10dp"
android:maxLines="1"
android:ellipsize="end"
android:gravity="bottom"
android:maxLines="1"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:scrollbars="none"
android:shadowColor="@color/black"
android:shadowDx="4"
android:shadowDy="4"
android:scrollbars="none"
android:shadowRadius="1.5"
android:textColor="#FFF"
android:textSize="12sp"
@ -115,33 +117,32 @@
app:layout_constraintStart_toStartOf="parent" />
<LinearLayout
android:id="@+id/download_button_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp"
<com.google.android.material.button.MaterialButton
android:id="@+id/download_music"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Icon.Secondary"
android:layout_width="55dp"
app:elevation="0dp"
android:elevation="0dp"
app:borderWidth="0dp"
android:layout_height="55dp"
android:layout_marginVertical="10dp"
android:layout_marginHorizontal="5dp"
app:icon="@drawable/ic_music"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent">
app:layout_constraintEnd_toStartOf="@+id/download_video" />
<com.google.android.material.button.MaterialButton
android:id="@+id/download_music"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Icon.Secondary"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_marginVertical="5dp"
android:layout_marginStart="5dp"
app:icon="@drawable/ic_music" />
<com.google.android.material.button.MaterialButton
android:id="@+id/download_video"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Icon.Secondary"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_margin="5dp"
app:icon="@drawable/ic_video" />
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/download_video"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Icon.Secondary"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_margin="10dp"
app:elevation="0dp"
android:elevation="0dp"
app:borderWidth="0dp"
app:icon="@drawable/ic_video"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -78,6 +78,7 @@
android:id="@+id/title"
android:layout_width="0dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="10dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:ellipsize="end"
@ -85,7 +86,7 @@
android:singleLine="false"
android:text="@string/app_name"
android:textSize="17sp"
app:layout_constraintEnd_toStartOf="@+id/download_button_layout"
app:layout_constraintEnd_toStartOf="@+id/download_music"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@ -94,53 +95,46 @@
android:id="@+id/bottom_info"
android:layout_width="0dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="10dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:singleLine="false"
android:text="@string/app_name"
android:textSize="12sp"
app:layout_constraintEnd_toStartOf="@+id/download_button_layout"
app:layout_constraintEnd_toStartOf="@+id/download_music"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title" />
<LinearLayout
android:id="@+id/download_button_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginEnd="20dp"
android:orientation="horizontal"
<com.google.android.material.button.MaterialButton
android:id="@+id/download_music"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Icon.Secondary"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_marginVertical="10dp"
android:layout_marginEnd="5dp"
app:icon="@drawable/ic_music"
app:layout_constraintEnd_toStartOf="@+id/download_video"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/download_video"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Icon.Secondary"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_margin="10dp"
app:icon="@drawable/ic_video"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.button.MaterialButton
android:id="@+id/download_music"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Icon.Secondary"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_marginVertical="5dp"
android:layout_marginStart="5dp"
app:icon="@drawable/ic_music" />
<com.google.android.material.button.MaterialButton
android:id="@+id/download_video"
style="@style/Widget.Material3.ExtendedFloatingActionButton.Icon.Secondary"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_margin="5dp"
app:icon="@drawable/ic_video" />
</LinearLayout>
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/title_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="bottom_info,download_button_layout" />
app:constraint_referenced_ids="bottom_info,download_music" />
<androidx.core.widget.NestedScrollView

View file

@ -5,6 +5,8 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">

View file

@ -85,7 +85,6 @@
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@ -96,10 +95,9 @@
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/from_textinput"
style="@style/Widget.Material3.TextInputLayout.FilledBox"
android:layout_width="70dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
app:errorEnabled="true"
android:hint="@string/start">
<com.google.android.material.textfield.TextInputEditText
@ -112,10 +110,9 @@
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/to_textinput"
style="@style/Widget.Material3.TextInputLayout.FilledBox"
android:layout_width="70dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@string/end"
app:errorEnabled="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/colon">
@ -139,6 +136,11 @@
android:focusableInTouchMode="true"
android:orientation="vertical"
android:paddingBottom="80dp"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

View file

@ -12,7 +12,7 @@
<item
android:id="@+id/redownload"
android:title="@string/redownload"
android:icon="@drawable/ic_refresh"
android:icon="@drawable/ic_refresh_white"
app:showAsAction="ifRoom" />
<item

View file

@ -241,8 +241,7 @@
<string name="sponsorblock">حاجب شرائح الإعلانات</string>
<string name="keep_cache">الاحتفاظ بالملفات المؤقتة</string>
<string name="backup">عمل نسخة احتياطية</string>
<string name="pause">إيقاف
\n</string>
<string name="pause">إيقاف</string>
<string name="resume">استئناف</string>
<string name="updating_download_data">يتم جلب بيانات التحميل</string>
<string name="keep_cache_summary">عدم مسح ملفات الكاش بعد اكتمال التحميل</string>

View file

@ -236,8 +236,7 @@
<string name="preferred_format_id">Üstünlük Verilən Format ID</string>
<string name="download_already_exists">Yükləmə artıq var</string>
<string name="preferred_locale">Üstünlük Verilən Yer</string>
<string name="pause">Dayandır
\n</string>
<string name="pause">Dayandır</string>
<string name="resume">Təkrar başlat</string>
<string name="updating_download_data">Yükləmə Element Məlumatı Yenilənir</string>
<string name="keep_cache">Qalığı Saxla</string>
@ -282,7 +281,7 @@
<string name="subtitle_format">Titr Formatı
\n</string>
<string name="hide_thumbnails">Miniatürləri Gizlət</string>
<string name="hide_terminal">Sıxacı paylaşma menyusundan gizlət</string>
<string name="show_terminal">Sıxacı paylaşma menyusundan gizlət</string>
<string name="saved">Saxlanıldı</string>
<string name="clear_saved">Saxlanılanları Təmizlə</string>
<string name="use_extra_commands">Əlavə Əmrlər Əlavə Et</string>

View file

@ -232,8 +232,7 @@
<string name="quick_download">Schneller Download</string>
<string name="quick_download_summary">Daten nicht vorab abrufen</string>
<string name="download_already_exists">Download bereits vorhanden</string>
<string name="pause">Pause
\n</string>
<string name="pause">Pause</string>
<string name="backup">Sichern</string>
<string name="restore">Wiederherstellen</string>
<string name="network_error">Netzwerkfehler</string>

View file

@ -221,8 +221,7 @@
<string name="download_now">Λήψη Τώρα</string>
<string name="download_already_exists">Η λήψη υπάρχει ήδη</string>
<string name="preferred_locale">Προτιμώμενες Τοπικές Ρυθμίσεις</string>
<string name="pause">Παύση
\n</string>
<string name="pause">Παύση</string>
<string name="resume">Συνέχεια</string>
<string name="incognito">Ανώνυμη Λειτουργία</string>
<string name="sponsorblock">SponsorBlock</string>

View file

@ -236,8 +236,7 @@
<string name="finished_download_notification_channel_name">Descargas terminadas</string>
<string name="finished_download_notification_channel_description">Notificación de descargas finalizadas o defectuosas</string>
<string name="preferred_locale">Ubicación preferida</string>
<string name="pause">Pausa
\n</string>
<string name="pause">Pausa</string>
<string name="resume">Resumen</string>
<string name="updating_download_data">Actualizando los datos de los elementos de la descarga</string>
<string name="keep_cache">Conservar la caché</string>
@ -285,7 +284,7 @@
\n</string>
<string name="hide_thumbnails">Ocultar las miniaturas</string>
<string name="saved">Guardado</string>
<string name="hide_terminal">Ocultar Terminal del menú compartir</string>
<string name="show_terminal">Ocultar Terminal del menú compartir</string>
<string name="save_for_later">¿Guardar la descarga para más tarde\?</string>
<string name="clear_saved">Borrar lo Guardado</string>
</resources>

View file

@ -153,8 +153,7 @@
<string name="preferred_format_id">पसंदीदा प्रारूप आईडी</string>
<string name="download_now">तुरंत डाउनलोड करें</string>
<string name="preferred_locale">पसंदीदा लोकेल</string>
<string name="pause">रोको
\n</string>
<string name="pause">रोको</string>
<string name="resume">चालू करो</string>
<string name="keep_cache">कैचे रखें</string>
<string name="keep_cache_summary">डाउनलोड समाप्त होने के बाद अस्थायी फ़ाइलें न हटाएं</string>

View file

@ -235,8 +235,7 @@
<string name="preferred_format_id">Preferensi ID Format</string>
<string name="download_now">Unduh Sekarang</string>
<string name="remove_audio">Hilangkan Audio</string>
<string name="pause">Jeda
\n</string>
<string name="pause">Jeda</string>
<string name="updating_download_data">Memutakhirkan Data Butir Unduhan</string>
<string name="preferred_locale">Preferensi Wilayah</string>
<string name="resume">Lanjutkan</string>
@ -278,9 +277,8 @@
<string name="format_order">Urutan Format</string>
<string name="force_keyframes">Paksakan Keyframe saat Pemotongan</string>
<string name="force_keyframes_summary">Proses yang lebih lambat, tetapi pemotongannya lebih akurat</string>
<string name="hide_terminal">Sembunyikan Terminal dari menu berbagi</string>
<string name="subtitle_format">Format Takarir (Subtitle)
\n</string>
<string name="show_terminal">Sembunyikan Terminal dari menu berbagi</string>
<string name="subtitle_format">Format Takarir (Subtitle)</string>
<string name="hide_thumbnails">Sembunyikan Keluku (Thumbnail)</string>
<string name="clear_saved">Bersihkan yang Disimpan</string>
<string name="saved">Disimpan</string>

View file

@ -238,8 +238,7 @@
<string name="quick_download_summary">事前にデータを取得しません</string>
<string name="remove_audio">音声を削除</string>
<string name="download_already_exists">ダウンロードに既に存在します</string>
<string name="pause">一時停止
\n</string>
<string name="pause">一時停止</string>
<string name="resume">再開</string>
<string name="keep_cache">キャッシュを保持</string>
<string name="keep_cache_summary">ダウンロードの完了後に一時ファイルを削除しないようにします</string>

View file

@ -241,8 +241,7 @@
<string name="system">시스템</string>
<string name="red">빨간색</string>
<string name="pure_theme_summary">퓨어 블랙 / 화이트 테마</string>
<string name="pause">일시정지
\n</string>
<string name="pause">일시정지</string>
<string name="material_you">머터리얼</string>
<string name="green">녹색</string>
<string name="blue">파란색</string>

View file

@ -228,8 +228,7 @@
<string name="preferred_format_id">Vēlamā Formāta ID</string>
<string name="download_now">Lejupielādēt Tagad</string>
<string name="download_already_exists">Lejupielāde jau pastāv</string>
<string name="pause">Pauze
\n</string>
<string name="pause">Pauze</string>
<string name="updating_download_data">Notiek Lejupielādes Vienuma Datu Atjaunināšana</string>
<string name="keep_cache">Paturēt Kešatmiņu</string>
<string name="keep_cache_summary">Neizdzēst pagaidu failus pēc lejupielādes pabeigšanas</string>

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pause">Stans
\n</string>
<string name="pause">Stans</string>
<string name="keep_cache">Behald mellomgøymet</string>
<string name="keep_cache_summary">Ikkje slett mellombelse filer etter at hentinga er ferdig</string>
<string name="network_error">Nettverksfeil</string>

View file

@ -204,8 +204,7 @@
<string name="download_now">Pobierz teraz</string>
<string name="download_already_exists">Pobieranie już istnieje</string>
<string name="preferred_locale">Preferowane ustawienia regionalne</string>
<string name="pause">Pauza
\n</string>
<string name="pause">Pauza</string>
<string name="updating_download_data">Aktualizowanie danych elementu do pobrania</string>
<string name="keep_cache_summary">Nie usuwaj plików tymczasowych po zakończeniu pobierania</string>
<string name="restore">Przywróć</string>

View file

@ -215,8 +215,7 @@
<string name="download_already_exists">O download já existe</string>
<string name="keep_cache">manter cache</string>
<string name="updating_download_data">Atualizando os dados do item de download</string>
<string name="pause">Pausar
\n</string>
<string name="pause">Pausar</string>
<string name="preferred_locale">Local preferido</string>
<string name="resume">Continuar</string>
<string name="network_error">Erro de rede</string>
@ -278,8 +277,7 @@
<string name="sponsorblock">Bloqueio do Patrocinador</string>
<string name="force_keyframes">Forçar quadros-chave nos cortes</string>
<string name="force_keyframes_summary">Processo mais lento, mas cortes mais precisos</string>
<string name="subtitle_format">Formato da legenda
\n</string>
<string name="subtitle_format">Formato da legenda</string>
<string name="hide_thumbnails">Ocultar miniaturas</string>
<string name="hide_terminal">Ocultar Terminal do menu de compartilhamento</string>
<string name="saved">Salvou</string>

View file

@ -168,8 +168,7 @@
<string name="download_now">Descarregar agora</string>
<string name="download_already_exists">Descarga já existente</string>
<string name="preferred_locale">Idioma preferido</string>
<string name="pause">Pausa
\n</string>
<string name="pause">Pausa</string>
<string name="resume">Retomar</string>
<string name="keep_cache">Manter em cache</string>
<string name="backup">Backup</string>

View file

@ -239,8 +239,7 @@
<string name="format_filtering_hint">Чтобы использовать эту опцию, все элементы должны быть одного типа</string>
<string name="restore_complete">Восстановление приложения завершено. Были восстановлены следующие объекты:</string>
<string name="download_already_exists">Загрузка уже существует</string>
<string name="pause">Пауза
\n</string>
<string name="pause">Пауза</string>
<string name="preferred_locale">Предпочтительный регион</string>
<string name="video_recommendations">Видеорекомендации</string>
<string name="couldnt_parse_file">Не удалось разобрать файл</string>
@ -281,8 +280,7 @@
<string name="use_extra_commands_summary">Добавьте свои дополнительные команды перед загрузкой аудио/видео</string>
<string name="use_extra_commands">Дополнительные команды</string>
<string name="current">Текущая команда</string>
<string name="subtitle_format">Формат субтитров
\n</string>
<string name="subtitle_format">Формат субтитров</string>
<string name="hide_thumbnails">Скрыть миниатюры</string>
<string name="hide_terminal">Скрыть терминал из меню «Поделиться»</string>
<string name="saved">Сохранено</string>

View file

@ -241,8 +241,7 @@
<string name="keep_cache">Mbaj Memoriken Kashe</string>
<string name="keep_cache_summary">Mos fshi skedarët e përkohshëm pasi shkarkimi ka mbaruar</string>
<string name="restore">Rikthe</string>
<string name="pause">Pauzë
\n</string>
<string name="pause">Pauzë</string>
<string name="updating_download_data">Duke Përditësuar Informacionet e Objektit</string>
<string name="backup">Ruaj</string>
<string name="select_backup_categories">Zgjidh kategoritë për ruajtje</string>
@ -281,7 +280,7 @@
<string name="force_keyframes_summary">Proçes më i ngadaltë, por prerje më të sakta</string>
<string name="subtitle_format">Formati i Titrave
\n</string>
<string name="hide_terminal">Fshihe Terminalin nga fleta e shpërndarjes</string>
<string name="show_terminal">Fshihe Terminalin nga fleta e shpërndarjes</string>
<string name="saved">Të ruajtura</string>
<string name="clear_saved">Pastro të ruajturat</string>
<string name="save_for_later">Ruaj Shkarkimin për më vonë\?</string>

View file

@ -240,8 +240,7 @@
<string name="download_now">Şimdi İndir</string>
<string name="remove_audio">Sesi Kaldır</string>
<string name="preferred_locale">Tercih Edilen Konum</string>
<string name="pause">Duraklat
\n</string>
<string name="pause">Duraklat</string>
<string name="resume">Devam et</string>
<string name="updating_download_data">İndirilen Öğe Verilerini Güncelleme</string>
<string name="keep_cache">Önbelleği Sakla</string>

View file

@ -236,8 +236,7 @@
<string name="download_now">Завантажити зараз</string>
<string name="download_already_exists">Завантаження вже існує</string>
<string name="preferred_locale">Бажане розташування</string>
<string name="pause">Пауза
\n</string>
<string name="pause">Пауза</string>
<string name="resume">Продовжити</string>
<string name="updating_download_data">Оновлення даних завантажуваного елемента</string>
<string name="keep_cache">Зберегти кеш</string>

View file

@ -122,8 +122,7 @@
<string name="download_now">立即下載</string>
<string name="download_already_exists">已存在下載</string>
<string name="preferred_locale">首選地區設定</string>
<string name="pause">暫停
\n</string>
<string name="pause">暫停</string>
<string name="resume">繼續</string>
<string name="updating_download_data">正在更新下載項目資料</string>
<string name="keep_cache">保留快取</string>

View file

@ -5,7 +5,7 @@
<color name="grey">#323232</color>
<color name="light_grey">#A6A6A6</color>
<color name="icon_bg">#FFFFFF</color>
<color name="icon_fg">#d43c3c</color>
<color name="icon_fg">#db2e2e</color>
<color name="blue_md_theme_light_primary">#0058CB</color>
@ -320,9 +320,9 @@
<color name="orange_md_theme_dark_errorContainer">#93000A</color>
<color name="orange_md_theme_dark_onError">#690005</color>
<color name="orange_md_theme_dark_onErrorContainer">#FFDAD6</color>
<color name="orange_md_theme_dark_background">#1F1B16</color>
<color name="orange_md_theme_dark_background">#1b1b1b</color>
<color name="orange_md_theme_dark_onBackground">#EBE1D9</color>
<color name="orange_md_theme_dark_surface">#1F1B16</color>
<color name="orange_md_theme_dark_surface">#1b1b1b</color>
<color name="orange_md_theme_dark_onSurface">#EBE1D9</color>
<color name="orange_md_theme_dark_surfaceVariant">#504539</color>
<color name="orange_md_theme_dark_onSurfaceVariant">#D4C4B5</color>

View file

@ -244,7 +244,7 @@
<string name="download_now">Download Now</string>
<string name="download_already_exists">Download already exists</string>
<string name="preferred_locale">Preferred Locale</string>
<string name="pause">Pause\n</string>
<string name="pause">Pause</string>
<string name="resume">Resume</string>
<string name="updating_download_data">Updating Download Item Data</string>
<string name="keep_cache">Keep Cache</string>
@ -290,7 +290,7 @@
<string name="current">Current Command</string>
<string name="subtitle_format">Subtitle Format\n</string>
<string name="hide_thumbnails">Hide Thumbnails</string>
<string name="hide_terminal">Hide Terminal from share menu</string>
<string name="show_terminal">Show Terminal in share menu</string>
<string name="saved">Saved</string>
<string name="clear_saved">Clear Saved</string>
<string name="save_for_later">Save Download for Later?</string>

View file

@ -44,7 +44,6 @@
app:title="@string/socks5_proxy" />
<ListPreference
android:dependency="download_card"
android:defaultValue="video"
android:entries="@array/download_types"
android:entryValues="@array/download_types_values"

View file

@ -112,9 +112,9 @@
<SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch"
app:defaultValue="true"
app:defaultValue="false"
android:icon="@drawable/baseline_share_24"
android:key="hide_terminal"
app:title="@string/hide_terminal" />
android:key="show_terminal"
app:title="@string/show_terminal" />
</PreferenceCategory>
</PreferenceScreen>