pull/322/head
deniscerri 3 years ago
parent 9c81b871df
commit 99b2938366
No known key found for this signature in database
GPG Key ID: 95C43D517D830350

@ -10,7 +10,7 @@ def properties = new Properties()
def versionMajor = 1
def versionMinor = 6
def versionPatch = 8
def versionBuild = 4 // bump for dogfood builds, public betas, etc.
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
def versionExt = ""
if (versionBuild > 0){
@ -130,13 +130,13 @@ dependencies {
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
implementation 'com.google.android.material:material:1.10.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.3.1'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'androidx.preference:preference-ktx:1.2.1'
implementation "androidx.navigation:navigation-fragment-ktx:$navVer"
implementation "androidx.navigation:navigation-ui-ktx:$navVer"
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.test.ext:junit-ktx:1.1.5'
implementation 'androidx.compose.ui:ui-android:1.5.3'
implementation 'androidx.compose.ui:ui-android:1.5.4'
testImplementation "junit:junit:$junitVer"
androidTestImplementation "junit:junit:$junitVer"
androidTestImplementation "androidx.test.ext:junit:$androidJunitVer"
@ -154,12 +154,12 @@ dependencies {
implementation "androidx.work:work-runtime-ktx:$workVer"
implementation "androidx.room:room-runtime:2.5.2"
implementation "androidx.room:room-ktx:2.5.2"
ksp "androidx.room:room-compiler:2.5.2"
implementation "androidx.room:room-runtime:$roomVer"
implementation "androidx.room:room-ktx:$roomVer"
ksp "androidx.room:room-compiler:$roomVer"
implementation 'androidx.paging:paging-runtime-ktx:3.2.1'
implementation "androidx.room:room-paging:2.5.2"
androidTestImplementation "androidx.room:room-testing:2.5.2"
implementation "androidx.room:room-paging:$roomVer"
androidTestImplementation "androidx.room:room-testing:$roomVer"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
implementation "androidx.compose.runtime:runtime:$composeVer"
androidTestImplementation 'com.google.truth:truth:1.1.5'

@ -3,6 +3,8 @@ package com.deniscerri.ytdlnis.database.repository
import com.deniscerri.ytdlnis.database.dao.LogDao
import com.deniscerri.ytdlnis.database.models.LogItem
import kotlinx.coroutines.flow.Flow
import java.util.regex.MatchResult
import java.util.regex.Pattern
class LogRepository(private val logDao: LogDao) {
val items : Flow<List<LogItem>> = logDao.getAllLogsFlow()
@ -42,7 +44,22 @@ class LogRepository(private val logDao: LogDao) {
val item = getItem(id)
val log = item.content
//clean duplicate progress + add newline
//item.content = log.replace("(?s:.*\\n)?\\K\\[download\\]( *?)(\\d)(.*?)\\n(?!.*\\[download\\]( *?)(\\d)(.*?)\\n)".toRegex()).replac { it.contains("[download") }.joinToString("\n") + "\n${line}"
val lines = log.split("\n").toMutableList()
run loop@ {
for(i in lines.size - 1 downTo 0){
val l = lines[i]
if(l.contains("\\[download]( *?)(\\d)(.*?)".toRegex())){
lines[i] = ""
return@loop
}
}
}
val l = if (line.contains("[download]")) {
"[download]" + line.split("[download]").last()
}else {
line
}
item.content = lines.filter { it.isNotBlank() }.joinToString("\n") + "\n${l}"
logDao.update(item)
}
}

@ -17,7 +17,6 @@ class CancelDownloadNotificationReceiver : BroadcastReceiver() {
val id = intent.getIntExtra("itemID", 0)
if (id > 0) {
runCatching {
Log.e("aa", id.toString())
val notificationUtil = NotificationUtil(c)
YoutubeDL.getInstance().destroyProcessById(id.toString())
notificationUtil.cancelDownloadNotification(id)
@ -28,6 +27,9 @@ class CancelDownloadNotificationReceiver : BroadcastReceiver() {
item.status = DownloadRepository.Status.Cancelled.toString()
dbManager.downloadDao.update(item)
}
runCatching {
dbManager.terminalDao.delete(id.toLong())
}
}
}

@ -204,11 +204,14 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val urls
val currentTime = infoUtil.formatIntegerDuration(p, Locale.US)
durationText.text = "$currentTime / ${item.duration}"
val startTimestamp = convertStringToTimestamp(fromTextInput.editText!!.text.toString())
val endTimestamp = convertStringToTimestamp(toTextInput.editText!!.text.toString())
if (p >= endTimestamp){
player.prepare()
player.seekTo((startTimestamp * 1000).toLong())
if (toTextInput.editText!!.text.isNotBlank()){
val endTimestamp = convertStringToTimestamp(toTextInput.editText!!.text.toString())
if (p >= endTimestamp){
player.prepare()
player.seekTo((startTimestamp * 1000).toLong())
}
}
}
}

@ -303,7 +303,7 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
initUpdateData(view)
}else {
val usingGenericFormatsOrEmpty = result.formats.isEmpty() || result.formats.any { it.format_note.contains("ytdlnisgeneric") }
if (usingGenericFormatsOrEmpty && sharedPreferences.getBoolean("update_formats", false)){
if (usingGenericFormatsOrEmpty && sharedPreferences.getBoolean("update_formats", false) && !sharedPreferences.getBoolean("quick_download", false)){
initUpdateFormats(result.url)
}
}
@ -311,7 +311,17 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
CoroutineScope(Dispatchers.IO).launch{
downloadViewModel.uiState.collectLatest { res ->
if (res.errorMessage != null) {
UiUtil.handleDownloadsResponse(requireActivity() as MainActivity, res, downloadViewModel, historyViewModel)
withContext(Dispatchers.Main){
kotlin.runCatching {
UiUtil.handleDownloadsResponse(
requireActivity(),
requireActivity().lifecycleScope,
requireActivity().supportFragmentManager,
res,
downloadViewModel,
historyViewModel)
}
}
downloadViewModel.uiState.value = DownloadViewModel.DownloadsUiState(
errorMessage = null,
actions = null
@ -432,7 +442,7 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
parentFragmentManager.addFragmentOnAttachListener { fragmentManager, fragment ->
dismiss()
}
playlistSelect.show(parentFragmentManager, "downloadMultipleSheet")
playlistSelect.show(parentFragmentManager, "downloadPlaylistSheet")
}
}else{
dismiss()
@ -441,6 +451,7 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
}
shimmerLoading.setOnClickListener {
updateJob.cancel()
(updateItem.parent as LinearLayout).visibility = View.VISIBLE
}
updateJob.invokeOnCompletion {
requireActivity().runOnUiThread {
@ -450,7 +461,6 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
shimmerLoadingSubtitle.visibility = View.GONE
shimmerLoading.stopShimmer()
shimmerLoadingSubtitle.stopShimmer()
(updateItem.parent as LinearLayout).visibility = View.VISIBLE
}
}
updateJob.start()
@ -515,11 +525,7 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
private fun cleanUp(){
kotlin.runCatching {
repeat((parentFragmentManager.findFragmentByTag("downloadSingleSheet")?.fragmentManager?.backStackEntryCount?.minus(1))?: 0){
parentFragmentManager.findFragmentByTag("downloadSingleSheet")?.fragmentManager?.popBackStack()
}
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("downloadSingleSheet")!!).commit()
if (activity is ShareActivity && !parentFragmentManager.fragments.map { it.tag }.contains("downloadMultipleSheet")){
if (activity is ShareActivity && !parentFragmentManager.fragments.map { it.tag }.contains("downloadPlaylistSheet")){
(activity as ShareActivity).finish()
}
}

@ -57,6 +57,7 @@ import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
@ -507,10 +508,20 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
true
}
lifecycleScope.launch {
CoroutineScope(Dispatchers.IO).launch{
downloadViewModel.uiState.collectLatest { res ->
if (res.errorMessage != null) {
UiUtil.handleDownloadsResponse(requireActivity() as MainActivity, res, downloadViewModel, historyViewModel)
withContext(Dispatchers.Main){
kotlin.runCatching {
UiUtil.handleDownloadsResponse(
requireActivity(),
requireActivity().lifecycleScope,
requireActivity().supportFragmentManager,
res,
downloadViewModel,
historyViewModel)
}
}
downloadViewModel.uiState.value = DownloadViewModel.DownloadsUiState(
errorMessage = null,
actions = null
@ -560,8 +571,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
private fun cleanup(){
kotlin.runCatching {
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("downloadMultipleSheet")!!).commit()
if (parentFragmentManager.fragments.size == 1){
if (activity is ShareActivity){
(activity as ShareActivity).finish()
}
}

@ -157,6 +157,9 @@ class SelectPlaylistItemsDialog(private val items: List<ResultItem?>, private va
if (checkedResultItems.size == 1){
val resultItem = resultViewModel.getItemByURL(checkedResultItems[0]!!.url)
val bottomSheet = DownloadBottomSheetDialog(resultItem, type)
parentFragmentManager.addFragmentOnAttachListener { fragmentManager, fragment ->
dismiss()
}
bottomSheet.show(parentFragmentManager, "downloadSingleSheet")
}else{
val downloadItems = mutableListOf<DownloadItem>()
@ -171,6 +174,9 @@ class SelectPlaylistItemsDialog(private val items: List<ResultItem?>, private va
}
val bottomSheet = DownloadMultipleBottomSheetDialog(downloadItems)
parentFragmentManager.addFragmentOnAttachListener { fragmentManager, fragment ->
dismiss()
}
bottomSheet.show(parentFragmentManager, "downloadMultipleSheet")
}
@ -216,10 +222,10 @@ class SelectPlaylistItemsDialog(private val items: List<ResultItem?>, private va
private fun cleanup(){
kotlin.runCatching {
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("downloadPlaylistSheet")!!).commit()
}
if (activity is ShareActivity){
(activity as ShareActivity).finishAffinity()
val movedToMultipleOrSingleSheet = parentFragmentManager.fragments.map { it.tag }.contains("downloadMultipleSheet") || parentFragmentManager.fragments.map { it.tag }.contains("downloadSingleSheet")
if (activity is ShareActivity && !movedToMultipleOrSingleSheet){
(activity as ShareActivity).finish()
}
}
}

@ -1095,22 +1095,22 @@ class InfoUtil(private val context: Context) {
request.addOption("--write-description")
}
if (downloadItem.playlistTitle.isNotBlank()){
request.addOption("--parse-metadata","${downloadItem.playlistTitle.split("[")[0]}:%(playlist)s")
runCatching {
request.addOption("--parse-metadata",
downloadItem.playlistTitle
.substring(
downloadItem.playlistTitle.indexOf("[") + 1,
downloadItem.playlistTitle.indexOf("]"),
) + ":%(playlist_index)s"
)
}
}
downloadItem.customFileNameTemplate = downloadItem.customFileNameTemplate.replace("%(uploader)s", "%(uploader,channel)s")
}
if (downloadItem.playlistTitle.isNotBlank()){
request.addOption("--parse-metadata","${downloadItem.playlistTitle.split("[")[0]}:%(playlist)s")
runCatching {
request.addOption("--parse-metadata",
downloadItem.playlistTitle
.substring(
downloadItem.playlistTitle.indexOf("[") + 1,
downloadItem.playlistTitle.indexOf("]"),
) + ":%(playlist_index)s"
)
}
}
when(type){
DownloadViewModel.Type.audio -> {
val supportedContainers = context.resources.getStringArray(R.array.audio_containers)

@ -363,6 +363,38 @@ class NotificationUtil(var context: Context) {
e.printStackTrace()
}
}
fun updateTerminalDownloadNotification(
id: Int,
desc: String,
progress: Int,
title: String?,
channel : String
) {
val notificationBuilder = getBuilder(channel)
var contentText = ""
contentText += desc.replace("\\[.*?\\] ".toRegex(), "")
val cancelIntent = Intent(context, CancelDownloadNotificationReceiver::class.java)
cancelIntent.putExtra("itemID", id)
val cancelNotificationPendingIntent = PendingIntent.getBroadcast(
context,
id,
cancelIntent,
PendingIntent.FLAG_IMMUTABLE
)
try {
notificationBuilder.setProgress(100, progress, false)
.setContentTitle(title)
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
.clearActions()
.addAction(0, resources.getString(R.string.cancel), cancelNotificationPendingIntent)
notificationManager.notify(id, notificationBuilder.build())
} catch (e: Exception) {
e.printStackTrace()
}
}
fun cancelDownloadNotification(id: Int) {
notificationManager.cancel(id)

@ -39,11 +39,9 @@ import androidx.core.view.isVisible
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.DownloadItem
@ -1341,7 +1339,7 @@ object UiUtil {
private var textHighLightSchemes = listOf(
ColorScheme(Pattern.compile("([\"'])(?:\\\\1|.)*?\\1"), Color.parseColor("#FC8500")),
ColorScheme(Pattern.compile("yt-dlp"), Color.parseColor("#FF0000")),
ColorScheme(Pattern.compile("yt-dlp"), Color.parseColor("#77eb09")),
ColorScheme(Pattern.compile("(https?://(?:www\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|www\\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|https?://(?:www\\.|(?!www))[a-zA-Z0-9]+\\.[^\\s]{2,}|www\\.[a-zA-Z0-9]+\\.[^\\s]{2,})"), Color.parseColor("#b5942f")),
ColorScheme(Pattern.compile("\\d+(\\.\\d)?%"), Color.parseColor("#43a564"))
)
@ -1407,7 +1405,7 @@ object UiUtil {
}
@SuppressLint("SetTextI18n")
fun handleDownloadsResponse(context: MainActivity, it: DownloadViewModel.DownloadsUiState, downloadViewModel: DownloadViewModel, historyViewModel: HistoryViewModel){
fun handleDownloadsResponse(context: Activity, lifecycleScope: CoroutineScope, supportFragmentManager: FragmentManager, it: DownloadViewModel.DownloadsUiState, downloadViewModel: DownloadViewModel, historyViewModel: HistoryViewModel){
val downloadAnywayAction = it.actions?.first { it.second == DownloadViewModel.DownloadsAction.DOWNLOAD_ANYWAY}
if (downloadAnywayAction != null){
if (downloadAnywayAction.third == null) return
@ -1454,7 +1452,7 @@ object UiUtil {
resultItemID: Long,
item: DownloadItem
) {
context.lifecycleScope.launch {
lifecycleScope.launch {
val idx = downloads.indexOfFirst { it.id == item.id }
downloads[idx] = item
withContext(Dispatchers.Main){
@ -1464,7 +1462,7 @@ object UiUtil {
}
}
val bottomSheet = ConfigureDownloadBottomSheetDialog(resultItem, downloadItem, onItemUpdated)
bottomSheet.show(context.supportFragmentManager, "configureDownloadSingleSheet")
bottomSheet.show(supportFragmentManager, "configureDownloadSingleSheet")
}
if (historyItem != null){
@ -1507,7 +1505,7 @@ object UiUtil {
}
errDialog.setNegativeButton(R.string.schedule) { d:DialogInterface?, _:Int ->
showDatePicker(context.supportFragmentManager) { calendar ->
showDatePicker(supportFragmentManager) { calendar ->
CoroutineScope(Dispatchers.IO).launch {
val items = mutableListOf<DownloadItem>()
linearLayout.children.forEach {view ->

@ -75,7 +75,8 @@ class DownloadWorker(
.createPendingIntent()
val workNotif = notificationUtil.createDefaultWorkerNotification()
val foregroundInfo = ForegroundInfo(Random.nextInt(1000000000), workNotif)
val workNotifID = Random.nextInt(900000000, 1000000000)
val foregroundInfo = ForegroundInfo(workNotifID, workNotif)
setForegroundAsync(foregroundInfo)
queuedItems.collect { items ->
@ -100,8 +101,8 @@ class DownloadWorker(
val notification = notificationUtil.createDownloadServiceNotification(pendingIntent, downloadItem.title, downloadItem.id.toInt())
notificationUtil.notify(downloadItem.id.toInt(), notification)
CoroutineScope(Dispatchers.IO).launch {
val request = infoUtil.buildYoutubeDLRequest(downloadItem)
downloadItem.status = DownloadRepository.Status.Active.toString()
CoroutineScope(Dispatchers.IO).launch {

@ -108,9 +108,9 @@ class TerminalDownloadWorker(
}
val title: String = command.take(65)
notificationUtil.updateDownloadNotification(
notificationUtil.updateTerminalDownloadNotification(
itemId,
line, progress.toInt(), 0, title,
line, progress.toInt(), title,
NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
)
CoroutineScope(Dispatchers.IO).launch {

@ -25,6 +25,7 @@ buildscript {
media3_version = "1.1.1"
agp_version = '8.1.0'
agp_version1 = '7.4.2'
roomVer = '2.5.2'
}
repositories {
mavenCentral()

@ -0,0 +1,79 @@
## What's New
- removed trim filenames from command type downloads
- fixed app not showing formats when it has generic formats
- fixed author getting written as NA for kick videos
- added separate channel for the worker notification that users can turn off
- include search history when searching
- removed scroll bug from command tab
- added spacing between command template title and ok button in selection card
- made download progress not dublicate in terminal
- made ability to store terminal state
- added ability to create multiple terminal instances and show them as a list similar to download queue
- fixed thumbnail download not working
- fixed app crashing when clicking on format updated notification
- fixed app crashing when double clicking format on multiple download card
- added custom sponsorblock api preference
- removed contextual app bar when you its enabled and the user taps the log button in the erorred tab
- made app always show quick download card and asynchoronously load data. Quick Download now if its on, it wont load data at all
- added shimmer when loading data in the download card
- fixed app showing no formats if there were no common formats. Now it will give you generic formats
- made open command template list be half the screen, shortcuts third of the screen so the user can see what its being added
- fixed sometimes app slipping queued downloads even though its told to pause all
- fixed trim filenames cutting files too short
- made mediastore scanning of files one by one
- fixed filename template not working in multiple download card
- fixed -F in terminal not being inline
- added preferred audio codec
- made auto update on boot if there are no active downloads
- fixed format text overlapping
- added a new error dialog in cases yt-dlp data fetching in the home screen fails. You can copy the log
- formats auto updating as soon as the download card opens if auto-update is on
- added preferred audio format always in the video tab
- made app post downloads for queue in chunks
- made app always save logs in case it fails, and if succeeds and logs are off it deletes it
- fixed app navigating to home screen when cancelling download card in history screen
- added a button to skip incoming app update so it wont bother you anymore
- fixed settings not restoring some fields
- fixed crunchyroll not working with cookies
- added search for command templates
- added sort filtering for command templates
- added all shortcuts inside filename template creation dialog. Long click them to see the explanation
- added preference to hide elements from the download card
- made avc1 and m4a as preferred codecs for noobs
- u can edit the duplicated download item right from the error dialog or access the history item to view the file
- added extractor args lang when searching in yt-dlp
- removed webarchive search engine since its not supported anymore
- fixed terminal prematurely closing
- made format auto-update on by default for new users
- fixed main activity getting removed from recents when using the app with rvx
- added ability to have the last used command template for the next download
- fixed app crashing in landscape logs
- fixed app constantly going back to home in landscape or config change. now it keeps state
- add subtitle language suggestions in the subtitle dialog
- made command template scroll state hold even if fragment is destroyed
- added slovak sk language
- fixed terminal icon being blank in white mode, and now its red
- fixed share files from notification showing 2 files even though its 1
- fixed history item not being deleted from the bottom sheet
- cleared outdated player urls for stale result items
- added export cookies as file
- added export command templates for selected templates
- added icons for history details sheet chips
- added markdown in the update dialog
- and other random bug fixes
## Autogenerated Changes
* Translations update from Hosted Weblate by @weblate in https://github.com/deniscerri/ytdlnis/pull/283
* Create README-id.MD (Indonesian README) by @teddysulaimanGL in https://github.com/deniscerri/ytdlnis/pull/291
* Translations update from Hosted Weblate by @weblate in https://github.com/deniscerri/ytdlnis/pull/288
* Update README-pt.md by @gigoloinc in https://github.com/deniscerri/ytdlnis/pull/293
* Translations update from Hosted Weblate by @weblate in https://github.com/deniscerri/ytdlnis/pull/292
* Translations update from Hosted Weblate by @weblate in https://github.com/deniscerri/ytdlnis/pull/301
* Translations update from Hosted Weblate by @weblate in https://github.com/deniscerri/ytdlnis/pull/305
* Translations update from Hosted Weblate by @weblate in https://github.com/deniscerri/ytdlnis/pull/310
## New Contributors
* @teddysulaimanGL made their first contribution in https://github.com/deniscerri/ytdlnis/pull/291
* @gigoloinc made their first contribution in https://github.com/deniscerri/ytdlnis/pull/293
**Full Changelog**: https://github.com/deniscerri/ytdlnis/compare/v1.6.7...v1.6.8
Loading…
Cancel
Save