other fixes
This commit is contained in:
parent
8e63219d73
commit
fdb143310e
30 changed files with 549 additions and 522 deletions
|
|
@ -6,6 +6,7 @@ import android.widget.Toast
|
|||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.deniscerri.ytdl.util.NotificationUtil
|
||||
import com.deniscerri.ytdl.util.ThemeUtil
|
||||
import com.yausername.aria2c.Aria2c
|
||||
import com.yausername.ffmpeg.FFmpeg
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
|
|
@ -44,6 +45,7 @@ class App : Application() {
|
|||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
ThemeUtil.init(this)
|
||||
}
|
||||
@Throws(YoutubeDLException::class)
|
||||
private fun initLibraries() {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import java.util.regex.Pattern
|
||||
|
||||
class ResultRepository(private val resultDao: ResultDao, private val context: Context) {
|
||||
private val tag: String = "ResultRepository"
|
||||
val YTDLNIS_SEARCH = "YTDLNIS_SEARCH"
|
||||
val allResults : Flow<List<ResultItem>> = resultDao.getResults()
|
||||
var itemCount = MutableStateFlow(-1)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.deniscerri.ytdl.database.viewmodel
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.Configuration
|
||||
|
|
@ -9,12 +8,9 @@ import android.os.Handler
|
|||
import android.os.Looper
|
||||
import android.os.Parcelable
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.PagingData
|
||||
|
|
@ -39,27 +35,24 @@ import com.deniscerri.ytdl.database.models.VideoPreferences
|
|||
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||
import com.deniscerri.ytdl.database.repository.HistoryRepository
|
||||
import com.deniscerri.ytdl.database.repository.ResultRepository
|
||||
import com.deniscerri.ytdl.ui.downloadcard.FormatTuple
|
||||
import com.deniscerri.ytdl.ui.downloadcard.MultipleItemFormatTuple
|
||||
import com.deniscerri.ytdl.util.Extensions.toListString
|
||||
import com.deniscerri.ytdl.util.FileUtil
|
||||
import com.deniscerri.ytdl.util.FormatSorter
|
||||
import com.deniscerri.ytdl.util.InfoUtil
|
||||
import com.deniscerri.ytdl.work.AlarmScheduler
|
||||
import com.deniscerri.ytdl.work.UpdatePlaylistFormatsWorker
|
||||
import com.deniscerri.ytdl.work.UpdateMultipleDownloadsFormatsWorker
|
||||
import com.google.gson.Gson
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import okhttp3.internal.format
|
||||
import java.io.File
|
||||
import java.util.Locale
|
||||
|
||||
|
|
@ -539,18 +532,17 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
}
|
||||
|
||||
|
||||
fun turnDownloadItemsToProcessingDownloads(itemIDs: List<Long>) = viewModelScope.launch(Dispatchers.IO){
|
||||
fun turnDownloadItemsToProcessingDownloads(itemIDs: List<Long>, deleteExisting : Boolean = false) = viewModelScope.launch(Dispatchers.IO){
|
||||
val job = viewModelScope.launch(Dispatchers.IO) {
|
||||
repository.deleteProcessing()
|
||||
processingItems.emit(true)
|
||||
try {
|
||||
itemIDs.forEachIndexed { idx, it ->
|
||||
itemIDs.forEach {
|
||||
val item = repository.getItemByID(it)
|
||||
if (processingItemsJob?.isCancelled == true) throw CancellationException()
|
||||
|
||||
item.id = 0
|
||||
if (!deleteExisting) item.id = 0
|
||||
item.status = DownloadRepository.Status.Processing.toString()
|
||||
repository.insert(item)
|
||||
repository.update(item)
|
||||
}
|
||||
processingItems.emit(false)
|
||||
} catch (e: Exception) {
|
||||
|
|
@ -1059,7 +1051,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
dao.updateProcessingtoSavedStatus()
|
||||
|
||||
val id = System.currentTimeMillis().toInt()
|
||||
val workRequest = OneTimeWorkRequestBuilder<UpdatePlaylistFormatsWorker>()
|
||||
val workRequest = OneTimeWorkRequestBuilder<UpdateMultipleDownloadsFormatsWorker>()
|
||||
.setInputData(
|
||||
Data.Builder()
|
||||
.putLongArray("ids", ids.toLongArray())
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
|||
arguments?.remove("showDownloadsWithUpdatedFormats")
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val ids = arguments?.getLongArray("downloadIds") ?: return@launch
|
||||
downloadViewModel.turnDownloadItemsToProcessingDownloads(ids.toList())
|
||||
downloadViewModel.turnDownloadItemsToProcessingDownloads(ids.toList(), deleteExisting = true)
|
||||
withContext(Dispatchers.Main){
|
||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,7 +202,8 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
|
|||
companion object {
|
||||
private val DIFF_CALLBACK: DiffUtil.ItemCallback<ResultItem> = object : DiffUtil.ItemCallback<ResultItem>() {
|
||||
override fun areItemsTheSame(oldItem: ResultItem, newItem: ResultItem): Boolean {
|
||||
return oldItem.id === newItem.id
|
||||
val ranged = arrayListOf(oldItem.id, newItem.id)
|
||||
return ranged[0] == ranged[1]
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: ResultItem, newItem: ResultItem): Boolean {
|
||||
|
|
|
|||
|
|
@ -108,8 +108,11 @@ class ConfigureDownloadBottomSheetDialog(private val currentDownloadItem: Downlo
|
|||
fragmentManager,
|
||||
lifecycle,
|
||||
null,
|
||||
currentDownloadItem
|
||||
currentDownloadItem,
|
||||
isIncognito = incognito
|
||||
)
|
||||
|
||||
|
||||
viewPager2.adapter = fragmentAdapter
|
||||
viewPager2.isSaveFromParentEnabled = false
|
||||
|
||||
|
|
@ -182,7 +185,7 @@ class ConfigureDownloadBottomSheetDialog(private val currentDownloadItem: Downlo
|
|||
putString("last_used_download_type",
|
||||
listOf(Type.audio, Type.video, Type.command)[position].toString())
|
||||
}
|
||||
updateWhenSwitching()
|
||||
fragmentAdapter.updateWhenSwitching(viewPager2.currentItem)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -216,6 +219,7 @@ class ConfigureDownloadBottomSheetDialog(private val currentDownloadItem: Downlo
|
|||
}
|
||||
|
||||
incognito = !incognito
|
||||
fragmentAdapter.isIncognito = incognito
|
||||
val onOff = if (incognito) getString(R.string.ok) else getString(R.string.disabled)
|
||||
Toast.makeText(requireContext(), "${getString(R.string.incognito)}: $onOff", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
|
@ -224,54 +228,7 @@ class ConfigureDownloadBottomSheetDialog(private val currentDownloadItem: Downlo
|
|||
}
|
||||
|
||||
private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem{
|
||||
val item = when(selectedTabPosition){
|
||||
0 -> {
|
||||
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
f.downloadItem.apply { id = currentDownloadItem.id }
|
||||
}
|
||||
1 -> {
|
||||
val f = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f.downloadItem.apply { id = currentDownloadItem.id }
|
||||
}
|
||||
else -> {
|
||||
val f = fragmentManager?.findFragmentByTag("f2") as DownloadCommandFragment
|
||||
f.downloadItem.apply { id = currentDownloadItem.id }
|
||||
}
|
||||
}
|
||||
item.incognito = incognito
|
||||
return item
|
||||
}
|
||||
|
||||
|
||||
private fun updateWhenSwitching(){
|
||||
val prevDownloadItem = getDownloadItem(
|
||||
if (viewPager2.currentItem == 1) 0 else 1
|
||||
)
|
||||
fragmentAdapter.setTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
|
||||
when(viewPager2.currentItem){
|
||||
0 -> {
|
||||
kotlin.runCatching {
|
||||
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
f.updateSelectedAudioFormat(getDownloadItem(1).videoPreferences.audioFormatIDs.first())
|
||||
}
|
||||
}
|
||||
1 -> {
|
||||
kotlin.runCatching {
|
||||
val f = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
f.updateSelectedAudioFormat(getDownloadItem(0).format)
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
kotlin.runCatching {
|
||||
val f = fragmentManager?.findFragmentByTag("f2") as DownloadCommandFragment
|
||||
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
return fragmentAdapter.getDownloadItem(selectedTabPosition)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -180,38 +180,32 @@ class CutVideoBottomSheetDialog(private val _item: DownloadItem? = null, private
|
|||
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
val data : MutableList<String?> = withContext(Dispatchers.IO){
|
||||
val data = withContext(Dispatchers.IO) {
|
||||
if (urls.isNullOrEmpty()) {
|
||||
infoUtil.getStreamingUrlAndChapters(item.url)
|
||||
}else {
|
||||
urls.split("\n").toMutableList()
|
||||
}else{
|
||||
Pair(urls.split("\n"), chapters)
|
||||
}
|
||||
}
|
||||
|
||||
if (data.isEmpty()) throw Exception("No Data found!")
|
||||
if (data.first.isEmpty()) throw Exception("No Data found!")
|
||||
|
||||
if (chapters!!.isEmpty() && urls!!.isBlank()){
|
||||
try{
|
||||
val listType: Type = object : TypeToken<List<ChapterItem>>() {}.type
|
||||
chapters = Gson().fromJson(data.first().toString(), listType)
|
||||
data.removeFirst()
|
||||
}catch (ignored: Exception) {
|
||||
data.removeFirst()
|
||||
}
|
||||
chapters = data.second
|
||||
}
|
||||
|
||||
if (data.isEmpty()) throw Exception("No Streaming URL found!")
|
||||
if (data.size == 2){
|
||||
val urls = data.first
|
||||
if (urls.size == 2){
|
||||
val audioSource : MediaSource =
|
||||
DefaultMediaSourceFactory(requireContext())
|
||||
.createMediaSource(fromUri(Uri.parse(data[0])))
|
||||
.createMediaSource(fromUri(Uri.parse(urls[0])))
|
||||
val videoSource: MediaSource =
|
||||
DefaultMediaSourceFactory(requireContext())
|
||||
.createMediaSource(fromUri(Uri.parse(data[1])))
|
||||
.createMediaSource(fromUri(Uri.parse(urls[1])))
|
||||
player.setMediaSource(MergingMediaSource(videoSource, audioSource))
|
||||
}else{
|
||||
player.addMediaItem(fromUri(Uri.parse(data[0])))
|
||||
player.addMediaItem(fromUri(Uri.parse(urls[0])))
|
||||
}
|
||||
player.addMediaItem(fromUri(Uri.parse(data[0])))
|
||||
|
||||
progress.visibility = View.GONE
|
||||
populateSuggestedChapters()
|
||||
|
|
@ -221,6 +215,7 @@ class CutVideoBottomSheetDialog(private val _item: DownloadItem? = null, private
|
|||
}catch (e: Exception){
|
||||
progress.visibility = View.GONE
|
||||
frame.visibility = View.GONE
|
||||
videoView.visibility = View.GONE
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -239,10 +239,6 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
|
|||
this.formats.removeAll(formats.toSet())
|
||||
this.formats.addAll(allFormats.filter { !genericAudioFormats.contains(it) })
|
||||
resultViewModel.update(this)
|
||||
kotlin.runCatching {
|
||||
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f1.updateUI(this)
|
||||
}
|
||||
}
|
||||
|
||||
currentDownloadItem?.apply {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import android.annotation.SuppressLint
|
|||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
|
|
@ -18,9 +17,7 @@ import android.widget.Button
|
|||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
|
|
@ -190,18 +187,6 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.alpha = 0.3f
|
||||
}
|
||||
|
||||
//check if the item is coming from a text file
|
||||
val isCommandOnly = (type == Type.command && !Patterns.WEB_URL.matcher(result.url).matches())
|
||||
if (isCommandOnly){
|
||||
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(0)?.isClickable = false
|
||||
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(0)?.alpha = 0.3f
|
||||
|
||||
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.isClickable = false
|
||||
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.alpha = 0.3f
|
||||
|
||||
(updateItem.parent as LinearLayout).visibility = View.GONE
|
||||
}
|
||||
|
||||
//remove outdated player url of 1hr so it can refetch it in the cut player
|
||||
if (result.creationTime > System.currentTimeMillis() - 3600000) result.urls = ""
|
||||
|
||||
|
|
@ -210,7 +195,8 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
fragmentManager,
|
||||
lifecycle,
|
||||
result,
|
||||
currentDownloadItem
|
||||
currentDownloadItem,
|
||||
isIncognito = incognito
|
||||
)
|
||||
|
||||
viewPager2.adapter = fragmentAdapter
|
||||
|
|
@ -239,6 +225,18 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
}, 200)
|
||||
}
|
||||
}
|
||||
|
||||
//check if the item is coming from a text file
|
||||
val isCommandOnly = (type == Type.command && !Patterns.WEB_URL.matcher(result.url).matches())
|
||||
if (isCommandOnly){
|
||||
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(0)?.isClickable = false
|
||||
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(0)?.alpha = 0.3f
|
||||
|
||||
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.isClickable = false
|
||||
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.alpha = 0.3f
|
||||
|
||||
(updateItem.parent as LinearLayout).visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
sharedPreferences.edit(commit = true) {
|
||||
|
|
@ -294,7 +292,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
putString("last_used_download_type",
|
||||
listOf(Type.audio, Type.video, Type.command)[position].toString())
|
||||
}
|
||||
updateWhenSwitching()
|
||||
fragmentAdapter.updateWhenSwitching(viewPager2.currentItem)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -441,6 +439,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
}
|
||||
|
||||
incognito = !incognito
|
||||
fragmentAdapter.isIncognito = incognito
|
||||
val onOff = if (incognito) getString(R.string.ok) else getString(R.string.disabled)
|
||||
Toast.makeText(requireContext(), "${getString(R.string.incognito)}: $onOff", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
|
@ -498,42 +497,46 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
if (it){
|
||||
delay(500)
|
||||
runCatching {
|
||||
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||
isVisible = true
|
||||
isClickable = true
|
||||
setOnClickListener {
|
||||
lifecycleScope.launch {
|
||||
resultViewModel.cancelUpdateFormatsItemData()
|
||||
(fragmentAdapter.fragments[0] as DownloadAudioFragment).apply {
|
||||
view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||
isVisible = true
|
||||
isClickable = true
|
||||
setOnClickListener {
|
||||
lifecycleScope.launch {
|
||||
resultViewModel.cancelUpdateFormatsItemData()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
runCatching {
|
||||
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||
isVisible = true
|
||||
isClickable = true
|
||||
setOnClickListener {
|
||||
lifecycleScope.launch {
|
||||
resultViewModel.cancelUpdateFormatsItemData()
|
||||
(fragmentAdapter.fragments[1] as DownloadVideoFragment).apply {
|
||||
view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||
isVisible = true
|
||||
isClickable = true
|
||||
setOnClickListener {
|
||||
lifecycleScope.launch {
|
||||
resultViewModel.cancelUpdateFormatsItemData()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
runCatching {
|
||||
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||
isVisible = false
|
||||
isClickable = false
|
||||
(fragmentAdapter.fragments[0] as DownloadAudioFragment).apply {
|
||||
view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||
isVisible = false
|
||||
isClickable = false
|
||||
}
|
||||
}
|
||||
}
|
||||
runCatching {
|
||||
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||
isVisible = false
|
||||
isClickable = false
|
||||
(fragmentAdapter.fragments[1] as DownloadVideoFragment).apply {
|
||||
view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||
isVisible = false
|
||||
isClickable = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -546,43 +549,34 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
if (result == null) return@collectLatest
|
||||
kotlin.runCatching {
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
if (result.isNotEmpty()){
|
||||
if (result.size == 1 && result[0] != null){
|
||||
fragmentAdapter.setResultItem(result[0]!!)
|
||||
runCatching {
|
||||
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
f.updateUI(result[0])
|
||||
}
|
||||
if (result.size == 1 && result[0] != null) {
|
||||
val res = result[0]!!
|
||||
fragmentAdapter.setResultItem(res)
|
||||
|
||||
runCatching {
|
||||
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f1.updateUI(result[0])
|
||||
}
|
||||
title.visibility = View.VISIBLE
|
||||
subtitle.visibility = View.VISIBLE
|
||||
shimmerLoading.visibility = View.GONE
|
||||
shimmerLoadingSubtitle.visibility = View.GONE
|
||||
shimmerLoading.stopShimmer()
|
||||
shimmerLoadingSubtitle.stopShimmer()
|
||||
|
||||
title.visibility = View.VISIBLE
|
||||
subtitle.visibility = View.VISIBLE
|
||||
shimmerLoading.visibility = View.GONE
|
||||
shimmerLoadingSubtitle.visibility = View.GONE
|
||||
shimmerLoading.stopShimmer()
|
||||
shimmerLoadingSubtitle.stopShimmer()
|
||||
val usingGenericFormatsOrEmpty = res.formats.isEmpty() || res.formats.any { it.format_note.contains("ytdlnisgeneric") }
|
||||
arguments?.putParcelable("result", res)
|
||||
if (usingGenericFormatsOrEmpty && sharedPreferences.getBoolean("update_formats", false)){
|
||||
initUpdateFormats(res)
|
||||
}
|
||||
|
||||
val usingGenericFormatsOrEmpty = result[0]!!.formats.isEmpty() || result[0]!!.formats.any { it.format_note.contains("ytdlnisgeneric") }
|
||||
fragmentAdapter.setResultItem(result[0]!!)
|
||||
arguments?.putParcelable("result", result[0]!!)
|
||||
if (usingGenericFormatsOrEmpty && sharedPreferences.getBoolean("update_formats", false)){
|
||||
initUpdateFormats(result[0]!!)
|
||||
}
|
||||
}else if (result.size > 1) {
|
||||
//open multi download card instead
|
||||
if (activity is ShareActivity){
|
||||
findNavController().navigate(R.id.action_downloadBottomSheetDialog_to_selectPlaylistItemsDialog, bundleOf(
|
||||
Pair("resultIDs", result.map { it!!.id }.toLongArray()),
|
||||
))
|
||||
}else{
|
||||
//open multi download card instead
|
||||
if (activity is ShareActivity){
|
||||
findNavController().navigate(R.id.action_downloadBottomSheetDialog_to_selectPlaylistItemsDialog, bundleOf(
|
||||
Pair("resultIDs", result.map { it!!.id }.toLongArray()),
|
||||
))
|
||||
}else{
|
||||
dismiss()
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
resultViewModel.updateResultData.emit(null)
|
||||
}
|
||||
|
||||
|
|
@ -606,19 +600,17 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.Main){
|
||||
runCatching {
|
||||
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
val f1 = fragmentAdapter.fragments[0] as DownloadAudioFragment
|
||||
val resultItem = downloadViewModel.createResultItemFromDownload(f1.downloadItem)
|
||||
resultItem.formats = formats
|
||||
fragmentAdapter.setResultItem(resultItem)
|
||||
f1.updateUI(resultItem)
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
|
||||
}
|
||||
runCatching {
|
||||
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
val f1 = fragmentAdapter.fragments[1] as DownloadVideoFragment
|
||||
val resultItem = downloadViewModel.createResultItemFromDownload(f1.downloadItem)
|
||||
resultItem.formats = formats
|
||||
fragmentAdapter.setResultItem(resultItem)
|
||||
f1.updateUI(resultItem)
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
|
@ -634,28 +626,12 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
}
|
||||
|
||||
private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem {
|
||||
val item = when(selectedTabPosition){
|
||||
0 -> {
|
||||
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
f.downloadItem
|
||||
}
|
||||
1 -> {
|
||||
val f = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f.downloadItem
|
||||
}
|
||||
else -> {
|
||||
val f = fragmentManager?.findFragmentByTag("f2") as DownloadCommandFragment
|
||||
f.downloadItem
|
||||
}
|
||||
}
|
||||
|
||||
item.incognito = incognito
|
||||
return item
|
||||
return fragmentAdapter.getDownloadItem(selectedTabPosition)
|
||||
}
|
||||
|
||||
private fun getAlsoAudioDownloadItem(finished: (it: DownloadItem) -> Unit) {
|
||||
try {
|
||||
val ff = (fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment)
|
||||
val ff = fragmentAdapter.fragments[0] as DownloadAudioFragment
|
||||
ff.updateSelectedAudioFormat(getDownloadItem(1).videoPreferences.audioFormatIDs.first())
|
||||
finished(ff.downloadItem)
|
||||
}catch (e: Exception){
|
||||
|
|
@ -680,37 +656,6 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateWhenSwitching(){
|
||||
val prevDownloadItem = getDownloadItem(
|
||||
if (viewPager2.currentItem == 1) 0 else 1
|
||||
)
|
||||
fragmentAdapter.setTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
|
||||
when(viewPager2.currentItem){
|
||||
0 -> {
|
||||
kotlin.runCatching {
|
||||
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
f.updateSelectedAudioFormat(getDownloadItem(1).videoPreferences.audioFormatIDs.first())
|
||||
}
|
||||
}
|
||||
1 -> {
|
||||
kotlin.runCatching {
|
||||
val f = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
f.updateSelectedAudioFormat(getDownloadItem(0).format)
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
kotlin.runCatching {
|
||||
val f = fragmentManager?.findFragmentByTag("f2") as DownloadCommandFragment
|
||||
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initUpdateData() {
|
||||
kotlin.runCatching {
|
||||
if (result.url.isBlank()) {
|
||||
|
|
|
|||
|
|
@ -12,33 +12,86 @@ class DownloadFragmentAdapter (
|
|||
lifecycle : Lifecycle,
|
||||
private var result: ResultItem?,
|
||||
private var downloadItem: DownloadItem?,
|
||||
private var nonSpecific: Boolean = false
|
||||
nonSpecific: Boolean = false,
|
||||
var isIncognito: Boolean = false
|
||||
) : FragmentStateAdapter(fragmentManager, lifecycle) {
|
||||
|
||||
fun setResultItem(res: ResultItem){
|
||||
result = res
|
||||
|
||||
fragments.forEachIndexed { idx, it ->
|
||||
when(idx) {
|
||||
0 -> kotlin.runCatching { (it as DownloadAudioFragment).updateUI(res) }
|
||||
1 -> kotlin.runCatching { (it as DownloadVideoFragment).updateUI(res) }
|
||||
2 -> kotlin.runCatching { (it as DownloadCommandFragment).updateUI(res) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setDownloadItem(dd: DownloadItem){
|
||||
downloadItem = dd
|
||||
}
|
||||
|
||||
fun setTitleAuthor(t: String, a: String){
|
||||
private fun setTitleAuthor(t: String, a: String){
|
||||
result?.title = t
|
||||
result?.author = a
|
||||
downloadItem?.title = t
|
||||
downloadItem?.author = a
|
||||
}
|
||||
|
||||
|
||||
val fragments = listOf<Fragment>(
|
||||
DownloadAudioFragment(result, downloadItem,"", nonSpecific),
|
||||
DownloadVideoFragment(result, downloadItem,"", nonSpecific),
|
||||
DownloadCommandFragment(result, downloadItem)
|
||||
)
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return 3
|
||||
return fragments.size
|
||||
}
|
||||
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return when(position){
|
||||
0 -> DownloadAudioFragment(result, downloadItem,"", nonSpecific)
|
||||
1 -> DownloadVideoFragment(result, downloadItem,"", nonSpecific)
|
||||
else -> DownloadCommandFragment(result, downloadItem)
|
||||
return fragments[position]
|
||||
}
|
||||
|
||||
fun updateWhenSwitching(currentViewPagerItem : Int) {
|
||||
val prevDownloadItem = getDownloadItem(
|
||||
if (currentViewPagerItem == 1) 0 else 1
|
||||
)
|
||||
setTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
|
||||
when(currentViewPagerItem){
|
||||
0 -> {
|
||||
kotlin.runCatching {
|
||||
(fragments[0] as DownloadAudioFragment).apply {
|
||||
updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
updateSelectedAudioFormat(getDownloadItem(1).videoPreferences.audioFormatIDs.first())
|
||||
}
|
||||
}
|
||||
}
|
||||
1 -> {
|
||||
kotlin.runCatching {
|
||||
(fragments[1] as DownloadVideoFragment).apply {
|
||||
updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
updateSelectedAudioFormat(getDownloadItem(0).format)
|
||||
}
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
kotlin.runCatching {
|
||||
(fragments[2] as DownloadCommandFragment).apply {
|
||||
updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
fun getDownloadItem(position: Int) : DownloadItem {
|
||||
return when(position) {
|
||||
0 -> (fragments[0] as DownloadAudioFragment).downloadItem
|
||||
1 -> (fragments[1] as DownloadVideoFragment).downloadItem
|
||||
else -> (fragments[2] as DownloadCommandFragment).downloadItem
|
||||
}.apply {
|
||||
incognito = isIncognito
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -257,10 +257,6 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
|
|||
this.formats.removeAll(formats)
|
||||
this.formats.addAll(allFormats.filter { !genericVideoFormats.contains(it) })
|
||||
resultViewModel.update(this)
|
||||
kotlin.runCatching {
|
||||
val f1 = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
f1.updateUI(this)
|
||||
}
|
||||
}
|
||||
|
||||
currentDownloadItem?.apply {
|
||||
|
|
|
|||
|
|
@ -220,6 +220,7 @@ class FormatSelectionBottomSheetDialog(
|
|||
if (items.size == 1) {
|
||||
kotlin.runCatching {
|
||||
val res = infoUtil.getFormats(items.first()!!.url, currentFormatSource)
|
||||
if (!isActive) return@launch
|
||||
res.filter { it.format_note != "storyboard" }
|
||||
chosenFormats = if (items.first()?.type == Type.audio) {
|
||||
res.filter { it.format_note.contains("audio", ignoreCase = true) }
|
||||
|
|
@ -230,6 +231,8 @@ class FormatSelectionBottomSheetDialog(
|
|||
|
||||
formats.clear()
|
||||
formats.addAll(res)
|
||||
|
||||
|
||||
withContext(Dispatchers.Main){
|
||||
listener.onFormatsUpdated(res)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -283,29 +283,28 @@ class ResultCardDetailsDialog : BottomSheetDialogFragment(), GenericDownloadAdap
|
|||
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
val data : MutableList<String?> = withContext(Dispatchers.IO){
|
||||
val data = withContext(Dispatchers.IO) {
|
||||
if (item.urls.isEmpty()) {
|
||||
infoUtil.getStreamingUrlAndChapters(item.url)
|
||||
}else {
|
||||
item.urls.split("\n").toMutableList()
|
||||
}else{
|
||||
Pair(item.urls.split("\n"), null)
|
||||
}
|
||||
}
|
||||
|
||||
if (data.size > 1) data.removeFirst()
|
||||
if (data.first.isEmpty()) throw Exception("No Data found!")
|
||||
|
||||
if (data.isEmpty()) throw Exception("No Streaming URL found!")
|
||||
if (data.size == 2){
|
||||
val urls = data.first
|
||||
if (urls.size == 2){
|
||||
val audioSource : MediaSource =
|
||||
DefaultMediaSourceFactory(requireContext())
|
||||
.createMediaSource(MediaItem.fromUri(Uri.parse(data[0])))
|
||||
.createMediaSource(MediaItem.fromUri(Uri.parse(urls[0])))
|
||||
val videoSource: MediaSource =
|
||||
DefaultMediaSourceFactory(requireContext())
|
||||
.createMediaSource(MediaItem.fromUri(Uri.parse(data[1])))
|
||||
.createMediaSource(MediaItem.fromUri(Uri.parse(urls[1])))
|
||||
player.setMediaSource(MergingMediaSource(videoSource, audioSource))
|
||||
}else{
|
||||
player.addMediaItem(MediaItem.fromUri(Uri.parse(data[0])))
|
||||
player.addMediaItem(MediaItem.fromUri(Uri.parse(urls[0])))
|
||||
}
|
||||
player.addMediaItem(MediaItem.fromUri(Uri.parse(data[0])))
|
||||
|
||||
player.prepare()
|
||||
player.play()
|
||||
|
|
|
|||
|
|
@ -126,6 +126,8 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
|||
|
||||
pause.setOnClickListener {
|
||||
lifecycleScope.launch {
|
||||
pause.isEnabled = false
|
||||
delay(1000)
|
||||
workManager.cancelAllWorkByTag("download")
|
||||
val activeDownloadsList = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getActiveDownloads()
|
||||
|
|
@ -140,16 +142,15 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
|||
resume.isEnabled = false
|
||||
resume.isVisible = true
|
||||
activeDownloads.notifyDataSetChanged()
|
||||
withContext(Dispatchers.Main){
|
||||
delay(1000)
|
||||
resume.isEnabled = true
|
||||
}
|
||||
resume.isEnabled = true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
resume.setOnClickListener {
|
||||
lifecycleScope.launch {
|
||||
resume.isEnabled = false
|
||||
delay(1000)
|
||||
preferences.edit().putBoolean("paused_downloads", false).apply()
|
||||
resume.isVisible = false
|
||||
pause.isEnabled = false
|
||||
|
|
@ -159,12 +160,9 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
|||
downloadViewModel.startDownloadWorker(listOf())
|
||||
withContext(Dispatchers.Main){
|
||||
activeDownloads.notifyDataSetChanged()
|
||||
pause.isEnabled = true
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main){
|
||||
delay(1000)
|
||||
pause.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,9 +202,6 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
|||
requireActivity().runOnUiThread {
|
||||
try {
|
||||
progressBar?.setProgressCompat(event.progress, true)
|
||||
if (event.progress > 95) {
|
||||
pause.isEnabled = false
|
||||
}
|
||||
outputText?.text = event.output
|
||||
}catch (ignored: Exception) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
|||
import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel
|
||||
import com.deniscerri.ytdl.ui.adapter.HistoryAdapter
|
||||
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
|
||||
import com.deniscerri.ytdl.util.FileUtil
|
||||
import com.deniscerri.ytdl.util.NavbarUtil
|
||||
import com.deniscerri.ytdl.util.UiUtil
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
|
|
@ -134,7 +135,6 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
|||
requireActivity()
|
||||
)
|
||||
recyclerView = view.findViewById(R.id.recyclerviewhistorys)
|
||||
recyclerView.adapter = historyAdapter
|
||||
recyclerView.enableFastScroll()
|
||||
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
|
|
@ -165,6 +165,10 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
|||
historyAdapter!!.submitList(it)
|
||||
historyList = it
|
||||
scrollToTop()
|
||||
|
||||
if (recyclerView.adapter == null){
|
||||
recyclerView.adapter = historyAdapter
|
||||
}
|
||||
}
|
||||
|
||||
historyViewModel.sortOrder.observe(viewLifecycleOwner){
|
||||
|
|
@ -552,7 +556,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
|||
true
|
||||
}
|
||||
R.id.share -> {
|
||||
UiUtil.shareFileIntent(requireContext(), selectedObjects.map { it.downloadPath }.flatten())
|
||||
FileUtil.shareFileIntent(requireContext(), selectedObjects.map { it.downloadPath }.flatten())
|
||||
clearCheckedItems()
|
||||
actionMode?.finish()
|
||||
true
|
||||
|
|
@ -677,7 +681,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
|||
|
||||
override fun onButtonClick(itemID: Long, isPresent: Boolean) {
|
||||
if (isPresent){
|
||||
UiUtil.shareFileIntent(requireContext(), historyList!!.first { it!!.id == itemID }!!.downloadPath)
|
||||
FileUtil.shareFileIntent(requireContext(), historyList!!.first { it!!.id == itemID }!!.downloadPath)
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ class CookiesFragment : Fragment(), CookieAdapter.OnItemClickListener {
|
|||
}else{
|
||||
val snack = Snackbar.make(recyclerView, getString(R.string.backup_created_successfully), Snackbar.LENGTH_LONG)
|
||||
snack.setAction(R.string.share) {
|
||||
UiUtil.shareFileIntent(requireContext(), listOf(f.absolutePath))
|
||||
FileUtil.shareFileIntent(requireContext(), listOf(f.absolutePath))
|
||||
}
|
||||
snack.show()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
|||
findPreference<Preference>("label_visibility")?.apply {
|
||||
isVisible = !resources.getBoolean(R.bool.uses_side_nav)
|
||||
setOnPreferenceChangeListener { preference, newValue ->
|
||||
restartApp()
|
||||
ThemeUtil.recreateMain()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
@ -162,7 +162,7 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
|||
.setPositiveButton(R.string.ok) { _, _ ->
|
||||
NavbarUtil.setNavBarItems(adapter.items, requireContext())
|
||||
NavbarUtil.setStartFragment(adapter.selectedHomeTabId)
|
||||
restartApp()
|
||||
ThemeUtil.recreateMain()
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
|
|
@ -185,34 +185,18 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
|||
theme!!.summary = getString(R.string.light)
|
||||
}
|
||||
}
|
||||
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
||||
val intent = Intent(requireContext(), MainActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
this.startActivity(intent)
|
||||
requireActivity().finishAffinity()
|
||||
ThemeUtil.updateThemes()
|
||||
true
|
||||
}
|
||||
accent!!.summary = accent!!.entry
|
||||
accent!!.onPreferenceChangeListener =
|
||||
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
|
||||
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
||||
val intent = Intent(requireContext(), MainActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
this.startActivity(intent)
|
||||
requireActivity().finishAffinity()
|
||||
ThemeUtil.updateThemes()
|
||||
true
|
||||
}
|
||||
highContrast!!.onPreferenceChangeListener =
|
||||
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
|
||||
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
||||
val intent = Intent(requireContext(), MainActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
this.startActivity(intent)
|
||||
requireActivity().finishAffinity()
|
||||
|
||||
ThemeUtil.updateThemes()
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
|||
import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel
|
||||
import com.deniscerri.ytdl.database.viewmodel.ObserveSourcesViewModel
|
||||
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
||||
import com.deniscerri.ytdl.util.FileUtil
|
||||
import com.deniscerri.ytdl.util.UiUtil
|
||||
import com.deniscerri.ytdl.util.UpdateUtil
|
||||
import com.google.android.material.chip.Chip
|
||||
|
|
@ -220,7 +221,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
|||
saveFile.writeText(GsonBuilder().setPrettyPrinting().create().toJson(json))
|
||||
val s = Snackbar.make(requireView(), getString(R.string.backup_created_successfully), Snackbar.LENGTH_LONG)
|
||||
s.setAction(R.string.Open_File){
|
||||
UiUtil.openFileIntent(requireActivity(), saveFile.absolutePath)
|
||||
FileUtil.openFileIntent(requireActivity(), saveFile.absolutePath)
|
||||
}
|
||||
s.show()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,50 @@
|
|||
package com.deniscerri.ytdl.util
|
||||
|
||||
import android.R.attr.mimeType
|
||||
import android.app.Activity
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.media.MediaScannerConnection
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.DocumentsContract
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Log
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.work.impl.utils.PreferenceUtils
|
||||
import com.anggrayudi.storage.callback.FileCallback
|
||||
import com.anggrayudi.storage.callback.FolderCallback
|
||||
import com.anggrayudi.storage.file.copyFolderTo
|
||||
import com.anggrayudi.storage.file.getAbsolutePath
|
||||
import com.anggrayudi.storage.file.moveFileTo
|
||||
import com.deniscerri.ytdl.App
|
||||
import com.deniscerri.ytdl.BuildConfig
|
||||
import com.deniscerri.ytdl.MainActivity
|
||||
import com.deniscerri.ytdl.R
|
||||
import com.deniscerri.ytdl.util.Extensions.getMediaDuration
|
||||
import com.deniscerri.ytdl.util.Extensions.toStringDuration
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.yausername.youtubedl_android.YoutubeDLRequest
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.internal.closeQuietly
|
||||
import okhttp3.internal.lowercase
|
||||
import java.io.File
|
||||
import java.net.URLDecoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.text.DecimalFormat
|
||||
import java.text.DecimalFormatSymbols
|
||||
|
|
@ -327,4 +346,58 @@ object FileUtil {
|
|||
return "${DecimalFormat("#,##0.#", symbols).format(s / 1024.0.pow(digitGroups.toDouble()))} ${units[digitGroups]}"
|
||||
}
|
||||
|
||||
|
||||
fun openFileIntent(context: Context, downloadPath: String) {
|
||||
val uri = FileProvider.getUriForFile(context, context.packageName + ".fileprovider", File(downloadPath))
|
||||
println(uri)
|
||||
|
||||
if (uri == null){
|
||||
Toast.makeText(context, "Error opening file!", Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
val ext = downloadPath.substring(downloadPath.lastIndexOf(".") + 1).lowercase()
|
||||
val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext)
|
||||
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
.setDataAndType(uri, mime)
|
||||
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun shareFileIntent(context: Context, paths: List<String>){
|
||||
val uris : ArrayList<Uri> = arrayListOf()
|
||||
paths.runCatching {
|
||||
this.forEach {path ->
|
||||
val uri = DocumentFile.fromSingleUri(context, Uri.parse(path)).run{
|
||||
if (this?.exists() == true){
|
||||
this.uri
|
||||
}else if (File(path).exists()){
|
||||
FileProvider.getUriForFile(context, context.packageName + ".fileprovider",
|
||||
File(path))
|
||||
}else null
|
||||
}
|
||||
if (uri != null) uris.add(uri)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (uris.isEmpty()){
|
||||
Toast.makeText(context, "Error sharing files!", Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
Intent().apply {
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
action = Intent.ACTION_SEND_MULTIPLE
|
||||
putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
|
||||
type = if (uris.size == 1) uris[0].let { context.contentResolver.getType(it) } ?: "media/*" else "*/*"
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
}.run{
|
||||
context.startActivity(Intent.createChooser(this, context.getString(R.string.share)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -929,13 +929,22 @@ class InfoUtil(private val context: Context) {
|
|||
|
||||
|
||||
|
||||
fun getStreamingUrlAndChapters(url: String) : MutableList<String?> {
|
||||
fun getStreamingUrlAndChapters(url: String) : Pair<List<String>, List<ChapterItem>?> {
|
||||
try {
|
||||
val p = Pattern.compile("(^(https?)://(www.)?(music.)?youtu(.be)?)|(^(https?)://(www.)?piped.video)")
|
||||
val m = p.matcher(url)
|
||||
if (url.isYoutubeURL()) {
|
||||
val id = getIDFromYoutubeURL(url)
|
||||
val res = genericRequest("$pipedURL/streams/$id")
|
||||
if (res.length() == 0) {
|
||||
throw Exception()
|
||||
}else{
|
||||
val item = createVideoFromPipedJSON(res, url)
|
||||
if (item!!.urls.isBlank()) throw Exception()
|
||||
|
||||
val urls = item.urls.split(",")
|
||||
val chapters = item.chapters
|
||||
return Pair(urls, chapters)
|
||||
}
|
||||
|
||||
if (m.find()){
|
||||
return getStreamingUrlAndChaptersFromPIPED(url)
|
||||
}else{
|
||||
throw Exception()
|
||||
}
|
||||
|
|
@ -943,7 +952,7 @@ class InfoUtil(private val context: Context) {
|
|||
try {
|
||||
val request = YoutubeDLRequest(url)
|
||||
request.addOption("--get-url")
|
||||
request.addOption("--print", "%(chapters)s")
|
||||
request.addOption("--print", "%(.{urls,chapters})s")
|
||||
request.addOption("--skip-download")
|
||||
request.addOption("-R", "1")
|
||||
request.addOption("--socket-timeout", "5")
|
||||
|
|
@ -964,8 +973,6 @@ class InfoUtil(private val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
val proxy = sharedPreferences.getString("proxy", "")
|
||||
if (proxy!!.isNotBlank()){
|
||||
request.addOption("--proxy", proxy)
|
||||
|
|
@ -975,36 +982,41 @@ class InfoUtil(private val context: Context) {
|
|||
|
||||
|
||||
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)
|
||||
val json = JSONObject(youtubeDLResponse.out)
|
||||
val urls = if (json.has("urls")) {
|
||||
json.getString("urls").split("\n")
|
||||
}else{
|
||||
listOf()
|
||||
}
|
||||
return results.filter { it!!.isNotEmpty() }.toMutableList()
|
||||
|
||||
val chapters = if (json.has("chapters")) {
|
||||
val arr = json.getJSONArray("chapters")
|
||||
val list = mutableListOf<ChapterItem>()
|
||||
for (i in 0 until arr.length()) {
|
||||
list.add(
|
||||
Gson().fromJson(arr.getJSONObject(i).toString(), ChapterItem::class.java)
|
||||
)
|
||||
}
|
||||
|
||||
list
|
||||
}else{
|
||||
listOf()
|
||||
}
|
||||
|
||||
return Pair(urls, chapters)
|
||||
|
||||
} catch (e: Exception) {
|
||||
return mutableListOf()
|
||||
return Pair(listOf(), listOf())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStreamingUrlAndChaptersFromPIPED(url: String) : MutableList<String?> {
|
||||
val id = getIDFromYoutubeURL(url)
|
||||
val res = genericRequest("$pipedURL/streams/$id")
|
||||
if (res.length() == 0) {
|
||||
throw Exception()
|
||||
}else{
|
||||
val item = createVideoFromPipedJSON(res, url)
|
||||
if (item!!.urls.isBlank()) throw Exception()
|
||||
val list = mutableListOf<String?>(Gson().toJson(item.chapters))
|
||||
list.addAll(item.urls.split(","))
|
||||
return list
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
fun buildYoutubeDLRequest(downloadItem: DownloadItem) : YoutubeDLRequest {
|
||||
val request = if (downloadItem.playlistURL.isNullOrBlank() || downloadItem.playlistTitle.isBlank()){
|
||||
val useItemURL = sharedPreferences.getBoolean("use_itemurl_instead_playlisturl", false)
|
||||
|
||||
val request = if (downloadItem.playlistURL.isNullOrBlank() || downloadItem.playlistTitle.isBlank() || useItemURL){
|
||||
YoutubeDLRequest(downloadItem.url)
|
||||
}else{
|
||||
YoutubeDLRequest(downloadItem.playlistURL!!).apply {
|
||||
|
|
@ -1017,6 +1029,10 @@ class InfoUtil(private val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
if (downloadItem.playlistIndex != null && useItemURL) {
|
||||
request.addOption("--parse-metadata", " ${downloadItem.playlistIndex}: %(playlist_index)s")
|
||||
}
|
||||
|
||||
val type = downloadItem.type
|
||||
|
||||
val downDir : File
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import androidx.core.view.forEach
|
|||
import androidx.core.view.get
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.deniscerri.ytdl.R
|
||||
import com.deniscerri.ytdl.util.NavbarUtil.applyNavBarStyle
|
||||
import com.google.android.material.navigation.NavigationBarView
|
||||
|
||||
|
||||
|
|
@ -95,7 +96,7 @@ object NavbarUtil {
|
|||
return navBarItems
|
||||
}
|
||||
|
||||
fun NavigationBarView.applyNavBarStyle(): Int {
|
||||
fun NavigationBarView.setLabelVisibility() {
|
||||
val labelVisibilityMode = when (
|
||||
settings.getString("label_visibility", "always")
|
||||
) {
|
||||
|
|
@ -105,6 +106,10 @@ object NavbarUtil {
|
|||
else -> NavigationBarView.LABEL_VISIBILITY_AUTO
|
||||
}
|
||||
this.labelVisibilityMode = labelVisibilityMode
|
||||
}
|
||||
|
||||
fun NavigationBarView.applyNavBarStyle(): Int {
|
||||
setLabelVisibility()
|
||||
|
||||
val navBarItems = getNavBarItems(this.context)
|
||||
|
||||
|
|
|
|||
|
|
@ -546,7 +546,7 @@ class NotificationUtil(var context: Context) {
|
|||
)
|
||||
)
|
||||
.setContentText("")
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setProgress(PROGRESS_MAX, PROGRESS_CURR, false)
|
||||
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
package com.deniscerri.ytdl.util
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.text.Spanned
|
||||
import android.util.TypedValue
|
||||
import androidx.annotation.DrawableRes
|
||||
|
|
@ -11,12 +14,50 @@ import androidx.appcompat.app.AppCompatDelegate
|
|||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.test.runner.lifecycle.ActivityLifecycleCallback
|
||||
import androidx.test.runner.lifecycle.Stage
|
||||
import com.deniscerri.ytdl.MainActivity
|
||||
import com.deniscerri.ytdl.R
|
||||
import com.google.android.material.color.DynamicColors
|
||||
|
||||
|
||||
object ThemeUtil {
|
||||
|
||||
private val activities = mutableListOf<Activity>()
|
||||
|
||||
fun init(app: Application) {
|
||||
app.registerActivityLifecycleCallbacks(object: Application.ActivityLifecycleCallbacks {
|
||||
override fun onActivityCreated(p0: Activity, p1: Bundle?) {
|
||||
activities.add(p0)
|
||||
}
|
||||
|
||||
override fun onActivityStarted(p0: Activity) {
|
||||
|
||||
}
|
||||
|
||||
override fun onActivityResumed(p0: Activity) {
|
||||
|
||||
}
|
||||
|
||||
override fun onActivityPaused(p0: Activity) {
|
||||
|
||||
}
|
||||
|
||||
override fun onActivityStopped(p0: Activity) {
|
||||
|
||||
}
|
||||
|
||||
override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {
|
||||
|
||||
}
|
||||
|
||||
override fun onActivityDestroyed(p0: Activity) {
|
||||
activities.remove(p0)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
sealed class AppIcon(
|
||||
@DrawableRes val iconResource: Int,
|
||||
val activityAlias: String
|
||||
|
|
@ -32,7 +73,18 @@ object ThemeUtil {
|
|||
AppIcon.Dark
|
||||
)
|
||||
|
||||
fun updateTheme(activity: AppCompatActivity) {
|
||||
fun recreateMain() {
|
||||
activities.firstOrNull { it.javaClass == MainActivity::class.java }?.recreate()
|
||||
}
|
||||
|
||||
fun updateThemes() {
|
||||
activities.forEach {
|
||||
updateTheme(it)
|
||||
it.recreate()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateTheme(activity: Activity) {
|
||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
|
||||
//update accent
|
||||
|
|
|
|||
|
|
@ -389,112 +389,6 @@ object UiUtil {
|
|||
}
|
||||
|
||||
|
||||
fun openFileIntent(context: Context, downloadPath: String) {
|
||||
val uri = downloadPath.runCatching {
|
||||
DocumentFile.fromSingleUri(context, Uri.parse(downloadPath)).run{
|
||||
if (this?.exists() == true){
|
||||
this.uri
|
||||
}else if (File(this@runCatching).exists()){
|
||||
FileProvider.getUriForFile(context, context.packageName + ".fileprovider",
|
||||
File(this@runCatching))
|
||||
}else null
|
||||
}
|
||||
}.getOrNull()
|
||||
|
||||
if (uri == null){
|
||||
Toast.makeText(context, "Error opening file!", Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
Intent().apply {
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
action = Intent.ACTION_VIEW
|
||||
data = uri
|
||||
}.run{
|
||||
context.startActivity(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openMultipleFilesIntent(context: Activity, path: List<String>){
|
||||
val bottomSheet = BottomSheetDialog(context)
|
||||
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
bottomSheet.setContentView(R.layout.filepathlist)
|
||||
|
||||
val list = bottomSheet.findViewById<LinearLayout>(R.id.filepath_list)
|
||||
|
||||
list?.apply {
|
||||
path.forEach {path ->
|
||||
val file = File(path)
|
||||
val item = context.layoutInflater.inflate(R.layout.filepath_card, list, false)
|
||||
item.apply {
|
||||
findViewById<TextView>(R.id.file_name).text = file.nameWithoutExtension
|
||||
|
||||
findViewById<TextView>(R.id.duration).apply {
|
||||
val duration = file.getMediaDuration(context)
|
||||
isVisible = duration > 0
|
||||
text = duration.toStringDuration(Locale.US)
|
||||
}
|
||||
|
||||
|
||||
findViewById<TextView>(R.id.filesize).text = FileUtil.convertFileSize(file.length())
|
||||
findViewById<TextView>(R.id.extension).text = file.extension.uppercase()
|
||||
if (!file.exists()){
|
||||
isEnabled = false
|
||||
alpha = 0.7f
|
||||
}
|
||||
isEnabled = file.exists()
|
||||
setOnClickListener {
|
||||
openFileIntent(context, path)
|
||||
bottomSheet.dismiss()
|
||||
}
|
||||
}
|
||||
list.addView(item)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bottomSheet.show()
|
||||
val displayMetrics = DisplayMetrics()
|
||||
context.windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||
bottomSheet.behavior.peekHeight = displayMetrics.heightPixels
|
||||
bottomSheet.window!!.setLayout(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
}
|
||||
|
||||
fun shareFileIntent(context: Context, paths: List<String>){
|
||||
val uris : ArrayList<Uri> = arrayListOf()
|
||||
paths.runCatching {
|
||||
this.forEach {path ->
|
||||
val uri = DocumentFile.fromSingleUri(context, Uri.parse(path)).run{
|
||||
if (this?.exists() == true){
|
||||
this.uri
|
||||
}else if (File(path).exists()){
|
||||
FileProvider.getUriForFile(context, context.packageName + ".fileprovider",
|
||||
File(path))
|
||||
}else null
|
||||
}
|
||||
if (uri != null) uris.add(uri)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (uris.isEmpty()){
|
||||
Toast.makeText(context, "Error sharing files!", Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
Intent().apply {
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
action = Intent.ACTION_SEND_MULTIPLE
|
||||
putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
|
||||
type = if (uris.size == 1) uris[0].let { context.contentResolver.getType(it) } ?: "media/*" else "*/*"
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
}.run{
|
||||
context.startActivity(Intent.createChooser(this, context.getString(R.string.share)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun showDatePickerOnly(fragmentManager: FragmentManager , onSubmit : (chosenDate: Calendar) -> Unit ){
|
||||
val date = Calendar.getInstance()
|
||||
|
|
@ -816,7 +710,7 @@ object UiUtil {
|
|||
}
|
||||
|
||||
setOnClickListener {
|
||||
shareFileIntent(context, item.downloadPath)
|
||||
FileUtil.shareFileIntent(context, item.downloadPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -903,7 +797,7 @@ object UiUtil {
|
|||
openFile!!.tag = item.id
|
||||
openFile.setOnClickListener{
|
||||
if (item.downloadPath.size == 1) {
|
||||
openFileIntent(context, item.downloadPath.first())
|
||||
FileUtil.openFileIntent(context, item.downloadPath.first())
|
||||
}else{
|
||||
openMultipleFilesIntent(context, item.downloadPath)
|
||||
}
|
||||
|
|
@ -2046,4 +1940,52 @@ object UiUtil {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun openMultipleFilesIntent(context: Activity, path: List<String>){
|
||||
val bottomSheet = BottomSheetDialog(context)
|
||||
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
bottomSheet.setContentView(R.layout.filepathlist)
|
||||
|
||||
val list = bottomSheet.findViewById<LinearLayout>(R.id.filepath_list)
|
||||
|
||||
list?.apply {
|
||||
path.forEach {path ->
|
||||
val file = File(path)
|
||||
val item = context.layoutInflater.inflate(R.layout.filepath_card, list, false)
|
||||
item.apply {
|
||||
findViewById<TextView>(R.id.file_name).text = file.nameWithoutExtension
|
||||
|
||||
findViewById<TextView>(R.id.duration).apply {
|
||||
val duration = file.getMediaDuration(context)
|
||||
isVisible = duration > 0
|
||||
text = duration.toStringDuration(Locale.US)
|
||||
}
|
||||
|
||||
|
||||
findViewById<TextView>(R.id.filesize).text = FileUtil.convertFileSize(file.length())
|
||||
findViewById<TextView>(R.id.extension).text = file.extension.uppercase()
|
||||
if (!file.exists()){
|
||||
isEnabled = false
|
||||
alpha = 0.7f
|
||||
}
|
||||
isEnabled = file.exists()
|
||||
setOnClickListener {
|
||||
FileUtil.openFileIntent(context, path)
|
||||
bottomSheet.dismiss()
|
||||
}
|
||||
}
|
||||
list.addView(item)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bottomSheet.show()
|
||||
val displayMetrics = DisplayMetrics()
|
||||
context.windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||
bottomSheet.behavior.peekHeight = displayMetrics.heightPixels
|
||||
bottomSheet.window!!.setLayout(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -137,7 +137,7 @@ class UpdateUtil(var context: Context) {
|
|||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent) {
|
||||
context?.unregisterReceiver(this)
|
||||
UiUtil.openFileIntent(context!!,
|
||||
FileUtil.openFileIntent(context!!,
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)?.absolutePath +
|
||||
File.separator + releaseVersion.name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import com.deniscerri.ytdl.util.NotificationUtil
|
|||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
|
||||
class UpdatePlaylistFormatsWorker(
|
||||
class UpdateMultipleDownloadsFormatsWorker(
|
||||
private val context: Context,
|
||||
workerParams: WorkerParameters
|
||||
) : Worker(context, workerParams) {
|
||||
|
|
@ -38,7 +38,7 @@ class UpdatePlaylistFormatsWorker(
|
|||
ids.forEach {
|
||||
if (!isStopped){
|
||||
val d = dao.getDownloadById(it)
|
||||
val r = resDao.getResultByURL(d.url)!!
|
||||
val r = resDao.getResultByURL(d.url)
|
||||
|
||||
if (d.allFormats.isNotEmpty()){
|
||||
count++
|
||||
|
|
@ -50,18 +50,18 @@ class UpdatePlaylistFormatsWorker(
|
|||
d.allFormats.addAll(infoUtil.getFormats(d.url))
|
||||
d.format = vm.getFormat(d.allFormats,d.type)
|
||||
|
||||
r.formats.clear()
|
||||
r.formats.addAll(d.allFormats)
|
||||
r?.formats?.clear()
|
||||
r?.formats?.addAll(d.allFormats)
|
||||
|
||||
runBlocking {
|
||||
resDao.update(r)
|
||||
r?.apply { resDao.update(this) }
|
||||
dao.update(d)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
count++
|
||||
notificationUtil.updateFormatUpdateNotification(workID, UpdatePlaylistFormatsWorker::class.java.name, count, ids.size)
|
||||
notificationUtil.updateFormatUpdateNotification(workID, UpdateMultipleDownloadsFormatsWorker::class.java.name, count, ids.size)
|
||||
}else{
|
||||
throw Exception()
|
||||
}
|
||||
|
|
@ -79,8 +79,4 @@ class UpdatePlaylistFormatsWorker(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onStopped() {
|
||||
Log.e("asd", "Asd")
|
||||
super.onStopped()
|
||||
}
|
||||
}
|
||||
|
|
@ -37,103 +37,110 @@
|
|||
app:useDefaultControls="true"
|
||||
app:use_controller="false" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:translationZ="10dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/pause"
|
||||
android:visibility="gone"
|
||||
android:clickable="false"
|
||||
app:iconSize="50dp"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
app:iconTint="?android:colorAccent"
|
||||
app:icon="@drawable/exomedia_ic_play_arrow_white"
|
||||
android:layout_width="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/rewind"
|
||||
android:clickable="false"
|
||||
app:iconSize="50dp"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
app:iconTint="?android:colorAccent"
|
||||
app:icon="@drawable/exomedia_ic_rewind_white"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:contentDescription="@string/restart"
|
||||
android:indeterminate="true"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/mute"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:contentDescription="@string/remove_audio"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_music"
|
||||
app:iconSize="30dp"
|
||||
app:iconTint="?android:colorAccent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/forward"
|
||||
android:clickable="false"
|
||||
app:iconSize="50dp"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
app:iconTint="?android:colorAccent"
|
||||
app:icon="@drawable/exomedia_ic_fast_forward_white"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:contentDescription="@string/end"
|
||||
android:indeterminate="true"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/durationText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:background="#80000000"
|
||||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLength="17"
|
||||
android:maxLines="1"
|
||||
android:minWidth="30dp"
|
||||
android:paddingHorizontal="5dp"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
app:cornerRadius="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:translationZ="10dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/frame_layout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/pause"
|
||||
android:visibility="gone"
|
||||
android:clickable="false"
|
||||
app:iconSize="50dp"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
app:iconTint="?android:colorAccent"
|
||||
app:icon="@drawable/exomedia_ic_play_arrow_white"
|
||||
android:layout_width="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/frame_layout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/rewind"
|
||||
android:clickable="false"
|
||||
app:iconSize="50dp"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
app:iconTint="?android:colorAccent"
|
||||
app:icon="@drawable/exomedia_ic_rewind_white"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:contentDescription="@string/restart"
|
||||
android:indeterminate="true"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/frame_layout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/mute"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:contentDescription="@string/remove_audio"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_music"
|
||||
app:iconSize="30dp"
|
||||
app:iconTint="?android:colorAccent"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/frame_layout"
|
||||
app:layout_constraintEnd_toEndOf="@+id/frame_layout" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/forward"
|
||||
android:clickable="false"
|
||||
app:iconSize="50dp"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
app:iconTint="?android:colorAccent"
|
||||
app:icon="@drawable/exomedia_ic_fast_forward_white"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:contentDescription="@string/end"
|
||||
android:indeterminate="true"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/frame_layout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/durationText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:background="#80000000"
|
||||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLength="17"
|
||||
android:maxLines="1"
|
||||
android:minWidth="30dp"
|
||||
android:paddingHorizontal="5dp"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
app:cornerRadius="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/frame_layout"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cut_section"
|
||||
|
|
@ -167,7 +174,6 @@
|
|||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toTopOf="@id/suggested_cuts"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.533"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/rangeSlider"
|
||||
app:layout_constraintVertical_bias="0.0">
|
||||
|
|
@ -177,6 +183,7 @@
|
|||
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="70dp"
|
||||
android:hint="@string/start">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
|
|
@ -203,6 +210,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/end"
|
||||
android:minWidth="70dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/colon">
|
||||
|
||||
|
|
|
|||
|
|
@ -410,4 +410,6 @@
|
|||
<string name="restore_info">Press \'Restore\' to merge the saved data with your current data.\nPress \'Reset\' to erase and only use the saved data in the file.</string>
|
||||
<string name="use_alarm_manager">Use AlarmManager instead of WorkManager for Scheduling</string>
|
||||
<string name="use_alarm_manager_summary">Enable this if WorkManager is restricted by your device vendor or scheduled jobs are not too precise</string>
|
||||
<string name="use_item_url_not_playlist">Use Item URL instead of Playlist URL</string>
|
||||
<string name="use_item_url_not_playlist_summary">Useful for Libretube offline playlists that are not recognised by yt-dlp. Playlist metadata wont be embedded with this enabled</string>
|
||||
</resources>
|
||||
|
|
@ -99,6 +99,17 @@
|
|||
app:defaultValue=""
|
||||
android:summary="@string/piped_instance_summary"
|
||||
app:title="@string/piped_instance" />
|
||||
|
||||
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
app:defaultValue="false"
|
||||
app:icon="@drawable/baseline_format_quote_24"
|
||||
app:key="use_itemurl_instead_playlisturl"
|
||||
app:summary="@string/use_item_url_not_playlist_summary"
|
||||
app:title="@string/use_item_url_not_playlist" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/misc">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<external-path name="external_files" path="."/>
|
||||
<root-path name="external_files" path="/storage/" />
|
||||
<root-path name="external_files" path="." />
|
||||
<cache-path
|
||||
name="cache"
|
||||
path="." />
|
||||
|
|
|
|||
Loading…
Reference in a new issue