diff --git a/app/build.gradle b/app/build.gradle
index 14a56faa6..77db803b3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -51,7 +51,7 @@ android {
}
dependencies {
- debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
+ //debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
@@ -74,5 +74,5 @@ dependencies {
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.1'
implementation 'com.squareup.retrofit2:converter-scalars:2.1.0'
- implementation 'com.arthenica:ffmpeg-kit-https:4.5.1.LTS'
+ implementation 'com.arthenica:ffmpeg-kit-min:4.5.1.LTS'
}
\ No newline at end of file
diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json
index 024e64961..6946d9a63 100644
--- a/app/release/output-metadata.json
+++ b/app/release/output-metadata.json
@@ -8,12 +8,30 @@
"variantName": "release",
"elements": [
{
- "type": "UNIVERSAL",
- "filters": [],
+ "type": "ONE_OF_MANY",
+ "filters": [
+ {
+ "filterType": "ABI",
+ "value": "x86_64"
+ }
+ ],
"attributes": [],
"versionCode": 4,
"versionName": "0.2.2",
- "outputFile": "app-universal-release.apk"
+ "outputFile": "app-x86_64-release.apk"
+ },
+ {
+ "type": "ONE_OF_MANY",
+ "filters": [
+ {
+ "filterType": "ABI",
+ "value": "x86"
+ }
+ ],
+ "attributes": [],
+ "versionCode": 4,
+ "versionName": "0.2.2",
+ "outputFile": "app-x86-release.apk"
},
{
"type": "ONE_OF_MANY",
@@ -40,32 +58,6 @@
"versionCode": 4,
"versionName": "0.2.2",
"outputFile": "app-armeabi-v7a-release.apk"
- },
- {
- "type": "ONE_OF_MANY",
- "filters": [
- {
- "filterType": "ABI",
- "value": "x86_64"
- }
- ],
- "attributes": [],
- "versionCode": 4,
- "versionName": "0.2.2",
- "outputFile": "app-x86_64-release.apk"
- },
- {
- "type": "ONE_OF_MANY",
- "filters": [
- {
- "filterType": "ABI",
- "value": "x86"
- }
- ],
- "attributes": [],
- "versionCode": 4,
- "versionName": "0.2.2",
- "outputFile": "app-x86-release.apk"
}
],
"elementType": "File"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e0cc0a308..a04fe4ee8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -7,6 +7,7 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/github/libretube/DownloadService.kt b/app/src/main/java/com/github/libretube/DownloadService.kt
new file mode 100644
index 000000000..eb194e16c
--- /dev/null
+++ b/app/src/main/java/com/github/libretube/DownloadService.kt
@@ -0,0 +1,173 @@
+package com.github.libretube
+
+import android.app.*
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.graphics.Color
+import android.net.Uri
+import android.os.Build
+import android.os.Environment
+import android.os.IBinder
+import android.util.Log
+import android.widget.Toast
+import androidx.annotation.RequiresApi
+import androidx.core.app.NotificationCompat
+import com.arthenica.ffmpegkit.FFmpegKit
+import java.io.File
+
+class DownloadService : Service(){
+ val TAG = "DownloadService"
+ private var downloadId: Long =0
+ private lateinit var videoId: String
+ private lateinit var videoUrl: String
+ private lateinit var audioUrl: String
+ private var duration: Int = 0
+ //private lateinit var command: String
+ private lateinit var audioDir: File
+ private lateinit var videoDir: File
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ videoId = intent?.getStringExtra("videoId")!!
+ videoUrl = intent.getStringExtra("videoUrl")!!
+ audioUrl = intent.getStringExtra("audioUrl")!!
+ //command = intent.getStringExtra("command")!!
+ duration = intent.getIntExtra("duration",1)
+ downloadManager()
+
+ return super.onStartCommand(intent, flags, startId)
+ }
+ override fun onBind(intent: Intent?): IBinder? {
+ TODO("Not yet implemented")
+ }
+
+ private fun downloadManager() {
+ val path = applicationContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
+ val folder_main = ".tmp"
+ val f = File(path, folder_main)
+ if (!f.exists()) {
+ f.mkdirs()
+ Log.e(TAG, "Directory make")
+ } else {
+ Log.e(TAG, "Directory already have")
+ }
+ audioDir = File(f, "$videoId-audio")
+ videoDir = File(f, "$videoId-video")
+ try {
+ Log.e(TAG, "Directory make")
+ val request: DownloadManager.Request =
+ DownloadManager.Request(Uri.parse(videoUrl))
+ .setTitle("Video") // Title of the Download Notification
+ .setDescription("Downloading") // Description of the Download Notification
+ .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) // Visibility of the download Notification
+ .setDestinationUri(Uri.fromFile(videoDir))
+ .setAllowedOverMetered(true) // Set if download is allowed on Mobile network
+ .setAllowedOverRoaming(true) //
+ val downloadManager: DownloadManager =
+ applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
+ downloadId = downloadManager.enqueue(request)
+
+ registerReceiver(onDownloadComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
+ } catch (e: IllegalArgumentException) {
+ Log.e(TAG, "download error $e")
+ }
+ }
+
+ 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) {
+ Toast.makeText(this@DownloadService, "Download Completed", Toast.LENGTH_SHORT)
+ .show()
+ downloadId=0
+ val request: DownloadManager.Request =
+ DownloadManager.Request(Uri.parse(audioUrl))
+ .setTitle("Audio") // Title of the Download Notification
+ .setDescription("Downloading") // Description of the Download Notification
+ .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) // Visibility of the download Notification
+ .setDestinationUri(Uri.fromFile(audioDir))
+ .setAllowedOverMetered(true) // Set if download is allowed on Mobile network
+ .setAllowedOverRoaming(true) //
+ val downloadManager: DownloadManager =
+ applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
+ downloadManager.enqueue(request)
+
+ }else if (downloadId == 0L){
+ val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ val channelId =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val chan = NotificationChannel("service",
+ "DownloadService", NotificationManager.IMPORTANCE_NONE)
+ chan.lightColor = Color.BLUE
+ chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
+ service.createNotificationChannel(chan)
+ "service"
+ } else {
+ // If earlier version channel ID is not used
+ // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
+ ""
+ }
+ //Toast.makeText(this,command, Toast.LENGTH_SHORT).show()
+ val pendingIntent: PendingIntent = PendingIntent.getActivity(
+ this@DownloadService, 0, intent, 0)
+ //Sets the maximum progress as 100
+ val progressMax = 100
+ //Creating a notification and setting its various attributes
+ val notification =
+ NotificationCompat.Builder(this@DownloadService, channelId)
+ .setSmallIcon(R.drawable.ic_download)
+ .setContentTitle("LibreTube")
+ .setContentText("Downloading")
+ .setPriority(NotificationCompat.PRIORITY_LOW)
+ .setOngoing(true)
+ .setOnlyAlertOnce(true)
+ .setProgress(progressMax, 0, true)
+ .setContentIntent(pendingIntent)
+ .setAutoCancel(true)
+
+ FFmpegKit.executeAsync("-y -i $videoDir -i $audioDir -c copy ${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/${videoId}.mkv",
+ { 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(Environment.DIRECTORY_DOWNLOADS)
+ val folder_main = ".tmp"
+ val f = File(path, folder_main)
+ f.deleteRecursively()
+ stopForeground(true)
+ }, {
+ // 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())
+ }
+ }
+ startForeground(1,notification.build())
+ }
+ }
+ }
+
+
+ override fun onDestroy() {
+ unregisterReceiver(onDownloadComplete)
+ super.onDestroy()
+ }
+
+}
diff --git a/app/src/main/java/com/github/libretube/PlayerFragment.kt b/app/src/main/java/com/github/libretube/PlayerFragment.kt
index ee5fb82a5..06c6e663c 100644
--- a/app/src/main/java/com/github/libretube/PlayerFragment.kt
+++ b/app/src/main/java/com/github/libretube/PlayerFragment.kt
@@ -3,15 +3,16 @@ package com.github.libretube
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
+import android.app.ActivityManager
import android.content.Context
import android.content.DialogInterface
+import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import android.os.Environment
-import android.os.Environment.DIRECTORY_DOWNLOADS
import android.text.Html
import android.util.Log
import android.util.TypedValue
@@ -23,13 +24,13 @@ import androidx.appcompat.app.AlertDialog
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat.getSystemService
import androidx.core.net.toUri
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.arthenica.ffmpegkit.FFmpegKit
import com.github.libretube.adapters.TrendingAdapter
import com.github.libretube.obj.PipedStream
import com.github.libretube.obj.Subscribe
@@ -48,7 +49,6 @@ import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
import com.google.android.material.button.MaterialButton
import com.squareup.picasso.Picasso
import retrofit2.HttpException
-import java.io.File
import java.io.IOException
import kotlin.math.abs
@@ -364,6 +364,9 @@ class PlayerFragment : Fragment() {
val subButton = view.findViewById(R.id.player_subscribe)
isSubscribed(subButton, channelId!!)
}
+ //check if livestream
+ if (response.duration!!>0){
+ //download clicked
relDownloadVideo.setOnClickListener {
val mainActivity = activity as MainActivity
Log.e(TAG,"download button clicked!")
@@ -396,11 +399,24 @@ class PlayerFragment : Fragment() {
)
}
}
- FFmpegKit.executeAsync("-i ${response.videoStreams[0].url} -i ${response.audioStreams!![0].url} -c copy ${context?.getExternalFilesDir(DIRECTORY_DOWNLOADS)}${File.separator}output2.mkv",
+ val builderr: AlertDialog.Builder? = activity?.let {
+ AlertDialog.Builder(it)
+ }
+ var videos = videosNameArray.drop(1).toTypedArray()
+ builderr!!.setTitle(R.string.choose_quality_dialog)
+ .setItems(videos,
+ DialogInterface.OnClickListener { _, which ->
+ val intent = Intent(context,DownloadService::class.java)
+ intent.putExtra("videoId",videoId)
+ intent.putExtra("videoUrl",response.videoStreams[which].url)
+ intent.putExtra("audioUrl",response.audioStreams!![0].url)
+ intent.putExtra("duration",response.duration)
+ //intent.putExtra("command","-y -i ${response.videoStreams[which].url} -i ${response.audioStreams!![0].url} -c copy ${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/${videoId}.mkv")
+ context?.startService(intent)
+ /*FFmpegKit.executeAsync("-y -i ${response.videoStreams[which].url} -i ${response.audioStreams!![0].url} -c copy ${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/${videoId}.mkv",
{ session ->
val state = session.state
val returnCode = session.returnCode
-
// CALLED WHEN SESSION IS EXECUTED
Log.d(
TAG,
@@ -413,11 +429,17 @@ class PlayerFragment : Fragment() {
)
}, {
// CALLED WHEN SESSION PRINTS LOGS
- Log.e(TAG,it.toString())
+ Log.e(TAG,it.message.toString())
}) {
// CALLED WHEN SESSION GENERATES STATISTICS
- }
-
+ Log.e(TAG,it.time.toString())
+ }*/
+ })
+ val dialog: AlertDialog? = builderr?.create()
+ dialog?.show()
+ }
+ }else{
+ Toast.makeText(context,R.string.cannotDownload, Toast.LENGTH_SHORT).show()
}
}
}
@@ -427,8 +449,15 @@ class PlayerFragment : Fragment() {
}
-
-
+/* private fun isMyServiceRunning(serviceClass: Class<*>): Boolean {
+ val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager?
+ for (service in manager!!.getRunningServices(Int.MAX_VALUE)) {
+ if (serviceClass.name == service.service.className) {
+ return true
+ }
+ }
+ return false
+ }*/
private fun isSubscribed(button: MaterialButton, channel_id: String){
@SuppressLint("ResourceAsColor")
fun run() {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a21a2ad6c..3c24751f7 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -23,4 +23,5 @@
Login/Register
Please Login or Register from the settings to show your Subscriptions!
Subscribe to some channels first!
+ Can\'t Download this stream!
\ No newline at end of file