crumbs
This commit is contained in:
parent
6a2e827d77
commit
888dabc352
22 changed files with 115 additions and 76 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel target="11" />
|
<bytecodeTargetLevel target="17" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="jbr-17" project-jdk-type="JavaSDK" />
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="corretto-17" project-jdk-type="JavaSDK" />
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -97,12 +97,12 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_11
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
targetCompatibility JavaVersion.VERSION_11
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = '11'
|
jvmTarget = '17'
|
||||||
}
|
}
|
||||||
|
|
||||||
splits {
|
splits {
|
||||||
|
|
|
||||||
6
app/proguard-rules.pro
vendored
6
app/proguard-rules.pro
vendored
|
|
@ -61,4 +61,8 @@
|
||||||
public <init>(android.content.Context,androidx.work.WorkerParameters);
|
public <init>(android.content.Context,androidx.work.WorkerParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
-dontobfuscate
|
-dontobfuscate
|
||||||
|
-dontwarn com.google.android.exoplayer2.**
|
||||||
|
# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
|
||||||
|
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
|
||||||
|
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
|
||||||
|
|
@ -12,6 +12,9 @@ interface ResultDao {
|
||||||
@Query("SELECT COUNT(id) FROM results")
|
@Query("SELECT COUNT(id) FROM results")
|
||||||
fun getCount() : LiveData<Int>
|
fun getCount() : LiveData<Int>
|
||||||
|
|
||||||
|
@Query("SELECT COUNT(id) FROM results")
|
||||||
|
fun getCountInt() :Int
|
||||||
|
|
||||||
@Query("SELECT * FROM results LIMIT 1")
|
@Query("SELECT * FROM results LIMIT 1")
|
||||||
fun getFirstResult() : ResultItem
|
fun getFirstResult() : ResultItem
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
||||||
import com.google.android.exoplayer2.source.MediaSource
|
import com.google.android.exoplayer2.source.MediaSource
|
||||||
import com.google.android.exoplayer2.source.MergingMediaSource
|
import com.google.android.exoplayer2.source.MergingMediaSource
|
||||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
||||||
import com.google.android.exoplayer2.ui.PlayerView
|
import com.google.android.exoplayer2.ui.StyledPlayerView
|
||||||
import com.google.android.exoplayer2.util.MimeTypes
|
import com.google.android.exoplayer2.util.MimeTypes
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
|
@ -96,7 +96,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi", "SetTextI18n")
|
||||||
override fun setupDialog(dialog: Dialog, style: Int) {
|
override fun setupDialog(dialog: Dialog, style: Int) {
|
||||||
super.setupDialog(dialog, style)
|
super.setupDialog(dialog, style)
|
||||||
val view = LayoutInflater.from(context).inflate(R.layout.cut_video_sheet, null)
|
val view = LayoutInflater.from(context).inflate(R.layout.cut_video_sheet, null)
|
||||||
|
|
@ -106,14 +106,14 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
behavior = BottomSheetBehavior.from(view.parent as View)
|
behavior = BottomSheetBehavior.from(view.parent as View)
|
||||||
val displayMetrics = DisplayMetrics()
|
val displayMetrics = DisplayMetrics()
|
||||||
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||||
behavior.peekHeight = displayMetrics.heightPixels / 2
|
behavior.peekHeight = displayMetrics.heightPixels
|
||||||
}
|
}
|
||||||
|
|
||||||
val renderersFactory = DefaultRenderersFactory(requireContext().applicationContext)
|
val renderersFactory = DefaultRenderersFactory(requireContext().applicationContext)
|
||||||
.setEnableDecoderFallback(true)
|
.setEnableDecoderFallback(true)
|
||||||
.setExtensionRendererMode(EXTENSION_RENDERER_MODE_PREFER)
|
.setExtensionRendererMode(EXTENSION_RENDERER_MODE_PREFER)
|
||||||
.setMediaCodecSelector { mimeType, requiresSecureDecoder, requiresTunnelingDecoder ->
|
.setMediaCodecSelector { mimeType, requiresSecureDecoder, requiresTunnelingDecoder ->
|
||||||
var decoderInfos: MutableList<MediaCodecInfo> =
|
var decoderInfo: MutableList<MediaCodecInfo> =
|
||||||
MediaCodecSelector.DEFAULT
|
MediaCodecSelector.DEFAULT
|
||||||
.getDecoderInfos(
|
.getDecoderInfos(
|
||||||
mimeType,
|
mimeType,
|
||||||
|
|
@ -122,13 +122,13 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
)
|
)
|
||||||
if (MimeTypes.VIDEO_H264 == mimeType) {
|
if (MimeTypes.VIDEO_H264 == mimeType) {
|
||||||
// copy the list because MediaCodecSelector.DEFAULT returns an unmodifiable list
|
// copy the list because MediaCodecSelector.DEFAULT returns an unmodifiable list
|
||||||
decoderInfos = ArrayList(decoderInfos)
|
decoderInfo = ArrayList(decoderInfo)
|
||||||
decoderInfos.reverse()
|
decoderInfo.reverse()
|
||||||
}
|
}
|
||||||
decoderInfos
|
decoderInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
val trackSelector = DefaultTrackSelector()
|
val trackSelector = DefaultTrackSelector(requireContext())
|
||||||
val loadControl = DefaultLoadControl()
|
val loadControl = DefaultLoadControl()
|
||||||
|
|
||||||
player = ExoPlayer.Builder(requireContext(), renderersFactory)
|
player = ExoPlayer.Builder(requireContext(), renderersFactory)
|
||||||
|
|
@ -136,7 +136,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
.setLoadControl(loadControl)
|
.setLoadControl(loadControl)
|
||||||
.build()
|
.build()
|
||||||
val frame = view.findViewById<MaterialCardView>(R.id.frame_layout)
|
val frame = view.findViewById<MaterialCardView>(R.id.frame_layout)
|
||||||
val videoView = view.findViewById<PlayerView>(R.id.video_view)
|
val videoView = view.findViewById<StyledPlayerView>(R.id.video_view)
|
||||||
videoView.player = player
|
videoView.player = player
|
||||||
timeSeconds = convertStringToTimestamp(item.duration)
|
timeSeconds = convertStringToTimestamp(item.duration)
|
||||||
chapters = emptyList()
|
chapters = emptyList()
|
||||||
|
|
@ -207,9 +207,9 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
progress.visibility = View.GONE
|
progress.visibility = View.GONE
|
||||||
populateSuggestedChapters()
|
populateSuggestedChapters()
|
||||||
|
|
||||||
player.prepare()
|
player.prepare().apply {
|
||||||
player.seekTo((((rangeSlider.valueFrom.toInt() * timeSeconds) / 100) * 1000).toLong())
|
player.play()
|
||||||
player.play()
|
}
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
progress.visibility = View.GONE
|
progress.visibility = View.GONE
|
||||||
frame.visibility = View.GONE
|
frame.visibility = View.GONE
|
||||||
|
|
@ -253,11 +253,12 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
private fun initCutSection(){
|
private fun initCutSection(){
|
||||||
fromTextInput.editText!!.setText("0:00")
|
fromTextInput.editText!!.setText("0:00")
|
||||||
toTextInput.editText!!.setText(item.duration)
|
toTextInput.editText!!.setText(item.duration)
|
||||||
|
|
||||||
rangeSlider.addOnChangeListener { rangeSlider, value, fromUser ->
|
rangeSlider.addOnChangeListener { rangeSlider, _, _ ->
|
||||||
val values = rangeSlider.values
|
val values = rangeSlider.values
|
||||||
val startTimestamp = (values[0].toInt() * timeSeconds) / 100
|
val startTimestamp = (values[0].toInt() * timeSeconds) / 100
|
||||||
val endTimestamp = (values[1].toInt() * timeSeconds) / 100
|
val endTimestamp = (values[1].toInt() * timeSeconds) / 100
|
||||||
|
|
@ -304,9 +305,9 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
val startValueTimeStampSeconds = (startValue.toInt() * timeSeconds) / 100
|
val startValueTimeStampSeconds = (startValue.toInt() * timeSeconds) / 100
|
||||||
fromTextInput.editText!!.setText(infoUtil.formatIntegerDuration(startValueTimeStampSeconds, Locale.US))
|
fromTextInput.editText!!.setText(infoUtil.formatIntegerDuration(startValueTimeStampSeconds, Locale.US))
|
||||||
|
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -336,9 +337,9 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
val endValueTimeStampSeconds = (endValue.toInt() * timeSeconds) / 100
|
val endValueTimeStampSeconds = (endValue.toInt() * timeSeconds) / 100
|
||||||
toTextInput.editText!!.setText(infoUtil.formatIntegerDuration(endValueTimeStampSeconds, Locale.US))
|
toTextInput.editText!!.setText(infoUtil.formatIntegerDuration(endValueTimeStampSeconds, Locale.US))
|
||||||
|
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -409,7 +410,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
|
|
||||||
if (item.downloadSections.isNotBlank()){
|
if (item.downloadSections.isNotBlank()){
|
||||||
chipGroup.removeAllViews()
|
chipGroup.removeAllViews()
|
||||||
item.downloadSections.split(";").forEachIndexed { index, it ->
|
item.downloadSections.split(";").forEachIndexed { _, it ->
|
||||||
if (it.isBlank()) return
|
if (it.isBlank()) return
|
||||||
if (it.contains(":")) createChip(it.replace(";", ""))
|
if (it.contains(":")) createChip(it.replace(";", ""))
|
||||||
else createChapterChip(ChapterItem(0, 0, it), null)
|
else createChapterChip(ChapterItem(0, 0, it), null)
|
||||||
|
|
@ -436,7 +437,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
if (chip.isChecked) {
|
if (chip.isChecked) {
|
||||||
rangeSlider.setValues(startingValue.toFloat(), endingValue.toFloat())
|
rangeSlider.setValues(startingValue.toFloat(), endingValue.toFloat())
|
||||||
player.prepare()
|
player.prepare()
|
||||||
player.seekTo((((startingValue * timeSeconds) / 100) * 1000).toLong())
|
player.seekTo((startTimestamp * 1000).toLong())
|
||||||
player.play()
|
player.play()
|
||||||
}else {
|
}else {
|
||||||
player.seekTo(0)
|
player.seekTo(0)
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
|
||||||
behavior = BottomSheetBehavior.from(view.parent as View)
|
behavior = BottomSheetBehavior.from(view.parent as View)
|
||||||
val displayMetrics = DisplayMetrics()
|
val displayMetrics = DisplayMetrics()
|
||||||
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||||
behavior.peekHeight = displayMetrics.heightPixels
|
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,12 +69,6 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
|
||||||
uiUtil = UiUtil(fileUtil)
|
uiUtil = UiUtil(fileUtil)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
val behavior = BottomSheetBehavior.from(view.parent as View)
|
|
||||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi", "NotifyDataSetChanged")
|
@SuppressLint("RestrictedApi", "NotifyDataSetChanged")
|
||||||
override fun setupDialog(dialog: Dialog, style: Int) {
|
override fun setupDialog(dialog: Dialog, style: Int) {
|
||||||
super.setupDialog(dialog, style)
|
super.setupDialog(dialog, style)
|
||||||
|
|
@ -85,7 +79,7 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
|
||||||
behavior = BottomSheetBehavior.from(view.parent as View)
|
behavior = BottomSheetBehavior.from(view.parent as View)
|
||||||
val displayMetrics = DisplayMetrics()
|
val displayMetrics = DisplayMetrics()
|
||||||
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||||
behavior.peekHeight = displayMetrics.heightPixels / 2
|
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
}
|
}
|
||||||
|
|
||||||
listAdapter =
|
listAdapter =
|
||||||
|
|
|
||||||
|
|
@ -178,8 +178,10 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
|
||||||
containers
|
containers
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
downloadItem.format.container = containerPreference!!
|
downloadItem.format.container = if (containerPreference == getString(R.string.defaultValue)) "" else containerPreference!!
|
||||||
containerAutoCompleteTextView!!.setText(downloadItem.format.container, false)
|
containerAutoCompleteTextView!!.setText(
|
||||||
|
downloadItem.format.container.ifEmpty { getString(R.string.defaultValue) },
|
||||||
|
false)
|
||||||
|
|
||||||
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
|
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
|
||||||
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
|
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
|
||||||
|
|
|
||||||
|
|
@ -45,12 +45,6 @@ class SelectPlaylistItemsBottomSheetDialog(private val items: List<ResultItem?>,
|
||||||
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
|
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
val behavior = BottomSheetBehavior.from(view.parent as View)
|
|
||||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override fun setupDialog(dialog: Dialog, style: Int) {
|
override fun setupDialog(dialog: Dialog, style: Int) {
|
||||||
super.setupDialog(dialog, style)
|
super.setupDialog(dialog, style)
|
||||||
|
|
@ -61,7 +55,7 @@ class SelectPlaylistItemsBottomSheetDialog(private val items: List<ResultItem?>,
|
||||||
behavior = BottomSheetBehavior.from(view.parent as View)
|
behavior = BottomSheetBehavior.from(view.parent as View)
|
||||||
val displayMetrics = DisplayMetrics()
|
val displayMetrics = DisplayMetrics()
|
||||||
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||||
behavior.peekHeight = displayMetrics.heightPixels / 2
|
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
}
|
}
|
||||||
|
|
||||||
listAdapter =
|
listAdapter =
|
||||||
|
|
|
||||||
|
|
@ -358,6 +358,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
//val websites = historyRecyclerViewAdapter!!.websites
|
//val websites = historyRecyclerViewAdapter!!.websites
|
||||||
for (i in websites.indices) {
|
for (i in websites.indices) {
|
||||||
val w = websites[i]
|
val w = websites[i]
|
||||||
|
if (w == "null" || w.isEmpty()) continue
|
||||||
val tmp = layoutinflater!!.inflate(R.layout.filter_chip, websiteGroup, false) as Chip
|
val tmp = layoutinflater!!.inflate(R.layout.filter_chip, websiteGroup, false) as Chip
|
||||||
tmp.text = w
|
tmp.text = w
|
||||||
tmp.id = i
|
tmp.id = i
|
||||||
|
|
|
||||||
|
|
@ -256,6 +256,13 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
WorkManager.getInstance(requireContext()).cancelUniqueWork(id.toString())
|
WorkManager.getInstance(requireContext()).cancelUniqueWork(id.toString())
|
||||||
notificationUtil.cancelDownloadNotification(id)
|
notificationUtil.cancelDownloadNotification(id)
|
||||||
downloadViewModel.deleteDownload(item!!)
|
downloadViewModel.deleteDownload(item!!)
|
||||||
|
|
||||||
|
Snackbar.make(queuedRecyclerView, getString(R.string.cancelled) + ": " + item.title, Snackbar.LENGTH_LONG)
|
||||||
|
.setAction(getString(R.string.undo)) {
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
downloadViewModel.queueDownloads(listOf(item))
|
||||||
|
}
|
||||||
|
}.show()
|
||||||
}
|
}
|
||||||
deleteDialog.show()
|
deleteDialog.show()
|
||||||
}
|
}
|
||||||
|
|
@ -273,12 +280,6 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
ItemTouchHelper.LEFT -> {
|
ItemTouchHelper.LEFT -> {
|
||||||
val deletedItem = items[position]
|
val deletedItem = items[position]
|
||||||
removeItem(deletedItem.id)
|
removeItem(deletedItem.id)
|
||||||
Snackbar.make(queuedRecyclerView, getString(R.string.cancelled) + ": " + deletedItem.title, Snackbar.LENGTH_LONG)
|
|
||||||
.setAction(getString(R.string.undo)) {
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
downloadViewModel.queueDownloads(listOf(deletedItem))
|
|
||||||
}
|
|
||||||
}.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,11 @@ import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
|
||||||
import com.deniscerri.ytdlnis.database.models.Format
|
import com.deniscerri.ytdlnis.database.models.Format
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.JsonArray
|
|
||||||
import com.google.gson.JsonObject
|
|
||||||
import com.google.gson.reflect.TypeToken
|
|
||||||
import com.yausername.youtubedl_android.YoutubeDL
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
import com.yausername.youtubedl_android.YoutubeDLRequest
|
import com.yausername.youtubedl_android.YoutubeDLRequest
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
@ -137,7 +132,7 @@ class InfoUtil(private val context: Context) {
|
||||||
"thumb",
|
"thumb",
|
||||||
element.getJSONArray("videoThumbnails").getJSONObject(0).getString("url")
|
element.getJSONArray("videoThumbnails").getJSONObject(0).getString("url")
|
||||||
)
|
)
|
||||||
val v = createVideofromInvidiousJSON(element)
|
val v = createVideoFromInvidiousJSON(element)
|
||||||
if (v == null || v.thumb.isEmpty()) {
|
if (v == null || v.thumb.isEmpty()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -225,7 +220,7 @@ class InfoUtil(private val context: Context) {
|
||||||
val vids = res.getJSONArray("videos")
|
val vids = res.getJSONArray("videos")
|
||||||
for (i in 0 until vids.length()) {
|
for (i in 0 until vids.length()) {
|
||||||
val element = vids.getJSONObject(i)
|
val element = vids.getJSONObject(i)
|
||||||
val v = createVideofromInvidiousJSON(element)
|
val v = createVideoFromInvidiousJSON(element)
|
||||||
if (v == null || v.thumb.isEmpty()) continue
|
if (v == null || v.thumb.isEmpty()) continue
|
||||||
v.playlistTitle = res.getString("title")
|
v.playlistTitle = res.getString("title")
|
||||||
items.add(v)
|
items.add(v)
|
||||||
|
|
@ -243,7 +238,7 @@ class InfoUtil(private val context: Context) {
|
||||||
if (key!!.isEmpty()) {
|
if (key!!.isEmpty()) {
|
||||||
return if (useInvidous) {
|
return if (useInvidous) {
|
||||||
val res = genericRequest(invidousURL + "videos/" + id)
|
val res = genericRequest(invidousURL + "videos/" + id)
|
||||||
if (res.length() == 0) getFromYTDL("https://www.youtube.com/watch?v=$id")[0] else createVideofromInvidiousJSON(
|
if (res.length() == 0) getFromYTDL("https://www.youtube.com/watch?v=$id")[0] else createVideoFromInvidiousJSON(
|
||||||
res
|
res
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -293,7 +288,7 @@ class InfoUtil(private val context: Context) {
|
||||||
return video
|
return video
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createVideofromInvidiousJSON(obj: JSONObject): ResultItem? {
|
private fun createVideoFromInvidiousJSON(obj: JSONObject): ResultItem? {
|
||||||
var video: ResultItem? = null
|
var video: ResultItem? = null
|
||||||
try {
|
try {
|
||||||
val id = obj.getString("videoId")
|
val id = obj.getString("videoId")
|
||||||
|
|
@ -476,7 +471,7 @@ class InfoUtil(private val context: Context) {
|
||||||
for (i in 0 until res.length()) {
|
for (i in 0 until res.length()) {
|
||||||
val element = res.getJSONObject(i)
|
val element = res.getJSONObject(i)
|
||||||
if (element.getString("type") != "video") continue
|
if (element.getString("type") != "video") continue
|
||||||
val v = createVideofromInvidiousJSON(element)
|
val v = createVideoFromInvidiousJSON(element)
|
||||||
if (v == null || v.thumb.isEmpty()) continue
|
if (v == null || v.thumb.isEmpty()) continue
|
||||||
v.playlistTitle = context.getString(R.string.trendingPlaylist)
|
v.playlistTitle = context.getString(R.string.trendingPlaylist)
|
||||||
items.add(v)
|
items.add(v)
|
||||||
|
|
@ -513,7 +508,7 @@ class InfoUtil(private val context: Context) {
|
||||||
val id = getIDFromYoutubeURL(url)
|
val id = getIDFromYoutubeURL(url)
|
||||||
val res = genericRequest(invidousURL + "videos/" + id)
|
val res = genericRequest(invidousURL + "videos/" + id)
|
||||||
if (res.length() == 0) getFromYTDL(url)[0]!!
|
if (res.length() == 0) getFromYTDL(url)[0]!!
|
||||||
else createVideofromInvidiousJSON(
|
else createVideoFromInvidiousJSON(
|
||||||
res
|
res
|
||||||
)!!
|
)!!
|
||||||
}else{
|
}else{
|
||||||
|
|
@ -691,8 +686,29 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
|
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
|
||||||
val jsonObject = JSONObject(youtubeDLResponse.out)
|
val jsonObject = JSONObject(youtubeDLResponse.out)
|
||||||
val thumbs = jsonObject.getJSONArray("thumbnails")
|
|
||||||
val thumb = thumbs.getJSONObject(thumbs.length() - 1).getString("url")
|
var author: String? = ""
|
||||||
|
if (jsonObject.has("uploader")) {
|
||||||
|
author = jsonObject.getString("uploader")
|
||||||
|
}
|
||||||
|
|
||||||
|
var duration = ""
|
||||||
|
runCatching {
|
||||||
|
if (jsonObject.has("duration")) {
|
||||||
|
duration = formatIntegerDuration(jsonObject.getInt("duration"), Locale.getDefault())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var thumb: String? = ""
|
||||||
|
if (jsonObject.has("thumbnail")) {
|
||||||
|
thumb = jsonObject.getString("thumbnail")
|
||||||
|
} else if (jsonObject.has("thumbnails")) {
|
||||||
|
val thumbs = jsonObject.getJSONArray("thumbnails")
|
||||||
|
if (thumbs.length() > 0){
|
||||||
|
thumb = thumbs.getJSONObject(thumbs.length() - 1).getString("url")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val isPlaylist = jsonObject.has("playlist_count")
|
val isPlaylist = jsonObject.has("playlist_count")
|
||||||
return ResultItem(
|
return ResultItem(
|
||||||
0,
|
0,
|
||||||
|
|
@ -702,9 +718,9 @@ class InfoUtil(private val context: Context) {
|
||||||
}else{
|
}else{
|
||||||
jsonObject.getString("title")
|
jsonObject.getString("title")
|
||||||
},
|
},
|
||||||
jsonObject.getString("uploader"),
|
author!!,
|
||||||
if (jsonObject.has("duration")) formatIntegerDuration(jsonObject.getInt("duration"), Locale.US) else "",
|
duration,
|
||||||
thumb,
|
thumb!!,
|
||||||
jsonObject.getString("extractor"),
|
jsonObject.getString("extractor"),
|
||||||
if (isPlaylist) jsonObject.getString("title") else "",
|
if (isPlaylist) jsonObject.getString("title") else "",
|
||||||
arrayListOf(),
|
arrayListOf(),
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
|
import android.util.DisplayMetrics
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
|
@ -28,6 +29,7 @@ import com.deniscerri.ytdlnis.database.models.CommandTemplate
|
||||||
import com.deniscerri.ytdlnis.database.models.Format
|
import com.deniscerri.ytdlnis.database.models.Format
|
||||||
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
|
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
import com.google.android.material.chip.ChipGroup
|
import com.google.android.material.chip.ChipGroup
|
||||||
|
|
@ -154,6 +156,7 @@ class UiUtil(private val fileUtil: FileUtil) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bottomSheet.show()
|
bottomSheet.show()
|
||||||
|
bottomSheet.behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
bottomSheet.window!!.setLayout(
|
bottomSheet.window!!.setLayout(
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT
|
ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
|
@ -195,6 +198,7 @@ class UiUtil(private val fileUtil: FileUtil) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bottomSheet.show()
|
bottomSheet.show()
|
||||||
|
bottomSheet.behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
bottomSheet.window!!.setLayout(
|
bottomSheet.window!!.setLayout(
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT
|
ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ import com.deniscerri.ytdlnis.util.NotificationUtil
|
||||||
import com.yausername.youtubedl_android.YoutubeDL
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
import com.yausername.youtubedl_android.YoutubeDLRequest
|
import com.yausername.youtubedl_android.YoutubeDLRequest
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
@ -45,6 +47,7 @@ class DownloadWorker(
|
||||||
val dao = dbManager.downloadDao
|
val dao = dbManager.downloadDao
|
||||||
val repository = DownloadRepository(dao)
|
val repository = DownloadRepository(dao)
|
||||||
val historyDao = dbManager.historyDao
|
val historyDao = dbManager.historyDao
|
||||||
|
val resultDao = dbManager.resultDao
|
||||||
val handler = Handler(Looper.getMainLooper())
|
val handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -152,7 +155,7 @@ class DownloadWorker(
|
||||||
else if (audioQualityId == context.getString(R.string.worst_quality)) audioQualityId = "worstaudio"
|
else if (audioQualityId == context.getString(R.string.worst_quality)) audioQualityId = "worstaudio"
|
||||||
|
|
||||||
val ext = downloadItem.format.container
|
val ext = downloadItem.format.container
|
||||||
if (audioQualityId.isBlank() || ext == "Default" || ext == context.getString(R.string.defaultValue)) request.addOption("-x")
|
if (audioQualityId.isBlank()) request.addOption("-x")
|
||||||
else request.addOption("-f", audioQualityId)
|
else request.addOption("-f", audioQualityId)
|
||||||
|
|
||||||
if(ext != "webm"){
|
if(ext != "webm"){
|
||||||
|
|
@ -224,7 +227,7 @@ class DownloadWorker(
|
||||||
Log.e(TAG, formatArgument)
|
Log.e(TAG, formatArgument)
|
||||||
request.addOption("-f", formatArgument)
|
request.addOption("-f", formatArgument)
|
||||||
val format = downloadItem.format.container
|
val format = downloadItem.format.container
|
||||||
if(format.isNotEmpty() && format != "Default" && format != context.getString(R.string.defaultValue)){
|
if(format.isNotEmpty()){
|
||||||
request.addOption("--merge-output-format", format)
|
request.addOption("--merge-output-format", format)
|
||||||
if (format != "webm") {
|
if (format != "webm") {
|
||||||
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
|
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
|
||||||
|
|
@ -267,7 +270,7 @@ class DownloadWorker(
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var wasQuickDownloaded = false
|
||||||
//update item if its incomplete
|
//update item if its incomplete
|
||||||
if (downloadItem.title.isEmpty() || downloadItem.author.isEmpty() || downloadItem.thumb.isEmpty()){
|
if (downloadItem.title.isEmpty() || downloadItem.author.isEmpty() || downloadItem.thumb.isEmpty()){
|
||||||
runCatching {
|
runCatching {
|
||||||
|
|
@ -275,10 +278,11 @@ class DownloadWorker(
|
||||||
val info = infoUtil.getMissingInfo(downloadItem.url)
|
val info = infoUtil.getMissingInfo(downloadItem.url)
|
||||||
if (downloadItem.title.isEmpty()) downloadItem.title = info?.title.toString()
|
if (downloadItem.title.isEmpty()) downloadItem.title = info?.title.toString()
|
||||||
if (downloadItem.author.isEmpty()) downloadItem.author = info?.author.toString()
|
if (downloadItem.author.isEmpty()) downloadItem.author = info?.author.toString()
|
||||||
if (downloadItem.thumb.isEmpty()) downloadItem.thumb = info?.thumb.toString()
|
|
||||||
downloadItem.duration = info?.duration.toString()
|
downloadItem.duration = info?.duration.toString()
|
||||||
downloadItem.website = info?.website.toString()
|
downloadItem.website = info?.website.toString()
|
||||||
|
if (downloadItem.thumb.isEmpty()) downloadItem.thumb = info?.thumb.toString()
|
||||||
runBlocking {
|
runBlocking {
|
||||||
|
wasQuickDownloaded = resultDao.getCountInt() == 0
|
||||||
dao.update(downloadItem)
|
dao.update(downloadItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -345,6 +349,19 @@ class DownloadWorker(
|
||||||
NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID
|
NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (wasQuickDownloaded){
|
||||||
|
runCatching {
|
||||||
|
setProgressAsync(workDataOf("progress" to 100, "output" to "Creating Result Items", "id" to downloadItem.id, "log" to false))
|
||||||
|
runBlocking {
|
||||||
|
infoUtil.getFromYTDL(downloadItem.url).forEach { res ->
|
||||||
|
if (res != null) {
|
||||||
|
resultDao.insert(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
dao.delete(downloadItem.id)
|
dao.delete(downloadItem.id)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ class TerminalDownloadWorker(
|
||||||
request.addOption("--cookies", cookiesFile.absolutePath)
|
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!command.contains("-P ")) request.addOption("-P", tempFileDir.absolutePath)
|
request.addOption("-P", tempFileDir.absolutePath)
|
||||||
|
|
||||||
val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false)
|
val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false)
|
||||||
val logFolder = File(context.filesDir.absolutePath + "/logs")
|
val logFolder = File(context.filesDir.absolutePath + "/logs")
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" >
|
app:layout_constraintTop_toTopOf="parent" >
|
||||||
|
|
||||||
<com.google.android.exoplayer2.ui.PlayerView
|
<com.google.android.exoplayer2.ui.StyledPlayerView
|
||||||
android:id="@+id/video_view"
|
android:id="@+id/video_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|
|
||||||
|
|
@ -15,5 +15,5 @@
|
||||||
android:id="@+id/folder"
|
android:id="@+id/folder"
|
||||||
android:title="@string/command_directory"
|
android:title="@string/command_directory"
|
||||||
android:icon="@drawable/baseline_folder_24"
|
android:icon="@drawable/baseline_folder_24"
|
||||||
app:showAsAction="collapseActionView|ifRoom"/>
|
app:showAsAction="ifRoom"/>
|
||||||
</menu>
|
</menu>
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="audio_containers_values">
|
<string-array name="audio_containers_values">
|
||||||
<item>Default</item>
|
<item />
|
||||||
<item>mp3</item>
|
<item>mp3</item>
|
||||||
<item>m4a</item>
|
<item>m4a</item>
|
||||||
<item>aac</item>
|
<item>aac</item>
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="video_containers_values">
|
<string-array name="video_containers_values">
|
||||||
<item>Default</item>
|
<item />
|
||||||
<item>mp4</item>
|
<item>mp4</item>
|
||||||
<item>mkv</item>
|
<item>mkv</item>
|
||||||
<item>webm</item>
|
<item>webm</item>
|
||||||
|
|
@ -96,6 +96,7 @@
|
||||||
<item>tr</item>
|
<item>tr</item>
|
||||||
<item>uk</item>
|
<item>uk</item>
|
||||||
<item>vi</item>
|
<item>vi</item>
|
||||||
|
<item>zh</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
|
||||||
<style name="BaseTheme" parent="Theme.Material3.Light.NoActionBar">
|
<style name="BaseTheme" parent="Theme.Material3.Light.NoActionBar">
|
||||||
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
|
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
|
||||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
|
||||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||||
<item name="windowActionModeOverlay">true</item>
|
|
||||||
<item name="alertDialogTheme">@style/ThemeOverlay.Material3.MaterialAlertDialog</item>
|
<item name="alertDialogTheme">@style/ThemeOverlay.Material3.MaterialAlertDialog</item>
|
||||||
<item name="dialogCornerRadius">28dp</item>
|
<item name="dialogCornerRadius">28dp</item>
|
||||||
<item name="switchPreferenceCompatStyle">@style/Preference.SwitchPreferenceCompat.Material3</item>
|
<item name="switchPreferenceCompatStyle">@style/Preference.SwitchPreferenceCompat.Material3</item>
|
||||||
|
|
|
||||||
|
|
@ -18,4 +18,5 @@
|
||||||
<locale android:name="tr-TR"/>
|
<locale android:name="tr-TR"/>
|
||||||
<locale android:name="uk"/>
|
<locale android:name="uk"/>
|
||||||
<locale android:name="vi"/>
|
<locale android:name="vi"/>
|
||||||
|
<locale android:name="zh-CN"/>
|
||||||
</locale-config>
|
</locale-config>
|
||||||
|
|
@ -6,7 +6,10 @@
|
||||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
# Specifies the JVM arguments used for the daemon process.
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
android.defaults.buildfeatures.buildconfig=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
|
android.nonFinalResIds=false
|
||||||
|
android.nonTransitiveRClass=false
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue