This commit is contained in:
deniscerri 2023-04-29 17:57:07 +02:00
parent 794f591652
commit 98c1e20d51
No known key found for this signature in database
GPG key ID: 95C43D517D830350
17 changed files with 121 additions and 54 deletions

View file

@ -77,7 +77,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
if (title.length > 100) { if (title.length > 100) {
title = title.substring(0, 40) + "..." title = title.substring(0, 40) + "..."
} }
itemTitle.text = title.ifEmpty { "`${activity.getString(R.string.defaultValue)}`" } itemTitle.text = title.ifEmpty { "<${activity.getString(R.string.defaultValue)}>" }
// Author ---------------------------------- // Author ----------------------------------
val author = card.findViewById<TextView>(R.id.author) val author = card.findViewById<TextView>(R.id.author)
@ -86,7 +86,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
if (item.author.isNotEmpty()) info += "" if (item.author.isNotEmpty()) info += ""
info += item.duration info += item.duration
} }
author.text = info.ifEmpty { "`${activity.getString(R.string.defaultValue)}`" } author.text = info.ifEmpty { "<${activity.getString(R.string.defaultValue)}>" }
val type = card.findViewById<MaterialButton>(R.id.download_type) val type = card.findViewById<MaterialButton>(R.id.download_type)
when(item.type){ when(item.type){

View file

@ -73,7 +73,7 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
if (title.length > 100) { if (title.length > 100) {
title = title.substring(0, 40) + "..." title = title.substring(0, 40) + "..."
} }
itemTitle.text = title.ifEmpty { "`${activity.getString(R.string.defaultValue)}`" } itemTitle.text = title.ifEmpty { "<${activity.getString(R.string.defaultValue)}>" }
val formatNote = card.findViewById<TextView>(R.id.format_note) val formatNote = card.findViewById<TextView>(R.id.format_note)
if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility = if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility =

View file

@ -20,7 +20,7 @@ data class DownloadItem(
var downloadSections: String, var downloadSections: String,
val allFormats: ArrayList<Format>, val allFormats: ArrayList<Format>,
var downloadPath: String, var downloadPath: String,
val website: String, var website: String,
val downloadSize: String, val downloadSize: String,
val playlistTitle: String, val playlistTitle: String,
val audioPreferences : AudioPreferences, val audioPreferences : AudioPreferences,

View file

@ -193,6 +193,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
if (arguments?.getString("url") != null){ if (arguments?.getString("url") != null){
val url = requireArguments().getString("url") val url = requireArguments().getString("url")
arguments?.remove("url")
if (inputQueries == null) inputQueries = mutableListOf() if (inputQueries == null) inputQueries = mutableListOf()
searchBar?.text = url searchBar?.text = url
inputQueries!!.add(url!!) inputQueries!!.add(url!!)
@ -202,6 +203,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
resultViewModel.deleteAll() resultViewModel.deleteAll()
lifecycleScope.launch(Dispatchers.IO){ lifecycleScope.launch(Dispatchers.IO){
resultViewModel.parseQueries(inputQueries!!) resultViewModel.parseQueries(inputQueries!!)
inputQueries = null
} }
} }
} }

View file

@ -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 - 700 behavior.peekHeight = displayMetrics.heightPixels
} }
@ -159,9 +159,9 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
scheduleBtn.setOnClickListener{ scheduleBtn.setOnClickListener{
scheduleBtn.isEnabled = false
download.isEnabled = false
uiUtil.showDatePicker(fragmentManager) { uiUtil.showDatePicker(fragmentManager) {
scheduleBtn.isEnabled = false
download.isEnabled = false
val item: DownloadItem = getDownloadItem(); val item: DownloadItem = getDownloadItem();
item.downloadStartTime = it.timeInMillis item.downloadStartTime = it.timeInMillis
runBlocking { runBlocking {
@ -197,13 +197,13 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
(updateItem.parent as LinearLayout).visibility = View.VISIBLE (updateItem.parent as LinearLayout).visibility = View.VISIBLE
updateItem.setOnClickListener { updateItem.setOnClickListener {
if (activity is ShareActivity) { if (activity is ShareActivity) {
dismiss()
val intent = Intent(context, ShareActivity::class.java) val intent = Intent(context, ShareActivity::class.java)
intent.action = Intent.ACTION_SEND intent.action = Intent.ACTION_SEND
intent.type = "text/plain" intent.type = "text/plain"
intent.putExtra(Intent.EXTRA_TEXT, resultItem.url) intent.putExtra(Intent.EXTRA_TEXT, resultItem.url)
intent.putExtra("quick_download", false) intent.putExtra("quick_download", false)
startActivity(intent) startActivity(intent)
dismiss()
}else{ }else{
dismiss() dismiss()
val bundle = Bundle() val bundle = Bundle()

View file

@ -106,9 +106,9 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
scheduleBtn.setOnClickListener{ scheduleBtn.setOnClickListener{
scheduleBtn.isEnabled = false
download.isEnabled = false
uiUtil.showDatePicker(parentFragmentManager) { uiUtil.showDatePicker(parentFragmentManager) {
scheduleBtn.isEnabled = false
download.isEnabled = false
items.forEach { item -> items.forEach { item ->
item.downloadStartTime = it.timeInMillis item.downloadStartTime = it.timeInMillis
} }

View file

@ -88,13 +88,13 @@ class SelectPlaylistItemsBottomSheetDialog(private val items: List<ResultItem?>,
val end = toTextInput.editText!!.text.toString() val end = toTextInput.editText!!.text.toString()
if (checkRanges(start, end)) { if (checkRanges(start, end)) {
if (start < end){ if (start.toInt() < end.toInt()){
var startNr = Integer.parseInt(start) var startNr = Integer.parseInt(start)
startNr-- startNr--
var endNr = Integer.parseInt(end) var endNr = Integer.parseInt(end)
endNr-- endNr--
if (startNr <= 0) startNr = 0 if (startNr <= 0) startNr = 0
if (endNr > items.size) endNr = items.size if (endNr > items.size) endNr = items.size - 1
listAdapter.checkRange(startNr, endNr) listAdapter.checkRange(startNr, endNr)
selectedText.text = "${listAdapter.getCheckedItems().size} ${resources.getString(R.string.selected)}" selectedText.text = "${listAdapter.getCheckedItems().size} ${resources.getString(R.string.selected)}"
} }
@ -108,13 +108,13 @@ class SelectPlaylistItemsBottomSheetDialog(private val items: List<ResultItem?>,
val end = _text.toString() val end = _text.toString()
if (checkRanges(start, end)) { if (checkRanges(start, end)) {
if (start < end){ if (start.toInt() < end.toInt()){
var startNr = Integer.parseInt(start) var startNr = Integer.parseInt(start)
startNr-- startNr--
var endNr = Integer.parseInt(end) var endNr = Integer.parseInt(end)
endNr-- endNr--
if (startNr <= 0) startNr = 0 if (startNr <= 0) startNr = 0
if (endNr > items.size) endNr = items.size if (endNr > items.size) endNr = items.size -1
listAdapter.checkRange(startNr, endNr) listAdapter.checkRange(startNr, endNr)
selectedText.text = "${listAdapter.getCheckedItems().size} ${resources.getString(R.string.selected)}" selectedText.text = "${listAdapter.getCheckedItems().size} ${resources.getString(R.string.selected)}"
} }

View file

@ -120,7 +120,7 @@ class ActiveDownloadsFragment() : Fragment(), ActiveDownloadAdapter.OnItemClickL
val logFile = fileUtil.getLogFile(requireContext(), item) val logFile = fileUtil.getLogFile(requireContext(), item)
if (logFile.exists()) { if (logFile.exists()) {
val intent = Intent(requireContext(), DownloadLogActivity::class.java) val intent = Intent(requireContext(), DownloadLogActivity::class.java)
intent.putExtra("path", logFile.absolutePath) intent.putExtra("logpath", logFile.absolutePath)
startActivity(intent) startActivity(intent)
} }
} }

View file

@ -105,7 +105,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
val item = items.find { it.id == itemID } ?: return val item = items.find { it.id == itemID } ?: return
val file = fileUtil.getLogFile(requireContext(), item) val file = fileUtil.getLogFile(requireContext(), item)
val intent = Intent(requireContext(), DownloadLogActivity::class.java) val intent = Intent(requireContext(), DownloadLogActivity::class.java)
intent.putExtra("path", file.absolutePath) intent.putExtra("logpath", file.absolutePath)
startActivity(intent) startActivity(intent)
} }

View file

@ -45,7 +45,7 @@ class DownloadLogActivity : BaseActivity() {
clipboard.setText(content.text) clipboard.setText(content.text)
} }
val path = intent.getStringExtra("path") val path = intent.getStringExtra("logpath")
if (path == null) { if (path == null) {
onBackPressedDispatcher.onBackPressed() onBackPressedDispatcher.onBackPressed()
} }

View file

@ -125,7 +125,7 @@ class DownloadLogListActivity : BaseActivity(), DownloadLogsAdapter.OnItemClickL
override fun onItemClick(file: File) { override fun onItemClick(file: File) {
val intent = Intent(this, DownloadLogActivity::class.java) val intent = Intent(this, DownloadLogActivity::class.java)
intent.putExtra("path", file.absolutePath) intent.putExtra("logpath", file.absolutePath)
startActivity(intent) startActivity(intent)
} }

View file

@ -7,6 +7,7 @@ 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
@ -674,6 +675,51 @@ class InfoUtil(private val context: Context) {
return items return items
} }
fun getMissingInfo(url: String): ResultItem? {
try {
val request = YoutubeDLRequest(url)
request.addOption("--flat-playlist")
request.addOption("-J")
request.addOption("--skip-download")
request.addOption("-R", "1")
request.addOption("--socket-timeout", "5")
val cookiesFile = File(context.cacheDir, "cookies.txt")
if (cookiesFile.exists()){
request.addOption("--cookies", cookiesFile.absolutePath)
}
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
val jsonObject = JSONObject(youtubeDLResponse.out)
val thumbs = jsonObject.getJSONArray("thumbnails")
val thumb = thumbs.getJSONObject(thumbs.length() - 1).getString("url")
val isPlaylist = jsonObject.has("playlist_count")
return ResultItem(
0,
url,
if (isPlaylist){
"[${jsonObject.getInt("playlist_count")} Items] ${jsonObject.getString("title")}"
}else{
jsonObject.getString("title")
},
jsonObject.getString("uploader"),
if (jsonObject.has("duration")) formatIntegerDuration(jsonObject.getInt("duration"), Locale.US) else "",
thumb,
jsonObject.getString("extractor"),
if (isPlaylist) jsonObject.getString("title") else "",
arrayListOf(),
System.currentTimeMillis()
)
} catch (e: Exception) {
Looper.prepare().run {
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
}
e.printStackTrace()
}
return null
}
fun getSearchSuggestions(query: String): ArrayList<String> { fun getSearchSuggestions(query: String): ArrayList<String> {
val url = "https://suggestqueries.google.com/complete/search?client=youtube&ds=yt&client=firefox&q=$query" val url = "https://suggestqueries.google.com/complete/search?client=youtube&ds=yt&client=firefox&q=$query"
// invidousURL + "search/suggestions?q=" + query // invidousURL + "search/suggestions?q=" + query

View file

@ -1,9 +1,6 @@
package com.deniscerri.ytdlnis.util package com.deniscerri.ytdlnis.util
import android.app.Notification import android.app.*
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
@ -140,16 +137,22 @@ class NotificationUtil(var context: Context) {
intent.action = Intent.ACTION_VIEW intent.action = Intent.ACTION_VIEW
intent.setDataAndType(uri, "*/*") intent.setDataAndType(uri, "*/*")
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val openNotificationPendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) val openNotificationPendingIntent: PendingIntent = TaskStackBuilder.create(context).run {
addNextIntentWithParentStack(intent)
getPendingIntent(0,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
}
//share intent //share intent
val shareIntent = Intent(context, SharedDownloadNotificationReceiver::class.java) val shareIntent = Intent(context, SharedDownloadNotificationReceiver::class.java)
shareIntent.putExtra("share", "") shareIntent.putExtra("share", "")
shareIntent.putExtra("path", filepath) shareIntent.putExtra("path", filepath)
shareIntent.putExtra("notificationID", DOWNLOAD_FINISHED_NOTIFICATION_ID) shareIntent.putExtra("notificationID", DOWNLOAD_FINISHED_NOTIFICATION_ID)
shareIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK val shareNotificationPendingIntent: PendingIntent = TaskStackBuilder.create(context).run {
val shareNotificationPendingIntent = PendingIntent.getActivity(context, 0, shareIntent, PendingIntent.FLAG_IMMUTABLE) addNextIntentWithParentStack(shareIntent)
getPendingIntent(0,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
}
notificationBuilder.addAction(0, context.getString(R.string.Open_File), openNotificationPendingIntent) notificationBuilder.addAction(0, context.getString(R.string.Open_File), openNotificationPendingIntent)
notificationBuilder.addAction(0, context.getString(R.string.share), shareNotificationPendingIntent) notificationBuilder.addAction(0, context.getString(R.string.share), shareNotificationPendingIntent)
@ -160,31 +163,34 @@ class NotificationUtil(var context: Context) {
fun createDownloadErrored(title: String?, fun createDownloadErrored(title: String?,
error: String?, error: String?,
logFilePath: String?, logFile: File?,
channel: String channel: String
) { ) {
val notificationBuilder = getBuilder(channel) val notificationBuilder = getBuilder(channel)
val intent = Intent(context, DownloadLogActivity::class.java) val intent = Intent(context, DownloadLogActivity::class.java)
intent.putExtra("path", logFilePath) intent.putExtra("logpath", logFile?.absolutePath)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
val errorPendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
val errorPendingIntent: PendingIntent = TaskStackBuilder.create(context).run {
addNextIntentWithParentStack(intent)
getPendingIntent(0,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
}
notificationBuilder notificationBuilder
.setContentTitle("${context.getString(R.string.failed_download)}: $title") .setContentTitle("${context.getString(R.string.failed_download)}: $title")
.setContentText(error) .setContentText(error)
.setContentIntent(errorPendingIntent) .setSmallIcon(R.drawable.ic_launcher_foreground_large)
.setSmallIcon(R.drawable.ic_app_icon)
.setLargeIcon( .setLargeIcon(
BitmapFactory.decodeResource( BitmapFactory.decodeResource(
context.resources, context.resources,
R.drawable.ic_app_icon R.drawable.ic_launcher_foreground_large
) )
) )
.setPriority(NotificationCompat.PRIORITY_MAX) .setPriority(NotificationCompat.PRIORITY_MAX)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.clearActions() .clearActions()
if (logFilePath != null){ if (logFile != null){
notificationBuilder.setContentIntent(errorPendingIntent)
notificationBuilder.addAction(0, context.getString(R.string.logs), errorPendingIntent) notificationBuilder.addAction(0, context.getString(R.string.logs), errorPendingIntent)
} }
notificationManager.notify(DOWNLOAD_FINISHED_NOTIFICATION_ID, notificationBuilder.build()) notificationManager.notify(DOWNLOAD_FINISHED_NOTIFICATION_ID, notificationBuilder.build())

View file

@ -268,6 +268,22 @@ class DownloadWorker(
} }
} }
//update item if its incomplete
if (downloadItem.title.isEmpty() || downloadItem.author.isEmpty() || downloadItem.thumb.isEmpty()){
runCatching {
setProgressAsync(workDataOf("progress" to 0, "output" to "Updating Download Item Data", "id" to downloadItem.id, "log" to false))
val info = infoUtil.getMissingInfo(downloadItem.url)
if (downloadItem.title.isEmpty()) downloadItem.title = info?.title.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.website = info?.website.toString()
runBlocking {
dao.update(downloadItem)
}
}
}
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")
val logFile = fileUtil.getLogFile(context, downloadItem) val logFile = fileUtil.getLogFile(context, downloadItem)
@ -282,21 +298,6 @@ class DownloadWorker(
"Format: ${downloadItem.format}\n\n") "Format: ${downloadItem.format}\n\n")
} }
//update item if its incomplete
if (downloadItem.title.isEmpty() || downloadItem.author.isEmpty() || downloadItem.thumb.isEmpty()){
runCatching {
setProgressAsync(workDataOf("progress" to 0, "output" to "Updating Download Item Data", "id" to downloadItem.id, "log" to logDownloads))
val info = YoutubeDL.getInfo(downloadItem.url)
if (downloadItem.title.isEmpty()) downloadItem.title = info.title.toString()
if (downloadItem.author.isEmpty()) downloadItem.author = info.uploader.toString()
if (downloadItem.thumb.isEmpty()) downloadItem.thumb = info.thumbnail.toString()
downloadItem.duration = infoUtil.formatIntegerDuration(info.duration, Locale.US)
runBlocking {
dao.update(downloadItem)
}
}
}
runCatching { runCatching {
YoutubeDL.getInstance().execute(request, downloadItem.id.toString()){ progress, _, line -> YoutubeDL.getInstance().execute(request, downloadItem.id.toString()){ progress, _, line ->
setProgressAsync(workDataOf("progress" to progress.toInt(), "output" to line.chunked(5000).first().toString(), "id" to downloadItem.id, "log" to logDownloads)) setProgressAsync(workDataOf("progress" to progress.toInt(), "output" to line.chunked(5000).first().toString(), "id" to downloadItem.id, "log" to logDownloads))
@ -376,7 +377,7 @@ class DownloadWorker(
notificationUtil.createDownloadErrored( notificationUtil.createDownloadErrored(
downloadItem.title, it.message, downloadItem.title, it.message,
if (logDownloads) logFile.absolutePath else null, if (logDownloads) logFile else null,
NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID
) )

View file

@ -15,7 +15,7 @@
android:checkable="true" android:checkable="true"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:background="?android:attr/selectableItemBackground" android:backgroundTint="@android:color/transparent"
app:cardPreventCornerOverlap="true" app:cardPreventCornerOverlap="true"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -178,12 +178,13 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/action_button" android:id="@+id/action_button"
style="?attr/materialIconButtonStyle" style="?attr/materialIconButtonStyle"
android:layout_width="wrap_content" android:layout_width="0dp"
android:padding="0dp"
android:minHeight="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:cornerRadius="10dp" app:cornerRadius="10dp"
app:iconTint="?attr/colorAccent" app:iconTint="?attr/colorAccent"
app:icon="@drawable/ic_video" app:icon="@drawable/ic_video"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" /> app:layout_constraintVertical_bias="0.0" />

View file

@ -5,7 +5,7 @@ buildscript {
def versionMajor = 1 def versionMajor = 1
def versionMinor = 6 def versionMinor = 6
def versionPatch = 0 def versionPatch = 1
def versionBuild = 0 // bump for dogfood builds, public betas, etc. def versionBuild = 0 // bump for dogfood builds, public betas, etc.
ext { ext {

View file

@ -0,0 +1,11 @@
## Introducing Quick Download Mode
You can launch a download (or card) immediately without fetching data at first.
Added Theme Picker, Accents and High Contrast Mode
Added swipe actions on history,downloads,cookies,templates cards
Added finished download notification with actions
Fixed Tiktok videos not creating result cards
Added active downloads badge in the navigation bar
Added remove audio feature for videos
Added check for present downloads
Added preferred Format ID