package com.github.libretube import android.app.DownloadManager import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.app.Service import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.net.Uri import android.os.Build import android.os.Environment import android.os.Environment.DIRECTORY_DOWNLOADS import android.os.IBinder import android.util.Log import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import com.arthenica.ffmpegkit.FFmpegKit import java.io.File var IS_DOWNLOAD_RUNNING = false class DownloadService : Service() { val TAG = "DownloadService" private var downloadId: Long = -1 private lateinit var videoId: String private lateinit var videoUrl: String private lateinit var audioUrl: String private lateinit var extension: String private var duration: Int = 0 private lateinit var audioDir: File private lateinit var videoDir: File private lateinit var notification: NotificationCompat.Builder override fun onCreate() { super.onCreate() IS_DOWNLOAD_RUNNING = true } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { videoId = intent?.getStringExtra("videoId")!! videoUrl = intent.getStringExtra("videoUrl")!! audioUrl = intent.getStringExtra("audioUrl")!! extension = intent.getStringExtra("extension")!! duration = intent.getIntExtra("duration", 1) downloadNotification(intent) downloadManager() return super.onStartCommand(intent, flags, startId) } override fun onBind(intent: Intent?): IBinder? { TODO("Not yet implemented") } private fun downloadManager() { val path = applicationContext.getExternalFilesDir(DIRECTORY_DOWNLOADS) val folder_main = ".tmp" val f = File(path, folder_main) if (!f.exists()) { f.mkdirs() Log.e(TAG, "Directory make") } else { f.deleteRecursively() f.mkdirs() Log.e(TAG, "Directory already have") } audioDir = File(f, "$videoId-audio") videoDir = File(f, "$videoId-video") try { Log.e(TAG, "Directory make") registerReceiver( onDownloadComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE) ) downloadId = downloadManagerRequest("Video", "Downloading", videoUrl, videoDir) if (videoUrl != "") { downloadId = 0L } } catch (e: IllegalArgumentException) { Log.e(TAG, "download error $e") try { downloadId = 0L downloadManagerRequest("Audio", "Downloading", audioUrl, audioDir) } catch (e: Exception) { Log.e(TAG, "audio download error $e") stopService(Intent(this, DownloadService::class.java)) } } } private val onDownloadComplete: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { // Fetching the download id received with the broadcast val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) // Checking if the received broadcast is for our enqueued download by matching download id if (downloadId == id) { downloadId = 0L try { downloadManagerRequest("Audio", "Downloading", audioUrl, audioDir) } catch (e: Exception) { } } else if (downloadId == 0L) { val libreTube = File( Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS), "LibreTube" ) if (!libreTube.exists()) { libreTube.mkdirs() Log.e(TAG, "libreTube Directory make") } else { Log.e(TAG, "libreTube Directory already have") } muxDownloadedMedia(libreTube) } } } private fun downloadManagerRequest(title: String, descriptionText: String, url: String, fileDir: File): Long { val request: DownloadManager.Request = DownloadManager.Request(Uri.parse(url)) .setTitle(title) // Title of the Download Notification .setDescription(descriptionText) // Description of the Download Notification .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) // Visibility of the download Notification .setDestinationUri(Uri.fromFile(fileDir)) .setAllowedOverMetered(true) // Set if download is allowed on Mobile network .setAllowedOverRoaming(true) // val downloadManager: DownloadManager = applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager return downloadManager.enqueue(request) } private fun createNotificationChannel(id: String, name: String, descriptionText: String, importance: Int): String { // Create the NotificationChannel, but only on API 26+ because // the NotificationChannel class is new and not in the support library return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(id, name, importance).apply { description = descriptionText } // Register the channel with the system val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) id } else "" } private fun downloadNotification(intent: Intent) { // Creating the notification channel val channelId = createNotificationChannel( "service", "service", "DownloadService", NotificationManager.IMPORTANCE_NONE ) var pendingIntent: PendingIntent? = null pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE) } else { PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT) } // Creating a notification and setting its various attributes notification = NotificationCompat.Builder(this@DownloadService, channelId) .setSmallIcon(R.drawable.ic_download) .setContentTitle("LibreTube") .setContentText("Downloading") .setPriority(NotificationCompat.PRIORITY_LOW) .setOngoing(true) .setOnlyAlertOnce(true) .setProgress(100, 0, true) .setContentIntent(pendingIntent) .setAutoCancel(true) startForeground(2, notification.build()) } private fun downloadFailedNotification() { val builder = NotificationCompat.Builder(this@DownloadService, "failed") .setSmallIcon(R.drawable.ic_download) .setContentTitle(resources.getString(R.string.downloadfailed)) .setContentText("failure") .setPriority(NotificationCompat.PRIORITY_HIGH) createNotificationChannel("failed", "failed", "Download Failed", NotificationManager.IMPORTANCE_DEFAULT) with(NotificationManagerCompat.from(this@DownloadService)) { // notificationId is a unique int for each notification that you must define notify(69, builder.build()) } } private fun muxDownloadedMedia(targetDir: File) { val command: String = when { videoUrl == "" -> { "-y -i $audioDir -c copy $targetDir/$videoId-audio$extension" } audioUrl == "" -> { "-y -i $videoDir -c copy $targetDir/$videoId-video$extension" } else -> { "-y -i $videoDir -i $audioDir -c copy $targetDir/${videoId}$extension" } } notification.setContentTitle("Muxing") FFmpegKit.executeAsync( command, { session -> val state = session.state val returnCode = session.returnCode // CALLED WHEN SESSION IS EXECUTED Log.d( TAG, String.format( "FFmpeg process exited with state %s and rc %s.%s", state, returnCode, session.failStackTrace ) ) val path = applicationContext.getExternalFilesDir(DIRECTORY_DOWNLOADS) val folder_main = ".tmp" val f = File(path, folder_main) f.deleteRecursively() if (returnCode.toString() != "0") downloadFailedNotification() stopForeground(true) stopService(Intent(this@DownloadService, DownloadService::class.java)) }, { // CALLED WHEN SESSION PRINTS LOGS Log.e(TAG, it.message.toString()) } ) { // CALLED WHEN SESSION GENERATES STATISTICS Log.e(TAG + "stat", it.time.toString()) /*val progress = it.time/(10*duration!!) if (progress<1){ notification .setProgress(progressMax, progress.toInt(), false) service.notify(1,notification.build()) }*/ } } override fun onDestroy() { try { unregisterReceiver(onDownloadComplete) } catch (e: Exception) { } IS_DOWNLOAD_RUNNING = false Log.d(TAG, "dl finished!") super.onDestroy() } }