added range picker on playlist
This commit is contained in:
parent
3c99f3fdc2
commit
7a8341a3c3
11 changed files with 375 additions and 164 deletions
|
|
@ -76,12 +76,12 @@ class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activi
|
|||
// CHECKBOX ----------------------------------
|
||||
val check = card.findViewById<CheckBox>(R.id.checkBox)
|
||||
check.isChecked = checkedItems.contains(item.url)
|
||||
check.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||
checkCard(isChecked, item.url)
|
||||
check.setOnClickListener {
|
||||
checkCard(check.isChecked, item.url)
|
||||
}
|
||||
|
||||
card.setOnClickListener {
|
||||
check.isChecked = !check.isChecked
|
||||
check.performClick()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +119,22 @@ class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activi
|
|||
}
|
||||
}
|
||||
|
||||
fun checkRange(start: Int, end: Int){
|
||||
checkedItems.clear()
|
||||
if (start == end ){
|
||||
val item = getItem(start)
|
||||
checkedItems.add(item!!.url)
|
||||
notifyItemChanged(start)
|
||||
}else{
|
||||
for (i in start..end){
|
||||
val item = getItem(i)
|
||||
checkedItems.add(item!!.url)
|
||||
notifyItemChanged(i)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun getCheckedItems() : List<String>{
|
||||
return checkedItems
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,14 @@ import android.app.Dialog
|
|||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
|
|
@ -23,8 +27,10 @@ import com.deniscerri.ytdlnis.receiver.ShareActivity
|
|||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
|
||||
class SelectPlaylistItemsBottomSheetDialog(private val items: List<ResultItem?>, private val type: DownloadViewModel.Type) : BottomSheetDialogFragment(), PlaylistAdapter.OnItemClickListener {
|
||||
|
|
@ -34,7 +40,8 @@ class SelectPlaylistItemsBottomSheetDialog(private val items: List<ResultItem?>,
|
|||
private lateinit var recyclerView: RecyclerView
|
||||
private lateinit var behavior: BottomSheetBehavior<View>
|
||||
private lateinit var selectedText: TextView
|
||||
|
||||
private lateinit var fromTextInput: TextInputLayout
|
||||
private lateinit var toTextInput: TextInputLayout
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -75,41 +82,90 @@ class SelectPlaylistItemsBottomSheetDialog(private val items: List<ResultItem?>,
|
|||
selectedText = view.findViewById<Button>(R.id.selected)
|
||||
selectedText.text = "0 ${resources.getString(R.string.selected)}"
|
||||
|
||||
fromTextInput = view.findViewById(R.id.from_textinput)
|
||||
toTextInput = view.findViewById(R.id.to_textinput)
|
||||
|
||||
|
||||
fromTextInput.editText!!.doAfterTextChanged { _text ->
|
||||
reset()
|
||||
val start = _text.toString()
|
||||
val end = toTextInput.editText!!.text.toString()
|
||||
|
||||
if (checkRanges(start, end)) {
|
||||
if (start < end){
|
||||
var startNr = Integer.parseInt(start)
|
||||
startNr--
|
||||
var endNr = Integer.parseInt(end)
|
||||
endNr--
|
||||
if (startNr <= 0) startNr = 0
|
||||
if (endNr > items.size) endNr = items.size
|
||||
listAdapter.checkRange(startNr, endNr)
|
||||
selectedText.text = "${listAdapter.getCheckedItems().size} ${resources.getString(R.string.selected)}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toTextInput.editText!!.doAfterTextChanged { _text ->
|
||||
reset()
|
||||
|
||||
val start = fromTextInput.editText!!.text.toString()
|
||||
val end = _text.toString()
|
||||
|
||||
if (checkRanges(start, end)) {
|
||||
if (start < end){
|
||||
var startNr = Integer.parseInt(start)
|
||||
startNr--
|
||||
var endNr = Integer.parseInt(end)
|
||||
endNr--
|
||||
if (startNr <= 0) startNr = 0
|
||||
if (endNr > items.size) endNr = items.size
|
||||
listAdapter.checkRange(startNr, endNr)
|
||||
selectedText.text = "${listAdapter.getCheckedItems().size} ${resources.getString(R.string.selected)}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val checkAll = view.findViewById<FloatingActionButton>(R.id.check_all)
|
||||
checkAll!!.setOnClickListener {
|
||||
if (listAdapter.getCheckedItems().size != items.size){
|
||||
fromTextInput.editText!!.setText("1")
|
||||
toTextInput.editText!!.setText(items.size.toString())
|
||||
listAdapter.checkAll()
|
||||
selectedText.text = resources.getString(R.string.all_items_selected)
|
||||
}else{
|
||||
listAdapter.clearCheckeditems()
|
||||
selectedText.text = "0 ${resources.getString(R.string.selected)}"
|
||||
reset()
|
||||
fromTextInput.isEnabled = true
|
||||
toTextInput.isEnabled = true
|
||||
fromTextInput.editText!!.setText("")
|
||||
toTextInput.editText!!.setText("")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val ok = view.findViewById<Button>(R.id.bottomsheet_ok)
|
||||
ok!!.setOnClickListener {
|
||||
lifecycleScope.launch(Dispatchers.IO){
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val checkedItems = listAdapter.getCheckedItems()
|
||||
val checkedResultItems = items.filter { item -> checkedItems.contains(item!!.url) }
|
||||
val downloadItems = mutableListOf<DownloadItem>()
|
||||
checkedResultItems.forEach { c ->
|
||||
c!!.id = 0
|
||||
val i = downloadViewModel.createDownloadItemFromResult(c,type)
|
||||
if (type == DownloadViewModel.Type.command){
|
||||
i.format = downloadViewModel.getLatestCommandTemplateAsFormat()
|
||||
}
|
||||
downloadItems.add(i)
|
||||
}
|
||||
|
||||
if (downloadItems.size == 1){
|
||||
val resultItem = resultViewModel.getItemByURL(items[0]!!.url)
|
||||
if (checkedResultItems.size == 1){
|
||||
val resultItem = resultViewModel.getItemByURL(checkedResultItems[0]!!.url)
|
||||
val bottomSheet = DownloadBottomSheetDialog(resultItem, type)
|
||||
bottomSheet.show(parentFragmentManager, "downloadSingleSheet")
|
||||
}else{
|
||||
val downloadItems = mutableListOf<DownloadItem>()
|
||||
checkedResultItems.forEach { c ->
|
||||
c!!.id = 0
|
||||
val i = downloadViewModel.createDownloadItemFromResult(c,type)
|
||||
if (type == DownloadViewModel.Type.command){
|
||||
i.format = downloadViewModel.getLatestCommandTemplateAsFormat()
|
||||
}
|
||||
downloadItems.add(i)
|
||||
}
|
||||
|
||||
val bottomSheet = DownloadMultipleBottomSheetDialog(downloadItems)
|
||||
bottomSheet.show(parentFragmentManager, "downloadMultipleSheet")
|
||||
}
|
||||
|
||||
dismiss()
|
||||
|
||||
}
|
||||
|
|
@ -133,6 +189,15 @@ class SelectPlaylistItemsBottomSheetDialog(private val items: List<ResultItem?>,
|
|||
}
|
||||
}
|
||||
|
||||
private fun checkRanges(start: String, end: String) : Boolean {
|
||||
return start.isNotBlank() && end.isNotBlank()
|
||||
}
|
||||
|
||||
private fun reset(){
|
||||
listAdapter.clearCheckeditems()
|
||||
selectedText.text = "0 ${resources.getString(R.string.selected)}"
|
||||
}
|
||||
|
||||
|
||||
override fun onCardSelect(itemURL: String, isChecked: Boolean, checkedItems: List<String>) {
|
||||
if (checkedItems.size == items.size){
|
||||
|
|
@ -140,6 +205,13 @@ class SelectPlaylistItemsBottomSheetDialog(private val items: List<ResultItem?>,
|
|||
}else{
|
||||
selectedText.text = "${checkedItems.size} ${resources.getString(R.string.selected)}"
|
||||
}
|
||||
if (checkedItems.isEmpty()){
|
||||
fromTextInput.isEnabled = true
|
||||
toTextInput.isEnabled = true
|
||||
}else{
|
||||
fromTextInput.isEnabled = false
|
||||
toTextInput.isEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,60 +1,43 @@
|
|||
package com.deniscerri.ytdlnis.ui.more
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.app.Notification
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.os.Looper
|
||||
import android.text.format.DateFormat
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
import android.widget.*
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ScrollView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.work.*
|
||||
import com.deniscerri.ytdlnis.App
|
||||
import androidx.work.Data
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||
import com.deniscerri.ytdlnis.util.FileUtil
|
||||
import com.deniscerri.ytdlnis.util.NotificationUtil
|
||||
import com.deniscerri.ytdlnis.work.DownloadWorker
|
||||
import com.deniscerri.ytdlnis.work.TerminalDownloadWorker
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import com.google.android.material.bottomappbar.BottomAppBar
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.chip.ChipGroup
|
||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import com.yausername.youtubedl_android.YoutubeDLRequest
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
|
||||
class TerminalActivity : AppCompatActivity() {
|
||||
|
|
@ -67,11 +50,15 @@ class TerminalActivity : AppCompatActivity() {
|
|||
private lateinit var bottomAppBar: BottomAppBar
|
||||
private lateinit var commandTemplateViewModel: CommandTemplateViewModel
|
||||
private lateinit var sharedPreferences: SharedPreferences
|
||||
private var downloadID by Delegates.notNull<Int>()
|
||||
var context: Context? = null
|
||||
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_terminal)
|
||||
|
||||
downloadID = System.currentTimeMillis().toInt() % 100000
|
||||
|
||||
context = baseContext
|
||||
scrollView = findViewById(R.id.custom_command_scrollview)
|
||||
topAppBar = findViewById(R.id.custom_command_toolbar)
|
||||
|
|
@ -112,6 +99,28 @@ class TerminalActivity : AppCompatActivity() {
|
|||
}
|
||||
notificationUtil = NotificationUtil(this)
|
||||
handleIntent(intent)
|
||||
|
||||
WorkManager.getInstance(this)
|
||||
.getWorkInfosForUniqueWorkLiveData(downloadID.toString())
|
||||
.observe(this){ list ->
|
||||
list.forEach {work ->
|
||||
if (work.state == WorkInfo.State.SUCCEEDED || work.state == WorkInfo.State.FAILED || work.state == WorkInfo.State.CANCELLED) {
|
||||
input!!.visibility = View.VISIBLE
|
||||
input!!.requestFocus()
|
||||
hideCancelFab()
|
||||
}
|
||||
val line = work.progress.getString("output") ?: return@observe
|
||||
val id = work.progress.getInt("id", 0)
|
||||
if(id == 0) return@observe
|
||||
runOnUiThread {
|
||||
try {
|
||||
output!!.text = "${output!!.text}\n$line"
|
||||
output!!.scrollTo(0, output!!.height)
|
||||
scrollView!!.fullScroll(View.FOCUS_DOWN)
|
||||
}catch (ignored: Exception) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
|
|
@ -204,121 +213,31 @@ class TerminalActivity : AppCompatActivity() {
|
|||
val cmd = if (command!!.contains("yt-dlp")) command.replace("yt-dlp", "")
|
||||
else command
|
||||
|
||||
downloadID = System.currentTimeMillis().toInt()
|
||||
|
||||
val theIntent = Intent(this, TerminalActivity::class.java)
|
||||
val pendingIntent = PendingIntent.getActivity(this, 0, theIntent, PendingIntent.FLAG_IMMUTABLE)
|
||||
|
||||
val commandNotification: Notification =
|
||||
notificationUtil.createDownloadServiceNotification(
|
||||
pendingIntent,
|
||||
getString(R.string.terminal),
|
||||
downloadID,
|
||||
NotificationUtil.COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID
|
||||
val workRequest = OneTimeWorkRequestBuilder<TerminalDownloadWorker>()
|
||||
.setInputData(
|
||||
Data.Builder()
|
||||
.putInt("id", downloadID)
|
||||
.putString("command", cmd)
|
||||
.build()
|
||||
)
|
||||
with(NotificationManagerCompat.from(this)){
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
context!!,
|
||||
Manifest.permission.POST_NOTIFICATIONS
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
notify(downloadID, commandNotification)
|
||||
}
|
||||
}
|
||||
val request = YoutubeDLRequest(emptyList())
|
||||
.addTag("terminal")
|
||||
.build()
|
||||
|
||||
val tempFolder = StringBuilder(context!!.cacheDir.absolutePath + """/${System.currentTimeMillis()}##terminal""")
|
||||
val tempFileDir = File(tempFolder.toString())
|
||||
tempFileDir.delete()
|
||||
tempFileDir.mkdir()
|
||||
|
||||
request.addOption(
|
||||
"--config-locations",
|
||||
File(cacheDir, "config${System.currentTimeMillis()}.txt").apply {
|
||||
writeText(cmd)
|
||||
}.absolutePath
|
||||
)
|
||||
|
||||
|
||||
request.addOption("-P", tempFileDir.absolutePath)
|
||||
|
||||
showCancelFab()
|
||||
val disposable: Disposable = Observable.fromCallable {
|
||||
try{
|
||||
YoutubeDL.getInstance().execute(request, downloadID.toString()){ progress, _, line ->
|
||||
Log.e(TAG, line)
|
||||
runOnUiThread {
|
||||
output!!.text = "${output!!.text}\n$line"
|
||||
output!!.scrollTo(0, output!!.height)
|
||||
scrollView!!.fullScroll(View.FOCUS_DOWN)
|
||||
}
|
||||
|
||||
val title: String = getString(R.string.terminal)
|
||||
notificationUtil.updateDownloadNotification(
|
||||
downloadID,
|
||||
line, progress.toInt(), 0, title,
|
||||
NotificationUtil.COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID
|
||||
)
|
||||
}
|
||||
}catch (e: Exception){
|
||||
e.printStackTrace()
|
||||
runOnUiThread {
|
||||
output!!.text = "${output!!.text}\n${e.message}"
|
||||
output!!.scrollTo(0, output!!.height)
|
||||
scrollView!!.fullScroll(View.FOCUS_DOWN)
|
||||
|
||||
input!!.visibility = View.VISIBLE
|
||||
input!!.requestFocus()
|
||||
|
||||
hideCancelFab()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.subscribeOn(Schedulers.newThread())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({
|
||||
input!!.setText("yt-dlp ")
|
||||
input!!.visibility = View.VISIBLE
|
||||
input!!.requestFocus()
|
||||
|
||||
try{
|
||||
val fileUtil = FileUtil()
|
||||
fileUtil.moveFile(tempFileDir.absoluteFile, this, sharedPreferences.getString("command_path", getString(R.string.command))!!) {}
|
||||
}catch (e: Exception){
|
||||
output!!.text = "${output!!.text}\n${e.message}"
|
||||
output!!.scrollTo(0, output!!.height)
|
||||
scrollView!!.fullScroll(View.FOCUS_DOWN)
|
||||
}
|
||||
|
||||
hideCancelFab()
|
||||
notificationUtil.cancelDownloadNotification(downloadID)
|
||||
}) { e ->
|
||||
e.printStackTrace()
|
||||
output!!.text = "${output!!.text}\n${e.message}"
|
||||
output!!.scrollTo(0, output!!.height)
|
||||
scrollView!!.fullScroll(View.FOCUS_DOWN)
|
||||
input!!.setText("yt-dlp ")
|
||||
input!!.visibility = View.VISIBLE
|
||||
|
||||
hideCancelFab();
|
||||
|
||||
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
|
||||
Log.e(DownloadWorker.TAG, context?.getString(R.string.failed_download), e)
|
||||
notificationUtil.cancelDownloadNotification(downloadID)
|
||||
}
|
||||
compositeDisposable.add(disposable)
|
||||
WorkManager.getInstance(this).beginUniqueWork(
|
||||
downloadID.toString(),
|
||||
ExistingWorkPolicy.KEEP,
|
||||
workRequest
|
||||
).enqueue()
|
||||
}
|
||||
|
||||
private fun cancelDownload() {
|
||||
YoutubeDL.getInstance().destroyProcessById(downloadID.toString())
|
||||
WorkManager.getInstance(this).cancelUniqueWork(downloadID.toString())
|
||||
notificationUtil.cancelDownloadNotification(downloadID)
|
||||
notificationUtil.cancelDownloadNotification(downloadID.toInt())
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "CustomCommandActivity"
|
||||
private var downloadID = System.currentTimeMillis().toInt()
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ class DownloadLogListActivity : AppCompatActivity(), DownloadLogsAdapter.OnItemC
|
|||
fileList = mutableListOf()
|
||||
try{
|
||||
fileList.addAll(logFolder.listFiles()!!)
|
||||
fileList.sortByDescending { it.name.split(" -")[0].toInt() }
|
||||
fileList.sortByDescending { it.lastModified()}
|
||||
}catch (e: Exception){
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,6 +140,10 @@ class FileUtil() {
|
|||
val titleRegex = Regex("[^A-Za-z\\d ]")
|
||||
return File(context.filesDir.absolutePath + """/logs/${item.id} - ${titleRegex.replace(item.title, "")}##${item.type}##${item.format.format_id}.log""")
|
||||
}
|
||||
fun getLogFileForTerminal(context: Context, command: String) : File {
|
||||
val titleRegex = Regex("[^A-Za-z\\d ]")
|
||||
return File(context.filesDir.absolutePath + """/logs/Terminal - ${titleRegex.replace(command.take(30), "")}##terminal.log""")
|
||||
}
|
||||
|
||||
fun convertFileSize(s: Long): String{
|
||||
if (s <= 0) return "?"
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class DownloadWorker(
|
|||
val type = downloadItem.type
|
||||
val downloadLocation = downloadItem.downloadPath
|
||||
|
||||
val tempFileDir = File(context.cacheDir.absolutePath + downloadItem.id)
|
||||
val tempFileDir = File(context.cacheDir.absolutePath + "/" + downloadItem.id)
|
||||
tempFileDir.delete()
|
||||
tempFileDir.mkdir()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,155 @@
|
|||
package com.deniscerri.ytdlnis.work
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.work.ForegroundInfo
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.workDataOf
|
||||
import com.deniscerri.ytdlnis.MainActivity
|
||||
import com.deniscerri.ytdlnis.R
|
||||
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.*
|
||||
|
||||
|
||||
class TerminalDownloadWorker(
|
||||
private val context: Context,
|
||||
workerParams: WorkerParameters
|
||||
) : Worker(context, workerParams) {
|
||||
override fun doWork(): Result {
|
||||
val itemId = inputData.getInt("id", 0)
|
||||
val command = inputData.getString("command")
|
||||
if (itemId == 0) return Result.failure()
|
||||
if (command!!.isEmpty()) return Result.failure()
|
||||
|
||||
val notificationUtil = NotificationUtil(context)
|
||||
val fileUtil = FileUtil()
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
val intent = Intent(context, MainActivity::class.java)
|
||||
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||
val notification = notificationUtil.createDownloadServiceNotification(pendingIntent, command.take(20), itemId, NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID)
|
||||
val foregroundInfo = ForegroundInfo(itemId, notification)
|
||||
setForegroundAsync(foregroundInfo)
|
||||
|
||||
val request = YoutubeDLRequest(emptyList())
|
||||
val sharedPreferences = context.getSharedPreferences("root_preferences",
|
||||
Service.MODE_PRIVATE
|
||||
)
|
||||
|
||||
val downloadLocation = sharedPreferences.getString("command_path", context.getString(R.string.command_path))
|
||||
val tempFileDir = File(context.cacheDir.absolutePath + "/" + itemId)
|
||||
tempFileDir.delete()
|
||||
tempFileDir.mkdir()
|
||||
|
||||
request.addOption(
|
||||
"--config-locations",
|
||||
File(context.cacheDir, "config${System.currentTimeMillis()}.txt").apply {
|
||||
writeText(command)
|
||||
}.absolutePath
|
||||
)
|
||||
|
||||
request.addOption("-P", tempFileDir.absolutePath)
|
||||
|
||||
val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false)
|
||||
val logFolder = File(context.filesDir.absolutePath + "/logs")
|
||||
val logFile = fileUtil.getLogFileForTerminal(context, command)
|
||||
|
||||
runCatching {
|
||||
if (logDownloads){
|
||||
logFolder.mkdirs()
|
||||
logFile.createNewFile()
|
||||
logFile.writeText("Downloading:\n" +
|
||||
"Terminal Download\n" +
|
||||
"Command: ${command}\n\n")
|
||||
}
|
||||
|
||||
YoutubeDL.getInstance().execute(request, itemId.toString()){ progress, _, line ->
|
||||
setProgressAsync(workDataOf("progress" to progress.toInt(), "output" to line, "id" to itemId, "log" to logDownloads))
|
||||
val title: String = command.take(20)
|
||||
notificationUtil.updateDownloadNotification(
|
||||
itemId,
|
||||
line, progress.toInt(), 0, title,
|
||||
NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
|
||||
)
|
||||
if (logDownloads && logFile.exists()){
|
||||
logFile.appendText("${line}\n")
|
||||
}
|
||||
}
|
||||
}.onSuccess {
|
||||
//move file from internal to set download directory
|
||||
var finalPath : String?
|
||||
try {
|
||||
finalPath = moveFile(tempFileDir.absoluteFile, downloadLocation!!){ progress ->
|
||||
setProgressAsync(workDataOf("progress" to progress))
|
||||
}
|
||||
setProgressAsync(workDataOf("progress" to 100, "output" to "Moved file to $finalPath", "id" to itemId, "log" to logDownloads))
|
||||
}catch (e: Exception){
|
||||
finalPath = context.getString(R.string.unfound_file)
|
||||
e.printStackTrace()
|
||||
handler.postDelayed({
|
||||
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
|
||||
}, 1000)
|
||||
}
|
||||
//put download in history
|
||||
// val incognito = sharedPreferences.getBoolean("incognito", false)
|
||||
// if (!incognito) {
|
||||
// val unixtime = System.currentTimeMillis() / 1000
|
||||
// val file = File(finalPath!!)
|
||||
// downloadItem.format.filesize = if (file.exists()) file.length() else 0L
|
||||
// val historyItem = HistoryItem(0, downloadItem.url, downloadItem.title, downloadItem.author, downloadItem.duration, downloadItem.thumb, downloadItem.type, unixtime, finalPath, downloadItem.website, downloadItem.format)
|
||||
// runBlocking {
|
||||
// historyDao.insert(historyItem)
|
||||
// }
|
||||
// }
|
||||
// runBlocking {
|
||||
// dao.delete(downloadItem.id)
|
||||
// }
|
||||
}.onFailure {
|
||||
if (it is YoutubeDL.CanceledException) {
|
||||
return Result.failure()
|
||||
}
|
||||
|
||||
if (logDownloads && logFile.exists()){
|
||||
logFile.appendText("${it.message}\n")
|
||||
}
|
||||
setProgressAsync(workDataOf("progress" to -1, "output" to it.message, "id" to itemId, "log" to logDownloads))
|
||||
|
||||
tempFileDir.delete()
|
||||
|
||||
Log.e(TAG, context.getString(R.string.failed_download), it)
|
||||
notificationUtil.updateDownloadNotification(
|
||||
itemId,
|
||||
context.getString(R.string.failed_download), 0, 0, command.take(20),
|
||||
NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
|
||||
)
|
||||
return Result.failure()
|
||||
}
|
||||
|
||||
return Result.success()
|
||||
|
||||
}
|
||||
@Throws(Exception::class)
|
||||
private fun moveFile(originDir: File, downLocation: String, progress: (progress: Int) -> Unit) : String{
|
||||
val fileUtil = FileUtil()
|
||||
val path = fileUtil.moveFile(originDir, context, downLocation){ p ->
|
||||
progress(p)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "DownloadWorker"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -12,4 +12,4 @@
|
|||
android:fillColor="@color/icon_fg"
|
||||
android:strokeWidth="1"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
</vector>
|
||||
|
|
@ -57,11 +57,12 @@
|
|||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/cancelButton"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/rangeSlider">
|
||||
|
||||
|
|
@ -69,8 +70,8 @@
|
|||
android:id="@+id/from_textinput"
|
||||
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
||||
android:layout_width="wrap_content"
|
||||
android:hint="@string/start"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/start">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -94,8 +95,8 @@
|
|||
android:id="@+id/to_textinput"
|
||||
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
||||
android:layout_width="wrap_content"
|
||||
android:hint="@string/end"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/end"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/colon">
|
||||
|
||||
|
|
|
|||
|
|
@ -4,17 +4,18 @@
|
|||
android:id="@+id/playlist_card_constraintLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:checkable="true"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/download_card_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:padding="10dp"
|
||||
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar"
|
||||
android:layout_height="wrap_content"
|
||||
android:checkable="true"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:cardPreventCornerOverlap="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/checkBox"
|
||||
|
|
|
|||
|
|
@ -77,17 +77,60 @@
|
|||
android:autoLink="all"
|
||||
android:text="@string/ok"
|
||||
app:icon="@drawable/ic_check"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/bottomsheet_ok"
|
||||
app:layout_constraintVertical_bias="0.0">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/from_textinput"
|
||||
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
app:errorEnabled="true"
|
||||
android:hint="@string/start">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="number" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/to_textinput"
|
||||
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/end"
|
||||
app:errorEnabled="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/colon">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/downloadMultipleRecyclerview"
|
||||
android:layout_width="match_parent"
|
||||
android:paddingTop="10dp"
|
||||
tools:listitem="@layout/playlist_item"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusableInTouchMode="true"
|
||||
|
|
|
|||
Loading…
Reference in a new issue