CHANGELOG

made from and to textboxes wider in the cut video screen
added search engines in the search view
removed filename template override if the user leaves it empty. Now it follows the yt-dlp default
fixed app crashing when its tablet
added drag handle to scrolling content
added pause functionality
made video player faster to load for youtube videos
This commit is contained in:
deniscerri 2023-07-22 20:11:41 +02:00
parent 0cacff6e9f
commit 6dc25bab9a
No known key found for this signature in database
GPG key ID: 95C43D517D830350
67 changed files with 883 additions and 435 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">Sicherung</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,10 +277,9 @@
<string name="sponsorblock">Bloqueio de Patrocinadores</string>
<string name="force_keyframes">Forçar quadros-chave nos cortes</string>
<string name="force_keyframes_summary">Processo 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="show_terminal">Ocultar Terminal do menu de compartilhamento</string>
<string name="saved">Salvo</string>
<string name="clear_saved">Limpar Salvo</string>
<string name="save_for_later">Salvar download para mais tarde\?</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>
@ -284,7 +283,7 @@
<string name="subtitle_format">Формат субтиров
\n</string>
<string name="hide_thumbnails">Скрыть миниатюры видео</string>
<string name="hide_terminal">Скрыть терминал из меню \"Поделиться\"</string>
<string name="show_terminal">Скрыть терминал из меню \"Поделиться\"</string>
<string name="saved">Сохраненные</string>
<string name="clear_saved">Очистить сохраненные</string>
<string name="save_for_later">Сохранить загрузку для возврата к ней позднее\?</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>
@ -282,7 +281,7 @@
<string name="current">Поточна команда</string>
<string name="use_extra_commands_summary">Додайте свої додаткові команди перед завантаженням аудіо/відео</string>
<string name="hide_thumbnails">Приховати мініатюри відео</string>
<string name="hide_terminal">Приховати термінал з меню \"Поділитися\"</string>
<string name="show_terminal">Приховати термінал з меню \"Поділитися\"</string>
<string name="saved">Збережені</string>
<string name="clear_saved">Очистити збережені</string>
<string name="subtitle_format">Формат субтитрів

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>