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 { android {
val props = gradleLocalProperties(rootDir) val props = gradleLocalProperties(rootDir)
if (props.containsKey("storeFileDir")) { if (props.containsKey("storeFileDir")) {
signingConfigs { signingConfigs {
create("release") { create("release") {

View File

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

View File

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

View File

@ -46,6 +46,7 @@ import com.shabinder.common.translations.Strings
import com.shabinder.spotiflyer.R import com.shabinder.spotiflyer.R
import com.shabinder.spotiflyer.utils.autoclear.AutoClear import com.shabinder.spotiflyer.utils.autoclear.AutoClear
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.Dispatchers
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
@ -184,7 +185,14 @@ class ForegroundService : LifecycleService() {
addToNotification(Message(track.title, DownloadStatus.Converting)) addToNotification(Message(track.title, DownloadStatus.Converting))
// All Processing Completed for this Track // 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++ converted++
trackStatusFlowMap[track.title] = DownloadStatus.Downloaded trackStatusFlowMap[track.title] = DownloadStatus.Downloaded
removeFromNotification(Message(track.title, DownloadStatus.Converting)) removeFromNotification(Message(track.title, DownloadStatus.Converting))

View File

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

View File

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

View File

@ -30,6 +30,7 @@ import io.ktor.client.request.*
import io.ktor.client.statement.* import io.ktor.client.statement.*
import io.ktor.http.* import io.ktor.http.*
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import org.koin.core.module.Module import org.koin.core.module.Module
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -102,7 +103,6 @@ fun getNameURL(url: String): String {
suspend fun downloadFile(url: String): Flow<DownloadResult> { suspend fun downloadFile(url: String): Flow<DownloadResult> {
return flow { return flow {
try {
val client = createHttpClient() val client = createHttpClient()
val response = client.get<HttpStatement>(url).execute() val response = client.get<HttpStatement>(url).execute()
val data = ByteArray(response.contentLength()!!.toInt()) val data = ByteArray(response.contentLength()!!.toInt())
@ -120,11 +120,10 @@ suspend fun downloadFile(url: String): Flow<DownloadResult> {
emit(DownloadResult.Error("File not downloaded")) emit(DownloadResult.Error("File not downloaded"))
} }
client.close() client.close()
} catch (e: Exception) { }.catch { e ->
e.printStackTrace() e.printStackTrace()
emit(DownloadResult.Error(e.message ?: "File not downloaded")) emit(DownloadResult.Error(e.message ?: "File not downloaded"))
} }
}
} }
suspend fun downloadByteArray( suspend fun downloadByteArray(

View File

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

View File

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

View File

@ -61,9 +61,7 @@ internal class SpotiFlyerPreferenceStoreProvider(
is Intent.ShareApp -> methods.value.shareApp() is Intent.ShareApp -> methods.value.shareApp()
is Intent.ToggleAnalytics -> { is Intent.ToggleAnalytics -> {
dispatch(Result.AnalyticsToggled(intent.enabled)) dispatch(Result.AnalyticsToggled(intent.enabled))
preferenceManager.toggleAnalytics(intent.enabled) { preferenceManager.toggleAnalytics(intent.enabled)
if (intent.enabled) analyticsManager.giveConsent() else analyticsManager.revokeConsent()
}
} }
is Intent.SetDownloadDirectory -> { is Intent.SetDownloadDirectory -> {
dispatch(Result.DownloadPathSet(intent.path)) 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 fetchQuery: FetchPlatformQueryResult = koin.get()
override val fileManager: FileManager = koin.get() override val fileManager: FileManager = koin.get()
override val database: Database? = fileManager.db override val database: Database? = fileManager.db
override val preferenceManager: PreferenceManager = koin.get()
override val analyticsManager: AnalyticsManager = 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 downloadProgressFlow = DownloadProgressFlow
override val actions: Actions = object : Actions { override val actions: Actions = object : Actions {
override val platformActions = object : PlatformActions {} 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. # Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the # 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 # For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html # 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 android.os.AsyncTask;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.Map; import java.util.Map;
public class FFmpeg implements FFbinaryInterface { public class FFmpeg implements FFbinaryInterface {
@ -34,7 +35,6 @@ public class FFmpeg implements FFbinaryInterface {
@Override @Override
public boolean isSupported() { public boolean isSupported() {
// get ffmpeg file // get ffmpeg file
File ffmpeg = FileUtils.getFFmpeg(context.provide()); 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 // check if ffprobe can be executed
if (!ffprobe.canExecute()) { if (!ffprobe.canExecute()) {
// try to make executable
Log.e("ffprobe cannot execute"); Log.e("ffprobe cannot execute");
return false; return false;
} }

View File

@ -5,8 +5,8 @@ import android.content.Context;
import java.io.File; import java.io.File;
class FileUtils { class FileUtils {
private static final String FFMPEG_FILE_NAME = "ffmpeg"; private static final String FFMPEG_FILE_NAME = "lib..ffmpeg..so";
private static final String FFPROBE_FILE_NAME = "ffprobe"; private static final String FFPROBE_FILE_NAME = "lib..ffprobe..so";
static File getFFmpeg(Context context) { static File getFFmpeg(Context context) {
File folder = new File(context.getApplicationInfo().nativeLibraryDir); 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 # Copy ffmpeg executables for all targets
for target in arm64-v8a armeabi-v7a x86 x86_64 for target in arm64-v8a armeabi-v7a x86 x86_64
do 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 done