Browse Source

Download page

master
CookieGamesOfficial 8 months ago
parent
commit
a934e64664
  1. 26
      app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/database/Download.kt
  2. 35
      app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/database/DownloadDatabase.kt
  3. 20
      app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/database/DownloadsDao.kt
  4. 21
      app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/database/DownloadsRepository.kt
  5. 37
      app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/fragments/DownloadsFragment.kt
  6. 23
      app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/models/DownloadsViewModel.kt
  7. 154
      app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/worker/DownloadWorker.kt
  8. 13
      app/src/main/res/layout/dialog_download_path.xml
  9. 62
      app/src/main/res/layout/download_row.xml

26
app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/database/Download.kt

@ -0,0 +1,26 @@
package com.cookiejarapps.smartcookieweb_ytdl.database
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "downloads_table")
data class Download(
@ColumnInfo(name = "name")
val name: String,
@ColumnInfo(name = "time")
val timestamp: Long
) {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
@ColumnInfo(name = "download_progress")
var downloadPercent: Double = 0.0
@ColumnInfo(name = "download_path")
lateinit var downloadPath: String
@ColumnInfo(name = "file_type")
lateinit var fileType: String
}

35
app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/database/DownloadDatabase.kt

@ -0,0 +1,35 @@
package com.cookiejarapps.smartcookieweb_ytdl.database
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = [Download::class], version = 1, exportSchema = true)
abstract class DownloadDatabase : RoomDatabase() {
abstract fun downloadsDao(): DownloadsDao
companion object {
@Volatile
private var dbInstance: DownloadDatabase? = null
private const val name = "ytdl_db"
fun getDatabase(context: Context): DownloadDatabase {
val tempInstance = dbInstance
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
DownloadDatabase::class.java,
name
).build()
dbInstance = instance
return instance
}
}
}
}

20
app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/database/DownloadsDao.kt

@ -0,0 +1,20 @@
package com.cookiejarapps.smartcookieweb_ytdl.database
import androidx.lifecycle.LiveData
import androidx.room.*
@Dao
interface DownloadsDao {
@Insert
suspend fun insertDownloads(item: Download)
@Update
suspend fun updateDownloads(item: Download)
@Delete
suspend fun deleteDownloads(item: Download)
@Query("SELECT * from downloads_table ORDER BY time DESC")
fun getAll(): LiveData<List<Download>>
}

21
app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/database/DownloadsRepository.kt

@ -0,0 +1,21 @@
package com.cookiejarapps.smartcookieweb_ytdl.database
import androidx.lifecycle.LiveData
class DownloadsRepository(private val downloadsDao: DownloadsDao) {
val allDownloads: LiveData<List<Download>> = downloadsDao.getAll()
suspend fun insertDownloads(download: Download) {
downloadsDao.insertDownloads(download)
}
suspend fun updateDownloads(download: Download) {
downloadsDao.updateDownloads(download)
}
suspend fun deleteDownloads(download: Download) {
downloadsDao.deleteDownloads(download)
}
}

37
app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/fragments/DownloadsFragment.kt

@ -0,0 +1,37 @@
package com.cookiejarapps.smartcookieweb_ytdl.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView.OnItemLongClickListener
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import com.cookiejarapps.smartcookieweb_ytdl.R
import com.cookiejarapps.smartcookieweb_ytdl.adapters.DownloadsAdapter
import com.cookiejarapps.smartcookieweb_ytdl.models.DownloadsViewModel
class DownloadsFragment : Fragment() {
private lateinit var downloadsViewModel: DownloadsViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val list: RecyclerView = inflater.inflate(R.layout.fragment_downloads_list, container, false) as RecyclerView
list.adapter = DownloadsAdapter()
downloadsViewModel = ViewModelProvider(this).get(DownloadsViewModel::class.java)
downloadsViewModel.downloadList.observe(viewLifecycleOwner, { downloads ->
downloads?.let { (list.adapter as DownloadsAdapter).updateDataSet(downloads) }
})
return list
}
}

23
app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/models/DownloadsViewModel.kt

@ -0,0 +1,23 @@
package com.cookiejarapps.smartcookieweb_ytdl.models
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import com.cookiejarapps.smartcookieweb_ytdl.database.DownloadDatabase
import com.cookiejarapps.smartcookieweb_ytdl.database.Download
import com.cookiejarapps.smartcookieweb_ytdl.database.DownloadsRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class DownloadsViewModel(application: Application) : AndroidViewModel(application) {
private val repository: DownloadsRepository
val downloadList: LiveData<List<Download>>
init {
val downloadsDao = DownloadDatabase.getDatabase(application).downloadsDao()
repository = DownloadsRepository(downloadsDao)
downloadList = repository.allDownloads
}
}

154
app/src/main/java/com/cookiejarapps/smartcookieweb_ytdl/worker/DownloadWorker.kt

@ -0,0 +1,154 @@
package com.cookiejarapps.smartcookieweb_ytdl.worker
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.net.Uri
import android.os.Build
import android.provider.DocumentsContract
import android.webkit.MimeTypeMap
import androidx.core.app.NotificationCompat
import androidx.work.CoroutineWorker
import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
import com.cookiejarapps.smartcookieweb_ytdl.R
import com.cookiejarapps.smartcookieweb_ytdl.database.DownloadDatabase
import com.cookiejarapps.smartcookieweb_ytdl.database.Download
import com.cookiejarapps.smartcookieweb_ytdl.database.DownloadsRepository
import com.yausername.youtubedl_android.YoutubeDL
import com.yausername.youtubedl_android.YoutubeDLRequest
import org.apache.commons.io.IOUtils
import java.io.File
import java.util.*
class DownloadWorker(appContext: Context, params: WorkerParameters) :
CoroutineWorker(appContext, params) {
private val notificationManager =
appContext.getSystemService(Context.NOTIFICATION_SERVICE) as
NotificationManager?
override suspend fun doWork(): Result {
val url = inputData.getString(urlKey)!!
val name = inputData.getString(nameKey)!!
val formatId = inputData.getString(formatIdKey)!!
val audioCodec = inputData.getString(audioCodecKey)
val videoCodec = inputData.getString(videoCodecKey)
val downloadDir = inputData.getString(downloadDirKey)!!
createNotificationChannel()
val notificationId = id.hashCode()
val notification = NotificationCompat.Builder(
applicationContext,
channelId
)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(name)
.setContentText(applicationContext.getString(R.string.download_start))
.build()
val foregroundInfo = ForegroundInfo(notificationId, notification)
setForeground(foregroundInfo)
val request = YoutubeDLRequest(url)
val tmpFile = File.createTempFile("ytdl", null, applicationContext.externalCacheDir)
tmpFile.delete()
tmpFile.mkdir()
tmpFile.deleteOnExit()
request.addOption("-o", "${tmpFile.absolutePath}/%(title)s.%(ext)s")
val videoOnly = videoCodec != "none" && audioCodec == "none"
if (videoOnly) {
request.addOption("-f", "${formatId}+bestaudio")
} else {
request.addOption("-f", formatId)
}
var destUri: Uri? = null
try {
YoutubeDL.getInstance()
.execute(request) { progress, _ ->
showProgress(id.hashCode(), name, progress.toInt())
}
val treeUri = Uri.parse(downloadDir)
val docId = DocumentsContract.getTreeDocumentId(treeUri)
val destDir = DocumentsContract.buildDocumentUriUsingTree(treeUri, docId)
tmpFile.listFiles().forEach {
val mimeType =
MimeTypeMap.getSingleton().getMimeTypeFromExtension(it.extension) ?: "*/*"
destUri = DocumentsContract.createDocument(
applicationContext.contentResolver,
destDir,
mimeType,
it.name
)
val ins = it.inputStream()
val ops = applicationContext.contentResolver.openOutputStream(destUri!!)
IOUtils.copy(ins, ops)
IOUtils.closeQuietly(ops)
IOUtils.closeQuietly(ins)
}
} finally {
tmpFile.deleteRecursively()
}
val downloadsDao = DownloadDatabase.getDatabase(
applicationContext
).downloadsDao()
val repository =
DownloadsRepository(downloadsDao)
val download =
Download(name, Date().time)
download.downloadPath = destUri.toString()
download.downloadPercent = 100.00
download.fileType = if (videoCodec == "none" && audioCodec != "none"){ "audio" } else{ "video" }
repository.insertDownloads(download)
return Result.success()
}
private fun showProgress(id: Int, name: String, progress: Int) {
val notification = NotificationCompat.Builder(
applicationContext,
channelId
)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(name)
.setProgress(100, progress, false)
.build()
notificationManager?.notify(id, notification)
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
var notificationChannel =
notificationManager?.getNotificationChannel(channelId)
if (notificationChannel == null) {
val channelName = channelId
notificationChannel = NotificationChannel(
channelId,
channelName, NotificationManager.IMPORTANCE_LOW
)
notificationChannel.description =
channelName
notificationManager?.createNotificationChannel(notificationChannel)
}
}
}
companion object {
private const val channelId = "scw_download"
const val urlKey = "url"
const val nameKey = "name"
const val formatIdKey = "formatId"
const val downloadDirKey = "downloadDir"
const val sizeKey = "size"
const val audioCodecKey = "acodec"
const val videoCodecKey = "vcodec"
}
}

13
app/src/main/res/layout/dialog_download_path.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/download_path"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:layout_marginTop="10dp"
android:text="@string/placeholder" />
</RelativeLayout>

62
app/src/main/res/layout/download_row.xml

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:cardElevation="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp">
<TextView
android:id="@+id/download_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:ellipsize="end"
android:singleLine="true"
app:layout_constraintBottom_toTopOf="@+id/download_progress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/icon"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="@+id/download_progress"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="0dp"
android:layout_height="10dp"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/icon"
app:layout_constraintTop_toBottomOf="@id/download_title" />
<TextView
android:id="@+id/percentage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:paddingHorizontal="5dp"
android:textAlignment="textEnd"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/download_progress" />
<ImageView
android:id="@+id/icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:padding="8dp"
android:src="@drawable/ic_audio"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
Loading…
Cancel
Save