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:
Denis Çerri 2023-01-08 00:32:46 +01:00
parent 6a1ed4fc7e
commit 51ff22abcd
No known key found for this signature in database
GPG key ID: 96B3554AF5B193EE
16 changed files with 295 additions and 68 deletions

View file

@ -119,7 +119,7 @@ dependencies {
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.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-ktx:2.4.3"
@ -127,7 +127,8 @@ dependencies {
androidTestImplementation "androidx.room:room-testing:2.4.3"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
implementation "androidx.compose.runtime:runtime:$composeVer"
androidTestImplementation "com.google.truth:truth:1.1.3"
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVer"
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVer"
}

View file

@ -22,7 +22,6 @@
android:theme="@style/AppTheme" >
<activity android:name=".MainActivity"
android:windowSoftInputMode="adjustResize"
android:label="@string/download"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View file

@ -2,10 +2,15 @@ package com.deniscerri.ytdlnis
import android.annotation.SuppressLint
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.widget.Toast
import androidx.preference.PreferenceManager
import com.deniscerri.ytdlnis.util.NotificationUtil
import com.deniscerri.ytdlnis.util.UpdateUtil
import com.google.android.material.color.DynamicColors
import com.yausername.aria2c.Aria2c
import com.yausername.ffmpeg.FFmpeg
@ -17,6 +22,7 @@ import io.reactivex.exceptions.UndeliverableException
import io.reactivex.observers.DisposableCompletableObserver
import io.reactivex.plugins.RxJavaPlugins
import io.reactivex.schedulers.Schedulers
import java.io.File
class App : Application() {
override fun onCreate() {

View file

@ -7,10 +7,17 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.os.SystemClock
import android.provider.DocumentsContract
import android.provider.Settings.Global
import android.util.Log
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.page.CustomCommandActivity
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.util.FileUtil
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.YoutubeDL
import com.yausername.youtubedl_android.YoutubeDLRequest
@ -26,6 +37,7 @@ import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.*
import java.io.File
import java.io.FileOutputStream
import java.util.*
@ -46,6 +58,8 @@ class DownloaderService : Service() {
private var context: Context? = null
var downloadProcessID = "processID"
private var downloadNotificationID = 0
private lateinit var notificationChannelID : String
private lateinit var workManager : WorkManager
private val callback: (Float, Long?, String?) -> Unit =
{ progress: Float, _: Long?, line: String? ->
@ -58,7 +72,8 @@ class DownloaderService : Service() {
}
notificationUtil.updateDownloadNotification(
downloadNotificationID,
line!!, progress.toInt(), downloadQueue.size, title
line!!, progress.toInt(), downloadQueue.size, title,
notificationChannelID
)
try {
for (activity in activities.keys) {
@ -74,17 +89,11 @@ class DownloaderService : Service() {
} 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() {
super.onCreate()
context = this
this.workManager = WorkManager.getInstance(context!!.applicationContext)
}
override fun onBind(intent: Intent): IBinder? {
@ -93,9 +102,9 @@ class DownloaderService : Service() {
if (intent.getBooleanExtra("rebind", false)) {
return binder
}
val id = intent.getIntExtra("id", 1)
when (id) {
when (val id = intent.getIntExtra("id", 1)) {
NotificationUtil.DOWNLOAD_NOTIFICATION_ID -> {
notificationChannelID = NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
theIntent = Intent(this, MainActivity::class.java)
pendingIntent =
PendingIntent.getActivity(this, 0, theIntent, PendingIntent.FLAG_IMMUTABLE)
@ -106,21 +115,23 @@ class DownloaderService : Service() {
downloadInfo.downloadQueue = downloadQueue
val title = downloadInfo.video.title
val notification =
App.notificationUtil.createDownloadServiceNotification(pendingIntent, title)
App.notificationUtil.createDownloadServiceNotification(pendingIntent, title, NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID)
startForeground(downloadNotificationID, notification)
startDownload(downloadQueue)
}
NotificationUtil.COMMAND_DOWNLOAD_NOTIFICATION_ID -> {
notificationChannelID = NotificationUtil.COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID
theIntent = Intent(this, CustomCommandActivity::class.java)
pendingIntent =
PendingIntent.getActivity(this, 0, theIntent, PendingIntent.FLAG_IMMUTABLE)
downloadNotificationID = id
val command = intent.getStringExtra("command")
val command_notification = App.notificationUtil.createDownloadServiceNotification(
val commandNotification = App.notificationUtil.createDownloadServiceNotification(
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)
}
}
@ -129,7 +140,11 @@ class DownloaderService : Service() {
override fun onDestroy() {
super.onDestroy()
stopForeground(true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
stopForeground(STOP_FOREGROUND_REMOVE)
}else{
stopForeground(true)
}
stopSelf()
}
@ -294,6 +309,11 @@ class DownloaderService : Service() {
val url = video.getURL()
val request = YoutubeDLRequest(url)
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 aria2 = sharedPreferences.getBoolean("aria2", false)
@ -312,11 +332,16 @@ class DownloaderService : Service() {
request.addOption("--convert-thumbnails", "png")
}
request.addOption("--no-mtime")
val sponsorBlockFilters = sharedPreferences.getString("sponsorblock_filter", "")
if (!sponsorBlockFilters!!.isEmpty()) {
request.addOption("--sponsorblock-remove", sponsorBlockFilters)
val sponsorBlockFilters = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
if (sponsorBlockFilters!!.isNotEmpty()) {
val filters = java.lang.String.join(",", sponsorBlockFilters)
request.addOption("--sponsorblock-remove", filters)
}
if (type == "audio") {
if (downloadLocation.equals(getString(R.string.music_path))){
tempFileDir = File(getString(R.string.music_path))
}
request.addOption("-x")
var format = video.audioFormat
if (format == null) format = sharedPreferences.getString("audio_format", "")
@ -348,8 +373,12 @@ class DownloaderService : Service() {
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") {
if (downloadLocation.equals(getString(R.string.video_path))){
tempFileDir = File(getString(R.string.video_path))
}
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
if (addChapters) {
request.addOption("--sponsorblock-mark", "all")
@ -380,7 +409,7 @@ class DownloaderService : Service() {
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 {
@ -389,10 +418,22 @@ class DownloaderService : Service() {
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
val destDir = Uri.parse(getDownloadLocation(type)).run {
DocumentsContract.buildChildDocumentsUriUsingTree(this, DocumentsContract.getTreeDocumentId(this))
}
fileUtil.moveFile(filesDir, context!!, destDir)
val workTag = video.getURL()
val workData = workDataOf(
originDir to tempFileDir.absolutePath,
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)!!)
Log.e(TAG, downloadInfo.downloadPath)
downloadInfo.downloadType = type
@ -416,10 +457,15 @@ class DownloaderService : Service() {
downloadInfo.downloadQueue = videos
startDownload(videos)
}) { 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.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
try {
@ -445,9 +491,22 @@ class DownloaderService : Service() {
private fun startCommandDownload(text: String?) {
var text = text
if (text!!.startsWith("yt-dlp")) {
text = text.substring(5).trim { it <= ' ' }
if (text!!.startsWith("yt-dlp ")) {
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 commandRegex = "\"([^\"]*)\"|(\\S+)"
val m = Pattern.compile(commandRegex).matcher(text)
@ -458,8 +517,7 @@ class DownloaderService : Service() {
request.addOption(m.group(2))
}
}
val sharedPreferences = context!!.getSharedPreferences("root_preferences", MODE_PRIVATE)
request.addOption("-o", filesDir.absolutePath + "/%(title)s.%(ext)s")
request.addOption("-o", tempFileDir.absolutePath + "/%(title)s.%(ext)s")
val disposable = Observable.fromCallable {
YoutubeDL.getInstance().execute(request, downloadProcessID, callback)
}
@ -467,12 +525,22 @@ class DownloaderService : Service() {
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ youtubeDLResponse: YoutubeDLResponse ->
downloadInfo.outputLine = youtubeDLResponse.out
val downloadsDir = sharedPreferences.getString("command_path", getString(R.string.command_path))
val destDir = Uri.parse(downloadsDir).run {
DocumentsContract.buildChildDocumentsUriUsingTree(this, DocumentsContract.getTreeDocumentId(this))
}
fileUtil.moveFile(filesDir, context!!, destDir)
downloadInfo.outputLine += "\n\nMoved File to ${fileUtil.formatPath(downloadsDir!!)}"
val workTag = SystemClock.uptimeMillis().toString()
val workData = workDataOf(
originDir to tempFileDir.absolutePath,
downLocation to downloadLocation,
title to ""
)
val fileTransferWorkRequest = OneTimeWorkRequestBuilder<FileTransferWorker>()
.addTag(workTag)
.setInputData(workData)
.build()
workManager.enqueueUniqueWork(
workTag,
ExistingWorkPolicy.KEEP,
fileTransferWorkRequest
)
try {
for (activity in activities.keys) {
@ -489,6 +557,7 @@ class DownloaderService : Service() {
}
}) { e: Throwable ->
downloadInfo.outputLine = e.message
tempFileDir.delete()
try {
for (activity in activities.keys) {
activity.runOnUiThread {

View file

@ -30,6 +30,7 @@ import com.deniscerri.ytdlnis.service.IDownloaderService
import com.deniscerri.ytdlnis.util.UpdateUtil
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader
import java.io.Reader
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(
requestCode: Int,
permissions: Array<String>,
@ -325,6 +337,8 @@ class MainActivity : AppCompatActivity() {
for (i in permissions.indices) {
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
createPermissionRequestDialog()
}else{
createDefaultFolders()
}
}
}

View file

@ -76,7 +76,14 @@ public class CustomCommandActivity extends AppCompatActivity {
}
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());
scrollView.fullScroll(View.FOCUS_DOWN);
}

View file

@ -162,6 +162,7 @@ public class DownloadsFragment extends Fragment implements DownloadsRecyclerView
}
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 {
item.setDownloadedTime(downloadedTime);
item.setDownloadPath(downloadPath);

View file

@ -85,10 +85,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
editor.putInt("concurrent_fragments", concurrentFragments!!.value)
editor.putString("limit_rate", limitRate!!.text)
editor.putBoolean("aria2", aria2!!.isChecked)
editor.putString(
"sponsorblock_filter",
java.lang.String.join(",", sponsorblockFilters!!.values)
)
editor.putStringSet("sponsorblock_filters", sponsorblockFilters!!.values)
editor.putBoolean("embed_subtitles", embedSubtitles!!.isChecked)
editor.putBoolean("embed_thumbnail", embedThumbnail!!.isChecked)
editor.putBoolean("add_chapters", addChapters!!.isChecked)
@ -178,7 +175,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
sponsorblockFilters!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any? ->
sponsorblockFilters!!.values = newValue as Set<String?>?
editor.putString("sponsorblock_filter", java.lang.String.join(",", newValue))
editor.putStringSet("sponsorblock_filters", newValue)
editor.apply()
true
}

View file

@ -3,13 +3,17 @@ package com.deniscerri.ytdlnis.util
import android.content.Context
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.FileUtils
import android.provider.DocumentsContract
import android.util.Log
import android.webkit.MimeTypeMap
import android.widget.Toast
import com.deniscerri.ytdlnis.DownloaderService
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.Video
import java.io.File
import java.io.InputStream
import java.io.OutputStream
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
@ -77,9 +81,8 @@ class FileUtil() {
return context.getString(R.string.unfound_file);
}
fun moveFile(originDir : File, context:Context, destDir : Uri){
originDir.listFiles().forEach {
fun moveFile(originDir : File, context:Context, destDir : Uri, progress: (p: Int) -> Unit){
originDir.listFiles()?.forEach {
val mimeType =
MimeTypeMap.getSingleton().getMimeTypeFromExtension(it.extension) ?: "*/*"
@ -96,13 +99,28 @@ class FileUtil() {
) ?: return@forEach
val inputStream = it.inputStream()
val outputStream =
context.contentResolver.openOutputStream(destUri) ?: return@forEach
inputStream.copyTo(outputStream)
val outputStream = context.contentResolver.openOutputStream(destUri) ?: return@forEach
val fileLength = it.length()
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()
outputStream.close()
it.delete()
}
originDir.delete()
}
}

View file

@ -13,8 +13,9 @@ import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.receiver.NotificationReceiver
class NotificationUtil(var context: Context) {
private var notificationBuilder: NotificationCompat.Builder =
NotificationCompat.Builder(context, DOWNLOAD_SERVICE_CHANNEL_ID)
private val downloadNotificationBuilder: NotificationCompat.Builder = 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)
fun createNotificationChannel() {
@ -38,13 +39,63 @@ class NotificationUtil(var context: Context) {
channel = NotificationChannel(COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID, name, importance)
channel.description = description
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(
pendingIntent: PendingIntent?,
title: String?
title: String?,
channel: String
): Notification {
val notificationBuilder = getBuilder(channel)
val intent = Intent(context, NotificationReceiver::class.java)
intent.putExtra("cancel", "")
val cancelNotificationPendingIntent = PendingIntent.getBroadcast(
@ -80,17 +131,21 @@ class NotificationUtil(var context: Context) {
desc: String,
progress: Int,
queue: Int,
title: String?
title: String?,
channel : String
) {
val notificationBuilder = getBuilder(channel)
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(), "")
try {
notificationBuilder.setProgress(100, progress, false)
.setContentTitle(title)
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
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 COMMAND_DOWNLOAD_SERVICE_CHANNEL_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_CURR = 0
}

View file

@ -24,6 +24,7 @@ import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
import java.util.concurrent.atomic.AtomicReference
import java.io.File
class UpdateUtil(var context: Context) {
private val tag = "UpdateUtil"
@ -163,11 +164,12 @@ class UpdateUtil(var context: Context) {
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ status: UpdateStatus ->
when (status) {
UpdateStatus.DONE -> Toast.makeText(
context,
context.getString(R.string.ytld_update_success),
Toast.LENGTH_LONG
).show()
UpdateStatus.DONE ->
Toast.makeText(
context,
context.getString(R.string.ytld_update_success),
Toast.LENGTH_LONG
).show()
UpdateStatus.ALREADY_UP_TO_DATE -> Toast.makeText(
context,
context.getString(R.string.you_are_in_latest_version),

View file

@ -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"
}
}

View file

@ -49,7 +49,7 @@
<TextView
android:id="@+id/result_title"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_height="120dp"
android:paddingTop="10dp"
android:paddingEnd="20dp"
android:paddingBottom="20dp"

View file

@ -65,9 +65,9 @@
<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="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="video_path" translatable="false">/storage/emulated/0/Download</string>
<string name="command_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/YTDLnis/Video</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">Concurrent Fragments</string>
<string name="downloading">Downloading</string>
@ -149,4 +149,6 @@
<string name="sponsorblock_reminders">Subscription Reminders</string>
<string name="select_sponsorblock_filtering">Select SponsorBlock Filtering</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>

View file

@ -5,7 +5,6 @@
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:color">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
</style>
<style name="AppTheme" parent="AppDefaultTheme" />

View file

@ -25,6 +25,7 @@ buildscript {
workVer = "2.7.1"
composeVer = "1.3.2"
kotlinVer = "1.7.20"
coroutineVer = "1.6.4"
}
repositories {
mavenCentral()