FFmpeg -> jniLibs Fix

This commit is contained in:
shabinder 2021-09-04 00:28:13 +05:30
parent 9d445fe34b
commit 4dda5037dd
24 changed files with 165 additions and 88 deletions

View File

@ -36,7 +36,6 @@ repositories {
android {
val props = gradleLocalProperties(rootDir)
if (props.containsKey("storeFileDir")) {
signingConfigs {
create("release") {

View File

@ -49,11 +49,13 @@
android:usesCleartextTraffic="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
android:icon="@mipmap/ic_launcher"
android:hardwareAccelerated="true"
android:largeHeap="true"
android:label="SpotiFlyer"
android:roundIcon="@mipmap/ic_launcher_round"
android:configChanges="orientation|screenSize"
android:forceDarkAllowed="true"
android:extractNativeLibs="true"
android:requestLegacyExternalStorage="true"
tools:targetApi="q">
<activity android:name=".MainActivity"

View File

@ -102,6 +102,7 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
preferenceManager.analyticsManager = analyticsManager
// This app draws behind the system bars, so we want to handle fitting system windows
WindowCompat.setDecorFitsSystemWindows(window, false)
rootComponent = spotiFlyerRoot(defaultComponentContext())
@ -137,9 +138,7 @@ class MainActivity : ComponentActivity() {
AnalyticsDialog(
askForAnalyticsPermission,
enableAnalytics = {
preferenceManager.toggleAnalytics(true) {
analyticsManager.giveConsent()
}
preferenceManager.toggleAnalytics(true)
preferenceManager.firstLaunchDone()
},
dismissDialog = {

View File

@ -46,6 +46,7 @@ import com.shabinder.common.translations.Strings
import com.shabinder.spotiflyer.R
import com.shabinder.spotiflyer.utils.autoclear.AutoClear
import com.shabinder.spotiflyer.utils.autoclear.autoClear
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
@ -184,7 +185,14 @@ class ForegroundService : LifecycleService() {
addToNotification(Message(track.title, DownloadStatus.Converting))
// All Processing Completed for this Track
job.invokeOnCompletion {
job.invokeOnCompletion { throwable ->
if(throwable != null && throwable !is CancellationException) {
// handle error
failed++
trackStatusFlowMap[track.title] = DownloadStatus.Failed(throwable)
removeFromNotification(Message(track.title, DownloadStatus.Converting))
return@invokeOnCompletion
}
converted++
trackStatusFlowMap[track.title] = DownloadStatus.Downloaded
removeFromNotification(Message(track.title, DownloadStatus.Converting))

View File

@ -16,14 +16,13 @@
@file:Suppress("MayBeConstant", "SpellCheckingInspection")
import org.gradle.api.Action
import org.gradle.api.artifacts.ExternalModuleDependency
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.kotlin.dsl.accessors.runtime.addDependencyTo
object Versions {
// App's Version (To be bumped at each update)
const val versionName = "3.2.5"
const val versionName = "3.3.0"
const val versionCode = 24

View File

@ -20,6 +20,7 @@ class AndroidMediaConverter(private val appContext: Context) : MediaConverter()
progressCallbacks: (Long) -> Unit,
) = executeSafelyInPool {
var progressing = true
var error = ""
var timeout = 600_000L * 2 // 20 min
val progressDelayCheck = 500L
// 192 is Default
@ -47,17 +48,19 @@ class AndroidMediaConverter(private val appContext: Context) : MediaConverter()
}
override fun onFailure(message: String?) {
Log.d("FFmpeg Command", "Failed $message")
error = "Failed: $message $inputFilePath"
error += "FFmpeg Support" + FFmpeg.getInstance(appContext).isSupported.toString()
Log.d("FFmpeg Error", error)
progressing = false
throw SpotiFlyerException.MP3ConversionFailed(message = "Android FFmpeg Failed: $message")
}
}
)
while (progressing) {
if (timeout < 0) throw SpotiFlyerException.MP3ConversionFailed("Conversion Timeout for $inputFilePath")
if (timeout < 0) throw SpotiFlyerException.MP3ConversionFailed("$error Conversion Timeout for $inputFilePath")
delay(progressDelayCheck)
timeout -= progressDelayCheck
}
if(error.isNotBlank()) throw SpotiFlyerException.MP3ConversionFailed(error)
// Return output file path after successful conversion
outputFilePath
}

View File

@ -30,6 +30,7 @@ import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import org.koin.core.module.Module
import kotlin.math.roundToInt
@ -102,28 +103,26 @@ fun getNameURL(url: String): String {
suspend fun downloadFile(url: String): Flow<DownloadResult> {
return flow {
try {
val client = createHttpClient()
val response = client.get<HttpStatement>(url).execute()
val data = ByteArray(response.contentLength()!!.toInt())
var offset = 0
do {
// Set Length optimally, after how many kb you want a progress update, now it 0.25mb
val currentRead = response.content.readAvailable(data, offset, 2_50_000)
offset += currentRead
val progress = (offset * 100f / data.size).roundToInt()
emit(DownloadResult.Progress(progress))
} while (currentRead > 0)
if (response.status.isSuccess()) {
emit(DownloadResult.Success(data))
} else {
emit(DownloadResult.Error("File not downloaded"))
}
client.close()
} catch (e: Exception) {
e.printStackTrace()
emit(DownloadResult.Error(e.message ?: "File not downloaded"))
val client = createHttpClient()
val response = client.get<HttpStatement>(url).execute()
val data = ByteArray(response.contentLength()!!.toInt())
var offset = 0
do {
// Set Length optimally, after how many kb you want a progress update, now it 0.25mb
val currentRead = response.content.readAvailable(data, offset, 2_50_000)
offset += currentRead
val progress = (offset * 100f / data.size).roundToInt()
emit(DownloadResult.Progress(progress))
} while (currentRead > 0)
if (response.status.isSuccess()) {
emit(DownloadResult.Success(data))
} else {
emit(DownloadResult.Error("File not downloaded"))
}
client.close()
}.catch { e ->
e.printStackTrace()
emit(DownloadResult.Error(e.message ?: "File not downloaded"))
}
}

View File

@ -1,10 +1,11 @@
package com.shabinder.common.core_components.preference_manager
import com.russhwolf.settings.Settings
import com.shabinder.common.core_components.analytics.AnalyticsManager
import com.shabinder.common.models.AudioQuality
class PreferenceManager(
settings: Settings
settings: Settings,
) : Settings by settings {
companion object {
@ -15,11 +16,15 @@ class PreferenceManager(
const val PREFERRED_AUDIO_QUALITY = "preferredAudioQuality"
}
lateinit var analyticsManager: AnalyticsManager
/* ANALYTICS */
val isAnalyticsEnabled get() = getBooleanOrNull(ANALYTICS_KEY) ?: false
fun toggleAnalytics(enabled: Boolean,f: () -> Unit = {}) {
fun toggleAnalytics(enabled: Boolean) {
putBoolean(ANALYTICS_KEY, enabled)
f()
if (this::analyticsManager.isInitialized) {
if (enabled) analyticsManager.giveConsent() else analyticsManager.revokeConsent()
}
}
/* DOWNLOAD DIRECTORY */

View File

@ -82,9 +82,7 @@ internal class SpotiFlyerMainStoreProvider(dependencies: SpotiFlyerMain.Dependen
is Intent.SelectCategory -> dispatch(Result.CategoryChanged(intent.category))
is Intent.ToggleAnalytics -> {
dispatch(Result.AnalyticsToggled(intent.enabled))
preferenceManager.toggleAnalytics(intent.enabled) {
if (intent.enabled) analyticsManager.giveConsent() else analyticsManager.revokeConsent()
}
preferenceManager.toggleAnalytics(intent.enabled)
}
}
}

View File

@ -61,9 +61,7 @@ internal class SpotiFlyerPreferenceStoreProvider(
is Intent.ShareApp -> methods.value.shareApp()
is Intent.ToggleAnalytics -> {
dispatch(Result.AnalyticsToggled(intent.enabled))
preferenceManager.toggleAnalytics(intent.enabled) {
if (intent.enabled) analyticsManager.giveConsent() else analyticsManager.revokeConsent()
}
preferenceManager.toggleAnalytics(intent.enabled)
}
is Intent.SetDownloadDirectory -> {
dispatch(Result.DownloadPathSet(intent.path))

View File

@ -105,8 +105,10 @@ private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot =
override val fetchQuery: FetchPlatformQueryResult = koin.get()
override val fileManager: FileManager = koin.get()
override val database: Database? = fileManager.db
override val preferenceManager: PreferenceManager = koin.get()
override val analyticsManager: AnalyticsManager = koin.get()
override val preferenceManager: PreferenceManager = koin.get<PreferenceManager>().also {
it.analyticsManager = analyticsManager
}
override val downloadProgressFlow = DownloadProgressFlow
override val actions: Actions = object : Actions {
override val platformActions = object : PlatformActions {}

View File

@ -0,0 +1,73 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
ext {
publishedGroupId = 'nl.bravobit'
libraryName = 'Android FFmpeg'
artifact = 'android-ffmpeg'
libraryDescription = 'FFmpeg/FFprobe compiled for Android. Execute FFmpeg and FFprobe commands with ease in your Android project.'
siteUrl = 'https://github.com/bravobit/FFmpeg-Android'
gitUrl = 'https://github.com/bravobit/FFmpeg-Android.git'
libraryVersion = '1.1.7'
developerId = 'Bravobit'
developerName = 'Bravobit'
developerEmail = 'info@bravobit.nl'
licenseName = 'GNU General Public License v3.0'
licenseUrl = 'https://github.com/bravobit/FFmpeg-Android/blob/master/LICENSE'
allLicenses = ["GPL-3.0"]
}
android {
compileSdkVersion 29
defaultConfig {
minSdkVersion 16
targetSdkVersion 29
versionCode 18
versionName "1.2.1"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.3.1'
}
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives javadocJar
archives sourcesJar
}

View File

@ -1,6 +1,6 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.kts.
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

View File

@ -0,0 +1,5 @@
package nl.bravobit.ffmpeg;
public enum CpuArch {
ARMv7, x86, x86_64, ARM_64, NONE
}

View File

@ -0,0 +1,27 @@
package nl.bravobit.ffmpeg;
import android.os.Build;
public class CpuArchHelper {
public static final String X86_CPU = "x86";
public static final String X86_64_CPU = "x86_64";
public static final String ARM_64_CPU = "arm64-v8a";
public static final String ARM_V7_CPU = "armeabi-v7a";
public static CpuArch getCpuArch() {
Log.d("Build.CPU_ABI : " + Build.CPU_ABI);
switch (Build.CPU_ABI) {
case X86_CPU:
return CpuArch.x86;
case X86_64_CPU:
return CpuArch.x86_64;
case ARM_64_CPU:
return CpuArch.ARM_64;
case ARM_V7_CPU:
return CpuArch.ARMv7;
default:
return CpuArch.NONE;
}
}
}

View File

@ -4,6 +4,7 @@ import android.content.Context;
import android.os.AsyncTask;
import java.io.File;
import java.io.IOException;
import java.util.Map;
public class FFmpeg implements FFbinaryInterface {
@ -34,7 +35,6 @@ public class FFmpeg implements FFbinaryInterface {
@Override
public boolean isSupported() {
// get ffmpeg file
File ffmpeg = FileUtils.getFFmpeg(context.provide());

View File

@ -1,42 +0,0 @@
package nl.bravobit.ffmpeg
import android.content.Context
object FFmpegConfig {
fun versionFFmpeg(context: Context) {
FFmpeg.getInstance(context).execute(arrayOf("-version"), object : ExecuteBinaryResponseHandler() {
override fun onSuccess(message: String) {
Log.d(message)
}
override fun onProgress(message: String) {
Log.d(message)
}
})
}
fun codecsFFmpeg(context: Context) {
FFmpeg.getInstance(context).execute(arrayOf("-codecs"), object : ExecuteBinaryResponseHandler() {
override fun onSuccess(message: String) {
Log.d(message)
}
override fun onProgress(message: String) {
Log.d(message)
}
})
}
fun versionFFprobe(context: Context) {
Log.d("version ffprobe")
FFprobe.getInstance(context).execute(arrayOf("-version"), object : ExecuteBinaryResponseHandler() {
override fun onSuccess(message: String) {
Log.d(message)
}
override fun onProgress(message: String) {
Log.d(message)
}
})
}
}

View File

@ -39,6 +39,7 @@ public class FFprobe implements FFbinaryInterface {
// check if ffprobe can be executed
if (!ffprobe.canExecute()) {
// try to make executable
Log.e("ffprobe cannot execute");
return false;
}

View File

@ -5,8 +5,8 @@ import android.content.Context;
import java.io.File;
class FileUtils {
private static final String FFMPEG_FILE_NAME = "ffmpeg";
private static final String FFPROBE_FILE_NAME = "ffprobe";
private static final String FFMPEG_FILE_NAME = "lib..ffmpeg..so";
private static final String FFPROBE_FILE_NAME = "lib..ffprobe..so";
static File getFFmpeg(Context context) {
File folder = new File(context.getApplicationInfo().nativeLibraryDir);

View File

@ -6,5 +6,6 @@ cd "$(dirname "$0")" || echo "cd to $(dirname "$0") Failed"
# Copy ffmpeg executables for all targets
for target in arm64-v8a armeabi-v7a x86 x86_64
do
cp ./ffmpeg-android-maker/build/ffmpeg/$target/bin/ffmpeg ./android-ffmpeg/src/main/resources/lib/$target/
mkdir -p ./android-ffmpeg/src/main/jniLibs/$target/
cp ./ffmpeg-android-maker/build/ffmpeg/$target/bin/ffmpeg ./android-ffmpeg/src/main/jniLibs/$target/lib..ffmpeg.so
done