made file transfer a separate worker
made the app create default folders since downloading on a fresh install wouldnt work without checking the settings first, now you can made each download have their own separate temporary folder using an url and download type. this way they can be resumed fixed sponsorblock filters not working on a fresh install fixed custom command notification not being in the proper channel and other small stuff
This commit is contained in:
parent
6a1ed4fc7e
commit
51ff22abcd
16 changed files with 295 additions and 68 deletions
|
|
@ -119,7 +119,7 @@ dependencies {
|
||||||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
|
||||||
implementation 'com.facebook.shimmer:shimmer:0.5.0'
|
implementation 'com.facebook.shimmer:shimmer:0.5.0'
|
||||||
|
|
||||||
implementation "androidx.work:work-runtime:$workVer"
|
implementation "androidx.work:work-runtime-ktx:$workVer"
|
||||||
|
|
||||||
implementation "androidx.room:room-runtime:2.4.3"
|
implementation "androidx.room:room-runtime:2.4.3"
|
||||||
implementation "androidx.room:room-ktx:2.4.3"
|
implementation "androidx.room:room-ktx:2.4.3"
|
||||||
|
|
@ -127,7 +127,8 @@ dependencies {
|
||||||
androidTestImplementation "androidx.room:room-testing:2.4.3"
|
androidTestImplementation "androidx.room:room-testing:2.4.3"
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
|
||||||
implementation "androidx.compose.runtime:runtime:$composeVer"
|
implementation "androidx.compose.runtime:runtime:$composeVer"
|
||||||
|
|
||||||
|
|
||||||
androidTestImplementation "com.google.truth:truth:1.1.3"
|
androidTestImplementation "com.google.truth:truth:1.1.3"
|
||||||
|
|
||||||
|
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVer"
|
||||||
|
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVer"
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,6 @@
|
||||||
android:theme="@style/AppTheme" >
|
android:theme="@style/AppTheme" >
|
||||||
<activity android:name=".MainActivity"
|
<activity android:name=".MainActivity"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:label="@string/download"
|
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,15 @@ package com.deniscerri.ytdlnis
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.deniscerri.ytdlnis.util.NotificationUtil
|
import com.deniscerri.ytdlnis.util.NotificationUtil
|
||||||
|
import com.deniscerri.ytdlnis.util.UpdateUtil
|
||||||
import com.google.android.material.color.DynamicColors
|
import com.google.android.material.color.DynamicColors
|
||||||
import com.yausername.aria2c.Aria2c
|
import com.yausername.aria2c.Aria2c
|
||||||
import com.yausername.ffmpeg.FFmpeg
|
import com.yausername.ffmpeg.FFmpeg
|
||||||
|
|
@ -17,6 +22,7 @@ import io.reactivex.exceptions.UndeliverableException
|
||||||
import io.reactivex.observers.DisposableCompletableObserver
|
import io.reactivex.observers.DisposableCompletableObserver
|
||||||
import io.reactivex.plugins.RxJavaPlugins
|
import io.reactivex.plugins.RxJavaPlugins
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class App : Application() {
|
class App : Application() {
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,17 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Binder
|
import android.os.Binder
|
||||||
|
import android.os.Build
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
|
import android.os.SystemClock
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
|
import android.provider.Settings.Global
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.work.ExistingWorkPolicy
|
||||||
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.workDataOf
|
||||||
import com.deniscerri.ytdlnis.database.Video
|
import com.deniscerri.ytdlnis.database.Video
|
||||||
import com.deniscerri.ytdlnis.page.CustomCommandActivity
|
import com.deniscerri.ytdlnis.page.CustomCommandActivity
|
||||||
import com.deniscerri.ytdlnis.service.DownloadInfo
|
import com.deniscerri.ytdlnis.service.DownloadInfo
|
||||||
|
|
@ -18,6 +25,10 @@ import com.deniscerri.ytdlnis.service.IDownloaderListener
|
||||||
import com.deniscerri.ytdlnis.service.IDownloaderService
|
import com.deniscerri.ytdlnis.service.IDownloaderService
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.deniscerri.ytdlnis.util.NotificationUtil
|
import com.deniscerri.ytdlnis.util.NotificationUtil
|
||||||
|
import com.deniscerri.ytdlnis.work.FileTransferWorker
|
||||||
|
import com.deniscerri.ytdlnis.work.FileTransferWorker.Companion.originDir
|
||||||
|
import com.deniscerri.ytdlnis.work.FileTransferWorker.Companion.title
|
||||||
|
import com.deniscerri.ytdlnis.work.FileTransferWorker.Companion.downLocation
|
||||||
import com.yausername.youtubedl_android.DownloadProgressCallback
|
import com.yausername.youtubedl_android.DownloadProgressCallback
|
||||||
import com.yausername.youtubedl_android.YoutubeDL
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
import com.yausername.youtubedl_android.YoutubeDLRequest
|
import com.yausername.youtubedl_android.YoutubeDLRequest
|
||||||
|
|
@ -26,6 +37,7 @@ import io.reactivex.Observable
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import kotlinx.coroutines.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
@ -46,6 +58,8 @@ class DownloaderService : Service() {
|
||||||
private var context: Context? = null
|
private var context: Context? = null
|
||||||
var downloadProcessID = "processID"
|
var downloadProcessID = "processID"
|
||||||
private var downloadNotificationID = 0
|
private var downloadNotificationID = 0
|
||||||
|
private lateinit var notificationChannelID : String
|
||||||
|
private lateinit var workManager : WorkManager
|
||||||
|
|
||||||
private val callback: (Float, Long?, String?) -> Unit =
|
private val callback: (Float, Long?, String?) -> Unit =
|
||||||
{ progress: Float, _: Long?, line: String? ->
|
{ progress: Float, _: Long?, line: String? ->
|
||||||
|
|
@ -58,7 +72,8 @@ class DownloaderService : Service() {
|
||||||
}
|
}
|
||||||
notificationUtil.updateDownloadNotification(
|
notificationUtil.updateDownloadNotification(
|
||||||
downloadNotificationID,
|
downloadNotificationID,
|
||||||
line!!, progress.toInt(), downloadQueue.size, title
|
line!!, progress.toInt(), downloadQueue.size, title,
|
||||||
|
notificationChannelID
|
||||||
)
|
)
|
||||||
try {
|
try {
|
||||||
for (activity in activities.keys) {
|
for (activity in activities.keys) {
|
||||||
|
|
@ -74,17 +89,11 @@ class DownloaderService : Service() {
|
||||||
} catch (ignored: Exception) {
|
} catch (ignored: Exception) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// private val callback = object : DownloadProgressCallback {
|
|
||||||
// override fun onProgressUpdate(progress: Float, etaInSeconds: Long, line: String?) {
|
|
||||||
// override fun onProgressUpdate(progress: Float, etaInSeconds: Long, line: String?) {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
context = this
|
context = this
|
||||||
|
this.workManager = WorkManager.getInstance(context!!.applicationContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBind(intent: Intent): IBinder? {
|
override fun onBind(intent: Intent): IBinder? {
|
||||||
|
|
@ -93,9 +102,9 @@ class DownloaderService : Service() {
|
||||||
if (intent.getBooleanExtra("rebind", false)) {
|
if (intent.getBooleanExtra("rebind", false)) {
|
||||||
return binder
|
return binder
|
||||||
}
|
}
|
||||||
val id = intent.getIntExtra("id", 1)
|
when (val id = intent.getIntExtra("id", 1)) {
|
||||||
when (id) {
|
|
||||||
NotificationUtil.DOWNLOAD_NOTIFICATION_ID -> {
|
NotificationUtil.DOWNLOAD_NOTIFICATION_ID -> {
|
||||||
|
notificationChannelID = NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
|
||||||
theIntent = Intent(this, MainActivity::class.java)
|
theIntent = Intent(this, MainActivity::class.java)
|
||||||
pendingIntent =
|
pendingIntent =
|
||||||
PendingIntent.getActivity(this, 0, theIntent, PendingIntent.FLAG_IMMUTABLE)
|
PendingIntent.getActivity(this, 0, theIntent, PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
|
@ -106,21 +115,23 @@ class DownloaderService : Service() {
|
||||||
downloadInfo.downloadQueue = downloadQueue
|
downloadInfo.downloadQueue = downloadQueue
|
||||||
val title = downloadInfo.video.title
|
val title = downloadInfo.video.title
|
||||||
val notification =
|
val notification =
|
||||||
App.notificationUtil.createDownloadServiceNotification(pendingIntent, title)
|
App.notificationUtil.createDownloadServiceNotification(pendingIntent, title, NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID)
|
||||||
startForeground(downloadNotificationID, notification)
|
startForeground(downloadNotificationID, notification)
|
||||||
startDownload(downloadQueue)
|
startDownload(downloadQueue)
|
||||||
}
|
}
|
||||||
NotificationUtil.COMMAND_DOWNLOAD_NOTIFICATION_ID -> {
|
NotificationUtil.COMMAND_DOWNLOAD_NOTIFICATION_ID -> {
|
||||||
|
notificationChannelID = NotificationUtil.COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID
|
||||||
theIntent = Intent(this, CustomCommandActivity::class.java)
|
theIntent = Intent(this, CustomCommandActivity::class.java)
|
||||||
pendingIntent =
|
pendingIntent =
|
||||||
PendingIntent.getActivity(this, 0, theIntent, PendingIntent.FLAG_IMMUTABLE)
|
PendingIntent.getActivity(this, 0, theIntent, PendingIntent.FLAG_IMMUTABLE)
|
||||||
downloadNotificationID = id
|
downloadNotificationID = id
|
||||||
val command = intent.getStringExtra("command")
|
val command = intent.getStringExtra("command")
|
||||||
val command_notification = App.notificationUtil.createDownloadServiceNotification(
|
val commandNotification = App.notificationUtil.createDownloadServiceNotification(
|
||||||
pendingIntent,
|
pendingIntent,
|
||||||
getString(R.string.running_ytdlp_command)
|
getString(R.string.running_ytdlp_command),
|
||||||
|
NotificationUtil.COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID
|
||||||
)
|
)
|
||||||
startForeground(downloadNotificationID, command_notification)
|
startForeground(downloadNotificationID, commandNotification)
|
||||||
startCommandDownload(command)
|
startCommandDownload(command)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -129,7 +140,11 @@ class DownloaderService : Service() {
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
stopForeground(true)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
|
||||||
|
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||||
|
}else{
|
||||||
|
stopForeground(true)
|
||||||
|
}
|
||||||
stopSelf()
|
stopSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -294,6 +309,11 @@ class DownloaderService : Service() {
|
||||||
val url = video.getURL()
|
val url = video.getURL()
|
||||||
val request = YoutubeDLRequest(url)
|
val request = YoutubeDLRequest(url)
|
||||||
val type = video.downloadedType
|
val type = video.downloadedType
|
||||||
|
val downloadLocation = getDownloadLocation(type)
|
||||||
|
|
||||||
|
var tempFileDir = File(cacheDir.absolutePath + """/${video.title}##${video.downloadedType}""")
|
||||||
|
tempFileDir.delete()
|
||||||
|
tempFileDir.mkdir()
|
||||||
|
|
||||||
val sharedPreferences = context!!.getSharedPreferences("root_preferences", MODE_PRIVATE)
|
val sharedPreferences = context!!.getSharedPreferences("root_preferences", MODE_PRIVATE)
|
||||||
val aria2 = sharedPreferences.getBoolean("aria2", false)
|
val aria2 = sharedPreferences.getBoolean("aria2", false)
|
||||||
|
|
@ -312,11 +332,16 @@ class DownloaderService : Service() {
|
||||||
request.addOption("--convert-thumbnails", "png")
|
request.addOption("--convert-thumbnails", "png")
|
||||||
}
|
}
|
||||||
request.addOption("--no-mtime")
|
request.addOption("--no-mtime")
|
||||||
val sponsorBlockFilters = sharedPreferences.getString("sponsorblock_filter", "")
|
val sponsorBlockFilters = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
|
||||||
if (!sponsorBlockFilters!!.isEmpty()) {
|
if (sponsorBlockFilters!!.isNotEmpty()) {
|
||||||
request.addOption("--sponsorblock-remove", sponsorBlockFilters)
|
val filters = java.lang.String.join(",", sponsorBlockFilters)
|
||||||
|
request.addOption("--sponsorblock-remove", filters)
|
||||||
}
|
}
|
||||||
if (type == "audio") {
|
if (type == "audio") {
|
||||||
|
if (downloadLocation.equals(getString(R.string.music_path))){
|
||||||
|
tempFileDir = File(getString(R.string.music_path))
|
||||||
|
}
|
||||||
|
|
||||||
request.addOption("-x")
|
request.addOption("-x")
|
||||||
var format = video.audioFormat
|
var format = video.audioFormat
|
||||||
if (format == null) format = sharedPreferences.getString("audio_format", "")
|
if (format == null) format = sharedPreferences.getString("audio_format", "")
|
||||||
|
|
@ -348,8 +373,12 @@ class DownloaderService : Service() {
|
||||||
video.author
|
video.author
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
request.addOption("-o", filesDir.absolutePath + "/%(uploader)s - %(title)s.%(ext)s")
|
request.addOption("-o", tempFileDir.absolutePath + "/%(uploader)s - %(title)s.%(ext)s")
|
||||||
} else if (type == "video") {
|
} else if (type == "video") {
|
||||||
|
if (downloadLocation.equals(getString(R.string.video_path))){
|
||||||
|
tempFileDir = File(getString(R.string.video_path))
|
||||||
|
}
|
||||||
|
|
||||||
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
|
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
|
||||||
if (addChapters) {
|
if (addChapters) {
|
||||||
request.addOption("--sponsorblock-mark", "all")
|
request.addOption("--sponsorblock-mark", "all")
|
||||||
|
|
@ -380,7 +409,7 @@ class DownloaderService : Service() {
|
||||||
request.addOption("--embed-thumbnail")
|
request.addOption("--embed-thumbnail")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
request.addOption("-o", filesDir.absolutePath + "/%(uploader)s - %(title)s.%(ext)s")
|
request.addOption("-o", tempFileDir.absolutePath + "/%(uploader)s - %(title)s.%(ext)s")
|
||||||
}
|
}
|
||||||
|
|
||||||
val disposable = Observable.fromCallable {
|
val disposable = Observable.fromCallable {
|
||||||
|
|
@ -389,10 +418,22 @@ class DownloaderService : Service() {
|
||||||
.subscribeOn(Schedulers.newThread())
|
.subscribeOn(Schedulers.newThread())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe({
|
.subscribe({
|
||||||
val destDir = Uri.parse(getDownloadLocation(type)).run {
|
val workTag = video.getURL()
|
||||||
DocumentsContract.buildChildDocumentsUriUsingTree(this, DocumentsContract.getTreeDocumentId(this))
|
val workData = workDataOf(
|
||||||
}
|
originDir to tempFileDir.absolutePath,
|
||||||
fileUtil.moveFile(filesDir, context!!, destDir)
|
downLocation to downloadLocation,
|
||||||
|
title to video.title
|
||||||
|
)
|
||||||
|
val fileTransferWorkRequest = OneTimeWorkRequestBuilder<FileTransferWorker>()
|
||||||
|
.addTag(workTag)
|
||||||
|
.setInputData(workData)
|
||||||
|
.build()
|
||||||
|
workManager.enqueueUniqueWork(
|
||||||
|
workTag,
|
||||||
|
ExistingWorkPolicy.KEEP,
|
||||||
|
fileTransferWorkRequest
|
||||||
|
)
|
||||||
|
|
||||||
downloadInfo.downloadPath = fileUtil.formatPath(getDownloadLocation(type)!!)
|
downloadInfo.downloadPath = fileUtil.formatPath(getDownloadLocation(type)!!)
|
||||||
Log.e(TAG, downloadInfo.downloadPath)
|
Log.e(TAG, downloadInfo.downloadPath)
|
||||||
downloadInfo.downloadType = type
|
downloadInfo.downloadType = type
|
||||||
|
|
@ -416,10 +457,15 @@ class DownloaderService : Service() {
|
||||||
downloadInfo.downloadQueue = videos
|
downloadInfo.downloadQueue = videos
|
||||||
startDownload(videos)
|
startDownload(videos)
|
||||||
}) { e: Throwable? ->
|
}) { e: Throwable? ->
|
||||||
if (BuildConfig.DEBUG) Log.e(TAG, getString(R.string.failed_download), e)
|
tempFileDir.delete()
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Toast.makeText(context, e!!.message, Toast.LENGTH_LONG).show()
|
||||||
|
Log.e(TAG, getString(R.string.failed_download), e)
|
||||||
|
}
|
||||||
notificationUtil.updateDownloadNotification(
|
notificationUtil.updateDownloadNotification(
|
||||||
NotificationUtil.DOWNLOAD_NOTIFICATION_ID,
|
NotificationUtil.DOWNLOAD_NOTIFICATION_ID,
|
||||||
getString(R.string.failed_download), 0, 0, downloadQueue.peek()?.title
|
getString(R.string.failed_download), 0, 0, downloadQueue.peek()?.title,
|
||||||
|
NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
|
||||||
)
|
)
|
||||||
downloadInfo.downloadType = type
|
downloadInfo.downloadType = type
|
||||||
try {
|
try {
|
||||||
|
|
@ -445,9 +491,22 @@ class DownloaderService : Service() {
|
||||||
|
|
||||||
private fun startCommandDownload(text: String?) {
|
private fun startCommandDownload(text: String?) {
|
||||||
var text = text
|
var text = text
|
||||||
if (text!!.startsWith("yt-dlp")) {
|
if (text!!.startsWith("yt-dlp ")) {
|
||||||
text = text.substring(5).trim { it <= ' ' }
|
text = text.substring(6).trim { it <= ' ' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val sharedPreferences = context!!.getSharedPreferences("root_preferences", MODE_PRIVATE)
|
||||||
|
val downloadLocation = sharedPreferences.getString("command_path", getString(R.string.command_path))
|
||||||
|
|
||||||
|
var tempFileDir = File(cacheDir.absolutePath + "/command")
|
||||||
|
tempFileDir.delete()
|
||||||
|
tempFileDir.mkdir()
|
||||||
|
|
||||||
|
if (downloadLocation.equals(getString(R.string.command_path))){
|
||||||
|
tempFileDir = File(getString(R.string.command_path))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
val request = YoutubeDLRequest(emptyList())
|
val request = YoutubeDLRequest(emptyList())
|
||||||
val commandRegex = "\"([^\"]*)\"|(\\S+)"
|
val commandRegex = "\"([^\"]*)\"|(\\S+)"
|
||||||
val m = Pattern.compile(commandRegex).matcher(text)
|
val m = Pattern.compile(commandRegex).matcher(text)
|
||||||
|
|
@ -458,8 +517,7 @@ class DownloaderService : Service() {
|
||||||
request.addOption(m.group(2))
|
request.addOption(m.group(2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val sharedPreferences = context!!.getSharedPreferences("root_preferences", MODE_PRIVATE)
|
request.addOption("-o", tempFileDir.absolutePath + "/%(title)s.%(ext)s")
|
||||||
request.addOption("-o", filesDir.absolutePath + "/%(title)s.%(ext)s")
|
|
||||||
val disposable = Observable.fromCallable {
|
val disposable = Observable.fromCallable {
|
||||||
YoutubeDL.getInstance().execute(request, downloadProcessID, callback)
|
YoutubeDL.getInstance().execute(request, downloadProcessID, callback)
|
||||||
}
|
}
|
||||||
|
|
@ -467,12 +525,22 @@ class DownloaderService : Service() {
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe({ youtubeDLResponse: YoutubeDLResponse ->
|
.subscribe({ youtubeDLResponse: YoutubeDLResponse ->
|
||||||
downloadInfo.outputLine = youtubeDLResponse.out
|
downloadInfo.outputLine = youtubeDLResponse.out
|
||||||
val downloadsDir = sharedPreferences.getString("command_path", getString(R.string.command_path))
|
|
||||||
val destDir = Uri.parse(downloadsDir).run {
|
val workTag = SystemClock.uptimeMillis().toString()
|
||||||
DocumentsContract.buildChildDocumentsUriUsingTree(this, DocumentsContract.getTreeDocumentId(this))
|
val workData = workDataOf(
|
||||||
}
|
originDir to tempFileDir.absolutePath,
|
||||||
fileUtil.moveFile(filesDir, context!!, destDir)
|
downLocation to downloadLocation,
|
||||||
downloadInfo.outputLine += "\n\nMoved File to ${fileUtil.formatPath(downloadsDir!!)}"
|
title to ""
|
||||||
|
)
|
||||||
|
val fileTransferWorkRequest = OneTimeWorkRequestBuilder<FileTransferWorker>()
|
||||||
|
.addTag(workTag)
|
||||||
|
.setInputData(workData)
|
||||||
|
.build()
|
||||||
|
workManager.enqueueUniqueWork(
|
||||||
|
workTag,
|
||||||
|
ExistingWorkPolicy.KEEP,
|
||||||
|
fileTransferWorkRequest
|
||||||
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (activity in activities.keys) {
|
for (activity in activities.keys) {
|
||||||
|
|
@ -489,6 +557,7 @@ class DownloaderService : Service() {
|
||||||
}
|
}
|
||||||
}) { e: Throwable ->
|
}) { e: Throwable ->
|
||||||
downloadInfo.outputLine = e.message
|
downloadInfo.outputLine = e.message
|
||||||
|
tempFileDir.delete()
|
||||||
try {
|
try {
|
||||||
for (activity in activities.keys) {
|
for (activity in activities.keys) {
|
||||||
activity.runOnUiThread {
|
activity.runOnUiThread {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import com.deniscerri.ytdlnis.service.IDownloaderService
|
||||||
import com.deniscerri.ytdlnis.util.UpdateUtil
|
import com.deniscerri.ytdlnis.util.UpdateUtil
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
|
import java.io.File
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.io.Reader
|
import java.io.Reader
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
@ -315,6 +316,17 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun createDefaultFolders(){
|
||||||
|
val audio = File(getString(R.string.music_path))
|
||||||
|
val video = File(getString(R.string.video_path))
|
||||||
|
val command = File(getString(R.string.command_path))
|
||||||
|
|
||||||
|
audio.mkdirs()
|
||||||
|
video.mkdirs()
|
||||||
|
command.mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(
|
override fun onRequestPermissionsResult(
|
||||||
requestCode: Int,
|
requestCode: Int,
|
||||||
permissions: Array<String>,
|
permissions: Array<String>,
|
||||||
|
|
@ -325,6 +337,8 @@ class MainActivity : AppCompatActivity() {
|
||||||
for (i in permissions.indices) {
|
for (i in permissions.indices) {
|
||||||
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
|
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
|
||||||
createPermissionRequestDialog()
|
createPermissionRequestDialog()
|
||||||
|
}else{
|
||||||
|
createDefaultFolders()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,14 @@ public class CustomCommandActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDownloadProgress(DownloadInfo info) {
|
public void onDownloadProgress(DownloadInfo info) {
|
||||||
output.append("\n" + info.getOutputLine() + "\n");
|
String newInfo = info.getOutputLine();
|
||||||
|
if (newInfo.contains("[download]")){
|
||||||
|
String temp = output.getText().toString();
|
||||||
|
output.setText(
|
||||||
|
temp.substring(0, temp.lastIndexOf(System.getProperty("line.separator")) - 2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
output.append(info.getOutputLine() + " \n");
|
||||||
output.scrollTo(0, output.getHeight());
|
output.scrollTo(0, output.getHeight());
|
||||||
scrollView.fullScroll(View.FOCUS_DOWN);
|
scrollView.fullScroll(View.FOCUS_DOWN);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,7 @@ public class DownloadsFragment extends Fragment implements DownloadsRecyclerView
|
||||||
}
|
}
|
||||||
|
|
||||||
databaseManager = new DatabaseManager(context);
|
databaseManager = new DatabaseManager(context);
|
||||||
|
//TODO observe file transfer worker here and then update, but first you need to turn this to kotlin and implement proper mvvm
|
||||||
try {
|
try {
|
||||||
item.setDownloadedTime(downloadedTime);
|
item.setDownloadedTime(downloadedTime);
|
||||||
item.setDownloadPath(downloadPath);
|
item.setDownloadPath(downloadPath);
|
||||||
|
|
|
||||||
|
|
@ -85,10 +85,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
editor.putInt("concurrent_fragments", concurrentFragments!!.value)
|
editor.putInt("concurrent_fragments", concurrentFragments!!.value)
|
||||||
editor.putString("limit_rate", limitRate!!.text)
|
editor.putString("limit_rate", limitRate!!.text)
|
||||||
editor.putBoolean("aria2", aria2!!.isChecked)
|
editor.putBoolean("aria2", aria2!!.isChecked)
|
||||||
editor.putString(
|
editor.putStringSet("sponsorblock_filters", sponsorblockFilters!!.values)
|
||||||
"sponsorblock_filter",
|
|
||||||
java.lang.String.join(",", sponsorblockFilters!!.values)
|
|
||||||
)
|
|
||||||
editor.putBoolean("embed_subtitles", embedSubtitles!!.isChecked)
|
editor.putBoolean("embed_subtitles", embedSubtitles!!.isChecked)
|
||||||
editor.putBoolean("embed_thumbnail", embedThumbnail!!.isChecked)
|
editor.putBoolean("embed_thumbnail", embedThumbnail!!.isChecked)
|
||||||
editor.putBoolean("add_chapters", addChapters!!.isChecked)
|
editor.putBoolean("add_chapters", addChapters!!.isChecked)
|
||||||
|
|
@ -178,7 +175,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
sponsorblockFilters!!.onPreferenceChangeListener =
|
sponsorblockFilters!!.onPreferenceChangeListener =
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any? ->
|
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any? ->
|
||||||
sponsorblockFilters!!.values = newValue as Set<String?>?
|
sponsorblockFilters!!.values = newValue as Set<String?>?
|
||||||
editor.putString("sponsorblock_filter", java.lang.String.join(",", newValue))
|
editor.putStringSet("sponsorblock_filters", newValue)
|
||||||
editor.apply()
|
editor.apply()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,17 @@ package com.deniscerri.ytdlnis.util
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.media.MediaScannerConnection
|
import android.media.MediaScannerConnection
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.FileUtils
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
|
import android.widget.Toast
|
||||||
import com.deniscerri.ytdlnis.DownloaderService
|
import com.deniscerri.ytdlnis.DownloaderService
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.Video
|
import com.deniscerri.ytdlnis.database.Video
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
|
|
@ -77,9 +81,8 @@ class FileUtil() {
|
||||||
|
|
||||||
return context.getString(R.string.unfound_file);
|
return context.getString(R.string.unfound_file);
|
||||||
}
|
}
|
||||||
|
fun moveFile(originDir : File, context:Context, destDir : Uri, progress: (p: Int) -> Unit){
|
||||||
fun moveFile(originDir : File, context:Context, destDir : Uri){
|
originDir.listFiles()?.forEach {
|
||||||
originDir.listFiles().forEach {
|
|
||||||
val mimeType =
|
val mimeType =
|
||||||
MimeTypeMap.getSingleton().getMimeTypeFromExtension(it.extension) ?: "*/*"
|
MimeTypeMap.getSingleton().getMimeTypeFromExtension(it.extension) ?: "*/*"
|
||||||
|
|
||||||
|
|
@ -96,13 +99,28 @@ class FileUtil() {
|
||||||
) ?: return@forEach
|
) ?: return@forEach
|
||||||
|
|
||||||
val inputStream = it.inputStream()
|
val inputStream = it.inputStream()
|
||||||
val outputStream =
|
val outputStream = context.contentResolver.openOutputStream(destUri) ?: return@forEach
|
||||||
context.contentResolver.openOutputStream(destUri) ?: return@forEach
|
val fileLength = it.length()
|
||||||
inputStream.copyTo(outputStream)
|
var bytesCopied: Long = 0
|
||||||
|
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
||||||
|
var bytes = inputStream.read(buffer)
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (bytes >= 0) {
|
||||||
|
outputStream.write(buffer, 0, bytes)
|
||||||
|
bytesCopied += bytes
|
||||||
|
progress((bytesCopied * 100 / fileLength).toInt())
|
||||||
|
bytes = inputStream.read(buffer)
|
||||||
|
}
|
||||||
|
}catch (e : Exception){
|
||||||
|
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
inputStream.close()
|
inputStream.close()
|
||||||
outputStream.close()
|
outputStream.close()
|
||||||
|
|
||||||
it.delete()
|
it.delete()
|
||||||
}
|
}
|
||||||
|
originDir.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -13,8 +13,9 @@ import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.receiver.NotificationReceiver
|
import com.deniscerri.ytdlnis.receiver.NotificationReceiver
|
||||||
|
|
||||||
class NotificationUtil(var context: Context) {
|
class NotificationUtil(var context: Context) {
|
||||||
private var notificationBuilder: NotificationCompat.Builder =
|
private val downloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_SERVICE_CHANNEL_ID)
|
||||||
NotificationCompat.Builder(context, DOWNLOAD_SERVICE_CHANNEL_ID)
|
private val commandDownloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID)
|
||||||
|
private val fileTransferNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, FILE_TRANSFER_CHANNEL_ID)
|
||||||
private val notificationManager: NotificationManager = context.getSystemService(NotificationManager::class.java)
|
private val notificationManager: NotificationManager = context.getSystemService(NotificationManager::class.java)
|
||||||
|
|
||||||
fun createNotificationChannel() {
|
fun createNotificationChannel() {
|
||||||
|
|
@ -38,13 +39,63 @@ class NotificationUtil(var context: Context) {
|
||||||
channel = NotificationChannel(COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID, name, importance)
|
channel = NotificationChannel(COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID, name, importance)
|
||||||
channel.description = description
|
channel.description = description
|
||||||
notificationManager.createNotificationChannel(channel)
|
notificationManager.createNotificationChannel(channel)
|
||||||
|
|
||||||
|
//file transfers
|
||||||
|
name = context.getString(R.string.file_transfer_notification_channel_name)
|
||||||
|
description = context.getString(R.string.file_transfer_notification_channel_description)
|
||||||
|
channel = NotificationChannel(FILE_TRANSFER_CHANNEL_ID, name, importance)
|
||||||
|
channel.description = description
|
||||||
|
notificationManager.createNotificationChannel(channel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createFileTransferNotification(
|
||||||
|
pendingIntent: PendingIntent?,
|
||||||
|
title: String?,
|
||||||
|
) : Notification {
|
||||||
|
val intent = Intent(context, NotificationReceiver::class.java)
|
||||||
|
|
||||||
|
return fileTransferNotificationBuilder
|
||||||
|
.setContentTitle(title)
|
||||||
|
.setCategory(Notification.CATEGORY_PROGRESS)
|
||||||
|
.setSmallIcon(R.drawable.ic_app_icon)
|
||||||
|
.setContentText("")
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
|
.setProgress(PROGRESS_MAX, PROGRESS_CURR, false)
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
|
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateFileTransferNotification(
|
||||||
|
id: Int,
|
||||||
|
progress: Int
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
fileTransferNotificationBuilder.setProgress(100, progress, false)
|
||||||
|
notificationManager.notify(id, fileTransferNotificationBuilder.build())
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBuilder(channel: String) : NotificationCompat.Builder {
|
||||||
|
when(channel) {
|
||||||
|
DOWNLOAD_SERVICE_CHANNEL_ID -> { return downloadNotificationBuilder}
|
||||||
|
COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID -> { return commandDownloadNotificationBuilder }
|
||||||
|
FILE_TRANSFER_CHANNEL_ID -> { return fileTransferNotificationBuilder }
|
||||||
|
}
|
||||||
|
return downloadNotificationBuilder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun createDownloadServiceNotification(
|
fun createDownloadServiceNotification(
|
||||||
pendingIntent: PendingIntent?,
|
pendingIntent: PendingIntent?,
|
||||||
title: String?
|
title: String?,
|
||||||
|
channel: String
|
||||||
): Notification {
|
): Notification {
|
||||||
|
val notificationBuilder = getBuilder(channel)
|
||||||
|
|
||||||
val intent = Intent(context, NotificationReceiver::class.java)
|
val intent = Intent(context, NotificationReceiver::class.java)
|
||||||
intent.putExtra("cancel", "")
|
intent.putExtra("cancel", "")
|
||||||
val cancelNotificationPendingIntent = PendingIntent.getBroadcast(
|
val cancelNotificationPendingIntent = PendingIntent.getBroadcast(
|
||||||
|
|
@ -80,17 +131,21 @@ class NotificationUtil(var context: Context) {
|
||||||
desc: String,
|
desc: String,
|
||||||
progress: Int,
|
progress: Int,
|
||||||
queue: Int,
|
queue: Int,
|
||||||
title: String?
|
title: String?,
|
||||||
|
channel : String
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val notificationBuilder = getBuilder(channel)
|
||||||
var contentText = ""
|
var contentText = ""
|
||||||
if (queue > 1) contentText += """${queue - 1} ${context.getString(R.string.items_left)}"""
|
if (queue > 1) contentText += """${queue - 1} ${context.getString(R.string.items_left)}""" + "\n"
|
||||||
contentText += desc.replace("\\[.*?\\] ".toRegex(), "")
|
contentText += desc.replace("\\[.*?\\] ".toRegex(), "")
|
||||||
try {
|
try {
|
||||||
notificationBuilder.setProgress(100, progress, false)
|
notificationBuilder.setProgress(100, progress, false)
|
||||||
.setContentTitle(title)
|
.setContentTitle(title)
|
||||||
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
|
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
|
||||||
notificationManager.notify(id, notificationBuilder.build())
|
notificationManager.notify(id, notificationBuilder.build())
|
||||||
} catch (ignored: Exception) {
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,6 +158,7 @@ class NotificationUtil(var context: Context) {
|
||||||
const val DOWNLOAD_NOTIFICATION_ID = 1
|
const val DOWNLOAD_NOTIFICATION_ID = 1
|
||||||
const val COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID = "2"
|
const val COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID = "2"
|
||||||
const val COMMAND_DOWNLOAD_NOTIFICATION_ID = 2
|
const val COMMAND_DOWNLOAD_NOTIFICATION_ID = 2
|
||||||
|
const val FILE_TRANSFER_CHANNEL_ID = "3"
|
||||||
private const val PROGRESS_MAX = 100
|
private const val PROGRESS_MAX = 100
|
||||||
private const val PROGRESS_CURR = 0
|
private const val PROGRESS_CURR = 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import java.io.InputStreamReader
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class UpdateUtil(var context: Context) {
|
class UpdateUtil(var context: Context) {
|
||||||
private val tag = "UpdateUtil"
|
private val tag = "UpdateUtil"
|
||||||
|
|
@ -163,11 +164,12 @@ class UpdateUtil(var context: Context) {
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe({ status: UpdateStatus ->
|
.subscribe({ status: UpdateStatus ->
|
||||||
when (status) {
|
when (status) {
|
||||||
UpdateStatus.DONE -> Toast.makeText(
|
UpdateStatus.DONE ->
|
||||||
context,
|
Toast.makeText(
|
||||||
context.getString(R.string.ytld_update_success),
|
context,
|
||||||
Toast.LENGTH_LONG
|
context.getString(R.string.ytld_update_success),
|
||||||
).show()
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
UpdateStatus.ALREADY_UP_TO_DATE -> Toast.makeText(
|
UpdateStatus.ALREADY_UP_TO_DATE -> Toast.makeText(
|
||||||
context,
|
context,
|
||||||
context.getString(R.string.you_are_in_latest_version),
|
context.getString(R.string.you_are_in_latest_version),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.deniscerri.ytdlnis.work
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.SystemClock
|
||||||
|
import android.provider.DocumentsContract
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.ForegroundInfo
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import com.deniscerri.ytdlnis.MainActivity
|
||||||
|
import com.deniscerri.ytdlnis.page.CustomCommandActivity
|
||||||
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
|
import com.deniscerri.ytdlnis.util.NotificationUtil
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class FileTransferWorker(
|
||||||
|
private val context: Context,
|
||||||
|
workerParams: WorkerParameters
|
||||||
|
) : CoroutineWorker(context, workerParams) {
|
||||||
|
|
||||||
|
override suspend fun doWork(): Result {
|
||||||
|
val originDir = File(inputData.getString(originDir)!!)
|
||||||
|
val downLocation = inputData.getString(downLocation)
|
||||||
|
val fileTitle = inputData.getString(title) ?: ""
|
||||||
|
val destDir = Uri.parse(downLocation).run {
|
||||||
|
DocumentsContract.buildChildDocumentsUriUsingTree(this, DocumentsContract.getTreeDocumentId(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
val fileUtil = FileUtil()
|
||||||
|
|
||||||
|
val notificationUtil = NotificationUtil(context)
|
||||||
|
val intent = Intent(context, MainActivity::class.java)
|
||||||
|
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
val title = "Moving $fileTitle to ${fileUtil.formatPath(downLocation!!)}"
|
||||||
|
val id : Int = SystemClock.uptimeMillis().toInt()
|
||||||
|
val notification = notificationUtil.createFileTransferNotification(pendingIntent, title)
|
||||||
|
val foregroundInfo = ForegroundInfo(id, notification)
|
||||||
|
setForeground(foregroundInfo)
|
||||||
|
|
||||||
|
fileUtil.moveFile(originDir, context, destDir){ progress ->
|
||||||
|
notificationUtil.updateFileTransferNotification(id, progress)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val downLocation = "downLocation"
|
||||||
|
const val originDir = "originDir"
|
||||||
|
const val title = "title"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -49,7 +49,7 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/result_title"
|
android:id="@+id/result_title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="100dp"
|
android:layout_height="120dp"
|
||||||
android:paddingTop="10dp"
|
android:paddingTop="10dp"
|
||||||
android:paddingEnd="20dp"
|
android:paddingEnd="20dp"
|
||||||
android:paddingBottom="20dp"
|
android:paddingBottom="20dp"
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,9 @@
|
||||||
<string name="choose_range_desc">Pick a range to download from the playlist (to not download everything).</string>
|
<string name="choose_range_desc">Pick a range to download from the playlist (to not download everything).</string>
|
||||||
<string name="last_cant_be_smaller_than_first">Last index cannot be smaller than the first.</string>
|
<string name="last_cant_be_smaller_than_first">Last index cannot be smaller than the first.</string>
|
||||||
<string name="first_cant_be_larger_than_last">First index cannot be larger than the last.</string>
|
<string name="first_cant_be_larger_than_last">First index cannot be larger than the last.</string>
|
||||||
<string name="music_path" translatable="false">/storage/emulated/0/Download</string>
|
<string name="music_path" translatable="false">/storage/emulated/0/Download/YTDLnis/Audio</string>
|
||||||
<string name="video_path" translatable="false">/storage/emulated/0/Download</string>
|
<string name="video_path" translatable="false">/storage/emulated/0/Download/YTDLnis/Video</string>
|
||||||
<string name="command_path" translatable="false">/storage/emulated/0/Download</string>
|
<string name="command_path" translatable="false">/storage/emulated/0/Download/YTDLnis/Command</string>
|
||||||
<string name="concurrent_fragments_summary">Number of fragments of a DASH/HLS native video to download concurrently</string>
|
<string name="concurrent_fragments_summary">Number of fragments of a DASH/HLS native video to download concurrently</string>
|
||||||
<string name="concurrent_fragments">Concurrent Fragments</string>
|
<string name="concurrent_fragments">Concurrent Fragments</string>
|
||||||
<string name="downloading">Downloading</string>
|
<string name="downloading">Downloading</string>
|
||||||
|
|
@ -149,4 +149,6 @@
|
||||||
<string name="sponsorblock_reminders">Subscription Reminders</string>
|
<string name="sponsorblock_reminders">Subscription Reminders</string>
|
||||||
<string name="select_sponsorblock_filtering">Select SponsorBlock Filtering</string>
|
<string name="select_sponsorblock_filtering">Select SponsorBlock Filtering</string>
|
||||||
<string name="unfound_file" translatable="false">/404.404</string>
|
<string name="unfound_file" translatable="false">/404.404</string>
|
||||||
|
<string name="file_transfer_notification_channel_name">File Transfers</string>
|
||||||
|
<string name="file_transfer_notification_channel_description">Notification showing the progress of moving files from the temporary folder to set destination</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
|
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
|
||||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||||
<item name="android:color">@android:color/transparent</item>
|
<item name="android:color">@android:color/transparent</item>
|
||||||
<item name="android:windowIsTranslucent">true</item>
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppTheme" parent="AppDefaultTheme" />
|
<style name="AppTheme" parent="AppDefaultTheme" />
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ buildscript {
|
||||||
workVer = "2.7.1"
|
workVer = "2.7.1"
|
||||||
composeVer = "1.3.2"
|
composeVer = "1.3.2"
|
||||||
kotlinVer = "1.7.20"
|
kotlinVer = "1.7.20"
|
||||||
|
coroutineVer = "1.6.4"
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue