diff --git a/app/schemas/com.deniscerri.ytdlnis.database.DBManager/1.json b/app/schemas/com.deniscerri.ytdlnis.database.DBManager/1.json
deleted file mode 100644
index 599d0a37..00000000
--- a/app/schemas/com.deniscerri.ytdlnis.database.DBManager/1.json
+++ /dev/null
@@ -1,254 +0,0 @@
-{
- "formatVersion": 1,
- "database": {
- "version": 1,
- "identityHash": "e2280d10a40623d3a51ecf5205500351",
- "entities": [
- {
- "tableName": "results",
- "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `website` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL)",
- "fields": [
- {
- "fieldPath": "id",
- "columnName": "id",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "url",
- "columnName": "url",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "title",
- "columnName": "title",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "author",
- "columnName": "author",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "duration",
- "columnName": "duration",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "thumb",
- "columnName": "thumb",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "website",
- "columnName": "website",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "playlistTitle",
- "columnName": "playlistTitle",
- "affinity": "TEXT",
- "notNull": true
- }
- ],
- "primaryKey": {
- "columnNames": [
- "id"
- ],
- "autoGenerate": true
- },
- "indices": [],
- "foreignKeys": []
- },
- {
- "tableName": "history",
- "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `type` TEXT NOT NULL, `time` INTEGER NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL)",
- "fields": [
- {
- "fieldPath": "id",
- "columnName": "id",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "url",
- "columnName": "url",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "title",
- "columnName": "title",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "author",
- "columnName": "author",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "duration",
- "columnName": "duration",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "thumb",
- "columnName": "thumb",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "type",
- "columnName": "type",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "time",
- "columnName": "time",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "downloadPath",
- "columnName": "downloadPath",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "website",
- "columnName": "website",
- "affinity": "TEXT",
- "notNull": true
- }
- ],
- "primaryKey": {
- "columnNames": [
- "id"
- ],
- "autoGenerate": true
- },
- "indices": [],
- "foreignKeys": []
- },
- {
- "tableName": "downloads",
- "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `startTime` TEXT NOT NULL, `endTime` TEXT NOT NULL, `thumb` TEXT NOT NULL, `formatId` INTEGER NOT NULL, `formatDesc` TEXT NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `progress` REAL NOT NULL, `custom` INTEGER NOT NULL, `customTemplateId` INTEGER NOT NULL)",
- "fields": [
- {
- "fieldPath": "id",
- "columnName": "id",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "url",
- "columnName": "url",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "title",
- "columnName": "title",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "author",
- "columnName": "author",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "startTime",
- "columnName": "startTime",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "endTime",
- "columnName": "endTime",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "thumb",
- "columnName": "thumb",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "formatId",
- "columnName": "formatId",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "formatDesc",
- "columnName": "formatDesc",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "downloadPath",
- "columnName": "downloadPath",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "website",
- "columnName": "website",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "downloadSize",
- "columnName": "downloadSize",
- "affinity": "TEXT",
- "notNull": true
- },
- {
- "fieldPath": "progress",
- "columnName": "progress",
- "affinity": "REAL",
- "notNull": true
- },
- {
- "fieldPath": "custom",
- "columnName": "custom",
- "affinity": "INTEGER",
- "notNull": true
- },
- {
- "fieldPath": "customTemplateId",
- "columnName": "customTemplateId",
- "affinity": "INTEGER",
- "notNull": true
- }
- ],
- "primaryKey": {
- "columnNames": [
- "id"
- ],
- "autoGenerate": true
- },
- "indices": [],
- "foreignKeys": []
- }
- ],
- "views": [],
- "setupQueries": [
- "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
- "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e2280d10a40623d3a51ecf5205500351')"
- ]
- }
-}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 886e4b13..7ed8199e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -67,7 +67,7 @@
android:name=".DownloaderService"
android:enabled="true"
android:exported="false" />
-
+
+
+ @Query("SELECT * FROM commandTemplates WHERE id=:id LIMIT 1")
+ fun getTemplateById(id: Int) : CommandTemplate
+
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ suspend fun insert(item: CommandTemplate)
+
+ @Query("DELETE FROM commandTemplates")
+ suspend fun deleteAll()
+
+ @Delete
+ suspend fun delete(item: CommandTemplate)
+
+ @Update(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun update(item: CommandTemplate)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt
index 285799bb..f2c72d17 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt
@@ -10,10 +10,13 @@ import com.deniscerri.ytdlnis.util.FileUtil
interface DownloadDao {
@Query("SELECT * FROM downloads")
- fun getAllDownloads() : LiveData>
+ fun getAllDownloads() : List
+
+ @Query("SELECT * FROM downloads WHERE status='Queued'")
+ fun getQueuedDownloads() : List
@Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")
- suspend fun getDownloadById(id: Int) : DownloadItem
+ fun getDownloadById(id: Int) : DownloadItem
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(item: DownloadItem)
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/CommandTemplate.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/CommandTemplate.kt
new file mode 100644
index 00000000..96024e4a
--- /dev/null
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/CommandTemplate.kt
@@ -0,0 +1,13 @@
+package com.deniscerri.ytdlnis.database.models
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "downloads")
+data class CommandTemplate(
+ @PrimaryKey(autoGenerate = true)
+ val id: Int,
+ val title: String,
+ val content: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/DownloadItem.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/DownloadItem.kt
index b63bc725..7b9c3058 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/models/DownloadItem.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/DownloadItem.kt
@@ -1,5 +1,6 @@
package com.deniscerri.ytdlnis.database.models
+import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@@ -10,15 +11,21 @@ data class DownloadItem(
val url: String,
val title: String,
val author: String,
- val startTime: String,
- val endTime: String,
val thumb: String,
- val formatId: Int,
+ val duration: String,
+ val type: String,
+ val audioQualityId: String,
+ val videoQualityId: String,
+ val customTemplateId: Int,
+ val audioFormat: String,
+ val videoFormat: String,
val formatDesc: String,
+ @ColumnInfo(defaultValue = "0")
+ val removeAudio: Boolean,
val downloadPath: String,
val website: String,
val downloadSize: String,
- val progress: Float,
- val custom: Boolean,
- val customTemplateId: Int
+ val playlistTitle: String,
+ @ColumnInfo(defaultValue = "Queued")
+ var status: String
)
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/HistoryItem.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/HistoryItem.kt
index 3a841663..5783fc93 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/models/HistoryItem.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/HistoryItem.kt
@@ -5,8 +5,6 @@ import androidx.room.PrimaryKey
@Entity(tableName = "history")
data class HistoryItem(
- @PrimaryKey(autoGenerate = true)
- var id: Int,
val url: String,
val title: String,
val author: String,
@@ -16,5 +14,7 @@ data class HistoryItem(
val time: Long,
val downloadPath: String,
val website: String
- //val isQueuedDownload: Int
-)
\ No newline at end of file
+){
+ @PrimaryKey(autoGenerate = true)
+ var id: Int? = null
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt
index e9538190..852055b3 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt
@@ -5,7 +5,6 @@ import com.deniscerri.ytdlnis.database.dao.DownloadDao
import com.deniscerri.ytdlnis.database.models.DownloadItem
class DownloadRepository(private val downloadDao: DownloadDao) {
- val allDownloads : LiveData> = downloadDao.getAllDownloads()
suspend fun insert(item: DownloadItem){
downloadDao.insert(item)
@@ -19,5 +18,14 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
downloadDao.update(item)
}
+ suspend fun getQueuedDownloads() : List{
+ var items = downloadDao.getQueuedDownloads();
+ for (i in items){
+ i.status = "Downloading"
+ downloadDao.update(i)
+ }
+ return items
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/receiver/CancelDownloadNotificationReceiver.kt b/app/src/main/java/com/deniscerri/ytdlnis/receiver/CancelDownloadNotificationReceiver.kt
new file mode 100644
index 00000000..64f09ae5
--- /dev/null
+++ b/app/src/main/java/com/deniscerri/ytdlnis/receiver/CancelDownloadNotificationReceiver.kt
@@ -0,0 +1,14 @@
+package com.deniscerri.ytdlnis.receiver
+
+import android.content.*
+import androidx.work.WorkManager
+
+class CancelDownloadNotificationReceiver : BroadcastReceiver() {
+ override fun onReceive(c: Context, intent: Intent) {
+ val message = intent.getStringExtra("cancel")
+ val id = intent.getIntExtra("workID", 0)
+ if (message != null) {
+ WorkManager.getInstance(c).cancelAllWorkByTag(id.toString())
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/receiver/NotificationReceiver.kt b/app/src/main/java/com/deniscerri/ytdlnis/receiver/NotificationReceiver.kt
deleted file mode 100644
index d3e1f54c..00000000
--- a/app/src/main/java/com/deniscerri/ytdlnis/receiver/NotificationReceiver.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.deniscerri.ytdlnis.receiver
-
-import android.content.*
-import android.os.IBinder
-import com.deniscerri.ytdlnis.DownloaderService
-import com.deniscerri.ytdlnis.DownloaderService.LocalBinder
-import com.deniscerri.ytdlnis.service.IDownloaderService
-
-class NotificationReceiver : BroadcastReceiver() {
- var downloaderService: DownloaderService? = null
- private var iDownloaderService: IDownloaderService? = null
- private var context: Context? = null
- private val serviceConnection: ServiceConnection = object : ServiceConnection {
- override fun onServiceConnected(className: ComponentName, service: IBinder) {
- downloaderService = (service as LocalBinder).service
- iDownloaderService = service
- cancelDownload()
- }
-
- override fun onServiceDisconnected(componentName: ComponentName) {
- downloaderService = null
- iDownloaderService = null
- }
- }
-
- override fun onReceive(c: Context, intent: Intent) {
- context = c
- val message = intent.getStringExtra("cancel")
- if (message != null) {
- val serviceIntent = Intent(context!!.applicationContext, DownloaderService::class.java)
- serviceIntent.putExtra("rebind", true)
- context!!.applicationContext.bindService(
- serviceIntent,
- serviceConnection,
- Context.BIND_AUTO_CREATE
- )
- }
- }
-
- private fun cancelDownload() {
- try {
- iDownloaderService!!.cancelDownload(true)
- context!!.applicationContext.unbindService(serviceConnection)
- context!!.applicationContext.stopService(
- Intent(
- context!!.applicationContext,
- DownloaderService::class.java
- )
- )
- } catch (ignored: Exception) {
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/NotificationUtil.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/NotificationUtil.kt
index 6a005eea..5a3856dd 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/util/NotificationUtil.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/util/NotificationUtil.kt
@@ -10,7 +10,7 @@ import android.graphics.BitmapFactory
import android.os.Build
import androidx.core.app.NotificationCompat
import com.deniscerri.ytdlnis.R
-import com.deniscerri.ytdlnis.receiver.NotificationReceiver
+import com.deniscerri.ytdlnis.receiver.CancelDownloadNotificationReceiver
class NotificationUtil(var context: Context) {
private val downloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_SERVICE_CHANNEL_ID)
@@ -53,7 +53,7 @@ class NotificationUtil(var context: Context) {
pendingIntent: PendingIntent?,
title: String?,
) : Notification {
- val intent = Intent(context, NotificationReceiver::class.java)
+ val intent = Intent(context, CancelDownloadNotificationReceiver::class.java)
return fileTransferNotificationBuilder
.setContentTitle(title)
@@ -92,12 +92,14 @@ class NotificationUtil(var context: Context) {
fun createDownloadServiceNotification(
pendingIntent: PendingIntent?,
title: String?,
+ workID: Int,
channel: String
): Notification {
val notificationBuilder = getBuilder(channel)
- val intent = Intent(context, NotificationReceiver::class.java)
+ val intent = Intent(context, CancelDownloadNotificationReceiver::class.java)
intent.putExtra("cancel", "")
+ intent.putExtra("workID", workID)
val cancelNotificationPendingIntent = PendingIntent.getBroadcast(
context,
0,
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.java b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.java
deleted file mode 100644
index e843f580..00000000
--- a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.deniscerri.ytdlnis.work;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-
-import androidx.annotation.NonNull;
-import androidx.work.ForegroundInfo;
-import androidx.work.Worker;
-import androidx.work.WorkerParameters;
-
-import com.deniscerri.ytdlnis.MainActivity;
-import com.deniscerri.ytdlnis.database.Video;
-
-import java.util.ArrayList;
-
-import kotlin.random.Random;
-
-public class DownloadWorker extends Worker {
- private Context context;
- private WorkerParameters workerParams;
- public DownloadWorker(
- @NonNull Context appContext,
- @NonNull WorkerParameters workerParams) {
- super(appContext, workerParams);
- this.context = appContext;
- this.workerParams = workerParams;
- }
-
- @NonNull
- @Override
- public Result doWork() {
- //TODO
- return Result.success();
- }
-}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt
new file mode 100644
index 00000000..7e4d43d1
--- /dev/null
+++ b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt
@@ -0,0 +1,253 @@
+package com.deniscerri.ytdlnis.work
+
+import android.app.PendingIntent
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.SystemClock
+import android.provider.DocumentsContract
+import android.util.Log
+import android.widget.Toast
+import androidx.work.CoroutineWorker
+import androidx.work.ForegroundInfo
+import androidx.work.WorkerParameters
+import androidx.work.workDataOf
+import com.deniscerri.ytdlnis.BuildConfig
+import com.deniscerri.ytdlnis.MainActivity
+import com.deniscerri.ytdlnis.R
+import com.deniscerri.ytdlnis.database.DBManager
+import com.deniscerri.ytdlnis.database.models.DownloadItem
+import com.deniscerri.ytdlnis.database.models.HistoryItem
+import com.deniscerri.ytdlnis.database.repository.DownloadRepository
+import com.deniscerri.ytdlnis.util.FileUtil
+import com.deniscerri.ytdlnis.util.NotificationUtil
+import com.yausername.youtubedl_android.YoutubeDL
+import com.yausername.youtubedl_android.YoutubeDLRequest
+import java.io.File
+import java.util.regex.Pattern
+
+
+class DownloadWorker(
+ private val context: Context,
+ workerParams: WorkerParameters
+) : CoroutineWorker(context, workerParams) {
+ override suspend fun doWork(): Result {
+ workID = inputData.getInt("workID", SystemClock.uptimeMillis().toInt())
+ val notificationUtil = NotificationUtil(context)
+ val dbManager = DBManager.getInstance(context)
+ val dao = dbManager.downloadDao
+ val repository = DownloadRepository(dao)
+ val commandTemplateDao = dbManager.commandTemplateDao
+ val historyDao = dbManager.historyDao
+
+ queue = repository.getQueuedDownloads() as MutableList
+
+ val intent = Intent(context, MainActivity::class.java)
+ val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
+ val notification = notificationUtil.createDownloadServiceNotification(pendingIntent, queue[0].title, workID, NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID)
+ val foregroundInfo = ForegroundInfo(workID, notification)
+ setForeground(foregroundInfo)
+
+ while (queue.isNotEmpty()){
+ val item = dao.getDownloadById(queue[0].id)
+ queue.removeFirst()
+ val url = item.url
+ val request = YoutubeDLRequest(url)
+ val type = item.type
+ val downloadLocation = item.downloadPath
+
+ val tempFolder = StringBuilder(context.cacheDir.absolutePath + """/${item.title}##${item.type}""")
+ when(type){
+ "audio" -> tempFolder.append("##${item.audioQualityId}")
+ "video" -> tempFolder.append("##${item.videoQualityId}")
+ "command" -> tempFolder.append("##${item.customTemplateId}")
+ }
+ var tempFileDir = File(tempFolder.toString())
+ tempFileDir.delete()
+ tempFileDir.mkdir()
+
+ val sharedPreferences = context.getSharedPreferences("root_preferences",
+ Service.MODE_PRIVATE
+ )
+ val aria2 = sharedPreferences.getBoolean("aria2", false)
+ if (aria2) {
+ request.addOption("--downloader", "libaria2c.so")
+ request.addOption("--external-downloader-args", "aria2c:\"--summary-interval=1\"")
+ } else {
+ val concurrentFragments = sharedPreferences.getInt("concurrent_fragments", 1)
+ if (concurrentFragments > 1) request.addOption("-N", concurrentFragments)
+ }
+ val limitRate = sharedPreferences.getString("limit_rate", "")
+ if (limitRate != "") request.addOption("-r", limitRate!!)
+ val writeThumbnail = sharedPreferences.getBoolean("write_thumbnail", false)
+ if (writeThumbnail) {
+ request.addOption("--write-thumbnail")
+ request.addOption("--convert-thumbnails", "png")
+ }
+ request.addOption("--no-mtime")
+ val sponsorBlockFilters = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
+ if (sponsorBlockFilters!!.isNotEmpty()) {
+ val filters = java.lang.String.join(",", sponsorBlockFilters)
+ request.addOption("--sponsorblock-remove", filters)
+ }
+
+ when(type){
+ "audio" -> {
+ request.addOption("-x")
+ var audioQualityId : String = item.audioQualityId
+ if (audioQualityId == "0") audioQualityId = "ba"
+ var format = item.audioFormat
+ if (format.isEmpty()){
+ format = sharedPreferences.getString("audio_format", "")!!
+ }
+ request.addOption("-f", audioQualityId)
+ request.addOption("--audio-format", format)
+ request.addOption("--embed-metadata")
+
+ val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
+ if (embedThumb) {
+ request.addOption("--embed-thumbnail")
+ request.addOption("--convert-thumbnails", "png")
+ try {
+ val config = File(context.cacheDir, "config" + item.title + "##" + item.audioQualityId + ".txt")
+ val configData =
+ "--ppa \"ffmpeg: -c:v png -vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\""
+ config.writeText(configData)
+ request.addOption("--config", config.absolutePath)
+ } catch (ignored: Exception) {}
+ }
+ request.addOption("--parse-metadata", "%(release_year,upload_date)s:%(meta_date)s")
+ request.addCommands(listOf("--replace-in-metadata", "title", ".*.", item.title))
+ request.addCommands(listOf("--replace-in-metadata", "uploader", ".*.", item.author))
+
+ if (item.playlistTitle.isNotEmpty()) {
+ request.addOption("--parse-metadata", "%(album,playlist,title)s:%(meta_album)s")
+ request.addOption("--parse-metadata", "%(track_number,playlist_index)d:%(meta_track)s")
+ } else {
+ request.addOption("--parse-metadata", "%(album,title)s:%(meta_album)s")
+ }
+ request.addOption("-o", tempFileDir.absolutePath + "/%(uploader)s - %(title)s.%(ext)s")
+ }
+ "video" -> {
+ if (downloadLocation == context.getString(R.string.video_path)){
+ tempFileDir = File(context.getString(R.string.video_path))
+ }
+
+ val addChapters = sharedPreferences.getBoolean("add_chapters", false)
+ if (addChapters) {
+ request.addOption("--sponsorblock-mark", "all")
+ }
+ val embedSubs = sharedPreferences.getBoolean("embed_subtitles", false)
+ if (embedSubs) {
+ request.addOption("--embed-subs", "")
+ }
+ var videoQualityId = item.videoQualityId
+ val audioQualityId = item.audioQualityId
+ if (videoQualityId.isEmpty()) videoQualityId = "bestvideo"
+ val formatArgument = StringBuilder(videoQualityId)
+ if (videoQualityId != "worst"){
+ if (audioQualityId != "0") formatArgument.append("+", audioQualityId, "/best")
+ }
+ request.addOption("-f", formatArgument.toString())
+ var format = item.videoFormat
+ if (format.isNotEmpty()) {
+ format = sharedPreferences.getString("video_format", "")!!
+ if (format != "DEFAULT") request.addOption("--merge-output-format", format)
+ }
+
+ if (format != "webm") {
+ val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
+ if (embedThumb) {
+ request.addOption("--embed-thumbnail")
+ }
+ }
+ request.addOption("-o", tempFileDir.absolutePath + "/%(uploader)s - %(title)s.%(ext)s")
+ }
+ "command" -> {
+ val commandRegex = "\"([^\"]*)\"|(\\S+)"
+ val command = commandTemplateDao.getTemplateById(item.customTemplateId)
+ val m = Pattern.compile(commandRegex).matcher(command.content)
+ while (m.find()) {
+ if (m.group(1) != null) {
+ request.addOption(m.group(1)!!)
+ } else {
+ request.addOption(m.group(2)!!)
+ }
+ }
+ }
+ }
+
+ runCatching {
+ YoutubeDL.getInstance().execute(request, item.id.toString()){ progress, _, line ->
+ setProgressAsync(workDataOf("progress" to progress.toInt()))
+ var title: String? = context.getString(R.string.download_start)
+ if (queue.isNotEmpty()) {
+ title = queue[0].title
+ }
+ notificationUtil.updateDownloadNotification(
+ workID,
+ line, progress.toInt(), queue.size, title,
+ NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
+ )
+ }
+ }.onSuccess {
+ //move file from internal to set download directory
+ moveFile(tempFileDir.absoluteFile, downloadLocation){ progress ->
+ setProgressAsync(workDataOf("progress" to progress))
+ }
+ //put download in history
+ val incognito = sharedPreferences.getBoolean("incognito", false)
+ if (!incognito) {
+ val unixtime = System.currentTimeMillis() / 1000
+ val historyItem = HistoryItem(item.url, item.title, item.author, item.duration, item.thumb, item.type, unixtime, item.downloadPath, item.website)
+ historyDao.insert(historyItem)
+ }
+
+ }.onFailure {
+ tempFileDir.delete()
+ if (BuildConfig.DEBUG) {
+ Toast.makeText(context, it.message, Toast.LENGTH_LONG).show()
+ Log.e(DownloadWorker.TAG, context.getString(R.string.failed_download), it)
+ }
+ notificationUtil.updateDownloadNotification(
+ workID,
+ context.getString(R.string.failed_download), 0, 0, item.title,
+ NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
+ )
+ return Result.failure()
+ }
+ }
+ return Result.success()
+ }
+
+ private fun getDownloadLocation(type: String, context: Context): String? {
+ val sharedPreferences = context.getSharedPreferences("root_preferences",
+ Service.MODE_PRIVATE
+ )
+ val downloadsDir: String? = if (type == "audio") {
+ sharedPreferences.getString("music_path", context.getString(R.string.music_path))
+ } else {
+ sharedPreferences.getString("video_path", context.getString(R.string.video_path))
+ }
+ return downloadsDir
+ }
+
+
+ private fun moveFile(originDir: File, downLocation: String, progress: (progress: Int) -> Unit){
+ val destDir = Uri.parse(downLocation).run {
+ DocumentsContract.buildChildDocumentsUriUsingTree(this, DocumentsContract.getTreeDocumentId(this))
+ }
+ val fileUtil = FileUtil()
+ fileUtil.moveFile(originDir, context, destDir){ p ->
+ progress(p)
+ }
+ }
+
+ companion object {
+ var workID: Int = 0
+ const val TAG = "DownloadWorker"
+ lateinit var queue: MutableList
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/work/FileTransferWorker.kt b/app/src/main/java/com/deniscerri/ytdlnis/work/FileTransferWorker.kt
index 9e2cf182..e79d7760 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/work/FileTransferWorker.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/work/FileTransferWorker.kt
@@ -9,6 +9,7 @@ import android.provider.DocumentsContract
import androidx.work.CoroutineWorker
import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
+import androidx.work.workDataOf
import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.NotificationUtil
@@ -39,6 +40,7 @@ class FileTransferWorker(
setForeground(foregroundInfo)
fileUtil.moveFile(originDir, context, destDir){ progress ->
+ setProgressAsync(workDataOf("progress" to progress))
notificationUtil.updateFileTransferNotification(id, progress)
}
diff --git a/app/src/main/res/layout/downloads_card.xml b/app/src/main/res/layout/downloads_card.xml
index 7a1e4d6c..373e39c1 100644
--- a/app/src/main/res/layout/downloads_card.xml
+++ b/app/src/main/res/layout/downloads_card.xml
@@ -49,11 +49,11 @@
android:id="@+id/downloads_title"
android:layout_width="300dp"
android:layout_height="100dp"
- android:paddingTop="15dp"
- android:paddingRight="20dp"
- android:paddingBottom="20dp"
- android:paddingLeft="20dp"
- android:textSize="16sp"
+ android:paddingTop="10dp"
+ android:paddingEnd="10dp"
+ android:paddingBottom="10dp"
+ android:paddingStart="10dp"
+ android:textSize="15sp"
android:textColor="@color/white"
android:textStyle="bold"
android:shadowRadius="2"
@@ -67,9 +67,9 @@
android:layout_height="match_parent"
android:gravity="bottom"
android:paddingTop="10dp"
- android:paddingRight="20dp"
+ android:paddingRight="10dp"
android:paddingBottom="10dp"
- android:paddingLeft="20dp"
+ android:paddingLeft="10dp"
android:textSize="12sp"
android:textColor="@color/white"
android:textStyle="bold"
@@ -82,11 +82,11 @@
android:id="@+id/downloads_info_time"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:gravity="bottom|right"
+ android:gravity="bottom|end"
android:paddingTop="10dp"
- android:paddingRight="20dp"
+ android:paddingRight="10dp"
android:paddingBottom="10dp"
- android:paddingLeft="20dp"
+ android:paddingLeft="10dp"
android:textSize="12sp"
android:textColor="@color/white"
android:textStyle="bold"
diff --git a/app/src/main/res/layout/result_card.xml b/app/src/main/res/layout/result_card.xml
index af7dde22..8fe15e8f 100644
--- a/app/src/main/res/layout/result_card.xml
+++ b/app/src/main/res/layout/result_card.xml
@@ -59,7 +59,7 @@
android:paddingTop="10dp"
android:paddingEnd="10dp"
android:paddingStart="10dp"
- android:textSize="15sp"
+ android:textSize="17sp"
android:textColor="@color/white"
android:textStyle="bold"
android:shadowRadius="2"
@@ -75,7 +75,7 @@
android:paddingEnd="10dp"
android:paddingBottom="5dp"
android:paddingStart="10dp"
- android:textSize="10sp"
+ android:textSize="12sp"
android:textColor="@color/white"
android:textStyle="bold"
android:shadowRadius="1.5"