From 181c6f49c67f3ce371452cf7e550d5823b990c63 Mon Sep 17 00:00:00 2001 From: rimthekid Date: Sat, 26 Feb 2022 21:19:42 +0400 Subject: [PATCH 1/8] . --- app/build.gradle | 4 +- app/src/main/AndroidManifest.xml | 2 + .../com/github/libretube/PlayerFragment.kt | 90 +++++++++++-------- .../java/com/github/libretube/Settings.kt | 3 +- app/src/main/res/layout/fragment_player.xml | 1 + 5 files changed, 62 insertions(+), 38 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index cd43f2185..974a0412a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,7 +8,7 @@ android { defaultConfig { applicationId 'com.github.libretube' - minSdk 21 + minSdk 24 targetSdk 31 versionCode 4 versionName '0.2.2' @@ -61,4 +61,6 @@ dependencies { implementation 'com.squareup.retrofit2:converter-jackson:2.9.0' implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.1' implementation 'com.squareup.retrofit2:converter-scalars:2.1.0' + + implementation 'com.arthenica:ffmpeg-kit-full:4.5.1' } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e4a15fec5..f27bc0190 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -35,6 +35,8 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/github/libretube/PlayerFragment.kt b/app/src/main/java/com/github/libretube/PlayerFragment.kt index 8e0ec11b9..f0a9a06f0 100644 --- a/app/src/main/java/com/github/libretube/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/PlayerFragment.kt @@ -1,58 +1,49 @@ package com.github.libretube import android.annotation.SuppressLint -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.constraintlayout.motion.widget.MotionLayout -import androidx.recyclerview.widget.RecyclerView -import com.google.android.exoplayer2.ExoPlayer -import com.google.android.exoplayer2.MediaItem -import com.google.android.exoplayer2.MediaItem.SubtitleConfiguration -import com.google.android.exoplayer2.source.MediaSource - -import com.google.android.exoplayer2.ui.StyledPlayerView - -import java.io.IOException -import kotlin.math.abs -import com.google.android.exoplayer2.util.MimeTypes -import com.google.common.collect.ImmutableList -import android.app.ActionBar import android.app.Activity import android.content.Context import android.content.DialogInterface import android.content.pm.ActivityInfo -import android.widget.* -import androidx.core.net.toUri -import com.google.android.exoplayer2.MediaItem.fromUri -import com.google.android.exoplayer2.source.MergingMediaSource -import com.google.android.exoplayer2.source.ProgressiveMediaSource -import com.google.android.exoplayer2.upstream.DataSource - -import com.google.android.exoplayer2.upstream.DefaultHttpDataSource -import android.widget.TextView - import android.os.Build +import android.os.Bundle import android.text.Html import android.util.Log import android.util.TypedValue +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* import androidx.appcompat.app.AlertDialog +import androidx.constraintlayout.motion.widget.MotionLayout import androidx.constraintlayout.widget.ConstraintLayout +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 com.google.android.exoplayer2.Player -import com.google.android.exoplayer2.source.DefaultMediaSourceFactory - -import com.squareup.picasso.Picasso -import retrofit2.HttpException +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 +import com.google.android.exoplayer2.ExoPlayer +import com.google.android.exoplayer2.MediaItem +import com.google.android.exoplayer2.MediaItem.SubtitleConfiguration +import com.google.android.exoplayer2.MediaItem.fromUri +import com.google.android.exoplayer2.Player +import com.google.android.exoplayer2.source.DefaultMediaSourceFactory +import com.google.android.exoplayer2.source.MediaSource +import com.google.android.exoplayer2.source.MergingMediaSource +import com.google.android.exoplayer2.source.ProgressiveMediaSource +import com.google.android.exoplayer2.ui.StyledPlayerView +import com.google.android.exoplayer2.upstream.DataSource +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.IOException +import kotlin.math.abs var isFullScreen = false @@ -75,6 +66,8 @@ class PlayerFragment : Fragment() { private lateinit var exoPlayer: ExoPlayer private lateinit var mediaSource: MediaSource + private lateinit var relDownloadVideo: RelativeLayout + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { @@ -92,6 +85,7 @@ class PlayerFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + relDownloadVideo = view.findViewById(R.id.relPlayer_download) val mainActivity = activity as MainActivity mainActivity.findViewById(R.id.container).visibility=View.VISIBLE val playerMotionLayout = view.findViewById(R.id.playerMotionLayout) @@ -363,6 +357,30 @@ class PlayerFragment : Fragment() { val subButton = view.findViewById(R.id.player_subscribe) isSubscribed(subButton, channelId!!) } + relDownloadVideo.setOnClickListener { + Log.e(TAG,"download button clicked!") + FFmpegKit.executeAsync("-i ${response.videoStreams[0].url} -i ${response.audioStreams!![0].url} -c copy output.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 + ) + ) + }, { + // CALLED WHEN SESSION PRINTS LOGS + }) { + // CALLED WHEN SESSION GENERATES STATISTICS + } + + } } } @@ -371,6 +389,8 @@ class PlayerFragment : Fragment() { } + + private fun isSubscribed(button: MaterialButton, channel_id: String){ @SuppressLint("ResourceAsColor") fun run() { diff --git a/app/src/main/java/com/github/libretube/Settings.kt b/app/src/main/java/com/github/libretube/Settings.kt index 93cafa75d..3454b7ccb 100644 --- a/app/src/main/java/com/github/libretube/Settings.kt +++ b/app/src/main/java/com/github/libretube/Settings.kt @@ -24,7 +24,7 @@ class Settings : PreferenceFragmentCompat() { setPreferencesFromResource(R.xml.settings, rootKey) val instance = findPreference("instance") fetchInstance() - instance?.setOnPreferenceChangeListener { preference, newValue -> + instance?.setOnPreferenceChangeListener { _, newValue -> RetrofitInstance.url = newValue.toString() RetrofitInstance.lazyMgr.reset() val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE) @@ -62,7 +62,6 @@ class Settings : PreferenceFragmentCompat() { Log.e("settings",e.toString()) return@launchWhenCreated } - //println("dafaq $response") val listEntries: MutableList = ArrayList() val listEntryValues: MutableList = ArrayList() for(item in response){ diff --git a/app/src/main/res/layout/fragment_player.xml b/app/src/main/res/layout/fragment_player.xml index 041f6ff40..8ad0a1a43 100644 --- a/app/src/main/res/layout/fragment_player.xml +++ b/app/src/main/res/layout/fragment_player.xml @@ -101,6 +101,7 @@ From 13d86cce193113491ec02a9c54933b3b1852e240 Mon Sep 17 00:00:00 2001 From: rimthekid Date: Sat, 26 Feb 2022 22:57:05 +0400 Subject: [PATCH 2/8] ffmpeg init --- app/build.gradle | 4 +- app/src/main/AndroidManifest.xml | 3 +- .../com/github/libretube/PlayerFragment.kt | 40 ++++++++++++++++++- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 974a0412a..21cd69f15 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,7 +8,7 @@ android { defaultConfig { applicationId 'com.github.libretube' - minSdk 24 + minSdk 21 targetSdk 31 versionCode 4 versionName '0.2.2' @@ -62,5 +62,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-full:4.5.1' + implementation 'com.arthenica:ffmpeg-kit-https:4.5.1.LTS' } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f27bc0190..4e8448cbe 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,7 +4,8 @@ - + + = Build.VERSION_CODES.R) { + Log.d("myz", "" + SDK_INT) + if (!Environment.isExternalStorageManager()) { + ActivityCompat.requestPermissions( + mainActivity, arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.MANAGE_EXTERNAL_STORAGE + ), 1 + ) //permission request code is just an int + } + } else { + if (ActivityCompat.checkSelfPermission( + requireContext(), + Manifest.permission.READ_EXTERNAL_STORAGE + ) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission( + requireContext(), + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) != PackageManager.PERMISSION_GRANTED + ) { + ActivityCompat.requestPermissions( + mainActivity, + arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ), + 1 + ) + } + } + FFmpegKit.executeAsync("-i ${response.videoStreams[0].url} -i ${response.audioStreams!![0].url} -c copy ${context?.getExternalFilesDir(DIRECTORY_DOWNLOADS)}${File.separator}output1.mkv", { session -> val state = session.state val returnCode = session.returnCode @@ -376,6 +413,7 @@ class PlayerFragment : Fragment() { ) }, { // CALLED WHEN SESSION PRINTS LOGS + Log.e(TAG,it.toString()) }) { // CALLED WHEN SESSION GENERATES STATISTICS } From 6879c91dd213767f7da3371f3c52571b6f835281 Mon Sep 17 00:00:00 2001 From: rimthekid Date: Sun, 27 Feb 2022 21:51:17 +0400 Subject: [PATCH 3/8] shrink apk size and enable minify --- app/build.gradle | 14 ++++- app/proguard-rules.pro | 5 +- app/release/output-metadata.json | 56 ++++++++++++++++++- app/src/main/AndroidManifest.xml | 3 +- .../com/github/libretube/PlayerFragment.kt | 2 +- .../libretube/adapters/TrendingAdapter.kt | 12 +++- 6 files changed, 83 insertions(+), 9 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 21cd69f15..14a56faa6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,7 +19,7 @@ android { buildTypes { release { - minifyEnabled false + minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } @@ -36,6 +36,18 @@ android { kotlinOptions { jvmTarget = '1.8' } + splits { + abi { + enable true + reset() + include "armeabi-v7a","arm64-v8a","x86","x86_64" + universalApk false + + } + } + packagingOptions { + exclude 'lib/armeabi-v7a/*_neon.so' + } } dependencies { diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 481bb4348..6cd107af9 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -18,4 +18,7 @@ # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +#-renamesourcefileattribute SourceFile +#uncomment for debug +#-keepnames class ** +-keep class com.github.libretube.obj.** { *; } diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index 9ae28ab5c..024e64961 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -8,12 +8,64 @@ "variantName": "release", "elements": [ { - "type": "SINGLE", + "type": "UNIVERSAL", "filters": [], "attributes": [], "versionCode": 4, "versionName": "0.2.2", - "outputFile": "app-release.apk" + "outputFile": "app-universal-release.apk" + }, + { + "type": "ONE_OF_MANY", + "filters": [ + { + "filterType": "ABI", + "value": "arm64-v8a" + } + ], + "attributes": [], + "versionCode": 4, + "versionName": "0.2.2", + "outputFile": "app-arm64-v8a-release.apk" + }, + { + "type": "ONE_OF_MANY", + "filters": [ + { + "filterType": "ABI", + "value": "armeabi-v7a" + } + ], + "attributes": [], + "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 4e8448cbe..e0cc0a308 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ + - - \ No newline at end of file diff --git a/app/src/main/java/com/github/libretube/PlayerFragment.kt b/app/src/main/java/com/github/libretube/PlayerFragment.kt index 83f7dc363..ee5fb82a5 100644 --- a/app/src/main/java/com/github/libretube/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/PlayerFragment.kt @@ -396,7 +396,7 @@ class PlayerFragment : Fragment() { ) } } - FFmpegKit.executeAsync("-i ${response.videoStreams[0].url} -i ${response.audioStreams!![0].url} -c copy ${context?.getExternalFilesDir(DIRECTORY_DOWNLOADS)}${File.separator}output1.mkv", + FFmpegKit.executeAsync("-i ${response.videoStreams[0].url} -i ${response.audioStreams!![0].url} -c copy ${context?.getExternalFilesDir(DIRECTORY_DOWNLOADS)}${File.separator}output2.mkv", { session -> val state = session.state val returnCode = session.returnCode diff --git a/app/src/main/java/com/github/libretube/adapters/TrendingAdapter.kt b/app/src/main/java/com/github/libretube/adapters/TrendingAdapter.kt index 6fabbd68f..5cfdb6316 100644 --- a/app/src/main/java/com/github/libretube/adapters/TrendingAdapter.kt +++ b/app/src/main/java/com/github/libretube/adapters/TrendingAdapter.kt @@ -50,8 +50,16 @@ class TrendingAdapter(private val videoFeed: List): RecyclerView.Ada } } - Picasso.get().load(trending.thumbnail).into(thumbnailImage) - Picasso.get().load(trending.uploaderAvatar).into(channelImage) + if (trending.thumbnail!!.isEmpty()) { + } else{ + Picasso.get().load(trending.thumbnail).into(thumbnailImage) + } + if (trending.uploaderAvatar!!.isEmpty()) { + } else{ + Picasso.get().load(trending.uploaderAvatar).into(channelImage) + } + + holder.v.setOnClickListener{ var bundle = Bundle() bundle.putString("videoId",trending.url!!.replace("/watch?v=","")) From 7159eb0472acbdff4f2164b0bd17ee585d2d6df6 Mon Sep 17 00:00:00 2001 From: rimthekid Date: Thu, 3 Mar 2022 10:38:36 +0400 Subject: [PATCH 4/8] use download manager --- app/build.gradle | 4 +- app/release/output-metadata.json | 50 +++-- app/src/main/AndroidManifest.xml | 6 + .../com/github/libretube/DownloadService.kt | 173 ++++++++++++++++++ .../com/github/libretube/PlayerFragment.kt | 49 ++++- app/src/main/res/values/strings.xml | 1 + 6 files changed, 242 insertions(+), 41 deletions(-) create mode 100644 app/src/main/java/com/github/libretube/DownloadService.kt 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 From f8a0c949f95535723dd8b31aa6e67a3539edf224 Mon Sep 17 00:00:00 2001 From: rimthekid Date: Fri, 4 Mar 2022 19:57:10 +0400 Subject: [PATCH 5/8] download dialog --- app/release/output-metadata.json | 8 +- .../com/github/libretube/DownloadDialog.kt | 96 ++++++++++++++++ .../com/github/libretube/DownloadService.kt | 105 +++++++++++++----- .../com/github/libretube/PlayerFragment.kt | 82 ++++++-------- app/src/main/res/layout/dialog_download.xml | 53 +++++++++ app/src/main/res/layout/fragment_player.xml | 3 +- app/src/main/res/values/strings.xml | 2 + 7 files changed, 269 insertions(+), 80 deletions(-) create mode 100644 app/src/main/java/com/github/libretube/DownloadDialog.kt create mode 100644 app/src/main/res/layout/dialog_download.xml diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index 6946d9a63..8d992d142 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -25,13 +25,13 @@ "filters": [ { "filterType": "ABI", - "value": "x86" + "value": "armeabi-v7a" } ], "attributes": [], "versionCode": 4, "versionName": "0.2.2", - "outputFile": "app-x86-release.apk" + "outputFile": "app-armeabi-v7a-release.apk" }, { "type": "ONE_OF_MANY", @@ -51,13 +51,13 @@ "filters": [ { "filterType": "ABI", - "value": "armeabi-v7a" + "value": "x86" } ], "attributes": [], "versionCode": 4, "versionName": "0.2.2", - "outputFile": "app-armeabi-v7a-release.apk" + "outputFile": "app-x86-release.apk" } ], "elementType": "File" diff --git a/app/src/main/java/com/github/libretube/DownloadDialog.kt b/app/src/main/java/com/github/libretube/DownloadDialog.kt new file mode 100644 index 000000000..133b83dfb --- /dev/null +++ b/app/src/main/java/com/github/libretube/DownloadDialog.kt @@ -0,0 +1,96 @@ +package com.github.libretube + +import android.app.Dialog +import android.content.Intent +import android.os.Bundle +import android.util.Log +import android.view.View +import android.widget.* +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment + + +class DownloadDialog : DialogFragment() { + private val TAG = "DownloadDialog" + var vidName = arrayListOf() + var vidUrl = arrayListOf() + var audioName = arrayListOf() + var audioUrl = arrayListOf() + var selectedVideo = 0 + var selectedAudio = 0 + var extension = ".mkv" + var duration = 0 + private lateinit var videoId: String + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return activity?.let { + vidName = arguments?.getStringArrayList("videoName") as ArrayList + vidUrl = arguments?.getStringArrayList("videoUrl") as ArrayList + audioName = arguments?.getStringArrayList("audioName") as ArrayList + audioUrl = arguments?.getStringArrayList("audioUrl") as ArrayList + duration = arguments?.getInt("duration")!! + videoId = arguments?.getString("videoId")!! + val builder = AlertDialog.Builder(it) + // Get the layout inflater + val inflater = requireActivity().layoutInflater + var view: View = inflater.inflate(R.layout.dialog_download, null) + val videoSpinner = view.findViewById(R.id.video_spinner) + val videoArrayAdapter =ArrayAdapter(requireContext(),android.R.layout.simple_spinner_item,vidName) + videoArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + videoSpinner.adapter=videoArrayAdapter + videoSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>, + view: View, + position: Int, + id: Long + ) { + selectedVideo = position + Log.d(TAG,selectedVideo.toString()) + } + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + val audioSpinner = view.findViewById(R.id.audio_spinner) + val audioArrayAdapter = ArrayAdapter(requireContext(),android.R.layout.simple_spinner_item,audioName) + audioArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + audioSpinner.adapter = audioArrayAdapter + audioSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>, + view: View, + position: Int, + id: Long + ) { + selectedAudio = position + Log.d(TAG,selectedAudio.toString()) + } + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + val radioGroup = view.findViewById(R.id.radioGp) + radioGroup.setOnCheckedChangeListener { group, checkedId -> + val radio: RadioButton = view.findViewById(checkedId) + extension = radio.text.toString() + Log.d(TAG,extension) + } + view.findViewById