mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-13 22:00:30 +05:30
commit
5798090356
@ -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'
|
||||||
}
|
}
|
3
app/proguard-rules.pro
vendored
3
app/proguard-rules.pro
vendored
@ -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.** { *; }
|
||||||
|
@ -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"
|
||||||
|
@ -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>
|
96
app/src/main/java/com/github/libretube/DownloadDialog.kt
Normal file
96
app/src/main/java/com/github/libretube/DownloadDialog.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
254
app/src/main/java/com/github/libretube/DownloadService.kt
Normal file
254
app/src/main/java/com/github/libretube/DownloadService.kt
Normal 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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){
|
||||||
|
@ -50,8 +50,16 @@ class TrendingAdapter(private val videoFeed: List<StreamItem>): RecyclerView.Ada
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Picasso.get().load(trending.thumbnail).into(thumbnailImage)
|
if (trending.thumbnail!!.isEmpty()) {
|
||||||
Picasso.get().load(trending.uploaderAvatar).into(channelImage)
|
} else{
|
||||||
|
Picasso.get().load(trending.thumbnail).into(thumbnailImage)
|
||||||
|
}
|
||||||
|
if (trending.uploaderAvatar!!.isEmpty()) {
|
||||||
|
} else{
|
||||||
|
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=",""))
|
||||||
|
53
app/src/main/res/layout/dialog_download.xml
Normal file
53
app/src/main/res/layout/dialog_download.xml
Normal 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>
|
@ -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"
|
||||||
|
@ -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>
|
Loading…
Reference in New Issue
Block a user