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.core.content.edit
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.deniscerri.ytdl.util.NotificationUtil
|
import com.deniscerri.ytdl.util.NotificationUtil
|
||||||
|
import com.deniscerri.ytdl.util.ThemeUtil
|
||||||
import com.yausername.aria2c.Aria2c
|
import com.yausername.aria2c.Aria2c
|
||||||
import com.yausername.ffmpeg.FFmpeg
|
import com.yausername.ffmpeg.FFmpeg
|
||||||
import com.yausername.youtubedl_android.YoutubeDL
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
|
|
@ -44,6 +45,7 @@ class App : Application() {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ThemeUtil.init(this)
|
||||||
}
|
}
|
||||||
@Throws(YoutubeDLException::class)
|
@Throws(YoutubeDLException::class)
|
||||||
private fun initLibraries() {
|
private fun initLibraries() {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
class ResultRepository(private val resultDao: ResultDao, private val context: Context) {
|
class ResultRepository(private val resultDao: ResultDao, private val context: Context) {
|
||||||
private val tag: String = "ResultRepository"
|
|
||||||
val YTDLNIS_SEARCH = "YTDLNIS_SEARCH"
|
val YTDLNIS_SEARCH = "YTDLNIS_SEARCH"
|
||||||
val allResults : Flow<List<ResultItem>> = resultDao.getResults()
|
val allResults : Flow<List<ResultItem>> = resultDao.getResults()
|
||||||
var itemCount = MutableStateFlow(-1)
|
var itemCount = MutableStateFlow(-1)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.deniscerri.ytdl.database.viewmodel
|
package com.deniscerri.ytdl.database.viewmodel
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
|
@ -9,12 +8,9 @@ import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.util.Log
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.res.ResourcesCompat
|
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MediatorLiveData
|
|
||||||
import androidx.lifecycle.asLiveData
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.paging.PagingData
|
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.DownloadRepository
|
||||||
import com.deniscerri.ytdl.database.repository.HistoryRepository
|
import com.deniscerri.ytdl.database.repository.HistoryRepository
|
||||||
import com.deniscerri.ytdl.database.repository.ResultRepository
|
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.ui.downloadcard.MultipleItemFormatTuple
|
||||||
import com.deniscerri.ytdl.util.Extensions.toListString
|
import com.deniscerri.ytdl.util.Extensions.toListString
|
||||||
import com.deniscerri.ytdl.util.FileUtil
|
import com.deniscerri.ytdl.util.FileUtil
|
||||||
import com.deniscerri.ytdl.util.FormatSorter
|
import com.deniscerri.ytdl.util.FormatSorter
|
||||||
import com.deniscerri.ytdl.util.InfoUtil
|
import com.deniscerri.ytdl.util.InfoUtil
|
||||||
import com.deniscerri.ytdl.work.AlarmScheduler
|
import com.deniscerri.ytdl.work.AlarmScheduler
|
||||||
import com.deniscerri.ytdl.work.UpdatePlaylistFormatsWorker
|
import com.deniscerri.ytdl.work.UpdateMultipleDownloadsFormatsWorker
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import okhttp3.internal.format
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Locale
|
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) {
|
val job = viewModelScope.launch(Dispatchers.IO) {
|
||||||
repository.deleteProcessing()
|
repository.deleteProcessing()
|
||||||
processingItems.emit(true)
|
processingItems.emit(true)
|
||||||
try {
|
try {
|
||||||
itemIDs.forEachIndexed { idx, it ->
|
itemIDs.forEach {
|
||||||
val item = repository.getItemByID(it)
|
val item = repository.getItemByID(it)
|
||||||
if (processingItemsJob?.isCancelled == true) throw CancellationException()
|
if (processingItemsJob?.isCancelled == true) throw CancellationException()
|
||||||
|
if (!deleteExisting) item.id = 0
|
||||||
item.id = 0
|
|
||||||
item.status = DownloadRepository.Status.Processing.toString()
|
item.status = DownloadRepository.Status.Processing.toString()
|
||||||
repository.insert(item)
|
repository.update(item)
|
||||||
}
|
}
|
||||||
processingItems.emit(false)
|
processingItems.emit(false)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
@ -1059,7 +1051,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
dao.updateProcessingtoSavedStatus()
|
dao.updateProcessingtoSavedStatus()
|
||||||
|
|
||||||
val id = System.currentTimeMillis().toInt()
|
val id = System.currentTimeMillis().toInt()
|
||||||
val workRequest = OneTimeWorkRequestBuilder<UpdatePlaylistFormatsWorker>()
|
val workRequest = OneTimeWorkRequestBuilder<UpdateMultipleDownloadsFormatsWorker>()
|
||||||
.setInputData(
|
.setInputData(
|
||||||
Data.Builder()
|
Data.Builder()
|
||||||
.putLongArray("ids", ids.toLongArray())
|
.putLongArray("ids", ids.toLongArray())
|
||||||
|
|
|
||||||
|
|
@ -308,7 +308,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
arguments?.remove("showDownloadsWithUpdatedFormats")
|
arguments?.remove("showDownloadsWithUpdatedFormats")
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val ids = arguments?.getLongArray("downloadIds") ?: return@launch
|
val ids = arguments?.getLongArray("downloadIds") ?: return@launch
|
||||||
downloadViewModel.turnDownloadItemsToProcessingDownloads(ids.toList())
|
downloadViewModel.turnDownloadItemsToProcessingDownloads(ids.toList(), deleteExisting = true)
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2)
|
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,8 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
|
||||||
companion object {
|
companion object {
|
||||||
private val DIFF_CALLBACK: DiffUtil.ItemCallback<ResultItem> = object : DiffUtil.ItemCallback<ResultItem>() {
|
private val DIFF_CALLBACK: DiffUtil.ItemCallback<ResultItem> = object : DiffUtil.ItemCallback<ResultItem>() {
|
||||||
override fun areItemsTheSame(oldItem: ResultItem, newItem: ResultItem): Boolean {
|
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 {
|
override fun areContentsTheSame(oldItem: ResultItem, newItem: ResultItem): Boolean {
|
||||||
|
|
|
||||||
|
|
@ -108,8 +108,11 @@ class ConfigureDownloadBottomSheetDialog(private val currentDownloadItem: Downlo
|
||||||
fragmentManager,
|
fragmentManager,
|
||||||
lifecycle,
|
lifecycle,
|
||||||
null,
|
null,
|
||||||
currentDownloadItem
|
currentDownloadItem,
|
||||||
|
isIncognito = incognito
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
viewPager2.adapter = fragmentAdapter
|
viewPager2.adapter = fragmentAdapter
|
||||||
viewPager2.isSaveFromParentEnabled = false
|
viewPager2.isSaveFromParentEnabled = false
|
||||||
|
|
||||||
|
|
@ -182,7 +185,7 @@ class ConfigureDownloadBottomSheetDialog(private val currentDownloadItem: Downlo
|
||||||
putString("last_used_download_type",
|
putString("last_used_download_type",
|
||||||
listOf(Type.audio, Type.video, Type.command)[position].toString())
|
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
|
incognito = !incognito
|
||||||
|
fragmentAdapter.isIncognito = incognito
|
||||||
val onOff = if (incognito) getString(R.string.ok) else getString(R.string.disabled)
|
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()
|
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{
|
private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem{
|
||||||
val item = when(selectedTabPosition){
|
return fragmentAdapter.getDownloadItem(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 -> {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -180,38 +180,32 @@ class CutVideoBottomSheetDialog(private val _item: DownloadItem? = null, private
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
try {
|
try {
|
||||||
val data : MutableList<String?> = withContext(Dispatchers.IO){
|
val data = withContext(Dispatchers.IO) {
|
||||||
if (urls.isNullOrEmpty()) {
|
if (urls.isNullOrEmpty()) {
|
||||||
infoUtil.getStreamingUrlAndChapters(item.url)
|
infoUtil.getStreamingUrlAndChapters(item.url)
|
||||||
}else {
|
}else{
|
||||||
urls.split("\n").toMutableList()
|
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()){
|
if (chapters!!.isEmpty() && urls!!.isBlank()){
|
||||||
try{
|
chapters = data.second
|
||||||
val listType: Type = object : TypeToken<List<ChapterItem>>() {}.type
|
|
||||||
chapters = Gson().fromJson(data.first().toString(), listType)
|
|
||||||
data.removeFirst()
|
|
||||||
}catch (ignored: Exception) {
|
|
||||||
data.removeFirst()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.isEmpty()) throw Exception("No Streaming URL found!")
|
val urls = data.first
|
||||||
if (data.size == 2){
|
if (urls.size == 2){
|
||||||
val audioSource : MediaSource =
|
val audioSource : MediaSource =
|
||||||
DefaultMediaSourceFactory(requireContext())
|
DefaultMediaSourceFactory(requireContext())
|
||||||
.createMediaSource(fromUri(Uri.parse(data[0])))
|
.createMediaSource(fromUri(Uri.parse(urls[0])))
|
||||||
val videoSource: MediaSource =
|
val videoSource: MediaSource =
|
||||||
DefaultMediaSourceFactory(requireContext())
|
DefaultMediaSourceFactory(requireContext())
|
||||||
.createMediaSource(fromUri(Uri.parse(data[1])))
|
.createMediaSource(fromUri(Uri.parse(urls[1])))
|
||||||
player.setMediaSource(MergingMediaSource(videoSource, audioSource))
|
player.setMediaSource(MergingMediaSource(videoSource, audioSource))
|
||||||
}else{
|
}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
|
progress.visibility = View.GONE
|
||||||
populateSuggestedChapters()
|
populateSuggestedChapters()
|
||||||
|
|
@ -221,6 +215,7 @@ class CutVideoBottomSheetDialog(private val _item: DownloadItem? = null, private
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
progress.visibility = View.GONE
|
progress.visibility = View.GONE
|
||||||
frame.visibility = View.GONE
|
frame.visibility = View.GONE
|
||||||
|
videoView.visibility = View.GONE
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -239,10 +239,6 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
|
||||||
this.formats.removeAll(formats.toSet())
|
this.formats.removeAll(formats.toSet())
|
||||||
this.formats.addAll(allFormats.filter { !genericAudioFormats.contains(it) })
|
this.formats.addAll(allFormats.filter { !genericAudioFormats.contains(it) })
|
||||||
resultViewModel.update(this)
|
resultViewModel.update(this)
|
||||||
kotlin.runCatching {
|
|
||||||
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
|
||||||
f1.updateUI(this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentDownloadItem?.apply {
|
currentDownloadItem?.apply {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import android.annotation.SuppressLint
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
|
@ -18,9 +17,7 @@ import android.widget.Button
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.core.content.res.ResourcesCompat
|
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
|
@ -190,18 +187,6 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
(tabLayout.getChildAt(0) as? ViewGroup)?.getChildAt(1)?.alpha = 0.3f
|
(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
|
//remove outdated player url of 1hr so it can refetch it in the cut player
|
||||||
if (result.creationTime > System.currentTimeMillis() - 3600000) result.urls = ""
|
if (result.creationTime > System.currentTimeMillis() - 3600000) result.urls = ""
|
||||||
|
|
||||||
|
|
@ -210,7 +195,8 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
fragmentManager,
|
fragmentManager,
|
||||||
lifecycle,
|
lifecycle,
|
||||||
result,
|
result,
|
||||||
currentDownloadItem
|
currentDownloadItem,
|
||||||
|
isIncognito = incognito
|
||||||
)
|
)
|
||||||
|
|
||||||
viewPager2.adapter = fragmentAdapter
|
viewPager2.adapter = fragmentAdapter
|
||||||
|
|
@ -239,6 +225,18 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
}, 200)
|
}, 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) {
|
sharedPreferences.edit(commit = true) {
|
||||||
|
|
@ -294,7 +292,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
putString("last_used_download_type",
|
putString("last_used_download_type",
|
||||||
listOf(Type.audio, Type.video, Type.command)[position].toString())
|
listOf(Type.audio, Type.video, Type.command)[position].toString())
|
||||||
}
|
}
|
||||||
updateWhenSwitching()
|
fragmentAdapter.updateWhenSwitching(viewPager2.currentItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -441,6 +439,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
incognito = !incognito
|
incognito = !incognito
|
||||||
|
fragmentAdapter.isIncognito = incognito
|
||||||
val onOff = if (incognito) getString(R.string.ok) else getString(R.string.disabled)
|
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()
|
Toast.makeText(requireContext(), "${getString(R.string.incognito)}: $onOff", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
@ -498,42 +497,46 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
if (it){
|
if (it){
|
||||||
delay(500)
|
delay(500)
|
||||||
runCatching {
|
runCatching {
|
||||||
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
|
(fragmentAdapter.fragments[0] as DownloadAudioFragment).apply {
|
||||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||||
isVisible = true
|
isVisible = true
|
||||||
isClickable = true
|
isClickable = true
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
resultViewModel.cancelUpdateFormatsItemData()
|
resultViewModel.cancelUpdateFormatsItemData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runCatching {
|
runCatching {
|
||||||
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
|
(fragmentAdapter.fragments[1] as DownloadVideoFragment).apply {
|
||||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||||
isVisible = true
|
isVisible = true
|
||||||
isClickable = true
|
isClickable = true
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
resultViewModel.cancelUpdateFormatsItemData()
|
resultViewModel.cancelUpdateFormatsItemData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
runCatching {
|
runCatching {
|
||||||
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
|
(fragmentAdapter.fragments[0] as DownloadAudioFragment).apply {
|
||||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||||
isVisible = false
|
isVisible = false
|
||||||
isClickable = false
|
isClickable = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runCatching {
|
runCatching {
|
||||||
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
|
(fragmentAdapter.fragments[1] as DownloadVideoFragment).apply {
|
||||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||||
isVisible = false
|
isVisible = false
|
||||||
isClickable = false
|
isClickable = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -546,43 +549,34 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
if (result == null) return@collectLatest
|
if (result == null) return@collectLatest
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
lifecycleScope.launch(Dispatchers.Main) {
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
if (result.isNotEmpty()){
|
if (result.size == 1 && result[0] != null) {
|
||||||
if (result.size == 1 && result[0] != null){
|
val res = result[0]!!
|
||||||
fragmentAdapter.setResultItem(result[0]!!)
|
fragmentAdapter.setResultItem(res)
|
||||||
runCatching {
|
|
||||||
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
|
||||||
f.updateUI(result[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
runCatching {
|
title.visibility = View.VISIBLE
|
||||||
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
subtitle.visibility = View.VISIBLE
|
||||||
f1.updateUI(result[0])
|
shimmerLoading.visibility = View.GONE
|
||||||
}
|
shimmerLoadingSubtitle.visibility = View.GONE
|
||||||
|
shimmerLoading.stopShimmer()
|
||||||
|
shimmerLoadingSubtitle.stopShimmer()
|
||||||
|
|
||||||
title.visibility = View.VISIBLE
|
val usingGenericFormatsOrEmpty = res.formats.isEmpty() || res.formats.any { it.format_note.contains("ytdlnisgeneric") }
|
||||||
subtitle.visibility = View.VISIBLE
|
arguments?.putParcelable("result", res)
|
||||||
shimmerLoading.visibility = View.GONE
|
if (usingGenericFormatsOrEmpty && sharedPreferences.getBoolean("update_formats", false)){
|
||||||
shimmerLoadingSubtitle.visibility = View.GONE
|
initUpdateFormats(res)
|
||||||
shimmerLoading.stopShimmer()
|
}
|
||||||
shimmerLoadingSubtitle.stopShimmer()
|
|
||||||
|
|
||||||
val usingGenericFormatsOrEmpty = result[0]!!.formats.isEmpty() || result[0]!!.formats.any { it.format_note.contains("ytdlnisgeneric") }
|
}else if (result.size > 1) {
|
||||||
fragmentAdapter.setResultItem(result[0]!!)
|
//open multi download card instead
|
||||||
arguments?.putParcelable("result", result[0]!!)
|
if (activity is ShareActivity){
|
||||||
if (usingGenericFormatsOrEmpty && sharedPreferences.getBoolean("update_formats", false)){
|
findNavController().navigate(R.id.action_downloadBottomSheetDialog_to_selectPlaylistItemsDialog, bundleOf(
|
||||||
initUpdateFormats(result[0]!!)
|
Pair("resultIDs", result.map { it!!.id }.toLongArray()),
|
||||||
}
|
))
|
||||||
}else{
|
}else{
|
||||||
//open multi download card instead
|
dismiss()
|
||||||
if (activity is ShareActivity){
|
|
||||||
findNavController().navigate(R.id.action_downloadBottomSheetDialog_to_selectPlaylistItemsDialog, bundleOf(
|
|
||||||
Pair("resultIDs", result.map { it!!.id }.toLongArray()),
|
|
||||||
))
|
|
||||||
}else{
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resultViewModel.updateResultData.emit(null)
|
resultViewModel.updateResultData.emit(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -606,19 +600,17 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
runCatching {
|
runCatching {
|
||||||
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
|
val f1 = fragmentAdapter.fragments[0] as DownloadAudioFragment
|
||||||
val resultItem = downloadViewModel.createResultItemFromDownload(f1.downloadItem)
|
val resultItem = downloadViewModel.createResultItemFromDownload(f1.downloadItem)
|
||||||
resultItem.formats = formats
|
resultItem.formats = formats
|
||||||
fragmentAdapter.setResultItem(resultItem)
|
fragmentAdapter.setResultItem(resultItem)
|
||||||
f1.updateUI(resultItem)
|
|
||||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
|
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
runCatching {
|
runCatching {
|
||||||
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
|
val f1 = fragmentAdapter.fragments[1] as DownloadVideoFragment
|
||||||
val resultItem = downloadViewModel.createResultItemFromDownload(f1.downloadItem)
|
val resultItem = downloadViewModel.createResultItemFromDownload(f1.downloadItem)
|
||||||
resultItem.formats = formats
|
resultItem.formats = formats
|
||||||
fragmentAdapter.setResultItem(resultItem)
|
fragmentAdapter.setResultItem(resultItem)
|
||||||
f1.updateUI(resultItem)
|
|
||||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
|
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 {
|
private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem {
|
||||||
val item = when(selectedTabPosition){
|
return fragmentAdapter.getDownloadItem(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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAlsoAudioDownloadItem(finished: (it: DownloadItem) -> Unit) {
|
private fun getAlsoAudioDownloadItem(finished: (it: DownloadItem) -> Unit) {
|
||||||
try {
|
try {
|
||||||
val ff = (fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment)
|
val ff = fragmentAdapter.fragments[0] as DownloadAudioFragment
|
||||||
ff.updateSelectedAudioFormat(getDownloadItem(1).videoPreferences.audioFormatIDs.first())
|
ff.updateSelectedAudioFormat(getDownloadItem(1).videoPreferences.audioFormatIDs.first())
|
||||||
finished(ff.downloadItem)
|
finished(ff.downloadItem)
|
||||||
}catch (e: Exception){
|
}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() {
|
private fun initUpdateData() {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
if (result.url.isBlank()) {
|
if (result.url.isBlank()) {
|
||||||
|
|
|
||||||
|
|
@ -12,33 +12,86 @@ class DownloadFragmentAdapter (
|
||||||
lifecycle : Lifecycle,
|
lifecycle : Lifecycle,
|
||||||
private var result: ResultItem?,
|
private var result: ResultItem?,
|
||||||
private var downloadItem: DownloadItem?,
|
private var downloadItem: DownloadItem?,
|
||||||
private var nonSpecific: Boolean = false
|
nonSpecific: Boolean = false,
|
||||||
|
var isIncognito: Boolean = false
|
||||||
) : FragmentStateAdapter(fragmentManager, lifecycle) {
|
) : FragmentStateAdapter(fragmentManager, lifecycle) {
|
||||||
|
|
||||||
fun setResultItem(res: ResultItem){
|
fun setResultItem(res: ResultItem){
|
||||||
result = res
|
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){
|
private fun setTitleAuthor(t: String, a: String){
|
||||||
downloadItem = dd
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setTitleAuthor(t: String, a: String){
|
|
||||||
result?.title = t
|
result?.title = t
|
||||||
result?.author = a
|
result?.author = a
|
||||||
downloadItem?.title = t
|
downloadItem?.title = t
|
||||||
downloadItem?.author = a
|
downloadItem?.author = a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val fragments = listOf<Fragment>(
|
||||||
|
DownloadAudioFragment(result, downloadItem,"", nonSpecific),
|
||||||
|
DownloadVideoFragment(result, downloadItem,"", nonSpecific),
|
||||||
|
DownloadCommandFragment(result, downloadItem)
|
||||||
|
)
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return 3
|
return fragments.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun createFragment(position: Int): Fragment {
|
override fun createFragment(position: Int): Fragment {
|
||||||
return when(position){
|
return fragments[position]
|
||||||
0 -> DownloadAudioFragment(result, downloadItem,"", nonSpecific)
|
}
|
||||||
1 -> DownloadVideoFragment(result, downloadItem,"", nonSpecific)
|
|
||||||
else -> DownloadCommandFragment(result, downloadItem)
|
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.removeAll(formats)
|
||||||
this.formats.addAll(allFormats.filter { !genericVideoFormats.contains(it) })
|
this.formats.addAll(allFormats.filter { !genericVideoFormats.contains(it) })
|
||||||
resultViewModel.update(this)
|
resultViewModel.update(this)
|
||||||
kotlin.runCatching {
|
|
||||||
val f1 = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
|
||||||
f1.updateUI(this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentDownloadItem?.apply {
|
currentDownloadItem?.apply {
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,7 @@ class FormatSelectionBottomSheetDialog(
|
||||||
if (items.size == 1) {
|
if (items.size == 1) {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val res = infoUtil.getFormats(items.first()!!.url, currentFormatSource)
|
val res = infoUtil.getFormats(items.first()!!.url, currentFormatSource)
|
||||||
|
if (!isActive) return@launch
|
||||||
res.filter { it.format_note != "storyboard" }
|
res.filter { it.format_note != "storyboard" }
|
||||||
chosenFormats = if (items.first()?.type == Type.audio) {
|
chosenFormats = if (items.first()?.type == Type.audio) {
|
||||||
res.filter { it.format_note.contains("audio", ignoreCase = true) }
|
res.filter { it.format_note.contains("audio", ignoreCase = true) }
|
||||||
|
|
@ -230,6 +231,8 @@ class FormatSelectionBottomSheetDialog(
|
||||||
|
|
||||||
formats.clear()
|
formats.clear()
|
||||||
formats.addAll(res)
|
formats.addAll(res)
|
||||||
|
|
||||||
|
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
listener.onFormatsUpdated(res)
|
listener.onFormatsUpdated(res)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -283,29 +283,28 @@ class ResultCardDetailsDialog : BottomSheetDialogFragment(), GenericDownloadAdap
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
try {
|
try {
|
||||||
val data : MutableList<String?> = withContext(Dispatchers.IO){
|
val data = withContext(Dispatchers.IO) {
|
||||||
if (item.urls.isEmpty()) {
|
if (item.urls.isEmpty()) {
|
||||||
infoUtil.getStreamingUrlAndChapters(item.url)
|
infoUtil.getStreamingUrlAndChapters(item.url)
|
||||||
}else {
|
}else{
|
||||||
item.urls.split("\n").toMutableList()
|
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!")
|
val urls = data.first
|
||||||
if (data.size == 2){
|
if (urls.size == 2){
|
||||||
val audioSource : MediaSource =
|
val audioSource : MediaSource =
|
||||||
DefaultMediaSourceFactory(requireContext())
|
DefaultMediaSourceFactory(requireContext())
|
||||||
.createMediaSource(MediaItem.fromUri(Uri.parse(data[0])))
|
.createMediaSource(MediaItem.fromUri(Uri.parse(urls[0])))
|
||||||
val videoSource: MediaSource =
|
val videoSource: MediaSource =
|
||||||
DefaultMediaSourceFactory(requireContext())
|
DefaultMediaSourceFactory(requireContext())
|
||||||
.createMediaSource(MediaItem.fromUri(Uri.parse(data[1])))
|
.createMediaSource(MediaItem.fromUri(Uri.parse(urls[1])))
|
||||||
player.setMediaSource(MergingMediaSource(videoSource, audioSource))
|
player.setMediaSource(MergingMediaSource(videoSource, audioSource))
|
||||||
}else{
|
}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.prepare()
|
||||||
player.play()
|
player.play()
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,8 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
||||||
|
|
||||||
pause.setOnClickListener {
|
pause.setOnClickListener {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
pause.isEnabled = false
|
||||||
|
delay(1000)
|
||||||
workManager.cancelAllWorkByTag("download")
|
workManager.cancelAllWorkByTag("download")
|
||||||
val activeDownloadsList = withContext(Dispatchers.IO){
|
val activeDownloadsList = withContext(Dispatchers.IO){
|
||||||
downloadViewModel.getActiveDownloads()
|
downloadViewModel.getActiveDownloads()
|
||||||
|
|
@ -140,16 +142,15 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
||||||
resume.isEnabled = false
|
resume.isEnabled = false
|
||||||
resume.isVisible = true
|
resume.isVisible = true
|
||||||
activeDownloads.notifyDataSetChanged()
|
activeDownloads.notifyDataSetChanged()
|
||||||
withContext(Dispatchers.Main){
|
resume.isEnabled = true
|
||||||
delay(1000)
|
|
||||||
resume.isEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resume.setOnClickListener {
|
resume.setOnClickListener {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
resume.isEnabled = false
|
||||||
|
delay(1000)
|
||||||
preferences.edit().putBoolean("paused_downloads", false).apply()
|
preferences.edit().putBoolean("paused_downloads", false).apply()
|
||||||
resume.isVisible = false
|
resume.isVisible = false
|
||||||
pause.isEnabled = false
|
pause.isEnabled = false
|
||||||
|
|
@ -159,12 +160,9 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
||||||
downloadViewModel.startDownloadWorker(listOf())
|
downloadViewModel.startDownloadWorker(listOf())
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
activeDownloads.notifyDataSetChanged()
|
activeDownloads.notifyDataSetChanged()
|
||||||
|
pause.isEnabled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main){
|
|
||||||
delay(1000)
|
|
||||||
pause.isEnabled = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,9 +202,6 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
||||||
requireActivity().runOnUiThread {
|
requireActivity().runOnUiThread {
|
||||||
try {
|
try {
|
||||||
progressBar?.setProgressCompat(event.progress, true)
|
progressBar?.setProgressCompat(event.progress, true)
|
||||||
if (event.progress > 95) {
|
|
||||||
pause.isEnabled = false
|
|
||||||
}
|
|
||||||
outputText?.text = event.output
|
outputText?.text = event.output
|
||||||
}catch (ignored: Exception) {}
|
}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.database.viewmodel.HistoryViewModel
|
||||||
import com.deniscerri.ytdl.ui.adapter.HistoryAdapter
|
import com.deniscerri.ytdl.ui.adapter.HistoryAdapter
|
||||||
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
|
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
|
||||||
|
import com.deniscerri.ytdl.util.FileUtil
|
||||||
import com.deniscerri.ytdl.util.NavbarUtil
|
import com.deniscerri.ytdl.util.NavbarUtil
|
||||||
import com.deniscerri.ytdl.util.UiUtil
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
|
|
@ -134,7 +135,6 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
requireActivity()
|
requireActivity()
|
||||||
)
|
)
|
||||||
recyclerView = view.findViewById(R.id.recyclerviewhistorys)
|
recyclerView = view.findViewById(R.id.recyclerviewhistorys)
|
||||||
recyclerView.adapter = historyAdapter
|
|
||||||
recyclerView.enableFastScroll()
|
recyclerView.enableFastScroll()
|
||||||
|
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
|
@ -165,6 +165,10 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
historyAdapter!!.submitList(it)
|
historyAdapter!!.submitList(it)
|
||||||
historyList = it
|
historyList = it
|
||||||
scrollToTop()
|
scrollToTop()
|
||||||
|
|
||||||
|
if (recyclerView.adapter == null){
|
||||||
|
recyclerView.adapter = historyAdapter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
historyViewModel.sortOrder.observe(viewLifecycleOwner){
|
historyViewModel.sortOrder.observe(viewLifecycleOwner){
|
||||||
|
|
@ -552,7 +556,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.share -> {
|
R.id.share -> {
|
||||||
UiUtil.shareFileIntent(requireContext(), selectedObjects.map { it.downloadPath }.flatten())
|
FileUtil.shareFileIntent(requireContext(), selectedObjects.map { it.downloadPath }.flatten())
|
||||||
clearCheckedItems()
|
clearCheckedItems()
|
||||||
actionMode?.finish()
|
actionMode?.finish()
|
||||||
true
|
true
|
||||||
|
|
@ -677,7 +681,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
|
|
||||||
override fun onButtonClick(itemID: Long, isPresent: Boolean) {
|
override fun onButtonClick(itemID: Long, isPresent: Boolean) {
|
||||||
if (isPresent){
|
if (isPresent){
|
||||||
UiUtil.shareFileIntent(requireContext(), historyList!!.first { it!!.id == itemID }!!.downloadPath)
|
FileUtil.shareFileIntent(requireContext(), historyList!!.first { it!!.id == itemID }!!.downloadPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ class CookiesFragment : Fragment(), CookieAdapter.OnItemClickListener {
|
||||||
}else{
|
}else{
|
||||||
val snack = Snackbar.make(recyclerView, getString(R.string.backup_created_successfully), Snackbar.LENGTH_LONG)
|
val snack = Snackbar.make(recyclerView, getString(R.string.backup_created_successfully), Snackbar.LENGTH_LONG)
|
||||||
snack.setAction(R.string.share) {
|
snack.setAction(R.string.share) {
|
||||||
UiUtil.shareFileIntent(requireContext(), listOf(f.absolutePath))
|
FileUtil.shareFileIntent(requireContext(), listOf(f.absolutePath))
|
||||||
}
|
}
|
||||||
snack.show()
|
snack.show()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
||||||
findPreference<Preference>("label_visibility")?.apply {
|
findPreference<Preference>("label_visibility")?.apply {
|
||||||
isVisible = !resources.getBoolean(R.bool.uses_side_nav)
|
isVisible = !resources.getBoolean(R.bool.uses_side_nav)
|
||||||
setOnPreferenceChangeListener { preference, newValue ->
|
setOnPreferenceChangeListener { preference, newValue ->
|
||||||
restartApp()
|
ThemeUtil.recreateMain()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +162,7 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
NavbarUtil.setNavBarItems(adapter.items, requireContext())
|
NavbarUtil.setNavBarItems(adapter.items, requireContext())
|
||||||
NavbarUtil.setStartFragment(adapter.selectedHomeTabId)
|
NavbarUtil.setStartFragment(adapter.selectedHomeTabId)
|
||||||
restartApp()
|
ThemeUtil.recreateMain()
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.show()
|
.show()
|
||||||
|
|
@ -185,34 +185,18 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
||||||
theme!!.summary = getString(R.string.light)
|
theme!!.summary = getString(R.string.light)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
ThemeUtil.updateThemes()
|
||||||
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()
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
accent!!.summary = accent!!.entry
|
accent!!.summary = accent!!.entry
|
||||||
accent!!.onPreferenceChangeListener =
|
accent!!.onPreferenceChangeListener =
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
|
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
|
||||||
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
ThemeUtil.updateThemes()
|
||||||
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()
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
highContrast!!.onPreferenceChangeListener =
|
highContrast!!.onPreferenceChangeListener =
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
|
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
|
||||||
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
ThemeUtil.updateThemes()
|
||||||
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()
|
|
||||||
|
|
||||||
true
|
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.HistoryViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.ObserveSourcesViewModel
|
import com.deniscerri.ytdl.database.viewmodel.ObserveSourcesViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
||||||
|
import com.deniscerri.ytdl.util.FileUtil
|
||||||
import com.deniscerri.ytdl.util.UiUtil
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
import com.deniscerri.ytdl.util.UpdateUtil
|
import com.deniscerri.ytdl.util.UpdateUtil
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
|
|
@ -220,7 +221,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||||
saveFile.writeText(GsonBuilder().setPrettyPrinting().create().toJson(json))
|
saveFile.writeText(GsonBuilder().setPrettyPrinting().create().toJson(json))
|
||||||
val s = Snackbar.make(requireView(), getString(R.string.backup_created_successfully), Snackbar.LENGTH_LONG)
|
val s = Snackbar.make(requireView(), getString(R.string.backup_created_successfully), Snackbar.LENGTH_LONG)
|
||||||
s.setAction(R.string.Open_File){
|
s.setAction(R.string.Open_File){
|
||||||
UiUtil.openFileIntent(requireActivity(), saveFile.absolutePath)
|
FileUtil.openFileIntent(requireActivity(), saveFile.absolutePath)
|
||||||
}
|
}
|
||||||
s.show()
|
s.show()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,50 @@
|
||||||
package com.deniscerri.ytdl.util
|
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.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.media.MediaScannerConnection
|
import android.media.MediaScannerConnection
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
|
import android.util.DisplayMetrics
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.Window
|
||||||
import android.webkit.MimeTypeMap
|
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.net.toUri
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.work.impl.utils.PreferenceUtils
|
|
||||||
import com.anggrayudi.storage.callback.FileCallback
|
import com.anggrayudi.storage.callback.FileCallback
|
||||||
import com.anggrayudi.storage.callback.FolderCallback
|
import com.anggrayudi.storage.callback.FolderCallback
|
||||||
import com.anggrayudi.storage.file.copyFolderTo
|
import com.anggrayudi.storage.file.copyFolderTo
|
||||||
import com.anggrayudi.storage.file.getAbsolutePath
|
import com.anggrayudi.storage.file.getAbsolutePath
|
||||||
import com.anggrayudi.storage.file.moveFileTo
|
import com.anggrayudi.storage.file.moveFileTo
|
||||||
import com.deniscerri.ytdl.App
|
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 com.yausername.youtubedl_android.YoutubeDLRequest
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.internal.closeQuietly
|
import okhttp3.internal.closeQuietly
|
||||||
|
import okhttp3.internal.lowercase
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
import java.nio.file.StandardCopyOption
|
import java.nio.file.StandardCopyOption
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import java.text.DecimalFormatSymbols
|
import java.text.DecimalFormatSymbols
|
||||||
|
|
@ -327,4 +346,58 @@ object FileUtil {
|
||||||
return "${DecimalFormat("#,##0.#", symbols).format(s / 1024.0.pow(digitGroups.toDouble()))} ${units[digitGroups]}"
|
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 {
|
try {
|
||||||
val p = Pattern.compile("(^(https?)://(www.)?(music.)?youtu(.be)?)|(^(https?)://(www.)?piped.video)")
|
if (url.isYoutubeURL()) {
|
||||||
val m = p.matcher(url)
|
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{
|
}else{
|
||||||
throw Exception()
|
throw Exception()
|
||||||
}
|
}
|
||||||
|
|
@ -943,7 +952,7 @@ class InfoUtil(private val context: Context) {
|
||||||
try {
|
try {
|
||||||
val request = YoutubeDLRequest(url)
|
val request = YoutubeDLRequest(url)
|
||||||
request.addOption("--get-url")
|
request.addOption("--get-url")
|
||||||
request.addOption("--print", "%(chapters)s")
|
request.addOption("--print", "%(.{urls,chapters})s")
|
||||||
request.addOption("--skip-download")
|
request.addOption("--skip-download")
|
||||||
request.addOption("-R", "1")
|
request.addOption("-R", "1")
|
||||||
request.addOption("--socket-timeout", "5")
|
request.addOption("--socket-timeout", "5")
|
||||||
|
|
@ -964,8 +973,6 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val proxy = sharedPreferences.getString("proxy", "")
|
val proxy = sharedPreferences.getString("proxy", "")
|
||||||
if (proxy!!.isNotBlank()){
|
if (proxy!!.isNotBlank()){
|
||||||
request.addOption("--proxy", proxy)
|
request.addOption("--proxy", proxy)
|
||||||
|
|
@ -975,36 +982,41 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
|
|
||||||
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
|
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
|
||||||
val results: Array<String?> = try {
|
val json = JSONObject(youtubeDLResponse.out)
|
||||||
val lineSeparator = System.getProperty("line.separator")
|
val urls = if (json.has("urls")) {
|
||||||
youtubeDLResponse.out.split(lineSeparator!!).toTypedArray()
|
json.getString("urls").split("\n")
|
||||||
} catch (e: Exception) {
|
}else{
|
||||||
arrayOf(youtubeDLResponse.out)
|
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) {
|
} 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")
|
@SuppressLint("RestrictedApi")
|
||||||
fun buildYoutubeDLRequest(downloadItem: DownloadItem) : YoutubeDLRequest {
|
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)
|
YoutubeDLRequest(downloadItem.url)
|
||||||
}else{
|
}else{
|
||||||
YoutubeDLRequest(downloadItem.playlistURL!!).apply {
|
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 type = downloadItem.type
|
||||||
|
|
||||||
val downDir : File
|
val downDir : File
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import androidx.core.view.forEach
|
||||||
import androidx.core.view.get
|
import androidx.core.view.get
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
|
import com.deniscerri.ytdl.util.NavbarUtil.applyNavBarStyle
|
||||||
import com.google.android.material.navigation.NavigationBarView
|
import com.google.android.material.navigation.NavigationBarView
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -95,7 +96,7 @@ object NavbarUtil {
|
||||||
return navBarItems
|
return navBarItems
|
||||||
}
|
}
|
||||||
|
|
||||||
fun NavigationBarView.applyNavBarStyle(): Int {
|
fun NavigationBarView.setLabelVisibility() {
|
||||||
val labelVisibilityMode = when (
|
val labelVisibilityMode = when (
|
||||||
settings.getString("label_visibility", "always")
|
settings.getString("label_visibility", "always")
|
||||||
) {
|
) {
|
||||||
|
|
@ -105,6 +106,10 @@ object NavbarUtil {
|
||||||
else -> NavigationBarView.LABEL_VISIBILITY_AUTO
|
else -> NavigationBarView.LABEL_VISIBILITY_AUTO
|
||||||
}
|
}
|
||||||
this.labelVisibilityMode = labelVisibilityMode
|
this.labelVisibilityMode = labelVisibilityMode
|
||||||
|
}
|
||||||
|
|
||||||
|
fun NavigationBarView.applyNavBarStyle(): Int {
|
||||||
|
setLabelVisibility()
|
||||||
|
|
||||||
val navBarItems = getNavBarItems(this.context)
|
val navBarItems = getNavBarItems(this.context)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -546,7 +546,7 @@ class NotificationUtil(var context: Context) {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.setContentText("")
|
.setContentText("")
|
||||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
.setProgress(PROGRESS_MAX, PROGRESS_CURR, false)
|
.setProgress(PROGRESS_MAX, PROGRESS_CURR, false)
|
||||||
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
package com.deniscerri.ytdl.util
|
package com.deniscerri.ytdl.util
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Application
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Bundle
|
||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
|
|
@ -11,12 +14,50 @@ import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.text.HtmlCompat
|
import androidx.core.text.HtmlCompat
|
||||||
import androidx.core.text.parseAsHtml
|
import androidx.core.text.parseAsHtml
|
||||||
import androidx.preference.PreferenceManager
|
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.deniscerri.ytdl.R
|
||||||
import com.google.android.material.color.DynamicColors
|
import com.google.android.material.color.DynamicColors
|
||||||
|
|
||||||
|
|
||||||
object ThemeUtil {
|
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(
|
sealed class AppIcon(
|
||||||
@DrawableRes val iconResource: Int,
|
@DrawableRes val iconResource: Int,
|
||||||
val activityAlias: String
|
val activityAlias: String
|
||||||
|
|
@ -32,7 +73,18 @@ object ThemeUtil {
|
||||||
AppIcon.Dark
|
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)
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
|
|
||||||
//update accent
|
//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 ){
|
fun showDatePickerOnly(fragmentManager: FragmentManager , onSubmit : (chosenDate: Calendar) -> Unit ){
|
||||||
val date = Calendar.getInstance()
|
val date = Calendar.getInstance()
|
||||||
|
|
@ -816,7 +710,7 @@ object UiUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
shareFileIntent(context, item.downloadPath)
|
FileUtil.shareFileIntent(context, item.downloadPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -903,7 +797,7 @@ object UiUtil {
|
||||||
openFile!!.tag = item.id
|
openFile!!.tag = item.id
|
||||||
openFile.setOnClickListener{
|
openFile.setOnClickListener{
|
||||||
if (item.downloadPath.size == 1) {
|
if (item.downloadPath.size == 1) {
|
||||||
openFileIntent(context, item.downloadPath.first())
|
FileUtil.openFileIntent(context, item.downloadPath.first())
|
||||||
}else{
|
}else{
|
||||||
openMultipleFilesIntent(context, item.downloadPath)
|
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() {
|
object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent) {
|
override fun onReceive(context: Context?, intent: Intent) {
|
||||||
context?.unregisterReceiver(this)
|
context?.unregisterReceiver(this)
|
||||||
UiUtil.openFileIntent(context!!,
|
FileUtil.openFileIntent(context!!,
|
||||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)?.absolutePath +
|
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)?.absolutePath +
|
||||||
File.separator + releaseVersion.name)
|
File.separator + releaseVersion.name)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import com.deniscerri.ytdl.util.NotificationUtil
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
|
||||||
class UpdatePlaylistFormatsWorker(
|
class UpdateMultipleDownloadsFormatsWorker(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
workerParams: WorkerParameters
|
workerParams: WorkerParameters
|
||||||
) : Worker(context, workerParams) {
|
) : Worker(context, workerParams) {
|
||||||
|
|
@ -38,7 +38,7 @@ class UpdatePlaylistFormatsWorker(
|
||||||
ids.forEach {
|
ids.forEach {
|
||||||
if (!isStopped){
|
if (!isStopped){
|
||||||
val d = dao.getDownloadById(it)
|
val d = dao.getDownloadById(it)
|
||||||
val r = resDao.getResultByURL(d.url)!!
|
val r = resDao.getResultByURL(d.url)
|
||||||
|
|
||||||
if (d.allFormats.isNotEmpty()){
|
if (d.allFormats.isNotEmpty()){
|
||||||
count++
|
count++
|
||||||
|
|
@ -50,18 +50,18 @@ class UpdatePlaylistFormatsWorker(
|
||||||
d.allFormats.addAll(infoUtil.getFormats(d.url))
|
d.allFormats.addAll(infoUtil.getFormats(d.url))
|
||||||
d.format = vm.getFormat(d.allFormats,d.type)
|
d.format = vm.getFormat(d.allFormats,d.type)
|
||||||
|
|
||||||
r.formats.clear()
|
r?.formats?.clear()
|
||||||
r.formats.addAll(d.allFormats)
|
r?.formats?.addAll(d.allFormats)
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
resDao.update(r)
|
r?.apply { resDao.update(this) }
|
||||||
dao.update(d)
|
dao.update(d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
count++
|
count++
|
||||||
notificationUtil.updateFormatUpdateNotification(workID, UpdatePlaylistFormatsWorker::class.java.name, count, ids.size)
|
notificationUtil.updateFormatUpdateNotification(workID, UpdateMultipleDownloadsFormatsWorker::class.java.name, count, ids.size)
|
||||||
}else{
|
}else{
|
||||||
throw Exception()
|
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:useDefaultControls="true"
|
||||||
app:use_controller="false" />
|
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>
|
</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
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/cut_section"
|
android:id="@+id/cut_section"
|
||||||
|
|
@ -167,7 +174,6 @@
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
app:layout_constraintBottom_toTopOf="@id/suggested_cuts"
|
app:layout_constraintBottom_toTopOf="@id/suggested_cuts"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0.533"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/rangeSlider"
|
app:layout_constraintTop_toBottomOf="@+id/rangeSlider"
|
||||||
app:layout_constraintVertical_bias="0.0">
|
app:layout_constraintVertical_bias="0.0">
|
||||||
|
|
@ -177,6 +183,7 @@
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:minWidth="70dp"
|
||||||
android:hint="@string/start">
|
android:hint="@string/start">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
|
@ -203,6 +210,7 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/end"
|
android:hint="@string/end"
|
||||||
|
android:minWidth="70dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/colon">
|
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="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">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_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>
|
</resources>
|
||||||
|
|
@ -99,6 +99,17 @@
|
||||||
app:defaultValue=""
|
app:defaultValue=""
|
||||||
android:summary="@string/piped_instance_summary"
|
android:summary="@string/piped_instance_summary"
|
||||||
app:title="@string/piped_instance" />
|
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>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/misc">
|
<PreferenceCategory android:title="@string/misc">
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<paths>
|
<paths>
|
||||||
<external-path name="external_files" path="."/>
|
<external-path name="external_files" path="."/>
|
||||||
<root-path name="external_files" path="/storage/" />
|
<root-path name="external_files" path="." />
|
||||||
<cache-path
|
<cache-path
|
||||||
name="cache"
|
name="cache"
|
||||||
path="." />
|
path="." />
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue