Merge pull request #46 from libre-tube/ffmpeg

Ffmpeg
This commit is contained in:
Farbod 2022-03-05 09:09:07 +03:30 committed by GitHub
commit 5798090356
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 605 additions and 47 deletions

View File

@ -19,7 +19,7 @@ android {
buildTypes { buildTypes {
release { release {
minifyEnabled false minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
@ -36,10 +36,22 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' 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 { 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.appcompat:appcompat:1.4.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3' implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
@ -61,4 +73,6 @@ dependencies {
implementation 'com.squareup.retrofit2:converter-jackson:2.9.0' implementation 'com.squareup.retrofit2:converter-jackson:2.9.0'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.1' implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.1'
implementation 'com.squareup.retrofit2:converter-scalars:2.1.0' implementation 'com.squareup.retrofit2:converter-scalars:2.1.0'
implementation 'com.arthenica:ffmpeg-kit-min:4.5.1.LTS'
} }

View File

@ -19,3 +19,6 @@
# If you keep the line number information, uncomment this to # If you keep the line number information, uncomment this to
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-renamesourcefileattribute SourceFile
#uncomment for debug
#-keepnames class **
-keep class com.github.libretube.obj.** { *; }

View File

@ -8,12 +8,56 @@
"variantName": "release", "variantName": "release",
"elements": [ "elements": [
{ {
"type": "SINGLE", "type": "ONE_OF_MANY",
"filters": [], "filters": [
{
"filterType": "ABI",
"value": "x86_64"
}
],
"attributes": [], "attributes": [],
"versionCode": 4, "versionCode": 4,
"versionName": "0.2.2", "versionName": "0.2.2",
"outputFile": "app-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",
"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": "arm64-v8a"
}
],
"attributes": [],
"versionCode": 4,
"versionName": "0.2.2",
"outputFile": "app-arm64-v8a-release.apk"
} }
], ],
"elementType": "File" "elementType": "File"

View File

@ -4,7 +4,10 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application <application
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_libretube" android:icon="@mipmap/ic_libretube"
@ -15,6 +18,7 @@
android:name=".myApp" android:name=".myApp"
android:networkSecurityConfig="@xml/network_security_config" android:networkSecurityConfig="@xml/network_security_config"
android:largeHeap="true" android:largeHeap="true"
android:requestLegacyExternalStorage="true"
> >
<activity <activity
android:name=".Player" android:name=".Player"
@ -35,6 +39,10 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<service
android:name=".DownloadService"
android:enabled="true"
android:exported="false" />
</application> </application>
</manifest> </manifest>

View File

@ -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<String>()
var vidUrl = arrayListOf<String>()
var audioName = arrayListOf<String>()
var audioUrl = arrayListOf<String>()
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<String>
vidUrl = arguments?.getStringArrayList("videoUrl") as ArrayList<String>
audioName = arguments?.getStringArrayList("audioName") as ArrayList<String>
audioUrl = arguments?.getStringArrayList("audioUrl") as ArrayList<String>
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<Spinner>(R.id.video_spinner)
val videoArrayAdapter =ArrayAdapter<String>(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<Spinner>(R.id.audio_spinner)
val audioArrayAdapter = ArrayAdapter<String>(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<RadioGroup>(R.id.radioGp)
radioGroup.setOnCheckedChangeListener { group, checkedId ->
val radio: RadioButton = view.findViewById(checkedId)
extension = radio.text.toString()
Log.d(TAG,extension)
}
view.findViewById<Button>(R.id.download).setOnClickListener {
val intent = Intent(context,DownloadService::class.java)
intent.putExtra("videoId",videoId)
intent.putExtra("videoUrl",vidUrl[selectedVideo])
intent.putExtra("audioUrl",audioUrl[selectedAudio])
intent.putExtra("duration",duration)
intent.putExtra("extension",extension)
//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)
dismiss()
}
builder.setView(view)
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
override fun onDestroy() {
vidName.clear()
vidUrl.clear()
audioUrl.clear()
audioName.clear()
super.onDestroy()
}
}

View File

@ -0,0 +1,254 @@
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 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 command: String
private lateinit var audioDir: File
private lateinit var videoDir: File
lateinit var service: NotificationManager
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")!!
//command = intent.getStringExtra("command")!!
duration = intent.getIntExtra("duration",1)
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)
""
}
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(1,notification.build())
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 {
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))
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)
if(audioUrl==""){downloadId = 0L}
} catch (e: IllegalArgumentException) {
Log.e(TAG, "download error $e")
try{
downloadId = 0L
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)
}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{
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)
}catch (e: Exception){}
}else if (downloadId == 0L){
val libreTube = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),"LibreTube")
if (!libreTube.exists()) {
libreTube.mkdirs()
Log.e(TAG, "libreTube Directory make")
} else {
Log.e(TAG, "libreTube Directory already have")
}
var command: String = when {
videoUrl=="" -> {
"-y -i $audioDir -c copy ${libreTube}/${videoId}-audio$extension"
}
audioUrl=="" -> {
"-y -i $videoDir -c copy ${libreTube}/${videoId}-video$extension"
}
else -> {
"-y -i $videoDir -i $audioDir -c copy ${libreTube}/${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(Environment.DIRECTORY_DOWNLOADS)
val folder_main = ".tmp"
val f = File(path, folder_main)
f.deleteRecursively()
if (returnCode.toString()!="0"){
var builder = NotificationCompat.Builder(this@DownloadService, "failed")
.setSmallIcon(R.drawable.ic_download)
.setContentTitle(resources.getString(R.string.downloadfailed))
.setContentText("failure")
.setPriority(NotificationCompat.PRIORITY_HIGH)
createNotificationChannel()
with(NotificationManagerCompat.from(this@DownloadService)) {
// notificationId is a unique int for each notification that you must define
notify(69, builder.build())
}
}
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())
}*/
}
}
}
}
private fun createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = "failed"
val descriptionText = "Download Failed"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel("failed", name, importance).apply {
description = descriptionText
}
// Register the channel with the system
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
override fun onDestroy() {
try {
unregisterReceiver(onDownloadComplete)
}catch (e: Exception){}
IS_DOWNLOAD_RUNNING = false
Log.d(TAG,"dl finished!")
super.onDestroy()
}
}

View File

@ -1,58 +1,55 @@
package com.github.libretube package com.github.libretube
import android.Manifest
import android.annotation.SuppressLint 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.app.Activity
import android.app.ActivityManager
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.widget.* import android.content.pm.PackageManager
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.Build
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import android.os.Environment
import android.text.Html import android.text.Html
import android.util.Log import android.util.Log
import android.util.TypedValue 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.appcompat.app.AlertDialog
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.widget.ConstraintLayout 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.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
import com.squareup.picasso.Picasso
import retrofit2.HttpException
import com.github.libretube.adapters.TrendingAdapter import com.github.libretube.adapters.TrendingAdapter
import com.github.libretube.obj.PipedStream import com.github.libretube.obj.PipedStream
import com.github.libretube.obj.Subscribe 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.google.android.material.button.MaterialButton
import com.squareup.picasso.Picasso
import retrofit2.HttpException
import java.io.IOException
import kotlin.math.abs
var isFullScreen = false var isFullScreen = false
@ -75,6 +72,8 @@ class PlayerFragment : Fragment() {
private lateinit var exoPlayer: ExoPlayer private lateinit var exoPlayer: ExoPlayer
private lateinit var mediaSource: MediaSource private lateinit var mediaSource: MediaSource
private lateinit var relDownloadVideo: RelativeLayout
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
arguments?.let { arguments?.let {
@ -92,6 +91,7 @@ class PlayerFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
relDownloadVideo = view.findViewById(R.id.relPlayer_download)
val mainActivity = activity as MainActivity val mainActivity = activity as MainActivity
mainActivity.findViewById<FrameLayout>(R.id.container).visibility=View.VISIBLE mainActivity.findViewById<FrameLayout>(R.id.container).visibility=View.VISIBLE
val playerMotionLayout = view.findViewById<MotionLayout>(R.id.playerMotionLayout) val playerMotionLayout = view.findViewById<MotionLayout>(R.id.playerMotionLayout)
@ -363,6 +363,79 @@ class PlayerFragment : Fragment() {
val subButton = view.findViewById<MaterialButton>(R.id.player_subscribe) val subButton = view.findViewById<MaterialButton>(R.id.player_subscribe)
isSubscribed(subButton, channelId!!) isSubscribed(subButton, channelId!!)
} }
//check if livestream
if (response.duration!!>0){
//download clicked
relDownloadVideo.setOnClickListener {
if(!IS_DOWNLOAD_RUNNING){
val mainActivity = activity as MainActivity
Log.e(TAG,"download button clicked!")
if (SDK_INT >= 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
)
}
}
var vidName = arrayListOf<String>()
vidName.add("No video")
var vidUrl = arrayListOf<String>()
vidUrl.add("")
for (vid in response.videoStreams!!){
val name = vid.quality +" "+ vid.format
vidName.add(name)
vidUrl.add(vid.url!!)
}
var audioName = arrayListOf<String>()
audioName.add("No audio")
var audioUrl = arrayListOf<String>()
audioUrl.add("")
for (audio in response.audioStreams!!){
val name = audio.quality +" "+ audio.format
audioName.add(name)
audioUrl.add(audio.url!!)
}
val newFragment = DownloadDialog()
var bundle = Bundle()
bundle.putStringArrayList("videoName",vidName)
bundle.putStringArrayList("videoUrl",vidUrl)
bundle.putStringArrayList("audioName",audioName)
bundle.putStringArrayList("audioUrl",audioUrl)
bundle.putString("videoId",videoId)
bundle.putInt("duration",response.duration)
newFragment.arguments = bundle
newFragment.show(childFragmentManager, "Download")
}else{
Toast.makeText(context, R.string.dlisinprogress, Toast.LENGTH_SHORT)
.show()
}
}
}else{
Toast.makeText(context,R.string.cannotDownload, Toast.LENGTH_SHORT).show()
}
} }
} }

View File

@ -24,7 +24,7 @@ class Settings : PreferenceFragmentCompat() {
setPreferencesFromResource(R.xml.settings, rootKey) setPreferencesFromResource(R.xml.settings, rootKey)
val instance = findPreference<ListPreference>("instance") val instance = findPreference<ListPreference>("instance")
fetchInstance() fetchInstance()
instance?.setOnPreferenceChangeListener { preference, newValue -> instance?.setOnPreferenceChangeListener { _, newValue ->
RetrofitInstance.url = newValue.toString() RetrofitInstance.url = newValue.toString()
RetrofitInstance.lazyMgr.reset() RetrofitInstance.lazyMgr.reset()
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE) val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
@ -62,7 +62,6 @@ class Settings : PreferenceFragmentCompat() {
Log.e("settings",e.toString()) Log.e("settings",e.toString())
return@launchWhenCreated return@launchWhenCreated
} }
//println("dafaq $response")
val listEntries: MutableList<String> = ArrayList() val listEntries: MutableList<String> = ArrayList()
val listEntryValues: MutableList<String> = ArrayList() val listEntryValues: MutableList<String> = ArrayList()
for(item in response){ for(item in response){

View File

@ -50,8 +50,16 @@ class TrendingAdapter(private val videoFeed: List<StreamItem>): RecyclerView.Ada
} }
} }
if (trending.thumbnail!!.isEmpty()) {
} else{
Picasso.get().load(trending.thumbnail).into(thumbnailImage) Picasso.get().load(trending.thumbnail).into(thumbnailImage)
}
if (trending.uploaderAvatar!!.isEmpty()) {
} else{
Picasso.get().load(trending.uploaderAvatar).into(channelImage) Picasso.get().load(trending.uploaderAvatar).into(channelImage)
}
holder.v.setOnClickListener{ holder.v.setOnClickListener{
var bundle = Bundle() var bundle = Bundle()
bundle.putString("videoId",trending.url!!.replace("/watch?v=","")) bundle.putString("videoId",trending.url!!.replace("/watch?v=",""))

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<ImageView
android:src="@drawable/ic_libretube_foreground"
android:layout_width="match_parent"
android:layout_height="64dp"
android:scaleType="center"
android:background="#CD5757"
android:contentDescription="@string/app_name" />
<Spinner
android:id="@+id/video_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
/>
<Spinner
android:id="@+id/audio_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"/>
<RadioGroup
android:id="@+id/radioGp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<RadioButton
android:id="@+id/mkv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=".mkv"
android:checked="true"/>
<RadioButton
android:id="@+id/mp4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=".mp4"/>
</RadioGroup>
<Button
android:id="@+id/download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/download"
android:padding="8dp"
android:layout_margin="8dp"
android:layout_gravity="center"/>
</LinearLayout>

View File

@ -101,9 +101,11 @@
</RelativeLayout> </RelativeLayout>
<RelativeLayout <RelativeLayout
android:id="@+id/relPlayer_download"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"> android:layout_weight="1"
android:background="?android:attr/selectableItemBackground">
<ImageView <ImageView
android:id="@+id/player_download" android:id="@+id/player_download"

View File

@ -23,4 +23,8 @@
<string name="login_register">Login/Register</string> <string name="login_register">Login/Register</string>
<string name="please_login">Please Login or Register from the settings to show your Subscriptions!</string> <string name="please_login">Please Login or Register from the settings to show your Subscriptions!</string>
<string name="subscribeIsEmpty">Subscribe to some channels first!</string> <string name="subscribeIsEmpty">Subscribe to some channels first!</string>
<string name="cannotDownload">Can\'t Download this stream!</string>
<string name="dlcomplete">Download is completed!</string>
<string name="dlisinprogress">Another Download is already in progress please wait till it\'s finished!</string>
<string name="downloadfailed">Download Failed!</string>
</resources> </resources>